Compare commits

..

No commits in common. "mirror/master" and "master" have entirely different histories.

60 changed files with 643 additions and 1374 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
patreon: Ryujinx

View File

@ -22,8 +22,8 @@ body:
id: log id: log
attributes: attributes:
label: Log file label: Log file
description: A log file will help us to better diagnose and fix the issue. description: A log file will help our developers to better diagnose and fix the issue.
placeholder: Logs files can be found under "Logs" folder in the Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste). placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste).
validations: validations:
required: true required: true
- type: input - type: input
@ -36,8 +36,8 @@ body:
- type: input - type: input
id: ryujinx-version id: ryujinx-version
attributes: attributes:
label: Ryujinx version (revision hash) label: Ryujinx version
placeholder: "e.g. r67111a5" placeholder: "e.g. 1.0.470"
validations: validations:
required: true required: true
- type: input - type: input

View File

@ -1,5 +1,5 @@
blank_issues_enabled: true blank_issues_enabled: false
contact_links: contact_links:
- name: ryujinx-mirror (Discord) - name: Ryujinx Discord
url: https://discord.gg/xmHPGDfVCa url: https://discord.gg/N2FmfVc
about: This is the home of development for the ryujinx-mirror fork, feel free to make a post in `#ryujinx-help` for general support & technical issues about: This is for development related issues. For support and technical issues, please come to our Discord server.

View File

@ -0,0 +1,31 @@
name: Feature Request
description: Suggest a new feature for Ryujinx.
title: "[Feature Request]"
labels: enhancement
body:
- type: textarea
id: overview
attributes:
label: Overview
description: Include the basic, high-level concepts for this feature here.
validations:
required: true
- type: textarea
id: details
attributes:
label: Smaller details
description: These may include specific methods of implementation etc.
validations:
required: true
- type: textarea
id: request
attributes:
label: Nature of request
validations:
required: true
- type: textarea
id: feature
attributes:
label: Why would this feature be useful?
validations:
required: true

View File

@ -21,6 +21,6 @@ body:
id: required id: required
attributes: attributes:
label: Required by label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/ryujinx-mirror/Ryujinx-Games-List/issues) of the game(s) that require this instruction. description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
validations: validations:
required: true required: true

View File

@ -20,6 +20,6 @@ body:
id: required id: required
attributes: attributes:
label: Required by label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/ryujinx-mirror/Ryujinx-Games-List/issues) of the game(s) that require this service. description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this service.
validations: validations:
required: true required: true

View File

@ -14,6 +14,6 @@ body:
id: required id: required
attributes: attributes:
label: Required by label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/ryujinx-mirror/Ryujinx-Games-List/issues) of the game(s) that require this instruction. description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
validations: validations:
required: true required: true

View File

@ -7,7 +7,7 @@ updates:
labels: labels:
- "infra" - "infra"
reviewers: reviewers:
- regginator - TSRBerry
commit-message: commit-message:
prefix: "ci" prefix: "ci"
@ -19,7 +19,7 @@ updates:
labels: labels:
- "infra" - "infra"
reviewers: reviewers:
- regginator - TSRBerry
commit-message: commit-message:
prefix: nuget prefix: nuget
groups: groups:

4
.github/labeler.yml vendored
View File

@ -33,7 +33,3 @@ kernel:
infra: infra:
- changed-files: - changed-files:
- any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props'] - any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props']
meta:
- changed-files:
- any-glob-to-any-file: ['docs/**', 'README.md', 'CONTRIBUTING.md']

25
.github/reviewers.yml vendored Normal file
View File

@ -0,0 +1,25 @@
cpu:
- gdkchan
- riperiperi
- LDj3SNuD
gpu:
- gdkchan
- riperiperi
gui:
- Ack77
- emmauss
- TSRBerry
horizon:
- gdkchan
- Ack77
- TSRBerry
infra:
- TSRBerry
default:
- '@developers'

View File

@ -1,7 +1,6 @@
name: Build job name: Build job
on: on:
workflow_dispatch:
workflow_call: workflow_call:
env: env:
@ -40,102 +39,65 @@ jobs:
shell: bash shell: bash
- name: Change config filename - name: Change config filename
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Change config filename for macOS - name: Change config filename for macOS
if: github.event_name == 'pull_request' && matrix.platform.os == 'macos-13'
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os == 'macos-13'
- 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
- name: Run tests - name: Test
uses: ryujinx-mirror/unstable-commands@releases/v1.0.6 uses: TSRBerry/unstable-commands@v1
if: matrix.platform.name != 'linux-arm64'
with: with:
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
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
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.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
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Headless.SDL2 - name: Publish Ryujinx.Headless.SDL2
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
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.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
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Gtk3 - name: Publish Ryujinx.Gtk3
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
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.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
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Set executable bit - name: Set executable bit
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
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_gtk/Ryujinx.Gtk3 ./publish_gtk/Ryujinx.sh
- name: Build AppImage
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest' if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
run: |
PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt install -y zsync desktop-file-utils appstream
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
ARCH_NAME=x64
export ARCH=x86_64
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
ARCH_NAME=arm64
export ARCH=aarch64
else
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
exit 1
fi
export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
shell: bash
- name: Upload Ryujinx artifact - name: Upload Ryujinx artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
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.platform.zip_os_name }}
path: publish path: publish
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Upload Ryujinx (AppImage) artifact
uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage
path: publish_appimage
- name: Upload Ryujinx.Headless.SDL2 artifact - name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
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.platform.zip_os_name }}
path: publish_sdl2_headless path: publish_sdl2_headless
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Upload Ryujinx.Gtk3 artifact - name: Upload Ryujinx.Gtk3 artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
with: with:
name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish_gtk path: publish_gtk
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
build_macos: build_macos:
name: macOS Universal (${{ matrix.configuration }}) name: macOS Universal (${{ matrix.configuration }})
@ -174,9 +136,9 @@ jobs:
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: Change config filename
if: github.event_name == 'pull_request'
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash shell: bash
if: github.event_name == 'pull_request'
- name: Publish macOS Ryujinx - name: Publish macOS Ryujinx
run: | run: |
@ -188,14 +150,14 @@ jobs:
- name: Upload Ryujinx artifact - name: Upload Ryujinx artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request'
with: with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish/*.tar.gz" path: "publish/*.tar.gz"
if: github.event_name == 'pull_request'
- name: Upload Ryujinx.Headless.SDL2 artifact - name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request'
with: with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_headless/*.tar.gz" path: "publish_headless/*.tar.gz"
if: github.event_name == 'pull_request'

View File

@ -2,7 +2,7 @@ name: Perform checks
on: on:
pull_request: pull_request:
branches: [ mirror/master ] branches: [ master ]
paths: paths:
- '**' - '**'
- '!.github/**' - '!.github/**'
@ -46,14 +46,16 @@ jobs:
# For some unknown reason this step sometimes fails with exit code 139 (segfault?), # For some unknown reason this step sometimes fails with exit code 139 (segfault?),
# so in that case we'll try again (3 tries max). # so in that case we'll try again (3 tries max).
- name: Run dotnet format style - name: Run dotnet format style
uses: ryujinx-mirror/unstable-commands@releases/v1.0.6 uses: TSRBerry/unstable-commands@v1
with: with:
commands: dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d commands: dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d
timeout-minutes: 5 timeout-minutes: 5
retry-codes: 139 retry-codes: 139
# For some unknown reason this step sometimes fails with exit code 139 (segfault?),
# so in that case we'll try again (3 tries max).
- name: Run dotnet format analyzers - name: Run dotnet format analyzers
uses: ryujinx-mirror/unstable-commands@releases/v1.0.6 uses: TSRBerry/unstable-commands@v1
with: with:
commands: dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d commands: dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d
timeout-minutes: 5 timeout-minutes: 5

212
.github/workflows/flatpak.yml vendored Normal file
View File

@ -0,0 +1,212 @@
name: Flatpak release job
on:
workflow_call:
inputs:
ryujinx_version:
required: true
type: string
concurrency: flatpak-release
jobs:
release:
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
runs-on: ubuntu-latest
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
GIT_COMMITTER_NAME: "RyujinxBot"
GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com"
RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj"
NUGET_SOURCES_DESTDIR: "nuget-sources"
RYUJINX_VERSION: "${{ inputs.ryujinx_version }}"
steps:
- uses: actions/checkout@v4
with:
path: Ryujinx
- uses: actions/setup-dotnet@v4
with:
global-json-file: Ryujinx/global.json
- name: Get version info
id: version_info
working-directory: Ryujinx
run: |
echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4
with:
repository: flathub/org.ryujinx.Ryujinx
token: ${{ secrets.RYUJINX_BOT_PAT }}
submodules: recursive
path: flathub
- name: Install dependencies
run: python -m pip install PyYAML lxml
- 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.
# So we just publish to grab the dependencies
run: |
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
shell: python
run: |
import hashlib
from pathlib import Path
import base64
import binascii
import json
import os
import urllib.request
sources = []
def create_source_from_external(name, version):
full_dir_path = Path(os.environ["NUGET_PACKAGES"]).joinpath(name).joinpath(version)
os.makedirs(full_dir_path, exist_ok=True)
filename = "{}.{}.nupkg".format(name, version)
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
name, version, filename
)
print(f"Processing {url}...")
response = urllib.request.urlopen(url)
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
id: metadata
env:
RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }}
shell: python
run: |
import hashlib
import hmac
import json
import os
import yaml
from datetime import datetime
from lxml import etree
# Ensure we don't destroy multiline strings
def str_presenter(dumper, data):
if len(data.splitlines()) > 1:
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
yaml_file = "flathub/org.ryujinx.Ryujinx.yml"
xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml"
with open(yaml_file, "r") as f:
data = yaml.safe_load(f)
for source in data["modules"][0]["sources"]:
if type(source) is str:
continue
if (
source["type"] == "git"
and source["url"] == "https://github.com/Ryujinx/Ryujinx.git"
):
source["commit"] = os.environ['RYUJINX_GIT_HASH']
is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION']
with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out:
if is_same_version:
gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}")
else:
gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}")
if not is_same_version:
data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION']
with open(yaml_file, "w") as f:
yaml.safe_dump(data, f, sort_keys=False)
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(xml_file, parser)
root = tree.getroot()
releases = root.find("releases")
element = etree.Element("release")
element.set("version", os.environ['RYUJINX_VERSION'])
element.set("date", datetime.now().date().isoformat())
releases.insert(0, element)
# Ensure 4 spaces
etree.indent(root, space=" ")
with open(xml_file, "wb") as f:
f.write(
etree.tostring(
tree,
pretty_print=True,
encoding="UTF-8",
doctype='<?xml version="1.0" encoding="UTF-8"?>',
)
)
- name: Push flatpak update
working-directory: flathub
env:
COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }}
run: |
git config user.name "${{ env.GIT_COMMITTER_NAME }}"
git config user.email "${{ env.GIT_COMMITTER_EMAIL }}"
git add .
git commit -m "$COMMIT_MESSAGE"
git push origin master

41
.github/workflows/mako.yml vendored Normal file
View File

@ -0,0 +1,41 @@
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 }}

View File

@ -0,0 +1,71 @@
name: Comment PR artifacts links
on:
workflow_run:
workflows: ['Perform checks']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
steps:
- uses: actions/github-script@v6
with:
script: |
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
const issue_number = await (async () => {
const pulls = await github.rest.pulls.list({owner, repo});
for await (const {data} of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === pull_head_sha) {
return pull.number;
}
}
}
})();
if (issue_number) {
core.info(`Using pull request ${issue_number}`);
} else {
return core.error(`No matching pull request found`);
}
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
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_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
for (const art of artifacts) {
if(art.name.includes('Debug')) {
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else if(art.name.includes('gtk-ryujinx')) {
hidden_gtk_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else if(art.name.includes('sdl2-ryujinx-headless')) {
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else {
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
}
hidden_gtk_artifacts += `\n</details>`;
hidden_headless_artifacts += `\n</details>`;
hidden_debug_artifacts += `\n</details>`;
body += hidden_gtk_artifacts;
body += hidden_headless_artifacts;
body += hidden_debug_artifacts;
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`);
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
} else {
core.info(`Creating a comment`);
await github.rest.issues.createComment({repo, owner, issue_number, body});
}

View File

@ -5,22 +5,23 @@ on:
jobs: jobs:
triage: triage:
runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
pull-requests: write pull-requests: write
runs-on: ubuntu-latest
steps: steps:
- name: Fetch labeler.yml # Grab sources to get latest labeler.yml
- name: Fetch sources
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
# Ensure we pin the source origin as pull_request_target run under forks. # Ensure we pin the source origin as pull_request_target run under forks.
fetch-depth: 0 fetch-depth: 0
repository: ryujinx-mirror/ryujinx repository: Ryujinx/Ryujinx
ref: mirror/master ref: master
- name: Update labels based on changes in PR - name: Update labels based on changes
uses: actions/labeler@v5 uses: actions/labeler@v5
with: with:
sync-labels: true sync-labels: true

View File

@ -3,26 +3,24 @@ name: Release job
on: on:
workflow_dispatch: workflow_dispatch:
inputs: {} inputs: {}
#push: push:
# branches: [ mirror/release ] branches: [ master ]
# paths-ignore: paths-ignore:
# - '.github/**' - '.github/**'
# - '*.yml' - '*.yml'
# - '*.json' - '*.json'
# - '*.config' - '*.config'
# - '*.md' - '*.md'
concurrency: release concurrency: release
env: env:
POWERSHELL_TELEMETRY_OPTOUT: 1 POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1
#RYUJINX_BASE_VERSION: "1.1" # NOTE: For now, releases for the fork will be named after Git revision hashes, this is ignored RYUJINX_BASE_VERSION: "1.1"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
# Should be unnecessary for us, we're releasing in-repo RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
#RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "ryujinx-mirror" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
#RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "ryujinx"
#RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "mirror/master"
jobs: jobs:
tag: tag:
@ -32,7 +30,7 @@ jobs:
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
echo "build_version=r.`echo ${{ github.sha }} | cut -c1-7`" >> $GITHUB_OUTPUT echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Create tag - name: Create tag
@ -51,17 +49,16 @@ jobs:
with: with:
name: ${{ steps.version_info.outputs.build_version }} name: ${{ steps.version_info.outputs.build_version }}
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
draft: "true" body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
omitBody: true omitBodyDuringUpdate: true
#omitBodyDuringUpdate: true owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
#owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
#repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} token: ${{ secrets.RELEASE_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
release: release:
name: Release for ${{ matrix.platform.name }} name: Release for ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}
timeout-minutes: 60 timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
strategy: strategy:
matrix: matrix:
platform: platform:
@ -81,18 +78,18 @@ jobs:
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
echo "build_version=r.`echo ${{ github.sha }} | cut -c1-7`" >> $GITHUB_OUTPUT echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Configure for release - name: Configure for release
run: | run: |
sed -r --in-place 's&\%\%RYUJINX_BUILD_VERSION\%\%&${{ steps.version_info.outputs.build_version }}&g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's&\%\%RYUJINX_BUILD_GIT_HASH\%\%&${{ steps.version_info.outputs.git_short_hash }}&g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's&\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%&${{ github.repository_owner }}&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_REPO\%\%&${{ github.event.repository.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_NAME\%\%&${{ github.ref_name }}&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 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
@ -100,77 +97,36 @@ jobs:
- name: Publish - name: Publish
run: | run: |
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava -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_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.platform.name }}" -o ./publish_sdl2_headless -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.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
- name: Packing Windows builds - name: Packing Windows builds
if: matrix.platform.os == 'windows-latest' if: matrix.platform.os == 'windows-latest'
run: | run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
ZIP_OS_NAME="${{ matrix.platform.zip_os_name }}"
pushd publish_ava pushd publish_ava
cp Ryujinx.exe Ryujinx.Ava.exe cp publish/Ryujinx.exe publish/Ryujinx.Ava.exe
7z a ../release_output/ryujinx-$BUILD_VERSION-$ZIP_OS_NAME.zip * 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
popd popd
pushd publish_sdl2_headless pushd publish_sdl2_headless
7z a ../release_output/sdl2-ryujinx-headless-$BUILD_VERSION-$ZIP_OS_NAME.zip * 7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
popd
shell: bash
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt install -y zsync desktop-file-utils appstream
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
ARCH_NAME=x64
export ARCH=x86_64
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
ARCH_NAME=arm64
export ARCH=aarch64
else
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
exit 1
fi
export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
BUILDDIR=publish_ava OUTDIR=publish_ava_appimage distribution/linux/appimage/build-appimage.sh
# Add to release output
pushd publish_ava_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
popd popd
shell: bash shell: bash
- name: Packing Linux builds - name: Packing Linux builds
if: matrix.platform.os == 'ubuntu-latest' if: matrix.platform.os == 'ubuntu-latest'
run: | run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
ZIP_OS_NAME="${{ matrix.platform.zip_os_name }}"
pushd publish_ava pushd publish_ava
cp Ryujinx Ryujinx.Ava cp publish/Ryujinx publish/Ryujinx.Ava
chmod +x Ryujinx.sh Ryujinx Ryujinx.Ava chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava publish/Ryujinx
tar -czvf ../release_output/ryujinx-$BUILD_VERSION-$ZIP_OS_NAME.tar.gz * tar -czvf ../release_output/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 }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
popd popd
pushd publish_sdl2_headless pushd publish_sdl2_headless
chmod +x Ryujinx.sh Ryujinx.Headless.SDL2 chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
tar -czvf ../release_output/sdl2-ryujinx-headless-$BUILD_VERSION-$ZIP_OS_NAME.tar.gz * tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
popd popd
shell: bash shell: bash
@ -178,21 +134,20 @@ jobs:
uses: ncipollo/release-action@v1 uses: ncipollo/release-action@v1
with: with:
name: ${{ steps.version_info.outputs.build_version }} name: ${{ steps.version_info.outputs.build_version }}
artifacts: "release_output/*.tar.gz,release_output/*.zip"
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*" body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
draft: "true" omitBodyDuringUpdate: true
omitBody: true
#omitBodyDuringUpdate: true
allowUpdates: true allowUpdates: true
replacesArtifacts: true replacesArtifacts: true
#owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
#repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.RELEASE_TOKEN }}
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60 timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -220,18 +175,17 @@ jobs:
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
echo "build_version=r.`echo ${{ github.sha }} | cut -c1-7`" >> $GITHUB_OUTPUT echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Configure for release - name: Configure for release
run: | run: |
sed -r --in-place 's&\%\%RYUJINX_BUILD_VERSION\%\%&${{ steps.version_info.outputs.build_version }}&g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's&\%\%RYUJINX_BUILD_GIT_HASH\%\%&${{ steps.version_info.outputs.git_short_hash }}&g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's&\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%&${{ github.repository_owner }}&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_REPO\%\%&${{ github.event.repository.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_NAME\%\%&${{ github.ref_name }}&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 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
@ -246,36 +200,19 @@ jobs:
uses: ncipollo/release-action@v1 uses: ncipollo/release-action@v1
with: with:
name: ${{ steps.version_info.outputs.build_version }} name: ${{ steps.version_info.outputs.build_version }}
tag: ${{ steps.version_info.outputs.build_version }}
artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz" artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
draft: "true" tag: ${{ steps.version_info.outputs.build_version }}
omitBody: true body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
#omitBodyDuringUpdate: true omitBodyDuringUpdate: true
allowUpdates: true allowUpdates: true
replacesArtifacts: true replacesArtifacts: true
#owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
#repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.RELEASE_TOKEN }}
set_latest_release: flatpak_release:
name: Set as latest release uses: ./.github/workflows/flatpak.yml
runs-on: ubuntu-latest needs: release
needs: [release, macos_release] with:
steps: ryujinx_version: "1.1.${{ github.run_number }}"
- name: Get version info secrets: inherit
id: version_info
run: |
echo "build_version=r.`echo ${{ github.sha }} | cut -c1-7`" >> $GITHUB_OUTPUT
shell: bash
- name: Update release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
tag: ${{ steps.version_info.outputs.build_version }}
makeLatest: "true"
omitBody: true
allowUpdates: true
#owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
#repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
token: ${{ secrets.GITHUB_TOKEN }}

8
.gitignore vendored
View File

@ -10,15 +10,13 @@
# Build results # Build results
[Dd]ebug*/ [Dd]ebug/
[Rr]elease*/ [Rr]elease/
x64/ x64/
build/ build/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
AppDir/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/ !packages/*/build/
@ -97,7 +95,7 @@ DocProject/Help/Html2
DocProject/Help/html DocProject/Help/html
# Click-Once directory # Click-Once directory
publish*/ publish/
# Publish Web Output # Publish Web Output
*.Publish.xml *.Publish.xml

View File

@ -14,22 +14,22 @@ We always welcome bug reports, feature proposals and overall feedback. Here are
### Identify Where to Report ### Identify Where to Report
The Ryujinx codebase is distributed across multiple repositories in the [Ryujinx organization](https://github.com/ryujinx-mirror). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos: The Ryujinx codebase is distributed across multiple repositories in the [Ryujinx organization](https://github.com/Ryujinx). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos:
* [Ryujinx/Ryujinx](https://github.com/ryujinx-mirror/Ryujinx) Ryujinx core project files. * [Ryujinx/Ryujinx](https://github.com/Ryujinx/Ryujinx) Ryujinx core project files.
* [Ryujinx/Ryujinx-Games-List](https://github.com/ryujinx-mirror/Ryujinx-Games-List) Ryujinx game compatibility list. * [Ryujinx/Ryujinx-Games-List](https://github.com/Ryujinx/Ryujinx-Games-List) Ryujinx game compatibility list.
* [Ryujinx/Ryujinx-Website](https://github.com/ryujinx-mirror/Ryujinx-Website) Ryujinx website source code. * [Ryujinx/Ryujinx-Website](https://github.com/Ryujinx/Ryujinx-Website) Ryujinx website source code.
* [Ryujinx/Ryujinx-Ldn-Website](https://github.com/ryujinx-mirror/Ryujinx-Ldn-Website) Ryujinx LDN website source code. * [Ryujinx/Ryujinx-Ldn-Website](https://github.com/Ryujinx/Ryujinx-Ldn-Website) Ryujinx LDN website source code.
### Finding Existing Issues ### Finding Existing Issues
Before filing a new issue, please search our [open issues](https://github.com/ryujinx-mirror/Ryujinx/issues) to check if it already exists. Before filing a new issue, please search our [open issues](https://github.com/Ryujinx/Ryujinx/issues) to check if it already exists.
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog. If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
### Writing a Good Feature Request ### Writing a Good Feature Request
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/ryujinx-mirror/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D). Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
### Writing a Good Bug Report ### Writing a Good Bug Report
@ -43,13 +43,13 @@ Ideally, a bug report should contain the following information:
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically. * A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds? * Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/ryujinx-mirror/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D). When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
## Contributing Changes ## Contributing Changes
Project maintainers will merge changes that both improve the project and meet our standards for code quality. Project maintainers will merge changes that both improve the project and meet our standards for code quality.
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/ryujinx-mirror/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance. The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
### DOs and DON'Ts ### DOs and DON'Ts
@ -83,14 +83,14 @@ We use and recommend the following workflow:
3. In your fork, create a branch off of main (`git checkout -b mybranch`). 3. In your fork, create a branch off of main (`git checkout -b mybranch`).
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork. - Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
4. Make and commit your changes to your branch. 4. Make and commit your changes to your branch.
- [Build Instructions](https://github.com/ryujinx-mirror/Ryujinx#building) explains how to build and test. - [Build Instructions](https://github.com/Ryujinx/Ryujinx#building) explains how to build and test.
- Commit messages should be clear statements of action and intent. - Commit messages should be clear statements of action and intent.
6. Build the repository with your changes. 6. Build the repository with your changes.
- Make sure that the builds are clean. - Make sure that the builds are clean.
- Make sure that `dotnet format` has been run and any corrections tested and committed. - Make sure that `dotnet format` has been run and any corrections tested and committed.
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch. 7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
- State in the description what issue or improvement your change is addressing. - State in the description what issue or improvement your change is addressing.
- Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/ryujinx-mirror/Ryujinx/actions) to check for outstanding errors. - Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/Ryujinx/Ryujinx/actions) to check for outstanding errors.
8. Wait for feedback or approval of your changes from the [core development team](https://github.com/orgs/Ryujinx/teams/developers) 8. Wait for feedback or approval of your changes from the [core development team](https://github.com/orgs/Ryujinx/teams/developers)
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md). - Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
9. When the team members have signed off, and all checks are green, your PR will be merged. 9. When the team members have signed off, and all checks are green, your PR will be merged.
@ -99,7 +99,7 @@ We use and recommend the following workflow:
### Good First Issues ### Good First Issues
The team marks the most straightforward issues as [good first issues](https://github.com/ryujinx-mirror/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase. The team marks the most straightforward issues as [good first issues](https://github.com/Ryujinx/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
### Commit Messages ### Commit Messages
@ -122,7 +122,7 @@ Also do your best to factor commits appropriately, not too large with unrelated
### PR - CI Process ### PR - CI Process
The [Ryujinx continuous integration](https://github.com/ryujinx-mirror/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change. The [Ryujinx continuous integration](https://github.com/Ryujinx/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure: If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
* `dotnet format` has not been run on the PR and has outstanding stylistic issues. * `dotnet format` has not been run on the PR and has outstanding stylistic issues.
@ -143,5 +143,5 @@ Ryujinx uses some implementations and frameworks from other projects. The follow
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence). - The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
- The license of the file is left in-tact. - The license of the file is left in-tact.
- The contribution is correctly attributed in the [3rd party notices](https://github.com/ryujinx-mirror/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed. - The contribution is correctly attributed in the [3rd party notices](https://github.com/Ryujinx/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.

View File

@ -3,24 +3,24 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia" Version="11.1.4" /> <PackageVersion Include="Avalonia" Version="11.0.10" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.1.4" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
<PackageVersion Include="Avalonia.Desktop" Version="11.1.4" /> <PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.1.4" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.0.10" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.1.4" /> <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
<PackageVersion Include="Avalonia.Svg" Version="11.1.0.1" /> <PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.1.0.1" /> <PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="2.2.0" /> <PackageVersion Include="Concentus" Version="2.2.0" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="9.0.4" /> <PackageVersion Include="DynamicData" Version="9.0.4" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.1.0" /> <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="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.9.2" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.1.2" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
@ -42,7 +42,7 @@
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.8" /> <PackageVersion Include="SkiaSharp" Version="2.88.7" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
<PackageVersion Include="SPB" Version="0.0.4-build32" /> <PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" /> <PackageVersion Include="System.IO.Hashing" Version="8.0.0" />

View File

@ -1,31 +1,6 @@
[links/discord]: https://discord.gg/xmHPGDfVCa
[badges/discord]: https://img.shields.io/discord/1291765437100720243?label=ryujinx-mirror&logo=discord&logoColor=FFFFFF&color=5865F3
As of now, the [ryujinx-mirror/ryujinx](https://github.com/ryujinx-mirror/ryujinx) repository serves as a downstream hard fork of the original Ryujinx project. You can download nightly binaries for Windows, macOS, and Linux (including `AppImage`s) from the [latest release](https://github.com/ryujinx-mirror/ryujinx/releases/latest).
> [!NOTE]
> This fork is not affiliated with the **original** Ryujinx project, or Nintendo whatsoever.
### Current Goals
If you would like a version with more new features & improvements, feel free to check out [GreemDev's fork](https://github.com/GreemDev/Ryujinx). We aim to keep this repository more focused on small fixes and infrastructure reconstruction, staying more true to the original Ryujinx project.
* ☑️ Reconstruct basic build infrastructure & workflows for this repository, based on revision hashes as opposed to semver releases (for now)
* ☑️ To be as safe as possible, remove all previous in-app and meta references to Patreon, `ryujinx.org` etc while keeping full attribution of original authors and contributors in-tact.
* Keep 'branding' as pure and faithful to the original project as possible.
### Join Discussion
Feel free to join the [ryujinx-mirror Discord community][links/discord] to join in on the development of this fork going forward.<br>
See `#ryujinx-info` for more information.
[![ryujinx-mirror Discord][badges/discord]][links/discord]
___
<h1 align="center"> <h1 align="center">
<br> <br>
<a href="https://github.com/ryujinx-mirror/ryujinx"><img src="distribution/misc/Logo.svg" alt="Ryujinx" width="150"></a> <a href="https://ryujinx.org/"><img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx/master/distribution/misc/Logo.svg" alt="Ryujinx" width="150"></a>
<br> <br>
<b>Ryujinx</b> <b>Ryujinx</b>
<br> <br>
@ -37,16 +12,34 @@ ___
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="LICENSE.txt" target="_blank">MIT license</a>. Ryujinx is available on Github under the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
<br /> <br />
</p> </p>
<p align="center">
<a href="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml">
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
alt="">
</a>
<a href="https://crwd.in/ryujinx">
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
alt="">
</a>
<a href="https://discord.com/invite/VkQYXAZ">
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
alt="Discord">
</a>
<br>
<br>
<img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx-Website/master/public/assets/images/shell.png">
</p>
## Compatibility ## Compatibility
As of May 2024, Ryujinx has been tested on approximately 4,300 titles; As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable. over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
You can check out the compatibility list [here](https://github.com/ryujinx-mirror/Ryujinx-Games-List/issues). 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; 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. simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue.
@ -57,26 +50,21 @@ Use the search function to see if a game has been tested already!
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-mirror/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](https://github.com/ryujinx-mirror/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide). For our Local Wireless (LDN) builds, see our [Multiplayer: Local Play/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**.
See [the releases page](https://github.com/ryujinx-mirror/ryujinx/releases) for automatic builds for Windows, macOS, and Linux. 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-mirror/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 ## Documentation
@ -93,7 +81,7 @@ Make sure your SDK version is higher or equal to the required version specified
### Step 2 ### Step 2
Either use `git clone https://github.com/ryujinx-mirror/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
@ -147,12 +135,26 @@ This folder is located in the user folder, which can be accessed by clicking `Op
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-mirror/Ryujinx/wiki/Frequently-Asked-Questions). You may also review our [FAQ](https://github.com/Ryujinx/Ryujinx/wiki/Frequently-Asked-Questions).
-->
## Donations
If you'd like to support the project financially, Ryujinx has an active Patreon campaign.
<a href="https://www.patreon.com/ryujinx">
<img src="https://images.squarespace-cdn.com/content/v1/560c1d39e4b0b4fae0c9cf2a/1567548955044-WVD994WZP76EWF15T0L3/Patreon+Button.png?format=500w" width="150">
</a>
All developers working on the project do so in their free time, but the project has several expenses:
* Hackable Nintendo Switch consoles to reverse-engineer the hardware
* Additional computer hardware for testing purposes (e.g. GPUs to diagnose graphical bugs, etc.)
* Licenses for various software development tools (e.g. Jetbrains, IDA)
* Web hosting and infrastructure maintenance (e.g. LDN servers)
All funds received through Patreon are considered a donation to support the project. Patrons receive early access to progress reports and exclusive access to developer interviews.
## License ## License

View File

@ -1,3 +0,0 @@
#!/bin/sh
CURRENTDIR="$(readlink -f "$(dirname "$0")")"
exec "$CURRENTDIR"/usr/bin/Ryujinx.sh "$@"

View File

@ -1,30 +0,0 @@
#!/bin/sh
set -eu
ROOTDIR="$(readlink -f "$(dirname "$0")")"/../../../
cd "$ROOTDIR"
BUILDDIR=${BUILDDIR:-publish}
OUTDIR=${OUTDIR:-publish_appimage}
UFLAG=${UFLAG:-"gh-releases-zsync|ryujinx-mirror|ryujinx|latest|*-x64.AppImage.zsync"}
rm -rf AppDir
mkdir -p AppDir/usr/bin
cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop
cp distribution/linux/appimage/AppRun AppDir/AppRun
cp distribution/misc/Logo.svg AppDir/Ryujinx.svg
cp -r "$BUILDDIR"/* AppDir/usr/bin/
# Ensure necessary bins are set as executable
chmod +x AppDir/AppRun AppDir/usr/bin/Ryujinx*
mkdir -p "$OUTDIR"
appimagetool --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
-u "$UFLAG" \
AppDir "$OUTDIR"/Ryujinx.AppImage
# Move zsync file needed for delta updates
mv ./*.AppImage.zsync "$OUTDIR"

View File

@ -35,7 +35,7 @@ EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx
rm -rf "$TEMP_DIRECTORY" rm -rf "$TEMP_DIRECTORY"
mkdir -p "$TEMP_DIRECTORY" mkdir -p "$TEMP_DIRECTORY"
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -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
@ -80,7 +80,7 @@ fi
$LIPO "$ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" "$X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -create $LIPO "$ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" "$X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -create
# Patch up the Info.plist to have appropriate version # Patch up the Info.plist to have appropriate version
#sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist" sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist" sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
rm "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck" rm "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck"
@ -110,11 +110,11 @@ 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. # Create legacy update package for Avalonia to not left behind old testers.
#if [ "$VERSION" != "1.1.0" ]; if [ "$VERSION" != "1.1.0" ];
#then then
# cp $RELEASE_TAR_FILE_NAME.gz test-ava-ryujinx-$VERSION-macos_universal.app.tar.gz cp $RELEASE_TAR_FILE_NAME.gz test-ava-ryujinx-$VERSION-macos_universal.app.tar.gz
#fi fi
popd popd
echo "Done" echo "Done"

View File

@ -35,7 +35,7 @@ EXECUTABLE_SUB_PATH=Ryujinx.Headless.SDL2
rm -rf "$TEMP_DIRECTORY" rm -rf "$TEMP_DIRECTORY"
mkdir -p "$TEMP_DIRECTORY" mkdir -p "$TEMP_DIRECTORY"
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -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.Headless.SDL2 dotnet build -c "$CONFIGURATION" src/Ryujinx.Headless.SDL2

View File

@ -5,7 +5,7 @@ Using an IDE that supports the `.editorconfig` standard will make this much simp
1. We use [Allman style](http://en.wikipedia.org/wiki/Indent_style#Allman_style) braces, where each brace begins on a new line. A single line statement block can go without braces but the block must be properly indented on its own line and must not be nested in other statement blocks that use braces (See rule 18 for more details). One exception is that a `using` statement is permitted to be nested within another `using` statement by starting on the following line at the same indentation level, even if the nested `using` contains a controlled block. 1. We use [Allman style](http://en.wikipedia.org/wiki/Indent_style#Allman_style) braces, where each brace begins on a new line. A single line statement block can go without braces but the block must be properly indented on its own line and must not be nested in other statement blocks that use braces (See rule 18 for more details). One exception is that a `using` statement is permitted to be nested within another `using` statement by starting on the following line at the same indentation level, even if the nested `using` contains a controlled block.
2. We use four spaces of indentation (no tabs). 2. We use four spaces of indentation (no tabs).
3. We use `_camelCase` for internal and private fields and use `readonly` where possible. Prefix internal and private instance fields with `_`, thread static fields with `t_`. When used on static fields, `readonly` should come after `static` (e.g. `static readonly` not `readonly static`). Public fields should be used sparingly and should use PascalCasing with no prefix when used. 3. We use `_camelCase` for internal and private fields and use `readonly` where possible. Prefix internal and private instance fields with `_`, static fields with `s_` and thread static fields with `t_`. When used on static fields, `readonly` should come after `static` (e.g. `static readonly` not `readonly static`). Public fields should be used sparingly and should use PascalCasing with no prefix when used.
4. We avoid `this.` unless absolutely necessary. 4. We avoid `this.` unless absolutely necessary.
5. We always specify the visibility, even if it's the default (e.g. 5. We always specify the visibility, even if it's the default (e.g.
`private string _foo` not `string _foo`). Visibility should be the first modifier (e.g. `private string _foo` not `string _foo`). Visibility should be the first modifier (e.g.

View File

@ -24,7 +24,7 @@ If during the code review process a merge conflict occurs, the PR author is resp
## Pull Request Builds ## Pull Request Builds
When submitting a PR to the `Ryujinx/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/ryujinx-mirror/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion. When submitting a PR to the `Ryujinx/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
## Review Turnaround Times ## Review Turnaround Times
@ -42,7 +42,7 @@ Anyone with write access can merge a pull request manually when the following co
* The PR has been approved by two reviewers and any other objections are addressed. * The PR has been approved by two reviewers and any other objections are addressed.
* You can request follow up reviews from the original reviewers if they requested changes. * You can request follow up reviews from the original reviewers if they requested changes.
* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/ryujinx-mirror/Ryujinx/actions) tab of your PR. * The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of your PR.
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them. Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.

View File

@ -72,6 +72,5 @@ namespace Ryujinx.Common.Logging
TamperMachine, TamperMachine,
UI, UI,
Vic, Vic,
XCIFileTrimmer
} }
} }

View File

@ -1,30 +0,0 @@
using Ryujinx.Common.Utilities;
namespace Ryujinx.Common.Logging
{
public class XCIFileTrimmerLog : XCIFileTrimmer.ILog
{
public virtual void Progress(long current, long total, string text, bool complete)
{
}
public void Write(XCIFileTrimmer.LogType logType, string text)
{
switch (logType)
{
case XCIFileTrimmer.LogType.Info:
Logger.Notice.Print(LogClass.XCIFileTrimmer, text);
break;
case XCIFileTrimmer.LogType.Warn:
Logger.Warning?.Print(LogClass.XCIFileTrimmer, text);
break;
case XCIFileTrimmer.LogType.Error:
Logger.Error?.Print(LogClass.XCIFileTrimmer, text);
break;
case XCIFileTrimmer.LogType.Progress:
Logger.Info?.Print(LogClass.XCIFileTrimmer, text);
break;
}
}
}
}

View File

@ -1,507 +0,0 @@
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace Ryujinx.Common.Utilities
{
internal static class Performance
{
internal static TimeSpan Measure(Action action)
{
var sw = new Stopwatch();
sw.Start();
try
{
action();
}
finally
{
sw.Stop();
}
return sw.Elapsed;
}
}
public sealed class XCIFileTrimmer
{
private const long BytesInAMegabyte = 1024 * 1024;
private const int BufferSize = 8 * (int)BytesInAMegabyte;
private const long CartSizeMBinFormattedGB = 952;
private const int CartKeyAreaSize = 0x1000;
private const byte PaddingByte = 0xFF;
private const int HeaderFilePos = 0x100;
private const int CartSizeFilePos = 0x10D;
private const int DataSizeFilePos = 0x118;
private const string HeaderMagicValue = "HEAD";
/// <summary>
/// Cartridge Sizes (ByteIdentifier, SizeInGB)
/// </summary>
private static readonly Dictionary<byte, long> _cartSizesGB = new()
{
{ 0xFA, 1 },
{ 0xF8, 2 },
{ 0xF0, 4 },
{ 0xE0, 8 },
{ 0xE1, 16 },
{ 0xE2, 32 }
};
private static long RecordsToByte(long records)
{
return 512 + (records * 512);
}
public static bool CanTrim(string filename, ILog log = null)
{
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
{
var trimmer = new XCIFileTrimmer(filename, log);
return trimmer.CanBeTrimmed;
}
return false;
}
public static bool CanUntrim(string filename, ILog log = null)
{
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
{
var trimmer = new XCIFileTrimmer(filename, log);
return trimmer.CanBeUntrimmed;
}
return false;
}
private ILog _log;
private string _filename;
private FileStream _fileStream;
private BinaryReader _binaryReader;
private long _offsetB, _dataSizeB, _cartSizeB, _fileSizeB;
private bool _fileOK = true;
private bool _freeSpaceChecked = false;
private bool _freeSpaceValid = false;
public enum OperationOutcome
{
InvalidXCIFile,
NoTrimNecessary,
NoUntrimPossible,
FreeSpaceCheckFailed,
FileIOWriteError,
ReadOnlyFileCannotFix,
FileSizeChanged,
Successful
}
public enum LogType
{
Info,
Warn,
Error,
Progress
}
public interface ILog
{
public void Write(LogType logType, string text);
public void Progress(long current, long total, string text, bool complete);
}
public bool FileOK => _fileOK;
public bool Trimmed => _fileOK && FileSizeB < UntrimmedFileSizeB;
public bool ContainsKeyArea => _offsetB != 0;
public bool CanBeTrimmed => _fileOK && FileSizeB > TrimmedFileSizeB;
public bool CanBeUntrimmed => _fileOK && FileSizeB < UntrimmedFileSizeB;
public bool FreeSpaceChecked => _fileOK && _freeSpaceChecked;
public bool FreeSpaceValid => _fileOK && _freeSpaceValid;
public long DataSizeB => _dataSizeB;
public long CartSizeB => _cartSizeB;
public long FileSizeB => _fileSizeB;
public long DiskSpaceSavedB => CartSizeB - FileSizeB;
public long DiskSpaceSavingsB => CartSizeB - DataSizeB;
public long TrimmedFileSizeB => _offsetB + _dataSizeB;
public long UntrimmedFileSizeB => _offsetB + _cartSizeB;
public ILog Log
{
get => _log;
set => _log = value;
}
public String Filename
{
get => _filename;
set
{
_filename = value;
Reset();
}
}
public long Pos
{
get => _fileStream.Position;
set => _fileStream.Position = value;
}
public XCIFileTrimmer(string path, ILog log = null)
{
Log = log;
Filename = path;
ReadHeader();
}
public void CheckFreeSpace()
{
if (FreeSpaceChecked)
return;
try
{
if (CanBeTrimmed)
{
_freeSpaceValid = false;
OpenReaders();
try
{
Pos = TrimmedFileSizeB;
bool freeSpaceValid = true;
long readSizeB = FileSizeB - TrimmedFileSizeB;
TimeSpan time = Performance.Measure(() =>
{
freeSpaceValid = CheckPadding(readSizeB);
});
if (time.TotalSeconds > 0)
{
Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)XCIFileTrimmer.BytesInAMegabyte / time.TotalSeconds:N} Mb/sec");
}
if (freeSpaceValid)
Log?.Write(LogType.Info, "Free space is valid");
_freeSpaceValid = freeSpaceValid;
}
finally
{
CloseReaders();
}
}
else
{
Log?.Write(LogType.Warn, "There is no free space to check.");
_freeSpaceValid = false;
}
}
finally
{
_freeSpaceChecked = true;
}
}
private bool CheckPadding(long readSizeB)
{
long maxReads = readSizeB / XCIFileTrimmer.BufferSize;
long read = 0;
var buffer = new byte[BufferSize];
while (true)
{
int bytes = _fileStream.Read(buffer, 0, XCIFileTrimmer.BufferSize);
if (bytes == 0)
break;
Log?.Progress(read, maxReads, "Verifying file can be trimmed", false);
if (buffer.Take(bytes).AsParallel().Any(b => b != XCIFileTrimmer.PaddingByte))
{
Log?.Write(LogType.Warn, "Free space is NOT valid");
return false;
}
read++;
}
return true;
}
private void Reset()
{
_freeSpaceChecked = false;
_freeSpaceValid = false;
ReadHeader();
}
public OperationOutcome Trim()
{
if (!FileOK)
{
return OperationOutcome.InvalidXCIFile;
}
if (!CanBeTrimmed)
{
return OperationOutcome.NoTrimNecessary;
}
if (!FreeSpaceChecked)
{
CheckFreeSpace();
}
if (!FreeSpaceValid)
{
return OperationOutcome.FreeSpaceCheckFailed;
}
Log?.Write(LogType.Info, "Trimming...");
try
{
var info = new FileInfo(Filename);
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
try
{
Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute");
File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly);
}
catch (Exception e)
{
Log?.Write(LogType.Error, e.ToString());
return OperationOutcome.ReadOnlyFileCannotFix;
}
}
if (info.Length != FileSizeB)
{
Log?.Write(LogType.Error, "File size has changed, cannot safely trim.");
return OperationOutcome.FileSizeChanged;
}
var outfileStream = new FileStream(_filename, FileMode.Open, FileAccess.Write, FileShare.Write);
try
{
outfileStream.SetLength(TrimmedFileSizeB);
return OperationOutcome.Successful;
}
finally
{
outfileStream.Close();
Reset();
}
}
catch (Exception e)
{
Log?.Write(LogType.Error, e.ToString());
return OperationOutcome.FileIOWriteError;
}
}
public OperationOutcome Untrim()
{
if (!FileOK)
{
return OperationOutcome.InvalidXCIFile;
}
if (!CanBeUntrimmed)
{
return OperationOutcome.NoUntrimPossible;
}
try
{
Log?.Write(LogType.Info, "Untrimming...");
var info = new FileInfo(Filename);
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
try
{
Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute");
File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly);
}
catch (Exception e)
{
Log?.Write(LogType.Error, e.ToString());
return OperationOutcome.ReadOnlyFileCannotFix;
}
}
if (info.Length != FileSizeB)
{
Log?.Write(LogType.Error, "File size has changed, cannot safely untrim.");
return OperationOutcome.FileSizeChanged;
}
var outfileStream = new FileStream(_filename, FileMode.Append, FileAccess.Write, FileShare.Write);
long bytesToWriteB = UntrimmedFileSizeB - FileSizeB;
try
{
TimeSpan time = Performance.Measure(() =>
{
WritePadding(outfileStream, bytesToWriteB);
});
if (time.TotalSeconds > 0)
{
Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)XCIFileTrimmer.BytesInAMegabyte / time.TotalSeconds:N} Mb/sec");
}
return OperationOutcome.Successful;
}
finally
{
outfileStream.Close();
Reset();
}
}
catch (Exception e)
{
Log?.Write(LogType.Error, e.ToString());
return OperationOutcome.FileIOWriteError;
}
}
private void WritePadding(FileStream outfileStream, long bytesToWriteB)
{
long bytesLeftToWriteB = bytesToWriteB;
long writes = bytesLeftToWriteB / XCIFileTrimmer.BufferSize;
int write = 0;
try
{
var buffer = new byte[BufferSize];
Array.Fill<byte>(buffer, XCIFileTrimmer.PaddingByte);
while (bytesLeftToWriteB > 0)
{
long bytesToWrite = Math.Min(XCIFileTrimmer.BufferSize, bytesLeftToWriteB);
outfileStream.Write(buffer, 0, (int)bytesToWrite);
bytesLeftToWriteB -= bytesToWrite;
Log?.Progress(write, writes, "Writing padding data...", false);
write++;
}
}
finally
{
Log?.Progress(write, writes, "Writing padding data...", true);
}
}
private void OpenReaders()
{
if (_binaryReader == null)
{
_fileStream = new FileStream(_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
_binaryReader = new BinaryReader(_fileStream);
}
}
private void CloseReaders()
{
if (_binaryReader != null && _binaryReader.BaseStream != null)
_binaryReader.Close();
_binaryReader = null;
_fileStream = null;
GC.Collect();
}
private void ReadHeader()
{
try
{
OpenReaders();
try
{
// Attempt without key area
bool success = CheckAndReadHeader(false);
if (!success)
{
// Attempt with key area
success = CheckAndReadHeader(true);
}
_fileOK = success;
}
finally
{
CloseReaders();
}
}
catch (Exception ex)
{
Log?.Write(LogType.Error, ex.Message);
_fileOK = false;
_dataSizeB = 0;
_cartSizeB = 0;
_fileSizeB = 0;
_offsetB = 0;
}
}
private bool CheckAndReadHeader(bool assumeKeyArea)
{
// Read file size
_fileSizeB = _fileStream.Length;
if (_fileSizeB < 32 * 1024)
{
Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the data size is too small");
return false;
}
// Setup offset
_offsetB = (long)(assumeKeyArea ? XCIFileTrimmer.CartKeyAreaSize : 0);
// Check header
Pos = _offsetB + XCIFileTrimmer.HeaderFilePos;
string head = System.Text.Encoding.ASCII.GetString(_binaryReader.ReadBytes(4));
if (head != XCIFileTrimmer.HeaderMagicValue)
{
if (!assumeKeyArea)
{
Log?.Write(LogType.Warn, $"Incorrect header found, file mat contain a key area...");
}
else
{
Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the header is corrupted");
}
return false;
}
// Read Cart Size
Pos = _offsetB + XCIFileTrimmer.CartSizeFilePos;
byte cartSizeId = _binaryReader.ReadByte();
if (!_cartSizesGB.TryGetValue(cartSizeId, out long cartSizeNGB))
{
Log?.Write(LogType.Error, $"The source file doesn't look like an XCI file as the Cartridge Size is incorrect (0x{cartSizeId:X2})");
return false;
}
_cartSizeB = cartSizeNGB * XCIFileTrimmer.CartSizeMBinFormattedGB * XCIFileTrimmer.BytesInAMegabyte;
// Read data size
Pos = _offsetB + XCIFileTrimmer.DataSizeFilePos;
long records = (long)BitConverter.ToUInt32(_binaryReader.ReadBytes(4), 0);
_dataSizeB = RecordsToByte(records);
return true;
}
}
}

View File

@ -55,10 +55,8 @@ namespace Ryujinx.Graphics.Vulkan
if (_handle != BufferHandle.Null) if (_handle != BufferHandle.Null)
{ {
// May need to restride the vertex buffer. // May need to restride the vertex buffer.
//
// Fix divide by zero when recovering from missed draw (Oct. 16 2024) if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
// (fixes crash in 'Baldo: The Guardian Owls' opening cutscene)
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && alignment != 0 && (_stride % alignment) != 0)
{ {
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment); autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);

View File

@ -165,11 +165,6 @@ namespace Ryujinx
? appDataConfigurationPath ? appDataConfigurationPath
: null; : null;
if (!string.IsNullOrEmpty(CommandLineState.OverrideConfigFile) && File.Exists(CommandLineState.OverrideConfigFile))
{
ConfigurationPath = CommandLineState.OverrideConfigFile;
}
if (ConfigurationPath == null) if (ConfigurationPath == null)
{ {
// No configuration, we load the default values and save it to disk // No configuration, we load the default values and save it to disk

View File

@ -134,7 +134,6 @@ namespace Ryujinx.UI
[GUI] ScrolledWindow _gameTableWindow; [GUI] ScrolledWindow _gameTableWindow;
[GUI] Label _gpuName; [GUI] Label _gpuName;
[GUI] Label _progressLabel; [GUI] Label _progressLabel;
[GUI] Label _progressStatusLabel;
[GUI] Label _firmwareVersionLabel; [GUI] Label _firmwareVersionLabel;
[GUI] Gtk.ProgressBar _progressBar; [GUI] Gtk.ProgressBar _progressBar;
[GUI] Box _viewBox; [GUI] Box _viewBox;
@ -728,34 +727,6 @@ namespace Ryujinx.UI
}); });
} }
public void StartProgress(string action)
{
Application.Invoke(delegate
{
_progressStatusLabel.Text = action;
_progressStatusLabel.Visible = true;
_progressBar.Fraction = 0;
});
}
public void UpdateProgress(double percentage)
{
Application.Invoke(delegate
{
_progressBar.Fraction = percentage;
});
}
public void EndProgress()
{
Application.Invoke(delegate
{
_progressStatusLabel.Text = String.Empty;
_progressStatusLabel.Visible = false;
_progressBar.Fraction = 1.0;
});
}
public void UpdateGameTable() public void UpdateGameTable()
{ {
if (_updatingGameTable || _gameLoaded) if (_updatingGameTable || _gameLoaded)

View File

@ -667,22 +667,6 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="_progressStatusLabel">
<property name="visible">False</property>
<property name="can-focus">False</property>
<property name="margin-left">10</property>
<property name="margin-right">5</property>
<property name="margin-top">2</property>
<property name="margin-bottom">2</property>
<property name="label" translatable="yes"></property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child> <child>
<object class="GtkProgressBar" id="_progressBar"> <object class="GtkProgressBar" id="_progressBar">
<property name="width-request">200</property> <property name="width-request">200</property>
@ -696,7 +680,7 @@
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">3</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>

View File

@ -25,7 +25,6 @@ namespace Ryujinx.UI.Widgets
private MenuItem _openPtcDirMenuItem; private MenuItem _openPtcDirMenuItem;
private MenuItem _openShaderCacheDirMenuItem; private MenuItem _openShaderCacheDirMenuItem;
private MenuItem _createShortcutMenuItem; private MenuItem _createShortcutMenuItem;
private MenuItem _trimXCIMenuItem;
private void InitializeComponent() private void InitializeComponent()
{ {
@ -199,15 +198,6 @@ namespace Ryujinx.UI.Widgets
}; };
_createShortcutMenuItem.Activated += CreateShortcut_Clicked; _createShortcutMenuItem.Activated += CreateShortcut_Clicked;
//
// _trimXCIMenuItem
//
_trimXCIMenuItem = new MenuItem("Check and Trim XCI File")
{
TooltipText = "Check and Trim XCI File to Save Disk Space."
};
_trimXCIMenuItem.Activated += TrimXCI_Clicked;
ShowComponent(); ShowComponent();
} }
@ -234,8 +224,6 @@ namespace Ryujinx.UI.Widgets
Add(_openTitleModDirMenuItem); Add(_openTitleModDirMenuItem);
Add(_openTitleSdModDirMenuItem); Add(_openTitleSdModDirMenuItem);
Add(new SeparatorMenuItem()); Add(new SeparatorMenuItem());
Add(_trimXCIMenuItem);
Add(new SeparatorMenuItem());
Add(_manageCacheMenuItem); Add(_manageCacheMenuItem);
Add(_extractMenuItem); Add(_extractMenuItem);

View File

@ -13,7 +13,6 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
@ -76,7 +75,6 @@ namespace Ryujinx.UI.Widgets
_extractLogoMenuItem.Sensitive = hasNca; _extractLogoMenuItem.Sensitive = hasNca;
_createShortcutMenuItem.Sensitive = !ReleaseInformation.IsFlatHubBuild; _createShortcutMenuItem.Sensitive = !ReleaseInformation.IsFlatHubBuild;
_trimXCIMenuItem.Sensitive = _applicationData != null && Ryujinx.Common.Utilities.XCIFileTrimmer.CanTrim(_applicationData.Path, new XCIFileTrimmerLog(_parent));
PopupAtPointer(null); PopupAtPointer(null);
} }
@ -632,91 +630,5 @@ namespace Ryujinx.UI.Widgets
byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_applicationData.Path, ConfigurationState.Instance.System.Language, _applicationData.Id); byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_applicationData.Path, ConfigurationState.Instance.System.Language, _applicationData.Id);
ShortcutHelper.CreateAppShortcut(_applicationData.Path, _applicationData.Name, _applicationData.IdString, appIcon); ShortcutHelper.CreateAppShortcut(_applicationData.Path, _applicationData.Name, _applicationData.IdString, appIcon);
} }
private void ProcessTrimResult(String filename, Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome operationOutcome)
{
string notifyUser = null;
switch (operationOutcome)
{
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.NoTrimNecessary:
notifyUser = "XCI File does not need to be trimmed. Check logs for further details";
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.ReadOnlyFileCannotFix:
notifyUser = "XCI File is Read Only and could not be made writable. Check logs for further details";
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.FreeSpaceCheckFailed:
notifyUser = "XCI File has data in the free space area, it is not safe to trim";
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.InvalidXCIFile:
notifyUser = "XCI File contains invalid data. Check logs for further details";
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.FileIOWriteError:
notifyUser = "XCI File could not be opened for writing. Check logs for further details";
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.FileSizeChanged:
notifyUser = "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.";
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.Successful:
_parent.UpdateGameTable();
break;
}
if (notifyUser != null)
{
GtkDialog.CreateWarningDialog("Trimming of the XCI file failed", notifyUser);
}
}
private void TrimXCI_Clicked(object sender, EventArgs args)
{
if (_applicationData?.Path == null)
{
return;
}
var trimmer = new XCIFileTrimmer(_applicationData.Path, new XCIFileTrimmerLog(_parent));
if (trimmer.CanBeTrimmed)
{
var savings = (double)trimmer.DiskSpaceSavingsB / 1024.0 / 1024.0;
var currentFileSize = (double)trimmer.FileSizeB / 1024.0 / 1024.0;
var cartDataSize = (double)trimmer.DataSizeB / 1024.0 / 1024.0;
using MessageDialog confirmationDialog = GtkDialog.CreateConfirmationDialog(
$"This function will first check the empty space and then trim the XCI File to save disk space. Continue?",
$"Current File Size: {currentFileSize:n} MB\n" +
$"Game Data Size: {cartDataSize:n} MB\n" +
$"Disk Space Savings: {savings:n} MB\n"
);
if (confirmationDialog.Run() == (int)ResponseType.Yes)
{
Thread xciFileTrimmerThread = new(() =>
{
_parent.StartProgress($"Trimming file '{_applicationData.Path}");
try
{
XCIFileTrimmer.OperationOutcome operationOutcome = trimmer.Trim();
Gtk.Application.Invoke(delegate
{
ProcessTrimResult(_applicationData.Path, operationOutcome);
});
}
finally
{
_parent.EndProgress();
}
})
{
Name = "GUI.XCIFileTrimmerThread",
IsBackground = true,
};
xciFileTrimmerThread.Start();
}
}
}
} }
} }

View File

@ -6,7 +6,7 @@ namespace Ryujinx.UI.Widgets
{ {
internal class UserErrorDialog : MessageDialog internal class UserErrorDialog : MessageDialog
{ {
private const string SetupGuideUrl = "https://github.com/ryujinx-mirror/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"; private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide";
private const int OkResponseId = 0; private const int OkResponseId = 0;
private const int SetupGuideResponseId = 1; private const int SetupGuideResponseId = 1;

View File

@ -14,6 +14,29 @@ namespace Ryujinx.UI.Windows
{ {
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(OpenHelper)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(OpenHelper)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png");
InitializeComponent(); InitializeComponent();
_ = DownloadPatronsJson();
}
private async Task DownloadPatronsJson()
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
_patreonNamesText.Buffer.Text = "Connection Error.";
}
HttpClient httpClient = new();
try
{
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
_patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray));
}
catch
{
_patreonNamesText.Buffer.Text = "API Error.";
}
} }
// //
@ -21,7 +44,7 @@ namespace Ryujinx.UI.Windows
// //
private void RyujinxButton_Pressed(object sender, ButtonPressEventArgs args) private void RyujinxButton_Pressed(object sender, ButtonPressEventArgs args)
{ {
OpenHelper.OpenUrl("https://example.com/"); OpenHelper.OpenUrl("https://ryujinx.org");
} }
private void AmiiboApiButton_Pressed(object sender, ButtonPressEventArgs args) private void AmiiboApiButton_Pressed(object sender, ButtonPressEventArgs args)
@ -31,32 +54,32 @@ namespace Ryujinx.UI.Windows
private void PatreonButton_Pressed(object sender, ButtonPressEventArgs args) private void PatreonButton_Pressed(object sender, ButtonPressEventArgs args)
{ {
OpenHelper.OpenUrl("https://example.com/"); OpenHelper.OpenUrl("https://www.patreon.com/ryujinx");
} }
private void GitHubButton_Pressed(object sender, ButtonPressEventArgs args) private void GitHubButton_Pressed(object sender, ButtonPressEventArgs args)
{ {
OpenHelper.OpenUrl("https://github.com/ryujinx-mirror/Ryujinx"); OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx");
} }
private void DiscordButton_Pressed(object sender, ButtonPressEventArgs args) private void DiscordButton_Pressed(object sender, ButtonPressEventArgs args)
{ {
OpenHelper.OpenUrl("https://discord.gg/xmHPGDfVCa"); OpenHelper.OpenUrl("https://discordapp.com/invite/N2FmfVc");
} }
private void TwitterButton_Pressed(object sender, ButtonPressEventArgs args) private void TwitterButton_Pressed(object sender, ButtonPressEventArgs args)
{ {
OpenHelper.OpenUrl("https://example.com/"); OpenHelper.OpenUrl("https://twitter.com/RyujinxEmu");
} }
private void ContributorsButton_Pressed(object sender, ButtonPressEventArgs args) private void ContributorsButton_Pressed(object sender, ButtonPressEventArgs args)
{ {
OpenHelper.OpenUrl("https://github.com/ryujinx-mirror/Ryujinx/graphs/contributors?type=a"); OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a");
} }
private void ChangelogButton_Pressed(object sender, ButtonPressEventArgs args) private void ChangelogButton_Pressed(object sender, ButtonPressEventArgs args)
{ {
OpenHelper.OpenUrl("https://github.com/ryujinx-mirror/Ryujinx/wiki/Changelog#ryujinx-changelog"); OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog");
} }
} }
} }

View File

@ -1,27 +0,0 @@
using Ryujinx.Common.Logging;
using System;
namespace Ryujinx.UI
{
internal class XCIFileTrimmerLog : Ryujinx.Common.Logging.XCIFileTrimmerLog
{
private readonly MainWindow _mainWindow;
public XCIFileTrimmerLog(MainWindow mainWindow)
{
_mainWindow = mainWindow;
}
public override void Progress(long current, long total, string text, bool complete)
{
if (!complete)
{
_mainWindow.UpdateProgress((double)current / (double)total);
}
else
{
_mainWindow.EndProgress();
}
}
}
}

View File

@ -13,7 +13,6 @@ namespace Ryujinx.HLE.Generators
var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver; var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
CodeGenerator generator = new CodeGenerator(); CodeGenerator generator = new CodeGenerator();
generator.AppendLine("#nullable enable");
generator.AppendLine("using System;"); generator.AppendLine("using System;");
generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm"); generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
generator.EnterScope($"partial class IUserInterface"); generator.EnterScope($"partial class IUserInterface");
@ -59,7 +58,6 @@ namespace Ryujinx.HLE.Generators
generator.LeaveScope(); generator.LeaveScope();
generator.LeaveScope(); generator.LeaveScope();
generator.AppendLine("#nullable disable");
context.AddSource($"IUserInterface.g.cs", generator.ToString()); context.AddSource($"IUserInterface.g.cs", generator.ToString());
} }

View File

@ -659,7 +659,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
if (string.IsNullOrWhiteSpace(filePath)) if (string.IsNullOrWhiteSpace(filePath))
{ {
throw new InvalidSystemResourceException("JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/ryujinx-mirror/Ryujinx#requirements for more information)"); throw new InvalidSystemResourceException("JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)");
} }
context.Device.LoadNca(filePath); context.Device.LoadNca(filePath);

View File

@ -105,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
titleName = "Unknown"; titleName = "Unknown";
} }
throw new InvalidSystemResourceException($"{titleName} ({fontTitle:x8}) system title not found! This font will not work, provide the system archive to fix this error. (See https://github.com/ryujinx-mirror/Ryujinx#requirements for more information)"); throw new InvalidSystemResourceException($"{titleName} ({fontTitle:x8}) system title not found! This font will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)");
} }
} }
else else

View File

@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
{ {
private const long CertStoreTitleId = 0x0100000000000800; private const long CertStoreTitleId = 0x0100000000000800;
private const string CertStoreTitleMissingErrorMessage = "CertStore system title not found! SSL CA retrieving will not work, provide the system archive to fix this error. (See https://github.com/ryujinx-mirror/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; private const string CertStoreTitleMissingErrorMessage = "CertStore system title not found! SSL CA retrieving will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)";
private static BuiltInCertificateManager _instance; private static BuiltInCertificateManager _instance;

View File

@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
private const long TimeZoneBinaryTitleId = 0x010000000000080E; private const long TimeZoneBinaryTitleId = 0x010000000000080E;
private const string TimeZoneSystemTitleMissingErrorMessage = "TimeZoneBinary system title not found! TimeZone conversions will not work, provide the system archive to fix this error. (See https://github.com/ryujinx-mirror/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; private const string TimeZoneSystemTitleMissingErrorMessage = "TimeZoneBinary system title not found! TimeZone conversions will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)";
private VirtualFileSystem _virtualFileSystem; private VirtualFileSystem _virtualFileSystem;
private IntegrityCheckLevel _fsIntegrityCheckLevel; private IntegrityCheckLevel _fsIntegrityCheckLevel;

View File

@ -313,32 +313,6 @@ namespace Ryujinx.Input.SDL2
return value * ConvertRate; return value * ConvertRate;
} }
private JoyconConfigControllerStick<GamepadInputId, Common.Configuration.Hid.Controller.StickInputId> GetLogicalJoyStickConfig(StickInputId inputId)
{
switch (inputId)
{
case StickInputId.Left:
if (_configuration.RightJoyconStick.Joystick == Common.Configuration.Hid.Controller.StickInputId.Left)
{
return _configuration.RightJoyconStick;
}
else
{
return _configuration.LeftJoyconStick;
}
case StickInputId.Right:
if (_configuration.LeftJoyconStick.Joystick == Common.Configuration.Hid.Controller.StickInputId.Right)
{
return _configuration.LeftJoyconStick;
}
else
{
return _configuration.RightJoyconStick;
}
}
return null;
}
public (float, float) GetStick(StickInputId inputId) public (float, float) GetStick(StickInputId inputId)
{ {
if (inputId == StickInputId.Unbound) if (inputId == StickInputId.Unbound)
@ -369,26 +343,24 @@ namespace Ryujinx.Input.SDL2
if (HasConfiguration) if (HasConfiguration)
{ {
var joyconStickConfig = GetLogicalJoyStickConfig(inputId); if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.InvertStickX) ||
(inputId == StickInputId.Right && _configuration.RightJoyconStick.InvertStickX))
if (joyconStickConfig != null)
{ {
if (joyconStickConfig.InvertStickX) resultX = -resultX;
{ }
resultX = -resultX;
}
if (joyconStickConfig.InvertStickY) if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.InvertStickY) ||
{ (inputId == StickInputId.Right && _configuration.RightJoyconStick.InvertStickY))
resultY = -resultY; {
} resultY = -resultY;
}
if (joyconStickConfig.Rotate90CW) if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.Rotate90CW) ||
{ (inputId == StickInputId.Right && _configuration.RightJoyconStick.Rotate90CW))
float temp = resultX; {
resultX = resultY; float temp = resultX;
resultY = -temp; resultX = resultY;
} resultY = -temp;
} }
} }

View File

@ -1,6 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
namespace Ryujinx.UI.Common.Helper namespace Ryujinx.UI.Common.Helper
{ {
@ -17,7 +16,6 @@ namespace Ryujinx.UI.Common.Helper
public static string LaunchPathArg { get; private set; } public static string LaunchPathArg { get; private set; }
public static string LaunchApplicationId { get; private set; } public static string LaunchApplicationId { get; private set; }
public static bool StartFullscreenArg { get; private set; } public static bool StartFullscreenArg { get; private set; }
public static string OverrideConfigFile { get; private set; }
public static void ParseArguments(string[] args) public static void ParseArguments(string[] args)
{ {
@ -98,29 +96,6 @@ namespace Ryujinx.UI.Common.Helper
case "--software-gui": case "--software-gui":
OverrideHardwareAcceleration = false; OverrideHardwareAcceleration = false;
break; break;
case "-c":
case "--config":
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
string configFile = args[++i];
if (Path.GetExtension(configFile).ToLower() != ".json")
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
OverrideConfigFile = configFile;
arguments.Add(arg);
arguments.Add(args[i]);
break;
default: default:
LaunchPathArg = arg; LaunchPathArg = arg;
break; break;

View File

@ -82,11 +82,8 @@
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods", "GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
"GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", "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.", "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
"StatusBarGamesLoaded": "{0}/{1} Games Loaded", "StatusBarGamesLoaded": "{0}/{1} Games Loaded",
"StatusBarSystemVersion": "System Version: {0}", "StatusBarSystemVersion": "System Version: {0}",
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected", "LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
"LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}", "LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}",
"LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.", "LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.",
@ -707,16 +704,6 @@
"SelectDlcDialogTitle": "Select DLC files", "SelectDlcDialogTitle": "Select DLC files",
"SelectUpdateDialogTitle": "Select update files", "SelectUpdateDialogTitle": "Select update files",
"SelectModDialogTitle": "Select mod directory", "SelectModDialogTitle": "Select mod directory",
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
"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})",
@ -727,7 +714,6 @@
"DlcWindowHeading": "{0} Downloadable Content(s)", "DlcWindowHeading": "{0} Downloadable Content(s)",
"ModWindowHeading": "{0} Mod(s)", "ModWindowHeading": "{0} Mod(s)",
"UserProfilesEditProfile": "Edit Selected", "UserProfilesEditProfile": "Edit Selected",
"Continue": "Continue",
"Cancel": "Cancel", "Cancel": "Cancel",
"Save": "Save", "Save": "Save",
"Discard": "Discard", "Discard": "Discard",

View File

@ -1,24 +0,0 @@
using Ryujinx.Ava.UI.ViewModels;
namespace Ryujinx.Ava.Common
{
internal class XCIFileTrimmerLog : Ryujinx.Common.Logging.XCIFileTrimmerLog
{
private readonly MainWindowViewModel _viewModel;
public XCIFileTrimmerLog(MainWindowViewModel viewModel)
{
_viewModel = viewModel;
}
public override void Progress(long current, long total, string text, bool complete)
{
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{
_viewModel.StatusBarProgressMaximum = (int)(total);
_viewModel.StatusBarProgressValue = (int)(current);
});
}
}
}

View File

@ -36,6 +36,7 @@ namespace Ryujinx.Modules
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); 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 const int ConnectionCount = 4;
private static string _buildVer; private static string _buildVer;
@ -631,7 +632,7 @@ namespace Ryujinx.Modules
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
}); });
MoveAllFilesOver(_updateDir, _homeDir, taskDialog); MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
Directory.Delete(_updateDir, true); Directory.Delete(_updateDir, true);
} }
@ -708,7 +709,7 @@ namespace Ryujinx.Modules
{ {
// Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. // 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 oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var newFiles = Directory.EnumerateFiles(_updateDir, "*", 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)); var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename));
// Remove user files from the paths in files. // Remove user files from the paths in files.

View File

@ -150,11 +150,6 @@ namespace Ryujinx.Ava
ConfigurationPath = appDataConfigurationPath; ConfigurationPath = appDataConfigurationPath;
} }
if (!string.IsNullOrEmpty(CommandLineState.OverrideConfigFile) && File.Exists(CommandLineState.OverrideConfigFile))
{
ConfigurationPath = CommandLineState.OverrideConfigFile;
}
if (ConfigurationPath == null) if (ConfigurationPath == null)
{ {
// No configuration, we load the default values and save it to disk // No configuration, we load the default values and save it to disk

View File

@ -4,7 +4,7 @@
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.1.0-dirty</Version> <Version>1.0.0-dirty</Version>
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants> <DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
<SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate> <SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate>
<ApplicationIcon>Ryujinx.ico</ApplicationIcon> <ApplicationIcon>Ryujinx.ico</ApplicationIcon>

View File

@ -59,12 +59,6 @@
Click="OpenSdModsDirectory_Click" Click="OpenSdModsDirectory_Click"
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}" Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
<Separator />
<MenuItem
Click="TrimXCI_Click"
Header="{locale:Locale GameListContextMenuTrimXCI}"
IsEnabled="{Binding TrimXCIEnabled}"
ToolTip.Tip="{locale:Locale GameListContextMenuTrimXCIToolTip}" />
<Separator /> <Separator />
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}"> <MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
<MenuItem <MenuItem

View File

@ -1,7 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
@ -16,8 +15,6 @@ using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Path = System.IO.Path; using Path = System.IO.Path;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
@ -358,15 +355,5 @@ namespace Ryujinx.Ava.UI.Controls
await viewModel.LoadApplication(viewModel.SelectedApplication); await viewModel.LoadApplication(viewModel.SelectedApplication);
} }
} }
public async void TrimXCI_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await viewModel.TrimXCIFile(viewModel.SelectedApplication.Path);
}
}
} }
} }

View File

@ -7,7 +7,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
internal class UserErrorDialog internal class UserErrorDialog
{ {
private const string SetupGuideUrl = "https://github.com/ryujinx-mirror/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"; private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide";
private static string GetErrorCode(UserError error) private static string GetErrorCode(UserError error)
{ {

View File

@ -45,6 +45,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private PlayerIndex _playerId; private PlayerIndex _playerId;
private int _controller; private int _controller;
private int _controllerNumber;
private string _controllerImage; private string _controllerImage;
private int _device; private int _device;
private object _configViewModel; private object _configViewModel;
@ -438,24 +439,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public void LoadDevices() public void LoadDevices()
{ {
string GetGamepadName(IGamepad gamepad, int controllerNumber)
{
return $"{GetShortGamepadName(gamepad.Name)} ({controllerNumber})";
}
string GetUniqueGamepadName(IGamepad gamepad, ref int controllerNumber)
{
string name = GetGamepadName(gamepad, controllerNumber);
if (Devices.Any(controller => controller.Name == name))
{
controllerNumber++;
name = GetGamepadName(gamepad, controllerNumber);
}
return name;
}
lock (Devices) lock (Devices)
{ {
Devices.Clear(); Devices.Clear();
@ -472,18 +455,23 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
int controllerNumber = 0;
foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds) foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds)
{ {
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id); using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
if (gamepad != null) if (gamepad != null)
{ {
string name = GetUniqueGamepadName(gamepad, ref controllerNumber); if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
Devices.Add((DeviceType.Controller, id, name)); {
_controllerNumber++;
}
Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({_controllerNumber})"));
} }
} }
_controllerNumber = 0;
DeviceList.AddRange(Devices.Select(x => x.Name)); DeviceList.AddRange(Devices.Select(x => x.Name));
Device = Math.Min(Device, DeviceList.Count); Device = Math.Min(Device, DeviceList.Count);
} }
@ -697,7 +685,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (!File.Exists(path)) if (!File.Exists(path))
{ {
int index = ProfilesList.IndexOf(ProfileName); var index = ProfilesList.IndexOf(ProfileName);
if (index != -1) if (index != -1)
{ {
ProfilesList.RemoveAt(index); ProfilesList.RemoveAt(index);

View File

@ -20,7 +20,6 @@ using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@ -37,7 +36,6 @@ using SkiaSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
@ -80,8 +78,6 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _isAppletMenuActive; private bool _isAppletMenuActive;
private int _statusBarProgressMaximum; private int _statusBarProgressMaximum;
private int _statusBarProgressValue; private int _statusBarProgressValue;
private string _statusBarProgressStatusText;
private bool _statusBarProgressStatusVisible;
private bool _isPaused; private bool _isPaused;
private bool _showContent = true; private bool _showContent = true;
private bool _isLoadingIndeterminate = true; private bool _isLoadingIndeterminate = true;
@ -370,8 +366,6 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
public bool TrimXCIEnabled => Ryujinx.Common.Utilities.XCIFileTrimmer.CanTrim(SelectedApplication.Path, new Common.XCIFileTrimmerLog(this));
public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild; public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild;
@ -486,28 +480,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool StatusBarProgressStatusVisible
{
get => _statusBarProgressStatusVisible;
set
{
_statusBarProgressStatusVisible = value;
OnPropertyChanged();
}
}
public string StatusBarProgressStatusText
{
get => _statusBarProgressStatusText;
set
{
_statusBarProgressStatusText = value;
OnPropertyChanged();
}
}
public string FifoStatusText public string FifoStatusText
{ {
get => _fifoStatusText; get => _fifoStatusText;
@ -1775,114 +1747,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
} }
public async void ProcessTrimResult(String filename, Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome operationOutcome)
{
string notifyUser = null;
switch (operationOutcome)
{
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.NoTrimNecessary:
notifyUser = LocaleManager.Instance[LocaleKeys.TrimXCIFileNoTrimNecessary];
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.ReadOnlyFileCannotFix:
notifyUser = LocaleManager.Instance[LocaleKeys.TrimXCIFileReadOnlyFileCannotFix];
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.FreeSpaceCheckFailed:
notifyUser = LocaleManager.Instance[LocaleKeys.TrimXCIFileFreeSpaceCheckFailed];
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.InvalidXCIFile:
notifyUser = LocaleManager.Instance[LocaleKeys.TrimXCIFileInvalidXCIFile];
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.FileIOWriteError:
notifyUser = LocaleManager.Instance[LocaleKeys.TrimXCIFileFileIOWriteError];
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.FileSizeChanged:
notifyUser = LocaleManager.Instance[LocaleKeys.TrimXCIFileFileSizeChanged];
break;
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.Successful:
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
if (desktop.MainWindow is MainWindow mainWindow)
mainWindow.LoadApplications();
}
break;
}
if (notifyUser != null)
{
await ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.TrimXCIFileFailedPrimaryText],
notifyUser
);
}
}
public async Task TrimXCIFile(string filename)
{
if (filename == null)
{
return;
}
var trimmer = new XCIFileTrimmer(filename, new Common.XCIFileTrimmerLog(this));
if (trimmer.CanBeTrimmed)
{
var savings = (double)trimmer.DiskSpaceSavingsB / 1024.0 / 1024.0;
var currentFileSize = (double)trimmer.FileSizeB / 1024.0 / 1024.0;
var cartDataSize = (double)trimmer.DataSizeB / 1024.0 / 1024.0;
string secondaryText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TrimXCIFileDialogSecondaryText, currentFileSize, cartDataSize, savings);
var result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.TrimXCIFileDialogPrimaryText],
secondaryText,
LocaleManager.Instance[LocaleKeys.Continue],
LocaleManager.Instance[LocaleKeys.Cancel],
LocaleManager.Instance[LocaleKeys.TrimXCIFileDialogTitle]
);
if (result == UserResult.Yes)
{
Thread XCIFileTrimThread = new(() =>
{
Dispatcher.UIThread.Post(() =>
{
StatusBarProgressStatusText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarXCIFileTrimming, Path.GetFileName(filename));
StatusBarProgressStatusVisible = true;
StatusBarProgressMaximum = 1;
StatusBarProgressValue = 0;
StatusBarVisible = true;
});
try
{
XCIFileTrimmer.OperationOutcome operationOutcome = trimmer.Trim();
Dispatcher.UIThread.Post(() =>
{
ProcessTrimResult(filename, operationOutcome);
});
}
finally
{
Dispatcher.UIThread.Post(() =>
{
StatusBarProgressStatusVisible = false;
StatusBarProgressStatusText = string.Empty;
StatusBarVisible = false;
});
}
})
{
Name = "GUI.XCFileTrimmerThread",
IsBackground = true,
};
XCIFileTrimThread.Start();
}
}
}
#endregion #endregion
} }
} }

View File

@ -193,7 +193,6 @@
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}"> <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<!--
<MenuItem <MenuItem
Name="UpdateMenuItem" Name="UpdateMenuItem"
IsEnabled="{Binding CanUpdate}" IsEnabled="{Binding CanUpdate}"
@ -201,7 +200,6 @@
Header="{locale:Locale MenuBarHelpCheckForUpdates}" Header="{locale:Locale MenuBarHelpCheckForUpdates}"
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" /> ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
<Separator /> <Separator />
-->
<MenuItem <MenuItem
Click="OpenAboutWindow" Click="OpenAboutWindow"
Header="{locale:Locale MenuBarHelpAbout}" Header="{locale:Locale MenuBarHelpAbout}"

View File

@ -36,7 +36,6 @@
IsVisible="{Binding EnableNonGameRunningControls}"> IsVisible="{Binding EnableNonGameRunningControls}">
<Grid Margin="0"> <Grid Margin="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition /> <ColumnDefinition />
@ -61,16 +60,9 @@
VerticalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding EnableNonGameRunningControls}" IsVisible="{Binding EnableNonGameRunningControls}"
Text="{locale:Locale StatusBarGamesLoaded}" /> Text="{locale:Locale StatusBarGamesLoaded}" />
<TextBlock
Name="StatusBarProgressStatus"
Grid.Column="2"
Margin="10,0,5,0"
VerticalAlignment="Center"
IsVisible="{Binding StatusBarProgressStatusVisible}"
Text="{Binding StatusBarProgressStatusText}" />
<ProgressBar <ProgressBar
Name="LoadProgressBar" Name="LoadProgressBar"
Grid.Column="3" Grid.Column="2"
Height="6" Height="6"
VerticalAlignment="Center" VerticalAlignment="Center"
Foreground="{DynamicResource SystemAccentColorLight2}" Foreground="{DynamicResource SystemAccentColorLight2}"

View File

@ -88,7 +88,7 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
Background="Transparent" Background="Transparent"
Click="Button_OnClick" Click="Button_OnClick"
Tag="https://github.com/ryujinx-mirror/Ryujinx/wiki/Changelog#ryujinx-changelog"> Tag="https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog">
<TextBlock <TextBlock
FontSize="10" FontSize="10"
Text="{locale:Locale AboutChangelogButton}" Text="{locale:Locale AboutChangelogButton}"
@ -123,7 +123,6 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
Orientation="Horizontal" Orientation="Horizontal"
Spacing="10"> Spacing="10">
<!--
<Button <Button
MinWidth="30" MinWidth="30"
MinHeight="30" MinHeight="30"
@ -133,11 +132,10 @@
Background="Transparent" Background="Transparent"
Click="Button_OnClick" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Tag="https://example.com/" Tag="https://www.patreon.com/ryujinx"
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}"> ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
<Image Source="{Binding PatreonLogo}" /> <Image Source="{Binding PatreonLogo}" />
</Button> </Button>
-->
<Button <Button
MinWidth="30" MinWidth="30"
MinHeight="30" MinHeight="30"
@ -147,7 +145,7 @@
Background="Transparent" Background="Transparent"
Click="Button_OnClick" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Tag="https://github.com/ryujinx-mirror/Ryujinx" Tag="https://github.com/Ryujinx/Ryujinx"
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}"> ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
<Image Source="{Binding GithubLogo}" /> <Image Source="{Binding GithubLogo}" />
</Button> </Button>
@ -160,11 +158,10 @@
Background="Transparent" Background="Transparent"
Click="Button_OnClick" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Tag="https://discord.gg/xmHPGDfVCa" Tag="https://discordapp.com/invite/N2FmfVc"
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}"> ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
<Image Source="{Binding DiscordLogo}" /> <Image Source="{Binding DiscordLogo}" />
</Button> </Button>
<!--
<Button <Button
MinWidth="30" MinWidth="30"
MinHeight="30" MinHeight="30"
@ -191,7 +188,6 @@
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}"> ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}">
<ui:SymbolIcon Foreground="{DynamicResource ThemeForegroundColor}" Symbol="Link" /> <ui:SymbolIcon Foreground="{DynamicResource ThemeForegroundColor}" Symbol="Link" />
</Button> </Button>
-->
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
@ -221,7 +217,7 @@
Text="{locale:Locale AboutRyujinxAboutTitle}" /> Text="{locale:Locale AboutRyujinxAboutTitle}" />
<TextBlock <TextBlock
FontSize="10" FontSize="10"
Text="Ryujinx is an emulator for the Nintendo Switch™." Text="{locale:Locale AboutRyujinxAboutContent}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
@ -231,7 +227,7 @@
<TextBlock <TextBlock
FontSize="15" FontSize="15"
FontWeight="Bold" FontWeight="Bold"
Text="Created By:" /> Text="{locale:Locale AboutRyujinxMaintainersTitle}" />
<TextBlock <TextBlock
FontSize="10" FontSize="10"
Text="{Binding Developers}" Text="{Binding Developers}"
@ -241,7 +237,7 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
Background="Transparent" Background="Transparent"
Click="Button_OnClick" Click="Button_OnClick"
Tag="https://github.com/ryujinx-mirror/Ryujinx/graphs/contributors?type=a"> Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
<TextBlock <TextBlock
FontSize="10" FontSize="10"
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}" Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
@ -249,7 +245,6 @@
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" /> ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
</Button> </Button>
</StackPanel> </StackPanel>
<!--
<StackPanel <StackPanel
Grid.Row="2" Grid.Row="2"
Margin="0,10,0,0" Margin="0,10,0,0"
@ -270,7 +265,6 @@
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</ScrollViewer> </ScrollViewer>
</StackPanel> </StackPanel>
-->
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -361,8 +361,6 @@ namespace Ryujinx.Ava.UI.Windows
await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys)); await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
} }
// MIRROR ADJ: We aren't using semver release tags for the time being
/*
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
{ {
await Updater.BeginParse(this, false).ContinueWith(task => await Updater.BeginParse(this, false).ContinueWith(task =>
@ -370,7 +368,6 @@ namespace Ryujinx.Ava.UI.Windows
Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"); Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}");
}, TaskContinuationOptions.OnlyOnFaulted); }, TaskContinuationOptions.OnlyOnFaulted);
} }
*/
} }
private void Load() private void Load()