Compare commits
No commits in common. "master" and "1.1.1121" have entirely different histories.
@ -17,8 +17,8 @@ tab_width = 4
|
|||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Markdown, JSON, YAML, props and csproj files
|
# JSON files
|
||||||
[*.{md,json,yml,props,csproj}]
|
[*.json]
|
||||||
|
|
||||||
# Indentation and spacing
|
# Indentation and spacing
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
@ -259,14 +259,14 @@ dotnet_diagnostic.CA1861.severity = none
|
|||||||
# Disable "Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'"
|
# Disable "Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'"
|
||||||
dotnet_diagnostic.CA1862.severity = none
|
dotnet_diagnostic.CA1862.severity = none
|
||||||
|
|
||||||
[src/Ryujinx/UI/ViewModels/**.cs]
|
|
||||||
# Disable "mark members as static" rule for ViewModels
|
|
||||||
dotnet_diagnostic.CA1822.severity = none
|
|
||||||
|
|
||||||
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
||||||
# Disable "mark members as static" rule for services
|
# Disable "mark members as static" rule for services
|
||||||
dotnet_diagnostic.CA1822.severity = none
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
|
|
||||||
|
[src/Ryujinx.Ava/UI/ViewModels/**.cs]
|
||||||
|
# Disable "mark members as static" rule for ViewModels
|
||||||
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
|
|
||||||
[src/Ryujinx.Tests/Cpu/*.cs]
|
[src/Ryujinx.Tests/Cpu/*.cs]
|
||||||
# Disable naming rules for CPU tests
|
# Disable naming rules for CPU tests
|
||||||
dotnet_diagnostic.IDE1006.severity = none
|
dotnet_diagnostic.IDE1006.severity = none
|
||||||
|
2
.github/labeler.yml
vendored
@ -20,7 +20,7 @@ gpu:
|
|||||||
|
|
||||||
gui:
|
gui:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**', 'src/Ryujinx.Gtk3/**']
|
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.Ui.Common/**', 'src/Ryujinx.Ui.LocaleGenerator/**', 'src/Ryujinx.Ava/**']
|
||||||
|
|
||||||
horizon:
|
horizon:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
85
.github/workflows/build.yml
vendored
@ -10,17 +10,28 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.platform.name }} (${{ matrix.configuration }})
|
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||||
configuration: [Debug, Release]
|
configuration: [Debug, Release]
|
||||||
platform:
|
include:
|
||||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
- os: ubuntu-latest
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
OS_NAME: Linux x64
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||||
- { name: osx-x64, os: macOS-latest, zip_os_name: osx_x64 }
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
|
- os: macOS-latest
|
||||||
|
OS_NAME: macOS x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: osx_x64
|
||||||
|
|
||||||
|
- os: windows-latest
|
||||||
|
OS_NAME: Windows x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
@ -38,16 +49,6 @@ jobs:
|
|||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Change config filename
|
|
||||||
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
|
||||||
|
|
||||||
- name: Change config filename for macOS
|
|
||||||
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'macOS-latest'
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||||
|
|
||||||
@ -57,47 +58,46 @@ jobs:
|
|||||||
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
|
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
retry-codes: 139
|
retry-codes: 139
|
||||||
if: matrix.platform.name != 'linux-arm64'
|
|
||||||
|
|
||||||
- name: Publish Ryujinx
|
- name: Publish Ryujinx
|
||||||
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 true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -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 true
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
- 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 true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -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 true
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Gtk3
|
- name: Publish Ryujinx.Ava
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_gtk -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Gtk3 --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Set executable bit
|
- name: Set executable bit
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
||||||
chmod +x ./publish_gtk/Ryujinx.Gtk3 ./publish_gtk/Ryujinx.sh
|
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
|
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish
|
path: publish
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish_sdl2_headless
|
path: publish_sdl2_headless
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Gtk3 artifact
|
- name: Upload Ryujinx.Ava artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish_gtk
|
path: publish_ava
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
build_macos:
|
build_macos:
|
||||||
name: macOS Universal (${{ matrix.configuration }})
|
name: macOS Universal (${{ matrix.configuration }})
|
||||||
@ -135,24 +135,19 @@ jobs:
|
|||||||
id: git_short_hash
|
id: git_short_hash
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Change config filename
|
- name: Publish macOS Ryujinx.Ava
|
||||||
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx
|
|
||||||
run: |
|
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"
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./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
|
- name: Publish macOS Ryujinx.Headless.SDL2
|
||||||
run: |
|
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"
|
./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
|
- name: Upload Ryujinx.Ava artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||||
path: "publish/*.tar.gz"
|
path: "publish_ava/*.tar.gz"
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||||
|
2
.github/workflows/checks.yml
vendored
@ -8,7 +8,7 @@ on:
|
|||||||
- '!.github/**'
|
- '!.github/**'
|
||||||
- '!*.yml'
|
- '!*.yml'
|
||||||
- '!*.config'
|
- '!*.config'
|
||||||
- '!*.md'
|
- '!README.md'
|
||||||
- '.github/workflows/*.yml'
|
- '.github/workflows/*.yml'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
72
.github/workflows/flatpak.yml
vendored
@ -51,76 +51,38 @@ jobs:
|
|||||||
- name: Restore Nuget packages
|
- name: Restore Nuget packages
|
||||||
# With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing.
|
# With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing.
|
||||||
# So we just publish to grab the dependencies
|
# So we just publish to grab the dependencies
|
||||||
run: |
|
run: dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
||||||
dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
|
||||||
dotnet publish -c Release -r linux-arm64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
|
||||||
|
|
||||||
- name: Generate nuget_sources.json
|
- name: Generate nuget_sources.json
|
||||||
shell: python
|
shell: python
|
||||||
run: |
|
run: |
|
||||||
import hashlib
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
sources = []
|
sources = []
|
||||||
|
|
||||||
|
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||||
|
name = path.parent.parent.name
|
||||||
|
version = path.parent.name
|
||||||
|
filename = '{}.{}.nupkg'.format(name, version)
|
||||||
|
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||||
|
|
||||||
def create_source_from_external(name, version):
|
with path.open() as fp:
|
||||||
full_dir_path = Path(os.environ["NUGET_PACKAGES"]).joinpath(name).joinpath(version)
|
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||||
os.makedirs(full_dir_path, exist_ok=True)
|
|
||||||
|
|
||||||
filename = "{}.{}.nupkg".format(name, version)
|
sources.append({
|
||||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
'type': 'file',
|
||||||
name, version, filename
|
'url': url,
|
||||||
)
|
'sha512': sha512,
|
||||||
|
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||||
|
'dest-filename': filename,
|
||||||
|
})
|
||||||
|
|
||||||
print(f"Processing {url}...")
|
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||||
response = urllib.request.urlopen(url)
|
json.dump(sources, fp, indent=4)
|
||||||
sha512 = hashlib.sha512(response.read()).hexdigest()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"type": "file",
|
|
||||||
"url": url,
|
|
||||||
"sha512": sha512,
|
|
||||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
|
||||||
"dest-filename": filename,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
has_added_x64_apphost = False
|
|
||||||
|
|
||||||
for path in Path(os.environ["NUGET_PACKAGES"]).glob("**/*.nupkg.sha512"):
|
|
||||||
name = path.parent.parent.name
|
|
||||||
version = path.parent.name
|
|
||||||
filename = "{}.{}.nupkg".format(name, version)
|
|
||||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
|
||||||
name, version, filename
|
|
||||||
)
|
|
||||||
|
|
||||||
with path.open() as fp:
|
|
||||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode("ascii")
|
|
||||||
|
|
||||||
sources.append(
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"url": url,
|
|
||||||
"sha512": sha512,
|
|
||||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
|
||||||
"dest-filename": filename,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# .NET will not add current installed application host to the list, force inject it here.
|
|
||||||
if not has_added_x64_apphost and name.startswith('microsoft.netcore.app.host'):
|
|
||||||
sources.append(create_source_from_external("microsoft.netcore.app.host.linux-x64", version))
|
|
||||||
has_added_x64_apphost = True
|
|
||||||
|
|
||||||
with open("flathub/nuget_sources.json", "w") as fp:
|
|
||||||
json.dump(sources, fp, indent=4)
|
|
||||||
|
|
||||||
- name: Update flatpak metadata
|
- name: Update flatpak metadata
|
||||||
id: metadata
|
id: metadata
|
||||||
|
41
.github/workflows/mako.yml
vendored
@ -1,41 +0,0 @@
|
|||||||
name: Mako
|
|
||||||
on:
|
|
||||||
discussion:
|
|
||||||
types: [created, edited, answered, unanswered, category_changed]
|
|
||||||
discussion_comment:
|
|
||||||
types: [created, edited]
|
|
||||||
gollum:
|
|
||||||
issue_comment:
|
|
||||||
types: [created, edited]
|
|
||||||
issues:
|
|
||||||
types: [opened, edited, reopened, pinned, milestoned, demilestoned, assigned, unassigned, labeled, unlabeled]
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, edited, reopened, synchronize, ready_for_review, assigned, unassigned]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
tasks:
|
|
||||||
name: Run Ryujinx tasks
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
discussions: write
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
with:
|
|
||||||
# Ensure we pin the source origin as pull_request_target run under forks.
|
|
||||||
fetch-depth: 0
|
|
||||||
repository: Ryujinx/Ryujinx
|
|
||||||
ref: master
|
|
||||||
|
|
||||||
- name: Run Mako command
|
|
||||||
uses: Ryujinx/Ryujinx-Mako@master
|
|
||||||
with:
|
|
||||||
command: exec-ryujinx-tasks
|
|
||||||
args: --event-name "${{ github.event_name }}" --event-path "${{ github.event_path }}" -w "${{ github.workspace }}" "${{ github.repository }}" "${{ github.run_id }}"
|
|
||||||
app_id: ${{ secrets.MAKO_APP_ID }}
|
|
||||||
private_key: ${{ secrets.MAKO_PRIVATE_KEY }}
|
|
||||||
installation_id: ${{ secrets.MAKO_INSTALLATION_ID }}
|
|
10
.github/workflows/nightly_pr_comment.yml
vendored
@ -39,24 +39,24 @@ jobs:
|
|||||||
return core.error(`No artifacts found`);
|
return core.error(`No artifacts found`);
|
||||||
}
|
}
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
let body = `Download the artifacts for this pull request:\n`;
|
||||||
let hidden_gtk_artifacts = `\n\n <details><summary>Old GUI (GTK3)</summary>\n`;
|
let hidden_avalonia_artifacts = `\n\n <details><summary>Experimental GUI (Avalonia)</summary>\n`;
|
||||||
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||||
for (const art of artifacts) {
|
for (const art of artifacts) {
|
||||||
if(art.name.includes('Debug')) {
|
if(art.name.includes('Debug')) {
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else if(art.name.includes('gtk-ryujinx')) {
|
} else if(art.name.includes('ava-ryujinx')) {
|
||||||
hidden_gtk_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_avalonia_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
||||||
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else {
|
} else {
|
||||||
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hidden_gtk_artifacts += `\n</details>`;
|
hidden_avalonia_artifacts += `\n</details>`;
|
||||||
hidden_headless_artifacts += `\n</details>`;
|
hidden_headless_artifacts += `\n</details>`;
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
hidden_debug_artifacts += `\n</details>`;
|
||||||
body += hidden_gtk_artifacts;
|
body += hidden_avalonia_artifacts;
|
||||||
body += hidden_headless_artifacts;
|
body += hidden_headless_artifacts;
|
||||||
body += hidden_debug_artifacts;
|
body += hidden_debug_artifacts;
|
||||||
|
|
||||||
|
19
.github/workflows/pr_triage.yml
vendored
@ -21,8 +21,27 @@ jobs:
|
|||||||
repository: Ryujinx/Ryujinx
|
repository: Ryujinx/Ryujinx
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
|
- name: Checkout Ryujinx-Mako
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: Ryujinx/Ryujinx-Mako
|
||||||
|
ref: master
|
||||||
|
path: '.ryujinx-mako'
|
||||||
|
|
||||||
|
- name: Setup Ryujinx-Mako
|
||||||
|
uses: ./.ryujinx-mako/.github/actions/setup-mako
|
||||||
|
|
||||||
- name: Update labels based on changes
|
- name: Update labels based on changes
|
||||||
uses: actions/labeler@v5
|
uses: actions/labeler@v5
|
||||||
with:
|
with:
|
||||||
sync-labels: true
|
sync-labels: true
|
||||||
dot: true
|
dot: true
|
||||||
|
|
||||||
|
- name: Assign reviewers
|
||||||
|
run: |
|
||||||
|
poetry -n -C .ryujinx-mako run ryujinx-mako update-reviewers ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
MAKO_APP_ID: ${{ secrets.MAKO_APP_ID }}
|
||||||
|
MAKO_PRIVATE_KEY: ${{ secrets.MAKO_PRIVATE_KEY }}
|
||||||
|
MAKO_INSTALLATION_ID: ${{ secrets.MAKO_INSTALLATION_ID }}
|
||||||
|
63
.github/workflows/release.yml
vendored
@ -10,7 +10,7 @@ on:
|
|||||||
- '*.yml'
|
- '*.yml'
|
||||||
- '*.json'
|
- '*.json'
|
||||||
- '*.config'
|
- '*.config'
|
||||||
- '*.md'
|
- 'README.md'
|
||||||
|
|
||||||
concurrency: release
|
concurrency: release
|
||||||
|
|
||||||
@ -44,27 +44,23 @@ jobs:
|
|||||||
sha: context.sha
|
sha: context.sha
|
||||||
})
|
})
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
uses: ncipollo/release-action@v1
|
|
||||||
with:
|
|
||||||
name: ${{ steps.version_info.outputs.build_version }}
|
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
|
||||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
|
||||||
omitBodyDuringUpdate: true
|
|
||||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
|
||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Release for ${{ matrix.platform.name }}
|
name: Release ${{ matrix.OS_NAME }}
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
os: [ ubuntu-latest, windows-latest ]
|
||||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
include:
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- os: ubuntu-latest
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
OS_NAME: Linux x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
|
- os: windows-latest
|
||||||
|
OS_NAME: Windows x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -89,7 +85,6 @@ jobs:
|
|||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create output dir
|
- name: Create output dir
|
||||||
@ -97,43 +92,42 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
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 true
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_gtk/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 true
|
||||||
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 true
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -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 true
|
||||||
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -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.Ava --self-contained true
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish_gtk
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_ava
|
pushd publish_ava
|
||||||
mv publish/Ryujinx.exe publish/Ryujinx.Ava.exe
|
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
if: matrix.platform.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish_gtk
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_ava
|
pushd publish_ava
|
||||||
mv publish/Ryujinx publish/Ryujinx.Ava
|
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -192,10 +186,9 @@ jobs:
|
|||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx.Ava
|
||||||
run: |
|
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
|
./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
|
||||||
|
|
||||||
|
@ -8,34 +8,35 @@
|
|||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.7" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.0.7" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.7" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.7" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.7" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.7" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.13" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.10" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.13" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.10" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="8.3.27" />
|
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.3.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.2.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="7.0.0" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Core" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.1" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.28.1-build28" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
@ -44,8 +45,8 @@
|
|||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.2" />
|
<PackageVersion Include="System.Drawing.Common" Version="8.0.0" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
|
93
README.md
@ -1,21 +1,21 @@
|
|||||||
|
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<br>
|
<br>
|
||||||
<a href="https://ryujinx.org/"><img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx/master/distribution/misc/Logo.svg" alt="Ryujinx" width="150"></a>
|
<a href="https://ryujinx.org/"><img src="https://i.imgur.com/WcCj6Rt.png" alt="Ryujinx" width="150"></a>
|
||||||
<br>
|
<br>
|
||||||
<b>Ryujinx</b>
|
<b>Ryujinx</b>
|
||||||
<br>
|
<br>
|
||||||
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
||||||
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
||||||
It was written from scratch and development on the project began in September 2017.
|
It was written from scratch and development on the project began in September 2017. Ryujinx is available on Github under the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>. <br />
|
||||||
Ryujinx is available on Github under the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml">
|
<a href="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml">
|
||||||
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
||||||
@ -34,111 +34,87 @@
|
|||||||
<img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx-Website/master/public/assets/images/shell.png">
|
<img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx-Website/master/public/assets/images/shell.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h5 align="center">
|
||||||
|
|
||||||
|
</h5>
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of October 2023, Ryujinx has been tested on approximately 4,200 titles;
|
As of April 2023, Ryujinx has been tested on approximately 4,050 titles; over 4,000 boot past menus and into gameplay, with roughly 3,400 of those being considered playable.
|
||||||
over 4,150 boot past menus and into gameplay, with roughly 3,500 of those being considered playable.
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
|
||||||
|
|
||||||
Anyone is free to submit a new game test or update an existing game test entry;
|
|
||||||
simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue.
|
|
||||||
Use the search function to see if a game has been tested already!
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
To run this emulator, your PC must be equipped with at least 8GiB of RAM;
|
To run this emulator, your PC must be equipped with at least 8GiB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
||||||
failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
|
||||||
|
|
||||||
See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
|
See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
|
||||||
|
|
||||||
For our Local Wireless (LDN) builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
||||||
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
||||||
|
|
||||||
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
|
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
|
||||||
|
|
||||||
## Latest build
|
## Latest build
|
||||||
|
|
||||||
These builds are compiled automatically for each commit on the master branch.
|
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**
|
||||||
While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken**.
|
|
||||||
|
|
||||||
If you want to see details on updates to the emulator, you can visit our [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog).
|
If you want to see details on updates to the emulator, you can visit our [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog).
|
||||||
|
|
||||||
The latest automatic build for Windows, macOS, and Linux can be found on the [Official Website](https://ryujinx.org/download).
|
The latest automatic build for Windows, macOS, and Linux can be found on the [Official Website](https://ryujinx.org/download).
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
If you are planning to contribute or just want to learn more about this project please read through our [documentation](docs/README.md).
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
If you wish to build the emulator yourself, follow these steps:
|
If you wish to build the emulator yourself, follow these steps:
|
||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
|
Install the X64 version of [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
|
||||||
Install the [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
|
|
||||||
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
|
|
||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
|
|
||||||
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||||
|
|
||||||
### Step 3
|
### Step 3
|
||||||
|
|
||||||
To build Ryujinx, open a command prompt inside the project directory.
|
To build Ryujinx, open a command prompt inside the project directory. You can quickly access it on Windows by holding shift in File Explorer, then right clicking and selecting `Open command window here`. Then type the following command:
|
||||||
You can quickly access it on Windows by holding shift in File Explorer, then right clicking and selecting `Open command window here`.
|
`dotnet build -c Release -o build`
|
||||||
Then type the following command: `dotnet build -c Release -o build`
|
|
||||||
the built files will be found in the newly created build directory.
|
the built files will be found in the newly created build directory.
|
||||||
|
|
||||||
Ryujinx system files are stored in the `Ryujinx` folder.
|
Ryujinx system files are stored in the `Ryujinx` folder. This folder is located in the user folder, which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI.
|
||||||
This folder is located in the user folder, which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Audio**
|
- **Audio**
|
||||||
|
|
||||||
Audio output is entirely supported, audio input (microphone) isn't supported.
|
Audio output is entirely supported, audio input (microphone) isn't supported. We use C# wrappers for [OpenAL](https://openal-soft.org/), and [SDL2](https://www.libsdl.org/) & [libsoundio](http://libsound.io/) as fallbacks.
|
||||||
We use C# wrappers for [OpenAL](https://openal-soft.org/), and [SDL2](https://www.libsdl.org/) & [libsoundio](http://libsound.io/) as fallbacks.
|
|
||||||
|
|
||||||
- **CPU**
|
- **CPU**
|
||||||
|
|
||||||
The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions, including partial 32-bit support.
|
The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions, including partial 32-bit support. It translates the ARM code to a custom IR, performs a few optimizations, and turns that into x86 code.
|
||||||
It translates the ARM code to a custom IR, performs a few optimizations, and turns that into x86 code.
|
There are three memory manager options available depending on the user's preference, leveraging both software-based (slower) and host-mapped modes (much faster). The fastest option (host, unchecked) is set by default.
|
||||||
There are three memory manager options available depending on the user's preference, leveraging both software-based (slower) and host-mapped modes (much faster).
|
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads. The net result is a significant reduction in load times (the amount of time between launching a game and arriving at the title screen) for nearly every game. NOTE: this feature is enabled by default in the Options menu > System tab. You must launch the game at least twice to the title screen or beyond before performance improvements are unlocked on the third launch! These improvements are permanent and do not require any extra launches going forward.
|
||||||
The fastest option (host, unchecked) is set by default.
|
|
||||||
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads.
|
|
||||||
The net result is a significant reduction in load times (the amount of time between launching a game and arriving at the title screen) for nearly every game.
|
|
||||||
NOTE: This feature is enabled by default in the Options menu > System tab.
|
|
||||||
You must launch the game at least twice to the title screen or beyond before performance improvements are unlocked on the third launch!
|
|
||||||
These improvements are permanent and do not require any extra launches going forward.
|
|
||||||
|
|
||||||
- **GPU**
|
- **GPU**
|
||||||
|
|
||||||
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||||
There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment.
|
|
||||||
These enhancements can be adjusted or toggled as desired in the GUI.
|
|
||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers.
|
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers. Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
||||||
Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
In all scenarios, you can set up everything inside the input configuration menu.
|
||||||
In all scenarios, you can set up everything inside the input configuration menu.
|
|
||||||
|
|
||||||
- **DLC & Modifications**
|
- **DLC & Modifications**
|
||||||
|
|
||||||
Ryujinx is able to manage add-on content/downloadable content through the GUI.
|
Ryujinx is able to manage add-on content/downloadable content through the GUI. Mods (romfs, exefs, and runtime mods such as cheats) are also supported; the GUI contains a shortcut to open the respective mods folder for a particular game.
|
||||||
Mods (romfs, exefs, and runtime mods such as cheats) are also supported;
|
|
||||||
the GUI contains a shortcut to open the respective mods folder for a particular game.
|
|
||||||
|
|
||||||
- **Configuration**
|
- **Configuration**
|
||||||
|
|
||||||
The emulator has settings for enabling or disabling some logging, remapping controllers, and more.
|
The emulator has settings for enabling or disabling some logging, remapping controllers, and more. You can configure all of them through the graphical interface or manually through the config file, `Config.json`, found in the user folder which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI.
|
||||||
You can configure all of them through the graphical interface or manually through the config file, `Config.json`, found in the user folder which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI.
|
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
If you have contributions, suggestions, need emulator support or just want to get in touch with the team, join our [Discord server](https://discord.com/invite/Ryujinx).
|
If you have contributions, suggestions, need emulator support or just want to get in touch with the team, join our [Discord server](https://discord.com/invite/Ryujinx). You may also review our [FAQ](https://github.com/Ryujinx/Ryujinx/wiki/Frequently-Asked-Questions).
|
||||||
You may also review our [FAQ](https://github.com/Ryujinx/Ryujinx/wiki/Frequently-Asked-Questions).
|
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
@ -158,10 +134,9 @@ All funds received through Patreon are considered a donation to support the proj
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This software is licensed under the terms of the [MIT license](LICENSE.txt).
|
This software is licensed under the terms of the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license.</a></i><br />
|
||||||
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
|
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
|
||||||
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
|
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
||||||
|
@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.1.32228.430
|
VisualStudioVersion = 17.1.32228.430
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Gtk3", "src\Ryujinx.Gtk3\Ryujinx.Gtk3.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
||||||
EndProject
|
EndProject
|
||||||
@ -69,9 +69,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "sr
|
|||||||
EndProject
|
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}"
|
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
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "src\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
||||||
EndProject
|
EndProject
|
||||||
@ -79,7 +79,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.LocaleGenerator", "src\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "src\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -4,7 +4,7 @@ Name=Ryujinx
|
|||||||
Type=Application
|
Type=Application
|
||||||
Icon=Ryujinx
|
Icon=Ryujinx
|
||||||
Exec=Ryujinx.sh %f
|
Exec=Ryujinx.sh %f
|
||||||
Comment=A Nintendo Switch Emulator
|
Comment=Plays Nintendo Switch applications
|
||||||
GenericName=Nintendo Switch Emulator
|
GenericName=Nintendo Switch Emulator
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=Game;Emulator;
|
Categories=Game;Emulator;
|
||||||
|
15
distribution/linux/Ryujinx.sh
Executable file → Normal file
@ -1,23 +1,20 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||||
|
RYUJINX_BIN="Ryujinx"
|
||||||
|
|
||||||
|
if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then
|
||||||
|
RYUJINX_BIN="Ryujinx.Ava"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/Ryujinx" ]; then
|
|
||||||
RYUJINX_BIN="Ryujinx"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$RYUJINX_BIN" ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
||||||
|
|
||||||
if command -v gamemoderun > /dev/null 2>&1; then
|
if command -v gamemoderun > /dev/null 2>&1; then
|
||||||
COMMAND="$COMMAND gamemoderun"
|
COMMAND="$COMMAND gamemoderun"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec $COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
@ -14,8 +14,8 @@ mkdir "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
|||||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS"
|
mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS"
|
||||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
||||||
|
|
||||||
# Copy executable and nsure executable can be executed
|
# Copy executables first
|
||||||
cp "$PUBLISH_DIRECTORY/Ryujinx" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
cp "$PUBLISH_DIRECTORY/Ryujinx.Ava" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||||
chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||||
|
|
||||||
# Then all libraries
|
# Then all libraries
|
||||||
|
@ -22,9 +22,9 @@ EXTRA_ARGS=$8
|
|||||||
|
|
||||||
if [ "$VERSION" == "1.1.0" ];
|
if [ "$VERSION" == "1.1.0" ];
|
||||||
then
|
then
|
||||||
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||||
else
|
else
|
||||||
RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_universal.app.tar
|
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$VERSION-macos_universal.app.tar
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
|
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
|
||||||
@ -38,9 +38,9 @@ mkdir -p "$TEMP_DIRECTORY"
|
|||||||
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
|
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
|
||||||
|
|
||||||
dotnet restore
|
dotnet restore
|
||||||
dotnet build -c "$CONFIGURATION" src/Ryujinx
|
dotnet build -c "$CONFIGURATION" src/Ryujinx.Ava
|
||||||
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
|
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava
|
||||||
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
|
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava
|
||||||
|
|
||||||
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
||||||
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
||||||
@ -108,13 +108,6 @@ tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf "$RELEASE_TAR_FILE_NAME"
|
|||||||
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
|
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
|
||||||
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||||
rm "$RELEASE_TAR_FILE_NAME"
|
rm "$RELEASE_TAR_FILE_NAME"
|
||||||
|
|
||||||
# Create legacy update package for Avalonia to not left behind old testers.
|
|
||||||
if [ "$VERSION" != "1.1.0" ];
|
|
||||||
then
|
|
||||||
cp $RELEASE_TAR_FILE_NAME.gz test-ava-ryujinx-$VERSION-macos_universal.app.tar.gz
|
|
||||||
fi
|
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
echo "Done"
|
echo "Done"
|
@ -1,8 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
launch_arch="$(uname -m)"
|
|
||||||
if [ "$(sysctl -in sysctl.proc_translated)" = "1" ]
|
|
||||||
then
|
|
||||||
launch_arch="arm64"
|
|
||||||
fi
|
|
||||||
|
|
||||||
arch -$launch_arch {0} {1}
|
|
@ -517,10 +517,7 @@ namespace ARMeilleure.Decoders
|
|||||||
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", InstName.Sqrshrn_V, InstEmit.Sqrshrn_V, OpCodeSimdShImm.Create);
|
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", InstName.Sqrshrn_V, InstEmit.Sqrshrn_V, OpCodeSimdShImm.Create);
|
||||||
SetA64("0111111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_S, InstEmit.Sqrshrun_S, OpCodeSimdShImm.Create);
|
SetA64("0111111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_S, InstEmit.Sqrshrun_S, OpCodeSimdShImm.Create);
|
||||||
SetA64("0x10111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_V, InstEmit.Sqrshrun_V, OpCodeSimdShImm.Create);
|
SetA64("0x10111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_V, InstEmit.Sqrshrun_V, OpCodeSimdShImm.Create);
|
||||||
SetA64("010111110>>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Si, InstEmit.Sqshl_Si, OpCodeSimdShImm.Create);
|
|
||||||
SetA64("0>001110<<1xxxxx010011xxxxxxxxxx", InstName.Sqshl_V, InstEmit.Sqshl_V, OpCodeSimdReg.Create);
|
SetA64("0>001110<<1xxxxx010011xxxxxxxxxx", InstName.Sqshl_V, InstEmit.Sqshl_V, OpCodeSimdReg.Create);
|
||||||
SetA64("0000111100>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Vi, InstEmit.Sqshl_Vi, OpCodeSimdShImm.Create);
|
|
||||||
SetA64("010011110>>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Vi, InstEmit.Sqshl_Vi, OpCodeSimdShImm.Create);
|
|
||||||
SetA64("0101111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_S, InstEmit.Sqshrn_S, OpCodeSimdShImm.Create);
|
SetA64("0101111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_S, InstEmit.Sqshrn_S, OpCodeSimdShImm.Create);
|
||||||
SetA64("0x00111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_V, InstEmit.Sqshrn_V, OpCodeSimdShImm.Create);
|
SetA64("0x00111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_V, InstEmit.Sqshrn_V, OpCodeSimdShImm.Create);
|
||||||
SetA64("0111111100>>>xxx100001xxxxxxxxxx", InstName.Sqshrun_S, InstEmit.Sqshrun_S, OpCodeSimdShImm.Create);
|
SetA64("0111111100>>>xxx100001xxxxxxxxxx", InstName.Sqshrun_S, InstEmit.Sqshrun_S, OpCodeSimdShImm.Create);
|
||||||
@ -875,7 +872,6 @@ namespace ARMeilleure.Decoders
|
|||||||
SetVfp("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
SetVfp("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||||
SetVfp("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110110xxxx101x01x0xxxx", InstName.Vrintr, InstEmit32.Vrintr_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
|
||||||
SetVfp("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create, OpCode32SimdSel.CreateT32);
|
SetVfp("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create, OpCode32SimdSel.CreateT32);
|
||||||
@ -996,7 +992,6 @@ namespace ARMeilleure.Decoders
|
|||||||
SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32);
|
SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32);
|
||||||
SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100111x11<<00xxxx0110xxx0xxxx", InstName.Vpadal, InstEmit32.Vpadal, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
|
||||||
SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||||
SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
@ -1115,13 +1115,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Vpadal(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
EmitVectorPairwiseTernaryLongOpI32(context, (op1, op2, op3) => context.Add(context.Add(op1, op2), op3), op.Opc != 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vpaddl(ArmEmitterContext context)
|
public static void Vpaddl(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||||
|
@ -578,22 +578,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VRINTR (floating-point).
|
|
||||||
public static void Vrintr_S(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseAdvSimd)
|
|
||||||
{
|
|
||||||
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintiS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitScalarUnaryOpF32(context, (op1) =>
|
|
||||||
{
|
|
||||||
return EmitRoundByRMode(context, op1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VRINTZ (floating-point).
|
// VRINTZ (floating-point).
|
||||||
public static void Vrint_Z(ArmEmitterContext context)
|
public static void Vrint_Z(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
|
@ -673,35 +673,6 @@ namespace ARMeilleure.Instructions
|
|||||||
context.Copy(GetVecA32(op.Qd), res);
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVectorPairwiseTernaryLongOpI32(ArmEmitterContext context, Func3I emit, bool signed)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
int elems = op.GetBytesCount() >> op.Size;
|
|
||||||
int pairs = elems >> 1;
|
|
||||||
|
|
||||||
Operand res = GetVecA32(op.Qd);
|
|
||||||
|
|
||||||
for (int index = 0; index < pairs; index++)
|
|
||||||
{
|
|
||||||
int pairIndex = index * 2;
|
|
||||||
Operand m1 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex, op.Size, signed);
|
|
||||||
Operand m2 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex + 1, op.Size, signed);
|
|
||||||
|
|
||||||
if (op.Size == 2)
|
|
||||||
{
|
|
||||||
m1 = signed ? context.SignExtend32(OperandType.I64, m1) : context.ZeroExtend32(OperandType.I64, m1);
|
|
||||||
m2 = signed ? context.SignExtend32(OperandType.I64, m2) : context.ZeroExtend32(OperandType.I64, m2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand d1 = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size + 1, signed);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, emit(m1, m2, d1), op.Id + index, op.Size + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Narrow
|
// Narrow
|
||||||
|
|
||||||
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit, bool signed = false)
|
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit, bool signed = false)
|
||||||
|
@ -116,7 +116,7 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
else if (shift >= eSize)
|
else if (shift >= eSize)
|
||||||
{
|
{
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if ((op.RegisterSize == RegisterSize.Simd64))
|
||||||
{
|
{
|
||||||
Operand res = context.VectorZeroUpper64(GetVec(op.Rd));
|
Operand res = context.VectorZeroUpper64(GetVec(op.Rd));
|
||||||
|
|
||||||
@ -359,16 +359,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Sqshl_Si(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitShlImmOp(context, signedDst: true, ShlRegFlags.Signed | ShlRegFlags.Scalar | ShlRegFlags.Saturating);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sqshl_Vi(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitShlImmOp(context, signedDst: true, ShlRegFlags.Signed | ShlRegFlags.Saturating);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sqshrn_S(ArmEmitterContext context)
|
public static void Sqshrn_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.UseAdvSimd)
|
if (Optimizations.UseAdvSimd)
|
||||||
@ -1603,99 +1593,6 @@ namespace ARMeilleure.Instructions
|
|||||||
Saturating = 1 << 3,
|
Saturating = 1 << 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitShlImmOp(ArmEmitterContext context, bool signedDst, ShlRegFlags flags = ShlRegFlags.None)
|
|
||||||
{
|
|
||||||
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
|
||||||
bool signed = flags.HasFlag(ShlRegFlags.Signed);
|
|
||||||
bool saturating = flags.HasFlag(ShlRegFlags.Saturating);
|
|
||||||
|
|
||||||
OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp;
|
|
||||||
|
|
||||||
Operand res = context.VectorZero();
|
|
||||||
|
|
||||||
int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
|
|
||||||
|
|
||||||
for (int index = 0; index < elems; index++)
|
|
||||||
{
|
|
||||||
Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size, signed);
|
|
||||||
|
|
||||||
Operand e = !saturating
|
|
||||||
? EmitShlImm(context, ne, GetImmShl(op), op.Size)
|
|
||||||
: EmitShlImmSatQ(context, ne, GetImmShl(op), op.Size, signed, signedDst);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, e, index, op.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitShlImm(ArmEmitterContext context, Operand op, int shiftLsB, int size)
|
|
||||||
{
|
|
||||||
int eSize = 8 << size;
|
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
|
||||||
|
|
||||||
Operand res = context.AllocateLocal(OperandType.I64);
|
|
||||||
|
|
||||||
if (shiftLsB >= eSize)
|
|
||||||
{
|
|
||||||
Operand shl = context.ShiftLeft(op, Const(shiftLsB));
|
|
||||||
context.Copy(res, shl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand zeroL = Const(0L);
|
|
||||||
context.Copy(res, zeroL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitShlImmSatQ(ArmEmitterContext context, Operand op, int shiftLsB, int size, bool signedSrc, bool signedDst)
|
|
||||||
{
|
|
||||||
int eSize = 8 << size;
|
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
|
||||||
|
|
||||||
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op);
|
|
||||||
|
|
||||||
if (shiftLsB >= eSize)
|
|
||||||
{
|
|
||||||
context.Copy(res, signedSrc
|
|
||||||
? EmitSignedSignSatQ(context, op, size)
|
|
||||||
: EmitUnsignedSignSatQ(context, op, size));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand shl = context.ShiftLeft(op, Const(shiftLsB));
|
|
||||||
if (eSize == 64)
|
|
||||||
{
|
|
||||||
Operand sarOrShr = signedSrc
|
|
||||||
? context.ShiftRightSI(shl, Const(shiftLsB))
|
|
||||||
: context.ShiftRightUI(shl, Const(shiftLsB));
|
|
||||||
context.Copy(res, shl);
|
|
||||||
context.BranchIf(lblEnd, sarOrShr, op, Comparison.Equal);
|
|
||||||
context.Copy(res, signedSrc
|
|
||||||
? EmitSignedSignSatQ(context, op, size)
|
|
||||||
: EmitUnsignedSignSatQ(context, op, size));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Copy(res, signedSrc
|
|
||||||
? EmitSignedSrcSatQ(context, shl, size, signedDst)
|
|
||||||
: EmitUnsignedSrcSatQ(context, shl, size, signedDst));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.MarkLabel(lblEnd);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitShlRegOp(ArmEmitterContext context, ShlRegFlags flags = ShlRegFlags.None)
|
private static void EmitShlRegOp(ArmEmitterContext context, ShlRegFlags flags = ShlRegFlags.None)
|
||||||
{
|
{
|
||||||
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
||||||
|
@ -384,9 +384,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Sqrshrn_V,
|
Sqrshrn_V,
|
||||||
Sqrshrun_S,
|
Sqrshrun_S,
|
||||||
Sqrshrun_V,
|
Sqrshrun_V,
|
||||||
Sqshl_Si,
|
|
||||||
Sqshl_V,
|
Sqshl_V,
|
||||||
Sqshl_Vi,
|
|
||||||
Sqshrn_S,
|
Sqshrn_S,
|
||||||
Sqshrn_V,
|
Sqshrn_V,
|
||||||
Sqshrun_S,
|
Sqshrun_S,
|
||||||
@ -637,7 +635,6 @@ namespace ARMeilleure.Instructions
|
|||||||
Vorn,
|
Vorn,
|
||||||
Vorr,
|
Vorr,
|
||||||
Vpadd,
|
Vpadd,
|
||||||
Vpadal,
|
|
||||||
Vpaddl,
|
Vpaddl,
|
||||||
Vpmax,
|
Vpmax,
|
||||||
Vpmin,
|
Vpmin,
|
||||||
@ -657,7 +654,6 @@ namespace ARMeilleure.Instructions
|
|||||||
Vrintm,
|
Vrintm,
|
||||||
Vrintn,
|
Vrintn,
|
||||||
Vrintp,
|
Vrintp,
|
||||||
Vrintr,
|
|
||||||
Vrintx,
|
Vrintx,
|
||||||
Vrshr,
|
Vrshr,
|
||||||
Vrshrn,
|
Vrshrn,
|
||||||
|
@ -20,25 +20,6 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
private bool _stillRunning;
|
private bool _stillRunning;
|
||||||
private readonly Thread _updaterThread;
|
private readonly Thread _updaterThread;
|
||||||
|
|
||||||
private float _volume;
|
|
||||||
|
|
||||||
public float Volume
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _volume;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_volume = value;
|
|
||||||
|
|
||||||
foreach (OpenALHardwareDeviceSession session in _sessions.Keys)
|
|
||||||
{
|
|
||||||
session.UpdateMasterVolume(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenALHardwareDeviceDriver()
|
public OpenALHardwareDeviceDriver()
|
||||||
{
|
{
|
||||||
_device = ALC.OpenDevice("");
|
_device = ALC.OpenDevice("");
|
||||||
@ -53,8 +34,6 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
Name = "HardwareDeviceDriver.OpenAL",
|
Name = "HardwareDeviceDriver.OpenAL",
|
||||||
};
|
};
|
||||||
|
|
||||||
_volume = 1f;
|
|
||||||
|
|
||||||
_updaterThread.Start();
|
_updaterThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +52,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
@ -94,7 +73,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
throw new ArgumentException($"{channelCount}");
|
throw new ArgumentException($"{channelCount}");
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
|
|
||||||
_sessions.TryAdd(session, 0);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
|
@ -16,11 +16,10 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
private readonly Queue<OpenALAudioBuffer> _queuedBuffers;
|
private readonly Queue<OpenALAudioBuffer> _queuedBuffers;
|
||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
private float _volume;
|
|
||||||
|
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
|
|
||||||
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_driver = driver;
|
_driver = driver;
|
||||||
_queuedBuffers = new Queue<OpenALAudioBuffer>();
|
_queuedBuffers = new Queue<OpenALAudioBuffer>();
|
||||||
@ -28,7 +27,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
_targetFormat = GetALFormat();
|
_targetFormat = GetALFormat();
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
_playedSampleCount = 0;
|
_playedSampleCount = 0;
|
||||||
SetVolume(1f);
|
SetVolume(requestedVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ALFormat GetALFormat()
|
private ALFormat GetALFormat()
|
||||||
@ -86,22 +85,17 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
|
|
||||||
public override void SetVolume(float volume)
|
public override void SetVolume(float volume)
|
||||||
{
|
{
|
||||||
_volume = volume;
|
lock (_lock)
|
||||||
|
{
|
||||||
UpdateMasterVolume(_driver.Volume);
|
AL.Source(_sourceId, ALSourcef.Gain, volume);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override float GetVolume()
|
public override float GetVolume()
|
||||||
{
|
{
|
||||||
return _volume;
|
AL.GetSource(_sourceId, ALSourcef.Gain, out float volume);
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMasterVolume(float newVolume)
|
return volume;
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
AL.Source(_sourceId, ALSourcef.Gain, newVolume * _volume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
|
@ -20,8 +20,6 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
|
|
||||||
private readonly bool _supportSurroundConfiguration;
|
private readonly bool _supportSurroundConfiguration;
|
||||||
|
|
||||||
public float Volume { get; set; }
|
|
||||||
|
|
||||||
// TODO: Add this to SDL2-CS
|
// TODO: Add this to SDL2-CS
|
||||||
// NOTE: We use a DllImport here because of marshaling issue for spec.
|
// NOTE: We use a DllImport here because of marshaling issue for spec.
|
||||||
#pragma warning disable SYSLIB1054
|
#pragma warning disable SYSLIB1054
|
||||||
@ -50,8 +48,6 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
{
|
{
|
||||||
_supportSurroundConfiguration = spec.channels >= 6;
|
_supportSurroundConfiguration = spec.channels >= 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
Volume = 1f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsSupported => IsSupportedInternal();
|
public static bool IsSupported => IsSupportedInternal();
|
||||||
@ -78,7 +74,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
return _pauseEvent;
|
return _pauseEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
@ -95,7 +91,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
|
|
||||||
_sessions.TryAdd(session, 0);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
private float _volume;
|
private float _volume;
|
||||||
private readonly ushort _nativeSampleFormat;
|
private readonly ushort _nativeSampleFormat;
|
||||||
|
|
||||||
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_driver = driver;
|
_driver = driver;
|
||||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||||
@ -37,7 +37,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
||||||
_sampleCount = uint.MaxValue;
|
_sampleCount = uint.MaxValue;
|
||||||
_started = false;
|
_started = false;
|
||||||
_volume = 1f;
|
_volume = requestedVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
||||||
@ -99,7 +99,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
streamSpan.Clear();
|
streamSpan.Clear();
|
||||||
|
|
||||||
// Apply volume to written data
|
// Apply volume to written data
|
||||||
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_driver.Volume * _volume * SDL_MIX_MAXVOLUME));
|
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong sampleCount = GetSampleCount(samples.Length);
|
ulong sampleCount = GetSampleCount(samples.Length);
|
||||||
|
@ -11,15 +11,15 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dll</TargetPath>
|
<TargetPath>libsoundio.dll</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dylib</TargetPath>
|
<TargetPath>libsoundio.dylib</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.so</TargetPath>
|
<TargetPath>libsoundio.so</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
|
@ -19,25 +19,6 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
|
private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
|
||||||
private int _disposeState;
|
private int _disposeState;
|
||||||
|
|
||||||
private float _volume = 1f;
|
|
||||||
|
|
||||||
public float Volume
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _volume;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_volume = value;
|
|
||||||
|
|
||||||
foreach (SoundIoHardwareDeviceSession session in _sessions.Keys)
|
|
||||||
{
|
|
||||||
session.UpdateMasterVolume(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIoHardwareDeviceDriver()
|
public SoundIoHardwareDeviceDriver()
|
||||||
{
|
{
|
||||||
_audioContext = SoundIoContext.Create();
|
_audioContext = SoundIoContext.Create();
|
||||||
@ -141,7 +122,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
return _pauseEvent;
|
return _pauseEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
@ -153,12 +134,14 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
sampleRate = Constants.TargetSampleRate;
|
sampleRate = Constants.TargetSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volume = Math.Clamp(volume, 0, 1);
|
||||||
|
|
||||||
if (direction != Direction.Output)
|
if (direction != Direction.Output)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
|
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundIoHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
SoundIoHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
|
|
||||||
_sessions.TryAdd(session, 0);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
|
@ -18,18 +18,16 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
private readonly DynamicRingBuffer _ringBuffer;
|
private readonly DynamicRingBuffer _ringBuffer;
|
||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
private readonly ManualResetEvent _updateRequiredEvent;
|
private readonly ManualResetEvent _updateRequiredEvent;
|
||||||
private float _volume;
|
|
||||||
private int _disposeState;
|
private int _disposeState;
|
||||||
|
|
||||||
public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_driver = driver;
|
_driver = driver;
|
||||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||||
_queuedBuffers = new ConcurrentQueue<SoundIoAudioBuffer>();
|
_queuedBuffers = new ConcurrentQueue<SoundIoAudioBuffer>();
|
||||||
_ringBuffer = new DynamicRingBuffer();
|
_ringBuffer = new DynamicRingBuffer();
|
||||||
_volume = 1f;
|
|
||||||
|
|
||||||
SetupOutputStream(driver.Volume);
|
SetupOutputStream(requestedVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupOutputStream(float requestedVolume)
|
private void SetupOutputStream(float requestedVolume)
|
||||||
@ -49,7 +47,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
|
|
||||||
public override float GetVolume()
|
public override float GetVolume()
|
||||||
{
|
{
|
||||||
return _volume;
|
return _outputStream.Volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrepareToClose() { }
|
public override void PrepareToClose() { }
|
||||||
@ -65,14 +63,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
|
|
||||||
public override void SetVolume(float volume)
|
public override void SetVolume(float volume)
|
||||||
{
|
{
|
||||||
_volume = volume;
|
_outputStream.SetVolume(volume);
|
||||||
|
|
||||||
_outputStream.SetVolume(_driver.Volume * volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMasterVolume(float newVolume)
|
|
||||||
{
|
|
||||||
_outputStream.SetVolume(newVolume * _volume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
|
@ -16,12 +16,6 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
|
|
||||||
public static bool IsSupported => true;
|
public static bool IsSupported => true;
|
||||||
|
|
||||||
public float Volume
|
|
||||||
{
|
|
||||||
get => _realDriver.Volume;
|
|
||||||
set => _realDriver.Volume = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice)
|
public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice)
|
||||||
{
|
{
|
||||||
_realDriver = realDevice;
|
_realDriver = realDevice;
|
||||||
@ -96,7 +90,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
throw new ArgumentException("No valid sample format configuration found!");
|
throw new ArgumentException("No valid sample format configuration found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
@ -108,6 +102,8 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
sampleRate = Constants.TargetSampleRate;
|
sampleRate = Constants.TargetSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volume = Math.Clamp(volume, 0, 1);
|
||||||
|
|
||||||
if (!_realDriver.SupportsDirection(direction))
|
if (!_realDriver.SupportsDirection(direction))
|
||||||
{
|
{
|
||||||
if (direction == Direction.Input)
|
if (direction == Direction.Input)
|
||||||
@ -123,7 +119,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat);
|
SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat);
|
||||||
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
|
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
|
||||||
|
|
||||||
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount);
|
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount, volume);
|
||||||
|
|
||||||
if (hardwareChannelCount == channelCount && hardwareSampleFormat == sampleFormat)
|
if (hardwareChannelCount == channelCount && hardwareSampleFormat == sampleFormat)
|
||||||
{
|
{
|
||||||
|
@ -14,17 +14,13 @@ namespace Ryujinx.Audio.Backends.Dummy
|
|||||||
|
|
||||||
public static bool IsSupported => true;
|
public static bool IsSupported => true;
|
||||||
|
|
||||||
public float Volume { get; set; }
|
|
||||||
|
|
||||||
public DummyHardwareDeviceDriver()
|
public DummyHardwareDeviceDriver()
|
||||||
{
|
{
|
||||||
_updateRequiredEvent = new ManualResetEvent(false);
|
_updateRequiredEvent = new ManualResetEvent(false);
|
||||||
_pauseEvent = new ManualResetEvent(true);
|
_pauseEvent = new ManualResetEvent(true);
|
||||||
|
|
||||||
Volume = 1f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (sampleRate == 0)
|
if (sampleRate == 0)
|
||||||
{
|
{
|
||||||
@ -38,7 +34,7 @@ namespace Ryujinx.Audio.Backends.Dummy
|
|||||||
|
|
||||||
if (direction == Direction.Output)
|
if (direction == Direction.Output)
|
||||||
{
|
{
|
||||||
return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DummyHardwareDeviceSessionInput(this, memoryManager);
|
return new DummyHardwareDeviceSessionInput(this, memoryManager);
|
||||||
|
@ -13,9 +13,9 @@ namespace Ryujinx.Audio.Backends.Dummy
|
|||||||
|
|
||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
|
|
||||||
public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_volume = 1f;
|
_volume = requestedVolume;
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +166,7 @@ namespace Ryujinx.Audio.Input
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filtered">If true, filter disconnected devices</param>
|
/// <param name="filtered">If true, filter disconnected devices</param>
|
||||||
/// <returns>The list of all audio inputs name</returns>
|
/// <returns>The list of all audio inputs name</returns>
|
||||||
|
#pragma warning disable CA1822 // Mark member as static
|
||||||
public string[] ListAudioIns(bool filtered)
|
public string[] ListAudioIns(bool filtered)
|
||||||
{
|
{
|
||||||
if (filtered)
|
if (filtered)
|
||||||
@ -175,6 +176,7 @@ namespace Ryujinx.Audio.Input
|
|||||||
|
|
||||||
return new[] { Constants.DefaultDeviceInputName };
|
return new[] { Constants.DefaultDeviceInputName };
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1822
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open a new <see cref="AudioInputSystem"/>.
|
/// Open a new <see cref="AudioInputSystem"/>.
|
||||||
@ -186,6 +188,8 @@ namespace Ryujinx.Audio.Input
|
|||||||
/// <param name="inputDeviceName">The input device name wanted by the user</param>
|
/// <param name="inputDeviceName">The input device name wanted by the user</param>
|
||||||
/// <param name="sampleFormat">The sample format to use</param>
|
/// <param name="sampleFormat">The sample format to use</param>
|
||||||
/// <param name="parameter">The user configuration</param>
|
/// <param name="parameter">The user configuration</param>
|
||||||
|
/// <param name="appletResourceUserId">The applet resource user id of the application</param>
|
||||||
|
/// <param name="processHandle">The process handle of the application</param>
|
||||||
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
|
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
|
||||||
public ResultCode OpenAudioIn(out string outputDeviceName,
|
public ResultCode OpenAudioIn(out string outputDeviceName,
|
||||||
out AudioOutputConfiguration outputConfiguration,
|
out AudioOutputConfiguration outputConfiguration,
|
||||||
@ -193,7 +197,9 @@ namespace Ryujinx.Audio.Input
|
|||||||
IVirtualMemoryManager memoryManager,
|
IVirtualMemoryManager memoryManager,
|
||||||
string inputDeviceName,
|
string inputDeviceName,
|
||||||
SampleFormat sampleFormat,
|
SampleFormat sampleFormat,
|
||||||
ref AudioInputConfiguration parameter)
|
ref AudioInputConfiguration parameter,
|
||||||
|
ulong appletResourceUserId,
|
||||||
|
uint processHandle)
|
||||||
{
|
{
|
||||||
int sessionId = AcquireSessionId();
|
int sessionId = AcquireSessionId();
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ namespace Ryujinx.Audio.Integration
|
|||||||
|
|
||||||
private readonly byte[] _buffer;
|
private readonly byte[] _buffer;
|
||||||
|
|
||||||
public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate)
|
public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate, float volume)
|
||||||
{
|
{
|
||||||
_session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount);
|
_session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount, volume);
|
||||||
_channelCount = channelCount;
|
_channelCount = channelCount;
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
_currentBufferTag = 0;
|
_currentBufferTag = 0;
|
||||||
|
@ -16,9 +16,7 @@ namespace Ryujinx.Audio.Integration
|
|||||||
Output,
|
Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
float Volume { get; set; }
|
IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume = 1f);
|
||||||
|
|
||||||
IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount);
|
|
||||||
|
|
||||||
ManualResetEvent GetUpdateRequiredEvent();
|
ManualResetEvent GetUpdateRequiredEvent();
|
||||||
ManualResetEvent GetPauseEvent();
|
ManualResetEvent GetPauseEvent();
|
||||||
|
@ -165,10 +165,12 @@ namespace Ryujinx.Audio.Output
|
|||||||
/// Get the list of all audio outputs name.
|
/// Get the list of all audio outputs name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The list of all audio outputs name</returns>
|
/// <returns>The list of all audio outputs name</returns>
|
||||||
|
#pragma warning disable CA1822 // Mark member as static
|
||||||
public string[] ListAudioOuts()
|
public string[] ListAudioOuts()
|
||||||
{
|
{
|
||||||
return new[] { Constants.DefaultDeviceOutputName };
|
return new[] { Constants.DefaultDeviceOutputName };
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1822
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open a new <see cref="AudioOutputSystem"/>.
|
/// Open a new <see cref="AudioOutputSystem"/>.
|
||||||
@ -180,6 +182,9 @@ namespace Ryujinx.Audio.Output
|
|||||||
/// <param name="inputDeviceName">The input device name wanted by the user</param>
|
/// <param name="inputDeviceName">The input device name wanted by the user</param>
|
||||||
/// <param name="sampleFormat">The sample format to use</param>
|
/// <param name="sampleFormat">The sample format to use</param>
|
||||||
/// <param name="parameter">The user configuration</param>
|
/// <param name="parameter">The user configuration</param>
|
||||||
|
/// <param name="appletResourceUserId">The applet resource user id of the application</param>
|
||||||
|
/// <param name="processHandle">The process handle of the application</param>
|
||||||
|
/// <param name="volume">The volume level to request</param>
|
||||||
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
|
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
|
||||||
public ResultCode OpenAudioOut(out string outputDeviceName,
|
public ResultCode OpenAudioOut(out string outputDeviceName,
|
||||||
out AudioOutputConfiguration outputConfiguration,
|
out AudioOutputConfiguration outputConfiguration,
|
||||||
@ -187,13 +192,16 @@ namespace Ryujinx.Audio.Output
|
|||||||
IVirtualMemoryManager memoryManager,
|
IVirtualMemoryManager memoryManager,
|
||||||
string inputDeviceName,
|
string inputDeviceName,
|
||||||
SampleFormat sampleFormat,
|
SampleFormat sampleFormat,
|
||||||
ref AudioInputConfiguration parameter)
|
ref AudioInputConfiguration parameter,
|
||||||
|
ulong appletResourceUserId,
|
||||||
|
uint processHandle,
|
||||||
|
float volume)
|
||||||
{
|
{
|
||||||
int sessionId = AcquireSessionId();
|
int sessionId = AcquireSessionId();
|
||||||
|
|
||||||
_sessionsBufferEvents[sessionId].Clear();
|
_sessionsBufferEvents[sessionId].Clear();
|
||||||
|
|
||||||
IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount);
|
IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount, volume);
|
||||||
|
|
||||||
AudioOutputSystem audioOut = new(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]);
|
AudioOutputSystem audioOut = new(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]);
|
||||||
|
|
||||||
@ -226,6 +234,41 @@ namespace Ryujinx.Audio.Output
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the volume for all output devices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="volume">The volume to set.</param>
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
if (_sessions != null)
|
||||||
|
{
|
||||||
|
foreach (AudioOutputSystem session in _sessions)
|
||||||
|
{
|
||||||
|
session?.SetVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the volume for all output devices.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A float indicating the volume level.</returns>
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
if (_sessions != null)
|
||||||
|
{
|
||||||
|
foreach (AudioOutputSystem session in _sessions)
|
||||||
|
{
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
return session.GetVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
@ -45,6 +45,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
_event = new ManualResetEvent(false);
|
_event = new ManualResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable IDE0051 // Remove unused private member
|
||||||
private static uint GetHardwareChannelCount(IHardwareDeviceDriver deviceDriver)
|
private static uint GetHardwareChannelCount(IHardwareDeviceDriver deviceDriver)
|
||||||
{
|
{
|
||||||
// Get the real device driver (In case the compat layer is on top of it).
|
// Get the real device driver (In case the compat layer is on top of it).
|
||||||
@ -58,8 +59,9 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
// NOTE: We default to stereo as this will get downmixed to mono by the compat layer if it's not compatible.
|
// NOTE: We default to stereo as this will get downmixed to mono by the compat layer if it's not compatible.
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
#pragma warning restore IDE0051
|
||||||
|
|
||||||
public void Start(IHardwareDeviceDriver deviceDriver)
|
public void Start(IHardwareDeviceDriver deviceDriver, float volume)
|
||||||
{
|
{
|
||||||
OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax];
|
OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax];
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
for (int i = 0; i < OutputDevices.Length; i++)
|
for (int i = 0; i < OutputDevices.Length; i++)
|
||||||
{
|
{
|
||||||
// TODO: Don't hardcode sample rate.
|
// TODO: Don't hardcode sample rate.
|
||||||
OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate);
|
OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
_mailbox = new Mailbox<MailboxMessage>();
|
_mailbox = new Mailbox<MailboxMessage>();
|
||||||
@ -229,6 +231,33 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
_mailbox.SendResponse(MailboxMessage.Stop);
|
_mailbox.SendResponse(MailboxMessage.Stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
if (OutputDevices != null)
|
||||||
|
{
|
||||||
|
foreach (IHardwareDevice outputDevice in OutputDevices)
|
||||||
|
{
|
||||||
|
if (outputDevice != null)
|
||||||
|
{
|
||||||
|
return outputDevice.GetVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
if (OutputDevices != null)
|
||||||
|
{
|
||||||
|
foreach (IHardwareDevice outputDevice in OutputDevices)
|
||||||
|
{
|
||||||
|
outputDevice?.SetVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
@ -240,7 +269,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
_event.Dispose();
|
_event.Dispose();
|
||||||
_mailbox?.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,12 +177,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start the <see cref="AudioProcessor"/> and worker thread.
|
/// Start the <see cref="AudioProcessor"/> and worker thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void StartLocked()
|
private void StartLocked(float volume)
|
||||||
{
|
{
|
||||||
_isRunning = true;
|
_isRunning = true;
|
||||||
|
|
||||||
// TODO: virtual device mapping (IAudioDevice)
|
// TODO: virtual device mapping (IAudioDevice)
|
||||||
Processor.Start(_deviceDriver);
|
Processor.Start(_deviceDriver, volume);
|
||||||
|
|
||||||
_workerThread = new Thread(SendCommands)
|
_workerThread = new Thread(SendCommands)
|
||||||
{
|
{
|
||||||
@ -254,7 +254,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// Register a new <see cref="AudioRenderSystem"/>.
|
/// Register a new <see cref="AudioRenderSystem"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
|
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
|
||||||
private void Register(AudioRenderSystem renderer)
|
private void Register(AudioRenderSystem renderer, float volume)
|
||||||
{
|
{
|
||||||
lock (_sessionLock)
|
lock (_sessionLock)
|
||||||
{
|
{
|
||||||
@ -265,7 +265,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
if (!_isRunning)
|
if (!_isRunning)
|
||||||
{
|
{
|
||||||
StartLocked();
|
StartLocked(volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,7 +312,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
ulong appletResourceUserId,
|
ulong appletResourceUserId,
|
||||||
ulong workBufferAddress,
|
ulong workBufferAddress,
|
||||||
ulong workBufferSize,
|
ulong workBufferSize,
|
||||||
uint processHandle)
|
uint processHandle,
|
||||||
|
float volume)
|
||||||
{
|
{
|
||||||
int sessionId = AcquireSessionId();
|
int sessionId = AcquireSessionId();
|
||||||
|
|
||||||
@ -337,7 +338,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
renderer = audioRenderer;
|
renderer = audioRenderer;
|
||||||
|
|
||||||
Register(renderer);
|
Register(renderer, volume);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -349,6 +350,21 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
if (Processor != null)
|
||||||
|
{
|
||||||
|
return Processor.GetVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
Processor?.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
@ -8,10 +8,11 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.UI.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Ava
|
namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
@ -42,9 +43,9 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
ApplyConfiguredTheme();
|
ApplyConfiguredTheme();
|
||||||
|
|
||||||
ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event;
|
ConfigurationState.Instance.Ui.BaseStyle.Event += ThemeChanged_Event;
|
||||||
ConfigurationState.Instance.UI.CustomThemePath.Event += ThemeChanged_Event;
|
ConfigurationState.Instance.Ui.CustomThemePath.Event += ThemeChanged_Event;
|
||||||
ConfigurationState.Instance.UI.EnableCustomTheme.Event += CustomThemeChanged_Event;
|
ConfigurationState.Instance.Ui.EnableCustomTheme.Event += CustomThemeChanged_Event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,13 +89,15 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
string baseStyle = ConfigurationState.Instance.Ui.BaseStyle;
|
||||||
|
string themePath = ConfigurationState.Instance.Ui.CustomThemePath;
|
||||||
|
bool enableCustomTheme = ConfigurationState.Instance.Ui.EnableCustomTheme;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(baseStyle))
|
if (string.IsNullOrWhiteSpace(baseStyle))
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.UI.BaseStyle.Value = "Dark";
|
ConfigurationState.Instance.Ui.BaseStyle.Value = "Dark";
|
||||||
|
|
||||||
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
baseStyle = ConfigurationState.Instance.Ui.BaseStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestedThemeVariant = baseStyle switch
|
RequestedThemeVariant = baseStyle switch
|
||||||
@ -103,6 +106,24 @@ namespace Ryujinx.Ava
|
|||||||
"Dark" => ThemeVariant.Dark,
|
"Dark" => ThemeVariant.Dark,
|
||||||
_ => ThemeVariant.Default,
|
_ => ThemeVariant.Default,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (enableCustomTheme)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(themePath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var themeContent = File.ReadAllText(themePath);
|
||||||
|
var customStyle = AvaloniaRuntimeXamlLoader.Parse<IStyle>(themeContent);
|
||||||
|
|
||||||
|
Styles.Add(customStyle);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to Apply Custom Theme. Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
@ -34,10 +34,10 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.UI.Common;
|
using Ryujinx.Ui.Common;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.UI.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Formats.Png;
|
using SixLabors.ImageSharp.Formats.Png;
|
||||||
@ -978,7 +978,7 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||||
LocaleManager.Instance[LocaleKeys.Game] + $": {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} %",
|
$"FIFO: {Device.Statistics.GetFifoPercent():00.00} %",
|
||||||
$"GPU: {_renderer.GetHardwareInfo().GpuDriver}"));
|
$"GPU: {_renderer.GetHardwareInfo().GpuVendor}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ShowExitPrompt()
|
public async Task ShowExitPrompt()
|
||||||
@ -1070,7 +1070,7 @@ namespace Ryujinx.Ava
|
|||||||
case KeyboardHotkeyState.Screenshot:
|
case KeyboardHotkeyState.Screenshot:
|
||||||
ScreenshotRequested = true;
|
ScreenshotRequested = true;
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.ShowUI:
|
case KeyboardHotkeyState.ShowUi:
|
||||||
_viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar;
|
_viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar;
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.Pause:
|
case KeyboardHotkeyState.Pause:
|
||||||
@ -1160,9 +1160,9 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
state = KeyboardHotkeyState.Screenshot;
|
state = KeyboardHotkeyState.Screenshot;
|
||||||
}
|
}
|
||||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI))
|
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi))
|
||||||
{
|
{
|
||||||
state = KeyboardHotkeyState.ShowUI;
|
state = KeyboardHotkeyState.ShowUi;
|
||||||
}
|
}
|
||||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause))
|
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause))
|
||||||
{
|
{
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
@ -54,6 +54,8 @@
|
|||||||
"GameListContextMenuManageTitleUpdatesToolTip": "Opens the Title Update management window",
|
"GameListContextMenuManageTitleUpdatesToolTip": "Opens the Title Update management window",
|
||||||
"GameListContextMenuManageDlc": "Manage DLC",
|
"GameListContextMenuManageDlc": "Manage DLC",
|
||||||
"GameListContextMenuManageDlcToolTip": "Opens the DLC management window",
|
"GameListContextMenuManageDlcToolTip": "Opens the DLC management window",
|
||||||
|
"GameListContextMenuOpenModsDirectory": "Open Mods Directory",
|
||||||
|
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
|
||||||
"GameListContextMenuCacheManagement": "Cache Management",
|
"GameListContextMenuCacheManagement": "Cache Management",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Queue PPTC Rebuild",
|
"GameListContextMenuCacheManagementPurgePptc": "Queue PPTC Rebuild",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Trigger PPTC to rebuild at boot time on the next game launch",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Trigger PPTC to rebuild at boot time on the next game launch",
|
||||||
@ -72,11 +74,6 @@
|
|||||||
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
|
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
|
||||||
"GameListContextMenuCreateShortcut": "Create Application Shortcut",
|
"GameListContextMenuCreateShortcut": "Create Application Shortcut",
|
||||||
"GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application",
|
"GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application",
|
||||||
"GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application",
|
|
||||||
"GameListContextMenuOpenModsDirectory": "Open Mods Directory",
|
|
||||||
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
|
|
||||||
"GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory",
|
|
||||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
|
|
||||||
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
||||||
"StatusBarSystemVersion": "System Version: {0}",
|
"StatusBarSystemVersion": "System Version: {0}",
|
||||||
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
||||||
@ -155,7 +152,7 @@
|
|||||||
"SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)",
|
"SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)",
|
||||||
"SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)",
|
"SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)",
|
||||||
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
|
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
|
||||||
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)",
|
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p)",
|
||||||
"SettingsTabGraphicsAspectRatio": "Aspect Ratio:",
|
"SettingsTabGraphicsAspectRatio": "Aspect Ratio:",
|
||||||
"SettingsTabGraphicsAspectRatio4x3": "4:3",
|
"SettingsTabGraphicsAspectRatio4x3": "4:3",
|
||||||
"SettingsTabGraphicsAspectRatio16x9": "16:9",
|
"SettingsTabGraphicsAspectRatio16x9": "16:9",
|
||||||
@ -297,9 +294,13 @@
|
|||||||
"GameListContextMenuRunApplication": "Run Application",
|
"GameListContextMenuRunApplication": "Run Application",
|
||||||
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
||||||
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
||||||
"SettingsTabGeneralTheme": "Theme:",
|
"SettingsTabGeneralTheme": "Theme",
|
||||||
"SettingsTabGeneralThemeDark": "Dark",
|
"SettingsTabGeneralThemeCustomTheme": "Custom Theme Path",
|
||||||
"SettingsTabGeneralThemeLight": "Light",
|
"SettingsTabGeneralThemeBaseStyle": "Base Style",
|
||||||
|
"SettingsTabGeneralThemeBaseStyleDark": "Dark",
|
||||||
|
"SettingsTabGeneralThemeBaseStyleLight": "Light",
|
||||||
|
"SettingsTabGeneralThemeEnableCustomTheme": "Enable Custom Theme",
|
||||||
|
"ButtonBrowse": "Browse",
|
||||||
"ControllerSettingsConfigureGeneral": "Configure",
|
"ControllerSettingsConfigureGeneral": "Configure",
|
||||||
"ControllerSettingsRumble": "Rumble",
|
"ControllerSettingsRumble": "Rumble",
|
||||||
"ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier",
|
"ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier",
|
||||||
@ -333,6 +334,8 @@
|
|||||||
"DialogUpdaterAddingFilesMessage": "Adding New Update...",
|
"DialogUpdaterAddingFilesMessage": "Adding New Update...",
|
||||||
"DialogUpdaterCompleteMessage": "Update Complete!",
|
"DialogUpdaterCompleteMessage": "Update Complete!",
|
||||||
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
|
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
|
||||||
|
"DialogUpdaterArchNotSupportedMessage": "You are not running a supported system architecture!",
|
||||||
|
"DialogUpdaterArchNotSupportedSubMessage": "(Only x64 systems are supported!)",
|
||||||
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
||||||
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
|
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
|
||||||
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",
|
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",
|
||||||
@ -384,10 +387,7 @@
|
|||||||
"DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?",
|
"DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?",
|
||||||
"DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.",
|
"DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.",
|
||||||
"DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?",
|
"DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?",
|
||||||
"DialogLoadFileErrorMessage": "{0}. Errored File: {1}",
|
"DialogLoadNcaErrorMessage": "{0}. Errored File: {1}",
|
||||||
"DialogModAlreadyExistsMessage": "Mod already exists",
|
|
||||||
"DialogModInvalidMessage": "The specified directory does not contain a mod!",
|
|
||||||
"DialogModDeleteNoParentMessage": "Failed to Delete: Could not find the parent directory for mod \"{0}\"!",
|
|
||||||
"DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!",
|
"DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!",
|
||||||
"DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.",
|
"DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.",
|
||||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?",
|
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?",
|
||||||
@ -398,8 +398,6 @@
|
|||||||
"DialogUpdateAddUpdateErrorMessage": "The specified file does not contain an update for the selected title!",
|
"DialogUpdateAddUpdateErrorMessage": "The specified file does not contain an update for the selected title!",
|
||||||
"DialogSettingsBackendThreadingWarningTitle": "Warning - Backend Threading",
|
"DialogSettingsBackendThreadingWarningTitle": "Warning - Backend Threading",
|
||||||
"DialogSettingsBackendThreadingWarningMessage": "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's.",
|
"DialogSettingsBackendThreadingWarningMessage": "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's.",
|
||||||
"DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?",
|
|
||||||
"DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?",
|
|
||||||
"SettingsTabGraphicsFeaturesOptions": "Features",
|
"SettingsTabGraphicsFeaturesOptions": "Features",
|
||||||
"SettingsTabGraphicsBackendMultithreading": "Graphics Backend Multithreading:",
|
"SettingsTabGraphicsBackendMultithreading": "Graphics Backend Multithreading:",
|
||||||
"CommonAuto": "Auto",
|
"CommonAuto": "Auto",
|
||||||
@ -434,7 +432,6 @@
|
|||||||
"DlcManagerRemoveAllButton": "Remove All",
|
"DlcManagerRemoveAllButton": "Remove All",
|
||||||
"DlcManagerEnableAllButton": "Enable All",
|
"DlcManagerEnableAllButton": "Enable All",
|
||||||
"DlcManagerDisableAllButton": "Disable All",
|
"DlcManagerDisableAllButton": "Disable All",
|
||||||
"ModManagerDeleteAllButton": "Delete All",
|
|
||||||
"MenuBarOptionsChangeLanguage": "Change Language",
|
"MenuBarOptionsChangeLanguage": "Change Language",
|
||||||
"MenuBarShowFileTypes": "Show File Types",
|
"MenuBarShowFileTypes": "Show File Types",
|
||||||
"CommonSort": "Sort",
|
"CommonSort": "Sort",
|
||||||
@ -452,13 +449,13 @@
|
|||||||
"CustomThemePathTooltip": "Path to custom GUI theme",
|
"CustomThemePathTooltip": "Path to custom GUI theme",
|
||||||
"CustomThemeBrowseTooltip": "Browse for a custom GUI theme",
|
"CustomThemeBrowseTooltip": "Browse for a custom GUI theme",
|
||||||
"DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.",
|
"DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.",
|
||||||
"DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.",
|
"DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.",
|
||||||
"DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.",
|
"DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.",
|
||||||
"RegionTooltip": "Change System Region",
|
"RegionTooltip": "Change System Region",
|
||||||
"LanguageTooltip": "Change System Language",
|
"LanguageTooltip": "Change System Language",
|
||||||
"TimezoneTooltip": "Change System TimeZone",
|
"TimezoneTooltip": "Change System TimeZone",
|
||||||
"TimeTooltip": "Change System Time",
|
"TimeTooltip": "Change System Time",
|
||||||
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.",
|
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference. We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.",
|
||||||
"PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.",
|
"PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.",
|
||||||
"FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.",
|
"FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.",
|
||||||
"AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.",
|
"AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.",
|
||||||
@ -472,10 +469,10 @@
|
|||||||
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||||
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||||
"ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.",
|
"ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.",
|
||||||
"ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.",
|
"ResolutionScaleTooltip": "Resolution Scale applied to applicable render targets",
|
||||||
"ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.",
|
"ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.",
|
||||||
"AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.",
|
"AnisotropyTooltip": "Level of Anisotropic Filtering (set to Auto to use the value requested by the game)",
|
||||||
"AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
|
"AspectRatioTooltip": "Aspect Ratio applied to the renderer window.",
|
||||||
"ShaderDumpPathTooltip": "Graphics Shaders Dump Path",
|
"ShaderDumpPathTooltip": "Graphics Shaders Dump Path",
|
||||||
"FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.",
|
"FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.",
|
||||||
"StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.",
|
"StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.",
|
||||||
@ -509,8 +506,6 @@
|
|||||||
"EnableInternetAccessTooltip": "Allows the emulated application to connect to the Internet.\n\nGames with a LAN mode can connect to each other when this is enabled and the systems are connected to the same access point. This includes real consoles as well.\n\nDoes NOT allow connecting to Nintendo servers. May cause crashing in certain games that try to connect to the Internet.\n\nLeave OFF if unsure.",
|
"EnableInternetAccessTooltip": "Allows the emulated application to connect to the Internet.\n\nGames with a LAN mode can connect to each other when this is enabled and the systems are connected to the same access point. This includes real consoles as well.\n\nDoes NOT allow connecting to Nintendo servers. May cause crashing in certain games that try to connect to the Internet.\n\nLeave OFF if unsure.",
|
||||||
"GameListContextMenuManageCheatToolTip": "Manage Cheats",
|
"GameListContextMenuManageCheatToolTip": "Manage Cheats",
|
||||||
"GameListContextMenuManageCheat": "Manage Cheats",
|
"GameListContextMenuManageCheat": "Manage Cheats",
|
||||||
"GameListContextMenuManageModToolTip": "Manage Mods",
|
|
||||||
"GameListContextMenuManageMod": "Manage Mods",
|
|
||||||
"ControllerSettingsStickRange": "Range:",
|
"ControllerSettingsStickRange": "Range:",
|
||||||
"DialogStopEmulationTitle": "Ryujinx - Stop Emulation",
|
"DialogStopEmulationTitle": "Ryujinx - Stop Emulation",
|
||||||
"DialogStopEmulationMessage": "Are you sure you want to stop emulation?",
|
"DialogStopEmulationMessage": "Are you sure you want to stop emulation?",
|
||||||
@ -522,6 +517,8 @@
|
|||||||
"SettingsTabCpuMemory": "CPU Mode",
|
"SettingsTabCpuMemory": "CPU Mode",
|
||||||
"DialogUpdaterFlatpakNotSupportedMessage": "Please update Ryujinx via FlatHub.",
|
"DialogUpdaterFlatpakNotSupportedMessage": "Please update Ryujinx via FlatHub.",
|
||||||
"UpdaterDisabledWarningTitle": "Updater Disabled!",
|
"UpdaterDisabledWarningTitle": "Updater Disabled!",
|
||||||
|
"GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory",
|
||||||
|
"GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
|
||||||
"ControllerSettingsRotate90": "Rotate 90° Clockwise",
|
"ControllerSettingsRotate90": "Rotate 90° Clockwise",
|
||||||
"IconSize": "Icon Size",
|
"IconSize": "Icon Size",
|
||||||
"IconSizeTooltip": "Change the size of game icons",
|
"IconSizeTooltip": "Change the size of game icons",
|
||||||
@ -593,7 +590,6 @@
|
|||||||
"Writable": "Writable",
|
"Writable": "Writable",
|
||||||
"SelectDlcDialogTitle": "Select DLC files",
|
"SelectDlcDialogTitle": "Select DLC files",
|
||||||
"SelectUpdateDialogTitle": "Select update files",
|
"SelectUpdateDialogTitle": "Select update files",
|
||||||
"SelectModDialogTitle": "Select mod directory",
|
|
||||||
"UserProfileWindowTitle": "User Profiles Manager",
|
"UserProfileWindowTitle": "User Profiles Manager",
|
||||||
"CheatWindowTitle": "Cheats Manager",
|
"CheatWindowTitle": "Cheats Manager",
|
||||||
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
|
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
|
||||||
@ -601,7 +597,6 @@
|
|||||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||||
"BuildId": "BuildId:",
|
"BuildId": "BuildId:",
|
||||||
"DlcWindowHeading": "{0} Downloadable Content(s)",
|
"DlcWindowHeading": "{0} Downloadable Content(s)",
|
||||||
"ModWindowHeading": "{0} Mod(s)",
|
|
||||||
"UserProfilesEditProfile": "Edit Selected",
|
"UserProfilesEditProfile": "Edit Selected",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
@ -616,9 +611,9 @@
|
|||||||
"UserProfilesName": "Name:",
|
"UserProfilesName": "Name:",
|
||||||
"UserProfilesUserId": "User ID:",
|
"UserProfilesUserId": "User ID:",
|
||||||
"SettingsTabGraphicsBackend": "Graphics Backend",
|
"SettingsTabGraphicsBackend": "Graphics Backend",
|
||||||
"SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.",
|
"SettingsTabGraphicsBackendTooltip": "Graphics Backend to use",
|
||||||
"SettingsEnableTextureRecompression": "Enable Texture Recompression",
|
"SettingsEnableTextureRecompression": "Enable Texture Recompression",
|
||||||
"SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.",
|
"SettingsEnableTextureRecompressionTooltip": "Compresses certain textures in order to reduce VRAM usage.\n\nRecommended for use with GPUs that have less than 4GiB VRAM.\n\nLeave OFF if unsure.",
|
||||||
"SettingsTabGraphicsPreferredGpu": "Preferred GPU",
|
"SettingsTabGraphicsPreferredGpu": "Preferred GPU",
|
||||||
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
|
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
|
||||||
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
|
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
|
||||||
@ -644,12 +639,12 @@
|
|||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
|
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
|
||||||
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
||||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
|
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
||||||
"GraphicsAALabel": "Anti-Aliasing:",
|
"GraphicsAALabel": "Anti-Aliasing:",
|
||||||
"GraphicsScalingFilterLabel": "Scaling Filter:",
|
"GraphicsScalingFilterLabel": "Scaling Filter:",
|
||||||
"GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
"GraphicsScalingFilterTooltip": "Enables Framebuffer Scaling",
|
||||||
"GraphicsScalingFilterLevelLabel": "Level",
|
"GraphicsScalingFilterLevelLabel": "Level",
|
||||||
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
|
"GraphicsScalingFilterLevelTooltip": "Set Scaling Filter Level",
|
||||||
"SmaaLow": "SMAA Low",
|
"SmaaLow": "SMAA Low",
|
||||||
"SmaaMedium": "SMAA Medium",
|
"SmaaMedium": "SMAA Medium",
|
||||||
"SmaaHigh": "SMAA High",
|
"SmaaHigh": "SMAA High",
|
||||||
@ -657,12 +652,12 @@
|
|||||||
"UserEditorTitle": "Edit User",
|
"UserEditorTitle": "Edit User",
|
||||||
"UserEditorTitleCreate": "Create User",
|
"UserEditorTitleCreate": "Create User",
|
||||||
"SettingsTabNetworkInterface": "Network Interface:",
|
"SettingsTabNetworkInterface": "Network Interface:",
|
||||||
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.",
|
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features",
|
||||||
"NetworkInterfaceDefault": "Default",
|
"NetworkInterfaceDefault": "Default",
|
||||||
"PackagingShaders": "Packaging Shaders",
|
"PackagingShaders": "Packaging Shaders",
|
||||||
"AboutChangelogButton": "View Changelog on GitHub",
|
"AboutChangelogButton": "View Changelog on GitHub",
|
||||||
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.",
|
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.",
|
||||||
"SettingsTabNetworkMultiplayer": "Multiplayer",
|
"SettingsTabNetworkMultiplayer": "Multiplayer",
|
||||||
"MultiplayerMode": "Mode:",
|
"MultiplayerMode": "Mode:",
|
||||||
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure."
|
"MultiplayerModeTooltip": "Change multiplayer mode"
|
||||||
}
|
}
|
@ -18,8 +18,8 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.UI.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
@ -5,7 +5,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
None,
|
None,
|
||||||
ToggleVSync,
|
ToggleVSync,
|
||||||
Screenshot,
|
Screenshot,
|
||||||
ShowUI,
|
ShowUi,
|
||||||
Pause,
|
Pause,
|
||||||
ToggleMute,
|
ToggleMute,
|
||||||
ResScaleUp,
|
ResScaleUp,
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -38,9 +38,9 @@ namespace Ryujinx.Ava.Common.Locale
|
|||||||
// If the view is loaded with the UI Previewer detached, then override it with the saved one or default.
|
// If the view is loaded with the UI Previewer detached, then override it with the saved one or default.
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(ConfigurationState.Instance.UI.LanguageCode.Value))
|
if (!string.IsNullOrEmpty(ConfigurationState.Instance.Ui.LanguageCode.Value))
|
||||||
{
|
{
|
||||||
localeLanguageCode = ConfigurationState.Instance.UI.LanguageCode.Value;
|
localeLanguageCode = ConfigurationState.Instance.Ui.LanguageCode.Value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -143,7 +143,7 @@ namespace Ryujinx.Ava.Common.Locale
|
|||||||
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
|
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
|
||||||
{
|
{
|
||||||
var localeStrings = new Dictionary<LocaleKeys, string>();
|
var localeStrings = new Dictionary<LocaleKeys, string>();
|
||||||
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx/Assets/Locales/{languageCode}.json");
|
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
||||||
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||||
|
|
||||||
foreach (var item in strings)
|
foreach (var item in strings)
|
777
src/Ryujinx.Ava/Modules/Updater/Updater.cs
Normal file
@ -0,0 +1,777 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using ICSharpCode.SharpZipLib.GZip;
|
||||||
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
|
using Ryujinx.Ava;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.Ui.Common.Helper;
|
||||||
|
using Ryujinx.Ui.Common.Models.Github;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.Modules
|
||||||
|
{
|
||||||
|
internal static class Updater
|
||||||
|
{
|
||||||
|
private const string GitHubApiUrl = "https://api.github.com";
|
||||||
|
private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
|
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||||
|
private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
|
||||||
|
private const int ConnectionCount = 4;
|
||||||
|
|
||||||
|
private static string _buildVer;
|
||||||
|
private static string _platformExt;
|
||||||
|
private static string _buildUrl;
|
||||||
|
private static long _buildSize;
|
||||||
|
private static bool _updateSuccessful;
|
||||||
|
private static bool _running;
|
||||||
|
|
||||||
|
private static readonly string[] _windowsDependencyDirs = Array.Empty<string>();
|
||||||
|
|
||||||
|
public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate)
|
||||||
|
{
|
||||||
|
if (_running)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_running = true;
|
||||||
|
|
||||||
|
// Detect current platform
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
_platformExt = "macos_universal.app.tar.gz";
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
_platformExt = "win_x64.zip";
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsLinux())
|
||||||
|
{
|
||||||
|
_platformExt = "linux_x64.tar.gz";
|
||||||
|
}
|
||||||
|
|
||||||
|
Version newVersion;
|
||||||
|
Version currentVersion;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentVersion = Version.Parse(Program.Version);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||||
|
|
||||||
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get latest version number from GitHub API
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using HttpClient jsonClient = ConstructHttpClient();
|
||||||
|
|
||||||
|
string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||||
|
string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl);
|
||||||
|
var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse);
|
||||||
|
_buildVer = fetched.Name;
|
||||||
|
|
||||||
|
foreach (var asset in fetched.Assets)
|
||||||
|
{
|
||||||
|
if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt))
|
||||||
|
{
|
||||||
|
_buildUrl = asset.BrowserDownloadUrl;
|
||||||
|
|
||||||
|
if (asset.State != "uploaded")
|
||||||
|
{
|
||||||
|
if (showVersionUpToDate)
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If build not done, assume no new update are available.
|
||||||
|
if (_buildUrl is null)
|
||||||
|
{
|
||||||
|
if (showVersionUpToDate)
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||||
|
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newVersion = Version.Parse(_buildVer);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
||||||
|
|
||||||
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newVersion <= currentVersion)
|
||||||
|
{
|
||||||
|
if (showVersionUpToDate)
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch build size information to learn chunk sizes.
|
||||||
|
using HttpClient buildSizeClient = ConstructHttpClient();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
||||||
|
|
||||||
|
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
|
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
|
||||||
|
|
||||||
|
_buildSize = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
// Show a message asking the user if they want to update
|
||||||
|
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
|
||||||
|
$"{Program.Version} -> {newVersion}");
|
||||||
|
|
||||||
|
if (shouldUpdate)
|
||||||
|
{
|
||||||
|
await UpdateRyujinx(mainWindow, _buildUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_running = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpClient ConstructHttpClient()
|
||||||
|
{
|
||||||
|
HttpClient result = new();
|
||||||
|
|
||||||
|
// Required by GitHub to interact with APIs.
|
||||||
|
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task UpdateRyujinx(Window parent, string downloadUrl)
|
||||||
|
{
|
||||||
|
_updateSuccessful = false;
|
||||||
|
|
||||||
|
// Empty update dir, although it shouldn't ever have anything inside it
|
||||||
|
if (Directory.Exists(_updateDir))
|
||||||
|
{
|
||||||
|
Directory.Delete(_updateDir, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(_updateDir);
|
||||||
|
|
||||||
|
string updateFile = Path.Combine(_updateDir, "update.bin");
|
||||||
|
|
||||||
|
TaskDialog taskDialog = new()
|
||||||
|
{
|
||||||
|
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||||
|
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
|
||||||
|
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
|
||||||
|
ShowProgressBar = true,
|
||||||
|
XamlRoot = parent,
|
||||||
|
};
|
||||||
|
|
||||||
|
taskDialog.Opened += (s, e) =>
|
||||||
|
{
|
||||||
|
if (_buildSize >= 0)
|
||||||
|
{
|
||||||
|
DoUpdateWithMultipleThreads(taskDialog, downloadUrl, updateFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await taskDialog.ShowAsync(true);
|
||||||
|
|
||||||
|
if (_updateSuccessful)
|
||||||
|
{
|
||||||
|
bool shouldRestart = true;
|
||||||
|
|
||||||
|
if (!OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRestart)
|
||||||
|
{
|
||||||
|
List<string> arguments = CommandLineState.Arguments.ToList();
|
||||||
|
string executableDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
|
||||||
|
// On macOS we perform the update at relaunch.
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
|
||||||
|
string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app");
|
||||||
|
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
|
||||||
|
string currentPid = Environment.ProcessId.ToString();
|
||||||
|
|
||||||
|
arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid });
|
||||||
|
Process.Start("/bin/bash", arguments);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find the process name.
|
||||||
|
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||||
|
|
||||||
|
// Some operating systems can see the renamed executable, so strip off the .ryuold if found.
|
||||||
|
if (ryuName.EndsWith(".ryuold"))
|
||||||
|
{
|
||||||
|
ryuName = ryuName[..^7];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback if the executable could not be found.
|
||||||
|
if (!Path.Exists(Path.Combine(executableDirectory, ryuName)))
|
||||||
|
{
|
||||||
|
ryuName = OperatingSystem.IsWindows() ? "Ryujinx.Ava.exe" : "Ryujinx.Ava";
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessStartInfo processStart = new(ryuName)
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
WorkingDirectory = executableDirectory,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (string argument in CommandLineState.Arguments)
|
||||||
|
{
|
||||||
|
processStart.ArgumentList.Add(argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
Process.Start(processStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||||
|
{
|
||||||
|
// Multi-Threaded Updater
|
||||||
|
long chunkSize = _buildSize / ConnectionCount;
|
||||||
|
long remainderChunk = _buildSize % ConnectionCount;
|
||||||
|
|
||||||
|
int completedRequests = 0;
|
||||||
|
int totalProgressPercentage = 0;
|
||||||
|
int[] progressPercentage = new int[ConnectionCount];
|
||||||
|
|
||||||
|
List<byte[]> list = new(ConnectionCount);
|
||||||
|
List<WebClient> webClients = new(ConnectionCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < ConnectionCount; i++)
|
||||||
|
{
|
||||||
|
list.Add(Array.Empty<byte>());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ConnectionCount; i++)
|
||||||
|
{
|
||||||
|
#pragma warning disable SYSLIB0014
|
||||||
|
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
|
||||||
|
using WebClient client = new();
|
||||||
|
#pragma warning restore SYSLIB0014
|
||||||
|
|
||||||
|
webClients.Add(client);
|
||||||
|
|
||||||
|
if (i == ConnectionCount - 1)
|
||||||
|
{
|
||||||
|
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}");
|
||||||
|
}
|
||||||
|
|
||||||
|
client.DownloadProgressChanged += (_, args) =>
|
||||||
|
{
|
||||||
|
int index = (int)args.UserState;
|
||||||
|
|
||||||
|
Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]);
|
||||||
|
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
|
||||||
|
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
|
||||||
|
|
||||||
|
taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal);
|
||||||
|
};
|
||||||
|
|
||||||
|
client.DownloadDataCompleted += (_, args) =>
|
||||||
|
{
|
||||||
|
int index = (int)args.UserState;
|
||||||
|
|
||||||
|
if (args.Cancelled)
|
||||||
|
{
|
||||||
|
webClients[index].Dispose();
|
||||||
|
|
||||||
|
taskDialog.Hide();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list[index] = args.Result;
|
||||||
|
Interlocked.Increment(ref completedRequests);
|
||||||
|
|
||||||
|
if (Equals(completedRequests, ConnectionCount))
|
||||||
|
{
|
||||||
|
byte[] mergedFileBytes = new byte[_buildSize];
|
||||||
|
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++)
|
||||||
|
{
|
||||||
|
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
|
||||||
|
destinationOffset += list[connectionIndex].Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllBytes(updateFile, mergedFileBytes);
|
||||||
|
|
||||||
|
// On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution.
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
using Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile });
|
||||||
|
|
||||||
|
xattrProcess.WaitForExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InstallUpdate(taskDialog, updateFile);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, e.Message);
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
||||||
|
|
||||||
|
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.DownloadDataAsync(new Uri(downloadUrl), i);
|
||||||
|
}
|
||||||
|
catch (WebException ex)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
||||||
|
|
||||||
|
foreach (WebClient webClient in webClients)
|
||||||
|
{
|
||||||
|
webClient.CancelAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DoUpdateWithSingleThreadWorker(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||||
|
{
|
||||||
|
using HttpClient client = new();
|
||||||
|
// We do not want to timeout while downloading
|
||||||
|
client.Timeout = TimeSpan.FromDays(1);
|
||||||
|
|
||||||
|
using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result;
|
||||||
|
using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result;
|
||||||
|
using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
|
||||||
|
|
||||||
|
long totalBytes = response.Content.Headers.ContentLength.Value;
|
||||||
|
long byteWritten = 0;
|
||||||
|
|
||||||
|
byte[] buffer = new byte[32 * 1024];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int readSize = remoteFileStream.Read(buffer);
|
||||||
|
|
||||||
|
if (readSize == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
byteWritten += readSize;
|
||||||
|
|
||||||
|
taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
|
updateFileStream.Write(buffer, 0, readSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallUpdate(taskDialog, updateFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static double GetPercentage(double value, double max)
|
||||||
|
{
|
||||||
|
return max == 0 ? 0 : value / max * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DoUpdateWithSingleThread(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||||
|
{
|
||||||
|
Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile))
|
||||||
|
{
|
||||||
|
Name = "Updater.SingleThreadWorker",
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||||
|
{
|
||||||
|
using Stream inStream = File.OpenRead(archivePath);
|
||||||
|
using GZipInputStream gzipStream = new(inStream);
|
||||||
|
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
||||||
|
|
||||||
|
TarEntry tarEntry;
|
||||||
|
|
||||||
|
while ((tarEntry = tarStream.GetNextEntry()) is not null)
|
||||||
|
{
|
||||||
|
if (tarEntry.IsDirectory)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string outPath = Path.Combine(outputDirectoryPath, tarEntry.Name);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||||
|
|
||||||
|
using FileStream outStream = File.OpenWrite(outPath);
|
||||||
|
tarStream.CopyEntryContents(outStream);
|
||||||
|
|
||||||
|
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
|
||||||
|
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
if (tarEntry is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||||
|
{
|
||||||
|
using Stream inStream = File.OpenRead(archivePath);
|
||||||
|
using ZipFile zipFile = new(inStream);
|
||||||
|
|
||||||
|
double count = 0;
|
||||||
|
foreach (ZipEntry zipEntry in zipFile)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if (zipEntry.IsDirectory)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||||
|
|
||||||
|
using Stream zipStream = zipFile.GetInputStream(zipEntry);
|
||||||
|
using FileStream outStream = File.OpenWrite(outPath);
|
||||||
|
|
||||||
|
zipStream.CopyTo(outStream);
|
||||||
|
|
||||||
|
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||||
|
{
|
||||||
|
// Extract Update
|
||||||
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
||||||
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete downloaded zip
|
||||||
|
File.Delete(updateFile);
|
||||||
|
|
||||||
|
List<string> allFiles = EnumerateFilesToDelete().ToList();
|
||||||
|
|
||||||
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
|
||||||
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
|
// NOTE: On macOS, replacement is delayed to the restart phase.
|
||||||
|
if (!OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
// Replace old files
|
||||||
|
double count = 0;
|
||||||
|
foreach (string file in allFiles)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Move(file, file + ".ryuold");
|
||||||
|
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
||||||
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
});
|
||||||
|
|
||||||
|
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
|
||||||
|
|
||||||
|
Directory.Delete(_updateDir, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateSuccessful = true;
|
||||||
|
|
||||||
|
taskDialog.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanUpdate(bool showWarnings)
|
||||||
|
{
|
||||||
|
#if !DISABLE_UPDATER
|
||||||
|
if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
if (showWarnings)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||||
|
{
|
||||||
|
if (showWarnings)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid())
|
||||||
|
{
|
||||||
|
if (showWarnings)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
if (showWarnings)
|
||||||
|
{
|
||||||
|
if (ReleaseInformation.IsFlatHubBuild())
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This method should always reflect the latest build layout.
|
||||||
|
private static IEnumerable<string> EnumerateFilesToDelete()
|
||||||
|
{
|
||||||
|
var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir.
|
||||||
|
|
||||||
|
// Determine and exclude user files only when the updater is running, not when cleaning old files
|
||||||
|
if (_running && !OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
// Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list.
|
||||||
|
var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
|
||||||
|
var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
|
||||||
|
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename));
|
||||||
|
|
||||||
|
// Remove user files from the paths in files.
|
||||||
|
files = files.Except(userFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
foreach (string dir in _windowsDependencyDirs)
|
||||||
|
{
|
||||||
|
string dirPath = Path.Combine(_homeDir, dir);
|
||||||
|
if (Directory.Exists(dirPath))
|
||||||
|
{
|
||||||
|
files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
|
||||||
|
{
|
||||||
|
int total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length;
|
||||||
|
foreach (string directory in Directory.GetDirectories(root))
|
||||||
|
{
|
||||||
|
string dirName = Path.GetFileName(directory);
|
||||||
|
|
||||||
|
if (!Directory.Exists(Path.Combine(dest, dirName)))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(dest, dirName));
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveAllFilesOver(directory, Path.Combine(dest, dirName), taskDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
double count = 0;
|
||||||
|
foreach (string file in Directory.GetFiles(root))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
|
||||||
|
File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true);
|
||||||
|
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
taskDialog.SetProgressBarState(GetPercentage(count, total), TaskDialogProgressState.Normal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CleanupUpdate()
|
||||||
|
{
|
||||||
|
foreach (string file in Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
236
src/Ryujinx.Ava/Program.cs
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Ava.UI.Windows;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.GraphicsDriver;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.SystemInterop;
|
||||||
|
using Ryujinx.Modules;
|
||||||
|
using Ryujinx.SDL2.Common;
|
||||||
|
using Ryujinx.Ui.Common;
|
||||||
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
|
using Ryujinx.Ui.Common.Helper;
|
||||||
|
using Ryujinx.Ui.Common.SystemInfo;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava
|
||||||
|
{
|
||||||
|
internal partial class Program
|
||||||
|
{
|
||||||
|
public static double WindowScaleFactor { get; set; }
|
||||||
|
public static double DesktopScaleFactor { get; set; } = 1.0;
|
||||||
|
public static string Version { get; private set; }
|
||||||
|
public static string ConfigurationPath { get; private set; }
|
||||||
|
public static bool PreviewerDetached { get; private set; }
|
||||||
|
|
||||||
|
[LibraryImport("user32.dll", SetLastError = true)]
|
||||||
|
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
|
||||||
|
|
||||||
|
private const uint MbIconwarning = 0x30;
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Version = ReleaseInformation.GetVersion();
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||||
|
{
|
||||||
|
_ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviewerDetached = true;
|
||||||
|
|
||||||
|
Initialize(args);
|
||||||
|
|
||||||
|
LoggerAdapter.Register();
|
||||||
|
|
||||||
|
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
|
{
|
||||||
|
return AppBuilder.Configure<App>()
|
||||||
|
.UsePlatformDetect()
|
||||||
|
.With(new X11PlatformOptions
|
||||||
|
{
|
||||||
|
EnableMultiTouch = true,
|
||||||
|
EnableIme = true,
|
||||||
|
RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software },
|
||||||
|
})
|
||||||
|
.With(new Win32PlatformOptions
|
||||||
|
{
|
||||||
|
WinUICompositionBackdropCornerRadius = 8.0f,
|
||||||
|
RenderingMode = new[] { Win32RenderingMode.AngleEgl, Win32RenderingMode.Software },
|
||||||
|
})
|
||||||
|
.UseSkia();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Initialize(string[] args)
|
||||||
|
{
|
||||||
|
// Parse arguments
|
||||||
|
CommandLineState.ParseArguments(args);
|
||||||
|
|
||||||
|
// Delete backup files after updating.
|
||||||
|
Task.Run(Updater.CleanupUpdate);
|
||||||
|
|
||||||
|
Console.Title = $"Ryujinx Console {Version}";
|
||||||
|
|
||||||
|
// Hook unhandled exception and process exit events.
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
|
||||||
|
AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit();
|
||||||
|
|
||||||
|
// Setup base data directory.
|
||||||
|
AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
|
||||||
|
|
||||||
|
// Initialize the configuration.
|
||||||
|
ConfigurationState.Initialize();
|
||||||
|
|
||||||
|
// Initialize the logger system.
|
||||||
|
LoggerModule.Initialize();
|
||||||
|
|
||||||
|
// Initialize Discord integration.
|
||||||
|
DiscordIntegrationModule.Initialize();
|
||||||
|
|
||||||
|
// Initialize SDL2 driver
|
||||||
|
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||||
|
|
||||||
|
ReloadConfig();
|
||||||
|
|
||||||
|
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
||||||
|
|
||||||
|
// Logging system information.
|
||||||
|
PrintSystemInfo();
|
||||||
|
|
||||||
|
// Enable OGL multithreading on the driver, when available.
|
||||||
|
DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
||||||
|
|
||||||
|
// Check if keys exists.
|
||||||
|
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
||||||
|
{
|
||||||
|
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
|
||||||
|
{
|
||||||
|
MainWindow.ShowKeyErrorOnLoad = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CommandLineState.LaunchPathArg != null)
|
||||||
|
{
|
||||||
|
MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReloadConfig()
|
||||||
|
{
|
||||||
|
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
||||||
|
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
|
||||||
|
|
||||||
|
// Now load the configuration as the other subsystems are now registered
|
||||||
|
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;
|
||||||
|
|
||||||
|
ConfigurationState.Instance.LoadDefault();
|
||||||
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat))
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.LoadDefault();
|
||||||
|
|
||||||
|
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if graphics backend was overridden
|
||||||
|
if (CommandLineState.OverrideGraphicsBackend != null)
|
||||||
|
{
|
||||||
|
if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
|
||||||
|
}
|
||||||
|
else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if docked mode was overriden.
|
||||||
|
if (CommandLineState.OverrideDockedMode.HasValue)
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if HideCursor was overridden.
|
||||||
|
if (CommandLineState.OverrideHideCursor is not null)
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch
|
||||||
|
{
|
||||||
|
"never" => HideCursorMode.Never,
|
||||||
|
"onidle" => HideCursorMode.OnIdle,
|
||||||
|
"always" => HideCursorMode.Always,
|
||||||
|
_ => ConfigurationState.Instance.HideCursor.Value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintSystemInfo()
|
||||||
|
{
|
||||||
|
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
||||||
|
SystemInfo.Gather().Print();
|
||||||
|
|
||||||
|
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(Logger.GetEnabledLevels().Count == 0 ? "<None>" : string.Join(", ", Logger.GetEnabledLevels()))}");
|
||||||
|
|
||||||
|
if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom)
|
||||||
|
{
|
||||||
|
Logger.Notice.Print(LogClass.Application, $"Launch Mode: Custom Path {AppDataManager.BaseDirPath}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Notice.Print(LogClass.Application, $"Launch Mode: {AppDataManager.Mode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessUnhandledException(Exception ex, bool isTerminating)
|
||||||
|
{
|
||||||
|
string message = $"Unhandled exception caught: {ex}";
|
||||||
|
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Application, message);
|
||||||
|
|
||||||
|
if (Logger.Error == null)
|
||||||
|
{
|
||||||
|
Logger.Notice.PrintMsg(LogClass.Application, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTerminating)
|
||||||
|
{
|
||||||
|
Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Exit()
|
||||||
|
{
|
||||||
|
DiscordIntegrationModule.Exit();
|
||||||
|
|
||||||
|
Logger.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
168
src/Ryujinx.Ava/Ryujinx.Ava.csproj
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<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>
|
||||||
|
<RootNamespace>Ryujinx.Ava</RootNamespace>
|
||||||
|
<ApplicationIcon>Ryujinx.ico</ApplicationIcon>
|
||||||
|
<TieredPGO>true</TieredPGO>
|
||||||
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
|
||||||
|
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
|
||||||
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
|
<TrimmerSingleWarn>false</TrimmerSingleWarn>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
|
<TrimMode>partial</TrimMode>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
|
||||||
|
See:
|
||||||
|
https://github.com/amwx/FluentAvalonia/issues/481
|
||||||
|
https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
|
||||||
|
-->
|
||||||
|
<PropertyGroup>
|
||||||
|
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia" />
|
||||||
|
<PackageReference Include="Avalonia.Desktop" />
|
||||||
|
<PackageReference Include="Avalonia.Diagnostics" Condition="'$(Configuration)'=='Debug'" />
|
||||||
|
<PackageReference Include="Avalonia.Controls.DataGrid" />
|
||||||
|
<PackageReference Include="Avalonia.Markup.Xaml.Loader" />
|
||||||
|
<PackageReference Include="Avalonia.Svg" />
|
||||||
|
<PackageReference Include="Avalonia.Svg.Skia" />
|
||||||
|
<PackageReference Include="jp2masa.Avalonia.Flexbox" />
|
||||||
|
<PackageReference Include="DynamicData" />
|
||||||
|
<PackageReference Include="FluentAvaloniaUI" />
|
||||||
|
|
||||||
|
<PackageReference Include="OpenTK.Core" />
|
||||||
|
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
||||||
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
||||||
|
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
|
||||||
|
<PackageReference Include="Silk.NET.Vulkan" />
|
||||||
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
||||||
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
||||||
|
<PackageReference Include="SPB" />
|
||||||
|
<PackageReference Include="SharpZipLib" />
|
||||||
|
<PackageReference Include="SixLabors.ImageSharp" />
|
||||||
|
|
||||||
|
<!--NOTE: DO NOT REMOVE, THIS IS REQUIRED AS A RESULT OF A TRIMMING ISSUE IN AVALONIA -->
|
||||||
|
<PackageReference Include="System.Drawing.Common" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
|
||||||
|
<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.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.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" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
<TargetPath>alsoft.ini</TargetPath>
|
||||||
|
</Content>
|
||||||
|
<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'">
|
||||||
|
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="..\..\distribution\linux\mime\Ryujinx.xml">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
<TargetPath>mime\Ryujinx.xml</TargetPath>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AvaloniaResource Include="Ui\**\*.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</AvaloniaResource>
|
||||||
|
<AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />
|
||||||
|
<AvaloniaResource Include="Assets\Styles\Themes.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</AvaloniaResource>
|
||||||
|
<AvaloniaResource Include="Assets\Styles\Styles.xaml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Assets\Locales\el_GR.json" />
|
||||||
|
<None Remove="Assets\Locales\en_US.json" />
|
||||||
|
<None Remove="Assets\Locales\es_ES.json" />
|
||||||
|
<None Remove="Assets\Locales\fr_FR.json" />
|
||||||
|
<None Remove="Assets\Locales\he_IL.json" />
|
||||||
|
<None Remove="Assets\Locales\de_DE.json" />
|
||||||
|
<None Remove="Assets\Locales\it_IT.json" />
|
||||||
|
<None Remove="Assets\Locales\ja_JP.json" />
|
||||||
|
<None Remove="Assets\Locales\ko_KR.json" />
|
||||||
|
<None Remove="Assets\Locales\pl_PL.json" />
|
||||||
|
<None Remove="Assets\Locales\pt_BR.json" />
|
||||||
|
<None Remove="Assets\Locales\ru_RU.json" />
|
||||||
|
<None Remove="Assets\Locales\tr_TR.json" />
|
||||||
|
<None Remove="Assets\Locales\uk_UA.json" />
|
||||||
|
<None Remove="Assets\Locales\zh_CN.json" />
|
||||||
|
<None Remove="Assets\Locales\zh_TW.json" />
|
||||||
|
<None Remove="Assets\Styles\Styles.xaml" />
|
||||||
|
<None Remove="Assets\Styles\Themes.xaml" />
|
||||||
|
<None Remove="Assets\Icons\Controller_JoyConLeft.svg" />
|
||||||
|
<None Remove="Assets\Icons\Controller_JoyConPair.svg" />
|
||||||
|
<None Remove="Assets\Icons\Controller_JoyConRight.svg" />
|
||||||
|
<None Remove="Assets\Icons\Controller_ProCon.svg" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Assets\Locales\el_GR.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\en_US.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\es_ES.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\fr_FR.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\he_IL.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\de_DE.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\it_IT.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\ja_JP.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\ko_KR.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\pl_PL.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\pt_BR.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\ru_RU.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\tr_TR.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\uk_UA.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\zh_CN.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\zh_TW.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
|
||||||
|
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" />
|
||||||
|
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" />
|
||||||
|
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" />
|
||||||
|
<EmbeddedResource Include="Assets\Icons\Controller_ProCon.svg" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="Assets\Locales\en_US.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
@ -8,26 +8,26 @@ using Ryujinx.Ava.UI.Windows;
|
|||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.HOS.Applets;
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.Ui;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Applet
|
namespace Ryujinx.Ava.UI.Applet
|
||||||
{
|
{
|
||||||
internal class AvaHostUIHandler : IHostUIHandler
|
internal class AvaHostUiHandler : IHostUiHandler
|
||||||
{
|
{
|
||||||
private readonly MainWindow _parent;
|
private readonly MainWindow _parent;
|
||||||
|
|
||||||
public IHostUITheme HostUITheme { get; }
|
public IHostUiTheme HostUiTheme { get; }
|
||||||
|
|
||||||
public AvaHostUIHandler(MainWindow parent)
|
public AvaHostUiHandler(MainWindow parent)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
|
|
||||||
HostUITheme = new AvaloniaHostUITheme(parent);
|
HostUiTheme = new AvaloniaHostUiTheme(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DisplayMessageDialog(ControllerAppletUIArgs args)
|
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
|
||||||
{
|
{
|
||||||
ManualResetEvent dialogCloseEvent = new(false);
|
ManualResetEvent dialogCloseEvent = new(false);
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
return okPressed;
|
return okPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText)
|
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
|
||||||
{
|
{
|
||||||
ManualResetEvent dialogCloseEvent = new(false);
|
ManualResetEvent dialogCloseEvent = new(false);
|
||||||
|
|
@ -5,7 +5,7 @@ using Avalonia.Threading;
|
|||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.Ui;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using HidKey = Ryujinx.Common.Configuration.Hid.Key;
|
using HidKey = Ryujinx.Common.Configuration.Hid.Key;
|
@ -1,13 +1,13 @@
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.Ui;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Applet
|
namespace Ryujinx.Ava.UI.Applet
|
||||||
{
|
{
|
||||||
class AvaloniaHostUITheme : IHostUITheme
|
class AvaloniaHostUiTheme : IHostUiTheme
|
||||||
{
|
{
|
||||||
public AvaloniaHostUITheme(MainWindow parent)
|
public AvaloniaHostUiTheme(MainWindow parent)
|
||||||
{
|
{
|
||||||
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
|
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
|
||||||
DefaultBackgroundColor = BrushToThemeColor(parent.Background);
|
DefaultBackgroundColor = BrushToThemeColor(parent.Background);
|
@ -9,7 +9,6 @@ using Ryujinx.Ava.UI.Windows;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Applets;
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -17,10 +16,10 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
{
|
{
|
||||||
internal partial class ControllerAppletDialog : UserControl
|
internal partial class ControllerAppletDialog : UserControl
|
||||||
{
|
{
|
||||||
private const string ProControllerResource = "Ryujinx/Assets/Icons/Controller_ProCon.svg";
|
private const string ProControllerResource = "Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg";
|
||||||
private const string JoyConPairResource = "Ryujinx/Assets/Icons/Controller_JoyConPair.svg";
|
private const string JoyConPairResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg";
|
||||||
private const string JoyConLeftResource = "Ryujinx/Assets/Icons/Controller_JoyConLeft.svg";
|
private const string JoyConLeftResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg";
|
||||||
private const string JoyConRightResource = "Ryujinx/Assets/Icons/Controller_JoyConRight.svg";
|
private const string JoyConRightResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg";
|
||||||
|
|
||||||
public static SvgImage ProControllerImage => GetResource(ProControllerResource);
|
public static SvgImage ProControllerImage => GetResource(ProControllerResource);
|
||||||
public static SvgImage JoyconPairImage => GetResource(JoyConPairResource);
|
public static SvgImage JoyconPairImage => GetResource(JoyConPairResource);
|
||||||
@ -36,7 +35,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
private readonly MainWindow _mainWindow;
|
private readonly MainWindow _mainWindow;
|
||||||
|
|
||||||
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUIArgs args)
|
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUiArgs args)
|
||||||
{
|
{
|
||||||
if (args.PlayerCountMin == args.PlayerCountMax)
|
if (args.PlayerCountMin == args.PlayerCountMax)
|
||||||
{
|
{
|
||||||
@ -69,7 +68,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUIArgs args)
|
public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUiArgs args)
|
||||||
{
|
{
|
||||||
ContentDialog contentDialog = new();
|
ContentDialog contentDialog = new();
|
||||||
UserResult result = UserResult.Cancel;
|
UserResult result = UserResult.Cancel;
|
||||||
@ -104,7 +103,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(path))
|
if (!string.IsNullOrWhiteSpace(path))
|
||||||
{
|
{
|
||||||
SvgSource source = new(default(Uri));
|
SvgSource source = new();
|
||||||
|
|
||||||
source.Load(EmbeddedResources.GetStream(path));
|
source.Load(EmbeddedResources.GetStream(path));
|
||||||
|
|
@ -34,7 +34,7 @@
|
|||||||
Height="80"
|
Height="80"
|
||||||
MinWidth="50"
|
MinWidth="50"
|
||||||
Margin="5,10,20,10"
|
Margin="5,10,20,10"
|
||||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
@ -31,7 +31,7 @@
|
|||||||
MinWidth="50"
|
MinWidth="50"
|
||||||
Margin="5,10,20,10"
|
Margin="5,10,20,10"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
public string MainText { get; set; } = "";
|
public string MainText { get; set; } = "";
|
||||||
public string SecondaryText { get; set; } = "";
|
public string SecondaryText { get; set; } = "";
|
||||||
|
|
||||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUIArgs args)
|
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUiArgs args)
|
||||||
{
|
{
|
||||||
ContentDialog contentDialog = new();
|
ContentDialog contentDialog = new();
|
||||||
|
|
@ -16,7 +16,7 @@
|
|||||||
Click="CreateApplicationShortcut_Click"
|
Click="CreateApplicationShortcut_Click"
|
||||||
Header="{locale:Locale GameListContextMenuCreateShortcut}"
|
Header="{locale:Locale GameListContextMenuCreateShortcut}"
|
||||||
IsEnabled="{Binding CreateShortcutEnabled}"
|
IsEnabled="{Binding CreateShortcutEnabled}"
|
||||||
ToolTip.Tip="{OnPlatform Default={locale:Locale GameListContextMenuCreateShortcutToolTip}, macOS={locale:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
ToolTip.Tip="{locale:Locale GameListContextMenuCreateShortcutToolTip}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenUserSaveDirectory_Click"
|
Click="OpenUserSaveDirectory_Click"
|
||||||
@ -46,11 +46,6 @@
|
|||||||
Click="OpenCheatManager_Click"
|
Click="OpenCheatManager_Click"
|
||||||
Header="{locale:Locale GameListContextMenuManageCheat}"
|
Header="{locale:Locale GameListContextMenuManageCheat}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
|
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
|
||||||
<MenuItem
|
|
||||||
Click="OpenModManager_Click"
|
|
||||||
Header="{locale:Locale GameListContextMenuManageMod}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageModToolTip}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenModsDirectory_Click"
|
Click="OpenModsDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
|
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
|
@ -11,8 +11,8 @@ using Ryujinx.Ava.UI.ViewModels;
|
|||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.UI.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -133,7 +133,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
string modsBasePath = ModLoader.GetModsBasePath();
|
string modsBasePath = ModLoader.GetModsBasePath();
|
||||||
string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, viewModel.SelectedApplication.TitleId);
|
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId);
|
||||||
|
|
||||||
OpenHelper.OpenFolder(titleModsPath);
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
}
|
}
|
||||||
@ -146,22 +146,12 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
string sdModsBasePath = ModLoader.GetSdModsBasePath();
|
string sdModsBasePath = ModLoader.GetSdModsBasePath();
|
||||||
string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, viewModel.SelectedApplication.TitleId);
|
string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId);
|
||||||
|
|
||||||
OpenHelper.OpenFolder(titleModsPath);
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenModManager_Click(object sender, RoutedEventArgs args)
|
|
||||||
{
|
|
||||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
|
||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
|
||||||
{
|
|
||||||
await ModManagerWindow.Show(ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void PurgePtcCache_Click(object sender, RoutedEventArgs args)
|
public async void PurgePtcCache_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
@ -4,6 +4,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
||||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
@ -32,10 +33,11 @@
|
|||||||
SelectionChanged="GameList_SelectionChanged">
|
SelectionChanged="GameList_SelectionChanged">
|
||||||
<ListBox.ItemsPanel>
|
<ListBox.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<WrapPanel
|
<flex:FlexPanel
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Stretch"
|
||||||
Orientation="Horizontal" />
|
AlignContent="FlexStart"
|
||||||
|
JustifyContent="FlexStart" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ListBox.ItemsPanel>
|
</ListBox.ItemsPanel>
|
||||||
<ListBox.Styles>
|
<ListBox.Styles>
|
@ -3,7 +3,7 @@ using Avalonia.Input;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
namespace Ryujinx.Ava.UI.Controls
|
@ -3,7 +3,7 @@ using Avalonia.Input;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
namespace Ryujinx.Ava.UI.Controls
|
@ -26,7 +26,7 @@
|
|||||||
Height="70"
|
Height="70"
|
||||||
MinWidth="50"
|
MinWidth="50"
|
||||||
Margin="5,10,20,10"
|
Margin="5,10,20,10"
|
||||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
@ -1,5 +1,5 @@
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Helpers
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
{
|
{
|