Compare commits

..

2 Commits

Author SHA1 Message Date
Evan Husted
9ef57edfd1 Undo a testing change 2024-12-24 14:38:24 -06:00
Evan Husted
1360ef2ec8 WIP AMOLED theme. Likely will not continue working on this as the only way forward is a hacky mess. 2024-12-24 14:35:49 -06:00
200 changed files with 1815 additions and 3225 deletions

View File

@@ -64,9 +64,14 @@ jobs:
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Headless.SDL2
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Set executable bit - name: Set executable bit
run: | run: |
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest' if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
- name: Build AppImage - name: Build AppImage
@@ -114,6 +119,13 @@ jobs:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage
path: publish_appimage path: publish_appimage
- name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v4
with:
name: nogui-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish_sdl2_headless
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
build_macos: build_macos:
name: macOS Universal (${{ matrix.configuration }}) name: macOS Universal (${{ matrix.configuration }})
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -159,9 +171,20 @@ jobs:
run: | run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER" ./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
- name: Publish macOS Ryujinx.Headless.SDL2
run: |
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
- name: Upload Ryujinx artifact - name: Upload Ryujinx artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-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' if: github.event_name == 'pull_request'
- name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v4
with:
name: nogui-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_headless/*.tar.gz"
if: github.event_name == 'pull_request'

View File

@@ -21,9 +21,9 @@ env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.2" RYUJINX_BASE_VERSION: "1.2"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary" RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing" RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx" RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Canary-Releases" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx-Canary"
RELEASE: 1 RELEASE: 1
jobs: jobs:
@@ -43,8 +43,8 @@ jobs:
with: with:
script: | script: |
github.rest.git.createRef({ github.rest.git.createRef({
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}", owner: context.repo.owner,
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}", repo: context.repo.repo,
ref: 'refs/tags/Canary-${{ steps.version_info.outputs.build_version }}', ref: 'refs/tags/Canary-${{ steps.version_info.outputs.build_version }}',
sha: context.sha sha: context.sha
}) })
@@ -64,7 +64,7 @@ jobs:
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | | Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | | Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Canary macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | | macOS | [Canary macOS artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }} **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}
omitBodyDuringUpdate: true omitBodyDuringUpdate: true
@@ -116,6 +116,7 @@ jobs:
- name: Publish - name: Publish
run: | run: |
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained
- name: Packing Windows builds - name: Packing Windows builds
if: matrix.platform.os == 'windows-latest' if: matrix.platform.os == 'windows-latest'
@@ -124,6 +125,11 @@ jobs:
rm publish/libarmeilleure-jitsupport.dylib rm publish/libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish 7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
popd popd
pushd publish_sdl2_headless
rm publish/libarmeilleure-jitsupport.dylib
7z a ../release_output/nogui-ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
popd
shell: bash shell: bash
- name: Packing Linux builds - name: Packing Linux builds
@@ -134,6 +140,12 @@ jobs:
chmod +x publish/Ryujinx.sh publish/Ryujinx chmod +x publish/Ryujinx.sh publish/Ryujinx
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
popd popd
pushd publish_sdl2_headless
rm publish/libarmeilleure-jitsupport.dylib
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
tar -czvf ../release_output/nogui-ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
popd
shell: bash shell: bash
#- name: Build AppImage (Linux) #- name: Build AppImage (Linux)
@@ -179,21 +191,21 @@ jobs:
with: with:
name: ${{ steps.version_info.outputs.build_version }} name: ${{ steps.version_info.outputs.build_version }}
artifacts: "release_output/*.tar.gz,release_output/*.zip" artifacts: "release_output/*.tar.gz,release_output/*.zip"
#artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*" #artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*"
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
body: | body: |
# Canary builds: # Canary builds:
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/${{ github.repository }}/releases/latest) instead if that sounds like something you don't want to deal with. These builds are experimental and may sometimes not work, use [regular builds](https://github.com/GreemDev/Ryujinx/releases/latest) instead if that sounds like something you don't want to deal with.
| Platform | Artifact | | Platform | Artifact |
|--|--| |--|--|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | | Windows 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | | Linux 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | Linux ARM 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| macOS | [Canary macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | | macOS | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }} "**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
omitBodyDuringUpdate: true omitBodyDuringUpdate: true
allowUpdates: true allowUpdates: true
replacesArtifacts: true replacesArtifacts: true
@@ -250,11 +262,15 @@ jobs:
run: | run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1 ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
- name: Publish macOS Ryujinx.Headless.SDL2
run: |
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
- name: Pushing new release - name: Pushing new release
uses: ncipollo/release-action@v1 uses: ncipollo/release-action@v1
with: with:
name: "Canary ${{ steps.version_info.outputs.build_version }}" name: "Canary ${{ steps.version_info.outputs.build_version }}"
artifacts: "publish_ava/*.tar.gz" artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
body: "" body: ""
omitBodyDuringUpdate: true omitBodyDuringUpdate: true

View File

@@ -38,16 +38,20 @@ jobs:
return core.error(`No artifacts found`); return core.error(`No artifacts found`);
} }
let body = `Download the artifacts for this pull request:\n`; let body = `Download the artifacts for this pull request:\n`;
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less</summary>\n`;
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`; let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
for (const art of artifacts) { for (const art of artifacts) {
const url = `https://github.com/Ryubing/Ryujinx/actions/runs/${run_id}/artifacts/${art.id}`;
if(art.name.includes('Debug')) { if(art.name.includes('Debug')) {
hidden_debug_artifacts += `\n* [${art.name}](${url})`; hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else if(art.name.includes('nogui-ryujinx')) {
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else { } else {
body += `\n* [${art.name}](${url})`; body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} }
} }
hidden_headless_artifacts += `\n</details>`;
hidden_debug_artifacts += `\n</details>`; hidden_debug_artifacts += `\n</details>`;
body += hidden_headless_artifacts;
body += hidden_debug_artifacts; body += hidden_debug_artifacts;
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number}); const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});

View File

@@ -21,7 +21,7 @@ env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.2" RYUJINX_BASE_VERSION: "1.2"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release" RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing" RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
RELEASE: 1 RELEASE: 1
@@ -42,8 +42,8 @@ jobs:
with: with:
script: | script: |
github.rest.git.createRef({ github.rest.git.createRef({
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}", owner: context.repo.owner,
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}", repo: context.repo.repo,
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}', ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
sha: context.sha sha: context.sha
}) })
@@ -54,13 +54,13 @@ jobs:
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 }}
body: | body: |
# Stable builds: # Regular builds:
| Platform | Artifact | | Platform | Artifact |
|--|--| |--|--|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | | Windows 64-bit | [Release Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | | Linux 64-bit | [Release Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | Linux ARM 64-bit | [Release Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | | macOS | [Release macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }} **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
omitBodyDuringUpdate: true omitBodyDuringUpdate: true
@@ -112,6 +112,7 @@ jobs:
- name: Publish - name: Publish
run: | run: |
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained
- name: Packing Windows builds - name: Packing Windows builds
if: matrix.platform.os == 'windows-latest' if: matrix.platform.os == 'windows-latest'
@@ -120,6 +121,11 @@ jobs:
rm libarmeilleure-jitsupport.dylib rm libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd popd
pushd publish_sdl2_headless
rm libarmeilleure-jitsupport.dylib
7z a ../release_output/nogui-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd
shell: bash shell: bash
- name: Build AppImage (Linux) - name: Build AppImage (Linux)
@@ -166,6 +172,11 @@ jobs:
chmod +x Ryujinx.sh Ryujinx chmod +x Ryujinx.sh Ryujinx
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd popd
pushd publish_sdl2_headless
chmod +x Ryujinx.sh Ryujinx.Headless.SDL2
tar -czvf ../release_output/nogui-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd
shell: bash shell: bash
- name: Pushing new release - name: Pushing new release
@@ -175,15 +186,15 @@ jobs:
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*" artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
body: | body: |
# Stable builds: # Regular builds:
| Platform | Artifact | | Platform | Artifact |
|--|--| |--|--|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | | Windows 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | | Linux 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | Linux ARM 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | | macOS | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }} "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
omitBodyDuringUpdate: true omitBodyDuringUpdate: true
allowUpdates: true allowUpdates: true
replacesArtifacts: true replacesArtifacts: true
@@ -240,11 +251,15 @@ jobs:
run: | run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0 ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
- name: Publish macOS Ryujinx.Headless.SDL2
run: |
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
- name: Pushing new release - name: Pushing new release
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: "publish/*.tar.gz" artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
body: "" body: ""
omitBodyDuringUpdate: true omitBodyDuringUpdate: true

View File

@@ -44,7 +44,7 @@
<PackageVersion Include="Gommon" Version="2.6.8" /> <PackageVersion Include="Gommon" Version="2.6.8" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" /> <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" /> <PackageVersion Include="SharpMetal" Version="1.0.0-preview20" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" />
<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" />

View File

@@ -57,10 +57,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "src\Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}"
@@ -76,16 +80,11 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
@@ -95,6 +94,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\release.yml = .github\workflows\release.yml .github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -209,6 +210,10 @@ Global
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU {D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.Build.0 = Release|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -217,6 +222,10 @@ Global
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -256,10 +265,6 @@ Global
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -13,7 +13,7 @@ mkdir -p AppDir/usr/bin
cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop
cp distribution/linux/appimage/AppRun AppDir/AppRun cp distribution/linux/appimage/AppRun AppDir/AppRun
cp distribution/misc/Logo.svg AppDir/Ryujinx.svg cp src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png AppDir/Ryujinx.svg
cp -r "$BUILDDIR"/* AppDir/usr/bin/ cp -r "$BUILDDIR"/* AppDir/usr/bin/

View File

@@ -1,5 +1,4 @@
using ARMeilleure.State; using ARMeilleure.State;
using Humanizer;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
@@ -59,8 +58,8 @@ namespace ARMeilleure.Translation.PTC
{ {
_ptc = ptc; _ptc = ptc;
_timer = new Timer(SaveInterval.Seconds()); _timer = new Timer(SaveInterval * 1000d);
_timer.Elapsed += TimerElapsed; _timer.Elapsed += PreSave;
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
@@ -73,9 +72,6 @@ namespace ARMeilleure.Translation.PTC
Enabled = false; Enabled = false;
} }
private void TimerElapsed(object _, ElapsedEventArgs __)
=> new Thread(PreSave) { Name = "Ptc.DiskWriter" }.Start();
public void AddEntry(ulong address, ExecutionMode mode, bool highCq) public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
{ {
if (IsAddressInStaticCodeRange(address)) if (IsAddressInStaticCodeRange(address))
@@ -266,7 +262,7 @@ namespace ARMeilleure.Translation.PTC
compressedStream.SetLength(0L); compressedStream.SetLength(0L);
} }
private void PreSave() private void PreSave(object source, ElapsedEventArgs e)
{ {
_waitEvent.Reset(); _waitEvent.Reset();
@@ -432,7 +428,7 @@ namespace ARMeilleure.Translation.PTC
{ {
_disposed = true; _disposed = true;
_timer.Elapsed -= TimerElapsed; _timer.Elapsed -= PreSave;
_timer.Dispose(); _timer.Dispose();
Wait(); Wait();

View File

@@ -9,12 +9,20 @@ namespace Ryujinx.Audio.Backends.Dummy
{ {
public class DummyHardwareDeviceDriver : IHardwareDeviceDriver public class DummyHardwareDeviceDriver : IHardwareDeviceDriver
{ {
private readonly ManualResetEvent _updateRequiredEvent = new(false); private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent = new(true); private readonly ManualResetEvent _pauseEvent;
public static bool IsSupported => true; public static bool IsSupported => true;
public float Volume { get; set; } = 1f; public float Volume { get; set; }
public DummyHardwareDeviceDriver()
{
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
Volume = 1f;
}
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
{ {
@@ -52,7 +60,7 @@ namespace Ryujinx.Audio.Backends.Dummy
Dispose(true); Dispose(true);
} }
private void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {

View File

@@ -1,35 +0,0 @@
namespace Ryujinx.Common
{
public class BitTricks
{
// Never actually written bit packing logic before, so I looked it up.
// This code is from https://gist.github.com/Alan-FGR/04938e93e2bffdf5802ceb218a37c195
public static ulong PackBitFields(uint[] values, byte[] bitFields)
{
ulong retVal = values[0]; //we set the first value right away
for (int f = 1; f < values.Length; f++)
{
retVal <<= bitFields[f]; // we shift the previous value
retVal += values[f];// and add our current value
}
return retVal;
}
public static uint[] UnpackBitFields(ulong packed, byte[] bitFields)
{
int fields = bitFields.Length - 1; // number of fields to unpack
uint[] retArr = new uint[fields + 1]; // init return array
int curPos = 0; // current field bit position (start)
int lastEnd; // position where last field ended
for (int f = fields; f >= 0; f--) // loop from last
{
lastEnd = curPos; // we store where the last value ended
curPos += bitFields[f]; // we get where the current value starts
int leftShift = 64 - curPos; // we figure how much left shift we gotta apply for the other numbers to overflow into oblivion
retArr[f] = (uint)((packed << leftShift) >> leftShift + lastEnd); // we do magic
}
return retArr;
}
}
}

View File

@@ -35,8 +35,6 @@ namespace Ryujinx.Common.Configuration
#pragma warning restore IDE0055 #pragma warning restore IDE0055
}; };
} }
public static float ToFloatY(this AspectRatio aspectRatio) public static float ToFloatY(this AspectRatio aspectRatio)
{ {

View File

@@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Common.Configuration
{
[Flags]
public enum DirtyHacks : byte
{
Xc2MenuSoftlockFix = 1,
ShaderCompilationThreadSleep = 2
}
public record EnabledDirtyHack(DirtyHacks Hack, int Value)
{
public static readonly byte[] PackedFormat = [8, 32];
public ulong Pack() => BitTricks.PackBitFields([(uint)Hack, (uint)Value], PackedFormat);
public static EnabledDirtyHack Unpack(ulong packedHack)
{
var unpackedFields = BitTricks.UnpackBitFields(packedHack, PackedFormat);
if (unpackedFields is not [var hack, var value])
throw new ArgumentException(nameof(packedHack));
return new EnabledDirtyHack((DirtyHacks)hack, (int)value);
}
}
public class DirtyHackCollection : Dictionary<DirtyHacks, int>
{
public DirtyHackCollection(EnabledDirtyHack[] hacks)
{
foreach ((DirtyHacks dirtyHacks, int value) in hacks)
{
Add(dirtyHacks, value);
}
}
public DirtyHackCollection(ulong[] packedHacks)
{
foreach ((DirtyHacks dirtyHacks, int value) in packedHacks.Select(EnabledDirtyHack.Unpack))
{
Add(dirtyHacks, value);
}
}
public ulong[] PackEntries() =>
this
.Select(it =>
BitTricks.PackBitFields([(uint)it.Key, (uint)it.Value], EnabledDirtyHack.PackedFormat))
.ToArray();
public static implicit operator DirtyHackCollection(EnabledDirtyHack[] hacks) => new(hacks);
public static implicit operator DirtyHackCollection(ulong[] packedHacks) => new(packedHacks);
public new int this[DirtyHacks hack] => TryGetValue(hack, out var value) ? value : -1;
public bool IsEnabled(DirtyHacks hack) => ContainsKey(hack);
}
}

View File

@@ -9,6 +9,5 @@ namespace Ryujinx.Common.Configuration
Bilinear, Bilinear,
Nearest, Nearest,
Fsr, Fsr,
Area,
} }
} }

View File

@@ -8,10 +8,10 @@ namespace Ryujinx.Common
public static class StreamExtensions public static class StreamExtensions
{ {
/// <summary> /// <summary>
/// Writes an int span to this stream. /// Writes a <see cref="ReadOnlySpan{int}" /> to this stream.
/// ///
/// This default implementation converts each buffer value to a stack-allocated /// This default implementation converts each buffer value to a stack-allocated
/// byte array, then writes it to the Stream using <see cref="Stream.Write(ReadOnlySpan{byte})" />. /// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />.
/// </summary> /// </summary>
/// <param name="stream">The stream to be written to</param> /// <param name="stream">The stream to be written to</param>
/// <param name="buffer">The buffer of values to be written</param> /// <param name="buffer">The buffer of values to be written</param>

View File

@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@@ -158,16 +157,21 @@ namespace Ryujinx.Common.Logging
_time.Restart(); _time.Restart();
} }
private static ILogTarget GetTarget(string targetName) private static ILogTarget GetTarget(string targetName)
=> _logTargets.FirstOrDefault(target => target.Name.Equals(targetName)); {
foreach (var target in _logTargets)
{
if (target.Name.Equals(targetName))
{
return target;
}
}
return null;
}
public static void AddTarget(ILogTarget target) public static void AddTarget(ILogTarget target)
{ {
if (_logTargets.Any(t => t.Name == target.Name))
{
return;
}
_logTargets.Add(target); _logTargets.Add(target);
Updated += target.Log; Updated += target.Log;

View File

@@ -27,7 +27,11 @@ namespace Ryujinx.Common.Logging.Targets
private readonly int _overflowTimeout; private readonly int _overflowTimeout;
string ILogTarget.Name => _target.Name; string ILogTarget.Name { get => _target.Name; }
public AsyncLogTargetWrapper(ILogTarget target)
: this(target, -1)
{ }
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block) public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block)
{ {

View File

@@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.GAL
{
public enum AntiAliasing
{
None,
Fxaa,
SmaaLow,
SmaaMedium,
SmaaHigh,
SmaaUltra,
}
}

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Configuration;
using System; using System;
namespace Ryujinx.Graphics.GAL namespace Ryujinx.Graphics.GAL

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; using Ryujinx.Graphics.GAL.Multithreading.Commands.Window;
using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources; using Ryujinx.Graphics.GAL.Multithreading.Resources;

View File

@@ -0,0 +1,10 @@
namespace Ryujinx.Graphics.GAL
{
public enum ScalingFilter
{
Bilinear,
Nearest,
Fsr,
Area,
}
}

View File

@@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.GAL
{
public enum VSyncMode
{
Switch,
Unbounded,
Custom
}
}

View File

@@ -1,5 +1,4 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.Device; using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.GPFifo;
@@ -91,9 +90,6 @@ namespace Ryujinx.Graphics.Gpu
/// Support buffer updater. /// Support buffer updater.
/// </summary> /// </summary>
internal SupportBufferUpdater SupportBufferUpdater { get; } internal SupportBufferUpdater SupportBufferUpdater { get; }
internal DirtyHackCollection DirtyHacks { get; }
/// <summary> /// <summary>
/// Host hardware capabilities. /// Host hardware capabilities.
@@ -117,7 +113,7 @@ namespace Ryujinx.Graphics.Gpu
/// Creates a new instance of the GPU emulation context. /// Creates a new instance of the GPU emulation context.
/// </summary> /// </summary>
/// <param name="renderer">Host renderer</param> /// <param name="renderer">Host renderer</param>
public GpuContext(IRenderer renderer, DirtyHackCollection hackCollection) public GpuContext(IRenderer renderer)
{ {
Renderer = renderer; Renderer = renderer;
@@ -140,8 +136,6 @@ namespace Ryujinx.Graphics.Gpu
SupportBufferUpdater = new SupportBufferUpdater(renderer); SupportBufferUpdater = new SupportBufferUpdater(renderer);
DirtyHacks = hackCollection;
_firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); _firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
} }

View File

@@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Gpu
/// Enables or disables high-level emulation of common GPU Macro code. /// Enables or disables high-level emulation of common GPU Macro code.
/// </summary> /// </summary>
public static bool EnableMacroHLE = true; public static bool EnableMacroHLE = true;
/// <summary> /// <summary>
/// Title id of the current running game. /// Title id of the current running game.
/// Used by the shader cache. /// Used by the shader cache.

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
@@ -367,9 +366,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{ {
try try
{ {
if (_context.DirtyHacks.IsEnabled(DirtyHacks.ShaderCompilationThreadSleep))
Thread.Sleep(_context.DirtyHacks[DirtyHacks.ShaderCompilationThreadSleep]);
AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute); AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute);
_asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken);
} }

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;

View File

@@ -1,22 +0,0 @@
using SharpMetal;
using SharpMetal.Foundation;
using SharpMetal.ObjectiveCCore;
using SharpMetal.QuartzCore;
using System.Runtime.Versioning;
// ReSharper disable InconsistentNaming
namespace Ryujinx.Graphics.Metal.SharpMetalExtensions
{
[SupportedOSPlatform("macOS")]
public static class CAMetalLayerExtensions
{
private static readonly Selector sel_developerHUDProperties = "developerHUDProperties";
private static readonly Selector sel_setDeveloperHUDProperties = "setDeveloperHUDProperties:";
public static NSDictionary GetDeveloperHudProperties(this CAMetalLayer metalLayer)
=> new(ObjectiveCRuntime.IntPtr_objc_msgSend(metalLayer.NativePtr, sel_developerHUDProperties));
public static void SetDeveloperHudProperties(this CAMetalLayer metalLayer, NSDictionary dictionary)
=> ObjectiveCRuntime.objc_msgSend(metalLayer.NativePtr, sel_setDeveloperHUDProperties, dictionary);
}
}

View File

@@ -1,32 +0,0 @@
using SharpMetal.Foundation;
using SharpMetal.ObjectiveCCore;
using System.Runtime.Versioning;
// ReSharper disable InconsistentNaming
namespace Ryujinx.Graphics.Metal.SharpMetalExtensions
{
[SupportedOSPlatform("macOS")]
public static class NSHelper
{
private static readonly Selector sel_getCStringMaxLengthEncoding = "getCString:maxLength:encoding:";
private static readonly Selector sel_stringWithUTF8String = "stringWithUTF8String:";
public static unsafe string ToDotNetString(this NSString source)
{
char[] sourceBuffer = new char[source.Length];
fixed (char* pSourceBuffer = sourceBuffer)
{
ObjectiveC.bool_objc_msgSend(source,
sel_getCStringMaxLengthEncoding,
pSourceBuffer,
source.MaximumLengthOfBytes(NSStringEncoding.UTF16) + 1,
(ulong)NSStringEncoding.UTF16);
}
return new string(sourceBuffer);
}
public static NSString ToNSString(this string source)
=> new(ObjectiveC.IntPtr_objc_msgSend(new ObjectiveCClass(nameof(NSString)), sel_stringWithUTF8String, source));
}
}

View File

@@ -1,11 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpMetal" />
</ItemGroup>
</Project>

View File

@@ -5,9 +5,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj" /> </ItemGroup>
<ItemGroup>
<PackageReference Include="SharpMetal" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Metal.Effects; using Ryujinx.Graphics.Metal.Effects;
@@ -15,22 +14,20 @@ namespace Ryujinx.Graphics.Metal
public bool ScreenCaptureRequested { get; set; } public bool ScreenCaptureRequested { get; set; }
private readonly MetalRenderer _renderer; private readonly MetalRenderer _renderer;
private CAMetalLayer _metalLayer; private readonly CAMetalLayer _metalLayer;
private int _width; private int _width;
private int _height; private int _height;
private int _requestedWidth; private int _requestedWidth;
private int _requestedHeight; private int _requestedHeight;
// private bool _vsyncEnabled;
private AntiAliasing _currentAntiAliasing; private AntiAliasing _currentAntiAliasing;
private bool _updateEffect; private bool _updateEffect;
private IPostProcessingEffect _effect; private IPostProcessingEffect _effect;
private IScalingFilter _scalingFilter; private IScalingFilter _scalingFilter;
private bool _isLinear; private bool _isLinear;
public bool IsVSyncEnabled => _metalLayer.DisplaySyncEnabled;
// private float _scalingFilterLevel; // private float _scalingFilterLevel;
private bool _updateScalingFilter; private bool _updateScalingFilter;
private ScalingFilter _currentScalingFilter; private ScalingFilter _currentScalingFilter;
@@ -42,7 +39,7 @@ namespace Ryujinx.Graphics.Metal
_metalLayer = metalLayer; _metalLayer = metalLayer;
} }
private void ResizeIfNeeded() private unsafe void ResizeIfNeeded()
{ {
if (_requestedWidth != 0 && _requestedHeight != 0) if (_requestedWidth != 0 && _requestedHeight != 0)
{ {
@@ -56,7 +53,7 @@ namespace Ryujinx.Graphics.Metal
} }
} }
public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) public unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{ {
if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex)
{ {
@@ -143,7 +140,7 @@ namespace Ryujinx.Graphics.Metal
public void ChangeVSyncMode(VSyncMode vSyncMode) public void ChangeVSyncMode(VSyncMode vSyncMode)
{ {
_metalLayer.DisplaySyncEnabled = vSyncMode is VSyncMode.Switch; //_vSyncMode = vSyncMode;
} }
public void SetAntiAliasing(AntiAliasing effect) public void SetAntiAliasing(AntiAliasing effect)

View File

@@ -2,8 +2,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
{ {
internal enum BitDepth internal enum BitDepth
{ {
Bits8 = 8, // < 8 bits Bits8 = 8, /**< 8 bits */
Bits10 = 10, // < 10 bits Bits10 = 10, /**< 10 bits */
Bits12 = 12, // < 12 bits Bits12 = 12, /**< 12 bits */
} }
} }

View File

@@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
_current = new CounterQueueEvent(this, glType, 0); _current = new CounterQueueEvent(this, glType, 0);
_consumerThread = new Thread(EventConsumer) { Name = "CPU.CounterQueue." + (int)type }; _consumerThread = new Thread(EventConsumer);
_consumerThread.Start(); _consumerThread.Start();
} }

View File

@@ -1,5 +1,4 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Effects; using Ryujinx.Graphics.OpenGL.Effects;
using Ryujinx.Graphics.OpenGL.Effects.Smaa; using Ryujinx.Graphics.OpenGL.Effects.Smaa;

View File

@@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
_current = new CounterQueueEvent(this, type, 0); _current = new CounterQueueEvent(this, type, 0);
_consumerThread = new Thread(EventConsumer) { Name = "CPU.CounterQueue." + (int)type }; _consumerThread = new Thread(EventConsumer);
_consumerThread.Start(); _consumerThread.Start();
} }

View File

@@ -1,4 +1,3 @@
using Gommon;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
@@ -891,12 +890,7 @@ namespace Ryujinx.Graphics.Vulkan
private void PrintGpuInformation() private void PrintGpuInformation()
{ {
string gpuInfoMessage = $"{GpuRenderer} ({GpuVersion})"; Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
if (!GpuRenderer.StartsWithIgnoreCase(GpuVendor))
gpuInfoMessage = gpuInfoMessage.Prepend(GpuVendor);
Logger.Notice.Print(LogClass.Gpu, gpuInfoMessage);
Logger.Notice.Print(LogClass.Gpu, $"GPU Memory: {GetTotalGPUMemory() / (1024 * 1024)} MiB"); Logger.Notice.Print(LogClass.Gpu, $"GPU Memory: {GetTotalGPUMemory() / (1024 * 1024)} MiB");
} }

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Vulkan.Effects; using Ryujinx.Graphics.Vulkan.Effects;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using System; using System;

View File

@@ -1034,16 +1034,16 @@ namespace Ryujinx.HLE.FileSystem
switch (fileName) switch (fileName)
{ {
case "prod.keys": case "prod.keys":
verified = VerifyKeys(lines, genericPattern); verified = verifyKeys(lines, genericPattern);
break; break;
case "title.keys": case "title.keys":
verified = VerifyKeys(lines, titlePattern); verified = verifyKeys(lines, titlePattern);
break; break;
case "console.keys": case "console.keys":
verified = VerifyKeys(lines, genericPattern); verified = verifyKeys(lines, genericPattern);
break; break;
case "dev.keys": case "dev.keys":
verified = VerifyKeys(lines, genericPattern); verified = verifyKeys(lines, genericPattern);
break; break;
default: default:
throw new FormatException($"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported."); throw new FormatException($"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported.");
@@ -1056,22 +1056,20 @@ namespace Ryujinx.HLE.FileSystem
{ {
throw new FileNotFoundException($"Keys file not found at \"{filePath}\"."); throw new FileNotFoundException($"Keys file not found at \"{filePath}\".");
} }
return;
bool VerifyKeys(string[] lines, string regex)
{
foreach (string line in lines)
{
if (!Regex.IsMatch(line, regex))
{
return false;
}
}
return true;
}
} }
private bool verifyKeys(string[] lines, string regex)
{
foreach (string line in lines)
{
if (!Regex.IsMatch(line, regex))
{
return false;
}
}
return true;
}
public bool AreKeysAlredyPresent(string pathToCheck) public bool AreKeysAlredyPresent(string pathToCheck)
{ {
string[] fileNames = { "prod.keys", "title.keys", "console.keys", "dev.keys" }; string[] fileNames = { "prod.keys", "title.keys", "console.keys", "dev.keys" };

View File

@@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.UI; using Ryujinx.HLE.UI;
using System; using System;
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.HLE namespace Ryujinx.HLE
{ {
@@ -188,11 +189,6 @@ namespace Ryujinx.HLE
/// An action called when HLE force a refresh of output after docked mode changed. /// An action called when HLE force a refresh of output after docked mode changed.
/// </summary> /// </summary>
public Action RefreshInputConfig { internal get; set; } public Action RefreshInputConfig { internal get; set; }
/// <summary>
/// The desired hacky workarounds.
/// </summary>
public EnabledDirtyHack[] Hacks { internal get; set; }
public HLEConfiguration(VirtualFileSystem virtualFileSystem, public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager, LibHacHorizonManager libHacHorizonManager,
@@ -223,8 +219,7 @@ namespace Ryujinx.HLE
bool multiplayerDisableP2p, bool multiplayerDisableP2p,
string multiplayerLdnPassphrase, string multiplayerLdnPassphrase,
string multiplayerLdnServer, string multiplayerLdnServer,
int customVSyncInterval, int customVSyncInterval)
EnabledDirtyHack[] dirtyHacks = null)
{ {
VirtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager; LibHacHorizonManager = libHacHorizonManager;
@@ -256,7 +251,6 @@ namespace Ryujinx.HLE
MultiplayerDisableP2p = multiplayerDisableP2p; MultiplayerDisableP2p = multiplayerDisableP2p;
MultiplayerLdnPassphrase = multiplayerLdnPassphrase; MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
MultiplayerLdnServer = multiplayerLdnServer; MultiplayerLdnServer = multiplayerLdnServer;
Hacks = dirtyHacks ?? [];
} }
} }
} }

View File

@@ -181,7 +181,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
is64Bits = true; is64Bits = true;
} }
HostThread = new Thread(ThreadStart) { Name = "HLE.KThread" }; HostThread = new Thread(ThreadStart);
Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext(); Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext();

View File

@@ -1,4 +1,3 @@
using Gommon;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -7,13 +6,12 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Account.Acc namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
public class AccountSaveDataManager class AccountSaveDataManager
{ {
private static readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
private static readonly ProfilesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly ProfilesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@@ -51,16 +49,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
} }
} }
public static Optional<UserProfile> GetLastUsedUser()
{
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, _serializerContext.ProfilesJson);
return profilesJson.Profiles
.FindFirst(profile => profile.AccountState == AccountState.Open)
.Convert(profileJson => new UserProfile(new UserId(profileJson.UserId), profileJson.Name,
profileJson.Image, profileJson.LastModifiedTimestamp));
}
public void Save(ConcurrentDictionary<string, UserProfile> profiles) public void Save(ConcurrentDictionary<string, UserProfile> profiles)
{ {
ProfilesJson profilesJson = new() ProfilesJson profilesJson = new()

View File

@@ -1,9 +1,6 @@
using LibHac; using LibHac;
using LibHac.Common; using LibHac.Common;
using LibHac.Sf; using LibHac.Sf;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{ {
@@ -16,8 +13,6 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
_baseStorage = SharedRef<LibHac.FsSrv.Sf.IStorage>.CreateMove(ref baseStorage); _baseStorage = SharedRef<LibHac.FsSrv.Sf.IStorage>.CreateMove(ref baseStorage);
} }
private const string Xc2TitleId = "0100e95004038000";
[CommandCmif(0)] [CommandCmif(0)]
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer // Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
public ResultCode Read(ServiceCtx context) public ResultCode Read(ServiceCtx context)
@@ -38,13 +33,6 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size); Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
if (context.Device.DirtyHacks.IsEnabled(DirtyHacks.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication.Value == Xc2TitleId)
{
// Add a load-bearing sleep to avoid XC2 softlock
// https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357
Thread.Sleep(2);
}
return (ResultCode)result.Value; return (ResultCode)result.Value;
} }

View File

@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {

View File

@@ -4,10 +4,8 @@ using LibHac.Fs.Fsa;
using LibHac.Loader; using LibHac.Loader;
using LibHac.Ns; using LibHac.Ns;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
@@ -104,7 +102,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
} }
// Initialize GPU. // Initialize GPU.
GraphicsConfig.TitleId = programId.ToString("X16"); Graphics.Gpu.GraphicsConfig.TitleId = $"{programId:x16}";
device.Gpu.HostInitalized.Set(); device.Gpu.HostInitalized.Set();
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))

View File

@@ -6,9 +6,7 @@ using LibHac.Ns;
using LibHac.Tools.Fs; using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using System; using System;
@@ -61,8 +59,6 @@ namespace Ryujinx.HLE.Loaders.Processes
{ {
_latestPid = processResult.ProcessId; _latestPid = processResult.ProcessId;
TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
return true; return true;
} }
} }
@@ -90,8 +86,6 @@ namespace Ryujinx.HLE.Loaders.Processes
{ {
_latestPid = processResult.ProcessId; _latestPid = processResult.ProcessId;
TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
return true; return true;
} }
} }
@@ -119,8 +113,6 @@ namespace Ryujinx.HLE.Loaders.Processes
if (processResult.ProgramId > 0x01000000000007FF) if (processResult.ProgramId > 0x01000000000007FF)
{ {
_latestPid = processResult.ProcessId; _latestPid = processResult.ProcessId;
TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
} }
return true; return true;
@@ -140,8 +132,6 @@ namespace Ryujinx.HLE.Loaders.Processes
{ {
_latestPid = processResult.ProcessId; _latestPid = processResult.ProcessId;
TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
return true; return true;
} }
} }
@@ -193,17 +183,14 @@ namespace Ryujinx.HLE.Loaders.Processes
if (nacpData.Value.PresenceGroupId != 0) if (nacpData.Value.PresenceGroupId != 0)
{ {
programId = nacpData.Value.PresenceGroupId; programId = nacpData.Value.PresenceGroupId;
TitleIDs.CurrentApplication.Value = programId.ToString("X16");
} }
else if (nacpData.Value.SaveDataOwnerId != 0) else if (nacpData.Value.SaveDataOwnerId != 0)
{ {
programId = nacpData.Value.SaveDataOwnerId; programId = nacpData.Value.SaveDataOwnerId;
TitleIDs.CurrentApplication.Value = programId.ToString("X16");
} }
else if (nacpData.Value.AddOnContentBaseId != 0) else if (nacpData.Value.AddOnContentBaseId != 0)
{ {
programId = nacpData.Value.AddOnContentBaseId - 0x1000; programId = nacpData.Value.AddOnContentBaseId - 0x1000;
TitleIDs.CurrentApplication.Value = programId.ToString("X16");
} }
} }
@@ -217,7 +204,7 @@ namespace Ryujinx.HLE.Loaders.Processes
} }
// Explicitly null TitleId to disable the shader cache. // Explicitly null TitleId to disable the shader cache.
GraphicsConfig.TitleId = null; Graphics.Gpu.GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set(); _device.Gpu.HostInitalized.Set();
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device, ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,

View File

@@ -2,7 +2,6 @@ using LibHac.Common;
using LibHac.Ns; using LibHac.Ns;
using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@@ -18,8 +17,6 @@ namespace Ryujinx.HLE
{ {
public class Switch : IDisposable public class Switch : IDisposable
{ {
public static Switch Shared { get; private set; }
public HLEConfiguration Configuration { get; } public HLEConfiguration Configuration { get; }
public IHardwareDeviceDriver AudioDeviceDriver { get; } public IHardwareDeviceDriver AudioDeviceDriver { get; }
public MemoryBlock Memory { get; } public MemoryBlock Memory { get; }
@@ -40,8 +37,6 @@ namespace Ryujinx.HLE
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
public DirtyHackCollection DirtyHacks { get; }
public Switch(HLEConfiguration configuration) public Switch(HLEConfiguration configuration)
{ {
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer); ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
@@ -57,10 +52,9 @@ namespace Ryujinx.HLE
: MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable; : MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable;
#pragma warning disable IDE0055 // Disable formatting #pragma warning disable IDE0055 // Disable formatting
DirtyHacks = new DirtyHackCollection(Configuration.Hacks);
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags);
Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks); Gpu = new GpuContext(Configuration.GpuRenderer);
System = new HOS.Horizon(this); System = new HOS.Horizon(this);
Statistics = new PerformanceStatistics(); Statistics = new PerformanceStatistics();
Hid = new Hid(this, System.HidStorage); Hid = new Hid(this, System.HidStorage);
@@ -78,11 +72,8 @@ namespace Ryujinx.HLE
System.EnablePtc = Configuration.EnablePtc; System.EnablePtc = Configuration.EnablePtc;
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
UpdateVSyncInterval(); UpdateVSyncInterval();
#pragma warning restore IDE0055 #pragma warning restore IDE0055
Shared = this;
} }
public void ProcessFrame() public void ProcessFrame()
@@ -151,9 +142,6 @@ namespace Ryujinx.HLE
AudioDeviceDriver.Dispose(); AudioDeviceDriver.Dispose();
FileSystem.Dispose(); FileSystem.Dispose();
Memory.Dispose(); Memory.Dispose();
TitleIDs.CurrentApplication.Value = null;
Shared = null;
} }
} }
} }

View File

@@ -2,7 +2,7 @@ using Ryujinx.HLE.UI;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2
{ {
/// <summary> /// <summary>
/// Headless text processing class, right now there is no way to forward the input to it. /// Headless text processing class, right now there is no way to forward the input to it.

View File

@@ -1,6 +1,6 @@
using Ryujinx.HLE.UI; using Ryujinx.HLE.UI;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2
{ {
internal class HeadlessHostUiTheme : IHostUITheme internal class HeadlessHostUiTheme : IHostUITheme
{ {

View File

@@ -5,7 +5,7 @@ using SharpMetal.QuartzCore;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using static SDL2.SDL; using static SDL2.SDL;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2.Metal
{ {
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
class MetalWindow : WindowBase class MetalWindow : WindowBase

View File

@@ -7,7 +7,7 @@ using Ryujinx.Input.HLE;
using System; using System;
using static SDL2.SDL; using static SDL2.SDL;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2.OpenGL
{ {
class OpenGLWindow : WindowBase class OpenGLWindow : WindowBase
{ {

View File

@@ -1,168 +1,13 @@
using CommandLine; using CommandLine;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2
{ {
public class Options public class Options
{ {
public void InheritMainConfig(string[] originalArgs, ConfigurationState configurationState, out bool needsProfileSet)
{
needsProfileSet = NeedsOverride(nameof(UserProfile));
if (NeedsOverride(nameof(IsFullscreen)))
IsFullscreen = configurationState.UI.StartFullscreen;
if (NeedsOverride(nameof(EnableKeyboard)))
EnableKeyboard = configurationState.Hid.EnableKeyboard;
if (NeedsOverride(nameof(EnableMouse)))
EnableMouse = configurationState.Hid.EnableMouse;
if (NeedsOverride(nameof(HideCursorMode)))
HideCursorMode = configurationState.HideCursor;
if (NeedsOverride(nameof(DisablePTC)))
DisablePTC = !configurationState.System.EnablePtc;
if (NeedsOverride(nameof(EnableInternetAccess)))
EnableInternetAccess = configurationState.System.EnableInternetAccess;
if (NeedsOverride(nameof(DisableFsIntegrityChecks)))
DisableFsIntegrityChecks = configurationState.System.EnableFsIntegrityChecks;
if (NeedsOverride(nameof(FsGlobalAccessLogMode)))
FsGlobalAccessLogMode = configurationState.System.FsGlobalAccessLogMode;
if (NeedsOverride(nameof(VSyncMode)))
VSyncMode = configurationState.Graphics.VSyncMode;
if (NeedsOverride(nameof(CustomVSyncInterval)))
CustomVSyncInterval = configurationState.Graphics.CustomVSyncInterval;
if (NeedsOverride(nameof(DisableShaderCache)))
DisableShaderCache = !configurationState.Graphics.EnableShaderCache;
if (NeedsOverride(nameof(EnableTextureRecompression)))
EnableTextureRecompression = configurationState.Graphics.EnableTextureRecompression;
if (NeedsOverride(nameof(DisableDockedMode)))
DisableDockedMode = !configurationState.System.EnableDockedMode;
if (NeedsOverride(nameof(SystemLanguage)))
SystemLanguage = (SystemLanguage)(int)configurationState.System.Language.Value;
if (NeedsOverride(nameof(SystemRegion)))
SystemRegion = (RegionCode)(int)configurationState.System.Region.Value;
if (NeedsOverride(nameof(SystemTimeZone)))
SystemTimeZone = configurationState.System.TimeZone;
if (NeedsOverride(nameof(SystemTimeOffset)))
SystemTimeOffset = configurationState.System.SystemTimeOffset;
if (NeedsOverride(nameof(MemoryManagerMode)))
MemoryManagerMode = configurationState.System.MemoryManagerMode;
if (NeedsOverride(nameof(AudioVolume)))
AudioVolume = configurationState.System.AudioVolume;
if (NeedsOverride(nameof(UseHypervisor)) && OperatingSystem.IsMacOS())
UseHypervisor = configurationState.System.UseHypervisor;
if (NeedsOverride(nameof(MultiplayerLanInterfaceId)))
MultiplayerLanInterfaceId = configurationState.Multiplayer.LanInterfaceId;
if (NeedsOverride(nameof(DisableFileLog)))
DisableFileLog = !configurationState.Logger.EnableFileLog;
if (NeedsOverride(nameof(LoggingEnableDebug)))
LoggingEnableDebug = configurationState.Logger.EnableDebug;
if (NeedsOverride(nameof(LoggingDisableStub)))
LoggingDisableStub = !configurationState.Logger.EnableStub;
if (NeedsOverride(nameof(LoggingDisableInfo)))
LoggingDisableInfo = !configurationState.Logger.EnableInfo;
if (NeedsOverride(nameof(LoggingDisableWarning)))
LoggingDisableWarning = !configurationState.Logger.EnableWarn;
if (NeedsOverride(nameof(LoggingDisableError)))
LoggingDisableError = !configurationState.Logger.EnableError;
if (NeedsOverride(nameof(LoggingEnableTrace)))
LoggingEnableTrace = configurationState.Logger.EnableTrace;
if (NeedsOverride(nameof(LoggingDisableGuest)))
LoggingDisableGuest = !configurationState.Logger.EnableGuest;
if (NeedsOverride(nameof(LoggingEnableFsAccessLog)))
LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog;
if (NeedsOverride(nameof(LoggingGraphicsDebugLevel)))
LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel;
if (NeedsOverride(nameof(ResScale)))
ResScale = configurationState.Graphics.ResScale;
if (NeedsOverride(nameof(MaxAnisotropy)))
MaxAnisotropy = configurationState.Graphics.MaxAnisotropy;
if (NeedsOverride(nameof(AspectRatio)))
AspectRatio = configurationState.Graphics.AspectRatio;
if (NeedsOverride(nameof(BackendThreading)))
BackendThreading = configurationState.Graphics.BackendThreading;
if (NeedsOverride(nameof(DisableMacroHLE)))
DisableMacroHLE = !configurationState.Graphics.EnableMacroHLE;
if (NeedsOverride(nameof(GraphicsShadersDumpPath)))
GraphicsShadersDumpPath = configurationState.Graphics.ShadersDumpPath;
if (NeedsOverride(nameof(GraphicsBackend)))
GraphicsBackend = configurationState.Graphics.GraphicsBackend;
if (NeedsOverride(nameof(AntiAliasing)))
AntiAliasing = configurationState.Graphics.AntiAliasing;
if (NeedsOverride(nameof(ScalingFilter)))
ScalingFilter = configurationState.Graphics.ScalingFilter;
if (NeedsOverride(nameof(ScalingFilterLevel)))
ScalingFilterLevel = configurationState.Graphics.ScalingFilterLevel;
if (NeedsOverride(nameof(DramSize)))
DramSize = configurationState.System.DramSize;
if (NeedsOverride(nameof(IgnoreMissingServices)))
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
if (NeedsOverride(nameof(IgnoreControllerApplet)))
IgnoreControllerApplet = configurationState.IgnoreApplet;
return;
bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey)));
string OptionName(string propertyName) =>
typeof(Options)!.GetProperty(propertyName)!.GetCustomAttribute<OptionAttribute>()!.LongName;
}
// General // General
[Option("use-main-config", Required = false, Default = false, HelpText = "Use the settings from what was configured via the UI.")]
public bool InheritConfig { get; set; }
[Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")] [Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")]
public string BaseDataDir { get; set; } public string BaseDataDir { get; set; }
@@ -250,10 +95,10 @@ namespace Ryujinx.Headless
[Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")] [Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")]
public HideCursorMode HideCursorMode { get; set; } public HideCursorMode HideCursorMode { get; set; }
[Option("list-input-profiles", Required = false, HelpText = "List input profiles.")] [Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")]
public bool ListInputProfiles { get; set; } public bool ListInputProfiles { get; set; }
[Option("list-input-ids", Required = false, HelpText = "List input IDs.")] [Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")]
public bool ListInputIds { get; set; } public bool ListInputIds { get; set; }
// System // System
@@ -327,7 +172,7 @@ namespace Ryujinx.Headless
public bool LoggingDisableWarning { get; set; } public bool LoggingDisableWarning { get; set; }
[Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")] [Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")]
public bool LoggingDisableError { get; set; } public bool LoggingEnableError { get; set; }
[Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")] [Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")]
public bool LoggingEnableTrace { get; set; } public bool LoggingEnableTrace { get; set; }
@@ -370,7 +215,7 @@ namespace Ryujinx.Headless
[Option("anti-aliasing", Required = false, Default = AntiAliasing.None, HelpText = "Set the type of anti aliasing being used. [None|Fxaa|SmaaLow|SmaaMedium|SmaaHigh|SmaaUltra]")] [Option("anti-aliasing", Required = false, Default = AntiAliasing.None, HelpText = "Set the type of anti aliasing being used. [None|Fxaa|SmaaLow|SmaaMedium|SmaaHigh|SmaaUltra]")]
public AntiAliasing AntiAliasing { get; set; } public AntiAliasing AntiAliasing { get; set; }
[Option("scaling-filter", Required = false, Default = ScalingFilter.Bilinear, HelpText = "Set the scaling filter. [Bilinear|Nearest|Fsr|Area]")] [Option("scaling-filter", Required = false, Default = ScalingFilter.Bilinear, HelpText = "Set the scaling filter. [Bilinear|Nearest|Fsr]")]
public ScalingFilter ScalingFilter { get; set; } public ScalingFilter ScalingFilter { get; set; }
[Option("scaling-filter-level", Required = false, Default = 0, HelpText = "Set the scaling filter intensity (currently only applies to FSR). [0-100]")] [Option("scaling-filter-level", Required = false, Default = 0, HelpText = "Set the scaling filter intensity (currently only applies to FSR). [0-100]")]

View File

@@ -1,10 +1,13 @@
using CommandLine; using CommandLine;
using Gommon; using Gommon;
using Ryujinx.Ava; using LibHac.Tools.FsSystem;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Logging.Targets; using Ryujinx.Common.Logging.Targets;
@@ -12,9 +15,16 @@ using Ryujinx.Common.SystemInterop;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Metal;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Headless.SDL2.Metal;
using Ryujinx.Headless.SDL2.OpenGL;
using Ryujinx.Headless.SDL2.Vulkan;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
@@ -23,15 +33,22 @@ using Ryujinx.Input;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2; using Ryujinx.Input.SDL2;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.Json;
using System.Threading; using System.Threading;
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
using Key = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2
{ {
public partial class HeadlessRyujinx class Program
{ {
public static string Version { get; private set; }
private static VirtualFileSystem _virtualFileSystem; private static VirtualFileSystem _virtualFileSystem;
private static ContentManager _contentManager; private static ContentManager _contentManager;
private static AccountManager _accountManager; private static AccountManager _accountManager;
@@ -41,18 +58,20 @@ namespace Ryujinx.Headless
private static Switch _emulationContext; private static Switch _emulationContext;
private static WindowBase _window; private static WindowBase _window;
private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
private static List<InputConfig> _inputConfiguration = []; private static List<InputConfig> _inputConfiguration;
private static bool _enableKeyboard; private static bool _enableKeyboard;
private static bool _enableMouse; private static bool _enableMouse;
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public static void Entrypoint(string[] args) static void Main(string[] args)
{ {
Version = ReleaseInformation.Version;
// Make process DPI aware for proper window sizing on high-res screens. // Make process DPI aware for proper window sizing on high-res screens.
ForceDpiAware.Windows(); ForceDpiAware.Windows();
Console.Title = $"Ryujinx Console {Program.Version} (Headless)"; Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
{ {
@@ -80,7 +99,7 @@ namespace Ryujinx.Headless
} }
Parser.Default.ParseArguments<Options>(args) Parser.Default.ParseArguments<Options>(args)
.WithParsed(options => Load(args, options)) .WithParsed(Load)
.WithNotParsed(errors => .WithNotParsed(errors =>
{ {
Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:"); Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:");
@@ -88,81 +107,239 @@ namespace Ryujinx.Headless
errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}")); errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}"));
}); });
} }
public static void ReloadConfig(string customConfigPath = null) private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
{ {
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); if (inputId == null)
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
string configurationPath = null;
// Now load the configuration as the other subsystems are now registered
if (customConfigPath != null && File.Exists(customConfigPath))
{ {
configurationPath = customConfigPath; if (index == PlayerIndex.Player1)
}
else if (File.Exists(localConfigurationPath))
{
configurationPath = localConfigurationPath;
}
else if (File.Exists(appDataConfigurationPath))
{
configurationPath = appDataConfigurationPath;
}
if (configurationPath == null)
{
// No configuration, we load the default values and save it to disk
configurationPath = appDataConfigurationPath;
Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {configurationPath}");
ConfigurationState.Instance.LoadDefault();
ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
}
else
{
Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {configurationPath}");
if (ConfigurationFileFormat.TryLoad(configurationPath, out ConfigurationFileFormat configurationFileFormat))
{ {
ConfigurationState.Instance.Load(configurationFileFormat, configurationPath); Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard.");
// Default to keyboard
inputId = "0";
} }
else else
{ {
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {configurationPath}"); Logger.Info?.Print(LogClass.Application, $"{index} not configured");
ConfigurationState.Instance.LoadDefault(); return null;
} }
} }
IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId);
bool isKeyboard = true;
if (gamepad == null)
{
gamepad = _inputManager.GamepadDriver.GetGamepad(inputId);
isKeyboard = false;
if (gamepad == null)
{
Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")");
return null;
}
}
string gamepadName = gamepad.Name;
gamepad.Dispose();
InputConfig config;
if (inputProfileName == null || inputProfileName.Equals("default"))
{
if (isKeyboard)
{
config = new StandardKeyboardInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.WindowKeyboard,
Id = null,
ControllerType = ControllerType.JoyconPair,
LeftJoycon = new LeftJoyconCommonConfig<Key>
{
DpadUp = Key.Up,
DpadDown = Key.Down,
DpadLeft = Key.Left,
DpadRight = Key.Right,
ButtonMinus = Key.Minus,
ButtonL = Key.E,
ButtonZl = Key.Q,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
},
LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.W,
StickDown = Key.S,
StickLeft = Key.A,
StickRight = Key.D,
StickButton = Key.F,
},
RightJoycon = new RightJoyconCommonConfig<Key>
{
ButtonA = Key.Z,
ButtonB = Key.X,
ButtonX = Key.C,
ButtonY = Key.V,
ButtonPlus = Key.Plus,
ButtonR = Key.U,
ButtonZr = Key.O,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.I,
StickDown = Key.K,
StickLeft = Key.J,
StickRight = Key.L,
StickButton = Key.H,
},
};
}
else
{
bool isNintendoStyle = gamepadName.Contains("Nintendo");
config = new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Id = null,
ControllerType = ControllerType.JoyconPair,
DeadzoneLeft = 0.1f,
DeadzoneRight = 0.1f,
RangeLeft = 1.0f,
RangeRight = 1.0f,
TriggerThreshold = 0.5f,
LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
{
DpadUp = ConfigGamepadInputId.DpadUp,
DpadDown = ConfigGamepadInputId.DpadDown,
DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus,
ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Left,
StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false,
Rotate90CW = false,
},
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
{
ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
ButtonPlus = ConfigGamepadInputId.Plus,
ButtonR = ConfigGamepadInputId.RightShoulder,
ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Right,
StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false,
InvertStickY = false,
Rotate90CW = false,
},
Motion = new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver,
EnableMotion = true,
Sensitivity = 100,
GyroDeadzone = 1,
},
Rumble = new RumbleConfigController
{
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
},
};
}
}
else
{
string profileBasePath;
if (isKeyboard)
{
profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "keyboard");
}
else
{
profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "controller");
}
string path = Path.Combine(profileBasePath, inputProfileName + ".json");
if (!File.Exists(path))
{
Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" not found for \"{inputId}\"");
return null;
}
try
{
config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig);
}
catch (JsonException)
{
Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" parsing failed for \"{inputId}\"");
return null;
}
}
config.Id = inputId;
config.PlayerIndex = index;
string inputTypeName = isKeyboard ? "Keyboard" : "Gamepad";
Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} configured with {inputTypeName} \"{config.Id}\"");
// If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0.
if (config is StandardControllerInputConfig controllerConfig)
{
if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f)
{
controllerConfig.RangeLeft = 1.0f;
controllerConfig.RangeRight = 1.0f;
Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration");
}
}
return config;
} }
static void Load(string[] originalArgs, Options option) static void Load(Options option)
{ {
Initialize();
bool useLastUsedProfile = false;
if (option.InheritConfig)
{
option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out useLastUsedProfile);
}
AppDataManager.Initialize(option.BaseDataDir); AppDataManager.Initialize(option.BaseDataDir);
if (useLastUsedProfile && AccountSaveDataManager.GetLastUsedUser().TryGet(out var profile))
option.UserProfile = profile.Name;
// Check if keys exists.
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
{
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
{
Logger.Error?.Print(LogClass.Application, "Keys not found");
}
}
ReloadConfig();
_virtualFileSystem = VirtualFileSystem.CreateInstance(); _virtualFileSystem = VirtualFileSystem.CreateInstance();
_libHacHorizonManager = new LibHacHorizonManager(); _libHacHorizonManager = new LibHacHorizonManager();
@@ -177,7 +354,7 @@ namespace Ryujinx.Headless
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache; GraphicsConfig.EnableShaderCache = true;
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
{ {
@@ -188,13 +365,15 @@ namespace Ryujinx.Headless
} }
} }
IGamepad gamepad;
if (option.ListInputIds) if (option.ListInputIds)
{ {
Logger.Info?.Print(LogClass.Application, "Input Ids:"); Logger.Info?.Print(LogClass.Application, "Input Ids:");
foreach (string id in _inputManager.KeyboardDriver.GamepadsIds) foreach (string id in _inputManager.KeyboardDriver.GamepadsIds)
{ {
IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(id); gamepad = _inputManager.KeyboardDriver.GetGamepad(id);
Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")");
@@ -203,7 +382,7 @@ namespace Ryujinx.Headless
foreach (string id in _inputManager.GamepadDriver.GamepadsIds) foreach (string id in _inputManager.GamepadDriver.GamepadsIds)
{ {
IGamepad gamepad = _inputManager.GamepadDriver.GetGamepad(id); gamepad = _inputManager.GamepadDriver.GetGamepad(id);
Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")");
@@ -220,7 +399,7 @@ namespace Ryujinx.Headless
return; return;
} }
_inputConfiguration ??= []; _inputConfiguration = new List<InputConfig>();
_enableKeyboard = option.EnableKeyboard; _enableKeyboard = option.EnableKeyboard;
_enableMouse = option.EnableMouse; _enableMouse = option.EnableMouse;
@@ -233,9 +412,9 @@ namespace Ryujinx.Headless
_inputConfiguration.Add(inputConfig); _inputConfiguration.Add(inputConfig);
} }
} }
LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1);
LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2);
LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3);
LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4);
LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5);
@@ -243,7 +422,6 @@ namespace Ryujinx.Headless
LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7);
LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8);
LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld);
if (_inputConfiguration.Count == 0) if (_inputConfiguration.Count == 0)
{ {
@@ -255,7 +433,7 @@ namespace Ryujinx.Headless
Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub); Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub);
Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo); Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo);
Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning); Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning);
Logger.SetEnable(LogLevel.Error, !option.LoggingDisableError); Logger.SetEnable(LogLevel.Error, option.LoggingEnableError);
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace); Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest); Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog); Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
@@ -344,6 +522,88 @@ namespace Ryujinx.Headless
}; };
} }
private static IRenderer CreateRenderer(Options options, WindowBase window)
{
if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow)
{
string preferredGpuId = string.Empty;
Vk api = Vk.GetApi();
if (!string.IsNullOrEmpty(options.PreferredGPUVendor))
{
string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant();
var devices = VulkanRenderer.GetPhysicalDevices(api);
foreach (var device in devices)
{
if (device.Vendor.ToLowerInvariant() == preferredGpuVendor)
{
preferredGpuId = device.Id;
break;
}
}
}
return new VulkanRenderer(
api,
(instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))),
vulkanWindow.GetRequiredInstanceExtensions,
preferredGpuId);
}
if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS())
{
return new MetalRenderer(metalWindow.GetLayer);
}
return new OpenGLRenderer();
}
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options)
{
BackendThreading threadingMode = options.BackendThreading;
bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (threadedGAL)
{
renderer = new ThreadedRenderer(renderer);
}
HLEConfiguration configuration = new(_virtualFileSystem,
_libHacHorizonManager,
_contentManager,
_accountManager,
_userChannelPersistence,
renderer,
new SDL2HardwareDeviceDriver(),
options.DramSize,
window,
options.SystemLanguage,
options.SystemRegion,
options.VSyncMode,
!options.DisableDockedMode,
!options.DisablePTC,
options.EnableInternetAccess,
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode,
options.SystemTimeOffset,
options.SystemTimeZone,
options.MemoryManagerMode,
options.IgnoreMissingServices,
options.AspectRatio,
options.AudioVolume,
options.UseHypervisor ?? true,
options.MultiplayerLanInterfaceId,
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
false,
string.Empty,
string.Empty,
options.CustomVSyncInterval);
return new Switch(configuration);
}
private static void ExecutionEntrypoint() private static void ExecutionEntrypoint()
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())

View File

@@ -0,0 +1,73 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.0-dirty</Version>
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
<SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate>
<TieredPGO>true</TieredPGO>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
</Target>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
</ItemGroup>
<ItemGroup>
<Content Include="..\..\distribution\legal\THIRDPARTY.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>THIRDPARTY.md</TargetPath>
</Content>
<Content Include="..\..\LICENSE.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>LICENSE.txt</TargetPath>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
<Content Include="..\..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Ryujinx.bmp" />
</ItemGroup>
<!-- Due to .net core 3.1 embedded resource loading -->
<PropertyGroup>
<EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention>
<ApplicationIcon>..\Ryujinx\Ryujinx.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
</PropertyGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2
{ {
class StatusUpdatedEventArgs( class StatusUpdatedEventArgs(
string vSyncMode, string vSyncMode,

View File

@@ -6,7 +6,7 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static SDL2.SDL; using static SDL2.SDL;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2.Vulkan
{ {
class VulkanWindow : WindowBase class VulkanWindow : WindowBase
{ {

View File

@@ -1,6 +1,5 @@
using Humanizer; using Humanizer;
using LibHac.Tools.Fs; using LibHac.Tools.Fs;
using Ryujinx.Ava;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
@@ -27,7 +26,7 @@ using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
using Switch = Ryujinx.HLE.Switch; using Switch = Ryujinx.HLE.Switch;
namespace Ryujinx.Headless namespace Ryujinx.Headless.SDL2
{ {
abstract partial class WindowBase : IHostUIHandler, IDisposable abstract partial class WindowBase : IHostUIHandler, IDisposable
{ {
@@ -137,7 +136,7 @@ namespace Ryujinx.Headless
private void SetWindowIcon() private void SetWindowIcon()
{ {
Stream iconStream = typeof(Program).Assembly.GetManifestResourceStream("HeadlessLogo"); Stream iconStream = typeof(WindowBase).Assembly.GetManifestResourceStream("Ryujinx.Headless.SDL2.Ryujinx.bmp");
byte[] iconBytes = new byte[iconStream!.Length]; byte[] iconBytes = new byte[iconStream!.Length];
if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length) if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length)
@@ -255,12 +254,12 @@ namespace Ryujinx.Headless
private void SetAntiAliasing() private void SetAntiAliasing()
{ {
Renderer?.Window.SetAntiAliasing(AntiAliasing); Renderer?.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)AntiAliasing);
} }
private void SetScalingFilter() private void SetScalingFilter()
{ {
Renderer?.Window.SetScalingFilter(ScalingFilter); Renderer?.Window.SetScalingFilter((Graphics.GAL.ScalingFilter)ScalingFilter);
Renderer?.Window.SetScalingFilterLevel(ScalingFilterLevel); Renderer?.Window.SetScalingFilterLevel(ScalingFilterLevel);
} }
@@ -319,7 +318,7 @@ namespace Ryujinx.Headless
Device.VSyncMode.ToString(), Device.VSyncMode.ToString(),
dockedMode, dockedMode,
Device.Configuration.AspectRatio.ToText(), Device.Configuration.AspectRatio.ToText(),
$"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
$"FIFO: {Device.Statistics.GetFifoPercent():0.00} %", $"FIFO: {Device.Statistics.GetFifoPercent():0.00} %",
$"GPU: {_gpuDriverName}")); $"GPU: {_gpuDriverName}"));

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Ryujinx.Ava.Utilities.AppLibrary namespace Ryujinx.UI.App.Common
{ {
public class ApplicationCountUpdatedEventArgs : EventArgs public class ApplicationCountUpdatedEventArgs : EventArgs
{ {

View File

@@ -10,14 +10,17 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.UI.Common.Helper;
using System; using System;
using System.IO; using System.IO;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary namespace Ryujinx.UI.App.Common
{ {
public class ApplicationData public class ApplicationData
{ {
public static Func<string> LocalizedNever { get; set; } = () => "Never";
public bool Favorite { get; set; } public bool Favorite { get; set; }
public byte[] Icon { get; set; } public byte[] Icon { get; set; }
public string Name { get; set; } = "Unknown"; public string Name { get; set; } = "Unknown";
@@ -33,11 +36,9 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
public string Path { get; set; } public string Path { get; set; }
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
public bool HasControlHolder => ControlHolder.ByteSpan.Length > 0;
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed);
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n"); public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever();
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize); public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary namespace Ryujinx.UI.App.Common
{ {
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ApplicationMetadata))] [JsonSerializable(typeof(ApplicationMetadata))]

View File

@@ -10,10 +10,6 @@ using LibHac.Ns;
using LibHac.Tools.Fs; using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
@@ -23,11 +19,16 @@ using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.HLE.Loaders.Npdm;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Configuration.System;
using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Common.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
@@ -37,7 +38,7 @@ using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
using Path = System.IO.Path; using Path = System.IO.Path;
using TimeSpan = System.TimeSpan; using TimeSpan = System.TimeSpan;
namespace Ryujinx.Ava.Utilities.AppLibrary namespace Ryujinx.UI.App.Common
{ {
public class ApplicationLibrary public class ApplicationLibrary
{ {
@@ -75,11 +76,21 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
TitleUpdates = _titleUpdates.AsObservableCache(); TitleUpdates = _titleUpdates.AsObservableCache();
DownloadableContents = _downloadableContents.AsObservableCache(); DownloadableContents = _downloadableContents.AsObservableCache();
_nspIcon = EmbeddedResources.Read("Ryujinx.Assets.UIImages.Icon_NSP.png"); _nspIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSP.png");
_xciIcon = EmbeddedResources.Read("Ryujinx.Assets.UIImages.Icon_XCI.png"); _xciIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_XCI.png");
_ncaIcon = EmbeddedResources.Read("Ryujinx.Assets.UIImages.Icon_NCA.png"); _ncaIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NCA.png");
_nroIcon = EmbeddedResources.Read("Ryujinx.Assets.UIImages.Icon_NRO.png"); _nroIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NRO.png");
_nsoIcon = EmbeddedResources.Read("Ryujinx.Assets.UIImages.Icon_NSO.png"); _nsoIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSO.png");
}
private static byte[] GetResourceBytes(string resourceName)
{
Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName)!;
byte[] resourceByteArray = new byte[resourceStream.Length];
resourceStream.ReadExactly(resourceByteArray);
return resourceByteArray;
} }
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception> /// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
@@ -778,15 +789,16 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
using HttpClient httpClient = new HttpClient(); using HttpClient httpClient = new HttpClient();
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games"); string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData); ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs var evt = new LdnGameDataReceivedEventArgs
{ {
LdnData = ldnGameDataArray LdnData = ldnGameDataArray
}); };
LdnGameDataReceived?.Invoke(null, evt);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}"); Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs()
{ {
LdnData = Array.Empty<LdnGameData>() LdnData = Array.Empty<LdnGameData>()
}); });
@@ -794,7 +806,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
} }
else else
{ {
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs()
{ {
LdnData = Array.Empty<LdnGameData>() LdnData = Array.Empty<LdnGameData>()
}); });

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary namespace Ryujinx.UI.App.Common
{ {
public class ApplicationMetadata public class ApplicationMetadata
{ {

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Ryujinx.UI.App.Common
{
public struct LdnGameData
{
public string Id { get; set; }
public int PlayerCount { get; set; }
public int MaxPlayerCount { get; set; }
public string GameName { get; set; }
public string TitleId { get; set; }
public string Mode { get; set; }
public string Status { get; set; }
public IEnumerable<string> Players { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Ava.Utilities.AppLibrary namespace Ryujinx.UI.App.Common
{ {
public class LdnGameDataReceivedEventArgs : EventArgs public class LdnGameDataReceivedEventArgs : EventArgs
{ {

View File

@@ -1,8 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary namespace Ryujinx.UI.App.Common
{ {
[JsonSerializable(typeof(IEnumerable<LdnGameData>))] [JsonSerializable(typeof(IEnumerable<LdnGameData>))]
internal partial class LdnGameDataSerializerContext : JsonSerializerContext; internal partial class LdnGameDataSerializerContext : JsonSerializerContext
{
}
} }

View File

@@ -1,7 +1,7 @@
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<AudioBackend>))] [JsonConverter(typeof(TypedStringEnumConverter<AudioBackend>))]
public enum AudioBackend public enum AudioBackend

View File

@@ -1,5 +1,3 @@
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
@@ -7,17 +5,19 @@ using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.UI.Common.Configuration.System;
using Ryujinx.UI.Common.Configuration.UI;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
public class ConfigurationFileFormat public class ConfigurationFileFormat
{ {
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 59; public const int CurrentVersion = 57;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@@ -429,17 +429,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// Uses Hypervisor over JIT if available /// Uses Hypervisor over JIT if available
/// </summary> /// </summary>
public bool UseHypervisor { get; set; } public bool UseHypervisor { get; set; }
/// <summary>
/// Show toggles for dirty hacks in the UI.
/// </summary>
public bool ShowDirtyHacks { get; set; }
/// <summary>
/// The packed values of the enabled dirty hacks.
/// </summary>
public ulong[] DirtyHacks { get; set; }
/// <summary> /// <summary>
/// Loads a configuration file from disk /// Loads a configuration file from disk
/// </summary> /// </summary>

View File

@@ -1,6 +1,6 @@
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
internal static class ConfigurationFileFormatSettings internal static class ConfigurationFileFormatSettings
{ {

View File

@@ -1,8 +1,10 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ConfigurationFileFormat))] [JsonSerializable(typeof(ConfigurationFileFormat))]
internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext; internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext
{
}
} }

View File

@@ -1,5 +1,3 @@
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
@@ -7,11 +5,12 @@ using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.UI.Common.Configuration.System;
using Ryujinx.UI.Common.Configuration.UI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
public partial class ConfigurationState public partial class ConfigurationState
{ {
@@ -638,18 +637,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
configurationFileUpdated = true; configurationFileUpdated = true;
} }
// 58 migration accidentally got skipped but it worked with no issues somehow lol
if (configurationFileFormat.Version < 59)
{
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 59.");
configurationFileFormat.ShowDirtyHacks = false;
configurationFileFormat.DirtyHacks = [];
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScale.Value = configurationFileFormat.ResScale;
@@ -748,19 +735,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p; Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p;
Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase; Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase;
Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer; Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;
Hacks.ShowDirtyHacks.Value = configurationFileFormat.ShowDirtyHacks;
{
EnabledDirtyHack[] hacks = (configurationFileFormat.DirtyHacks ?? []).Select(EnabledDirtyHack.Unpack).ToArray();
Hacks.Xc2MenuSoftlockFix.Value = hacks.Any(it => it.Hack == DirtyHacks.Xc2MenuSoftlockFix);
var shaderCompilationThreadSleep = hacks.FirstOrDefault(it =>
it.Hack == DirtyHacks.ShaderCompilationThreadSleep);
Hacks.EnableShaderTranslationDelay.Value = shaderCompilationThreadSleep != null;
Hacks.ShaderTranslationDelay.Value = shaderCompilationThreadSleep?.Value ?? 0;
}
if (configurationFileUpdated) if (configurationFileUpdated)
{ {

View File

@@ -1,18 +1,15 @@
using ARMeilleure; using ARMeilleure;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.UI.Common.Configuration.System;
using Ryujinx.UI.Common.Helper;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using RyuLogger = Ryujinx.Common.Logging.Logger;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
public partial class ConfigurationState public partial class ConfigurationState
{ {
@@ -620,67 +617,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
} }
} }
public class HacksSection
{
/// <summary>
/// Show toggles for dirty hacks in the UI.
/// </summary>
public ReactiveObject<bool> ShowDirtyHacks { get; private set; }
public ReactiveObject<bool> Xc2MenuSoftlockFix { get; private set; }
public ReactiveObject<bool> EnableShaderTranslationDelay { get; private set; }
public ReactiveObject<int> ShaderTranslationDelay { get; private set; }
public HacksSection()
{
ShowDirtyHacks = new ReactiveObject<bool>();
Xc2MenuSoftlockFix = new ReactiveObject<bool>();
Xc2MenuSoftlockFix.Event += HackChanged;
EnableShaderTranslationDelay = new ReactiveObject<bool>();
EnableShaderTranslationDelay.Event += HackChanged;
ShaderTranslationDelay = new ReactiveObject<int>();
}
private void HackChanged(object sender, ReactiveEventArgs<bool> rxe)
{
var newHacks = EnabledHacks.Select(x => x.Hack)
.JoinToString(", ");
if (newHacks != _lastHackCollection)
{
RyuLogger.Info?.Print(LogClass.Configuration,
$"EnabledDirtyHacks set to: [{newHacks}]", "LogValueChange");
_lastHackCollection = newHacks;
}
}
private static string _lastHackCollection;
public EnabledDirtyHack[] EnabledHacks
{
get
{
List<EnabledDirtyHack> enabledHacks = [];
if (Xc2MenuSoftlockFix)
Apply(DirtyHacks.Xc2MenuSoftlockFix);
if (EnableShaderTranslationDelay)
Apply(DirtyHacks.ShaderCompilationThreadSleep, ShaderTranslationDelay);
return enabledHacks.ToArray();
void Apply(DirtyHacks hack, int value = 0)
{
enabledHacks.Add(new EnabledDirtyHack(hack, value));
}
}
}
}
/// <summary> /// <summary>
/// The default configuration instance /// The default configuration instance
/// </summary> /// </summary>
@@ -715,11 +651,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// The Multiplayer section /// The Multiplayer section
/// </summary> /// </summary>
public MultiplayerSection Multiplayer { get; private set; } public MultiplayerSection Multiplayer { get; private set; }
/// <summary>
/// The Dirty Hacks section
/// </summary>
public HacksSection Hacks { get; private set; }
/// <summary> /// <summary>
/// Enables or disables Discord Rich Presence /// Enables or disables Discord Rich Presence
@@ -769,7 +700,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
Graphics = new GraphicsSection(); Graphics = new GraphicsSection();
Hid = new HidSection(); Hid = new HidSection();
Multiplayer = new MultiplayerSection(); Multiplayer = new MultiplayerSection();
Hacks = new HacksSection();
EnableDiscordIntegration = new ReactiveObject<bool>(); EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>(); CheckUpdatesOnStart = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>(); ShowConfirmExit = new ReactiveObject<bool>();

View File

@@ -1,15 +1,14 @@
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.UI.Common.Configuration.System;
using Ryujinx.UI.Common.Configuration.UI;
using System; using System;
using System.Linq;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
public partial class ConfigurationState public partial class ConfigurationState
{ {
@@ -139,8 +138,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
MultiplayerDisableP2p = Multiplayer.DisableP2p, MultiplayerDisableP2p = Multiplayer.DisableP2p,
MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase, MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase,
LdnServer = Multiplayer.LdnServer, LdnServer = Multiplayer.LdnServer,
ShowDirtyHacks = Hacks.ShowDirtyHacks,
DirtyHacks = Hacks.EnabledHacks.Select(it => it.Pack()).ToArray(),
}; };
return configurationFile; return configurationFile;

View File

@@ -0,0 +1,12 @@
namespace Ryujinx.UI.Common
{
public enum FileTypes
{
NSP,
PFS0,
XCI,
NCA,
NRO,
NSO
}
}

View File

@@ -4,7 +4,7 @@ using Ryujinx.Common.Logging.Targets;
using System; using System;
using System.IO; using System.IO;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
public static class LoggerModule public static class LoggerModule
{ {

View File

@@ -1,7 +1,7 @@
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration.System namespace Ryujinx.UI.Common.Configuration.System
{ {
[JsonConverter(typeof(TypedStringEnumConverter<Language>))] [JsonConverter(typeof(TypedStringEnumConverter<Language>))]
public enum Language public enum Language

View File

@@ -1,7 +1,7 @@
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration.System namespace Ryujinx.UI.Common.Configuration.System
{ {
[JsonConverter(typeof(TypedStringEnumConverter<Region>))] [JsonConverter(typeof(TypedStringEnumConverter<Region>))]
public enum Region public enum Region

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI namespace Ryujinx.UI.Common.Configuration.UI
{ {
public struct ColumnSort public struct ColumnSort
{ {

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI namespace Ryujinx.UI.Common.Configuration.UI
{ {
public struct GuiColumns public struct GuiColumns
{ {

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI namespace Ryujinx.UI.Common.Configuration.UI
{ {
public struct ShownFileTypes public struct ShownFileTypes
{ {

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI namespace Ryujinx.UI.Common.Configuration.UI
{ {
public struct WindowStartup public struct WindowStartup
{ {

View File

@@ -1,50 +1,129 @@
using Gommon; using DiscordRPC;
using Ryujinx.Common.Configuration; using Humanizer;
using System; using Humanizer.Localisation;
using Ryujinx.Common;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Configuration;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Text;
namespace Ryujinx.Common namespace Ryujinx.UI.Common
{ {
public static class TitleIDs public static class DiscordIntegrationModule
{ {
public static ReactiveObject<Optional<string>> CurrentApplication { get; set; } = new(); public static Timestamps StartedAt { get; set; }
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend) private static string VersionString
=> (ReleaseInformation.IsCanaryBuild ? "Canary " : string.Empty) + $"v{ReleaseInformation.Version}";
private static readonly string _description =
ReleaseInformation.IsValid
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
: "dev build";
private const string ApplicationId = "1293250299716173864";
private const int ApplicationByteLimit = 128;
private const string Ellipsis = "…";
private static DiscordRpcClient _discordClient;
private static RichPresence _discordPresenceMain;
public static void Initialize()
{ {
switch (currentBackend) _discordPresenceMain = new RichPresence
{ {
case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS(): Assets = new Assets
return GraphicsBackend.Vulkan; {
case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal: LargeImageKey = "ryujinx",
return currentBackend; LargeImageText = TruncateToByteLength(_description)
},
Details = "Main Menu",
State = "Idling",
Timestamps = StartedAt
};
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
}
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
{
if (evnt.OldValue != evnt.NewValue)
{
// If the integration was active, disable it and unload everything
if (evnt.OldValue)
{
_discordClient?.Dispose();
_discordClient = null;
}
// If we need to activate it and the client isn't active, initialize it
if (evnt.NewValue && _discordClient == null)
{
_discordClient = new DiscordRpcClient(ApplicationId);
_discordClient.Initialize();
_discordClient.SetPresence(_discordPresenceMain);
}
}
}
public static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
{
_discordClient?.SetPresence(new RichPresence
{
Assets = new Assets
{
LargeImageKey = _discordGameAssetKeys.Contains(procRes.ProgramIdText) ? procRes.ProgramIdText : "game",
LargeImageText = TruncateToByteLength($"{appMeta.Title} (v{procRes.DisplayVersion})"),
SmallImageKey = "ryujinx",
SmallImageText = TruncateToByteLength(_description)
},
Details = TruncateToByteLength($"Playing {appMeta.Title}"),
State = appMeta.LastPlayed.HasValue && appMeta.TimePlayed.TotalSeconds > 5
? $"Total play time: {appMeta.TimePlayed.Humanize(2, false, maxUnit: TimeUnit.Hour)}"
: "Never played",
Timestamps = Timestamps.Now
});
}
public static void SwitchToMainState() => _discordClient?.SetPresence(_discordPresenceMain);
private static string TruncateToByteLength(string input)
{
if (Encoding.UTF8.GetByteCount(input) <= ApplicationByteLimit)
{
return input;
} }
if (!(OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture is Architecture.Arm64)) // Find the length to trim the string to guarantee we have space for the trailing ellipsis.
return GraphicsBackend.Vulkan; int trimLimit = ApplicationByteLimit - Encoding.UTF8.GetByteCount(Ellipsis);
return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan; // Make sure the string is long enough to perform the basic trim.
// Amount of bytes != Length of the string
if (input.Length > trimLimit)
{
// Basic trim to best case scenario of 1 byte characters.
input = input[..trimLimit];
}
while (Encoding.UTF8.GetByteCount(input) > trimLimit)
{
// Remove one character from the end of the string at a time.
input = input[..^1];
}
return input.TrimEnd() + Ellipsis;
} }
public static readonly string[] GreatMetalTitles =
[
"01006f8002326000", // Animal Crossings: New Horizons
"01009bf0072d4000", // Captain Toad: Treasure Tracker
"0100a5c00d162000", // Cuphead
"010023800d64a000", // Deltarune
"010028600EBDA000", // Mario 3D World
"0100152000022000", // Mario Kart 8 Deluxe
"01005CA01580E000", // Persona 5
"01001f5010dfa000", // Pokemon Legends Arceus
"01008C0016544000", // Sea of Stars
"01006A800016E000", // Smash Ultimate
"0100000000010000", // Super Mario Odyessy
];
public static string GetDiscordGameAsset(string titleId)
=> DiscordGameAssetKeys.Contains(titleId) ? titleId : "game";
public static readonly string[] DiscordGameAssetKeys = public static void Exit()
{
_discordClient?.Dispose();
}
private static readonly string[] _discordGameAssetKeys =
[ [
"010055d009f78000", // Fire Emblem: Three Houses "010055d009f78000", // Fire Emblem: Three Houses
"0100a12011cc8000", // Fire Emblem: Shadow Dragon "0100a12011cc8000", // Fire Emblem: Shadow Dragon

View File

@@ -1,19 +1,8 @@
using System; using System;
using static Ryujinx.UI.Common.Configuration.ConfigurationState.UISection;
using static Ryujinx.Ava.Utilities.Configuration.ConfigurationState.UISection; namespace Ryujinx.UI.Common
namespace Ryujinx.Ava.Utilities.Configuration
{ {
public enum FileTypes
{
NSP,
PFS0,
XCI,
NCA,
NRO,
NSO
}
public static class FileTypesExtensions public static class FileTypesExtensions
{ {
/// <summary> /// <summary>
@@ -21,7 +10,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary> /// </summary>
/// <param name="type">The name of the <see cref="ShownFileTypeSettings"/> parameter to get the value of.</param> /// <param name="type">The name of the <see cref="ShownFileTypeSettings"/> parameter to get the value of.</param>
/// <param name="config">The config instance to get the value from.</param> /// <param name="config">The config instance to get the value from.</param>
/// <returns>The current value of the setting. Value is <see langword="true"/> if the file type is to be shown on the games list, <see langword="false"/> otherwise.</returns> /// <returns>The current value of the setting. Value is <see langword="true"/> if the file type is the be shown on the games list, <see langword="false"/> otherwise.</returns>
public static bool GetConfigValue(this FileTypes type, ShownFileTypeSettings config) => type switch public static bool GetConfigValue(this FileTypes type, ShownFileTypeSettings config) => type switch
{ {
FileTypes.NSP => config.NSP.Value, FileTypes.NSP => config.NSP.Value,

View File

@@ -1,7 +1,7 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Ava.Utilities namespace Ryujinx.UI.Common.Helper
{ {
public static class CommandLineState public static class CommandLineState
{ {

View File

@@ -3,7 +3,7 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
namespace Ryujinx.Common.Helper namespace Ryujinx.UI.Common.Helper
{ {
public static partial class ConsoleHelper public static partial class ConsoleHelper
{ {

View File

@@ -3,18 +3,18 @@ using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using Ryujinx.UI.Common.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Path = System.IO.Path; using Path = System.IO.Path;
namespace Ryujinx.Ava.Utilities namespace Ryujinx.UI.Common.Helper
{ {
public static class DownloadableContentsHelper public static class DownloadableContentsHelper
{ {

View File

@@ -8,7 +8,7 @@ using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
namespace Ryujinx.Common.Helper namespace Ryujinx.UI.Common.Helper
{ {
public static partial class FileAssociationHelper public static partial class FileAssociationHelper
{ {
@@ -132,7 +132,7 @@ namespace Ryujinx.Common.Helper
if (uninstall) if (uninstall)
{ {
// If the types don't already exist, there's nothing to do, and we can call this operation successful. // If the types don't already exist, there's nothing to do and we can call this operation successful.
if (!AreMimeTypesRegisteredWindows()) if (!AreMimeTypesRegisteredWindows())
{ {
return true; return true;

View File

@@ -3,7 +3,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.Versioning; using System.Runtime.Versioning;
namespace Ryujinx.Common.Helper namespace Ryujinx.UI.Common.Helper
{ {
[SupportedOSPlatform("linux")] [SupportedOSPlatform("linux")]
public static class LinuxHelper public static class LinuxHelper

View File

@@ -2,7 +2,7 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
namespace Ryujinx.Common.Helper namespace Ryujinx.UI.Common.Helper
{ {
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
public static partial class ObjectiveC public static partial class ObjectiveC

View File

@@ -5,7 +5,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Common.Helper namespace Ryujinx.UI.Common.Helper
{ {
public static partial class OpenHelper public static partial class OpenHelper
{ {

View File

@@ -1,10 +1,9 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.UI;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using System; using System;
using System.IO; using System.IO;
namespace Ryujinx.Ava.Utilities namespace Ryujinx.UI.Common.Helper
{ {
/// <summary> /// <summary>
/// Ensure installation validity /// Ensure installation validity

View File

@@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.Versioning; using System.Runtime.Versioning;
namespace Ryujinx.Ava.Utilities namespace Ryujinx.UI.Common.Helper
{ {
public static class ShortcutHelper public static class ShortcutHelper
{ {
@@ -31,7 +31,7 @@ namespace Ryujinx.Ava.Utilities
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName) private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName)
{ {
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh"); string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh");
var desktopFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.desktop"); var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop");
iconPath += ".png"; iconPath += ".png";
var image = SKBitmap.Decode(iconData); var image = SKBitmap.Decode(iconData);
@@ -47,8 +47,8 @@ namespace Ryujinx.Ava.Utilities
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName) private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName)
{ {
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"); string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
var plistFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.plist"); var plistFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.plist");
var shortcutScript = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-launch-script.sh"); var shortcutScript = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-launch-script.sh");
// Macos .App folder // Macos .App folder
string contentFolderPath = Path.Combine("/Applications", cleanedAppName + ".app", "Contents"); string contentFolderPath = Path.Combine("/Applications", cleanedAppName + ".app", "Contents");
string scriptFolderPath = Path.Combine(contentFolderPath, "MacOS"); string scriptFolderPath = Path.Combine(contentFolderPath, "MacOS");

View File

@@ -1,6 +1,7 @@
using Ryujinx.HLE.Loaders.Processes; using Ryujinx.HLE.Loaders.Processes;
using System;
namespace Ryujinx.Ava.Utilities namespace Ryujinx.UI.Common.Helper
{ {
public static class TitleHelper public static class TitleHelper
{ {

View File

@@ -6,14 +6,14 @@ using LibHac.Ncm;
using LibHac.Ns; using LibHac.Ns;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -22,7 +22,7 @@ using Path = System.IO.Path;
using SpanHelpers = LibHac.Common.SpanHelpers; using SpanHelpers = LibHac.Common.SpanHelpers;
using TitleUpdateMetadata = Ryujinx.Common.Configuration.TitleUpdateMetadata; using TitleUpdateMetadata = Ryujinx.Common.Configuration.TitleUpdateMetadata;
namespace Ryujinx.Ava.Utilities namespace Ryujinx.UI.Common.Helper
{ {
public static class TitleUpdatesHelper public static class TitleUpdatesHelper
{ {

View File

@@ -1,9 +1,8 @@
using Ryujinx.Ava.Common.Locale;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
namespace Ryujinx.Ava.Utilities namespace Ryujinx.UI.Common.Helper
{ {
public static class ValueFormatUtils public static class ValueFormatUtils
{ {
@@ -76,7 +75,7 @@ namespace Ryujinx.Ava.Utilities
{ {
culture ??= CultureInfo.CurrentCulture; culture ??= CultureInfo.CurrentCulture;
return utcDateTime?.ToLocalTime().ToString(culture) ?? LocaleManager.Instance[LocaleKeys.Never]; return utcDateTime?.ToLocalTime().ToString(culture);
} }
/// <summary> /// <summary>

View File

@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Common.Models.Amiibo namespace Ryujinx.UI.Common.Models.Amiibo
{ {
public struct AmiiboApi : IEquatable<AmiiboApi> public struct AmiiboApi : IEquatable<AmiiboApi>
{ {

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Common.Models.Amiibo namespace Ryujinx.UI.Common.Models.Amiibo
{ {
public class AmiiboApiGamesSwitch public class AmiiboApiGamesSwitch
{ {

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Common.Models.Amiibo namespace Ryujinx.UI.Common.Models.Amiibo
{ {
public class AmiiboApiUsage public class AmiiboApiUsage
{ {

View File

@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Common.Models.Amiibo namespace Ryujinx.UI.Common.Models.Amiibo
{ {
public struct AmiiboJson public struct AmiiboJson
{ {

View File

@@ -1,7 +1,9 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Common.Models.Amiibo namespace Ryujinx.UI.Common.Models.Amiibo
{ {
[JsonSerializable(typeof(AmiiboJson))] [JsonSerializable(typeof(AmiiboJson))]
public partial class AmiiboJsonSerializerContext : JsonSerializerContext; public partial class AmiiboJsonSerializerContext : JsonSerializerContext
{
}
} }

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Common.Models namespace Ryujinx.UI.Common.Models
{ {
// NOTE: most consuming code relies on this model being value-comparable // NOTE: most consuming code relies on this model being value-comparable
public record DownloadableContentModel(ulong TitleId, string ContainerPath, string FullPath) public record DownloadableContentModel(ulong TitleId, string ContainerPath, string FullPath)

Some files were not shown because too many files have changed in this diff Show More