diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ba1a2df9c..b678e5f8e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -61,11 +61,11 @@ jobs:
if: matrix.platform.name != 'linux-arm64'
- name: Publish Ryujinx
- run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
+ run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Headless.SDL2
- run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
+ run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Set executable bit
@@ -74,36 +74,36 @@ jobs:
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
- #- name: Build AppImage
- # if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
- # run: |
- # PLATFORM_NAME="${{ matrix.platform.name }}"
+ - name: Build AppImage
+ if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
+ run: |
+ PLATFORM_NAME="${{ matrix.platform.name }}"
- # sudo apt install -y zsync desktop-file-utils appstream
+ sudo apt install -y zsync desktop-file-utils appstream
- # mkdir -p tools
- # export PATH="$PATH:$(readlink -f tools)"
+ mkdir -p tools
+ export PATH="$PATH:$(readlink -f tools)"
- # # Setup appimagetool
- # wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
- # chmod +x tools/appimagetool
- # chmod +x distribution/linux/appimage/build-appimage.sh
+ # Setup appimagetool
+ wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
+ chmod +x tools/appimagetool
+ chmod +x distribution/linux/appimage/build-appimage.sh
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
- # if [ "$PLATFORM_NAME" = "linux-x64" ]; then
- # ARCH_NAME=x64
- # export ARCH=x86_64
- # elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
- # ARCH_NAME=arm64
- # export ARCH=aarch64
- # else
- # echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
- # exit 1
- # fi
+ if [ "$PLATFORM_NAME" = "linux-x64" ]; then
+ ARCH_NAME=x64
+ export ARCH=x86_64
+ elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
+ ARCH_NAME=arm64
+ export ARCH=aarch64
+ else
+ echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
+ exit 1
+ fi
- # export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
- # BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
- # shell: bash
+ export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
+ BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
+ shell: bash
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v4
@@ -112,12 +112,12 @@ jobs:
path: publish
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- #- name: Upload Ryujinx (AppImage) artifact
- # uses: actions/upload-artifact@v4
- # if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
- # with:
- # name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage
- # path: publish_appimage
+ - name: Upload Ryujinx (AppImage) artifact
+ uses: actions/upload-artifact@v4
+ if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
+ with:
+ name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage
+ path: publish_appimage
- name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml
new file mode 100644
index 000000000..d102beaa3
--- /dev/null
+++ b/.github/workflows/canary.yml
@@ -0,0 +1,257 @@
+name: Canary release job
+
+on:
+ workflow_dispatch:
+ inputs: {}
+ push:
+ branches: [ master ]
+ paths-ignore:
+ - '.github/**'
+ - 'docs/**'
+ - 'assets/**'
+ - '*.yml'
+ - '*.json'
+ - '*.config'
+ - '*.md'
+
+concurrency: release
+
+env:
+ POWERSHELL_TELEMETRY_OPTOUT: 1
+ DOTNET_CLI_TELEMETRY_OPTOUT: 1
+ RYUJINX_BASE_VERSION: "1.2"
+ RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
+ RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
+ RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
+ RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx-Canary"
+ RELEASE: 1
+
+jobs:
+ tag:
+ name: Create tag
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Get version info
+ id: version_info
+ run: |
+ echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
+ echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - name: Create tag
+ uses: actions/github-script@v7
+ with:
+ script: |
+ github.rest.git.createRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: 'refs/tags/Canary-${{ steps.version_info.outputs.build_version }}',
+ sha: context.sha
+ })
+
+ - name: Create release
+ uses: ncipollo/release-action@v1
+ with:
+ name: "Canary ${{ steps.version_info.outputs.build_version }}"
+ tag: ${{ steps.version_info.outputs.build_version }}
+ body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
+ omitBodyDuringUpdate: true
+ owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
+ repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
+ token: ${{ secrets.RELEASE_TOKEN }}
+
+ release:
+ name: Release for ${{ matrix.platform.name }}
+ runs-on: ${{ matrix.platform.os }}
+ strategy:
+ matrix:
+ platform:
+ - { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
+ - { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
+ - { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-dotnet@v4
+ with:
+ global-json-file: global.json
+
+ - name: Overwrite csc problem matcher
+ run: echo "::add-matcher::.github/csc.json"
+
+ - name: Get version info
+ id: version_info
+ run: |
+ echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
+ echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
+ echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - name: Configure for release
+ run: |
+ sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ shell: bash
+
+ - name: Create output dir
+ run: "mkdir release_output"
+
+ - name: Publish
+ run: |
+ dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
+ dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
+
+ - name: Packing Windows builds
+ if: matrix.platform.os == 'windows-latest'
+ run: |
+ pushd publish_ava
+ rm publish/libarmeilleure-jitsupport.dylib
+ 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
+ popd
+
+ pushd publish_sdl2_headless
+ rm publish/libarmeilleure-jitsupport.dylib
+ 7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
+ popd
+ shell: bash
+
+ - name: Packing Linux builds
+ if: matrix.platform.os == 'ubuntu-latest'
+ run: |
+ pushd publish_ava
+ rm publish/libarmeilleure-jitsupport.dylib
+ chmod +x publish/Ryujinx.sh publish/Ryujinx
+ tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
+ popd
+
+ pushd publish_sdl2_headless
+ rm publish/libarmeilleure-jitsupport.dylib
+ chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
+ tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
+ popd
+ shell: bash
+
+ #- name: Build AppImage (Linux)
+ # if: matrix.platform.os == 'ubuntu-latest'
+ # run: |
+ # BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
+ # PLATFORM_NAME="${{ matrix.platform.name }}"
+
+ # sudo apt install -y zsync desktop-file-utils appstream
+
+ # mkdir -p tools
+ # export PATH="$PATH:$(readlink -f tools)"
+
+ # Setup appimagetool
+ # wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
+ # chmod +x tools/appimagetool
+ # chmod +x distribution/linux/appimage/build-appimage.sh
+
+ # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
+ # if [ "$PLATFORM_NAME" = "linux-x64" ]; then
+ # ARCH_NAME=x64
+ # export ARCH=x86_64
+ # elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
+ # ARCH_NAME=arm64
+ # export ARCH=aarch64
+ # else
+ # echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
+ # exit 1
+ # fi
+
+ # export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
+ # BUILDDIR=publish_ava OUTDIR=publish_ava_appimage distribution/linux/appimage/build-appimage.sh
+
+ # Add to release output
+ # pushd publish_ava_appimage
+ # mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
+ # mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
+ # popd
+ # shell: bash
+
+ - name: Pushing new release
+ uses: ncipollo/release-action@v1
+ with:
+ name: ${{ steps.version_info.outputs.build_version }}
+ artifacts: "release_output/*.tar.gz,release_output/*.zip"
+ #artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*"
+ tag: ${{ steps.version_info.outputs.build_version }}
+ body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
+ omitBodyDuringUpdate: true
+ allowUpdates: true
+ replacesArtifacts: true
+ owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
+ repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
+ token: ${{ secrets.RELEASE_TOKEN }}
+
+ macos_release:
+ name: Release MacOS universal
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-dotnet@v4
+ with:
+ global-json-file: global.json
+
+ - name: Setup LLVM 15
+ run: |
+ wget https://apt.llvm.org/llvm.sh
+ chmod +x llvm.sh
+ sudo ./llvm.sh 15
+
+ - name: Install rcodesign
+ run: |
+ mkdir -p $HOME/.bin
+ gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
+ tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
+ rm apple-codesign.tar.gz
+ mv rcodesign $HOME/.bin/
+ echo "$HOME/.bin" >> $GITHUB_PATH
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Get version info
+ id: version_info
+ run: |
+ echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
+ echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
+ echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
+
+ - name: Configure for release
+ run: |
+ sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ shell: bash
+
+ - name: Publish macOS Ryujinx
+ 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
+
+ - 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
+
+ - name: Pushing new release
+ uses: ncipollo/release-action@v1
+ with:
+ name: "Canary ${{ steps.version_info.outputs.build_version }}"
+ artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
+ tag: ${{ steps.version_info.outputs.build_version }}
+ body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
+ omitBodyDuringUpdate: true
+ allowUpdates: true
+ replacesArtifacts: true
+ owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
+ repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
+ token: ${{ secrets.RELEASE_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3caedacfc..183d4c618 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs: {}
push:
- branches: [ master ]
+ branches: [ release ]
paths-ignore:
- '.github/**'
- 'docs/**'
@@ -20,7 +20,7 @@ env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.2"
- RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
+ RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
RELEASE: 1
@@ -93,6 +93,7 @@ jobs:
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
@@ -101,79 +102,79 @@ jobs:
- name: Publish
run: |
- dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
- dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
+ dotnet publish -c Release -r "${{ matrix.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 -p:IncludeNativeLibrariesForSelfExtract=true
+ 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 -p:IncludeNativeLibrariesForSelfExtract=true
- name: Packing Windows builds
if: matrix.platform.os == 'windows-latest'
run: |
- pushd publish_ava
- 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
+ pushd publish
+ rm libarmeilleure-jitsupport.dylib
+ 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd
pushd publish_sdl2_headless
- 7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
+ rm libarmeilleure-jitsupport.dylib
+ 7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd
shell: bash
+
+ - name: Build AppImage (Linux)
+ if: matrix.platform.os == 'ubuntu-latest'
+ run: |
+ BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
+ PLATFORM_NAME="${{ matrix.platform.name }}"
+
+ sudo apt install -y zsync desktop-file-utils appstream
+
+ mkdir -p tools
+ export PATH="$PATH:$(readlink -f tools)"
+
+ # Setup appimagetool
+ wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
+ chmod +x tools/appimagetool
+ chmod +x distribution/linux/appimage/build-appimage.sh
+
+ # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
+ if [ "$PLATFORM_NAME" = "linux-x64" ]; then
+ ARCH_NAME=x64
+ export ARCH=x86_64
+ elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
+ ARCH_NAME=arm64
+ export ARCH=aarch64
+ else
+ echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
+ exit 1
+ fi
+
+ export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
+ BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
+
+ pushd publish_appimage
+ mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
+ mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
+ popd
+ shell: bash
- name: Packing Linux builds
if: matrix.platform.os == 'ubuntu-latest'
run: |
- pushd publish_ava
- chmod +x publish/Ryujinx.sh publish/Ryujinx
- tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
+ pushd publish
+ chmod +x Ryujinx.sh Ryujinx
+ tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd
pushd publish_sdl2_headless
- chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
- tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
+ chmod +x Ryujinx.sh Ryujinx.Headless.SDL2
+ tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd
shell: bash
-
- #- name: Build AppImage (Linux)
- # if: matrix.platform.os == 'ubuntu-latest'
- # run: |
- # BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
- # PLATFORM_NAME="${{ matrix.platform.name }}"
-
- # sudo apt install -y zsync desktop-file-utils appstream
-
- # mkdir -p tools
- # export PATH="$PATH:$(readlink -f tools)"
-
- # Setup appimagetool
- # wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
- # chmod +x tools/appimagetool
- # chmod +x distribution/linux/appimage/build-appimage.sh
-
- # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
- # if [ "$PLATFORM_NAME" = "linux-x64" ]; then
- # ARCH_NAME=x64
- # export ARCH=x86_64
- # elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
- # ARCH_NAME=arm64
- # export ARCH=aarch64
- # else
- # echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
- # exit 1
- # fi
-
- # export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
- # BUILDDIR=publish_ava OUTDIR=publish_ava_appimage distribution/linux/appimage/build-appimage.sh
-
- # Add to release output
- # pushd publish_ava_appimage
- # mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
- # mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
- # popd
- # shell: bash
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
- artifacts: "release_output/*.tar.gz,release_output/*.zip"
- #artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*"
+ artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*"
tag: ${{ steps.version_info.outputs.build_version }}
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
omitBodyDuringUpdate: true
@@ -224,12 +225,13 @@ jobs:
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
+ sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
- name: Publish macOS Ryujinx
run: |
- ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
+ ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
- name: Publish macOS Ryujinx.Headless.SDL2
run: |
@@ -239,7 +241,7 @@ jobs:
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
- artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
+ artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }}
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
omitBodyDuringUpdate: true
diff --git a/COMPILING.md b/COMPILING.md
new file mode 100644
index 000000000..06cebab44
--- /dev/null
+++ b/COMPILING.md
@@ -0,0 +1,23 @@
+## Compilation
+
+Building the project is for advanced users.
+If you wish to build the emulator yourself, follow these steps:
+
+### Step 1
+
+Install the [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
+Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
+
+### Step 2
+
+Either use `git clone https://github.com/GreemDev/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
+
+### Step 3
+
+To build Ryujinx, open a command prompt inside the project directory.
+You can quickly access it on Windows by holding shift in File Explorer, then right clicking and selecting `Open command window here`.
+Then type the following command: `dotnet build -c Release -o build`
+the built files will be found in the newly created build directory.
+
+Ryujinx system files are stored in the `Ryujinx` folder.
+This folder is located in the user folder, which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI.
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 366eb8435..af47fc9d9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,24 +12,15 @@ Please read the entire document before continuing as it can potentially save eve
We always welcome bug reports, feature proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible.
-### Identify Where to Report
-
-The Ryujinx codebase is distributed across multiple repositories in the [Ryujinx organization](https://github.com/Ryujinx). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos:
-
-* [Ryujinx/Ryujinx](https://github.com/Ryujinx/Ryujinx) Ryujinx core project files.
-* [Ryujinx/Ryujinx-Games-List](https://github.com/Ryujinx/Ryujinx-Games-List) Ryujinx game compatibility list.
-* [Ryujinx/Ryujinx-Website](https://github.com/Ryujinx/Ryujinx-Website) Ryujinx website source code.
-* [Ryujinx/Ryujinx-Ldn-Website](https://github.com/Ryujinx/Ryujinx-Ldn-Website) Ryujinx LDN website source code.
-
### Finding Existing Issues
-Before filing a new issue, please search our [open issues](https://github.com/Ryujinx/Ryujinx/issues) to check if it already exists.
+Before filing a new issue, please search our [open issues](https://github.com/GreemDev/Ryujinx/issues) to check if it already exists.
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
### Writing a Good Feature Request
-Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
+Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/GreemDev/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
### Writing a Good Bug Report
@@ -43,13 +34,13 @@ Ideally, a bug report should contain the following information:
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
-When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
+When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/GreemDev/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
## Contributing Changes
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
-The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
+The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/GreemDev/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
### DOs and DON'Ts
@@ -83,15 +74,15 @@ We use and recommend the following workflow:
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
4. Make and commit your changes to your branch.
- - [Build Instructions](https://github.com/Ryujinx/Ryujinx#building) explains how to build and test.
+ - [Build Instructions](https://github.com/GreemDev/Ryujinx#building) explains how to build and test.
- Commit messages should be clear statements of action and intent.
6. Build the repository with your changes.
- Make sure that the builds are clean.
- Make sure that `dotnet format` has been run and any corrections tested and committed.
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
- State in the description what issue or improvement your change is addressing.
- - Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/Ryujinx/Ryujinx/actions) to check for outstanding errors.
-8. Wait for feedback or approval of your changes from the [core development team](https://github.com/orgs/Ryujinx/teams/developers)
+ - Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/GreemDev/Ryujinx/actions) to check for outstanding errors.
+8. Wait for feedback or approval of your changes from the core development team
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
9. When the team members have signed off, and all checks are green, your PR will be merged.
- The next official build will automatically include your change.
@@ -99,7 +90,7 @@ We use and recommend the following workflow:
### Good First Issues
-The team marks the most straightforward issues as [good first issues](https://github.com/Ryujinx/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
+The team marks the most straightforward issues as [good first issues](https://github.com/GreemDev/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
### Commit Messages
@@ -122,7 +113,7 @@ Also do your best to factor commits appropriately, not too large with unrelated
### PR - CI Process
-The [Ryujinx continuous integration](https://github.com/Ryujinx/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
+The [Ryujinx continuous integration](https://github.com/GreemDev/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
* `dotnet format` has not been run on the PR and has outstanding stylistic issues.
@@ -143,5 +134,5 @@ Ryujinx uses some implementations and frameworks from other projects. The follow
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
- The license of the file is left in-tact.
-- The contribution is correctly attributed in the [3rd party notices](https://github.com/Ryujinx/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.
+- The contribution is correctly attributed in the [3rd party notices](https://github.com/GreemDev/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.
diff --git a/Directory.Packages.props b/Directory.Packages.props
index e6be60790..c0ace079d 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -22,7 +22,7 @@
-
+
@@ -33,6 +33,7 @@
+
diff --git a/README.md b/README.md
index 22f601083..3bc223f3a 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,23 @@
(REE-YOU-JINX)
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -17,40 +34,28 @@
On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project.
- This fork is intended to be a direct continuation for existing Ryujinx users.
- Guides and documentation will not be provided at this time, though you can find the old ones on the Internet Archive.
+
+ This fork is intended to be a QoL uplift for existing Ryujinx users.
+
+ This is not a Ryujinx revival project. This is not a Phoenix project.
+
+ Guides and documentation can be found on the Wiki tab .
- If you would like a version more true to original Ryujinx, check out ryujinx-mirror .
+ If you would like a version more preservative fork of Ryujinx, check out ryujinx-mirror .
-
-
-
-
-
-
+ Click below to join the Discord:
+
-
+
-## Compatibility
-
-As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
-over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
-
-Anyone is free to submit a new game test or update an existing game test entry;
-simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue.
-Use the search function to see if a game has been tested already!
-
## Usage
To run this emulator, your PC must be equipped with at least 8GiB of RAM;
@@ -58,36 +63,21 @@ failing to meet this requirement may result in a poor gameplay experience or une
## Latest build
-These builds are compiled automatically for each commit on the master branch.
-While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken**.
+Stable builds are made every so often onto a separate "release" branch that then gets put into the releases you know and love.
+These stable builds exist so that the end user can get a more **enjoyable and stable experience**.
+
+You can find the latest stable release [here](https://github.com/GreemDev/Ryujinx/releases/latest).
+
+Canary builds are compiled automatically for each commit on the master branch.
+While we strive to ensure optimal stability and performance prior to pushing an update, these builds **may be unstable or completely broken**.
+These canary builds are only recommended for experienced users.
+
+You can find the latest canary release [here](https://github.com/GreemDev/Ryujinx-Canary/releases/latest).
## Documentation
If you are planning to contribute or just want to learn more about this project please read through our [documentation](docs/README.md).
-## Building
-
-If you wish to build the emulator yourself, follow these steps:
-
-### Step 1
-
-Install the [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
-Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
-
-### Step 2
-
-Either use `git clone https://github.com/GreemDev/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
-
-### Step 3
-
-To build Ryujinx, open a command prompt inside the project directory.
-You can quickly access it on Windows by holding shift in File Explorer, then right clicking and selecting `Open command window here`.
-Then type the following command: `dotnet build -c Release -o build`
-the built files will be found in the newly created build directory.
-
-Ryujinx system files are stored in the `Ryujinx` folder.
-This folder is located in the user folder, which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI.
-
## Features
- **Audio**
diff --git a/Ryujinx.sln b/Ryujinx.sln
index 8a582d32d..d661b903c 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -29,12 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- Directory.Packages.props = Directory.Packages.props
- EndProjectSection
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
@@ -87,6 +81,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ Directory.Packages.props = Directory.Packages.props
+ .github/workflows/release.yml = .github/workflows/release.yml
+ .github/workflows/canary.yml = .github/workflows/canary.yml
+ .github/workflows/build.yml = .github/workflows/build.yml
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/distribution/macos/Ryujinx.icns b/distribution/macos/Ryujinx.icns
index f54a9aeb7..1bb88a5d7 100644
Binary files a/distribution/macos/Ryujinx.icns and b/distribution/macos/Ryujinx.icns differ
diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh
index 0fa54eadd..e4397da84 100755
--- a/distribution/macos/create_app_bundle.sh
+++ b/distribution/macos/create_app_bundle.sh
@@ -46,5 +46,5 @@ then
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY"
else
echo "Usign codesign for ad-hoc signing"
- codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY"
-fi
\ No newline at end of file
+ codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$APP_BUNDLE_DIRECTORY"
+fi
diff --git a/distribution/macos/create_macos_build_ava.sh b/distribution/macos/create_macos_build_ava.sh
index 6785cbb23..80bd6662c 100755
--- a/distribution/macos/create_macos_build_ava.sh
+++ b/distribution/macos/create_macos_build_ava.sh
@@ -99,7 +99,7 @@ then
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE"
else
echo "Using codesign for ad-hoc signing"
- codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE"
+ codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$UNIVERSAL_APP_BUNDLE"
fi
echo "Creating archive"
@@ -111,4 +111,4 @@ rm "$RELEASE_TAR_FILE_NAME"
popd
-echo "Done"
\ No newline at end of file
+echo "Done"
diff --git a/distribution/macos/create_macos_build_headless.sh b/distribution/macos/create_macos_build_headless.sh
index a439aef45..2715699d0 100755
--- a/distribution/macos/create_macos_build_headless.sh
+++ b/distribution/macos/create_macos_build_headless.sh
@@ -95,7 +95,7 @@ else
echo "Using codesign for ad-hoc signing"
for FILE in "$UNIVERSAL_OUTPUT"/*; do
if [[ $(file "$FILE") == *"Mach-O"* ]]; then
- codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$FILE"
+ codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$FILE"
fi
done
fi
@@ -108,4 +108,4 @@ gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
rm "$RELEASE_TAR_FILE_NAME"
popd
-echo "Done"
\ No newline at end of file
+echo "Done"
diff --git a/distribution/misc/Logo.svg b/distribution/misc/Logo.svg
index d6a76312a..d3327f2ef 100644
--- a/distribution/misc/Logo.svg
+++ b/distribution/misc/Logo.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/shell.png b/docs/shell.png
index 943e18b0e..f1eda13e8 100644
Binary files a/docs/shell.png and b/docs/shell.png differ
diff --git a/docs/workflow/pr-guide.md b/docs/workflow/pr-guide.md
index cc2c5900b..c03db210b 100644
--- a/docs/workflow/pr-guide.md
+++ b/docs/workflow/pr-guide.md
@@ -2,7 +2,7 @@
## Contributing Rules
-All contributions to Ryujinx/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
+All contributions to GreemDev/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
To merge pull requests, you must have write permissions in the repository.
@@ -24,7 +24,7 @@ If during the code review process a merge conflict occurs, the PR author is resp
## Pull Request Builds
-When submitting a PR to the `Ryujinx/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
+When submitting a PR to the `GreemDev/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/GreemDev/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
## Review Turnaround Times
@@ -42,7 +42,7 @@ Anyone with write access can merge a pull request manually when the following co
* The PR has been approved by two reviewers and any other objections are addressed.
* You can request follow up reviews from the original reviewers if they requested changes.
-* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of your PR.
+* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/GreemDev/Ryujinx/actions) tab of your PR.
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.
diff --git a/src/ARMeilleure/Translation/Dominance.cs b/src/ARMeilleure/Translation/Dominance.cs
index e2185bd85..b62714fdf 100644
--- a/src/ARMeilleure/Translation/Dominance.cs
+++ b/src/ARMeilleure/Translation/Dominance.cs
@@ -77,7 +77,7 @@ namespace ARMeilleure.Translation
{
continue;
}
-
+
for (int pBlkIndex = 0; pBlkIndex < block.Predecessors.Count; pBlkIndex++)
{
BasicBlock current = block.Predecessors[pBlkIndex];
diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs
index 19ca054c8..fbaa1cb3a 100644
--- a/src/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/src/ARMeilleure/Translation/PTC/Ptc.cs
@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
+using System.Linq;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -853,17 +854,15 @@ namespace ARMeilleure.Translation.PTC
}
}
- List threads = new();
- for (int i = 0; i < degreeOfParallelism; i++)
- {
- Thread thread = new(TranslateFuncs)
- {
- IsBackground = true,
- };
-
- threads.Add(thread);
- }
+ List threads = Enumerable.Range(0, degreeOfParallelism)
+ .Select(idx =>
+ new Thread(TranslateFuncs)
+ {
+ IsBackground = true,
+ Name = "Ptc.TranslateThread." + idx
+ }
+ ).ToList();
Stopwatch sw = Stopwatch.StartNew();
@@ -890,6 +889,7 @@ namespace ARMeilleure.Translation.PTC
Thread preSaveThread = new(PreSave)
{
IsBackground = true,
+ Name = "Ptc.DiskWriter"
};
preSaveThread.Start();
}
diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
index 01286992f..25f91f8e9 100644
--- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
+++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
@@ -41,7 +41,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
public OpenALHardwareDeviceDriver()
{
- _device = ALC.OpenDevice("");
+ _device = ALC.OpenDevice(string.Empty);
_context = ALC.CreateContext(_device, new ALContextAttributes());
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs
index 5cb4509ff..8b497fe2a 100644
--- a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs
+++ b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs
@@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static short GetCoefficientAtIndex(ReadOnlySpan coefficients, int index)
{
- if ((uint)index > (uint)coefficients.Length)
+ if ((uint)index >= (uint)coefficients.Length)
{
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs
index 0b1740ba2..ca8e389ba 100644
--- a/src/Ryujinx.Common/Configuration/AppDataManager.cs
+++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs
@@ -119,7 +119,7 @@ namespace Ryujinx.Common.Configuration
private static string SetUpLogsDir()
{
- string logDir = "";
+ string logDir = string.Empty;
if (Mode == LaunchMode.Portable)
{
@@ -148,7 +148,7 @@ namespace Ryujinx.Common.Configuration
catch
{
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
- logDir = "";
+ logDir = string.Empty;
}
if (string.IsNullOrEmpty(logDir))
@@ -179,7 +179,7 @@ namespace Ryujinx.Common.Configuration
catch
{
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
- logDir = "";
+ logDir = string.Empty;
}
if (string.IsNullOrEmpty(logDir))
diff --git a/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs b/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs
index 69f7d876d..be0e1518c 100644
--- a/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs
+++ b/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs
@@ -3,6 +3,7 @@ namespace Ryujinx.Common.Configuration.Multiplayer
public enum MultiplayerMode
{
Disabled,
+ LdnRyu,
LdnMitm,
}
}
diff --git a/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs b/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
index c1e9e8fbb..8d969bd6a 100644
--- a/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
+++ b/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
@@ -121,8 +121,8 @@ namespace Ryujinx.Common.GraphicsDriver
};
application.AppName.Set("Ryujinx.exe");
application.UserFriendlyName.Set("Ryujinx");
- application.Launcher.Set("");
- application.FileInFolder.Set("");
+ application.Launcher.Set(string.Empty);
+ application.FileInFolder.Set(string.Empty);
Check(NvAPI_DRS_CreateApplication(handle, profileHandle, ref application));
}
diff --git a/src/Ryujinx.Common/Logging/LogClass.cs b/src/Ryujinx.Common/Logging/LogClass.cs
index 1b404a06a..a4117580e 100644
--- a/src/Ryujinx.Common/Logging/LogClass.cs
+++ b/src/Ryujinx.Common/Logging/LogClass.cs
@@ -72,5 +72,6 @@ namespace Ryujinx.Common.Logging
TamperMachine,
UI,
Vic,
+ XCIFileTrimmer
}
}
diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs
index 8dcf1a7cf..26d343969 100644
--- a/src/Ryujinx.Common/Logging/Logger.cs
+++ b/src/Ryujinx.Common/Logging/Logger.cs
@@ -38,7 +38,7 @@ namespace Ryujinx.Common.Logging
{
if (_enabledClasses[(int)logClass])
{
- Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, "", message)));
+ Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, string.Empty, message)));
}
}
diff --git a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
index 02c6dc97b..a9dbe646a 100644
--- a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
+++ b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
@@ -30,10 +30,10 @@ namespace Ryujinx.Common.Logging.Targets
string ILogTarget.Name { get => _target.Name; }
public AsyncLogTargetWrapper(ILogTarget target)
- : this(target, -1, AsyncLogTargetOverflowAction.Block)
+ : this(target, -1)
{ }
- public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction)
+ public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block)
{
_target = target;
_messageQueue = new BlockingCollection(queueLimit);
diff --git a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
index 8d4ede96c..631df3056 100644
--- a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
+++ b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
@@ -47,7 +47,7 @@ namespace Ryujinx.Common.Logging.Targets
}
// Clean up old logs, should only keep 3
- FileInfo[] files = logDir.GetFiles("*.log").OrderBy((info => info.CreationTime)).ToArray();
+ FileInfo[] files = logDir.GetFiles("*.log").OrderBy(info => info.CreationTime).ToArray();
for (int i = 0; i < files.Length - 2; i++)
{
try
diff --git a/src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs b/src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs
new file mode 100644
index 000000000..fb11432b0
--- /dev/null
+++ b/src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Common.Utilities;
+
+namespace Ryujinx.Common.Logging
+{
+ public class XCIFileTrimmerLog : XCIFileTrimmer.ILog
+ {
+ public virtual void Progress(long current, long total, string text, bool complete)
+ {
+ }
+
+ public void Write(XCIFileTrimmer.LogType logType, string text)
+ {
+ switch (logType)
+ {
+ case XCIFileTrimmer.LogType.Info:
+ Logger.Notice.Print(LogClass.XCIFileTrimmer, text);
+ break;
+ case XCIFileTrimmer.LogType.Warn:
+ Logger.Warning?.Print(LogClass.XCIFileTrimmer, text);
+ break;
+ case XCIFileTrimmer.LogType.Error:
+ Logger.Error?.Print(LogClass.XCIFileTrimmer, text);
+ break;
+ case XCIFileTrimmer.LogType.Progress:
+ Logger.Info?.Print(LogClass.XCIFileTrimmer, text);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs
index 762c73889..fcb2229a7 100644
--- a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs
+++ b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs
@@ -803,18 +803,6 @@ namespace Ryujinx.Common.Memory
public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}
- public struct Array256 : IArray where T : unmanaged
- {
- T _e0;
- Array128 _other;
- Array127 _other2;
- public readonly int Length => 256;
- public ref T this[int index] => ref AsSpan()[index];
-
- [Pure]
- public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
- }
-
public struct Array140 : IArray where T : unmanaged
{
T _e0;
@@ -828,6 +816,18 @@ namespace Ryujinx.Common.Memory
public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}
+ public struct Array256 : IArray where T : unmanaged
+ {
+ T _e0;
+ Array128 _other;
+ Array127 _other2;
+ public readonly int Length => 256;
+ public ref T this[int index] => ref AsSpan()[index];
+
+ [Pure]
+ public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
+ }
+
public struct Array384 : IArray where T : unmanaged
{
T _e0;
diff --git a/src/Ryujinx.Common/ReactiveObject.cs b/src/Ryujinx.Common/ReactiveObject.cs
index 4f27af546..8df1e20fe 100644
--- a/src/Ryujinx.Common/ReactiveObject.cs
+++ b/src/Ryujinx.Common/ReactiveObject.cs
@@ -1,11 +1,13 @@
+using Ryujinx.Common.Logging;
using System;
+using System.Globalization;
using System.Threading;
namespace Ryujinx.Common
{
public class ReactiveObject
{
- private readonly ReaderWriterLockSlim _readerWriterLock = new();
+ private readonly ReaderWriterLockSlim _rwLock = new();
private bool _isInitialized;
private T _value;
@@ -15,15 +17,15 @@ namespace Ryujinx.Common
{
get
{
- _readerWriterLock.EnterReadLock();
+ _rwLock.EnterReadLock();
T value = _value;
- _readerWriterLock.ExitReadLock();
+ _rwLock.ExitReadLock();
return value;
}
set
{
- _readerWriterLock.EnterWriteLock();
+ _rwLock.EnterWriteLock();
T oldValue = _value;
@@ -32,7 +34,7 @@ namespace Ryujinx.Common
_isInitialized = true;
_value = value;
- _readerWriterLock.ExitWriteLock();
+ _rwLock.ExitWriteLock();
if (!oldIsInitialized || oldValue == null || !oldValue.Equals(_value))
{
@@ -40,12 +42,22 @@ namespace Ryujinx.Common
}
}
}
+
+ public void LogChangesToValue(string valueName, LogClass logClass = LogClass.Configuration)
+ => Event += (_, e) => ReactiveObjectHelper.LogValueChange(logClass, e, valueName);
public static implicit operator T(ReactiveObject obj) => obj.Value;
}
public static class ReactiveObjectHelper
{
+ public static void LogValueChange(LogClass logClass, ReactiveEventArgs eventArgs, string valueName)
+ {
+ string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}");
+
+ Logger.Info?.Print(logClass, message);
+ }
+
public static void Toggle(this ReactiveObject rBoolean) => rBoolean.Value = !rBoolean.Value;
}
diff --git a/src/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs
index dfba64191..f4c62155a 100644
--- a/src/Ryujinx.Common/ReleaseInformation.cs
+++ b/src/Ryujinx.Common/ReleaseInformation.cs
@@ -5,7 +5,9 @@ namespace Ryujinx.Common
// DO NOT EDIT, filled by CI
public static class ReleaseInformation
{
- private const string FlatHubChannelOwner = "flathub";
+ private const string FlatHubChannel = "flathub";
+ private const string CanaryChannel = "canary";
+ private const string ReleaseChannel = "release";
private const string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
@@ -13,6 +15,7 @@ namespace Ryujinx.Common
private const string ConfigFileName = "%%RYUJINX_CONFIG_FILE_NAME%%";
public const string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%";
+ public const string ReleaseChannelSourceRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO%%";
public const string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%";
public static string ConfigName => !ConfigFileName.StartsWith("%%") ? ConfigFileName : "Config.json";
@@ -21,10 +24,15 @@ namespace Ryujinx.Common
!BuildGitHash.StartsWith("%%") &&
!ReleaseChannelName.StartsWith("%%") &&
!ReleaseChannelOwner.StartsWith("%%") &&
+ !ReleaseChannelSourceRepo.StartsWith("%%") &&
!ReleaseChannelRepo.StartsWith("%%") &&
!ConfigFileName.StartsWith("%%");
- public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannelOwner);
+ public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannel);
+
+ public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel);
+
+ public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel);
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute()?.InformationalVersion;
}
diff --git a/src/Ryujinx.Common/Ryujinx.Common.csproj b/src/Ryujinx.Common/Ryujinx.Common.csproj
index 722509f33..dee462fdb 100644
--- a/src/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/src/Ryujinx.Common/Ryujinx.Common.csproj
@@ -10,6 +10,7 @@
+
diff --git a/src/Ryujinx.Common/Utilities/JsonHelper.cs b/src/Ryujinx.Common/Utilities/JsonHelper.cs
index 65b8cb109..276dd5f8c 100644
--- a/src/Ryujinx.Common/Utilities/JsonHelper.cs
+++ b/src/Ryujinx.Common/Utilities/JsonHelper.cs
@@ -17,29 +17,19 @@ namespace Ryujinx.Common.Utilities
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
///
- public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true)
- {
- JsonSerializerOptions options = new()
+ public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) =>
+ new()
{
DictionaryKeyPolicy = _snakeCasePolicy,
PropertyNamingPolicy = _snakeCasePolicy,
WriteIndented = indented,
AllowTrailingCommas = true,
- ReadCommentHandling = JsonCommentHandling.Skip,
+ ReadCommentHandling = JsonCommentHandling.Skip
};
- return options;
- }
+ public static string Serialize(T value, JsonTypeInfo typeInfo) => JsonSerializer.Serialize(value, typeInfo);
- public static string Serialize(T value, JsonTypeInfo typeInfo)
- {
- return JsonSerializer.Serialize(value, typeInfo);
- }
-
- public static T Deserialize(string value, JsonTypeInfo typeInfo)
- {
- return JsonSerializer.Deserialize(value, typeInfo);
- }
+ public static T Deserialize(string value, JsonTypeInfo typeInfo) => JsonSerializer.Deserialize(value, typeInfo);
public static void SerializeToFile(string filePath, T value, JsonTypeInfo typeInfo)
{
@@ -53,10 +43,7 @@ namespace Ryujinx.Common.Utilities
return JsonSerializer.Deserialize(file, typeInfo);
}
- public static void SerializeToStream(Stream stream, T value, JsonTypeInfo typeInfo)
- {
- JsonSerializer.Serialize(stream, value, typeInfo);
- }
+ public static void SerializeToStream(Stream stream, T value, JsonTypeInfo typeInfo) => JsonSerializer.Serialize(stream, value, typeInfo);
private class SnakeCaseNamingPolicy : JsonNamingPolicy
{
diff --git a/src/Ryujinx.Common/Utilities/NetworkHelpers.cs b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs
index 71e02184e..53d1e4f33 100644
--- a/src/Ryujinx.Common/Utilities/NetworkHelpers.cs
+++ b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs
@@ -1,6 +1,7 @@
using System.Buffers.Binary;
using System.Net;
using System.Net.NetworkInformation;
+using System.Runtime.InteropServices;
namespace Ryujinx.Common.Utilities
{
@@ -65,6 +66,11 @@ namespace Ryujinx.Common.Utilities
return (targetProperties, targetAddressInfo);
}
+ public static bool SupportsDynamicDns()
+ {
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ }
+
public static uint ConvertIpv4Address(IPAddress ipAddress)
{
return BinaryPrimitives.ReadUInt32BigEndian(ipAddress.GetAddressBytes());
diff --git a/src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs b/src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs
new file mode 100644
index 000000000..050e78d1e
--- /dev/null
+++ b/src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs
@@ -0,0 +1,524 @@
+// Uncomment the line below to ensure XCIFileTrimmer does not modify files
+//#define XCI_TRIMMER_READ_ONLY_MODE
+
+using Gommon;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+
+namespace Ryujinx.Common.Utilities
+{
+ public sealed class XCIFileTrimmer
+ {
+ private const long BytesInAMegabyte = 1024 * 1024;
+ private const int BufferSize = 8 * (int)BytesInAMegabyte;
+
+ private const long CartSizeMBinFormattedGB = 952;
+ private const int CartKeyAreaSize = 0x1000;
+ private const byte PaddingByte = 0xFF;
+ private const int HeaderFilePos = 0x100;
+ private const int CartSizeFilePos = 0x10D;
+ private const int DataSizeFilePos = 0x118;
+ private const string HeaderMagicValue = "HEAD";
+
+ ///
+ /// Cartridge Sizes (ByteIdentifier, SizeInGB)
+ ///
+ private static readonly Dictionary _cartSizesGB = new()
+ {
+ { 0xFA, 1 },
+ { 0xF8, 2 },
+ { 0xF0, 4 },
+ { 0xE0, 8 },
+ { 0xE1, 16 },
+ { 0xE2, 32 }
+ };
+
+ private static long RecordsToByte(long records)
+ {
+ return 512 + (records * 512);
+ }
+
+ public static bool CanTrim(string filename, ILog log = null)
+ {
+ if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var trimmer = new XCIFileTrimmer(filename, log);
+ return trimmer.CanBeTrimmed;
+ }
+
+ return false;
+ }
+
+ public static bool CanUntrim(string filename, ILog log = null)
+ {
+ if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var trimmer = new XCIFileTrimmer(filename, log);
+ return trimmer.CanBeUntrimmed;
+ }
+
+ return false;
+ }
+
+ private ILog _log;
+ private string _filename;
+ private FileStream _fileStream;
+ private BinaryReader _binaryReader;
+ private long _offsetB, _dataSizeB, _cartSizeB, _fileSizeB;
+ private bool _fileOK = true;
+ private bool _freeSpaceChecked = false;
+ private bool _freeSpaceValid = false;
+
+ public enum OperationOutcome
+ {
+ Undetermined,
+ InvalidXCIFile,
+ NoTrimNecessary,
+ NoUntrimPossible,
+ FreeSpaceCheckFailed,
+ FileIOWriteError,
+ ReadOnlyFileCannotFix,
+ FileSizeChanged,
+ Successful,
+ Cancelled
+ }
+
+ public enum LogType
+ {
+ Info,
+ Warn,
+ Error,
+ Progress
+ }
+
+ public interface ILog
+ {
+ public void Write(LogType logType, string text);
+ public void Progress(long current, long total, string text, bool complete);
+ }
+
+ public bool FileOK => _fileOK;
+ public bool Trimmed => _fileOK && FileSizeB < UntrimmedFileSizeB;
+ public bool ContainsKeyArea => _offsetB != 0;
+ public bool CanBeTrimmed => _fileOK && FileSizeB > TrimmedFileSizeB;
+ public bool CanBeUntrimmed => _fileOK && FileSizeB < UntrimmedFileSizeB;
+ public bool FreeSpaceChecked => _fileOK && _freeSpaceChecked;
+ public bool FreeSpaceValid => _fileOK && _freeSpaceValid;
+ public long DataSizeB => _dataSizeB;
+ public long CartSizeB => _cartSizeB;
+ public long FileSizeB => _fileSizeB;
+ public long DiskSpaceSavedB => CartSizeB - FileSizeB;
+ public long DiskSpaceSavingsB => CartSizeB - DataSizeB;
+ public long TrimmedFileSizeB => _offsetB + _dataSizeB;
+ public long UntrimmedFileSizeB => _offsetB + _cartSizeB;
+
+ public ILog Log
+ {
+ get => _log;
+ set => _log = value;
+ }
+
+ public String Filename
+ {
+ get => _filename;
+ set
+ {
+ _filename = value;
+ Reset();
+ }
+ }
+
+ public long Pos
+ {
+ get => _fileStream.Position;
+ set => _fileStream.Position = value;
+ }
+
+ public XCIFileTrimmer(string path, ILog log = null)
+ {
+ Log = log;
+ Filename = path;
+ ReadHeader();
+ }
+
+ public void CheckFreeSpace(CancellationToken? cancelToken = null)
+ {
+ if (FreeSpaceChecked)
+ return;
+
+ try
+ {
+ if (CanBeTrimmed)
+ {
+ _freeSpaceValid = false;
+
+ OpenReaders();
+
+ try
+ {
+ Pos = TrimmedFileSizeB;
+ bool freeSpaceValid = true;
+ long readSizeB = FileSizeB - TrimmedFileSizeB;
+
+ Stopwatch timedSw = Lambda.Timed(() =>
+ {
+ freeSpaceValid = CheckPadding(readSizeB, cancelToken);
+ });
+
+ if (timedSw.Elapsed.TotalSeconds > 0)
+ {
+ Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
+ }
+
+ if (freeSpaceValid)
+ Log?.Write(LogType.Info, "Free space is valid");
+
+ _freeSpaceValid = freeSpaceValid;
+ }
+ finally
+ {
+ CloseReaders();
+ }
+
+ }
+ else
+ {
+ Log?.Write(LogType.Warn, "There is no free space to check.");
+ _freeSpaceValid = false;
+ }
+ }
+ finally
+ {
+ _freeSpaceChecked = true;
+ }
+ }
+
+ private bool CheckPadding(long readSizeB, CancellationToken? cancelToken = null)
+ {
+ long maxReads = readSizeB / XCIFileTrimmer.BufferSize;
+ long read = 0;
+ var buffer = new byte[BufferSize];
+
+ while (true)
+ {
+ if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ int bytes = _fileStream.Read(buffer, 0, XCIFileTrimmer.BufferSize);
+ if (bytes == 0)
+ break;
+
+ Log?.Progress(read, maxReads, "Verifying file can be trimmed", false);
+ if (buffer.Take(bytes).AsParallel().Any(b => b != XCIFileTrimmer.PaddingByte))
+ {
+ Log?.Write(LogType.Warn, "Free space is NOT valid");
+ return false;
+ }
+
+ read++;
+ }
+
+ return true;
+ }
+
+ private void Reset()
+ {
+ _freeSpaceChecked = false;
+ _freeSpaceValid = false;
+ ReadHeader();
+ }
+
+ public OperationOutcome Trim(CancellationToken? cancelToken = null)
+ {
+ if (!FileOK)
+ {
+ return OperationOutcome.InvalidXCIFile;
+ }
+
+ if (!CanBeTrimmed)
+ {
+ return OperationOutcome.NoTrimNecessary;
+ }
+
+ if (!FreeSpaceChecked)
+ {
+ CheckFreeSpace(cancelToken);
+ }
+
+ if (!FreeSpaceValid)
+ {
+ if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
+ {
+ return OperationOutcome.Cancelled;
+ }
+ else
+ {
+ return OperationOutcome.FreeSpaceCheckFailed;
+ }
+ }
+
+ Log?.Write(LogType.Info, "Trimming...");
+
+ try
+ {
+ var info = new FileInfo(Filename);
+ if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
+ {
+ try
+ {
+ Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute");
+ File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly);
+ }
+ catch (Exception e)
+ {
+ Log?.Write(LogType.Error, e.ToString());
+ return OperationOutcome.ReadOnlyFileCannotFix;
+ }
+ }
+
+ if (info.Length != FileSizeB)
+ {
+ Log?.Write(LogType.Error, "File size has changed, cannot safely trim.");
+ return OperationOutcome.FileSizeChanged;
+ }
+
+ var outfileStream = new FileStream(_filename, FileMode.Open, FileAccess.Write, FileShare.Write);
+
+ try
+ {
+
+#if !XCI_TRIMMER_READ_ONLY_MODE
+ outfileStream.SetLength(TrimmedFileSizeB);
+#endif
+ return OperationOutcome.Successful;
+ }
+ finally
+ {
+ outfileStream.Close();
+ Reset();
+ }
+ }
+ catch (Exception e)
+ {
+ Log?.Write(LogType.Error, e.ToString());
+ return OperationOutcome.FileIOWriteError;
+ }
+ }
+
+ public OperationOutcome Untrim(CancellationToken? cancelToken = null)
+ {
+ if (!FileOK)
+ {
+ return OperationOutcome.InvalidXCIFile;
+ }
+
+ if (!CanBeUntrimmed)
+ {
+ return OperationOutcome.NoUntrimPossible;
+ }
+
+ try
+ {
+ Log?.Write(LogType.Info, "Untrimming...");
+
+ var info = new FileInfo(Filename);
+ if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
+ {
+ try
+ {
+ Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute");
+ File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly);
+ }
+ catch (Exception e)
+ {
+ Log?.Write(LogType.Error, e.ToString());
+ return OperationOutcome.ReadOnlyFileCannotFix;
+ }
+ }
+
+ if (info.Length != FileSizeB)
+ {
+ Log?.Write(LogType.Error, "File size has changed, cannot safely untrim.");
+ return OperationOutcome.FileSizeChanged;
+ }
+
+ var outfileStream = new FileStream(_filename, FileMode.Append, FileAccess.Write, FileShare.Write);
+ long bytesToWriteB = UntrimmedFileSizeB - FileSizeB;
+
+ try
+ {
+ Stopwatch timedSw = Lambda.Timed(() =>
+ {
+ WritePadding(outfileStream, bytesToWriteB, cancelToken);
+ });
+
+ if (timedSw.Elapsed.TotalSeconds > 0)
+ {
+ Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
+ }
+
+ if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
+ {
+ return OperationOutcome.Cancelled;
+ }
+ else
+ {
+ return OperationOutcome.Successful;
+ }
+ }
+ finally
+ {
+ outfileStream.Close();
+ Reset();
+ }
+ }
+ catch (Exception e)
+ {
+ Log?.Write(LogType.Error, e.ToString());
+ return OperationOutcome.FileIOWriteError;
+ }
+ }
+
+ private void WritePadding(FileStream outfileStream, long bytesToWriteB, CancellationToken? cancelToken = null)
+ {
+ long bytesLeftToWriteB = bytesToWriteB;
+ long writes = bytesLeftToWriteB / XCIFileTrimmer.BufferSize;
+ int write = 0;
+
+ try
+ {
+ var buffer = new byte[BufferSize];
+ Array.Fill(buffer, XCIFileTrimmer.PaddingByte);
+
+ while (bytesLeftToWriteB > 0)
+ {
+ if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
+ {
+ return;
+ }
+
+ long bytesToWrite = Math.Min(XCIFileTrimmer.BufferSize, bytesLeftToWriteB);
+
+#if !XCI_TRIMMER_READ_ONLY_MODE
+ outfileStream.Write(buffer, 0, (int)bytesToWrite);
+#endif
+
+ bytesLeftToWriteB -= bytesToWrite;
+ Log?.Progress(write, writes, "Writing padding data...", false);
+ write++;
+ }
+ }
+ finally
+ {
+ Log?.Progress(write, writes, "Writing padding data...", true);
+ }
+ }
+
+ private void OpenReaders()
+ {
+ if (_binaryReader == null)
+ {
+ _fileStream = new FileStream(_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
+ _binaryReader = new BinaryReader(_fileStream);
+ }
+ }
+
+ private void CloseReaders()
+ {
+ if (_binaryReader != null && _binaryReader.BaseStream != null)
+ _binaryReader.Close();
+ _binaryReader = null;
+ _fileStream = null;
+ GC.Collect();
+ }
+
+ private void ReadHeader()
+ {
+ try
+ {
+ OpenReaders();
+
+ try
+ {
+ // Attempt without key area
+ bool success = CheckAndReadHeader(false);
+
+ if (!success)
+ {
+ // Attempt with key area
+ success = CheckAndReadHeader(true);
+ }
+
+ _fileOK = success;
+ }
+ finally
+ {
+ CloseReaders();
+ }
+ }
+ catch (Exception ex)
+ {
+ Log?.Write(LogType.Error, ex.Message);
+ _fileOK = false;
+ _dataSizeB = 0;
+ _cartSizeB = 0;
+ _fileSizeB = 0;
+ _offsetB = 0;
+ }
+ }
+
+ private bool CheckAndReadHeader(bool assumeKeyArea)
+ {
+ // Read file size
+ _fileSizeB = _fileStream.Length;
+ if (_fileSizeB < 32 * 1024)
+ {
+ Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the data size is too small");
+ return false;
+ }
+
+ // Setup offset
+ _offsetB = (long)(assumeKeyArea ? XCIFileTrimmer.CartKeyAreaSize : 0);
+
+ // Check header
+ Pos = _offsetB + XCIFileTrimmer.HeaderFilePos;
+ string head = System.Text.Encoding.ASCII.GetString(_binaryReader.ReadBytes(4));
+ if (head != XCIFileTrimmer.HeaderMagicValue)
+ {
+ if (!assumeKeyArea)
+ {
+ Log?.Write(LogType.Warn, $"Incorrect header found, file mat contain a key area...");
+ }
+ else
+ {
+ Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the header is corrupted");
+ }
+
+ return false;
+ }
+
+ // Read Cart Size
+ Pos = _offsetB + XCIFileTrimmer.CartSizeFilePos;
+ byte cartSizeId = _binaryReader.ReadByte();
+ if (!_cartSizesGB.TryGetValue(cartSizeId, out long cartSizeNGB))
+ {
+ Log?.Write(LogType.Error, $"The source file doesn't look like an XCI file as the Cartridge Size is incorrect (0x{cartSizeId:X2})");
+ return false;
+ }
+ _cartSizeB = cartSizeNGB * XCIFileTrimmer.CartSizeMBinFormattedGB * XCIFileTrimmer.BytesInAMegabyte;
+
+ // Read data size
+ Pos = _offsetB + XCIFileTrimmer.DataSizeFilePos;
+ long records = (long)BitConverter.ToUInt32(_binaryReader.ReadBytes(4), 0);
+ _dataSizeB = RecordsToByte(records);
+
+ return true;
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs
index 9b5e2cc42..c2fdcbe4b 100644
--- a/src/Ryujinx.Graphics.GAL/IRenderer.cs
+++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.GAL
IPipeline Pipeline { get; }
IWindow Window { get; }
-
+
uint ProgramCount { get; }
void BackgroundContextAction(Action action, bool alwaysBackground = false);
diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs
index 048d32fb7..fb529e914 100644
--- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs
+++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs
@@ -152,16 +152,17 @@ namespace Ryujinx.Graphics.Gpu
/// Creates a new GPU memory manager.
///
/// ID of the process that owns the memory manager
+ /// The amount of physical CPU Memory Avaiable on the device.
/// The memory manager
/// Thrown when is invalid
- public MemoryManager CreateMemoryManager(ulong pid)
+ public MemoryManager CreateMemoryManager(ulong pid, ulong cpuMemorySize)
{
if (!PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
{
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
}
- return new MemoryManager(physicalMemory);
+ return new MemoryManager(physicalMemory, cpuMemorySize);
}
///
diff --git a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
index ad6c1fecb..74967b190 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Logging;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -47,11 +48,17 @@ namespace Ryujinx.Graphics.Gpu.Image
{
private const int MinCountForDeletion = 32;
private const int MaxCapacity = 2048;
+ private const ulong GiB = 1024 * 1024 * 1024;
+ private ulong MaxTextureSizeCapacity = 4UL * GiB;
private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024;
- private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024;
- private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024;
+ private const ulong DefaultTextureSizeCapacity = 1 * GiB;
+ private const ulong TextureSizeCapacity6GiB = 4 * GiB;
+ private const ulong TextureSizeCapacity8GiB = 6 * GiB;
+ private const ulong TextureSizeCapacity12GiB = 12 * GiB;
+
+
private const float MemoryScaleFactor = 0.50f;
- private ulong _maxCacheMemoryUsage = 0;
+ private ulong _maxCacheMemoryUsage = DefaultTextureSizeCapacity;
private readonly LinkedList _textures;
private ulong _totalSize;
@@ -66,18 +73,38 @@ namespace Ryujinx.Graphics.Gpu.Image
///
///
/// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`.
+ ///
+ /// Reads the current Device total CPU Memory, to determine the maximum amount of Vram available. Capped to 50% of Current GPU Memory.
///
/// The GPU context that the cache belongs to
- public void Initialize(GpuContext context)
+ /// The amount of physical CPU Memory Avaiable on the device.
+ public void Initialize(GpuContext context, ulong cpuMemorySize)
{
+ var cpuMemorySizeGiB = cpuMemorySize / GiB;
+
+ if (cpuMemorySizeGiB < 6 || context.Capabilities.MaximumGpuMemory == 0)
+ {
+ _maxCacheMemoryUsage = DefaultTextureSizeCapacity;
+ return;
+ }
+ else if (cpuMemorySizeGiB == 6)
+ {
+ MaxTextureSizeCapacity = TextureSizeCapacity6GiB;
+ }
+ else if (cpuMemorySizeGiB == 8)
+ {
+ MaxTextureSizeCapacity = TextureSizeCapacity8GiB;
+ }
+ else
+ {
+ MaxTextureSizeCapacity = TextureSizeCapacity12GiB;
+ }
+
var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor);
_maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity);
- if (context.Capabilities.MaximumGpuMemory == 0)
- {
- _maxCacheMemoryUsage = DefaultTextureSizeCapacity;
- }
+ Logger.Info?.Print(LogClass.Gpu, $"AutoDelete Cache Allocated VRAM : {_maxCacheMemoryUsage / GiB} GiB");
}
///
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 1587e2018..2cfd9af5b 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -71,9 +71,10 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
///
- public void Initialize()
+ /// The amount of physical CPU Memory Avaiable on the device.
+ public void Initialize(ulong cpuMemorySize)
{
- _cache.Initialize(_context);
+ _cache.Initialize(_context, cpuMemorySize);
}
///
diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
index d1065431d..59e618c02 100644
--- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
+++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
@@ -55,7 +55,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a new instance of the GPU memory manager.
///
/// Physical memory that this memory manager will map into
- internal MemoryManager(PhysicalMemory physicalMemory)
+ /// The amount of physical CPU Memory Avaiable on the device.
+ internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize)
{
Physical = physicalMemory;
VirtualRangeCache = new VirtualRangeCache(this);
@@ -65,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
- Physical.TextureCache.Initialize();
+ Physical.TextureCache.Initialize(cpuMemorySize);
}
///
diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
index 2deee045c..6ead314fd 100644
--- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
+++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
@@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.OpenGL
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
{
ProgramCount++;
-
+
return new Program(shaders, info.FragmentOutputMap);
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index 4308b08f8..56507a2a4 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -432,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
bool colorIsVector = isGather || !isShadow;
- texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : "");
+ texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : string.Empty);
return texCall;
}
diff --git a/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs b/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
index 714a9d68c..b792776df 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
@@ -830,12 +830,12 @@ namespace Ryujinx.Graphics.Shader.Translation
if (use.Node != null)
{
- Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})");
+ Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index})");
PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
}
else
{
- Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL");
+ Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index}) NULL");
}
}
}
@@ -852,12 +852,12 @@ namespace Ryujinx.Graphics.Shader.Translation
if (use.Node != null)
{
- Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})");
+ Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index})");
PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
}
else
{
- Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL");
+ Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index}) NULL");
}
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
index 6f27bb68b..ce1293589 100644
--- a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
@@ -55,8 +55,10 @@ namespace Ryujinx.Graphics.Vulkan
if (_handle != BufferHandle.Null)
{
// May need to restride the vertex buffer.
-
- if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
+ //
+ // Fix divide by zero when recovering from missed draw (Oct. 16 2024)
+ // (fixes crash in 'Baldo: The Guardian Owls' opening cutscene)
+ if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && alignment != 0 && (_stride % alignment) != 0)
{
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index af2cfa17f..cc2bc36c2 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -104,25 +104,27 @@ namespace Ryujinx.Graphics.Vulkan
public event EventHandler ScreenCaptured;
- public VulkanRenderer(Vk api, Func surfaceFunc, Func requiredExtensionsFunc, string preferredGpuId)
+ public VulkanRenderer(Vk api, Func getSurface, Func requiredExtensionsFunc, string preferredGpuId)
{
- _getSurface = surfaceFunc;
+ _getSurface = getSurface;
_getRequiredExtensions = requiredExtensionsFunc;
_preferredGpuId = preferredGpuId;
Api = api;
- Shaders = new HashSet();
- Textures = new HashSet();
- Samplers = new HashSet();
+ Shaders = [];
+ Textures = [];
+ Samplers = [];
- if (OperatingSystem.IsMacOS())
- {
+ // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
+ if (IsMoltenVk = OperatingSystem.IsMacOS())
MVKInitialization.Initialize();
-
- // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
- IsMoltenVk = true;
- }
}
+ public static VulkanRenderer Create(
+ string preferredGpuId,
+ Func getSurface,
+ Func getRequiredExtensions
+ ) => new(Vk.GetApi(), getSurface, getRequiredExtensions, preferredGpuId);
+
private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
{
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
@@ -547,7 +549,7 @@ namespace Ryujinx.Graphics.Vulkan
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
{
ProgramCount++;
-
+
bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute;
if (info.State.HasValue || isCompute)
diff --git a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
index d6b6f23ef..5cac4d13a 100644
--- a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
+++ b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
@@ -13,6 +13,7 @@ namespace Ryujinx.HLE.Generators
var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
CodeGenerator generator = new CodeGenerator();
+ generator.AppendLine("#nullable enable");
generator.AppendLine("using System;");
generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
generator.EnterScope($"partial class IUserInterface");
@@ -22,7 +23,7 @@ namespace Ryujinx.HLE.Generators
{
if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service"))))
continue;
- var name = GetFullName(className, context).Replace("global::", "");
+ var name = GetFullName(className, context).Replace("global::", string.Empty);
if (!name.StartsWith("Ryujinx.HLE.HOS.Services"))
continue;
var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax).ToArray();
@@ -58,6 +59,7 @@ namespace Ryujinx.HLE.Generators
generator.LeaveScope();
generator.LeaveScope();
+ generator.AppendLine("#nullable disable");
context.AddSource($"IUserInterface.g.cs", generator.ToString());
}
diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs
index 952271fa1..fc8def9d2 100644
--- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs
+++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs
@@ -523,7 +523,7 @@ namespace Ryujinx.HLE.FileSystem
{
// Clean up the name and get the NcaId
- string[] pathComponents = entry.FullName.Replace(".cnmt", "").Split('/');
+ string[] pathComponents = entry.FullName.Replace(".cnmt", string.Empty).Split('/');
string ncaId = pathComponents[^1];
diff --git a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
index 0e1265fb6..39c544eac 100644
--- a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
+++ b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
@@ -132,7 +132,7 @@ namespace Ryujinx.HLE.FileSystem
if (systemPath.StartsWith(baseSystemPath))
{
- string rawPath = systemPath.Replace(baseSystemPath, "");
+ string rawPath = systemPath.Replace(baseSystemPath, string.Empty);
int firstSeparatorOffset = rawPath.IndexOf(Path.DirectorySeparatorChar);
if (firstSeparatorOffset == -1)
diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs
index 955fee4b5..70fcf278d 100644
--- a/src/Ryujinx.HLE/HLEConfiguration.cs
+++ b/src/Ryujinx.HLE/HLEConfiguration.cs
@@ -164,6 +164,21 @@ namespace Ryujinx.HLE
///
public MultiplayerMode MultiplayerMode { internal get; set; }
+ ///
+ /// Disable P2P mode
+ ///
+ public bool MultiplayerDisableP2p { internal get; set; }
+
+ ///
+ /// Multiplayer Passphrase
+ ///
+ public string MultiplayerLdnPassphrase { internal get; set; }
+
+ ///
+ /// LDN Server
+ ///
+ public string MultiplayerLdnServer { internal get; set; }
+
///
/// An action called when HLE force a refresh of output after docked mode changed.
///
@@ -194,7 +209,10 @@ namespace Ryujinx.HLE
float audioVolume,
bool useHypervisor,
string multiplayerLanInterfaceId,
- MultiplayerMode multiplayerMode)
+ MultiplayerMode multiplayerMode,
+ bool multiplayerDisableP2p,
+ string multiplayerLdnPassphrase,
+ string multiplayerLdnServer)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
@@ -222,6 +240,9 @@ namespace Ryujinx.HLE
UseHypervisor = useHypervisor;
MultiplayerLanInterfaceId = multiplayerLanInterfaceId;
MultiplayerMode = multiplayerMode;
+ MultiplayerDisableP2p = multiplayerDisableP2p;
+ MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
+ MultiplayerLdnServer = multiplayerLdnServer;
}
}
}
diff --git a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
index 3c34d5c78..da4d2e51b 100644
--- a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
@@ -1,4 +1,6 @@
+using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Applets.Browser;
+using Ryujinx.HLE.HOS.Applets.Dummy;
using Ryujinx.HLE.HOS.Applets.Error;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using System;
@@ -26,9 +28,13 @@ namespace Ryujinx.HLE.HOS.Applets
return new BrowserApplet(system);
case AppletId.LibAppletOff:
return new BrowserApplet(system);
+ case AppletId.MiiEdit:
+ Logger.Warning?.Print(LogClass.Application, $"Please use the MiiEdit inside File/Open Applet");
+ return new DummyApplet(system);
}
- throw new NotImplementedException($"{applet} applet is not implemented.");
+ Logger.Warning?.Print(LogClass.Application, $"Applet {applet} not implemented!");
+ return new DummyApplet(system);
}
}
}
diff --git a/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs
new file mode 100644
index 000000000..75df7a373
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs
@@ -0,0 +1,43 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Applets;
+using Ryujinx.HLE.HOS.Services.Am.AppletAE;
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+namespace Ryujinx.HLE.HOS.Applets.Dummy
+{
+ internal class DummyApplet : IApplet
+ {
+ private readonly Horizon _system;
+ private AppletSession _normalSession;
+ public event EventHandler AppletStateChanged;
+ public DummyApplet(Horizon system)
+ {
+ _system = system;
+ }
+ public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
+ {
+ _normalSession = normalSession;
+ _normalSession.Push(BuildResponse());
+ AppletStateChanged?.Invoke(this, null);
+ _system.ReturnFocus();
+ return ResultCode.Success;
+ }
+ private static T ReadStruct(byte[] data) where T : struct
+ {
+ return MemoryMarshal.Read(data.AsSpan());
+ }
+ private static byte[] BuildResponse()
+ {
+ using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
+ using BinaryWriter writer = new(stream);
+ writer.Write((ulong)ResultCode.Success);
+ return stream.ToArray();
+ }
+ public ResultCode GetResult()
+ {
+ return ResultCode.Success;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs
index 5a40433cb..87d88fc65 100644
--- a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs
@@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
private static string CleanText(string value)
{
- return CleanTextRegex().Replace(value, "").Replace("\0", "");
+ return CleanTextRegex().Replace(value, string.Empty).Replace("\0", string.Empty);
}
private string GetMessageText(uint module, uint description, string key)
@@ -129,17 +129,15 @@ namespace Ryujinx.HLE.HOS.Applets.Error
return CleanText(reader.ReadToEnd());
}
- else
- {
- return "";
- }
+
+ return string.Empty;
}
private string[] GetButtonsText(uint module, uint description, string key)
{
string buttonsText = GetMessageText(module, description, key);
- return (buttonsText == "") ? null : buttonsText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
+ return (buttonsText == string.Empty) ? null : buttonsText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None);
}
private void ParseErrorCommonArg()
@@ -156,7 +154,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
string message = GetMessageText(module, description, "DlgMsg");
- if (message == "")
+ if (message == string.Empty)
{
message = "An error has occured.\n\nPlease try again later.";
}
@@ -190,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
// TODO: Handle the LanguageCode to return the translated "OK" and "Details".
- if (detailsText.Trim() != "")
+ if (detailsText.Trim() != string.Empty)
{
buttons.Add("Details");
}
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
index 0462a5b00..e04fc64fe 100644
--- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
@@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] _transferMemory;
- private string _textValue = "";
+ private string _textValue = string.Empty;
private int _cursorBegin = 0;
private Encoding _encoding = Encoding.Unicode;
private KeyboardResult _lastResult = KeyboardResult.NotSet;
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
index cc62eca1d..67b5f2b1f 100644
--- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
@@ -305,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
SKRect bounds = SKRect.Empty;
- if (text == "")
+ if (text == string.Empty)
{
paint.MeasureText(" ", ref bounds);
}
@@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
SKRect bounds = SKRect.Empty;
- if (text == "")
+ if (text == string.Empty)
{
paint.MeasureText(" ", ref bounds);
}
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs
index 6199ff666..da5220364 100644
--- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
///
internal class SoftwareKeyboardUIState
{
- public string InputText = "";
+ public string InputText = string.Empty;
public int CursorBegin = 0;
public int CursorEnd = 0;
public bool AcceptPressed = false;
diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs
index 171a083f3..2e7b8ee76 100644
--- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs
@@ -2463,7 +2463,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
return ParseIntegerLiteral("unsigned short");
case 'i':
_position++;
- return ParseIntegerLiteral("");
+ return ParseIntegerLiteral(string.Empty);
case 'j':
_position++;
return ParseIntegerLiteral("u");
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
index 7578f1d2f..ff3de4a17 100644
--- a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
+++ b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
@@ -238,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
}
else
{
- info.SubName = "";
+ info.SubName = string.Empty;
}
info.ImageName = GetGuessedNsoNameFromIndex(imageIndex);
diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs
index ee179c929..7cbe1afca 100644
--- a/src/Ryujinx.HLE/HOS/ModLoader.cs
+++ b/src/Ryujinx.HLE/HOS/ModLoader.cs
@@ -116,18 +116,13 @@ namespace Ryujinx.HLE.HOS
private readonly Dictionary _appMods; // key is ApplicationId
private PatchCache _patches;
- private static readonly EnumerationOptions _dirEnumOptions;
-
- static ModLoader()
+ private static readonly EnumerationOptions _dirEnumOptions = new()
{
- _dirEnumOptions = new EnumerationOptions
- {
- MatchCasing = MatchCasing.CaseInsensitive,
- MatchType = MatchType.Simple,
- RecurseSubdirectories = false,
- ReturnSpecialDirectories = false,
- };
- }
+ MatchCasing = MatchCasing.CaseInsensitive,
+ MatchType = MatchType.Simple,
+ RecurseSubdirectories = false,
+ ReturnSpecialDirectories = false,
+ };
public ModLoader()
{
@@ -169,7 +164,7 @@ namespace Ryujinx.HLE.HOS
foreach (var modDir in dir.EnumerateDirectories())
{
types.Clear();
- Mod mod = new("", null, true);
+ Mod mod = new(string.Empty, null, true);
if (StrEquals(RomfsDir, modDir.Name))
{
diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
index c724660ea..d2da9e248 100644
--- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
@@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
if (userId.IsNull)
{
- userId = new UserId(Guid.NewGuid().ToString().Replace("-", ""));
+ userId = new UserId(Guid.NewGuid().ToString().Replace("-", string.Empty));
}
UserProfile profile = new(userId, name, image);
diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs
index 0a032562a..b8741b22b 100644
--- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs
@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService;
+using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
{
@@ -25,5 +26,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
return ResultCode.Success;
}
+
+ [CommandCmif(350)]
+ // OpenSystemApplicationProxy(u64, pid, handle) -> object
+ public ResultCode OpenSystemApplicationProxy(ServiceCtx context)
+ {
+ MakeObject(context, new IApplicationProxy(context.Request.HandleDesc.PId));
+
+ return ResultCode.Success;
+ }
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
index dbcbe1870..66b5a5cba 100644
--- a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
@@ -5,6 +5,7 @@ using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
@@ -28,6 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public DebugPadDevice DebugPad;
public TouchDevice Touchscreen;
public MouseDevice Mouse;
+ public DebugMouseDevice DebugMouse;
public KeyboardDevice Keyboard;
public NpadDevices Npads;
@@ -44,6 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
CheckTypeSizeOrThrow>(0x2c8);
CheckTypeSizeOrThrow>(0x2C38);
CheckTypeSizeOrThrow>(0x350);
+ CheckTypeSizeOrThrow>(0x350);
CheckTypeSizeOrThrow>(0x3D8);
CheckTypeSizeOrThrow>(0x32000);
CheckTypeSizeOrThrow(Horizon.HidSize);
@@ -64,6 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
DebugPad = new DebugPadDevice(_device, true);
Touchscreen = new TouchDevice(_device, true);
Mouse = new MouseDevice(_device, false);
+ DebugMouse = new DebugMouseDevice(_device, false);
Keyboard = new KeyboardDevice(_device, false);
Npads = new NpadDevices(_device, true);
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugMouseDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugMouseDevice.cs
new file mode 100644
index 000000000..cb917444b
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugMouseDevice.cs
@@ -0,0 +1,29 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
+
+namespace Ryujinx.HLE.HOS.Services.Hid
+{
+ public class DebugMouseDevice : BaseDevice
+ {
+ public DebugMouseDevice(Switch device, bool active) : base(device, active) { }
+
+ public void Update()
+ {
+ ref RingLifo lifo = ref _device.Hid.SharedMemory.DebugMouse;
+
+ ref DebugMouseState previousEntry = ref lifo.GetCurrentEntryRef();
+
+ DebugMouseState newState = new()
+ {
+ SamplingNumber = previousEntry.SamplingNumber + 1,
+ };
+
+ if (Active)
+ {
+ // TODO: This is a debug device only present in dev environment, do we want to support it?
+ }
+
+ lifo.Write(ref newState);
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
index e3f505f37..556e35ea6 100644
--- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
@@ -130,6 +130,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return ResultCode.Success;
}
+
+ [CommandCmif(26)]
+ // ActivateDebugMouse(nn::applet::AppletResourceUserId)
+ public ResultCode ActivateDebugMouse(ServiceCtx context)
+ {
+ long appletResourceUserId = context.RequestData.ReadInt64();
+
+ context.Device.Hid.DebugMouse.Active = true;
+
+ // Initialize entries to avoid issues with some games.
+
+ for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
+ {
+ context.Device.Hid.DebugMouse.Update();
+ }
+
+ Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
+
+ return ResultCode.Success;
+ }
[CommandCmif(31)]
// ActivateKeyboard(nn::applet::AppletResourceUserId)
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseAttribute.cs
new file mode 100644
index 000000000..0b55277d4
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse
+{
+ [Flags]
+ enum DebugMouseAttribute : uint
+ {
+ None = 0,
+ Transferable = 1 << 0,
+ IsConnected = 1 << 1,
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseButton.cs
new file mode 100644
index 000000000..c07fa84af
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseButton.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse
+{
+ [Flags]
+ enum DebugMouseButton : uint
+ {
+ None = 0,
+ Left = 1 << 0,
+ Right = 1 << 1,
+ Middle = 1 << 2,
+ Forward = 1 << 3,
+ Back = 1 << 4,
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseState.cs
new file mode 100644
index 000000000..e2860c9f5
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseState.cs
@@ -0,0 +1,19 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ struct DebugMouseState : ISampledDataStruct
+ {
+ public ulong SamplingNumber;
+ public int X;
+ public int Y;
+ public int DeltaX;
+ public int DeltaY;
+ public int WheelDeltaX;
+ public int WheelDeltaY;
+ public DebugMouseButton Buttons;
+ public DebugMouseAttribute Attributes;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
index d6283eb57..59d8f4489 100644
--- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs
@@ -1,5 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
@@ -44,6 +45,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
///
[FieldOffset(0x9A00)]
public Array10 Npads;
+
+ ///
+ /// Debug mouse.
+ ///
+ [FieldOffset(0x3DC00)]
+ public RingLifo DebugMouse;
public static SharedMemory Create()
{
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs
index 4da5fe42b..c6d6ac944 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.Types
{
- [StructLayout(LayoutKind.Sequential, Size = 0x20)]
+ [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 8)]
struct NetworkConfig
{
public IntentId IntentId;
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs
index 449c923cc..f3ab1edd5 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.Types
{
- [StructLayout(LayoutKind.Sequential, Size = 0x60)]
+ [StructLayout(LayoutKind.Sequential, Size = 0x60, Pack = 8)]
struct ScanFilter
{
public NetworkId NetworkId;
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs
index 5939a1394..f3968aab4 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.Types
{
- [StructLayout(LayoutKind.Sequential, Size = 0x44)]
+ [StructLayout(LayoutKind.Sequential, Size = 0x44, Pack = 2)]
struct SecurityConfig
{
public SecurityMode SecurityMode;
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs
index dbcaa9eeb..e564a2ec9 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.Types
{
- [StructLayout(LayoutKind.Sequential, Size = 0x20)]
+ [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 1)]
struct SecurityParameter
{
public Array16 Data;
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs
index 3820f936e..7246f6f80 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.Types
{
- [StructLayout(LayoutKind.Sequential, Size = 0x30)]
+ [StructLayout(LayoutKind.Sequential, Size = 0x30, Pack = 1)]
struct UserConfig
{
public Array33 UserName;
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs
index 78ebcac82..bd00a3139 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs
@@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public Array8 LatestUpdates = new();
public bool Connected { get; private set; }
+ public ProxyConfig Config => _parent.NetworkClient.Config;
+
public AccessPoint(IUserLocalCommunicationService parent)
{
_parent = parent;
@@ -24,9 +26,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public void Dispose()
{
- _parent.NetworkClient.DisconnectNetwork();
+ if (_parent?.NetworkClient != null)
+ {
+ _parent.NetworkClient.DisconnectNetwork();
- _parent.NetworkClient.NetworkChange -= NetworkChanged;
+ _parent.NetworkClient.NetworkChange -= NetworkChanged;
+ }
}
private void NetworkChanged(object sender, NetworkChangeEventArgs e)
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs
index 7ad6de51d..028ab6cfc 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs
@@ -6,6 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
interface INetworkClient : IDisposable
{
+ ProxyConfig Config { get; }
bool NeedsRealId { get; }
event EventHandler NetworkChange;
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs
index 1d4b5485e..9f65aed4b 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs
@@ -9,6 +9,8 @@ using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Ldn.Types;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
using Ryujinx.Horizon.Common;
using Ryujinx.Memory;
using System;
@@ -21,6 +23,9 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
class IUserLocalCommunicationService : IpcService, IDisposable
{
+ public static string DefaultLanPlayHost = "ryuldn.vudjun.com";
+ public static short LanPlayPort = 30456;
+
public INetworkClient NetworkClient { get; private set; }
private const int NifmRequestID = 90;
@@ -175,19 +180,37 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_state == NetworkState.AccessPointCreated || _state == NetworkState.StationConnected)
{
- (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(context.Device.Configuration.MultiplayerLanInterfaceId);
-
- if (unicastAddress == null)
+ ProxyConfig config = _state switch
{
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultIPAddress));
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultSubnetMask));
+ NetworkState.AccessPointCreated => _accessPoint.Config,
+ NetworkState.StationConnected => _station.Config,
+
+ _ => default
+ };
+
+ if (config.ProxyIp == 0)
+ {
+ (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(context.Device.Configuration.MultiplayerLanInterfaceId);
+
+ if (unicastAddress == null)
+ {
+ context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultIPAddress));
+ context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultSubnetMask));
+ }
+ else
+ {
+ Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
+
+ context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
+ context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
+ }
}
else
{
- Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
+ Logger.Info?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
+ context.ResponseData.Write(config.ProxyIp);
+ context.ResponseData.Write(config.ProxySubnetMask);
}
}
else
@@ -1066,6 +1089,27 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
switch (mode)
{
+ case MultiplayerMode.LdnRyu:
+ try
+ {
+ string ldnServer = context.Device.Configuration.MultiplayerLdnServer;
+ if (string.IsNullOrEmpty(ldnServer))
+ {
+ ldnServer = DefaultLanPlayHost;
+ }
+ if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress))
+ {
+ ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0];
+ }
+ NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu server. Defaulting to stubbed wireless.");
+ Logger.Error?.Print(LogClass.ServiceLdn, ex.Message);
+ NetworkClient = new LdnDisabledClient();
+ }
+ break;
case MultiplayerMode.LdnMitm:
NetworkClient = new LdnMitmClient(context.Device.Configuration);
break;
@@ -1103,7 +1147,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
_accessPoint?.Dispose();
_accessPoint = null;
- NetworkClient?.Dispose();
+ NetworkClient?.DisconnectAndStop();
NetworkClient = null;
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs
index e3385a1ed..2e8bb8d83 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ldn.Types;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
using System;
@@ -6,12 +7,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
class LdnDisabledClient : INetworkClient
{
+ public ProxyConfig Config { get; }
public bool NeedsRealId => true;
public event EventHandler NetworkChange;
public NetworkError Connect(ConnectRequest request)
{
+ Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to connect to a network, but Multiplayer is disabled!");
NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false));
return NetworkError.None;
@@ -19,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public NetworkError ConnectPrivate(ConnectPrivateRequest request)
{
+ Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to connect to a network, but Multiplayer is disabled!");
NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false));
return NetworkError.None;
@@ -26,6 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public bool CreateNetwork(CreateAccessPointRequest request, byte[] advertiseData)
{
+ Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to create a network, but Multiplayer is disabled!");
NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false));
return true;
@@ -33,6 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public bool CreateNetworkPrivate(CreateAccessPointPrivateRequest request, byte[] advertiseData)
{
+ Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to create a network, but Multiplayer is disabled!");
NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false));
return true;
@@ -49,6 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public NetworkInfo[] Scan(ushort channel, ScanFilter scanFilter)
{
+ Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to scan for networks, but Multiplayer is disabled!");
return Array.Empty();
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs
index 4b01bfe31..b60b70d80 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs
@@ -1,3 +1,4 @@
+using Gommon;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
@@ -143,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
if (decompressedLdnData.Length != header.DecompressLength)
{
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error: length does not match. ({decompressedLdnData.Length} != {header.DecompressLength})");
- Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error data: '{string.Join("", decompressedLdnData.Select(x => (int)x).ToArray())}'");
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error data: '{decompressedLdnData.Select(x => (int)x).JoinToString(string.Empty)}'");
return;
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs
index 273acdd5e..40697d122 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs
@@ -12,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
///
internal class LdnMitmClient : INetworkClient
{
+ public ProxyConfig Config { get; }
public bool NeedsRealId => false;
public event EventHandler NetworkChange;
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/IProxyClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/IProxyClient.cs
new file mode 100644
index 000000000..a7c435506
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/IProxyClient.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
+{
+ interface IProxyClient
+ {
+ bool SendAsync(byte[] buffer);
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs
new file mode 100644
index 000000000..4c7814b8e
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs
@@ -0,0 +1,645 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Common.Utilities;
+using Ryujinx.HLE.HOS.Services.Ldn.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
+using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy;
+using Ryujinx.HLE.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using TcpClient = NetCoreServer.TcpClient;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
+{
+ class LdnMasterProxyClient : TcpClient, INetworkClient, IProxyClient
+ {
+ public bool NeedsRealId => true;
+
+ private static InitializeMessage InitializeMemory = new InitializeMessage();
+
+ private const int InactiveTimeout = 6000;
+ private const int FailureTimeout = 4000;
+ private const int ScanTimeout = 1000;
+
+ private bool _useP2pProxy;
+ private NetworkError _lastError;
+
+ private readonly ManualResetEvent _connected = new ManualResetEvent(false);
+ private readonly ManualResetEvent _error = new ManualResetEvent(false);
+ private readonly ManualResetEvent _scan = new ManualResetEvent(false);
+ private readonly ManualResetEvent _reject = new ManualResetEvent(false);
+ private readonly AutoResetEvent _apConnected = new AutoResetEvent(false);
+
+ private readonly RyuLdnProtocol _protocol;
+ private readonly NetworkTimeout _timeout;
+
+ private readonly List _availableGames = new List();
+ private DisconnectReason _disconnectReason;
+
+ private P2pProxyServer _hostedProxy;
+ private P2pProxyClient _connectedProxy;
+
+ private bool _networkConnected;
+
+ private string _passphrase;
+ private byte[] _gameVersion = new byte[0x10];
+
+ private readonly HLEConfiguration _config;
+
+ public event EventHandler NetworkChange;
+
+ public ProxyConfig Config { get; private set; }
+
+ public LdnMasterProxyClient(string address, int port, HLEConfiguration config) : base(address, port)
+ {
+ if (ProxyHelpers.SupportsNoDelay())
+ {
+ OptionNoDelay = true;
+ }
+
+ _protocol = new RyuLdnProtocol();
+ _timeout = new NetworkTimeout(InactiveTimeout, TimeoutConnection);
+
+ _protocol.Initialize += HandleInitialize;
+ _protocol.Connected += HandleConnected;
+ _protocol.Reject += HandleReject;
+ _protocol.RejectReply += HandleRejectReply;
+ _protocol.SyncNetwork += HandleSyncNetwork;
+ _protocol.ProxyConfig += HandleProxyConfig;
+ _protocol.Disconnected += HandleDisconnected;
+
+ _protocol.ScanReply += HandleScanReply;
+ _protocol.ScanReplyEnd += HandleScanReplyEnd;
+ _protocol.ExternalProxy += HandleExternalProxy;
+
+ _protocol.Ping += HandlePing;
+ _protocol.NetworkError += HandleNetworkError;
+
+ _config = config;
+ _useP2pProxy = !config.MultiplayerDisableP2p;
+ }
+
+ private void TimeoutConnection()
+ {
+ _connected.Reset();
+
+ DisconnectAsync();
+
+ while (IsConnected)
+ {
+ Thread.Yield();
+ }
+ }
+
+ private bool EnsureConnected()
+ {
+ if (IsConnected)
+ {
+ return true;
+ }
+
+ _error.Reset();
+
+ ConnectAsync();
+
+ int index = WaitHandle.WaitAny(new WaitHandle[] { _connected, _error }, FailureTimeout);
+
+ if (IsConnected)
+ {
+ SendAsync(_protocol.Encode(PacketId.Initialize, InitializeMemory));
+ }
+
+ return index == 0 && IsConnected;
+ }
+
+ private void UpdatePassphraseIfNeeded()
+ {
+ string passphrase = _config.MultiplayerLdnPassphrase ?? "";
+ if (passphrase != _passphrase)
+ {
+ _passphrase = passphrase;
+
+ SendAsync(_protocol.Encode(PacketId.Passphrase, StringUtils.GetFixedLengthBytes(passphrase, 0x80, Encoding.UTF8)));
+ }
+ }
+
+ protected override void OnConnected()
+ {
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}");
+
+ UpdatePassphraseIfNeeded();
+
+ _connected.Set();
+ }
+
+ protected override void OnDisconnected()
+ {
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}");
+
+ _passphrase = null;
+
+ _connected.Reset();
+
+ if (_networkConnected)
+ {
+ DisconnectInternal();
+ }
+ }
+
+ public void DisconnectAndStop()
+ {
+ _timeout.Dispose();
+
+ DisconnectAsync();
+
+ while (IsConnected)
+ {
+ Thread.Yield();
+ }
+
+ Dispose();
+ }
+
+ protected override void OnReceived(byte[] buffer, long offset, long size)
+ {
+ _protocol.Read(buffer, (int)offset, (int)size);
+ }
+
+ protected override void OnError(SocketError error)
+ {
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}");
+
+ _error.Set();
+ }
+
+
+
+ private void HandleInitialize(LdnHeader header, InitializeMessage initialize)
+ {
+ InitializeMemory = initialize;
+ }
+
+ private void HandleExternalProxy(LdnHeader header, ExternalProxyConfig config)
+ {
+ int length = config.AddressFamily switch
+ {
+ AddressFamily.InterNetwork => 4,
+ AddressFamily.InterNetworkV6 => 16,
+ _ => 0
+ };
+
+ if (length == 0)
+ {
+ return; // Invalid external proxy.
+ }
+
+ IPAddress address = new(config.ProxyIp.AsSpan()[..length].ToArray());
+ P2pProxyClient proxy = new(address.ToString(), config.ProxyPort);
+
+ _connectedProxy = proxy;
+
+ bool success = proxy.PerformAuth(config);
+
+ if (!success)
+ {
+ DisconnectInternal();
+ }
+ }
+
+ private void HandlePing(LdnHeader header, PingMessage ping)
+ {
+ if (ping.Requester == 0) // Server requested.
+ {
+ // Send the ping message back.
+
+ SendAsync(_protocol.Encode(PacketId.Ping, ping));
+ }
+ }
+
+ private void HandleNetworkError(LdnHeader header, NetworkErrorMessage error)
+ {
+ if (error.Error == NetworkError.PortUnreachable)
+ {
+ _useP2pProxy = false;
+ }
+ else
+ {
+ _lastError = error.Error;
+ }
+ }
+
+ private NetworkError ConsumeNetworkError()
+ {
+ NetworkError result = _lastError;
+
+ _lastError = NetworkError.None;
+
+ return result;
+ }
+
+ private void HandleSyncNetwork(LdnHeader header, NetworkInfo info)
+ {
+ NetworkChange?.Invoke(this, new NetworkChangeEventArgs(info, true));
+ }
+
+ private void HandleConnected(LdnHeader header, NetworkInfo info)
+ {
+ _networkConnected = true;
+ _disconnectReason = DisconnectReason.None;
+
+ _apConnected.Set();
+
+ NetworkChange?.Invoke(this, new NetworkChangeEventArgs(info, true));
+ }
+
+ private void HandleDisconnected(LdnHeader header, DisconnectMessage message)
+ {
+ DisconnectInternal();
+ }
+
+ private void HandleReject(LdnHeader header, RejectRequest reject)
+ {
+ // When the client receives a Reject request, we have been rejected and will be disconnected shortly.
+ _disconnectReason = reject.DisconnectReason;
+ }
+
+ private void HandleRejectReply(LdnHeader header)
+ {
+ _reject.Set();
+ }
+
+ private void HandleScanReply(LdnHeader header, NetworkInfo info)
+ {
+ _availableGames.Add(info);
+ }
+
+ private void HandleScanReplyEnd(LdnHeader obj)
+ {
+ _scan.Set();
+ }
+
+ private void DisconnectInternal()
+ {
+ if (_networkConnected)
+ {
+ _networkConnected = false;
+
+ _hostedProxy?.Dispose();
+ _hostedProxy = null;
+
+ _connectedProxy?.Dispose();
+ _connectedProxy = null;
+
+ _apConnected.Reset();
+
+ NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false, _disconnectReason));
+
+ if (IsConnected)
+ {
+ _timeout.RefreshTimeout();
+ }
+ }
+ }
+
+ public void DisconnectNetwork()
+ {
+ if (_networkConnected)
+ {
+ SendAsync(_protocol.Encode(PacketId.Disconnect, new DisconnectMessage()));
+
+ DisconnectInternal();
+ }
+ }
+
+ public ResultCode Reject(DisconnectReason disconnectReason, uint nodeId)
+ {
+ if (_networkConnected)
+ {
+ _reject.Reset();
+
+ SendAsync(_protocol.Encode(PacketId.Reject, new RejectRequest(disconnectReason, nodeId)));
+
+ int index = WaitHandle.WaitAny(new WaitHandle[] { _reject, _error }, InactiveTimeout);
+
+ if (index == 0)
+ {
+ return (ConsumeNetworkError() != NetworkError.None) ? ResultCode.InvalidState : ResultCode.Success;
+ }
+ }
+
+ return ResultCode.InvalidState;
+ }
+
+ public void SetAdvertiseData(byte[] data)
+ {
+ // TODO: validate we're the owner (the server will do this anyways tho)
+ if (_networkConnected)
+ {
+ SendAsync(_protocol.Encode(PacketId.SetAdvertiseData, data));
+ }
+ }
+
+ public void SetGameVersion(byte[] versionString)
+ {
+ _gameVersion = versionString;
+
+ if (_gameVersion.Length < 0x10)
+ {
+ Array.Resize(ref _gameVersion, 0x10);
+ }
+ }
+
+ public void SetStationAcceptPolicy(AcceptPolicy acceptPolicy)
+ {
+ // TODO: validate we're the owner (the server will do this anyways tho)
+ if (_networkConnected)
+ {
+ SendAsync(_protocol.Encode(PacketId.SetAcceptPolicy, new SetAcceptPolicyRequest
+ {
+ StationAcceptPolicy = acceptPolicy
+ }));
+ }
+ }
+
+ private void DisposeProxy()
+ {
+ _hostedProxy?.Dispose();
+ _hostedProxy = null;
+ }
+
+ private void ConfigureAccessPoint(ref RyuNetworkConfig request)
+ {
+ _gameVersion.AsSpan().CopyTo(request.GameVersion.AsSpan());
+
+ if (_useP2pProxy)
+ {
+ // Before sending the request, attempt to set up a proxy server.
+ // This can be on a range of private ports, which can be exposed on a range of public
+ // ports via UPnP. If any of this fails, we just fall back to using the master server.
+
+ int i = 0;
+ for (; i < P2pProxyServer.PrivatePortRange; i++)
+ {
+ _hostedProxy = new P2pProxyServer(this, (ushort)(P2pProxyServer.PrivatePortBase + i), _protocol);
+
+ try
+ {
+ _hostedProxy.Start();
+
+ break;
+ }
+ catch (SocketException e)
+ {
+ _hostedProxy.Dispose();
+ _hostedProxy = null;
+
+ if (e.SocketErrorCode != SocketError.AddressAlreadyInUse)
+ {
+ i = P2pProxyServer.PrivatePortRange; // Immediately fail.
+ }
+ }
+ }
+
+ bool openSuccess = i < P2pProxyServer.PrivatePortRange;
+
+ if (openSuccess)
+ {
+ Task natPunchResult = _hostedProxy.NatPunch();
+
+ try
+ {
+ if (natPunchResult.Result != 0)
+ {
+ // Tell the server that we are hosting the proxy.
+ request.ExternalProxyPort = natPunchResult.Result;
+ }
+ }
+ catch (Exception) { }
+
+ if (request.ExternalProxyPort == 0)
+ {
+ Logger.Warning?.Print(LogClass.ServiceLdn, "Failed to open a port with UPnP for P2P connection. Proxying through the master server instead. Expect higher latency.");
+ _hostedProxy.Dispose();
+ }
+ else
+ {
+ Logger.Info?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}.");
+ _hostedProxy.Start();
+
+ (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface();
+
+ unicastAddress.Address.GetAddressBytes().AsSpan().CopyTo(request.PrivateIp.AsSpan());
+ request.InternalProxyPort = _hostedProxy.PrivatePort;
+ request.AddressFamily = unicastAddress.Address.AddressFamily;
+ }
+ }
+ else
+ {
+ Logger.Warning?.Print(LogClass.ServiceLdn, "Cannot create a P2P server. Proxying through the master server instead. Expect higher latency.");
+ }
+ }
+ }
+
+ private bool CreateNetworkCommon()
+ {
+ bool signalled = _apConnected.WaitOne(FailureTimeout);
+
+ if (!_useP2pProxy && _hostedProxy != null)
+ {
+ Logger.Warning?.Print(LogClass.ServiceLdn, "Locally hosted proxy server was not externally reachable. Proxying through the master server instead. Expect higher latency.");
+
+ DisposeProxy();
+ }
+
+ if (signalled && _connectedProxy != null)
+ {
+ _connectedProxy.EnsureProxyReady();
+
+ Config = _connectedProxy.ProxyConfig;
+ }
+ else
+ {
+ DisposeProxy();
+ }
+
+ return signalled;
+ }
+
+ public bool CreateNetwork(CreateAccessPointRequest request, byte[] advertiseData)
+ {
+ _timeout.DisableTimeout();
+
+ ConfigureAccessPoint(ref request.RyuNetworkConfig);
+
+ if (!EnsureConnected())
+ {
+ DisposeProxy();
+
+ return false;
+ }
+
+ UpdatePassphraseIfNeeded();
+
+ SendAsync(_protocol.Encode(PacketId.CreateAccessPoint, request, advertiseData));
+
+ // Send a network change event with dummy data immediately. Necessary to avoid crashes in some games
+ var networkChangeEvent = new NetworkChangeEventArgs(new NetworkInfo()
+ {
+ Common = new CommonNetworkInfo()
+ {
+ MacAddress = InitializeMemory.MacAddress,
+ Channel = request.NetworkConfig.Channel,
+ LinkLevel = 3,
+ NetworkType = 2,
+ Ssid = new Ssid()
+ {
+ Length = 32
+ }
+ },
+ Ldn = new LdnNetworkInfo()
+ {
+ AdvertiseDataSize = (ushort)advertiseData.Length,
+ AuthenticationId = 0,
+ NodeCount = 1,
+ NodeCountMax = request.NetworkConfig.NodeCountMax,
+ SecurityMode = (ushort)request.SecurityConfig.SecurityMode
+ }
+ }, true);
+ networkChangeEvent.Info.Ldn.Nodes[0] = new NodeInfo()
+ {
+ Ipv4Address = 175243265,
+ IsConnected = 1,
+ LocalCommunicationVersion = request.NetworkConfig.LocalCommunicationVersion,
+ MacAddress = InitializeMemory.MacAddress,
+ NodeId = 0,
+ UserName = request.UserConfig.UserName
+ };
+ "12345678123456781234567812345678"u8.ToArray().CopyTo(networkChangeEvent.Info.Common.Ssid.Name.AsSpan());
+ NetworkChange?.Invoke(this, networkChangeEvent);
+
+ return CreateNetworkCommon();
+ }
+
+ public bool CreateNetworkPrivate(CreateAccessPointPrivateRequest request, byte[] advertiseData)
+ {
+ _timeout.DisableTimeout();
+
+ ConfigureAccessPoint(ref request.RyuNetworkConfig);
+
+ if (!EnsureConnected())
+ {
+ DisposeProxy();
+
+ return false;
+ }
+
+ UpdatePassphraseIfNeeded();
+
+ SendAsync(_protocol.Encode(PacketId.CreateAccessPointPrivate, request, advertiseData));
+
+ return CreateNetworkCommon();
+ }
+
+ public NetworkInfo[] Scan(ushort channel, ScanFilter scanFilter)
+ {
+ if (!_networkConnected)
+ {
+ _timeout.RefreshTimeout();
+ }
+
+ _availableGames.Clear();
+
+ int index = -1;
+
+ if (EnsureConnected())
+ {
+ UpdatePassphraseIfNeeded();
+
+ _scan.Reset();
+
+ SendAsync(_protocol.Encode(PacketId.Scan, scanFilter));
+
+ index = WaitHandle.WaitAny(new WaitHandle[] { _scan, _error }, ScanTimeout);
+ }
+
+ if (index != 0)
+ {
+ // An error occurred or timeout. Write 0 games.
+ return Array.Empty();
+ }
+
+ return _availableGames.ToArray();
+ }
+
+ private NetworkError ConnectCommon()
+ {
+ bool signalled = _apConnected.WaitOne(FailureTimeout);
+
+ NetworkError error = ConsumeNetworkError();
+
+ if (error != NetworkError.None)
+ {
+ return error;
+ }
+
+ if (signalled && _connectedProxy != null)
+ {
+ _connectedProxy.EnsureProxyReady();
+
+ Config = _connectedProxy.ProxyConfig;
+ }
+
+ return signalled ? NetworkError.None : NetworkError.ConnectTimeout;
+ }
+
+ public NetworkError Connect(ConnectRequest request)
+ {
+ _timeout.DisableTimeout();
+
+ if (!EnsureConnected())
+ {
+ return NetworkError.Unknown;
+ }
+
+ SendAsync(_protocol.Encode(PacketId.Connect, request));
+
+ var networkChangeEvent = new NetworkChangeEventArgs(new NetworkInfo()
+ {
+ Common = request.NetworkInfo.Common,
+ Ldn = request.NetworkInfo.Ldn
+ }, true);
+
+ NetworkChange?.Invoke(this, networkChangeEvent);
+
+ return ConnectCommon();
+ }
+
+ public NetworkError ConnectPrivate(ConnectPrivateRequest request)
+ {
+ _timeout.DisableTimeout();
+
+ if (!EnsureConnected())
+ {
+ return NetworkError.Unknown;
+ }
+
+ SendAsync(_protocol.Encode(PacketId.ConnectPrivate, request));
+
+ return ConnectCommon();
+ }
+
+ private void HandleProxyConfig(LdnHeader header, ProxyConfig config)
+ {
+ Config = config;
+
+ SocketHelpers.RegisterProxy(new LdnProxy(config, this, _protocol));
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/NetworkTimeout.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/NetworkTimeout.cs
new file mode 100644
index 000000000..5012d5d81
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/NetworkTimeout.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
+{
+ class NetworkTimeout : IDisposable
+ {
+ private readonly int _idleTimeout;
+ private readonly Action _timeoutCallback;
+ private CancellationTokenSource _cancel;
+
+ private readonly object _lock = new object();
+
+ public NetworkTimeout(int idleTimeout, Action timeoutCallback)
+ {
+ _idleTimeout = idleTimeout;
+ _timeoutCallback = timeoutCallback;
+ }
+
+ private async Task TimeoutTask()
+ {
+ CancellationTokenSource cts;
+
+ lock (_lock)
+ {
+ cts = _cancel;
+ }
+
+ if (cts == null)
+ {
+ return;
+ }
+
+ try
+ {
+ await Task.Delay(_idleTimeout, cts.Token);
+ }
+ catch (TaskCanceledException)
+ {
+ return; // Timeout cancelled.
+ }
+
+ lock (_lock)
+ {
+ // Run the timeout callback. If the cancel token source has been replaced, we have _just_ been cancelled.
+ if (cts == _cancel)
+ {
+ _timeoutCallback();
+ }
+ }
+ }
+
+ public bool RefreshTimeout()
+ {
+ lock (_lock)
+ {
+ _cancel?.Cancel();
+
+ _cancel = new CancellationTokenSource();
+
+ Task.Run(TimeoutTask);
+ }
+
+ return true;
+ }
+
+ public void DisableTimeout()
+ {
+ lock (_lock)
+ {
+ _cancel?.Cancel();
+
+ _cancel = new CancellationTokenSource();
+ }
+ }
+
+ public void Dispose()
+ {
+ DisableTimeout();
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/EphemeralPortPool.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/EphemeralPortPool.cs
new file mode 100644
index 000000000..bc3a5edf2
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/EphemeralPortPool.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
+{
+ public class EphemeralPortPool
+ {
+ private const ushort EphemeralBase = 49152;
+
+ private readonly List _ephemeralPorts = new List();
+
+ private readonly object _lock = new object();
+
+ public ushort Get()
+ {
+ ushort port = EphemeralBase;
+ lock (_lock)
+ {
+ // Starting at the ephemeral port base, return an ephemeral port that is not in use.
+ // Returns 0 if the range is exhausted.
+
+ for (int i = 0; i < _ephemeralPorts.Count; i++)
+ {
+ ushort existingPort = _ephemeralPorts[i];
+
+ if (existingPort > port)
+ {
+ // The port was free - take it.
+ _ephemeralPorts.Insert(i, port);
+
+ return port;
+ }
+
+ port++;
+ }
+
+ if (port != 0)
+ {
+ _ephemeralPorts.Add(port);
+ }
+
+ return port;
+ }
+ }
+
+ public void Return(ushort port)
+ {
+ lock (_lock)
+ {
+ _ephemeralPorts.Remove(port);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxy.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxy.cs
new file mode 100644
index 000000000..bb390d49a
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxy.cs
@@ -0,0 +1,254 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
+{
+ class LdnProxy : IDisposable
+ {
+ public EndPoint LocalEndpoint { get; }
+ public IPAddress LocalAddress { get; }
+
+ private readonly List _sockets = new List();
+ private readonly Dictionary _ephemeralPorts = new Dictionary();
+
+ private readonly IProxyClient _parent;
+ private RyuLdnProtocol _protocol;
+ private readonly uint _subnetMask;
+ private readonly uint _localIp;
+ private readonly uint _broadcast;
+
+ public LdnProxy(ProxyConfig config, IProxyClient client, RyuLdnProtocol protocol)
+ {
+ _parent = client;
+ _protocol = protocol;
+
+ _ephemeralPorts[ProtocolType.Udp] = new EphemeralPortPool();
+ _ephemeralPorts[ProtocolType.Tcp] = new EphemeralPortPool();
+
+ byte[] address = BitConverter.GetBytes(config.ProxyIp);
+ Array.Reverse(address);
+ LocalAddress = new IPAddress(address);
+
+ _subnetMask = config.ProxySubnetMask;
+ _localIp = config.ProxyIp;
+ _broadcast = _localIp | (~_subnetMask);
+
+ RegisterHandlers(protocol);
+ }
+
+ public bool Supported(AddressFamily domain, SocketType type, ProtocolType protocol)
+ {
+ if (protocol == ProtocolType.Tcp)
+ {
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, "Tcp proxy networking is untested. Please report this game so that it can be tested.");
+ }
+ return domain == AddressFamily.InterNetwork && (protocol == ProtocolType.Tcp || protocol == ProtocolType.Udp);
+ }
+
+ private void RegisterHandlers(RyuLdnProtocol protocol)
+ {
+ protocol.ProxyConnect += HandleConnectionRequest;
+ protocol.ProxyConnectReply += HandleConnectionResponse;
+ protocol.ProxyData += HandleData;
+ protocol.ProxyDisconnect += HandleDisconnect;
+
+ _protocol = protocol;
+ }
+
+ public void UnregisterHandlers(RyuLdnProtocol protocol)
+ {
+ protocol.ProxyConnect -= HandleConnectionRequest;
+ protocol.ProxyConnectReply -= HandleConnectionResponse;
+ protocol.ProxyData -= HandleData;
+ protocol.ProxyDisconnect -= HandleDisconnect;
+ }
+
+ public ushort GetEphemeralPort(ProtocolType type)
+ {
+ return _ephemeralPorts[type].Get();
+ }
+
+ public void ReturnEphemeralPort(ProtocolType type, ushort port)
+ {
+ _ephemeralPorts[type].Return(port);
+ }
+
+ public void RegisterSocket(LdnProxySocket socket)
+ {
+ lock (_sockets)
+ {
+ _sockets.Add(socket);
+ }
+ }
+
+ public void UnregisterSocket(LdnProxySocket socket)
+ {
+ lock (_sockets)
+ {
+ _sockets.Remove(socket);
+ }
+ }
+
+ private void ForRoutedSockets(ProxyInfo info, Action action)
+ {
+ lock (_sockets)
+ {
+ foreach (LdnProxySocket socket in _sockets)
+ {
+ // Must match protocol and destination port.
+ if (socket.ProtocolType != info.Protocol || socket.LocalEndPoint is not IPEndPoint endpoint || endpoint.Port != info.DestPort)
+ {
+ continue;
+ }
+
+ // We can assume packets routed to us have been sent to our destination.
+ // They will either be sent to us, or broadcast packets.
+
+ action(socket);
+ }
+ }
+ }
+
+ public void HandleConnectionRequest(LdnHeader header, ProxyConnectRequest request)
+ {
+ ForRoutedSockets(request.Info, (socket) =>
+ {
+ socket.HandleConnectRequest(request);
+ });
+ }
+
+ public void HandleConnectionResponse(LdnHeader header, ProxyConnectResponse response)
+ {
+ ForRoutedSockets(response.Info, (socket) =>
+ {
+ socket.HandleConnectResponse(response);
+ });
+ }
+
+ public void HandleData(LdnHeader header, ProxyDataHeader proxyHeader, byte[] data)
+ {
+ ProxyDataPacket packet = new ProxyDataPacket() { Header = proxyHeader, Data = data };
+
+ ForRoutedSockets(proxyHeader.Info, (socket) =>
+ {
+ socket.IncomingData(packet);
+ });
+ }
+
+ public void HandleDisconnect(LdnHeader header, ProxyDisconnectMessage disconnect)
+ {
+ ForRoutedSockets(disconnect.Info, (socket) =>
+ {
+ socket.HandleDisconnect(disconnect);
+ });
+ }
+
+ private uint GetIpV4(IPEndPoint endpoint)
+ {
+ if (endpoint.AddressFamily != AddressFamily.InterNetwork)
+ {
+ throw new NotSupportedException();
+ }
+
+ byte[] address = endpoint.Address.GetAddressBytes();
+ Array.Reverse(address);
+
+ return BitConverter.ToUInt32(address);
+ }
+
+ private ProxyInfo MakeInfo(IPEndPoint localEp, IPEndPoint remoteEP, ProtocolType type)
+ {
+ return new ProxyInfo
+ {
+ SourceIpV4 = GetIpV4(localEp),
+ SourcePort = (ushort)localEp.Port,
+
+ DestIpV4 = GetIpV4(remoteEP),
+ DestPort = (ushort)remoteEP.Port,
+
+ Protocol = type
+ };
+ }
+
+ public void RequestConnection(IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type)
+ {
+ // We must ask the other side to initialize a connection, so they can accept a socket for us.
+
+ ProxyConnectRequest request = new ProxyConnectRequest
+ {
+ Info = MakeInfo(localEp, remoteEp, type)
+ };
+
+ _parent.SendAsync(_protocol.Encode(PacketId.ProxyConnect, request));
+ }
+
+ public void SignalConnected(IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type)
+ {
+ // We must tell the other side that we have accepted their request for connection.
+
+ ProxyConnectResponse request = new ProxyConnectResponse
+ {
+ Info = MakeInfo(localEp, remoteEp, type)
+ };
+
+ _parent.SendAsync(_protocol.Encode(PacketId.ProxyConnectReply, request));
+ }
+
+ public void EndConnection(IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type)
+ {
+ // We must tell the other side that our connection is dropped.
+
+ ProxyDisconnectMessage request = new ProxyDisconnectMessage
+ {
+ Info = MakeInfo(localEp, remoteEp, type),
+ DisconnectReason = 0 // TODO
+ };
+
+ _parent.SendAsync(_protocol.Encode(PacketId.ProxyDisconnect, request));
+ }
+
+ public int SendTo(ReadOnlySpan buffer, SocketFlags flags, IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type)
+ {
+ // We send exactly as much as the user wants us to, currently instantly.
+ // TODO: handle over "virtual mtu" (we have a max packet size to worry about anyways). fragment if tcp? throw if udp?
+
+ ProxyDataHeader request = new ProxyDataHeader
+ {
+ Info = MakeInfo(localEp, remoteEp, type),
+ DataLength = (uint)buffer.Length
+ };
+
+ _parent.SendAsync(_protocol.Encode(PacketId.ProxyData, request, buffer.ToArray()));
+
+ return buffer.Length;
+ }
+
+ public bool IsBroadcast(uint ip)
+ {
+ return ip == _broadcast;
+ }
+
+ public bool IsMyself(uint ip)
+ {
+ return ip == _localIp;
+ }
+
+ public void Dispose()
+ {
+ UnregisterHandlers(_protocol);
+
+ lock (_sockets)
+ {
+ foreach (LdnProxySocket socket in _sockets)
+ {
+ socket.ProxyDestroyed();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxySocket.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxySocket.cs
new file mode 100644
index 000000000..ed7a9c751
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxySocket.cs
@@ -0,0 +1,797 @@
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
+using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl;
+using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
+{
+ ///
+ /// This socket is forwarded through a TCP stream that goes through the Ldn server.
+ /// The Ldn server will then route the packets we send (or need to receive) within the virtual adhoc network.
+ ///
+ class LdnProxySocket : ISocketImpl
+ {
+ private readonly LdnProxy _proxy;
+
+ private bool _isListening;
+ private readonly List _listenSockets = new List();
+
+ private readonly Queue _connectRequests = new Queue();
+
+ private readonly AutoResetEvent _acceptEvent = new AutoResetEvent(false);
+ private readonly int _acceptTimeout = -1;
+
+ private readonly Queue _errors = new Queue();
+
+ private readonly AutoResetEvent _connectEvent = new AutoResetEvent(false);
+ private ProxyConnectResponse _connectResponse;
+
+ private int _receiveTimeout = -1;
+ private readonly AutoResetEvent _receiveEvent = new AutoResetEvent(false);
+ private readonly Queue _receiveQueue = new Queue();
+
+ // private int _sendTimeout = -1; // Sends are techically instant right now, so not _really_ used.
+
+ private bool _connecting;
+ private bool _broadcast;
+ private bool _readShutdown;
+ // private bool _writeShutdown;
+ private bool _closed;
+
+ private readonly Dictionary _socketOptions = new Dictionary()
+ {
+ { SocketOptionName.Broadcast, 0 }, //TODO: honor this value
+ { SocketOptionName.DontLinger, 0 },
+ { SocketOptionName.Debug, 0 },
+ { SocketOptionName.Error, 0 },
+ { SocketOptionName.KeepAlive, 0 },
+ { SocketOptionName.OutOfBandInline, 0 },
+ { SocketOptionName.ReceiveBuffer, 131072 },
+ { SocketOptionName.ReceiveTimeout, -1 },
+ { SocketOptionName.SendBuffer, 131072 },
+ { SocketOptionName.SendTimeout, -1 },
+ { SocketOptionName.Type, 0 },
+ { SocketOptionName.ReuseAddress, 0 } //TODO: honor this value
+ };
+
+ public EndPoint RemoteEndPoint { get; private set; }
+
+ public EndPoint LocalEndPoint { get; private set; }
+
+ public bool Connected { get; private set; }
+
+ public bool IsBound { get; private set; }
+
+ public AddressFamily AddressFamily { get; }
+
+ public SocketType SocketType { get; }
+
+ public ProtocolType ProtocolType { get; }
+
+ public bool Blocking { get; set; }
+
+ public int Available
+ {
+ get
+ {
+ int result = 0;
+
+ lock (_receiveQueue)
+ {
+ foreach (ProxyDataPacket data in _receiveQueue)
+ {
+ result += data.Data.Length;
+ }
+ }
+
+ return result;
+ }
+ }
+
+ public bool Readable
+ {
+ get
+ {
+ if (_isListening)
+ {
+ lock (_connectRequests)
+ {
+ return _connectRequests.Count > 0;
+ }
+ }
+ else
+ {
+ if (_readShutdown)
+ {
+ return true;
+ }
+
+ lock (_receiveQueue)
+ {
+ return _receiveQueue.Count > 0;
+ }
+ }
+
+ }
+ }
+ public bool Writable => Connected || ProtocolType == ProtocolType.Udp;
+ public bool Error => false;
+
+ public LdnProxySocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, LdnProxy proxy)
+ {
+ AddressFamily = addressFamily;
+ SocketType = socketType;
+ ProtocolType = protocolType;
+
+ _proxy = proxy;
+ _socketOptions[SocketOptionName.Type] = (int)socketType;
+
+ proxy.RegisterSocket(this);
+ }
+
+ private IPEndPoint EnsureLocalEndpoint(bool replace)
+ {
+ if (LocalEndPoint != null)
+ {
+ if (replace)
+ {
+ _proxy.ReturnEphemeralPort(ProtocolType, (ushort)((IPEndPoint)LocalEndPoint).Port);
+ }
+ else
+ {
+ return (IPEndPoint)LocalEndPoint;
+ }
+ }
+
+ IPEndPoint localEp = new IPEndPoint(_proxy.LocalAddress, _proxy.GetEphemeralPort(ProtocolType));
+ LocalEndPoint = localEp;
+
+ return localEp;
+ }
+
+ public LdnProxySocket AsAccepted(IPEndPoint remoteEp)
+ {
+ Connected = true;
+ RemoteEndPoint = remoteEp;
+
+ IPEndPoint localEp = EnsureLocalEndpoint(true);
+
+ _proxy.SignalConnected(localEp, remoteEp, ProtocolType);
+
+ return this;
+ }
+
+ private void SignalError(WsaError error)
+ {
+ lock (_errors)
+ {
+ _errors.Enqueue((int)error);
+ }
+ }
+
+ private IPEndPoint GetEndpoint(uint ipv4, ushort port)
+ {
+ byte[] address = BitConverter.GetBytes(ipv4);
+ Array.Reverse(address);
+
+ return new IPEndPoint(new IPAddress(address), port);
+ }
+
+ public void IncomingData(ProxyDataPacket packet)
+ {
+ bool isBroadcast = _proxy.IsBroadcast(packet.Header.Info.DestIpV4);
+
+ if (!_closed && (_broadcast || !isBroadcast))
+ {
+ lock (_receiveQueue)
+ {
+ _receiveQueue.Enqueue(packet);
+ }
+ }
+ }
+
+ public ISocketImpl Accept()
+ {
+ if (!_isListening)
+ {
+ throw new InvalidOperationException();
+ }
+
+ // Accept a pending request to this socket.
+
+ lock (_connectRequests)
+ {
+ if (!Blocking && _connectRequests.Count == 0)
+ {
+ throw new SocketException((int)WsaError.WSAEWOULDBLOCK);
+ }
+ }
+
+ while (true)
+ {
+ _acceptEvent.WaitOne(_acceptTimeout);
+
+ lock (_connectRequests)
+ {
+ while (_connectRequests.Count > 0)
+ {
+ ProxyConnectRequest request = _connectRequests.Dequeue();
+
+ if (_connectRequests.Count > 0)
+ {
+ _acceptEvent.Set(); // Still more accepts to do.
+ }
+
+ // Is this request made for us?
+ IPEndPoint endpoint = GetEndpoint(request.Info.DestIpV4, request.Info.DestPort);
+
+ if (Equals(endpoint, LocalEndPoint))
+ {
+ // Yes - let's accept.
+ IPEndPoint remoteEndpoint = GetEndpoint(request.Info.SourceIpV4, request.Info.SourcePort);
+
+ LdnProxySocket socket = new LdnProxySocket(AddressFamily, SocketType, ProtocolType, _proxy).AsAccepted(remoteEndpoint);
+
+ lock (_listenSockets)
+ {
+ _listenSockets.Add(socket);
+ }
+
+ return socket;
+ }
+ }
+ }
+ }
+ }
+
+ public void Bind(EndPoint localEP)
+ {
+ ArgumentNullException.ThrowIfNull(localEP);
+
+ if (LocalEndPoint != null)
+ {
+ _proxy.ReturnEphemeralPort(ProtocolType, (ushort)((IPEndPoint)LocalEndPoint).Port);
+ }
+ var asIPEndpoint = (IPEndPoint)localEP;
+ if (asIPEndpoint.Port == 0)
+ {
+ asIPEndpoint.Port = (ushort)_proxy.GetEphemeralPort(ProtocolType);
+ }
+
+ LocalEndPoint = (IPEndPoint)localEP;
+
+ IsBound = true;
+ }
+
+ public void Close()
+ {
+ _closed = true;
+
+ _proxy.UnregisterSocket(this);
+
+ if (Connected)
+ {
+ Disconnect(false);
+ }
+
+ lock (_listenSockets)
+ {
+ foreach (LdnProxySocket socket in _listenSockets)
+ {
+ socket.Close();
+ }
+ }
+
+ _isListening = false;
+ }
+
+ public void Connect(EndPoint remoteEP)
+ {
+ if (_isListening || !IsBound)
+ {
+ throw new InvalidOperationException();
+ }
+
+ if (remoteEP is not IPEndPoint)
+ {
+ throw new NotSupportedException();
+ }
+
+ IPEndPoint localEp = EnsureLocalEndpoint(true);
+
+ _connecting = true;
+
+ _proxy.RequestConnection(localEp, (IPEndPoint)remoteEP, ProtocolType);
+
+ if (!Blocking && ProtocolType == ProtocolType.Tcp)
+ {
+ throw new SocketException((int)WsaError.WSAEWOULDBLOCK);
+ }
+
+ _connectEvent.WaitOne(); //timeout?
+
+ if (_connectResponse.Info.SourceIpV4 == 0)
+ {
+ throw new SocketException((int)WsaError.WSAECONNREFUSED);
+ }
+
+ _connectResponse = default;
+ }
+
+ public void HandleConnectResponse(ProxyConnectResponse obj)
+ {
+ if (!_connecting)
+ {
+ return;
+ }
+
+ _connecting = false;
+
+ if (_connectResponse.Info.SourceIpV4 != 0)
+ {
+ IPEndPoint remoteEp = GetEndpoint(obj.Info.SourceIpV4, obj.Info.SourcePort);
+ RemoteEndPoint = remoteEp;
+
+ Connected = true;
+ }
+ else
+ {
+ // Connection failed
+
+ SignalError(WsaError.WSAECONNREFUSED);
+ }
+ }
+
+ public void Disconnect(bool reuseSocket)
+ {
+ if (Connected)
+ {
+ ConnectionEnded();
+
+ // The other side needs to be notified that connection ended.
+ _proxy.EndConnection(LocalEndPoint as IPEndPoint, RemoteEndPoint as IPEndPoint, ProtocolType);
+ }
+ }
+
+ private void ConnectionEnded()
+ {
+ if (Connected)
+ {
+ RemoteEndPoint = null;
+ Connected = false;
+ }
+ }
+
+ public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue)
+ {
+ if (optionLevel != SocketOptionLevel.Socket)
+ {
+ throw new NotImplementedException();
+ }
+
+ if (_socketOptions.TryGetValue(optionName, out int result))
+ {
+ byte[] data = BitConverter.GetBytes(result);
+ Array.Copy(data, 0, optionValue, 0, Math.Min(data.Length, optionValue.Length));
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public void Listen(int backlog)
+ {
+ if (!IsBound)
+ {
+ throw new SocketException();
+ }
+
+ _isListening = true;
+ }
+
+ public void HandleConnectRequest(ProxyConnectRequest obj)
+ {
+ lock (_connectRequests)
+ {
+ _connectRequests.Enqueue(obj);
+ }
+
+ _connectEvent.Set();
+ }
+
+ public void HandleDisconnect(ProxyDisconnectMessage message)
+ {
+ Disconnect(false);
+ }
+
+ public int Receive(Span buffer)
+ {
+ EndPoint dummy = new IPEndPoint(IPAddress.Any, 0);
+
+ return ReceiveFrom(buffer, SocketFlags.None, ref dummy);
+ }
+
+ public int Receive(Span buffer, SocketFlags flags)
+ {
+ EndPoint dummy = new IPEndPoint(IPAddress.Any, 0);
+
+ return ReceiveFrom(buffer, flags, ref dummy);
+ }
+
+ public int Receive(Span buffer, SocketFlags flags, out SocketError socketError)
+ {
+ EndPoint dummy = new IPEndPoint(IPAddress.Any, 0);
+
+ return ReceiveFrom(buffer, flags, out socketError, ref dummy);
+ }
+
+ public int ReceiveFrom(Span buffer, SocketFlags flags, ref EndPoint remoteEp)
+ {
+ // We just receive all packets meant for us anyways regardless of EP in the actual implementation.
+ // The point is mostly to return the endpoint that we got the data from.
+
+ if (!Connected && ProtocolType == ProtocolType.Tcp)
+ {
+ throw new SocketException((int)WsaError.WSAECONNRESET);
+ }
+
+ lock (_receiveQueue)
+ {
+ if (_receiveQueue.Count > 0)
+ {
+ return ReceiveFromQueue(buffer, flags, ref remoteEp);
+ }
+ else if (_readShutdown)
+ {
+ return 0;
+ }
+ else if (!Blocking)
+ {
+ throw new SocketException((int)WsaError.WSAEWOULDBLOCK);
+ }
+ }
+
+ int timeout = _receiveTimeout;
+
+ _receiveEvent.WaitOne(timeout == 0 ? -1 : timeout);
+
+ if (!Connected && ProtocolType == ProtocolType.Tcp)
+ {
+ throw new SocketException((int)WsaError.WSAECONNRESET);
+ }
+
+ lock (_receiveQueue)
+ {
+ if (_receiveQueue.Count > 0)
+ {
+ return ReceiveFromQueue(buffer, flags, ref remoteEp);
+ }
+ else if (_readShutdown)
+ {
+ return 0;
+ }
+ else
+ {
+ throw new SocketException((int)WsaError.WSAETIMEDOUT);
+ }
+ }
+ }
+
+ public int ReceiveFrom(Span buffer, SocketFlags flags, out SocketError socketError, ref EndPoint remoteEp)
+ {
+ // We just receive all packets meant for us anyways regardless of EP in the actual implementation.
+ // The point is mostly to return the endpoint that we got the data from.
+
+ if (!Connected && ProtocolType == ProtocolType.Tcp)
+ {
+ socketError = SocketError.ConnectionReset;
+ return -1;
+ }
+
+ lock (_receiveQueue)
+ {
+ if (_receiveQueue.Count > 0)
+ {
+ return ReceiveFromQueue(buffer, flags, out socketError, ref remoteEp);
+ }
+ else if (_readShutdown)
+ {
+ socketError = SocketError.Success;
+ return 0;
+ }
+ else if (!Blocking)
+ {
+ throw new SocketException((int)WsaError.WSAEWOULDBLOCK);
+ }
+ }
+
+ int timeout = _receiveTimeout;
+
+ _receiveEvent.WaitOne(timeout == 0 ? -1 : timeout);
+
+ if (!Connected && ProtocolType == ProtocolType.Tcp)
+ {
+ throw new SocketException((int)WsaError.WSAECONNRESET);
+ }
+
+ lock (_receiveQueue)
+ {
+ if (_receiveQueue.Count > 0)
+ {
+ return ReceiveFromQueue(buffer, flags, out socketError, ref remoteEp);
+ }
+ else if (_readShutdown)
+ {
+ socketError = SocketError.Success;
+ return 0;
+ }
+ else
+ {
+ socketError = SocketError.TimedOut;
+ return -1;
+ }
+ }
+ }
+
+ private int ReceiveFromQueue(Span buffer, SocketFlags flags, ref EndPoint remoteEp)
+ {
+ int size = buffer.Length;
+
+ // Assumes we have the receive queue lock, and at least one item in the queue.
+ ProxyDataPacket packet = _receiveQueue.Peek();
+
+ remoteEp = GetEndpoint(packet.Header.Info.SourceIpV4, packet.Header.Info.SourcePort);
+
+ bool peek = (flags & SocketFlags.Peek) != 0;
+
+ int read;
+
+ if (packet.Data.Length > size)
+ {
+ read = size;
+
+ // Cannot fit in the output buffer. Copy up to what we've got.
+ packet.Data.AsSpan(0, size).CopyTo(buffer);
+
+ if (ProtocolType == ProtocolType.Udp)
+ {
+ // Udp overflows, loses the data, then throws an exception.
+
+ if (!peek)
+ {
+ _receiveQueue.Dequeue();
+ }
+
+ throw new SocketException((int)WsaError.WSAEMSGSIZE);
+ }
+ else if (ProtocolType == ProtocolType.Tcp)
+ {
+ // Split the data at the buffer boundary. It will stay on the recieve queue.
+
+ byte[] newData = new byte[packet.Data.Length - size];
+ Array.Copy(packet.Data, size, newData, 0, newData.Length);
+
+ packet.Data = newData;
+ }
+ }
+ else
+ {
+ read = packet.Data.Length;
+
+ packet.Data.AsSpan(0, packet.Data.Length).CopyTo(buffer);
+
+ if (!peek)
+ {
+ _receiveQueue.Dequeue();
+ }
+ }
+
+ return read;
+ }
+
+ private int ReceiveFromQueue(Span buffer, SocketFlags flags, out SocketError socketError, ref EndPoint remoteEp)
+ {
+ int size = buffer.Length;
+
+ // Assumes we have the receive queue lock, and at least one item in the queue.
+ ProxyDataPacket packet = _receiveQueue.Peek();
+
+ remoteEp = GetEndpoint(packet.Header.Info.SourceIpV4, packet.Header.Info.SourcePort);
+
+ bool peek = (flags & SocketFlags.Peek) != 0;
+
+ int read;
+
+ if (packet.Data.Length > size)
+ {
+ read = size;
+
+ // Cannot fit in the output buffer. Copy up to what we've got.
+ packet.Data.AsSpan(0, size).CopyTo(buffer);
+
+ if (ProtocolType == ProtocolType.Udp)
+ {
+ // Udp overflows, loses the data, then throws an exception.
+
+ if (!peek)
+ {
+ _receiveQueue.Dequeue();
+ }
+
+ socketError = SocketError.MessageSize;
+ return -1;
+ }
+ else if (ProtocolType == ProtocolType.Tcp)
+ {
+ // Split the data at the buffer boundary. It will stay on the recieve queue.
+
+ byte[] newData = new byte[packet.Data.Length - size];
+ Array.Copy(packet.Data, size, newData, 0, newData.Length);
+
+ packet.Data = newData;
+ }
+ }
+ else
+ {
+ read = packet.Data.Length;
+
+ packet.Data.AsSpan(0, packet.Data.Length).CopyTo(buffer);
+
+ if (!peek)
+ {
+ _receiveQueue.Dequeue();
+ }
+ }
+
+ socketError = SocketError.Success;
+
+ return read;
+ }
+
+ public int Send(ReadOnlySpan buffer)
+ {
+ // Send to the remote host chosen when we "connect" or "accept".
+ if (!Connected)
+ {
+ throw new SocketException();
+ }
+
+ return SendTo(buffer, SocketFlags.None, RemoteEndPoint);
+ }
+
+ public int Send(ReadOnlySpan buffer, SocketFlags flags)
+ {
+ // Send to the remote host chosen when we "connect" or "accept".
+ if (!Connected)
+ {
+ throw new SocketException();
+ }
+
+ return SendTo(buffer, flags, RemoteEndPoint);
+ }
+
+ public int Send(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError)
+ {
+ // Send to the remote host chosen when we "connect" or "accept".
+ if (!Connected)
+ {
+ throw new SocketException();
+ }
+
+ return SendTo(buffer, flags, out socketError, RemoteEndPoint);
+ }
+
+ public int SendTo(ReadOnlySpan buffer, SocketFlags flags, EndPoint remoteEP)
+ {
+ if (!Connected && ProtocolType == ProtocolType.Tcp)
+ {
+ throw new SocketException((int)WsaError.WSAECONNRESET);
+ }
+
+ IPEndPoint localEp = EnsureLocalEndpoint(false);
+
+ if (remoteEP is not IPEndPoint)
+ {
+ throw new NotSupportedException();
+ }
+
+ return _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType);
+ }
+
+ public int SendTo(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError, EndPoint remoteEP)
+ {
+ if (!Connected && ProtocolType == ProtocolType.Tcp)
+ {
+ socketError = SocketError.ConnectionReset;
+ return -1;
+ }
+
+ IPEndPoint localEp = EnsureLocalEndpoint(false);
+
+ if (remoteEP is not IPEndPoint)
+ {
+ // throw new NotSupportedException();
+ socketError = SocketError.OperationNotSupported;
+ return -1;
+ }
+
+ socketError = SocketError.Success;
+
+ return _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType);
+ }
+
+ public bool Poll(int microSeconds, SelectMode mode)
+ {
+ return mode switch
+ {
+ SelectMode.SelectRead => Readable,
+ SelectMode.SelectWrite => Writable,
+ SelectMode.SelectError => Error,
+ _ => false
+ };
+ }
+
+ public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
+ {
+ if (optionLevel != SocketOptionLevel.Socket)
+ {
+ throw new NotImplementedException();
+ }
+
+ switch (optionName)
+ {
+ case SocketOptionName.SendTimeout:
+ //_sendTimeout = optionValue;
+ break;
+ case SocketOptionName.ReceiveTimeout:
+ _receiveTimeout = optionValue;
+ break;
+ case SocketOptionName.Broadcast:
+ _broadcast = optionValue != 0;
+ break;
+ }
+
+ lock (_socketOptions)
+ {
+ _socketOptions[optionName] = optionValue;
+ }
+ }
+
+ public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue)
+ {
+ // Just linger uses this for now in BSD, which we ignore.
+ }
+
+ public void Shutdown(SocketShutdown how)
+ {
+ switch (how)
+ {
+ case SocketShutdown.Both:
+ _readShutdown = true;
+ // _writeShutdown = true;
+ break;
+ case SocketShutdown.Receive:
+ _readShutdown = true;
+ break;
+ case SocketShutdown.Send:
+ // _writeShutdown = true;
+ break;
+ }
+ }
+
+ public void ProxyDestroyed()
+ {
+ // Do nothing, for now. Will likely be more useful with TCP.
+ }
+
+ public void Dispose()
+ {
+
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyClient.cs
new file mode 100644
index 000000000..7da1aa998
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyClient.cs
@@ -0,0 +1,93 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
+using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy;
+using System.Net.Sockets;
+using System.Threading;
+using TcpClient = NetCoreServer.TcpClient;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
+{
+ class P2pProxyClient : TcpClient, IProxyClient
+ {
+ private const int FailureTimeout = 4000;
+
+ public ProxyConfig ProxyConfig { get; private set; }
+
+ private readonly RyuLdnProtocol _protocol;
+
+ private readonly ManualResetEvent _connected = new ManualResetEvent(false);
+ private readonly ManualResetEvent _ready = new ManualResetEvent(false);
+ private readonly AutoResetEvent _error = new AutoResetEvent(false);
+
+ public P2pProxyClient(string address, int port) : base(address, port)
+ {
+ if (ProxyHelpers.SupportsNoDelay())
+ {
+ OptionNoDelay = true;
+ }
+
+ _protocol = new RyuLdnProtocol();
+
+ _protocol.ProxyConfig += HandleProxyConfig;
+
+ ConnectAsync();
+ }
+
+ protected override void OnConnected()
+ {
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP client connected a new session with Id {Id}");
+
+ _connected.Set();
+ }
+
+ protected override void OnDisconnected()
+ {
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP client disconnected a session with Id {Id}");
+
+ SocketHelpers.UnregisterProxy();
+
+ _connected.Reset();
+ }
+
+ protected override void OnReceived(byte[] buffer, long offset, long size)
+ {
+ _protocol.Read(buffer, (int)offset, (int)size);
+ }
+
+ protected override void OnError(SocketError error)
+ {
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP client caught an error with code {error}");
+
+ _error.Set();
+ }
+
+ private void HandleProxyConfig(LdnHeader header, ProxyConfig config)
+ {
+ ProxyConfig = config;
+
+ SocketHelpers.RegisterProxy(new LdnProxy(config, this, _protocol));
+
+ _ready.Set();
+ }
+
+ public bool EnsureProxyReady()
+ {
+ return _ready.WaitOne(FailureTimeout);
+ }
+
+ public bool PerformAuth(ExternalProxyConfig config)
+ {
+ bool signalled = _connected.WaitOne(FailureTimeout);
+
+ if (!signalled)
+ {
+ return false;
+ }
+
+ SendAsync(_protocol.Encode(PacketId.ExternalProxy, config));
+
+ return true;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs
new file mode 100644
index 000000000..598fb654f
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs
@@ -0,0 +1,388 @@
+using NetCoreServer;
+using Open.Nat;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
+{
+ class P2pProxyServer : TcpServer, IDisposable
+ {
+ public const ushort PrivatePortBase = 39990;
+ public const int PrivatePortRange = 10;
+
+ private const ushort PublicPortBase = 39990;
+ private const int PublicPortRange = 10;
+
+ private const ushort PortLeaseLength = 60;
+ private const ushort PortLeaseRenew = 50;
+
+ private const ushort AuthWaitSeconds = 1;
+
+ private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
+
+ public ushort PrivatePort { get; }
+
+ private ushort _publicPort;
+
+ private bool _disposed;
+ private readonly CancellationTokenSource _disposedCancellation = new CancellationTokenSource();
+
+ private NatDevice _natDevice;
+ private Mapping _portMapping;
+
+ private readonly List _players = new List();
+
+ private readonly List _waitingTokens = new List();
+ private readonly AutoResetEvent _tokenEvent = new AutoResetEvent(false);
+
+ private uint _broadcastAddress;
+
+ private readonly LdnMasterProxyClient _master;
+ private readonly RyuLdnProtocol _masterProtocol;
+ private readonly RyuLdnProtocol _protocol;
+
+ public P2pProxyServer(LdnMasterProxyClient master, ushort port, RyuLdnProtocol masterProtocol) : base(IPAddress.Any, port)
+ {
+ if (ProxyHelpers.SupportsNoDelay())
+ {
+ OptionNoDelay = true;
+ }
+
+ PrivatePort = port;
+
+ _master = master;
+ _masterProtocol = masterProtocol;
+
+ _masterProtocol.ExternalProxyState += HandleStateChange;
+ _masterProtocol.ExternalProxyToken += HandleToken;
+
+ _protocol = new RyuLdnProtocol();
+ }
+
+ private void HandleToken(LdnHeader header, ExternalProxyToken token)
+ {
+ _lock.EnterWriteLock();
+
+ _waitingTokens.Add(token);
+
+ _lock.ExitWriteLock();
+
+ _tokenEvent.Set();
+ }
+
+ private void HandleStateChange(LdnHeader header, ExternalProxyConnectionState state)
+ {
+ if (!state.Connected)
+ {
+ _lock.EnterWriteLock();
+
+ _waitingTokens.RemoveAll(token => token.VirtualIp == state.IpAddress);
+
+ _players.RemoveAll(player =>
+ {
+ if (player.VirtualIpAddress == state.IpAddress)
+ {
+ player.DisconnectAndStop();
+
+ return true;
+ }
+
+ return false;
+ });
+
+ _lock.ExitWriteLock();
+ }
+ }
+
+ public void Configure(ProxyConfig config)
+ {
+ _broadcastAddress = config.ProxyIp | (~config.ProxySubnetMask);
+ }
+
+ public async Task NatPunch()
+ {
+ NatDiscoverer discoverer = new NatDiscoverer();
+ CancellationTokenSource cts = new CancellationTokenSource(1000);
+
+ NatDevice device;
+
+ try
+ {
+ device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts);
+ }
+ catch (NatDeviceNotFoundException)
+ {
+ return 0;
+ }
+
+ _publicPort = PublicPortBase;
+
+ for (int i = 0; i < PublicPortRange; i++)
+ {
+ try
+ {
+ _portMapping = new Mapping(Protocol.Tcp, PrivatePort, _publicPort, PortLeaseLength, "Ryujinx Local Multiplayer");
+
+ await device.CreatePortMapAsync(_portMapping);
+
+ break;
+ }
+ catch (MappingException)
+ {
+ _publicPort++;
+ }
+ catch (Exception)
+ {
+ return 0;
+ }
+
+ if (i == PublicPortRange - 1)
+ {
+ _publicPort = 0;
+ }
+ }
+
+ if (_publicPort != 0)
+ {
+ _ = Task.Delay(PortLeaseRenew * 1000, _disposedCancellation.Token).ContinueWith((task) => Task.Run(RefreshLease));
+ }
+
+ _natDevice = device;
+
+ return _publicPort;
+ }
+
+ // Proxy handlers
+
+ private void RouteMessage(P2pProxySession sender, ref ProxyInfo info, Action action)
+ {
+ if (info.SourceIpV4 == 0)
+ {
+ // If they sent from a connection bound on 0.0.0.0, make others see it as them.
+ info.SourceIpV4 = sender.VirtualIpAddress;
+ }
+ else if (info.SourceIpV4 != sender.VirtualIpAddress)
+ {
+ // Can't pretend to be somebody else.
+ return;
+ }
+
+ uint destIp = info.DestIpV4;
+
+ if (destIp == 0xc0a800ff)
+ {
+ destIp = _broadcastAddress;
+ }
+
+ bool isBroadcast = destIp == _broadcastAddress;
+
+ _lock.EnterReadLock();
+
+ if (isBroadcast)
+ {
+ _players.ForEach(player =>
+ {
+ action(player);
+ });
+ }
+ else
+ {
+ P2pProxySession target = _players.FirstOrDefault(player => player.VirtualIpAddress == destIp);
+
+ if (target != null)
+ {
+ action(target);
+ }
+ }
+
+ _lock.ExitReadLock();
+ }
+
+ public void HandleProxyDisconnect(P2pProxySession sender, LdnHeader header, ProxyDisconnectMessage message)
+ {
+ RouteMessage(sender, ref message.Info, (target) =>
+ {
+ target.SendAsync(sender.Protocol.Encode(PacketId.ProxyDisconnect, message));
+ });
+ }
+
+ public void HandleProxyData(P2pProxySession sender, LdnHeader header, ProxyDataHeader message, byte[] data)
+ {
+ RouteMessage(sender, ref message.Info, (target) =>
+ {
+ target.SendAsync(sender.Protocol.Encode(PacketId.ProxyData, message, data));
+ });
+ }
+
+ public void HandleProxyConnectReply(P2pProxySession sender, LdnHeader header, ProxyConnectResponse message)
+ {
+ RouteMessage(sender, ref message.Info, (target) =>
+ {
+ target.SendAsync(sender.Protocol.Encode(PacketId.ProxyConnectReply, message));
+ });
+ }
+
+ public void HandleProxyConnect(P2pProxySession sender, LdnHeader header, ProxyConnectRequest message)
+ {
+ RouteMessage(sender, ref message.Info, (target) =>
+ {
+ target.SendAsync(sender.Protocol.Encode(PacketId.ProxyConnect, message));
+ });
+ }
+
+ // End proxy handlers
+
+ private async Task RefreshLease()
+ {
+ if (_disposed || _natDevice == null)
+ {
+ return;
+ }
+
+ try
+ {
+ await _natDevice.CreatePortMapAsync(_portMapping);
+ }
+ catch (Exception)
+ {
+
+ }
+
+ _ = Task.Delay(PortLeaseRenew, _disposedCancellation.Token).ContinueWith((task) => Task.Run(RefreshLease));
+ }
+
+ public bool TryRegisterUser(P2pProxySession session, ExternalProxyConfig config)
+ {
+ _lock.EnterWriteLock();
+
+ // Attempt to find matching configuration. If we don't find one, wait for a bit and try again.
+ // Woken by new tokens coming in from the master server.
+
+ IPAddress address = (session.Socket.RemoteEndPoint as IPEndPoint).Address;
+ byte[] addressBytes = ProxyHelpers.AddressTo16Byte(address);
+
+ long time;
+ long endTime = Stopwatch.GetTimestamp() + Stopwatch.Frequency * AuthWaitSeconds;
+
+ do
+ {
+ for (int i = 0; i < _waitingTokens.Count; i++)
+ {
+ ExternalProxyToken waitToken = _waitingTokens[i];
+
+ // Allow any client that has a private IP to connect. (indicated by the server as all 0 in the token)
+
+ bool isPrivate = waitToken.PhysicalIp.AsSpan().SequenceEqual(new byte[16]);
+ bool ipEqual = isPrivate || waitToken.AddressFamily == address.AddressFamily && waitToken.PhysicalIp.AsSpan().SequenceEqual(addressBytes);
+
+ if (ipEqual && waitToken.Token.AsSpan().SequenceEqual(config.Token.AsSpan()))
+ {
+ // This is a match.
+
+ _waitingTokens.RemoveAt(i);
+
+ session.SetIpv4(waitToken.VirtualIp);
+
+ ProxyConfig pconfig = new ProxyConfig
+ {
+ ProxyIp = session.VirtualIpAddress,
+ ProxySubnetMask = 0xFFFF0000 // TODO: Use from server.
+ };
+
+ if (_players.Count == 0)
+ {
+ Configure(pconfig);
+ }
+
+ _players.Add(session);
+
+ session.SendAsync(_protocol.Encode(PacketId.ProxyConfig, pconfig));
+
+ _lock.ExitWriteLock();
+
+ return true;
+ }
+ }
+
+ // Couldn't find the token.
+ // It may not have arrived yet, so wait for one to arrive.
+
+ _lock.ExitWriteLock();
+
+ time = Stopwatch.GetTimestamp();
+ int remainingMs = (int)((endTime - time) / (Stopwatch.Frequency / 1000));
+
+ if (remainingMs < 0)
+ {
+ remainingMs = 0;
+ }
+
+ _tokenEvent.WaitOne(remainingMs);
+
+ _lock.EnterWriteLock();
+
+ } while (time < endTime);
+
+ _lock.ExitWriteLock();
+
+ return false;
+ }
+
+ public void DisconnectProxyClient(P2pProxySession session)
+ {
+ _lock.EnterWriteLock();
+
+ bool removed = _players.Remove(session);
+
+ if (removed)
+ {
+ _master.SendAsync(_masterProtocol.Encode(PacketId.ExternalProxyState, new ExternalProxyConnectionState
+ {
+ IpAddress = session.VirtualIpAddress,
+ Connected = false
+ }));
+ }
+
+ _lock.ExitWriteLock();
+ }
+
+ public new void Dispose()
+ {
+ base.Dispose();
+
+ _disposed = true;
+ _disposedCancellation.Cancel();
+
+ try
+ {
+ Task delete = _natDevice?.DeletePortMapAsync(new Mapping(Protocol.Tcp, PrivatePort, _publicPort, 60, "Ryujinx Local Multiplayer"));
+
+ // Just absorb any exceptions.
+ delete?.ContinueWith((task) => { });
+ }
+ catch (Exception)
+ {
+ // Fail silently.
+ }
+ }
+
+ protected override TcpSession CreateSession()
+ {
+ return new P2pProxySession(this);
+ }
+
+ protected override void OnError(SocketError error)
+ {
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP server caught an error with code {error}");
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxySession.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxySession.cs
new file mode 100644
index 000000000..515feeac5
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxySession.cs
@@ -0,0 +1,90 @@
+using NetCoreServer;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
+{
+ class P2pProxySession : TcpSession
+ {
+ public uint VirtualIpAddress { get; private set; }
+ public RyuLdnProtocol Protocol { get; }
+
+ private readonly P2pProxyServer _parent;
+
+ private bool _masterClosed;
+
+ public P2pProxySession(P2pProxyServer server) : base(server)
+ {
+ _parent = server;
+
+ Protocol = new RyuLdnProtocol();
+
+ Protocol.ProxyDisconnect += HandleProxyDisconnect;
+ Protocol.ProxyData += HandleProxyData;
+ Protocol.ProxyConnectReply += HandleProxyConnectReply;
+ Protocol.ProxyConnect += HandleProxyConnect;
+
+ Protocol.ExternalProxy += HandleAuthentication;
+ }
+
+ private void HandleAuthentication(LdnHeader header, ExternalProxyConfig token)
+ {
+ if (!_parent.TryRegisterUser(this, token))
+ {
+ Disconnect();
+ }
+ }
+
+ public void SetIpv4(uint ip)
+ {
+ VirtualIpAddress = ip;
+ }
+
+ public void DisconnectAndStop()
+ {
+ _masterClosed = true;
+
+ Disconnect();
+ }
+
+ protected override void OnDisconnected()
+ {
+ if (!_masterClosed)
+ {
+ _parent.DisconnectProxyClient(this);
+ }
+ }
+
+ protected override void OnReceived(byte[] buffer, long offset, long size)
+ {
+ try
+ {
+ Protocol.Read(buffer, (int)offset, (int)size);
+ }
+ catch (Exception)
+ {
+ Disconnect();
+ }
+ }
+
+ private void HandleProxyDisconnect(LdnHeader header, ProxyDisconnectMessage message)
+ {
+ _parent.HandleProxyDisconnect(this, header, message);
+ }
+
+ private void HandleProxyData(LdnHeader header, ProxyDataHeader message, byte[] data)
+ {
+ _parent.HandleProxyData(this, header, message, data);
+ }
+
+ private void HandleProxyConnectReply(LdnHeader header, ProxyConnectResponse data)
+ {
+ _parent.HandleProxyConnectReply(this, header, data);
+ }
+
+ private void HandleProxyConnect(LdnHeader header, ProxyConnectRequest message)
+ {
+ _parent.HandleProxyConnect(this, header, message);
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/ProxyHelpers.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/ProxyHelpers.cs
new file mode 100644
index 000000000..42b1ab6a2
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/ProxyHelpers.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Net;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
+{
+ static class ProxyHelpers
+ {
+ public static byte[] AddressTo16Byte(IPAddress address)
+ {
+ byte[] ipBytes = new byte[16];
+ byte[] srcBytes = address.GetAddressBytes();
+
+ Array.Copy(srcBytes, 0, ipBytes, 0, srcBytes.Length);
+
+ return ipBytes;
+ }
+
+ public static bool SupportsNoDelay()
+ {
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/RyuLdnProtocol.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/RyuLdnProtocol.cs
new file mode 100644
index 000000000..d0eeaf125
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/RyuLdnProtocol.cs
@@ -0,0 +1,380 @@
+using Ryujinx.Common.Utilities;
+using Ryujinx.HLE.HOS.Services.Ldn.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
+{
+ class RyuLdnProtocol
+ {
+ private const byte CurrentProtocolVersion = 1;
+ private const int Magic = ('R' << 0) | ('L' << 8) | ('D' << 16) | ('N' << 24);
+ private const int MaxPacketSize = 131072;
+
+ private readonly int _headerSize = Marshal.SizeOf();
+
+ private readonly byte[] _buffer = new byte[MaxPacketSize];
+ private int _bufferEnd = 0;
+
+ // Client Packets.
+ public event Action Initialize;
+ public event Action Passphrase;
+ public event Action Connected;
+ public event Action SyncNetwork;
+ public event Action ScanReply;
+ public event Action ScanReplyEnd;
+ public event Action Disconnected;
+
+ // External Proxy Packets.
+ public event Action ExternalProxy;
+ public event Action ExternalProxyState;
+ public event Action ExternalProxyToken;
+
+ // Server Packets.
+ public event Action CreateAccessPoint;
+ public event Action CreateAccessPointPrivate;
+ public event Action Reject;
+ public event Action RejectReply;
+ public event Action SetAcceptPolicy;
+ public event Action SetAdvertiseData;
+ public event Action Connect;
+ public event Action ConnectPrivate;
+ public event Action Scan;
+
+ // Proxy Packets.
+ public event Action ProxyConfig;
+ public event Action ProxyConnect;
+ public event Action ProxyConnectReply;
+ public event Action ProxyData;
+ public event Action ProxyDisconnect;
+
+ // Lifecycle Packets.
+ public event Action NetworkError;
+ public event Action Ping;
+
+ public RyuLdnProtocol() { }
+
+ public void Reset()
+ {
+ _bufferEnd = 0;
+ }
+
+ public void Read(byte[] data, int offset, int size)
+ {
+ int index = 0;
+
+ while (index < size)
+ {
+ if (_bufferEnd < _headerSize)
+ {
+ // Assemble the header first.
+
+ int copyable = Math.Min(size - index, Math.Min(size, _headerSize - _bufferEnd));
+
+ Array.Copy(data, index + offset, _buffer, _bufferEnd, copyable);
+
+ index += copyable;
+ _bufferEnd += copyable;
+ }
+
+ if (_bufferEnd >= _headerSize)
+ {
+ // The header is available. Make sure we received all the data (size specified in the header)
+
+ LdnHeader ldnHeader = MemoryMarshal.Cast(_buffer)[0];
+
+ if (ldnHeader.Magic != Magic)
+ {
+ throw new InvalidOperationException("Invalid magic number in received packet.");
+ }
+
+ if (ldnHeader.Version != CurrentProtocolVersion)
+ {
+ throw new InvalidOperationException($"Protocol version mismatch. Expected ${CurrentProtocolVersion}, was ${ldnHeader.Version}.");
+ }
+
+ int finalSize = _headerSize + ldnHeader.DataSize;
+
+ if (finalSize >= MaxPacketSize)
+ {
+ throw new InvalidOperationException($"Max packet size {MaxPacketSize} exceeded.");
+ }
+
+ int copyable = Math.Min(size - index, Math.Min(size, finalSize - _bufferEnd));
+
+ Array.Copy(data, index + offset, _buffer, _bufferEnd, copyable);
+
+ index += copyable;
+ _bufferEnd += copyable;
+
+ if (finalSize == _bufferEnd)
+ {
+ // The full packet has been retrieved. Send it to be decoded.
+
+ byte[] ldnData = new byte[ldnHeader.DataSize];
+
+ Array.Copy(_buffer, _headerSize, ldnData, 0, ldnData.Length);
+
+ DecodeAndHandle(ldnHeader, ldnData);
+
+ Reset();
+ }
+ }
+ }
+ }
+
+ private (T, byte[]) ParseWithData(byte[] data) where T : struct
+ {
+ T str = default;
+ int size = Marshal.SizeOf(str);
+
+ byte[] remainder = new byte[data.Length - size];
+
+ if (remainder.Length > 0)
+ {
+ Array.Copy(data, size, remainder, 0, remainder.Length);
+ }
+
+ return (MemoryMarshal.Read(data), remainder);
+ }
+
+ private void DecodeAndHandle(LdnHeader header, byte[] data)
+ {
+ switch ((PacketId)header.Type)
+ {
+ // Client Packets.
+ case PacketId.Initialize:
+ {
+ Initialize?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.Passphrase:
+ {
+ Passphrase?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.Connected:
+ {
+ Connected?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.SyncNetwork:
+ {
+ SyncNetwork?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.ScanReply:
+ {
+ ScanReply?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+
+ case PacketId.ScanReplyEnd:
+ {
+ ScanReplyEnd?.Invoke(header);
+
+ break;
+ }
+ case PacketId.Disconnect:
+ {
+ Disconnected?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+
+ // External Proxy Packets.
+ case PacketId.ExternalProxy:
+ {
+ ExternalProxy?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.ExternalProxyState:
+ {
+ ExternalProxyState?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.ExternalProxyToken:
+ {
+ ExternalProxyToken?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+
+ // Server Packets.
+ case PacketId.CreateAccessPoint:
+ {
+ (CreateAccessPointRequest packet, byte[] extraData) = ParseWithData(data);
+ CreateAccessPoint?.Invoke(header, packet, extraData);
+ break;
+ }
+ case PacketId.CreateAccessPointPrivate:
+ {
+ (CreateAccessPointPrivateRequest packet, byte[] extraData) = ParseWithData(data);
+ CreateAccessPointPrivate?.Invoke(header, packet, extraData);
+ break;
+ }
+ case PacketId.Reject:
+ {
+ Reject?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.RejectReply:
+ {
+ RejectReply?.Invoke(header);
+
+ break;
+ }
+ case PacketId.SetAcceptPolicy:
+ {
+ SetAcceptPolicy?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.SetAdvertiseData:
+ {
+ SetAdvertiseData?.Invoke(header, data);
+
+ break;
+ }
+ case PacketId.Connect:
+ {
+ Connect?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.ConnectPrivate:
+ {
+ ConnectPrivate?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.Scan:
+ {
+ Scan?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+
+ // Proxy Packets
+ case PacketId.ProxyConfig:
+ {
+ ProxyConfig?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.ProxyConnect:
+ {
+ ProxyConnect?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.ProxyConnectReply:
+ {
+ ProxyConnectReply?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.ProxyData:
+ {
+ (ProxyDataHeader packet, byte[] extraData) = ParseWithData(data);
+
+ ProxyData?.Invoke(header, packet, extraData);
+
+ break;
+ }
+ case PacketId.ProxyDisconnect:
+ {
+ ProxyDisconnect?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+
+ // Lifecycle Packets.
+ case PacketId.Ping:
+ {
+ Ping?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+ case PacketId.NetworkError:
+ {
+ NetworkError?.Invoke(header, MemoryMarshal.Read(data));
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ private static LdnHeader GetHeader(PacketId type, int dataSize)
+ {
+ return new LdnHeader()
+ {
+ Magic = Magic,
+ Version = CurrentProtocolVersion,
+ Type = (byte)type,
+ DataSize = dataSize
+ };
+ }
+
+ public byte[] Encode(PacketId type)
+ {
+ LdnHeader header = GetHeader(type, 0);
+
+ return SpanHelpers.AsSpan(ref header).ToArray();
+ }
+
+ public byte[] Encode(PacketId type, byte[] data)
+ {
+ LdnHeader header = GetHeader(type, data.Length);
+
+ byte[] result = SpanHelpers.AsSpan(ref header).ToArray();
+
+ Array.Resize(ref result, result.Length + data.Length);
+ Array.Copy(data, 0, result, Marshal.SizeOf(), data.Length);
+
+ return result;
+ }
+
+ public byte[] Encode(PacketId type, T packet) where T : unmanaged
+ {
+ byte[] packetData = SpanHelpers.AsSpan(ref packet).ToArray();
+
+ LdnHeader header = GetHeader(type, packetData.Length);
+
+ byte[] result = SpanHelpers.AsSpan(ref header).ToArray();
+
+ Array.Resize(ref result, result.Length + packetData.Length);
+ Array.Copy(packetData, 0, result, Marshal.SizeOf(), packetData.Length);
+
+ return result;
+ }
+
+ public byte[] Encode(PacketId type, T packet, byte[] data) where T : unmanaged
+ {
+ byte[] packetData = SpanHelpers.AsSpan(ref packet).ToArray();
+
+ LdnHeader header = GetHeader(type, packetData.Length + data.Length);
+
+ byte[] result = SpanHelpers.AsSpan(ref header).ToArray();
+
+ Array.Resize(ref result, result.Length + packetData.Length + data.Length);
+ Array.Copy(packetData, 0, result, Marshal.SizeOf(), packetData.Length);
+ Array.Copy(data, 0, result, Marshal.SizeOf() + packetData.Length, data.Length);
+
+ return result;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/DisconnectMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/DisconnectMessage.cs
new file mode 100644
index 000000000..448d33f29
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/DisconnectMessage.cs
@@ -0,0 +1,10 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x4)]
+ struct DisconnectMessage
+ {
+ public uint DisconnectIP;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConfig.cs
new file mode 100644
index 000000000..9cbb80242
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConfig.cs
@@ -0,0 +1,19 @@
+using Ryujinx.Common.Memory;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ ///
+ /// Sent by the server to point a client towards an external server being used as a proxy.
+ /// The client then forwards this to the external proxy after connecting, to verify the connection worked.
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 0x26, Pack = 1)]
+ struct ExternalProxyConfig
+ {
+ public Array16 ProxyIp;
+ public AddressFamily AddressFamily;
+ public ushort ProxyPort;
+ public Array16 Token;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConnectionState.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConnectionState.cs
new file mode 100644
index 000000000..ecf4e14f7
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConnectionState.cs
@@ -0,0 +1,18 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ ///
+ /// Indicates a change in connection state for the given client.
+ /// Is sent to notify the master server when connection is first established.
+ /// Can be sent by the external proxy to the master server to notify it of a proxy disconnect.
+ /// Can be sent by the master server to notify the external proxy of a user leaving a room.
+ /// Both will result in a force kick.
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 4)]
+ struct ExternalProxyConnectionState
+ {
+ public uint IpAddress;
+ public bool Connected;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyToken.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyToken.cs
new file mode 100644
index 000000000..0a8980c37
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyToken.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Common.Memory;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ ///
+ /// Sent by the master server to an external proxy to tell them someone is going to connect.
+ /// This drives authentication, and lets the proxy know what virtual IP to give to each joiner,
+ /// as these are managed by the master server.
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 0x28)]
+ struct ExternalProxyToken
+ {
+ public uint VirtualIp;
+ public Array16 Token;
+ public Array16 PhysicalIp;
+ public AddressFamily AddressFamily;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/InitializeMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/InitializeMessage.cs
new file mode 100644
index 000000000..36ddc65fe
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/InitializeMessage.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ ///
+ /// This message is first sent by the client to identify themselves.
+ /// If the server has a token+mac combo that matches the submission, then they are returned their new ID and mac address. (the mac is also reassigned to the new id)
+ /// Otherwise, they are returned a random mac address.
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 0x16)]
+ struct InitializeMessage
+ {
+ // All 0 if we don't have an ID yet.
+ public Array16 Id;
+
+ // All 0 if we don't have a mac yet.
+ public Array6 MacAddress;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/LdnHeader.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/LdnHeader.cs
new file mode 100644
index 000000000..f41f15ab4
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/LdnHeader.cs
@@ -0,0 +1,13 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0xA)]
+ struct LdnHeader
+ {
+ public uint Magic;
+ public byte Type;
+ public byte Version;
+ public int DataSize;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PacketId.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PacketId.cs
new file mode 100644
index 000000000..b8ef5fbc1
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PacketId.cs
@@ -0,0 +1,36 @@
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ enum PacketId
+ {
+ Initialize,
+ Passphrase,
+
+ CreateAccessPoint,
+ CreateAccessPointPrivate,
+ ExternalProxy,
+ ExternalProxyToken,
+ ExternalProxyState,
+ SyncNetwork,
+ Reject,
+ RejectReply,
+ Scan,
+ ScanReply,
+ ScanReplyEnd,
+ Connect,
+ ConnectPrivate,
+ Connected,
+ Disconnect,
+
+ ProxyConfig,
+ ProxyConnect,
+ ProxyConnectReply,
+ ProxyData,
+ ProxyDisconnect,
+
+ SetAcceptPolicy,
+ SetAdvertiseData,
+
+ Ping = 254,
+ NetworkError = 255
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PassphraseMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PassphraseMessage.cs
new file mode 100644
index 000000000..0deba0b07
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PassphraseMessage.cs
@@ -0,0 +1,11 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x80)]
+ struct PassphraseMessage
+ {
+ public Array128 Passphrase;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PingMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PingMessage.cs
new file mode 100644
index 000000000..135e39caa
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PingMessage.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x2)]
+ struct PingMessage
+ {
+ public byte Requester;
+ public byte Id;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectRequest.cs
new file mode 100644
index 000000000..ffce77791
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectRequest.cs
@@ -0,0 +1,10 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x10)]
+ struct ProxyConnectRequest
+ {
+ public ProxyInfo Info;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectResponse.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectResponse.cs
new file mode 100644
index 000000000..de2e430fb
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectResponse.cs
@@ -0,0 +1,10 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x10)]
+ struct ProxyConnectResponse
+ {
+ public ProxyInfo Info;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataHeader.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataHeader.cs
new file mode 100644
index 000000000..e46a40692
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataHeader.cs
@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ ///
+ /// Represents data sent over a transport layer.
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 0x14)]
+ struct ProxyDataHeader
+ {
+ public ProxyInfo Info;
+ public uint DataLength; // Followed by the data with the specified byte length.
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataPacket.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataPacket.cs
new file mode 100644
index 000000000..eb3648413
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataPacket.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ class ProxyDataPacket
+ {
+ public ProxyDataHeader Header;
+ public byte[] Data;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDisconnectMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDisconnectMessage.cs
new file mode 100644
index 000000000..2154ae109
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDisconnectMessage.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x14)]
+ struct ProxyDisconnectMessage
+ {
+ public ProxyInfo Info;
+ public int DisconnectReason;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyInfo.cs
new file mode 100644
index 000000000..d9338f244
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyInfo.cs
@@ -0,0 +1,20 @@
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ ///
+ /// Information included in all proxied communication.
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 1)]
+ struct ProxyInfo
+ {
+ public uint SourceIpV4;
+ public ushort SourcePort;
+
+ public uint DestIpV4;
+ public ushort DestPort;
+
+ public ProtocolType Protocol;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RejectRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RejectRequest.cs
new file mode 100644
index 000000000..1c2ce1f8b
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RejectRequest.cs
@@ -0,0 +1,18 @@
+using Ryujinx.HLE.HOS.Services.Ldn.Types;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x8)]
+ struct RejectRequest
+ {
+ public uint NodeId;
+ public DisconnectReason DisconnectReason;
+
+ public RejectRequest(DisconnectReason disconnectReason, uint nodeId)
+ {
+ DisconnectReason = disconnectReason;
+ NodeId = nodeId;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RyuNetworkConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RyuNetworkConfig.cs
new file mode 100644
index 000000000..f3bd72023
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RyuNetworkConfig.cs
@@ -0,0 +1,23 @@
+using Ryujinx.Common.Memory;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x28, Pack = 1)]
+ struct RyuNetworkConfig
+ {
+ public Array16 GameVersion;
+
+ // PrivateIp is included for external proxies for the case where a client attempts to join from
+ // their own LAN. UPnP forwarding can fail when connecting devices on the same network over the public IP,
+ // so if their public IP is identical, the internal address should be sent instead.
+
+ // The fields below are 0 if not hosting a p2p proxy.
+
+ public Array16 PrivateIp;
+ public AddressFamily AddressFamily;
+ public ushort ExternalProxyPort;
+ public ushort InternalProxyPort;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/SetAcceptPolicyRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/SetAcceptPolicyRequest.cs
new file mode 100644
index 000000000..c4a969901
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/SetAcceptPolicyRequest.cs
@@ -0,0 +1,11 @@
+using Ryujinx.HLE.HOS.Services.Ldn.Types;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x1, Pack = 1)]
+ struct SetAcceptPolicyRequest
+ {
+ public AcceptPolicy StationAcceptPolicy;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs
index e39c01978..fa43f789e 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs
@@ -14,6 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public bool Connected { get; private set; }
+ public ProxyConfig Config => _parent.NetworkClient.Config;
+
public Station(IUserLocalCommunicationService parent)
{
_parent = parent;
@@ -48,9 +50,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public void Dispose()
{
- _parent.NetworkClient.DisconnectNetwork();
+ if (_parent.NetworkClient != null)
+ {
+ _parent.NetworkClient.DisconnectNetwork();
- _parent.NetworkClient.NetworkChange -= NetworkChanged;
+ _parent.NetworkClient.NetworkChange -= NetworkChanged;
+ }
}
private ResultCode NetworkErrorToResult(NetworkError error)
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs
index ac0ff7d94..0972c21c0 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs
@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Ldn.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types
@@ -14,5 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types
public UserConfig UserConfig;
public NetworkConfig NetworkConfig;
public AddressList AddressList;
+
+ public RyuNetworkConfig RyuNetworkConfig;
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs
index f67f0aac9..d2dc5b698 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs
@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Ldn.Types;
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types
@@ -6,11 +7,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types
///
/// Advertise data is appended separately (remaining data in the buffer).
///
- [StructLayout(LayoutKind.Sequential, Size = 0x94, CharSet = CharSet.Ansi)]
+ [StructLayout(LayoutKind.Sequential, Size = 0xBC, Pack = 1)]
struct CreateAccessPointRequest
{
public SecurityConfig SecurityConfig;
public UserConfig UserConfig;
public NetworkConfig NetworkConfig;
+
+ public RyuNetworkConfig RyuNetworkConfig;
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/ProxyConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/ProxyConfig.cs
new file mode 100644
index 000000000..c89c08bbe
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/ProxyConfig.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x8)]
+ struct ProxyConfig
+ {
+ public uint ProxyIp;
+ public uint ProxySubnetMask;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs
index 65d380979..e1db98e5f 100644
--- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager
public uint FileVersion { get; set; }
public byte[] TagUuid { get; set; }
public string AmiiboId { get; set; }
+ public string NickName { get; set; }
public DateTime FirstWriteDate { get; set; }
public DateTime LastWriteDate { get; set; }
public ushort WriteCounter { get; set; }
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs
index ba4a81e0e..7ce749d1a 100644
--- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs
@@ -64,16 +64,17 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
};
}
- public static RegisterInfo GetRegisterInfo(ITickSource tickSource, string amiiboId, string nickname)
+ public static RegisterInfo GetRegisterInfo(ITickSource tickSource, string amiiboId, string userName)
{
VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId);
-
+ string nickname = amiiboFile.NickName ?? "Ryujinx";
UtilityImpl utilityImpl = new(tickSource);
CharInfo charInfo = new();
charInfo.SetFromStoreData(StoreData.BuildDefault(utilityImpl, 0));
- charInfo.Nickname = Nickname.FromString(nickname);
+ // This is the player's name
+ charInfo.Nickname = Nickname.FromString(userName);
RegisterInfo registerInfo = new()
{
@@ -85,7 +86,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
Reserved1 = new Array64(),
Reserved2 = new Array58(),
};
- "Ryujinx"u8.CopyTo(registerInfo.Nickname.AsSpan());
+ // This is the amiibo's name
+ byte[] nicknameBytes = System.Text.Encoding.UTF8.GetBytes(nickname);
+ nicknameBytes.CopyTo(registerInfo.Nickname.AsSpan());
return registerInfo;
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs
index f652ecda8..b076958eb 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
ulong bufferSize = context.Request.PtrBuff[0].Size;
bool isMatch = false;
- string text = "";
+ string text = string.Empty;
if (bufferSize != 0)
{
@@ -57,8 +57,8 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
ulong bufferFilteredPosition = context.Request.RecvListBuff[0].Position;
- string text = "";
- string textFiltered = "";
+ string text = string.Empty;
+ string textFiltered = string.Empty;
if (bufferSize != 0)
{
diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
index ff9a67644..0f5d7547c 100644
--- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
@@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner)
{
- _asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner));
+ _asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner, context.Device.Memory.Size));
_memoryAllocator = new NvMemoryAllocator();
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs
index b2d4d55cc..b0b854f74 100644
--- a/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs
@@ -73,1529 +73,1529 @@ namespace Ryujinx.HLE.HOS.Services.Settings
{ "ns.sdcard!compare_sdcard", 0 },
{ "ns.gamecard!mount_gamecard_result_value", 0 },
{ "ns.gamecard!try_gamecard_access_result_value", 0 },
- { "nv!00008600", "" },
- { "nv!0007b25e", "" },
- { "nv!0083e1", "" },
- { "nv!01621887", "" },
- { "nv!03134743", "" },
- { "nv!0356afd0", "" },
- { "nv!0356afd1", "" },
- { "nv!0356afd2", "" },
- { "nv!0356afd3", "" },
- { "nv!094313", "" },
- { "nv!0x04dc09", "" },
- { "nv!0x111133", "" },
- { "nv!0x1aa483", "" },
- { "nv!0x1cb1cf", "" },
- { "nv!0x1cb1d0", "" },
- { "nv!0x1e3221", "" },
- { "nv!0x300fc8", "" },
- { "nv!0x301fc8", "" },
- { "nv!0x302fc8", "" },
- { "nv!0x3eec59", "" },
- { "nv!0x46b3ed", "" },
- { "nv!0x523dc0", "" },
- { "nv!0x523dc1", "" },
- { "nv!0x523dc2", "" },
- { "nv!0x523dc3", "" },
- { "nv!0x523dc4", "" },
- { "nv!0x523dc5", "" },
- { "nv!0x523dc6", "" },
- { "nv!0x523dd0", "" },
- { "nv!0x523dd1", "" },
- { "nv!0x523dd3", "" },
- { "nv!0x5344bb", "" },
- { "nv!0x555237", "" },
- { "nv!0x58a234", "" },
- { "nv!0x7b4428", "" },
- { "nv!0x923dc0", "" },
- { "nv!0x923dc1", "" },
- { "nv!0x923dc2", "" },
- { "nv!0x923dc3", "" },
- { "nv!0x923dc4", "" },
- { "nv!0x923dd3", "" },
- { "nv!0x9abdc5", "" },
- { "nv!0x9abdc6", "" },
- { "nv!0xaaa36c", "" },
- { "nv!0xb09da0", "" },
- { "nv!0xb09da1", "" },
- { "nv!0xb09da2", "" },
- { "nv!0xb09da3", "" },
- { "nv!0xb09da4", "" },
- { "nv!0xb09da5", "" },
- { "nv!0xb0b348", "" },
- { "nv!0xb0b349", "" },
- { "nv!0xbb558f", "" },
- { "nv!0xbd10fb", "" },
- { "nv!0xc32ad3", "" },
- { "nv!0xce2348", "" },
- { "nv!0xcfd81f", "" },
- { "nv!0xe0036b", "" },
- { "nv!0xe01f2d", "" },
- { "nv!0xe17212", "" },
- { "nv!0xeae966", "" },
- { "nv!0xed4f82", "" },
- { "nv!0xf12335", "" },
- { "nv!0xf12336", "" },
- { "nv!10261989", "" },
- { "nv!1042d483", "" },
- { "nv!10572898", "" },
- { "nv!115631", "" },
- { "nv!12950094", "" },
- { "nv!1314f311", "" },
- { "nv!1314f312", "" },
- { "nv!13279512", "" },
- { "nv!13813496", "" },
- { "nv!14507179", "" },
- { "nv!15694569", "" },
- { "nv!16936964", "" },
- { "nv!17aa230c", "" },
- { "nv!182054", "" },
- { "nv!18273275", "" },
- { "nv!18273276", "" },
- { "nv!1854d03b", "" },
- { "nv!18add00d", "" },
- { "nv!19156670", "" },
- { "nv!19286545", "" },
- { "nv!1a298e9f", "" },
- { "nv!1acf43fe", "" },
- { "nv!1bda43fe", "" },
- { "nv!1c3b92", "" },
- { "nv!21509920", "" },
- { "nv!215323457", "" },
- { "nv!2165ad", "" },
- { "nv!2165ae", "" },
- { "nv!21be9c", "" },
- { "nv!233264316", "" },
- { "nv!234557580", "" },
- { "nv!23cd0e", "" },
- { "nv!24189123", "" },
- { "nv!2443266", "" },
- { "nv!25025519", "" },
- { "nv!255e39", "" },
- { "nv!2583364", "" },
- { "nv!2888c1", "" },
- { "nv!28ca3e", "" },
- { "nv!29871243", "" },
- { "nv!2a1f64", "" },
- { "nv!2dc432", "" },
- { "nv!2de437", "" },
- { "nv!2f3bb89c", "" },
- { "nv!2fd652", "" },
- { "nv!3001ac", "" },
- { "nv!31298772", "" },
- { "nv!313233", "" },
- { "nv!31f7d603", "" },
- { "nv!320ce4", "" },
- { "nv!32153248", "" },
- { "nv!32153249", "" },
- { "nv!335bca", "" },
- { "nv!342abb", "" },
- { "nv!34dfe6", "" },
- { "nv!34dfe7", "" },
- { "nv!34dfe8", "" },
- { "nv!34dfe9", "" },
- { "nv!35201578", "" },
- { "nv!359278", "" },
- { "nv!37f53a", "" },
- { "nv!38144972", "" },
- { "nv!38542646", "" },
- { "nv!3b74c9", "" },
- { "nv!3c136f", "" },
- { "nv!3cf72823", "" },
- { "nv!3d7af029", "" },
- { "nv!3ff34782", "" },
- { "nv!4129618", "" },
- { "nv!4189fac3", "" },
- { "nv!420bd4", "" },
- { "nv!42a699", "" },
- { "nv!441369", "" },
- { "nv!4458713e", "" },
- { "nv!4554b6", "" },
- { "nv!457425", "" },
- { "nv!4603b207", "" },
- { "nv!46574957", "" },
- { "nv!46574958", "" },
- { "nv!46813529", "" },
- { "nv!46f1e13d", "" },
- { "nv!47534c43", "" },
- { "nv!48550336", "" },
- { "nv!48576893", "" },
- { "nv!48576894", "" },
- { "nv!4889ac02", "" },
- { "nv!49005740", "" },
- { "nv!49867584", "" },
- { "nv!49960973", "" },
- { "nv!4a5341", "" },
- { "nv!4f4e48", "" },
- { "nv!4f8a0a", "" },
- { "nv!50299698", "" },
- { "nv!50299699", "" },
- { "nv!50361291", "" },
- { "nv!5242ae", "" },
- { "nv!53d30c", "" },
- { "nv!56347a", "" },
- { "nv!563a95f1", "" },
- { "nv!573823", "" },
- { "nv!58027529", "" },
- { "nv!5d2d63", "" },
- { "nv!5f7e3b", "" },
- { "nv!60461793", "" },
- { "nv!60d355", "" },
- { "nv!616627aa", "" },
- { "nv!62317182", "" },
- { "nv!6253fa2e", "" },
- { "nv!64100768", "" },
- { "nv!64100769", "" },
- { "nv!64100770", "" },
- { "nv!647395", "" },
- { "nv!66543234", "" },
- { "nv!67674763", "" },
- { "nv!67739784", "" },
- { "nv!68fb9c", "" },
- { "nv!69801276", "" },
- { "nv!6af9fa2f", "" },
- { "nv!6af9fa3f", "" },
- { "nv!6af9fa4f", "" },
- { "nv!6bd8c7", "" },
- { "nv!6c7691", "" },
- { "nv!6d4296ce", "" },
- { "nv!6dd7e7", "" },
- { "nv!6dd7e8", "" },
- { "nv!6fe11ec1", "" },
- { "nv!716511763", "" },
- { "nv!72504593", "" },
- { "nv!73304097", "" },
- { "nv!73314098", "" },
- { "nv!74095213", "" },
- { "nv!74095213a", "" },
- { "nv!74095213b", "" },
- { "nv!74095214", "" },
- { "nv!748f9649", "" },
- { "nv!75494732", "" },
- { "nv!78452832", "" },
- { "nv!784561", "" },
- { "nv!78e16b9c", "" },
- { "nv!79251225", "" },
- { "nv!7c128b", "" },
- { "nv!7ccd93", "" },
- { "nv!7df8d1", "" },
- { "nv!800c2310", "" },
- { "nv!80546710", "" },
- { "nv!80772310", "" },
- { "nv!808ee280", "" },
- { "nv!81131154", "" },
- { "nv!81274457", "" },
- { "nv!8292291f", "" },
- { "nv!83498426", "" },
- { "nv!84993794", "" },
- { "nv!84995585", "" },
- { "nv!84a0a0", "" },
- { "nv!852142", "" },
- { "nv!85612309", "" },
- { "nv!85612310", "" },
- { "nv!85612311", "" },
- { "nv!85612312", "" },
- { "nv!8623ff27", "" },
- { "nv!87364952", "" },
- { "nv!87f6275666", "" },
- { "nv!886748", "" },
- { "nv!89894423", "" },
- { "nv!8ad8a75", "" },
- { "nv!8ad8ad00", "" },
- { "nv!8bb815", "" },
- { "nv!8bb817", "" },
- { "nv!8bb818", "" },
- { "nv!8bb819", "" },
- { "nv!8e640cd1", "" },
- { "nv!8f34971a", "" },
- { "nv!8f773984", "" },
- { "nv!8f7a7d", "" },
- { "nv!902486209", "" },
- { "nv!90482571", "" },
- { "nv!91214835", "" },
- { "nv!912848290", "" },
- { "nv!915e56", "" },
- { "nv!92179063", "" },
- { "nv!92179064", "" },
- { "nv!92179065", "" },
- { "nv!92179066", "" },
- { "nv!92350358", "" },
- { "nv!92809063", "" },
- { "nv!92809064", "" },
- { "nv!92809065", "" },
- { "nv!92809066", "" },
- { "nv!92920143", "" },
- { "nv!93a89b12", "" },
- { "nv!93a89c0b", "" },
- { "nv!94812574", "" },
- { "nv!95282304", "" },
- { "nv!95394027", "" },
- { "nv!959b1f", "" },
- { "nv!9638af", "" },
- { "nv!96fd59", "" },
- { "nv!97f6275666", "" },
- { "nv!97f6275667", "" },
- { "nv!97f6275668", "" },
- { "nv!97f6275669", "" },
- { "nv!97f627566a", "" },
- { "nv!97f627566b", "" },
- { "nv!97f627566d", "" },
- { "nv!97f627566e", "" },
- { "nv!97f627566f", "" },
- { "nv!97f6275670", "" },
- { "nv!97f6275671", "" },
- { "nv!97f727566e", "" },
- { "nv!98480775", "" },
- { "nv!98480776", "" },
- { "nv!98480777", "" },
- { "nv!992431", "" },
- { "nv!9aa29065", "" },
- { "nv!9af32c", "" },
- { "nv!9af32d", "" },
- { "nv!9af32e", "" },
- { "nv!9c108b71", "" },
- { "nv!9f279065", "" },
- { "nv!a01bc728", "" },
- { "nv!a13b46c80", "" },
- { "nv!a22eb0", "" },
- { "nv!a2fb451e", "" },
- { "nv!a3456abe", "" },
- { "nv!a7044887", "" },
- { "nv!a7149200", "" },
- { "nv!a766215670", "" },
- { "nv!aac_drc_boost", "" },
- { "nv!aac_drc_cut", "" },
- { "nv!aac_drc_enc_target_level", "" },
- { "nv!aac_drc_heavy", "" },
- { "nv!aac_drc_reference_level", "" },
- { "nv!aalinegamma", "" },
- { "nv!aalinetweaks", "" },
- { "nv!ab34ee01", "" },
- { "nv!ab34ee02", "" },
- { "nv!ab34ee03", "" },
- { "nv!ac0274", "" },
- { "nv!af73c63e", "" },
- { "nv!af73c63f", "" },
- { "nv!af9927", "" },
- { "nv!afoverride", "" },
- { "nv!allocdeviceevents", "" },
- { "nv!applicationkey", "" },
- { "nv!appreturnonlybasicglsltype", "" },
- { "nv!app_softimage", "" },
- { "nv!app_supportbits2", "" },
- { "nv!assumetextureismipmappedatcreation", "" },
- { "nv!b1fb0f01", "" },
- { "nv!b3edd5", "" },
- { "nv!b40d9e03d", "" },
- { "nv!b7f6275666", "" },
- { "nv!b812c1", "" },
- { "nv!ba14ba1a", "" },
- { "nv!ba14ba1b", "" },
- { "nv!bd7559", "" },
- { "nv!bd755a", "" },
- { "nv!bd755c", "" },
- { "nv!bd755d", "" },
- { "nv!be58bb", "" },
- { "nv!be92cb", "" },
- { "nv!beefcba3", "" },
- { "nv!beefcba4", "" },
- { "nv!c023777f", "" },
- { "nv!c09dc8", "" },
- { "nv!c0d340", "" },
- { "nv!c2ff374c", "" },
- { "nv!c5e9d7a3", "" },
- { "nv!c5e9d7a4", "" },
- { "nv!c5e9d7b4", "" },
- { "nv!c618f9", "" },
- { "nv!ca345840", "" },
- { "nv!cachedisable", "" },
- { "nv!cast.on", "" },
- { "nv!cde", "" },
- { "nv!channelpriorityoverride", "" },
- { "nv!cleardatastorevidmem", "" },
- { "nv!cmdbufmemoryspaceenables", "" },
- { "nv!cmdbufminwords", "" },
- { "nv!cmdbufsizewords", "" },
- { "nv!conformantblitframebufferscissor", "" },
- { "nv!conformantincompletetextures", "" },
- { "nv!copybuffermethod", "" },
- { "nv!cubemapaniso", "" },
- { "nv!cubemapfiltering", "" },
- { "nv!d0e9a4d7", "" },
- { "nv!d13733f12", "" },
- { "nv!d1b399", "" },
- { "nv!d2983c32", "" },
- { "nv!d2983c33", "" },
- { "nv!d2e71b", "" },
- { "nv!d377dc", "" },
- { "nv!d377dd", "" },
- { "nv!d489f4", "" },
- { "nv!d4bce1", "" },
- { "nv!d518cb", "" },
- { "nv!d518cd", "" },
- { "nv!d518ce", "" },
- { "nv!d518d0", "" },
- { "nv!d518d1", "" },
- { "nv!d518d2", "" },
- { "nv!d518d3", "" },
- { "nv!d518d4", "" },
- { "nv!d518d5", "" },
- { "nv!d59eda", "" },
- { "nv!d83cbd", "" },
- { "nv!d8e777", "" },
- { "nv!debug_level", "" },
- { "nv!debug_mask", "" },
- { "nv!debug_options", "" },
- { "nv!devshmpageableallocations", "" },
- { "nv!df1f9812", "" },
- { "nv!df783c", "" },
- { "nv!diagenable", "" },
- { "nv!disallowcemask", "" },
- { "nv!disallowz16", "" },
- { "nv!dlmemoryspaceenables", "" },
- { "nv!e0bfec", "" },
- { "nv!e433456d", "" },
- { "nv!e435563f", "" },
- { "nv!e4cd9c", "" },
- { "nv!e5c972", "" },
- { "nv!e639ef", "" },
- { "nv!e802af", "" },
- { "nv!eae964", "" },
- { "nv!earlytexturehwallocation", "" },
- { "nv!eb92a3", "" },
- { "nv!ebca56", "" },
- { "nv!enable-noaud", "" },
- { "nv!enable-noavs", "" },
- { "nv!enable-prof", "" },
- { "nv!enable-sxesmode", "" },
- { "nv!enable-ulld", "" },
- { "nv!expert_detail_level", "" },
- { "nv!expert_output_mask", "" },
- { "nv!expert_report_mask", "" },
- { "nv!extensionstringnvarch", "" },
- { "nv!extensionstringversion", "" },
- { "nv!f00f1938", "" },
- { "nv!f10736", "" },
- { "nv!f1846870", "" },
- { "nv!f33bc370", "" },
- { "nv!f392a874", "" },
- { "nv!f49ae8", "" },
- { "nv!fa345cce", "" },
- { "nv!fa35cc4", "" },
- { "nv!faa14a", "" },
- { "nv!faf8a723", "" },
- { "nv!fastgs", "" },
- { "nv!fbf4ac45", "" },
- { "nv!fbo_blit_ignore_srgb", "" },
- { "nv!fc64c7", "" },
- { "nv!ff54ec97", "" },
- { "nv!ff54ec98", "" },
- { "nv!forceexitprocessdetach", "" },
- { "nv!forcerequestedesversion", "" },
- { "nv!__gl_", "" },
- { "nv!__gl_00008600", "" },
- { "nv!__gl_0007b25e", "" },
- { "nv!__gl_0083e1", "" },
- { "nv!__gl_01621887", "" },
- { "nv!__gl_03134743", "" },
- { "nv!__gl_0356afd0", "" },
- { "nv!__gl_0356afd1", "" },
- { "nv!__gl_0356afd2", "" },
- { "nv!__gl_0356afd3", "" },
- { "nv!__gl_094313", "" },
- { "nv!__gl_0x04dc09", "" },
- { "nv!__gl_0x111133", "" },
- { "nv!__gl_0x1aa483", "" },
- { "nv!__gl_0x1cb1cf", "" },
- { "nv!__gl_0x1cb1d0", "" },
- { "nv!__gl_0x1e3221", "" },
- { "nv!__gl_0x300fc8", "" },
- { "nv!__gl_0x301fc8", "" },
- { "nv!__gl_0x302fc8", "" },
- { "nv!__gl_0x3eec59", "" },
- { "nv!__gl_0x46b3ed", "" },
- { "nv!__gl_0x523dc0", "" },
- { "nv!__gl_0x523dc1", "" },
- { "nv!__gl_0x523dc2", "" },
- { "nv!__gl_0x523dc3", "" },
- { "nv!__gl_0x523dc4", "" },
- { "nv!__gl_0x523dc5", "" },
- { "nv!__gl_0x523dc6", "" },
- { "nv!__gl_0x523dd0", "" },
- { "nv!__gl_0x523dd1", "" },
- { "nv!__gl_0x523dd3", "" },
- { "nv!__gl_0x5344bb", "" },
- { "nv!__gl_0x555237", "" },
- { "nv!__gl_0x58a234", "" },
- { "nv!__gl_0x7b4428", "" },
- { "nv!__gl_0x923dc0", "" },
- { "nv!__gl_0x923dc1", "" },
- { "nv!__gl_0x923dc2", "" },
- { "nv!__gl_0x923dc3", "" },
- { "nv!__gl_0x923dc4", "" },
- { "nv!__gl_0x923dd3", "" },
- { "nv!__gl_0x9abdc5", "" },
- { "nv!__gl_0x9abdc6", "" },
- { "nv!__gl_0xaaa36c", "" },
- { "nv!__gl_0xb09da0", "" },
- { "nv!__gl_0xb09da1", "" },
- { "nv!__gl_0xb09da2", "" },
- { "nv!__gl_0xb09da3", "" },
- { "nv!__gl_0xb09da4", "" },
- { "nv!__gl_0xb09da5", "" },
- { "nv!__gl_0xb0b348", "" },
- { "nv!__gl_0xb0b349", "" },
- { "nv!__gl_0xbb558f", "" },
- { "nv!__gl_0xbd10fb", "" },
- { "nv!__gl_0xc32ad3", "" },
- { "nv!__gl_0xce2348", "" },
- { "nv!__gl_0xcfd81f", "" },
- { "nv!__gl_0xe0036b", "" },
- { "nv!__gl_0xe01f2d", "" },
- { "nv!__gl_0xe17212", "" },
- { "nv!__gl_0xeae966", "" },
- { "nv!__gl_0xed4f82", "" },
- { "nv!__gl_0xf12335", "" },
- { "nv!__gl_0xf12336", "" },
- { "nv!__gl_10261989", "" },
- { "nv!__gl_1042d483", "" },
- { "nv!__gl_10572898", "" },
- { "nv!__gl_115631", "" },
- { "nv!__gl_12950094", "" },
- { "nv!__gl_1314f311", "" },
- { "nv!__gl_1314f312", "" },
- { "nv!__gl_13279512", "" },
- { "nv!__gl_13813496", "" },
- { "nv!__gl_14507179", "" },
- { "nv!__gl_15694569", "" },
- { "nv!__gl_16936964", "" },
- { "nv!__gl_17aa230c", "" },
- { "nv!__gl_182054", "" },
- { "nv!__gl_18273275", "" },
- { "nv!__gl_18273276", "" },
- { "nv!__gl_1854d03b", "" },
- { "nv!__gl_18add00d", "" },
- { "nv!__gl_19156670", "" },
- { "nv!__gl_19286545", "" },
- { "nv!__gl_1a298e9f", "" },
- { "nv!__gl_1acf43fe", "" },
- { "nv!__gl_1bda43fe", "" },
- { "nv!__gl_1c3b92", "" },
- { "nv!__gl_21509920", "" },
- { "nv!__gl_215323457", "" },
- { "nv!__gl_2165ad", "" },
- { "nv!__gl_2165ae", "" },
- { "nv!__gl_21be9c", "" },
- { "nv!__gl_233264316", "" },
- { "nv!__gl_234557580", "" },
- { "nv!__gl_23cd0e", "" },
- { "nv!__gl_24189123", "" },
- { "nv!__gl_2443266", "" },
- { "nv!__gl_25025519", "" },
- { "nv!__gl_255e39", "" },
- { "nv!__gl_2583364", "" },
- { "nv!__gl_2888c1", "" },
- { "nv!__gl_28ca3e", "" },
- { "nv!__gl_29871243", "" },
- { "nv!__gl_2a1f64", "" },
- { "nv!__gl_2dc432", "" },
- { "nv!__gl_2de437", "" },
- { "nv!__gl_2f3bb89c", "" },
- { "nv!__gl_2fd652", "" },
- { "nv!__gl_3001ac", "" },
- { "nv!__gl_31298772", "" },
- { "nv!__gl_313233", "" },
- { "nv!__gl_31f7d603", "" },
- { "nv!__gl_320ce4", "" },
- { "nv!__gl_32153248", "" },
- { "nv!__gl_32153249", "" },
- { "nv!__gl_335bca", "" },
- { "nv!__gl_342abb", "" },
- { "nv!__gl_34dfe6", "" },
- { "nv!__gl_34dfe7", "" },
- { "nv!__gl_34dfe8", "" },
- { "nv!__gl_34dfe9", "" },
- { "nv!__gl_35201578", "" },
- { "nv!__gl_359278", "" },
- { "nv!__gl_37f53a", "" },
- { "nv!__gl_38144972", "" },
- { "nv!__gl_38542646", "" },
- { "nv!__gl_3b74c9", "" },
- { "nv!__gl_3c136f", "" },
- { "nv!__gl_3cf72823", "" },
- { "nv!__gl_3d7af029", "" },
- { "nv!__gl_3ff34782", "" },
- { "nv!__gl_4129618", "" },
- { "nv!__gl_4189fac3", "" },
- { "nv!__gl_420bd4", "" },
- { "nv!__gl_42a699", "" },
- { "nv!__gl_441369", "" },
- { "nv!__gl_4458713e", "" },
- { "nv!__gl_4554b6", "" },
- { "nv!__gl_457425", "" },
- { "nv!__gl_4603b207", "" },
- { "nv!__gl_46574957", "" },
- { "nv!__gl_46574958", "" },
- { "nv!__gl_46813529", "" },
- { "nv!__gl_46f1e13d", "" },
- { "nv!__gl_47534c43", "" },
- { "nv!__gl_48550336", "" },
- { "nv!__gl_48576893", "" },
- { "nv!__gl_48576894", "" },
- { "nv!__gl_4889ac02", "" },
- { "nv!__gl_49005740", "" },
- { "nv!__gl_49867584", "" },
- { "nv!__gl_49960973", "" },
- { "nv!__gl_4a5341", "" },
- { "nv!__gl_4f4e48", "" },
- { "nv!__gl_4f8a0a", "" },
- { "nv!__gl_50299698", "" },
- { "nv!__gl_50299699", "" },
- { "nv!__gl_50361291", "" },
- { "nv!__gl_5242ae", "" },
- { "nv!__gl_53d30c", "" },
- { "nv!__gl_56347a", "" },
- { "nv!__gl_563a95f1", "" },
- { "nv!__gl_573823", "" },
- { "nv!__gl_58027529", "" },
- { "nv!__gl_5d2d63", "" },
- { "nv!__gl_5f7e3b", "" },
- { "nv!__gl_60461793", "" },
- { "nv!__gl_60d355", "" },
- { "nv!__gl_616627aa", "" },
- { "nv!__gl_62317182", "" },
- { "nv!__gl_6253fa2e", "" },
- { "nv!__gl_64100768", "" },
- { "nv!__gl_64100769", "" },
- { "nv!__gl_64100770", "" },
- { "nv!__gl_647395", "" },
- { "nv!__gl_66543234", "" },
- { "nv!__gl_67674763", "" },
- { "nv!__gl_67739784", "" },
- { "nv!__gl_68fb9c", "" },
- { "nv!__gl_69801276", "" },
- { "nv!__gl_6af9fa2f", "" },
- { "nv!__gl_6af9fa3f", "" },
- { "nv!__gl_6af9fa4f", "" },
- { "nv!__gl_6bd8c7", "" },
- { "nv!__gl_6c7691", "" },
- { "nv!__gl_6d4296ce", "" },
- { "nv!__gl_6dd7e7", "" },
- { "nv!__gl_6dd7e8", "" },
- { "nv!__gl_6fe11ec1", "" },
- { "nv!__gl_716511763", "" },
- { "nv!__gl_72504593", "" },
- { "nv!__gl_73304097", "" },
- { "nv!__gl_73314098", "" },
- { "nv!__gl_74095213", "" },
- { "nv!__gl_74095213a", "" },
- { "nv!__gl_74095213b", "" },
- { "nv!__gl_74095214", "" },
- { "nv!__gl_748f9649", "" },
- { "nv!__gl_75494732", "" },
- { "nv!__gl_78452832", "" },
- { "nv!__gl_784561", "" },
- { "nv!__gl_78e16b9c", "" },
- { "nv!__gl_79251225", "" },
- { "nv!__gl_7c128b", "" },
- { "nv!__gl_7ccd93", "" },
- { "nv!__gl_7df8d1", "" },
- { "nv!__gl_800c2310", "" },
- { "nv!__gl_80546710", "" },
- { "nv!__gl_80772310", "" },
- { "nv!__gl_808ee280", "" },
- { "nv!__gl_81131154", "" },
- { "nv!__gl_81274457", "" },
- { "nv!__gl_8292291f", "" },
- { "nv!__gl_83498426", "" },
- { "nv!__gl_84993794", "" },
- { "nv!__gl_84995585", "" },
- { "nv!__gl_84a0a0", "" },
- { "nv!__gl_852142", "" },
- { "nv!__gl_85612309", "" },
- { "nv!__gl_85612310", "" },
- { "nv!__gl_85612311", "" },
- { "nv!__gl_85612312", "" },
- { "nv!__gl_8623ff27", "" },
- { "nv!__gl_87364952", "" },
- { "nv!__gl_87f6275666", "" },
- { "nv!__gl_886748", "" },
- { "nv!__gl_89894423", "" },
- { "nv!__gl_8ad8a75", "" },
- { "nv!__gl_8ad8ad00", "" },
- { "nv!__gl_8bb815", "" },
- { "nv!__gl_8bb817", "" },
- { "nv!__gl_8bb818", "" },
- { "nv!__gl_8bb819", "" },
- { "nv!__gl_8e640cd1", "" },
- { "nv!__gl_8f34971a", "" },
- { "nv!__gl_8f773984", "" },
- { "nv!__gl_8f7a7d", "" },
- { "nv!__gl_902486209", "" },
- { "nv!__gl_90482571", "" },
- { "nv!__gl_91214835", "" },
- { "nv!__gl_912848290", "" },
- { "nv!__gl_915e56", "" },
- { "nv!__gl_92179063", "" },
- { "nv!__gl_92179064", "" },
- { "nv!__gl_92179065", "" },
- { "nv!__gl_92179066", "" },
- { "nv!__gl_92350358", "" },
- { "nv!__gl_92809063", "" },
- { "nv!__gl_92809064", "" },
- { "nv!__gl_92809065", "" },
- { "nv!__gl_92809066", "" },
- { "nv!__gl_92920143", "" },
- { "nv!__gl_93a89b12", "" },
- { "nv!__gl_93a89c0b", "" },
- { "nv!__gl_94812574", "" },
- { "nv!__gl_95282304", "" },
- { "nv!__gl_95394027", "" },
- { "nv!__gl_959b1f", "" },
- { "nv!__gl_9638af", "" },
- { "nv!__gl_96fd59", "" },
- { "nv!__gl_97f6275666", "" },
- { "nv!__gl_97f6275667", "" },
- { "nv!__gl_97f6275668", "" },
- { "nv!__gl_97f6275669", "" },
- { "nv!__gl_97f627566a", "" },
- { "nv!__gl_97f627566b", "" },
- { "nv!__gl_97f627566d", "" },
- { "nv!__gl_97f627566e", "" },
- { "nv!__gl_97f627566f", "" },
- { "nv!__gl_97f6275670", "" },
- { "nv!__gl_97f6275671", "" },
- { "nv!__gl_97f727566e", "" },
- { "nv!__gl_98480775", "" },
- { "nv!__gl_98480776", "" },
- { "nv!__gl_98480777", "" },
- { "nv!__gl_992431", "" },
- { "nv!__gl_9aa29065", "" },
- { "nv!__gl_9af32c", "" },
- { "nv!__gl_9af32d", "" },
- { "nv!__gl_9af32e", "" },
- { "nv!__gl_9c108b71", "" },
- { "nv!__gl_9f279065", "" },
- { "nv!__gl_a01bc728", "" },
- { "nv!__gl_a13b46c80", "" },
- { "nv!__gl_a22eb0", "" },
- { "nv!__gl_a2fb451e", "" },
- { "nv!__gl_a3456abe", "" },
- { "nv!__gl_a7044887", "" },
- { "nv!__gl_a7149200", "" },
- { "nv!__gl_a766215670", "" },
- { "nv!__gl_aalinegamma", "" },
- { "nv!__gl_aalinetweaks", "" },
- { "nv!__gl_ab34ee01", "" },
- { "nv!__gl_ab34ee02", "" },
- { "nv!__gl_ab34ee03", "" },
- { "nv!__gl_ac0274", "" },
- { "nv!__gl_af73c63e", "" },
- { "nv!__gl_af73c63f", "" },
- { "nv!__gl_af9927", "" },
- { "nv!__gl_afoverride", "" },
- { "nv!__gl_allocdeviceevents", "" },
- { "nv!__gl_applicationkey", "" },
- { "nv!__gl_appreturnonlybasicglsltype", "" },
- { "nv!__gl_app_softimage", "" },
- { "nv!__gl_app_supportbits2", "" },
- { "nv!__gl_assumetextureismipmappedatcreation", "" },
- { "nv!__gl_b1fb0f01", "" },
- { "nv!__gl_b3edd5", "" },
- { "nv!__gl_b40d9e03d", "" },
- { "nv!__gl_b7f6275666", "" },
- { "nv!__gl_b812c1", "" },
- { "nv!__gl_ba14ba1a", "" },
- { "nv!__gl_ba14ba1b", "" },
- { "nv!__gl_bd7559", "" },
- { "nv!__gl_bd755a", "" },
- { "nv!__gl_bd755c", "" },
- { "nv!__gl_bd755d", "" },
- { "nv!__gl_be58bb", "" },
- { "nv!__gl_be92cb", "" },
- { "nv!__gl_beefcba3", "" },
- { "nv!__gl_beefcba4", "" },
- { "nv!__gl_c023777f", "" },
- { "nv!__gl_c09dc8", "" },
- { "nv!__gl_c0d340", "" },
- { "nv!__gl_c2ff374c", "" },
- { "nv!__gl_c5e9d7a3", "" },
- { "nv!__gl_c5e9d7a4", "" },
- { "nv!__gl_c5e9d7b4", "" },
- { "nv!__gl_c618f9", "" },
- { "nv!__gl_ca345840", "" },
- { "nv!__gl_cachedisable", "" },
- { "nv!__gl_channelpriorityoverride", "" },
- { "nv!__gl_cleardatastorevidmem", "" },
- { "nv!__gl_cmdbufmemoryspaceenables", "" },
- { "nv!__gl_cmdbufminwords", "" },
- { "nv!__gl_cmdbufsizewords", "" },
- { "nv!__gl_conformantblitframebufferscissor", "" },
- { "nv!__gl_conformantincompletetextures", "" },
- { "nv!__gl_copybuffermethod", "" },
- { "nv!__gl_cubemapaniso", "" },
- { "nv!__gl_cubemapfiltering", "" },
- { "nv!__gl_d0e9a4d7", "" },
- { "nv!__gl_d13733f12", "" },
- { "nv!__gl_d1b399", "" },
- { "nv!__gl_d2983c32", "" },
- { "nv!__gl_d2983c33", "" },
- { "nv!__gl_d2e71b", "" },
- { "nv!__gl_d377dc", "" },
- { "nv!__gl_d377dd", "" },
- { "nv!__gl_d489f4", "" },
- { "nv!__gl_d4bce1", "" },
- { "nv!__gl_d518cb", "" },
- { "nv!__gl_d518cd", "" },
- { "nv!__gl_d518ce", "" },
- { "nv!__gl_d518d0", "" },
- { "nv!__gl_d518d1", "" },
- { "nv!__gl_d518d2", "" },
- { "nv!__gl_d518d3", "" },
- { "nv!__gl_d518d4", "" },
- { "nv!__gl_d518d5", "" },
- { "nv!__gl_d59eda", "" },
- { "nv!__gl_d83cbd", "" },
- { "nv!__gl_d8e777", "" },
- { "nv!__gl_debug_level", "" },
- { "nv!__gl_debug_mask", "" },
- { "nv!__gl_debug_options", "" },
- { "nv!__gl_devshmpageableallocations", "" },
- { "nv!__gl_df1f9812", "" },
- { "nv!__gl_df783c", "" },
- { "nv!__gl_diagenable", "" },
- { "nv!__gl_disallowcemask", "" },
- { "nv!__gl_disallowz16", "" },
- { "nv!__gl_dlmemoryspaceenables", "" },
- { "nv!__gl_e0bfec", "" },
- { "nv!__gl_e433456d", "" },
- { "nv!__gl_e435563f", "" },
- { "nv!__gl_e4cd9c", "" },
- { "nv!__gl_e5c972", "" },
- { "nv!__gl_e639ef", "" },
- { "nv!__gl_e802af", "" },
- { "nv!__gl_eae964", "" },
- { "nv!__gl_earlytexturehwallocation", "" },
- { "nv!__gl_eb92a3", "" },
- { "nv!__gl_ebca56", "" },
- { "nv!__gl_expert_detail_level", "" },
- { "nv!__gl_expert_output_mask", "" },
- { "nv!__gl_expert_report_mask", "" },
- { "nv!__gl_extensionstringnvarch", "" },
- { "nv!__gl_extensionstringversion", "" },
- { "nv!__gl_f00f1938", "" },
- { "nv!__gl_f10736", "" },
- { "nv!__gl_f1846870", "" },
- { "nv!__gl_f33bc370", "" },
- { "nv!__gl_f392a874", "" },
- { "nv!__gl_f49ae8", "" },
- { "nv!__gl_fa345cce", "" },
- { "nv!__gl_fa35cc4", "" },
- { "nv!__gl_faa14a", "" },
- { "nv!__gl_faf8a723", "" },
- { "nv!__gl_fastgs", "" },
- { "nv!__gl_fbf4ac45", "" },
- { "nv!__gl_fbo_blit_ignore_srgb", "" },
- { "nv!__gl_fc64c7", "" },
- { "nv!__gl_ff54ec97", "" },
- { "nv!__gl_ff54ec98", "" },
- { "nv!__gl_forceexitprocessdetach", "" },
- { "nv!__gl_forcerequestedesversion", "" },
- { "nv!__gl_glsynctovblank", "" },
- { "nv!__gl_gvitimeoutcontrol", "" },
- { "nv!__gl_hcctrl", "" },
- { "nv!__gl_hwstate_per_ctx", "" },
- { "nv!__gl_machinecachelimit", "" },
- { "nv!__gl_maxframesallowed", "" },
- { "nv!__gl_memmgrcachedalloclimit", "" },
- { "nv!__gl_memmgrcachedalloclimitratio", "" },
- { "nv!__gl_memmgrsysheapalloclimit", "" },
- { "nv!__gl_memmgrsysheapalloclimitratio", "" },
- { "nv!__gl_memmgrvidheapalloclimit", "" },
- { "nv!__gl_mosaic_clip_to_subdev", "" },
- { "nv!__gl_mosaic_clip_to_subdev_h_overlap", "" },
- { "nv!__gl_mosaic_clip_to_subdev_v_overlap", "" },
- { "nv!__gl_overlaymergeblittimerms", "" },
- { "nv!__gl_perfmon_mode", "" },
- { "nv!__gl_pixbar_mode", "" },
- { "nv!__gl_qualityenhancements", "" },
- { "nv!__gl_r27s18q28", "" },
- { "nv!__gl_r2d7c1d8", "" },
- { "nv!__gl_renderer", "" },
- { "nv!__gl_renderqualityflags", "" },
- { "nv!__gl_s3tcquality", "" },
- { "nv!__gl_shaderatomics", "" },
- { "nv!__gl_shadercacheinitsize", "" },
- { "nv!__gl_shader_disk_cache_path", "" },
- { "nv!__gl_shader_disk_cache_read_only", "" },
- { "nv!__gl_shaderobjects", "" },
- { "nv!__gl_shaderportabilitywarnings", "" },
- { "nv!__gl_shaderwarningsaserrors", "" },
- { "nv!__gl_skiptexturehostcopies", "" },
- { "nv!__glslc_debug_level", "" },
- { "nv!__glslc_debug_mask", "" },
- { "nv!__glslc_debug_options", "" },
- { "nv!__glslc_debug_filename", "" },
- { "nv!__gl_sli_dli_control", "" },
- { "nv!__gl_sparsetexture", "" },
- { "nv!__gl_spinlooptimeout", "" },
- { "nv!__gl_sync_to_vblank", "" },
- { "nv!glsynctovblank", "" },
- { "nv!__gl_sysheapreuseratio", "" },
- { "nv!__gl_sysmemtexturepromotion", "" },
- { "nv!__gl_targetflushcount", "" },
- { "nv!__gl_tearingfreeswappresent", "" },
- { "nv!__gl_texclampbehavior", "" },
- { "nv!__gl_texlodbias", "" },
- { "nv!__gl_texmemoryspaceenables", "" },
- { "nv!__gl_textureprecache", "" },
- { "nv!__gl_threadcontrol", "" },
- { "nv!__gl_threadcontrol2", "" },
- { "nv!__gl_usegvievents", "" },
- { "nv!__gl_vbomemoryspaceenables", "" },
- { "nv!__gl_vertexlimit", "" },
- { "nv!__gl_vidheapreuseratio", "" },
- { "nv!__gl_vpipe", "" },
- { "nv!__gl_vpipeformatbloatlimit", "" },
- { "nv!__gl_wglmessageboxonabort", "" },
- { "nv!__gl_writeinfolog", "" },
- { "nv!__gl_writeprogramobjectassembly", "" },
- { "nv!__gl_writeprogramobjectsource", "" },
- { "nv!__gl_xnvadapterpresent", "" },
- { "nv!__gl_yield", "" },
- { "nv!__gl_yieldfunction", "" },
- { "nv!__gl_yieldfunctionfast", "" },
- { "nv!__gl_yieldfunctionslow", "" },
- { "nv!__gl_yieldfunctionwaitfordcqueue", "" },
- { "nv!__gl_yieldfunctionwaitforframe", "" },
- { "nv!__gl_yieldfunctionwaitforgpu", "" },
- { "nv!__gl_zbctableaddhysteresis", "" },
- { "nv!gpu_debug_mode", "" },
- { "nv!gpu_stay_on", "" },
- { "nv!gpu_timeout_ms_max", "" },
- { "nv!gvitimeoutcontrol", "" },
- { "nv!hcctrl", "" },
- { "nv!hwstate_per_ctx", "" },
- { "nv!libandroid_enable_log", "" },
- { "nv!machinecachelimit", "" },
- { "nv!maxframesallowed", "" },
- { "nv!media.aac_51_output_enabled", "" },
- { "nv!memmgrcachedalloclimit", "" },
- { "nv!memmgrcachedalloclimitratio", "" },
- { "nv!memmgrsysheapalloclimit", "" },
- { "nv!memmgrsysheapalloclimitratio", "" },
- { "nv!memmgrvidheapalloclimit", "" },
- { "nv!mosaic_clip_to_subdev", "" },
- { "nv!mosaic_clip_to_subdev_h_overlap", "" },
- { "nv!mosaic_clip_to_subdev_v_overlap", "" },
- { "nv!nvblit.dump", "" },
- { "nv!nvblit.profile", "" },
- { "nv!nvblit.twod", "" },
- { "nv!nvblit.vic", "" },
- { "nv!nvddk_vic_prevent_use", "" },
- { "nv!nv_decompression", "" },
+ { "nv!00008600", string.Empty },
+ { "nv!0007b25e", string.Empty },
+ { "nv!0083e1", string.Empty },
+ { "nv!01621887", string.Empty },
+ { "nv!03134743", string.Empty },
+ { "nv!0356afd0", string.Empty },
+ { "nv!0356afd1", string.Empty },
+ { "nv!0356afd2", string.Empty },
+ { "nv!0356afd3", string.Empty },
+ { "nv!094313", string.Empty },
+ { "nv!0x04dc09", string.Empty },
+ { "nv!0x111133", string.Empty },
+ { "nv!0x1aa483", string.Empty },
+ { "nv!0x1cb1cf", string.Empty },
+ { "nv!0x1cb1d0", string.Empty },
+ { "nv!0x1e3221", string.Empty },
+ { "nv!0x300fc8", string.Empty },
+ { "nv!0x301fc8", string.Empty },
+ { "nv!0x302fc8", string.Empty },
+ { "nv!0x3eec59", string.Empty },
+ { "nv!0x46b3ed", string.Empty },
+ { "nv!0x523dc0", string.Empty },
+ { "nv!0x523dc1", string.Empty },
+ { "nv!0x523dc2", string.Empty },
+ { "nv!0x523dc3", string.Empty },
+ { "nv!0x523dc4", string.Empty },
+ { "nv!0x523dc5", string.Empty },
+ { "nv!0x523dc6", string.Empty },
+ { "nv!0x523dd0", string.Empty },
+ { "nv!0x523dd1", string.Empty },
+ { "nv!0x523dd3", string.Empty },
+ { "nv!0x5344bb", string.Empty },
+ { "nv!0x555237", string.Empty },
+ { "nv!0x58a234", string.Empty },
+ { "nv!0x7b4428", string.Empty },
+ { "nv!0x923dc0", string.Empty },
+ { "nv!0x923dc1", string.Empty },
+ { "nv!0x923dc2", string.Empty },
+ { "nv!0x923dc3", string.Empty },
+ { "nv!0x923dc4", string.Empty },
+ { "nv!0x923dd3", string.Empty },
+ { "nv!0x9abdc5", string.Empty },
+ { "nv!0x9abdc6", string.Empty },
+ { "nv!0xaaa36c", string.Empty },
+ { "nv!0xb09da0", string.Empty },
+ { "nv!0xb09da1", string.Empty },
+ { "nv!0xb09da2", string.Empty },
+ { "nv!0xb09da3", string.Empty },
+ { "nv!0xb09da4", string.Empty },
+ { "nv!0xb09da5", string.Empty },
+ { "nv!0xb0b348", string.Empty },
+ { "nv!0xb0b349", string.Empty },
+ { "nv!0xbb558f", string.Empty },
+ { "nv!0xbd10fb", string.Empty },
+ { "nv!0xc32ad3", string.Empty },
+ { "nv!0xce2348", string.Empty },
+ { "nv!0xcfd81f", string.Empty },
+ { "nv!0xe0036b", string.Empty },
+ { "nv!0xe01f2d", string.Empty },
+ { "nv!0xe17212", string.Empty },
+ { "nv!0xeae966", string.Empty },
+ { "nv!0xed4f82", string.Empty },
+ { "nv!0xf12335", string.Empty },
+ { "nv!0xf12336", string.Empty },
+ { "nv!10261989", string.Empty },
+ { "nv!1042d483", string.Empty },
+ { "nv!10572898", string.Empty },
+ { "nv!115631", string.Empty },
+ { "nv!12950094", string.Empty },
+ { "nv!1314f311", string.Empty },
+ { "nv!1314f312", string.Empty },
+ { "nv!13279512", string.Empty },
+ { "nv!13813496", string.Empty },
+ { "nv!14507179", string.Empty },
+ { "nv!15694569", string.Empty },
+ { "nv!16936964", string.Empty },
+ { "nv!17aa230c", string.Empty },
+ { "nv!182054", string.Empty },
+ { "nv!18273275", string.Empty },
+ { "nv!18273276", string.Empty },
+ { "nv!1854d03b", string.Empty },
+ { "nv!18add00d", string.Empty },
+ { "nv!19156670", string.Empty },
+ { "nv!19286545", string.Empty },
+ { "nv!1a298e9f", string.Empty },
+ { "nv!1acf43fe", string.Empty },
+ { "nv!1bda43fe", string.Empty },
+ { "nv!1c3b92", string.Empty },
+ { "nv!21509920", string.Empty },
+ { "nv!215323457", string.Empty },
+ { "nv!2165ad", string.Empty },
+ { "nv!2165ae", string.Empty },
+ { "nv!21be9c", string.Empty },
+ { "nv!233264316", string.Empty },
+ { "nv!234557580", string.Empty },
+ { "nv!23cd0e", string.Empty },
+ { "nv!24189123", string.Empty },
+ { "nv!2443266", string.Empty },
+ { "nv!25025519", string.Empty },
+ { "nv!255e39", string.Empty },
+ { "nv!2583364", string.Empty },
+ { "nv!2888c1", string.Empty },
+ { "nv!28ca3e", string.Empty },
+ { "nv!29871243", string.Empty },
+ { "nv!2a1f64", string.Empty },
+ { "nv!2dc432", string.Empty },
+ { "nv!2de437", string.Empty },
+ { "nv!2f3bb89c", string.Empty },
+ { "nv!2fd652", string.Empty },
+ { "nv!3001ac", string.Empty },
+ { "nv!31298772", string.Empty },
+ { "nv!313233", string.Empty },
+ { "nv!31f7d603", string.Empty },
+ { "nv!320ce4", string.Empty },
+ { "nv!32153248", string.Empty },
+ { "nv!32153249", string.Empty },
+ { "nv!335bca", string.Empty },
+ { "nv!342abb", string.Empty },
+ { "nv!34dfe6", string.Empty },
+ { "nv!34dfe7", string.Empty },
+ { "nv!34dfe8", string.Empty },
+ { "nv!34dfe9", string.Empty },
+ { "nv!35201578", string.Empty },
+ { "nv!359278", string.Empty },
+ { "nv!37f53a", string.Empty },
+ { "nv!38144972", string.Empty },
+ { "nv!38542646", string.Empty },
+ { "nv!3b74c9", string.Empty },
+ { "nv!3c136f", string.Empty },
+ { "nv!3cf72823", string.Empty },
+ { "nv!3d7af029", string.Empty },
+ { "nv!3ff34782", string.Empty },
+ { "nv!4129618", string.Empty },
+ { "nv!4189fac3", string.Empty },
+ { "nv!420bd4", string.Empty },
+ { "nv!42a699", string.Empty },
+ { "nv!441369", string.Empty },
+ { "nv!4458713e", string.Empty },
+ { "nv!4554b6", string.Empty },
+ { "nv!457425", string.Empty },
+ { "nv!4603b207", string.Empty },
+ { "nv!46574957", string.Empty },
+ { "nv!46574958", string.Empty },
+ { "nv!46813529", string.Empty },
+ { "nv!46f1e13d", string.Empty },
+ { "nv!47534c43", string.Empty },
+ { "nv!48550336", string.Empty },
+ { "nv!48576893", string.Empty },
+ { "nv!48576894", string.Empty },
+ { "nv!4889ac02", string.Empty },
+ { "nv!49005740", string.Empty },
+ { "nv!49867584", string.Empty },
+ { "nv!49960973", string.Empty },
+ { "nv!4a5341", string.Empty },
+ { "nv!4f4e48", string.Empty },
+ { "nv!4f8a0a", string.Empty },
+ { "nv!50299698", string.Empty },
+ { "nv!50299699", string.Empty },
+ { "nv!50361291", string.Empty },
+ { "nv!5242ae", string.Empty },
+ { "nv!53d30c", string.Empty },
+ { "nv!56347a", string.Empty },
+ { "nv!563a95f1", string.Empty },
+ { "nv!573823", string.Empty },
+ { "nv!58027529", string.Empty },
+ { "nv!5d2d63", string.Empty },
+ { "nv!5f7e3b", string.Empty },
+ { "nv!60461793", string.Empty },
+ { "nv!60d355", string.Empty },
+ { "nv!616627aa", string.Empty },
+ { "nv!62317182", string.Empty },
+ { "nv!6253fa2e", string.Empty },
+ { "nv!64100768", string.Empty },
+ { "nv!64100769", string.Empty },
+ { "nv!64100770", string.Empty },
+ { "nv!647395", string.Empty },
+ { "nv!66543234", string.Empty },
+ { "nv!67674763", string.Empty },
+ { "nv!67739784", string.Empty },
+ { "nv!68fb9c", string.Empty },
+ { "nv!69801276", string.Empty },
+ { "nv!6af9fa2f", string.Empty },
+ { "nv!6af9fa3f", string.Empty },
+ { "nv!6af9fa4f", string.Empty },
+ { "nv!6bd8c7", string.Empty },
+ { "nv!6c7691", string.Empty },
+ { "nv!6d4296ce", string.Empty },
+ { "nv!6dd7e7", string.Empty },
+ { "nv!6dd7e8", string.Empty },
+ { "nv!6fe11ec1", string.Empty },
+ { "nv!716511763", string.Empty },
+ { "nv!72504593", string.Empty },
+ { "nv!73304097", string.Empty },
+ { "nv!73314098", string.Empty },
+ { "nv!74095213", string.Empty },
+ { "nv!74095213a", string.Empty },
+ { "nv!74095213b", string.Empty },
+ { "nv!74095214", string.Empty },
+ { "nv!748f9649", string.Empty },
+ { "nv!75494732", string.Empty },
+ { "nv!78452832", string.Empty },
+ { "nv!784561", string.Empty },
+ { "nv!78e16b9c", string.Empty },
+ { "nv!79251225", string.Empty },
+ { "nv!7c128b", string.Empty },
+ { "nv!7ccd93", string.Empty },
+ { "nv!7df8d1", string.Empty },
+ { "nv!800c2310", string.Empty },
+ { "nv!80546710", string.Empty },
+ { "nv!80772310", string.Empty },
+ { "nv!808ee280", string.Empty },
+ { "nv!81131154", string.Empty },
+ { "nv!81274457", string.Empty },
+ { "nv!8292291f", string.Empty },
+ { "nv!83498426", string.Empty },
+ { "nv!84993794", string.Empty },
+ { "nv!84995585", string.Empty },
+ { "nv!84a0a0", string.Empty },
+ { "nv!852142", string.Empty },
+ { "nv!85612309", string.Empty },
+ { "nv!85612310", string.Empty },
+ { "nv!85612311", string.Empty },
+ { "nv!85612312", string.Empty },
+ { "nv!8623ff27", string.Empty },
+ { "nv!87364952", string.Empty },
+ { "nv!87f6275666", string.Empty },
+ { "nv!886748", string.Empty },
+ { "nv!89894423", string.Empty },
+ { "nv!8ad8a75", string.Empty },
+ { "nv!8ad8ad00", string.Empty },
+ { "nv!8bb815", string.Empty },
+ { "nv!8bb817", string.Empty },
+ { "nv!8bb818", string.Empty },
+ { "nv!8bb819", string.Empty },
+ { "nv!8e640cd1", string.Empty },
+ { "nv!8f34971a", string.Empty },
+ { "nv!8f773984", string.Empty },
+ { "nv!8f7a7d", string.Empty },
+ { "nv!902486209", string.Empty },
+ { "nv!90482571", string.Empty },
+ { "nv!91214835", string.Empty },
+ { "nv!912848290", string.Empty },
+ { "nv!915e56", string.Empty },
+ { "nv!92179063", string.Empty },
+ { "nv!92179064", string.Empty },
+ { "nv!92179065", string.Empty },
+ { "nv!92179066", string.Empty },
+ { "nv!92350358", string.Empty },
+ { "nv!92809063", string.Empty },
+ { "nv!92809064", string.Empty },
+ { "nv!92809065", string.Empty },
+ { "nv!92809066", string.Empty },
+ { "nv!92920143", string.Empty },
+ { "nv!93a89b12", string.Empty },
+ { "nv!93a89c0b", string.Empty },
+ { "nv!94812574", string.Empty },
+ { "nv!95282304", string.Empty },
+ { "nv!95394027", string.Empty },
+ { "nv!959b1f", string.Empty },
+ { "nv!9638af", string.Empty },
+ { "nv!96fd59", string.Empty },
+ { "nv!97f6275666", string.Empty },
+ { "nv!97f6275667", string.Empty },
+ { "nv!97f6275668", string.Empty },
+ { "nv!97f6275669", string.Empty },
+ { "nv!97f627566a", string.Empty },
+ { "nv!97f627566b", string.Empty },
+ { "nv!97f627566d", string.Empty },
+ { "nv!97f627566e", string.Empty },
+ { "nv!97f627566f", string.Empty },
+ { "nv!97f6275670", string.Empty },
+ { "nv!97f6275671", string.Empty },
+ { "nv!97f727566e", string.Empty },
+ { "nv!98480775", string.Empty },
+ { "nv!98480776", string.Empty },
+ { "nv!98480777", string.Empty },
+ { "nv!992431", string.Empty },
+ { "nv!9aa29065", string.Empty },
+ { "nv!9af32c", string.Empty },
+ { "nv!9af32d", string.Empty },
+ { "nv!9af32e", string.Empty },
+ { "nv!9c108b71", string.Empty },
+ { "nv!9f279065", string.Empty },
+ { "nv!a01bc728", string.Empty },
+ { "nv!a13b46c80", string.Empty },
+ { "nv!a22eb0", string.Empty },
+ { "nv!a2fb451e", string.Empty },
+ { "nv!a3456abe", string.Empty },
+ { "nv!a7044887", string.Empty },
+ { "nv!a7149200", string.Empty },
+ { "nv!a766215670", string.Empty },
+ { "nv!aac_drc_boost", string.Empty },
+ { "nv!aac_drc_cut", string.Empty },
+ { "nv!aac_drc_enc_target_level", string.Empty },
+ { "nv!aac_drc_heavy", string.Empty },
+ { "nv!aac_drc_reference_level", string.Empty },
+ { "nv!aalinegamma", string.Empty },
+ { "nv!aalinetweaks", string.Empty },
+ { "nv!ab34ee01", string.Empty },
+ { "nv!ab34ee02", string.Empty },
+ { "nv!ab34ee03", string.Empty },
+ { "nv!ac0274", string.Empty },
+ { "nv!af73c63e", string.Empty },
+ { "nv!af73c63f", string.Empty },
+ { "nv!af9927", string.Empty },
+ { "nv!afoverride", string.Empty },
+ { "nv!allocdeviceevents", string.Empty },
+ { "nv!applicationkey", string.Empty },
+ { "nv!appreturnonlybasicglsltype", string.Empty },
+ { "nv!app_softimage", string.Empty },
+ { "nv!app_supportbits2", string.Empty },
+ { "nv!assumetextureismipmappedatcreation", string.Empty },
+ { "nv!b1fb0f01", string.Empty },
+ { "nv!b3edd5", string.Empty },
+ { "nv!b40d9e03d", string.Empty },
+ { "nv!b7f6275666", string.Empty },
+ { "nv!b812c1", string.Empty },
+ { "nv!ba14ba1a", string.Empty },
+ { "nv!ba14ba1b", string.Empty },
+ { "nv!bd7559", string.Empty },
+ { "nv!bd755a", string.Empty },
+ { "nv!bd755c", string.Empty },
+ { "nv!bd755d", string.Empty },
+ { "nv!be58bb", string.Empty },
+ { "nv!be92cb", string.Empty },
+ { "nv!beefcba3", string.Empty },
+ { "nv!beefcba4", string.Empty },
+ { "nv!c023777f", string.Empty },
+ { "nv!c09dc8", string.Empty },
+ { "nv!c0d340", string.Empty },
+ { "nv!c2ff374c", string.Empty },
+ { "nv!c5e9d7a3", string.Empty },
+ { "nv!c5e9d7a4", string.Empty },
+ { "nv!c5e9d7b4", string.Empty },
+ { "nv!c618f9", string.Empty },
+ { "nv!ca345840", string.Empty },
+ { "nv!cachedisable", string.Empty },
+ { "nv!cast.on", string.Empty },
+ { "nv!cde", string.Empty },
+ { "nv!channelpriorityoverride", string.Empty },
+ { "nv!cleardatastorevidmem", string.Empty },
+ { "nv!cmdbufmemoryspaceenables", string.Empty },
+ { "nv!cmdbufminwords", string.Empty },
+ { "nv!cmdbufsizewords", string.Empty },
+ { "nv!conformantblitframebufferscissor", string.Empty },
+ { "nv!conformantincompletetextures", string.Empty },
+ { "nv!copybuffermethod", string.Empty },
+ { "nv!cubemapaniso", string.Empty },
+ { "nv!cubemapfiltering", string.Empty },
+ { "nv!d0e9a4d7", string.Empty },
+ { "nv!d13733f12", string.Empty },
+ { "nv!d1b399", string.Empty },
+ { "nv!d2983c32", string.Empty },
+ { "nv!d2983c33", string.Empty },
+ { "nv!d2e71b", string.Empty },
+ { "nv!d377dc", string.Empty },
+ { "nv!d377dd", string.Empty },
+ { "nv!d489f4", string.Empty },
+ { "nv!d4bce1", string.Empty },
+ { "nv!d518cb", string.Empty },
+ { "nv!d518cd", string.Empty },
+ { "nv!d518ce", string.Empty },
+ { "nv!d518d0", string.Empty },
+ { "nv!d518d1", string.Empty },
+ { "nv!d518d2", string.Empty },
+ { "nv!d518d3", string.Empty },
+ { "nv!d518d4", string.Empty },
+ { "nv!d518d5", string.Empty },
+ { "nv!d59eda", string.Empty },
+ { "nv!d83cbd", string.Empty },
+ { "nv!d8e777", string.Empty },
+ { "nv!debug_level", string.Empty },
+ { "nv!debug_mask", string.Empty },
+ { "nv!debug_options", string.Empty },
+ { "nv!devshmpageableallocations", string.Empty },
+ { "nv!df1f9812", string.Empty },
+ { "nv!df783c", string.Empty },
+ { "nv!diagenable", string.Empty },
+ { "nv!disallowcemask", string.Empty },
+ { "nv!disallowz16", string.Empty },
+ { "nv!dlmemoryspaceenables", string.Empty },
+ { "nv!e0bfec", string.Empty },
+ { "nv!e433456d", string.Empty },
+ { "nv!e435563f", string.Empty },
+ { "nv!e4cd9c", string.Empty },
+ { "nv!e5c972", string.Empty },
+ { "nv!e639ef", string.Empty },
+ { "nv!e802af", string.Empty },
+ { "nv!eae964", string.Empty },
+ { "nv!earlytexturehwallocation", string.Empty },
+ { "nv!eb92a3", string.Empty },
+ { "nv!ebca56", string.Empty },
+ { "nv!enable-noaud", string.Empty },
+ { "nv!enable-noavs", string.Empty },
+ { "nv!enable-prof", string.Empty },
+ { "nv!enable-sxesmode", string.Empty },
+ { "nv!enable-ulld", string.Empty },
+ { "nv!expert_detail_level", string.Empty },
+ { "nv!expert_output_mask", string.Empty },
+ { "nv!expert_report_mask", string.Empty },
+ { "nv!extensionstringnvarch", string.Empty },
+ { "nv!extensionstringversion", string.Empty },
+ { "nv!f00f1938", string.Empty },
+ { "nv!f10736", string.Empty },
+ { "nv!f1846870", string.Empty },
+ { "nv!f33bc370", string.Empty },
+ { "nv!f392a874", string.Empty },
+ { "nv!f49ae8", string.Empty },
+ { "nv!fa345cce", string.Empty },
+ { "nv!fa35cc4", string.Empty },
+ { "nv!faa14a", string.Empty },
+ { "nv!faf8a723", string.Empty },
+ { "nv!fastgs", string.Empty },
+ { "nv!fbf4ac45", string.Empty },
+ { "nv!fbo_blit_ignore_srgb", string.Empty },
+ { "nv!fc64c7", string.Empty },
+ { "nv!ff54ec97", string.Empty },
+ { "nv!ff54ec98", string.Empty },
+ { "nv!forceexitprocessdetach", string.Empty },
+ { "nv!forcerequestedesversion", string.Empty },
+ { "nv!__gl_", string.Empty },
+ { "nv!__gl_00008600", string.Empty },
+ { "nv!__gl_0007b25e", string.Empty },
+ { "nv!__gl_0083e1", string.Empty },
+ { "nv!__gl_01621887", string.Empty },
+ { "nv!__gl_03134743", string.Empty },
+ { "nv!__gl_0356afd0", string.Empty },
+ { "nv!__gl_0356afd1", string.Empty },
+ { "nv!__gl_0356afd2", string.Empty },
+ { "nv!__gl_0356afd3", string.Empty },
+ { "nv!__gl_094313", string.Empty },
+ { "nv!__gl_0x04dc09", string.Empty },
+ { "nv!__gl_0x111133", string.Empty },
+ { "nv!__gl_0x1aa483", string.Empty },
+ { "nv!__gl_0x1cb1cf", string.Empty },
+ { "nv!__gl_0x1cb1d0", string.Empty },
+ { "nv!__gl_0x1e3221", string.Empty },
+ { "nv!__gl_0x300fc8", string.Empty },
+ { "nv!__gl_0x301fc8", string.Empty },
+ { "nv!__gl_0x302fc8", string.Empty },
+ { "nv!__gl_0x3eec59", string.Empty },
+ { "nv!__gl_0x46b3ed", string.Empty },
+ { "nv!__gl_0x523dc0", string.Empty },
+ { "nv!__gl_0x523dc1", string.Empty },
+ { "nv!__gl_0x523dc2", string.Empty },
+ { "nv!__gl_0x523dc3", string.Empty },
+ { "nv!__gl_0x523dc4", string.Empty },
+ { "nv!__gl_0x523dc5", string.Empty },
+ { "nv!__gl_0x523dc6", string.Empty },
+ { "nv!__gl_0x523dd0", string.Empty },
+ { "nv!__gl_0x523dd1", string.Empty },
+ { "nv!__gl_0x523dd3", string.Empty },
+ { "nv!__gl_0x5344bb", string.Empty },
+ { "nv!__gl_0x555237", string.Empty },
+ { "nv!__gl_0x58a234", string.Empty },
+ { "nv!__gl_0x7b4428", string.Empty },
+ { "nv!__gl_0x923dc0", string.Empty },
+ { "nv!__gl_0x923dc1", string.Empty },
+ { "nv!__gl_0x923dc2", string.Empty },
+ { "nv!__gl_0x923dc3", string.Empty },
+ { "nv!__gl_0x923dc4", string.Empty },
+ { "nv!__gl_0x923dd3", string.Empty },
+ { "nv!__gl_0x9abdc5", string.Empty },
+ { "nv!__gl_0x9abdc6", string.Empty },
+ { "nv!__gl_0xaaa36c", string.Empty },
+ { "nv!__gl_0xb09da0", string.Empty },
+ { "nv!__gl_0xb09da1", string.Empty },
+ { "nv!__gl_0xb09da2", string.Empty },
+ { "nv!__gl_0xb09da3", string.Empty },
+ { "nv!__gl_0xb09da4", string.Empty },
+ { "nv!__gl_0xb09da5", string.Empty },
+ { "nv!__gl_0xb0b348", string.Empty },
+ { "nv!__gl_0xb0b349", string.Empty },
+ { "nv!__gl_0xbb558f", string.Empty },
+ { "nv!__gl_0xbd10fb", string.Empty },
+ { "nv!__gl_0xc32ad3", string.Empty },
+ { "nv!__gl_0xce2348", string.Empty },
+ { "nv!__gl_0xcfd81f", string.Empty },
+ { "nv!__gl_0xe0036b", string.Empty },
+ { "nv!__gl_0xe01f2d", string.Empty },
+ { "nv!__gl_0xe17212", string.Empty },
+ { "nv!__gl_0xeae966", string.Empty },
+ { "nv!__gl_0xed4f82", string.Empty },
+ { "nv!__gl_0xf12335", string.Empty },
+ { "nv!__gl_0xf12336", string.Empty },
+ { "nv!__gl_10261989", string.Empty },
+ { "nv!__gl_1042d483", string.Empty },
+ { "nv!__gl_10572898", string.Empty },
+ { "nv!__gl_115631", string.Empty },
+ { "nv!__gl_12950094", string.Empty },
+ { "nv!__gl_1314f311", string.Empty },
+ { "nv!__gl_1314f312", string.Empty },
+ { "nv!__gl_13279512", string.Empty },
+ { "nv!__gl_13813496", string.Empty },
+ { "nv!__gl_14507179", string.Empty },
+ { "nv!__gl_15694569", string.Empty },
+ { "nv!__gl_16936964", string.Empty },
+ { "nv!__gl_17aa230c", string.Empty },
+ { "nv!__gl_182054", string.Empty },
+ { "nv!__gl_18273275", string.Empty },
+ { "nv!__gl_18273276", string.Empty },
+ { "nv!__gl_1854d03b", string.Empty },
+ { "nv!__gl_18add00d", string.Empty },
+ { "nv!__gl_19156670", string.Empty },
+ { "nv!__gl_19286545", string.Empty },
+ { "nv!__gl_1a298e9f", string.Empty },
+ { "nv!__gl_1acf43fe", string.Empty },
+ { "nv!__gl_1bda43fe", string.Empty },
+ { "nv!__gl_1c3b92", string.Empty },
+ { "nv!__gl_21509920", string.Empty },
+ { "nv!__gl_215323457", string.Empty },
+ { "nv!__gl_2165ad", string.Empty },
+ { "nv!__gl_2165ae", string.Empty },
+ { "nv!__gl_21be9c", string.Empty },
+ { "nv!__gl_233264316", string.Empty },
+ { "nv!__gl_234557580", string.Empty },
+ { "nv!__gl_23cd0e", string.Empty },
+ { "nv!__gl_24189123", string.Empty },
+ { "nv!__gl_2443266", string.Empty },
+ { "nv!__gl_25025519", string.Empty },
+ { "nv!__gl_255e39", string.Empty },
+ { "nv!__gl_2583364", string.Empty },
+ { "nv!__gl_2888c1", string.Empty },
+ { "nv!__gl_28ca3e", string.Empty },
+ { "nv!__gl_29871243", string.Empty },
+ { "nv!__gl_2a1f64", string.Empty },
+ { "nv!__gl_2dc432", string.Empty },
+ { "nv!__gl_2de437", string.Empty },
+ { "nv!__gl_2f3bb89c", string.Empty },
+ { "nv!__gl_2fd652", string.Empty },
+ { "nv!__gl_3001ac", string.Empty },
+ { "nv!__gl_31298772", string.Empty },
+ { "nv!__gl_313233", string.Empty },
+ { "nv!__gl_31f7d603", string.Empty },
+ { "nv!__gl_320ce4", string.Empty },
+ { "nv!__gl_32153248", string.Empty },
+ { "nv!__gl_32153249", string.Empty },
+ { "nv!__gl_335bca", string.Empty },
+ { "nv!__gl_342abb", string.Empty },
+ { "nv!__gl_34dfe6", string.Empty },
+ { "nv!__gl_34dfe7", string.Empty },
+ { "nv!__gl_34dfe8", string.Empty },
+ { "nv!__gl_34dfe9", string.Empty },
+ { "nv!__gl_35201578", string.Empty },
+ { "nv!__gl_359278", string.Empty },
+ { "nv!__gl_37f53a", string.Empty },
+ { "nv!__gl_38144972", string.Empty },
+ { "nv!__gl_38542646", string.Empty },
+ { "nv!__gl_3b74c9", string.Empty },
+ { "nv!__gl_3c136f", string.Empty },
+ { "nv!__gl_3cf72823", string.Empty },
+ { "nv!__gl_3d7af029", string.Empty },
+ { "nv!__gl_3ff34782", string.Empty },
+ { "nv!__gl_4129618", string.Empty },
+ { "nv!__gl_4189fac3", string.Empty },
+ { "nv!__gl_420bd4", string.Empty },
+ { "nv!__gl_42a699", string.Empty },
+ { "nv!__gl_441369", string.Empty },
+ { "nv!__gl_4458713e", string.Empty },
+ { "nv!__gl_4554b6", string.Empty },
+ { "nv!__gl_457425", string.Empty },
+ { "nv!__gl_4603b207", string.Empty },
+ { "nv!__gl_46574957", string.Empty },
+ { "nv!__gl_46574958", string.Empty },
+ { "nv!__gl_46813529", string.Empty },
+ { "nv!__gl_46f1e13d", string.Empty },
+ { "nv!__gl_47534c43", string.Empty },
+ { "nv!__gl_48550336", string.Empty },
+ { "nv!__gl_48576893", string.Empty },
+ { "nv!__gl_48576894", string.Empty },
+ { "nv!__gl_4889ac02", string.Empty },
+ { "nv!__gl_49005740", string.Empty },
+ { "nv!__gl_49867584", string.Empty },
+ { "nv!__gl_49960973", string.Empty },
+ { "nv!__gl_4a5341", string.Empty },
+ { "nv!__gl_4f4e48", string.Empty },
+ { "nv!__gl_4f8a0a", string.Empty },
+ { "nv!__gl_50299698", string.Empty },
+ { "nv!__gl_50299699", string.Empty },
+ { "nv!__gl_50361291", string.Empty },
+ { "nv!__gl_5242ae", string.Empty },
+ { "nv!__gl_53d30c", string.Empty },
+ { "nv!__gl_56347a", string.Empty },
+ { "nv!__gl_563a95f1", string.Empty },
+ { "nv!__gl_573823", string.Empty },
+ { "nv!__gl_58027529", string.Empty },
+ { "nv!__gl_5d2d63", string.Empty },
+ { "nv!__gl_5f7e3b", string.Empty },
+ { "nv!__gl_60461793", string.Empty },
+ { "nv!__gl_60d355", string.Empty },
+ { "nv!__gl_616627aa", string.Empty },
+ { "nv!__gl_62317182", string.Empty },
+ { "nv!__gl_6253fa2e", string.Empty },
+ { "nv!__gl_64100768", string.Empty },
+ { "nv!__gl_64100769", string.Empty },
+ { "nv!__gl_64100770", string.Empty },
+ { "nv!__gl_647395", string.Empty },
+ { "nv!__gl_66543234", string.Empty },
+ { "nv!__gl_67674763", string.Empty },
+ { "nv!__gl_67739784", string.Empty },
+ { "nv!__gl_68fb9c", string.Empty },
+ { "nv!__gl_69801276", string.Empty },
+ { "nv!__gl_6af9fa2f", string.Empty },
+ { "nv!__gl_6af9fa3f", string.Empty },
+ { "nv!__gl_6af9fa4f", string.Empty },
+ { "nv!__gl_6bd8c7", string.Empty },
+ { "nv!__gl_6c7691", string.Empty },
+ { "nv!__gl_6d4296ce", string.Empty },
+ { "nv!__gl_6dd7e7", string.Empty },
+ { "nv!__gl_6dd7e8", string.Empty },
+ { "nv!__gl_6fe11ec1", string.Empty },
+ { "nv!__gl_716511763", string.Empty },
+ { "nv!__gl_72504593", string.Empty },
+ { "nv!__gl_73304097", string.Empty },
+ { "nv!__gl_73314098", string.Empty },
+ { "nv!__gl_74095213", string.Empty },
+ { "nv!__gl_74095213a", string.Empty },
+ { "nv!__gl_74095213b", string.Empty },
+ { "nv!__gl_74095214", string.Empty },
+ { "nv!__gl_748f9649", string.Empty },
+ { "nv!__gl_75494732", string.Empty },
+ { "nv!__gl_78452832", string.Empty },
+ { "nv!__gl_784561", string.Empty },
+ { "nv!__gl_78e16b9c", string.Empty },
+ { "nv!__gl_79251225", string.Empty },
+ { "nv!__gl_7c128b", string.Empty },
+ { "nv!__gl_7ccd93", string.Empty },
+ { "nv!__gl_7df8d1", string.Empty },
+ { "nv!__gl_800c2310", string.Empty },
+ { "nv!__gl_80546710", string.Empty },
+ { "nv!__gl_80772310", string.Empty },
+ { "nv!__gl_808ee280", string.Empty },
+ { "nv!__gl_81131154", string.Empty },
+ { "nv!__gl_81274457", string.Empty },
+ { "nv!__gl_8292291f", string.Empty },
+ { "nv!__gl_83498426", string.Empty },
+ { "nv!__gl_84993794", string.Empty },
+ { "nv!__gl_84995585", string.Empty },
+ { "nv!__gl_84a0a0", string.Empty },
+ { "nv!__gl_852142", string.Empty },
+ { "nv!__gl_85612309", string.Empty },
+ { "nv!__gl_85612310", string.Empty },
+ { "nv!__gl_85612311", string.Empty },
+ { "nv!__gl_85612312", string.Empty },
+ { "nv!__gl_8623ff27", string.Empty },
+ { "nv!__gl_87364952", string.Empty },
+ { "nv!__gl_87f6275666", string.Empty },
+ { "nv!__gl_886748", string.Empty },
+ { "nv!__gl_89894423", string.Empty },
+ { "nv!__gl_8ad8a75", string.Empty },
+ { "nv!__gl_8ad8ad00", string.Empty },
+ { "nv!__gl_8bb815", string.Empty },
+ { "nv!__gl_8bb817", string.Empty },
+ { "nv!__gl_8bb818", string.Empty },
+ { "nv!__gl_8bb819", string.Empty },
+ { "nv!__gl_8e640cd1", string.Empty },
+ { "nv!__gl_8f34971a", string.Empty },
+ { "nv!__gl_8f773984", string.Empty },
+ { "nv!__gl_8f7a7d", string.Empty },
+ { "nv!__gl_902486209", string.Empty },
+ { "nv!__gl_90482571", string.Empty },
+ { "nv!__gl_91214835", string.Empty },
+ { "nv!__gl_912848290", string.Empty },
+ { "nv!__gl_915e56", string.Empty },
+ { "nv!__gl_92179063", string.Empty },
+ { "nv!__gl_92179064", string.Empty },
+ { "nv!__gl_92179065", string.Empty },
+ { "nv!__gl_92179066", string.Empty },
+ { "nv!__gl_92350358", string.Empty },
+ { "nv!__gl_92809063", string.Empty },
+ { "nv!__gl_92809064", string.Empty },
+ { "nv!__gl_92809065", string.Empty },
+ { "nv!__gl_92809066", string.Empty },
+ { "nv!__gl_92920143", string.Empty },
+ { "nv!__gl_93a89b12", string.Empty },
+ { "nv!__gl_93a89c0b", string.Empty },
+ { "nv!__gl_94812574", string.Empty },
+ { "nv!__gl_95282304", string.Empty },
+ { "nv!__gl_95394027", string.Empty },
+ { "nv!__gl_959b1f", string.Empty },
+ { "nv!__gl_9638af", string.Empty },
+ { "nv!__gl_96fd59", string.Empty },
+ { "nv!__gl_97f6275666", string.Empty },
+ { "nv!__gl_97f6275667", string.Empty },
+ { "nv!__gl_97f6275668", string.Empty },
+ { "nv!__gl_97f6275669", string.Empty },
+ { "nv!__gl_97f627566a", string.Empty },
+ { "nv!__gl_97f627566b", string.Empty },
+ { "nv!__gl_97f627566d", string.Empty },
+ { "nv!__gl_97f627566e", string.Empty },
+ { "nv!__gl_97f627566f", string.Empty },
+ { "nv!__gl_97f6275670", string.Empty },
+ { "nv!__gl_97f6275671", string.Empty },
+ { "nv!__gl_97f727566e", string.Empty },
+ { "nv!__gl_98480775", string.Empty },
+ { "nv!__gl_98480776", string.Empty },
+ { "nv!__gl_98480777", string.Empty },
+ { "nv!__gl_992431", string.Empty },
+ { "nv!__gl_9aa29065", string.Empty },
+ { "nv!__gl_9af32c", string.Empty },
+ { "nv!__gl_9af32d", string.Empty },
+ { "nv!__gl_9af32e", string.Empty },
+ { "nv!__gl_9c108b71", string.Empty },
+ { "nv!__gl_9f279065", string.Empty },
+ { "nv!__gl_a01bc728", string.Empty },
+ { "nv!__gl_a13b46c80", string.Empty },
+ { "nv!__gl_a22eb0", string.Empty },
+ { "nv!__gl_a2fb451e", string.Empty },
+ { "nv!__gl_a3456abe", string.Empty },
+ { "nv!__gl_a7044887", string.Empty },
+ { "nv!__gl_a7149200", string.Empty },
+ { "nv!__gl_a766215670", string.Empty },
+ { "nv!__gl_aalinegamma", string.Empty },
+ { "nv!__gl_aalinetweaks", string.Empty },
+ { "nv!__gl_ab34ee01", string.Empty },
+ { "nv!__gl_ab34ee02", string.Empty },
+ { "nv!__gl_ab34ee03", string.Empty },
+ { "nv!__gl_ac0274", string.Empty },
+ { "nv!__gl_af73c63e", string.Empty },
+ { "nv!__gl_af73c63f", string.Empty },
+ { "nv!__gl_af9927", string.Empty },
+ { "nv!__gl_afoverride", string.Empty },
+ { "nv!__gl_allocdeviceevents", string.Empty },
+ { "nv!__gl_applicationkey", string.Empty },
+ { "nv!__gl_appreturnonlybasicglsltype", string.Empty },
+ { "nv!__gl_app_softimage", string.Empty },
+ { "nv!__gl_app_supportbits2", string.Empty },
+ { "nv!__gl_assumetextureismipmappedatcreation", string.Empty },
+ { "nv!__gl_b1fb0f01", string.Empty },
+ { "nv!__gl_b3edd5", string.Empty },
+ { "nv!__gl_b40d9e03d", string.Empty },
+ { "nv!__gl_b7f6275666", string.Empty },
+ { "nv!__gl_b812c1", string.Empty },
+ { "nv!__gl_ba14ba1a", string.Empty },
+ { "nv!__gl_ba14ba1b", string.Empty },
+ { "nv!__gl_bd7559", string.Empty },
+ { "nv!__gl_bd755a", string.Empty },
+ { "nv!__gl_bd755c", string.Empty },
+ { "nv!__gl_bd755d", string.Empty },
+ { "nv!__gl_be58bb", string.Empty },
+ { "nv!__gl_be92cb", string.Empty },
+ { "nv!__gl_beefcba3", string.Empty },
+ { "nv!__gl_beefcba4", string.Empty },
+ { "nv!__gl_c023777f", string.Empty },
+ { "nv!__gl_c09dc8", string.Empty },
+ { "nv!__gl_c0d340", string.Empty },
+ { "nv!__gl_c2ff374c", string.Empty },
+ { "nv!__gl_c5e9d7a3", string.Empty },
+ { "nv!__gl_c5e9d7a4", string.Empty },
+ { "nv!__gl_c5e9d7b4", string.Empty },
+ { "nv!__gl_c618f9", string.Empty },
+ { "nv!__gl_ca345840", string.Empty },
+ { "nv!__gl_cachedisable", string.Empty },
+ { "nv!__gl_channelpriorityoverride", string.Empty },
+ { "nv!__gl_cleardatastorevidmem", string.Empty },
+ { "nv!__gl_cmdbufmemoryspaceenables", string.Empty },
+ { "nv!__gl_cmdbufminwords", string.Empty },
+ { "nv!__gl_cmdbufsizewords", string.Empty },
+ { "nv!__gl_conformantblitframebufferscissor", string.Empty },
+ { "nv!__gl_conformantincompletetextures", string.Empty },
+ { "nv!__gl_copybuffermethod", string.Empty },
+ { "nv!__gl_cubemapaniso", string.Empty },
+ { "nv!__gl_cubemapfiltering", string.Empty },
+ { "nv!__gl_d0e9a4d7", string.Empty },
+ { "nv!__gl_d13733f12", string.Empty },
+ { "nv!__gl_d1b399", string.Empty },
+ { "nv!__gl_d2983c32", string.Empty },
+ { "nv!__gl_d2983c33", string.Empty },
+ { "nv!__gl_d2e71b", string.Empty },
+ { "nv!__gl_d377dc", string.Empty },
+ { "nv!__gl_d377dd", string.Empty },
+ { "nv!__gl_d489f4", string.Empty },
+ { "nv!__gl_d4bce1", string.Empty },
+ { "nv!__gl_d518cb", string.Empty },
+ { "nv!__gl_d518cd", string.Empty },
+ { "nv!__gl_d518ce", string.Empty },
+ { "nv!__gl_d518d0", string.Empty },
+ { "nv!__gl_d518d1", string.Empty },
+ { "nv!__gl_d518d2", string.Empty },
+ { "nv!__gl_d518d3", string.Empty },
+ { "nv!__gl_d518d4", string.Empty },
+ { "nv!__gl_d518d5", string.Empty },
+ { "nv!__gl_d59eda", string.Empty },
+ { "nv!__gl_d83cbd", string.Empty },
+ { "nv!__gl_d8e777", string.Empty },
+ { "nv!__gl_debug_level", string.Empty },
+ { "nv!__gl_debug_mask", string.Empty },
+ { "nv!__gl_debug_options", string.Empty },
+ { "nv!__gl_devshmpageableallocations", string.Empty },
+ { "nv!__gl_df1f9812", string.Empty },
+ { "nv!__gl_df783c", string.Empty },
+ { "nv!__gl_diagenable", string.Empty },
+ { "nv!__gl_disallowcemask", string.Empty },
+ { "nv!__gl_disallowz16", string.Empty },
+ { "nv!__gl_dlmemoryspaceenables", string.Empty },
+ { "nv!__gl_e0bfec", string.Empty },
+ { "nv!__gl_e433456d", string.Empty },
+ { "nv!__gl_e435563f", string.Empty },
+ { "nv!__gl_e4cd9c", string.Empty },
+ { "nv!__gl_e5c972", string.Empty },
+ { "nv!__gl_e639ef", string.Empty },
+ { "nv!__gl_e802af", string.Empty },
+ { "nv!__gl_eae964", string.Empty },
+ { "nv!__gl_earlytexturehwallocation", string.Empty },
+ { "nv!__gl_eb92a3", string.Empty },
+ { "nv!__gl_ebca56", string.Empty },
+ { "nv!__gl_expert_detail_level", string.Empty },
+ { "nv!__gl_expert_output_mask", string.Empty },
+ { "nv!__gl_expert_report_mask", string.Empty },
+ { "nv!__gl_extensionstringnvarch", string.Empty },
+ { "nv!__gl_extensionstringversion", string.Empty },
+ { "nv!__gl_f00f1938", string.Empty },
+ { "nv!__gl_f10736", string.Empty },
+ { "nv!__gl_f1846870", string.Empty },
+ { "nv!__gl_f33bc370", string.Empty },
+ { "nv!__gl_f392a874", string.Empty },
+ { "nv!__gl_f49ae8", string.Empty },
+ { "nv!__gl_fa345cce", string.Empty },
+ { "nv!__gl_fa35cc4", string.Empty },
+ { "nv!__gl_faa14a", string.Empty },
+ { "nv!__gl_faf8a723", string.Empty },
+ { "nv!__gl_fastgs", string.Empty },
+ { "nv!__gl_fbf4ac45", string.Empty },
+ { "nv!__gl_fbo_blit_ignore_srgb", string.Empty },
+ { "nv!__gl_fc64c7", string.Empty },
+ { "nv!__gl_ff54ec97", string.Empty },
+ { "nv!__gl_ff54ec98", string.Empty },
+ { "nv!__gl_forceexitprocessdetach", string.Empty },
+ { "nv!__gl_forcerequestedesversion", string.Empty },
+ { "nv!__gl_glsynctovblank", string.Empty },
+ { "nv!__gl_gvitimeoutcontrol", string.Empty },
+ { "nv!__gl_hcctrl", string.Empty },
+ { "nv!__gl_hwstate_per_ctx", string.Empty },
+ { "nv!__gl_machinecachelimit", string.Empty },
+ { "nv!__gl_maxframesallowed", string.Empty },
+ { "nv!__gl_memmgrcachedalloclimit", string.Empty },
+ { "nv!__gl_memmgrcachedalloclimitratio", string.Empty },
+ { "nv!__gl_memmgrsysheapalloclimit", string.Empty },
+ { "nv!__gl_memmgrsysheapalloclimitratio", string.Empty },
+ { "nv!__gl_memmgrvidheapalloclimit", string.Empty },
+ { "nv!__gl_mosaic_clip_to_subdev", string.Empty },
+ { "nv!__gl_mosaic_clip_to_subdev_h_overlap", string.Empty },
+ { "nv!__gl_mosaic_clip_to_subdev_v_overlap", string.Empty },
+ { "nv!__gl_overlaymergeblittimerms", string.Empty },
+ { "nv!__gl_perfmon_mode", string.Empty },
+ { "nv!__gl_pixbar_mode", string.Empty },
+ { "nv!__gl_qualityenhancements", string.Empty },
+ { "nv!__gl_r27s18q28", string.Empty },
+ { "nv!__gl_r2d7c1d8", string.Empty },
+ { "nv!__gl_renderer", string.Empty },
+ { "nv!__gl_renderqualityflags", string.Empty },
+ { "nv!__gl_s3tcquality", string.Empty },
+ { "nv!__gl_shaderatomics", string.Empty },
+ { "nv!__gl_shadercacheinitsize", string.Empty },
+ { "nv!__gl_shader_disk_cache_path", string.Empty },
+ { "nv!__gl_shader_disk_cache_read_only", string.Empty },
+ { "nv!__gl_shaderobjects", string.Empty },
+ { "nv!__gl_shaderportabilitywarnings", string.Empty },
+ { "nv!__gl_shaderwarningsaserrors", string.Empty },
+ { "nv!__gl_skiptexturehostcopies", string.Empty },
+ { "nv!__glslc_debug_level", string.Empty },
+ { "nv!__glslc_debug_mask", string.Empty },
+ { "nv!__glslc_debug_options", string.Empty },
+ { "nv!__glslc_debug_filename", string.Empty },
+ { "nv!__gl_sli_dli_control", string.Empty },
+ { "nv!__gl_sparsetexture", string.Empty },
+ { "nv!__gl_spinlooptimeout", string.Empty },
+ { "nv!__gl_sync_to_vblank", string.Empty },
+ { "nv!glsynctovblank", string.Empty },
+ { "nv!__gl_sysheapreuseratio", string.Empty },
+ { "nv!__gl_sysmemtexturepromotion", string.Empty },
+ { "nv!__gl_targetflushcount", string.Empty },
+ { "nv!__gl_tearingfreeswappresent", string.Empty },
+ { "nv!__gl_texclampbehavior", string.Empty },
+ { "nv!__gl_texlodbias", string.Empty },
+ { "nv!__gl_texmemoryspaceenables", string.Empty },
+ { "nv!__gl_textureprecache", string.Empty },
+ { "nv!__gl_threadcontrol", string.Empty },
+ { "nv!__gl_threadcontrol2", string.Empty },
+ { "nv!__gl_usegvievents", string.Empty },
+ { "nv!__gl_vbomemoryspaceenables", string.Empty },
+ { "nv!__gl_vertexlimit", string.Empty },
+ { "nv!__gl_vidheapreuseratio", string.Empty },
+ { "nv!__gl_vpipe", string.Empty },
+ { "nv!__gl_vpipeformatbloatlimit", string.Empty },
+ { "nv!__gl_wglmessageboxonabort", string.Empty },
+ { "nv!__gl_writeinfolog", string.Empty },
+ { "nv!__gl_writeprogramobjectassembly", string.Empty },
+ { "nv!__gl_writeprogramobjectsource", string.Empty },
+ { "nv!__gl_xnvadapterpresent", string.Empty },
+ { "nv!__gl_yield", string.Empty },
+ { "nv!__gl_yieldfunction", string.Empty },
+ { "nv!__gl_yieldfunctionfast", string.Empty },
+ { "nv!__gl_yieldfunctionslow", string.Empty },
+ { "nv!__gl_yieldfunctionwaitfordcqueue", string.Empty },
+ { "nv!__gl_yieldfunctionwaitforframe", string.Empty },
+ { "nv!__gl_yieldfunctionwaitforgpu", string.Empty },
+ { "nv!__gl_zbctableaddhysteresis", string.Empty },
+ { "nv!gpu_debug_mode", string.Empty },
+ { "nv!gpu_stay_on", string.Empty },
+ { "nv!gpu_timeout_ms_max", string.Empty },
+ { "nv!gvitimeoutcontrol", string.Empty },
+ { "nv!hcctrl", string.Empty },
+ { "nv!hwstate_per_ctx", string.Empty },
+ { "nv!libandroid_enable_log", string.Empty },
+ { "nv!machinecachelimit", string.Empty },
+ { "nv!maxframesallowed", string.Empty },
+ { "nv!media.aac_51_output_enabled", string.Empty },
+ { "nv!memmgrcachedalloclimit", string.Empty },
+ { "nv!memmgrcachedalloclimitratio", string.Empty },
+ { "nv!memmgrsysheapalloclimit", string.Empty },
+ { "nv!memmgrsysheapalloclimitratio", string.Empty },
+ { "nv!memmgrvidheapalloclimit", string.Empty },
+ { "nv!mosaic_clip_to_subdev", string.Empty },
+ { "nv!mosaic_clip_to_subdev_h_overlap", string.Empty },
+ { "nv!mosaic_clip_to_subdev_v_overlap", string.Empty },
+ { "nv!nvblit.dump", string.Empty },
+ { "nv!nvblit.profile", string.Empty },
+ { "nv!nvblit.twod", string.Empty },
+ { "nv!nvblit.vic", string.Empty },
+ { "nv!nvddk_vic_prevent_use", string.Empty },
+ { "nv!nv_decompression", string.Empty },
{ "nv!nvdisp_bl_ctrl", "0" },
- { "nv!nvdisp_debug_mask", "" },
+ { "nv!nvdisp_debug_mask", string.Empty },
{ "nv!nvdisp_enable_ts", "0" },
{ "nv!nvhdcp_timeout_ms", "12000" },
{ "nv!nvhdcp_max_retries", "5" },
- { "nv!nv_emc_dvfs_test", "" },
- { "nv!nv_emc_init_rate_hz", "" },
- { "nv!nv_gmmu_va_page_split", "" },
- { "nv!nv_gmmu_va_range", "" },
- { "nv!nvhost_debug_mask", "" },
- { "nv!nvidia.hwc.dump_config", "" },
- { "nv!nvidia.hwc.dump_layerlist", "" },
- { "nv!nvidia.hwc.dump_windows", "" },
- { "nv!nvidia.hwc.enable_disp_trans", "" },
- { "nv!nvidia.hwc.ftrace_enable", "" },
- { "nv!nvidia.hwc.hdcp_enable", "" },
- { "nv!nvidia.hwc.hidden_window_mask0", "" },
- { "nv!nvidia.hwc.hidden_window_mask1", "" },
- { "nv!nvidia.hwc.immediate_modeset", "" },
- { "nv!nvidia.hwc.imp_enable", "" },
- { "nv!nvidia.hwc.no_egl", "" },
- { "nv!nvidia.hwc.no_scratchblit", "" },
- { "nv!nvidia.hwc.no_vic", "" },
- { "nv!nvidia.hwc.null_display", "" },
- { "nv!nvidia.hwc.scan_props", "" },
- { "nv!nvidia.hwc.swap_interval", "" },
+ { "nv!nv_emc_dvfs_test", string.Empty },
+ { "nv!nv_emc_init_rate_hz", string.Empty },
+ { "nv!nv_gmmu_va_page_split", string.Empty },
+ { "nv!nv_gmmu_va_range", string.Empty },
+ { "nv!nvhost_debug_mask", string.Empty },
+ { "nv!nvidia.hwc.dump_config", string.Empty },
+ { "nv!nvidia.hwc.dump_layerlist", string.Empty },
+ { "nv!nvidia.hwc.dump_windows", string.Empty },
+ { "nv!nvidia.hwc.enable_disp_trans", string.Empty },
+ { "nv!nvidia.hwc.ftrace_enable", string.Empty },
+ { "nv!nvidia.hwc.hdcp_enable", string.Empty },
+ { "nv!nvidia.hwc.hidden_window_mask0", string.Empty },
+ { "nv!nvidia.hwc.hidden_window_mask1", string.Empty },
+ { "nv!nvidia.hwc.immediate_modeset", string.Empty },
+ { "nv!nvidia.hwc.imp_enable", string.Empty },
+ { "nv!nvidia.hwc.no_egl", string.Empty },
+ { "nv!nvidia.hwc.no_scratchblit", string.Empty },
+ { "nv!nvidia.hwc.no_vic", string.Empty },
+ { "nv!nvidia.hwc.null_display", string.Empty },
+ { "nv!nvidia.hwc.scan_props", string.Empty },
+ { "nv!nvidia.hwc.swap_interval", string.Empty },
{ "nv!nvidia.hwc.war_1515812", "0" },
- { "nv!nvmap_debug_mask", "" },
- { "nv!nv_memory_profiler", "" },
- { "nv!nvnflinger_enable_log", "" },
- { "nv!nvnflinger_flip_policy", "" },
+ { "nv!nvmap_debug_mask", string.Empty },
+ { "nv!nv_memory_profiler", string.Empty },
+ { "nv!nvnflinger_enable_log", string.Empty },
+ { "nv!nvnflinger_flip_policy", string.Empty },
{ "nv!nvnflinger_hotplug_autoswitch", "0" },
{ "nv!nvnflinger_prefer_primary_layer", "0" },
- { "nv!nvnflinger_service_priority", "" },
- { "nv!nvnflinger_service_threads", "" },
- { "nv!nvnflinger_swap_interval", "" },
- { "nv!nvnflinger_track_perf", "" },
+ { "nv!nvnflinger_service_priority", string.Empty },
+ { "nv!nvnflinger_service_threads", string.Empty },
+ { "nv!nvnflinger_swap_interval", string.Empty },
+ { "nv!nvnflinger_track_perf", string.Empty },
{ "nv!nvnflinger_virtualdisplay_policy", "60hz" },
{ "nv!nvn_no_vsync_capability", false },
- { "nv!nvn_through_opengl", "" },
- { "nv!nv_pllcx_always_on", "" },
- { "nv!nv_pllcx_safe_div", "" },
- { "nv!nvrm_gpu_channel_interleave", "" },
- { "nv!nvrm_gpu_channel_priority", "" },
- { "nv!nvrm_gpu_channel_timeslice", "" },
- { "nv!nvrm_gpu_default_device_index", "" },
- { "nv!nvrm_gpu_dummy", "" },
- { "nv!nvrm_gpu_help", "" },
- { "nv!nvrm_gpu_nvgpu_disable", "" },
- { "nv!nvrm_gpu_nvgpu_do_nfa_partial_map", "" },
- { "nv!nvrm_gpu_nvgpu_ecc_overrides", "" },
- { "nv!nvrm_gpu_nvgpu_no_as_get_va_regions", "" },
- { "nv!nvrm_gpu_nvgpu_no_channel_abort", "" },
- { "nv!nvrm_gpu_nvgpu_no_cyclestats", "" },
- { "nv!nvrm_gpu_nvgpu_no_fixed", "" },
- { "nv!nvrm_gpu_nvgpu_no_gpu_characteristics", "" },
- { "nv!nvrm_gpu_nvgpu_no_ioctl_mutex", "" },
- { "nv!nvrm_gpu_nvgpu_no_map_buffer_ex", "" },
- { "nv!nvrm_gpu_nvgpu_no_robustness", "" },
- { "nv!nvrm_gpu_nvgpu_no_sparse", "" },
- { "nv!nvrm_gpu_nvgpu_no_syncpoints", "" },
- { "nv!nvrm_gpu_nvgpu_no_tsg", "" },
- { "nv!nvrm_gpu_nvgpu_no_zbc", "" },
- { "nv!nvrm_gpu_nvgpu_no_zcull", "" },
- { "nv!nvrm_gpu_nvgpu_wrap_channels_in_tsgs", "" },
- { "nv!nvrm_gpu_prevent_use", "" },
- { "nv!nvrm_gpu_trace", "" },
- { "nv!nvsched_debug_mask", "" },
- { "nv!nvsched_force_enable", "" },
- { "nv!nvsched_force_log", "" },
- { "nv!nv_usb_plls_hw_ctrl", "" },
- { "nv!nv_winsys", "" },
- { "nv!nvwsi_dump", "" },
- { "nv!nvwsi_fill", "" },
- { "nv!ogl_", "" },
- { "nv!ogl_0356afd0", "" },
- { "nv!ogl_0356afd1", "" },
- { "nv!ogl_0356afd2", "" },
- { "nv!ogl_0356afd3", "" },
- { "nv!ogl_0x923dc0", "" },
- { "nv!ogl_0x923dc1", "" },
- { "nv!ogl_0x923dc2", "" },
- { "nv!ogl_0x923dc3", "" },
- { "nv!ogl_0x923dc4", "" },
- { "nv!ogl_0x923dd3", "" },
- { "nv!ogl_0x9abdc5", "" },
- { "nv!ogl_0x9abdc6", "" },
- { "nv!ogl_0xbd10fb", "" },
- { "nv!ogl_0xce2348", "" },
- { "nv!ogl_10261989", "" },
- { "nv!ogl_1042d483", "" },
- { "nv!ogl_10572898", "" },
- { "nv!ogl_115631", "" },
- { "nv!ogl_12950094", "" },
- { "nv!ogl_1314f311", "" },
- { "nv!ogl_1314f312", "" },
- { "nv!ogl_13279512", "" },
- { "nv!ogl_13813496", "" },
- { "nv!ogl_14507179", "" },
- { "nv!ogl_15694569", "" },
- { "nv!ogl_16936964", "" },
- { "nv!ogl_17aa230c", "" },
- { "nv!ogl_182054", "" },
- { "nv!ogl_18273275", "" },
- { "nv!ogl_18273276", "" },
- { "nv!ogl_1854d03b", "" },
- { "nv!ogl_18add00d", "" },
- { "nv!ogl_19156670", "" },
- { "nv!ogl_19286545", "" },
- { "nv!ogl_1a298e9f", "" },
- { "nv!ogl_1acf43fe", "" },
- { "nv!ogl_1bda43fe", "" },
- { "nv!ogl_1c3b92", "" },
- { "nv!ogl_21509920", "" },
- { "nv!ogl_215323457", "" },
- { "nv!ogl_2165ad", "" },
- { "nv!ogl_2165ae", "" },
- { "nv!ogl_21be9c", "" },
- { "nv!ogl_233264316", "" },
- { "nv!ogl_234557580", "" },
- { "nv!ogl_23cd0e", "" },
- { "nv!ogl_24189123", "" },
- { "nv!ogl_2443266", "" },
- { "nv!ogl_25025519", "" },
- { "nv!ogl_255e39", "" },
- { "nv!ogl_2583364", "" },
- { "nv!ogl_2888c1", "" },
- { "nv!ogl_28ca3e", "" },
- { "nv!ogl_29871243", "" },
- { "nv!ogl_2a1f64", "" },
- { "nv!ogl_2dc432", "" },
- { "nv!ogl_2de437", "" },
- { "nv!ogl_2f3bb89c", "" },
- { "nv!ogl_2fd652", "" },
- { "nv!ogl_3001ac", "" },
- { "nv!ogl_31298772", "" },
- { "nv!ogl_313233", "" },
- { "nv!ogl_31f7d603", "" },
- { "nv!ogl_320ce4", "" },
- { "nv!ogl_32153248", "" },
- { "nv!ogl_32153249", "" },
- { "nv!ogl_335bca", "" },
- { "nv!ogl_342abb", "" },
- { "nv!ogl_34dfe6", "" },
- { "nv!ogl_34dfe7", "" },
- { "nv!ogl_34dfe8", "" },
- { "nv!ogl_34dfe9", "" },
- { "nv!ogl_35201578", "" },
- { "nv!ogl_359278", "" },
- { "nv!ogl_37f53a", "" },
- { "nv!ogl_38144972", "" },
- { "nv!ogl_38542646", "" },
- { "nv!ogl_3b74c9", "" },
- { "nv!ogl_3c136f", "" },
- { "nv!ogl_3cf72823", "" },
- { "nv!ogl_3d7af029", "" },
- { "nv!ogl_3ff34782", "" },
- { "nv!ogl_4129618", "" },
- { "nv!ogl_4189fac3", "" },
- { "nv!ogl_420bd4", "" },
- { "nv!ogl_42a699", "" },
- { "nv!ogl_441369", "" },
- { "nv!ogl_4458713e", "" },
- { "nv!ogl_4554b6", "" },
- { "nv!ogl_457425", "" },
- { "nv!ogl_4603b207", "" },
- { "nv!ogl_46574957", "" },
- { "nv!ogl_46574958", "" },
- { "nv!ogl_46813529", "" },
- { "nv!ogl_46f1e13d", "" },
- { "nv!ogl_47534c43", "" },
- { "nv!ogl_48550336", "" },
- { "nv!ogl_48576893", "" },
- { "nv!ogl_48576894", "" },
- { "nv!ogl_4889ac02", "" },
- { "nv!ogl_49005740", "" },
- { "nv!ogl_49867584", "" },
- { "nv!ogl_49960973", "" },
- { "nv!ogl_4a5341", "" },
- { "nv!ogl_4f4e48", "" },
- { "nv!ogl_4f8a0a", "" },
- { "nv!ogl_50299698", "" },
- { "nv!ogl_50299699", "" },
- { "nv!ogl_50361291", "" },
- { "nv!ogl_5242ae", "" },
- { "nv!ogl_53d30c", "" },
- { "nv!ogl_56347a", "" },
- { "nv!ogl_563a95f1", "" },
- { "nv!ogl_573823", "" },
- { "nv!ogl_58027529", "" },
- { "nv!ogl_5d2d63", "" },
- { "nv!ogl_5f7e3b", "" },
- { "nv!ogl_60461793", "" },
- { "nv!ogl_60d355", "" },
- { "nv!ogl_616627aa", "" },
- { "nv!ogl_62317182", "" },
- { "nv!ogl_6253fa2e", "" },
- { "nv!ogl_64100768", "" },
- { "nv!ogl_64100769", "" },
- { "nv!ogl_64100770", "" },
- { "nv!ogl_647395", "" },
- { "nv!ogl_66543234", "" },
- { "nv!ogl_67674763", "" },
- { "nv!ogl_67739784", "" },
- { "nv!ogl_68fb9c", "" },
- { "nv!ogl_69801276", "" },
- { "nv!ogl_6af9fa2f", "" },
- { "nv!ogl_6af9fa3f", "" },
- { "nv!ogl_6af9fa4f", "" },
- { "nv!ogl_6bd8c7", "" },
- { "nv!ogl_6c7691", "" },
- { "nv!ogl_6d4296ce", "" },
- { "nv!ogl_6dd7e7", "" },
- { "nv!ogl_6dd7e8", "" },
- { "nv!ogl_6fe11ec1", "" },
- { "nv!ogl_716511763", "" },
- { "nv!ogl_72504593", "" },
- { "nv!ogl_73304097", "" },
- { "nv!ogl_73314098", "" },
- { "nv!ogl_74095213", "" },
- { "nv!ogl_74095213a", "" },
- { "nv!ogl_74095213b", "" },
- { "nv!ogl_74095214", "" },
- { "nv!ogl_748f9649", "" },
- { "nv!ogl_75494732", "" },
- { "nv!ogl_78452832", "" },
- { "nv!ogl_784561", "" },
- { "nv!ogl_78e16b9c", "" },
- { "nv!ogl_79251225", "" },
- { "nv!ogl_7c128b", "" },
- { "nv!ogl_7ccd93", "" },
- { "nv!ogl_7df8d1", "" },
- { "nv!ogl_800c2310", "" },
- { "nv!ogl_80546710", "" },
- { "nv!ogl_80772310", "" },
- { "nv!ogl_808ee280", "" },
- { "nv!ogl_81131154", "" },
- { "nv!ogl_81274457", "" },
- { "nv!ogl_8292291f", "" },
- { "nv!ogl_83498426", "" },
- { "nv!ogl_84993794", "" },
- { "nv!ogl_84995585", "" },
- { "nv!ogl_84a0a0", "" },
- { "nv!ogl_852142", "" },
- { "nv!ogl_85612309", "" },
- { "nv!ogl_85612310", "" },
- { "nv!ogl_85612311", "" },
- { "nv!ogl_85612312", "" },
- { "nv!ogl_8623ff27", "" },
- { "nv!ogl_87364952", "" },
- { "nv!ogl_87f6275666", "" },
- { "nv!ogl_886748", "" },
- { "nv!ogl_89894423", "" },
- { "nv!ogl_8ad8a75", "" },
- { "nv!ogl_8ad8ad00", "" },
- { "nv!ogl_8bb815", "" },
- { "nv!ogl_8bb817", "" },
- { "nv!ogl_8bb818", "" },
- { "nv!ogl_8bb819", "" },
- { "nv!ogl_8e640cd1", "" },
- { "nv!ogl_8f34971a", "" },
- { "nv!ogl_8f773984", "" },
- { "nv!ogl_8f7a7d", "" },
- { "nv!ogl_902486209", "" },
- { "nv!ogl_90482571", "" },
- { "nv!ogl_91214835", "" },
- { "nv!ogl_912848290", "" },
- { "nv!ogl_915e56", "" },
- { "nv!ogl_92179063", "" },
- { "nv!ogl_92179064", "" },
- { "nv!ogl_92179065", "" },
- { "nv!ogl_92179066", "" },
- { "nv!ogl_92350358", "" },
- { "nv!ogl_92809063", "" },
- { "nv!ogl_92809064", "" },
- { "nv!ogl_92809065", "" },
- { "nv!ogl_92809066", "" },
- { "nv!ogl_92920143", "" },
- { "nv!ogl_93a89b12", "" },
- { "nv!ogl_93a89c0b", "" },
- { "nv!ogl_94812574", "" },
- { "nv!ogl_95282304", "" },
- { "nv!ogl_95394027", "" },
- { "nv!ogl_959b1f", "" },
- { "nv!ogl_9638af", "" },
- { "nv!ogl_96fd59", "" },
- { "nv!ogl_97f6275666", "" },
- { "nv!ogl_97f6275667", "" },
- { "nv!ogl_97f6275668", "" },
- { "nv!ogl_97f6275669", "" },
- { "nv!ogl_97f627566a", "" },
- { "nv!ogl_97f627566b", "" },
- { "nv!ogl_97f627566d", "" },
- { "nv!ogl_97f627566e", "" },
- { "nv!ogl_97f627566f", "" },
- { "nv!ogl_97f6275670", "" },
- { "nv!ogl_97f6275671", "" },
- { "nv!ogl_97f727566e", "" },
- { "nv!ogl_98480775", "" },
- { "nv!ogl_98480776", "" },
- { "nv!ogl_98480777", "" },
- { "nv!ogl_992431", "" },
- { "nv!ogl_9aa29065", "" },
- { "nv!ogl_9af32c", "" },
- { "nv!ogl_9af32d", "" },
- { "nv!ogl_9af32e", "" },
- { "nv!ogl_9c108b71", "" },
- { "nv!ogl_9f279065", "" },
- { "nv!ogl_a01bc728", "" },
- { "nv!ogl_a13b46c80", "" },
- { "nv!ogl_a22eb0", "" },
- { "nv!ogl_a2fb451e", "" },
- { "nv!ogl_a3456abe", "" },
- { "nv!ogl_a7044887", "" },
- { "nv!ogl_a7149200", "" },
- { "nv!ogl_a766215670", "" },
- { "nv!ogl_aalinegamma", "" },
- { "nv!ogl_aalinetweaks", "" },
- { "nv!ogl_ab34ee01", "" },
- { "nv!ogl_ab34ee02", "" },
- { "nv!ogl_ab34ee03", "" },
- { "nv!ogl_ac0274", "" },
- { "nv!ogl_af73c63e", "" },
- { "nv!ogl_af73c63f", "" },
- { "nv!ogl_af9927", "" },
- { "nv!ogl_afoverride", "" },
- { "nv!ogl_allocdeviceevents", "" },
- { "nv!ogl_applicationkey", "" },
- { "nv!ogl_appreturnonlybasicglsltype", "" },
- { "nv!ogl_app_softimage", "" },
- { "nv!ogl_app_supportbits2", "" },
- { "nv!ogl_assumetextureismipmappedatcreation", "" },
- { "nv!ogl_b1fb0f01", "" },
- { "nv!ogl_b3edd5", "" },
- { "nv!ogl_b40d9e03d", "" },
- { "nv!ogl_b7f6275666", "" },
- { "nv!ogl_b812c1", "" },
- { "nv!ogl_ba14ba1a", "" },
- { "nv!ogl_ba14ba1b", "" },
- { "nv!ogl_bd7559", "" },
- { "nv!ogl_bd755a", "" },
- { "nv!ogl_bd755c", "" },
- { "nv!ogl_bd755d", "" },
- { "nv!ogl_be58bb", "" },
- { "nv!ogl_be92cb", "" },
- { "nv!ogl_beefcba3", "" },
- { "nv!ogl_beefcba4", "" },
- { "nv!ogl_c023777f", "" },
- { "nv!ogl_c09dc8", "" },
- { "nv!ogl_c0d340", "" },
- { "nv!ogl_c2ff374c", "" },
- { "nv!ogl_c5e9d7a3", "" },
- { "nv!ogl_c5e9d7a4", "" },
- { "nv!ogl_c5e9d7b4", "" },
- { "nv!ogl_c618f9", "" },
- { "nv!ogl_ca345840", "" },
- { "nv!ogl_cachedisable", "" },
- { "nv!ogl_channelpriorityoverride", "" },
- { "nv!ogl_cleardatastorevidmem", "" },
- { "nv!ogl_cmdbufmemoryspaceenables", "" },
- { "nv!ogl_cmdbufminwords", "" },
- { "nv!ogl_cmdbufsizewords", "" },
- { "nv!ogl_conformantblitframebufferscissor", "" },
- { "nv!ogl_conformantincompletetextures", "" },
- { "nv!ogl_copybuffermethod", "" },
- { "nv!ogl_cubemapaniso", "" },
- { "nv!ogl_cubemapfiltering", "" },
- { "nv!ogl_d0e9a4d7", "" },
- { "nv!ogl_d13733f12", "" },
- { "nv!ogl_d1b399", "" },
- { "nv!ogl_d2983c32", "" },
- { "nv!ogl_d2983c33", "" },
- { "nv!ogl_d2e71b", "" },
- { "nv!ogl_d377dc", "" },
- { "nv!ogl_d377dd", "" },
- { "nv!ogl_d489f4", "" },
- { "nv!ogl_d4bce1", "" },
- { "nv!ogl_d518cb", "" },
- { "nv!ogl_d518cd", "" },
- { "nv!ogl_d518ce", "" },
- { "nv!ogl_d518d0", "" },
- { "nv!ogl_d518d1", "" },
- { "nv!ogl_d518d2", "" },
- { "nv!ogl_d518d3", "" },
- { "nv!ogl_d518d4", "" },
- { "nv!ogl_d518d5", "" },
- { "nv!ogl_d59eda", "" },
- { "nv!ogl_d83cbd", "" },
- { "nv!ogl_d8e777", "" },
- { "nv!ogl_debug_level", "" },
- { "nv!ogl_debug_mask", "" },
- { "nv!ogl_debug_options", "" },
- { "nv!ogl_devshmpageableallocations", "" },
- { "nv!ogl_df1f9812", "" },
- { "nv!ogl_df783c", "" },
- { "nv!ogl_diagenable", "" },
- { "nv!ogl_disallowcemask", "" },
- { "nv!ogl_disallowz16", "" },
- { "nv!ogl_dlmemoryspaceenables", "" },
- { "nv!ogl_e0bfec", "" },
- { "nv!ogl_e433456d", "" },
- { "nv!ogl_e435563f", "" },
- { "nv!ogl_e4cd9c", "" },
- { "nv!ogl_e5c972", "" },
- { "nv!ogl_e639ef", "" },
- { "nv!ogl_e802af", "" },
- { "nv!ogl_eae964", "" },
- { "nv!ogl_earlytexturehwallocation", "" },
- { "nv!ogl_eb92a3", "" },
- { "nv!ogl_ebca56", "" },
- { "nv!ogl_expert_detail_level", "" },
- { "nv!ogl_expert_output_mask", "" },
- { "nv!ogl_expert_report_mask", "" },
- { "nv!ogl_extensionstringnvarch", "" },
- { "nv!ogl_extensionstringversion", "" },
- { "nv!ogl_f00f1938", "" },
- { "nv!ogl_f10736", "" },
- { "nv!ogl_f1846870", "" },
- { "nv!ogl_f33bc370", "" },
- { "nv!ogl_f392a874", "" },
- { "nv!ogl_f49ae8", "" },
- { "nv!ogl_fa345cce", "" },
- { "nv!ogl_fa35cc4", "" },
- { "nv!ogl_faa14a", "" },
- { "nv!ogl_faf8a723", "" },
- { "nv!ogl_fastgs", "" },
- { "nv!ogl_fbf4ac45", "" },
- { "nv!ogl_fbo_blit_ignore_srgb", "" },
- { "nv!ogl_fc64c7", "" },
- { "nv!ogl_ff54ec97", "" },
- { "nv!ogl_ff54ec98", "" },
- { "nv!ogl_forceexitprocessdetach", "" },
- { "nv!ogl_forcerequestedesversion", "" },
- { "nv!ogl_glsynctovblank", "" },
- { "nv!ogl_gvitimeoutcontrol", "" },
- { "nv!ogl_hcctrl", "" },
- { "nv!ogl_hwstate_per_ctx", "" },
- { "nv!ogl_machinecachelimit", "" },
- { "nv!ogl_maxframesallowed", "" },
- { "nv!ogl_memmgrcachedalloclimit", "" },
- { "nv!ogl_memmgrcachedalloclimitratio", "" },
- { "nv!ogl_memmgrsysheapalloclimit", "" },
- { "nv!ogl_memmgrsysheapalloclimitratio", "" },
- { "nv!ogl_memmgrvidheapalloclimit", "" },
- { "nv!ogl_mosaic_clip_to_subdev", "" },
- { "nv!ogl_mosaic_clip_to_subdev_h_overlap", "" },
- { "nv!ogl_mosaic_clip_to_subdev_v_overlap", "" },
- { "nv!ogl_overlaymergeblittimerms", "" },
- { "nv!ogl_perfmon_mode", "" },
- { "nv!ogl_pixbar_mode", "" },
- { "nv!ogl_qualityenhancements", "" },
- { "nv!ogl_r27s18q28", "" },
- { "nv!ogl_r2d7c1d8", "" },
- { "nv!ogl_renderer", "" },
- { "nv!ogl_renderqualityflags", "" },
- { "nv!ogl_s3tcquality", "" },
- { "nv!ogl_shaderatomics", "" },
- { "nv!ogl_shadercacheinitsize", "" },
- { "nv!ogl_shader_disk_cache_path", "" },
- { "nv!ogl_shader_disk_cache_read_only", "" },
- { "nv!ogl_shaderobjects", "" },
- { "nv!ogl_shaderportabilitywarnings", "" },
- { "nv!ogl_shaderwarningsaserrors", "" },
- { "nv!ogl_skiptexturehostcopies", "" },
- { "nv!ogl_sli_dli_control", "" },
- { "nv!ogl_sparsetexture", "" },
- { "nv!ogl_spinlooptimeout", "" },
- { "nv!ogl_sync_to_vblank", "" },
- { "nv!ogl_sysheapreuseratio", "" },
- { "nv!ogl_sysmemtexturepromotion", "" },
- { "nv!ogl_targetflushcount", "" },
- { "nv!ogl_tearingfreeswappresent", "" },
- { "nv!ogl_texclampbehavior", "" },
- { "nv!ogl_texlodbias", "" },
- { "nv!ogl_texmemoryspaceenables", "" },
- { "nv!ogl_textureprecache", "" },
- { "nv!ogl_threadcontrol", "" },
- { "nv!ogl_threadcontrol2", "" },
- { "nv!ogl_usegvievents", "" },
- { "nv!ogl_vbomemoryspaceenables", "" },
- { "nv!ogl_vertexlimit", "" },
- { "nv!ogl_vidheapreuseratio", "" },
- { "nv!ogl_vpipe", "" },
- { "nv!ogl_vpipeformatbloatlimit", "" },
- { "nv!ogl_wglmessageboxonabort", "" },
- { "nv!ogl_writeinfolog", "" },
- { "nv!ogl_writeprogramobjectassembly", "" },
- { "nv!ogl_writeprogramobjectsource", "" },
- { "nv!ogl_xnvadapterpresent", "" },
- { "nv!ogl_yield", "" },
- { "nv!ogl_yieldfunction", "" },
- { "nv!ogl_yieldfunctionfast", "" },
- { "nv!ogl_yieldfunctionslow", "" },
- { "nv!ogl_yieldfunctionwaitfordcqueue", "" },
- { "nv!ogl_yieldfunctionwaitforframe", "" },
- { "nv!ogl_yieldfunctionwaitforgpu", "" },
- { "nv!ogl_zbctableaddhysteresis", "" },
- { "nv!overlaymergeblittimerms", "" },
- { "nv!perfmon_mode", "" },
- { "nv!persist.sys.display.resolution", "" },
- { "nv!persist.tegra.composite.fallb", "" },
- { "nv!persist.tegra.composite.policy", "" },
- { "nv!persist.tegra.composite.range", "" },
- { "nv!persist.tegra.compositor", "" },
- { "nv!persist.tegra.compositor.virt", "" },
- { "nv!persist.tegra.compression", "" },
- { "nv!persist.tegra.cursor.enable", "" },
- { "nv!persist.tegra.didim.enable", "" },
- { "nv!persist.tegra.didim.normal", "" },
- { "nv!persist.tegra.didim.video", "" },
- { "nv!persist.tegra.disp.heads", "" },
- { "nv!persist.tegra.gamma_correction", "" },
- { "nv!persist.tegra.gpu_mapping_cache", "" },
- { "nv!persist.tegra.grlayout", "" },
- { "nv!persist.tegra.hdmi.2020.10", "" },
- { "nv!persist.tegra.hdmi.2020.fake", "" },
- { "nv!persist.tegra.hdmi.2020.force", "" },
- { "nv!persist.tegra.hdmi.autorotate", "" },
- { "nv!persist.tegra.hdmi.hdr.fake", "" },
- { "nv!persist.tegra.hdmi.ignore_ratio", "" },
- { "nv!persist.tegra.hdmi.limit.clock", "" },
- { "nv!persist.tegra.hdmi.only_16_9", "" },
- { "nv!persist.tegra.hdmi.range", "" },
- { "nv!persist.tegra.hdmi.resolution", "" },
- { "nv!persist.tegra.hdmi.underscan", "" },
- { "nv!persist.tegra.hdmi.yuv.422", "" },
- { "nv!persist.tegra.hdmi.yuv.444", "" },
- { "nv!persist.tegra.hdmi.yuv.enable", "" },
- { "nv!persist.tegra.hdmi.yuv.force", "" },
- { "nv!persist.tegra.hwc.nvdc", "" },
- { "nv!persist.tegra.idle.minimum_fps", "" },
- { "nv!persist.tegra.panel.rotation", "" },
- { "nv!persist.tegra.scan_props", "" },
- { "nv!persist.tegra.stb.mode", "" },
- { "nv!persist.tegra.zbc_override", "" },
- { "nv!pixbar_mode", "" },
- { "nv!qualityenhancements", "" },
- { "nv!r27s18q28", "" },
- { "nv!r2d7c1d8", "" },
- { "nv!renderer", "" },
- { "nv!renderqualityflags", "" },
- { "nv!rmos_debug_mask", "" },
- { "nv!rmos_set_production_mode", "" },
- { "nv!s3tcquality", "" },
- { "nv!shaderatomics", "" },
- { "nv!shadercacheinitsize", "" },
- { "nv!shader_disk_cache_path", "" },
- { "nv!shader_disk_cache_read_only", "" },
- { "nv!shaderobjects", "" },
- { "nv!shaderportabilitywarnings", "" },
- { "nv!shaderwarningsaserrors", "" },
- { "nv!skiptexturehostcopies", "" },
- { "nv!sli_dli_control", "" },
- { "nv!sparsetexture", "" },
- { "nv!spinlooptimeout", "" },
- { "nv!sync_to_vblank", "" },
- { "nv!sysheapreuseratio", "" },
- { "nv!sysmemtexturepromotion", "" },
- { "nv!targetflushcount", "" },
- { "nv!tearingfreeswappresent", "" },
- { "nv!tegra.refresh", "" },
- { "nv!texclampbehavior", "" },
- { "nv!texlodbias", "" },
- { "nv!texmemoryspaceenables", "" },
- { "nv!textureprecache", "" },
- { "nv!threadcontrol", "" },
- { "nv!threadcontrol2", "" },
- { "nv!tvmr.avp.logs", "" },
- { "nv!tvmr.buffer.logs", "" },
- { "nv!tvmr.dec.prof", "" },
- { "nv!tvmr.deint.logs", "" },
- { "nv!tvmr.dfs.logs", "" },
- { "nv!tvmr.ffprof.logs", "" },
- { "nv!tvmr.game.stream", "" },
- { "nv!tvmr.general.logs", "" },
- { "nv!tvmr.input.dump", "" },
- { "nv!tvmr.seeking.logs", "" },
- { "nv!tvmr.ts_pulldown", "" },
- { "nv!usegvievents", "" },
- { "nv!vbomemoryspaceenables", "" },
- { "nv!vcc_debug_ip", "" },
- { "nv!vcc_verbose_level", "" },
- { "nv!vertexlimit", "" },
- { "nv!viccomposer.filter", "" },
- { "nv!videostats-enable", "" },
- { "nv!vidheapreuseratio", "" },
- { "nv!vpipe", "" },
- { "nv!vpipeformatbloatlimit", "" },
- { "nv!wglmessageboxonabort", "" },
- { "nv!writeinfolog", "" },
- { "nv!writeprogramobjectassembly", "" },
- { "nv!writeprogramobjectsource", "" },
- { "nv!xnvadapterpresent", "" },
- { "nv!yield", "" },
- { "nv!yieldfunction", "" },
- { "nv!yieldfunctionfast", "" },
- { "nv!yieldfunctionslow", "" },
- { "nv!yieldfunctionwaitfordcqueue", "" },
- { "nv!yieldfunctionwaitforframe", "" },
- { "nv!yieldfunctionwaitforgpu", "" },
- { "nv!zbctableaddhysteresis", "" },
+ { "nv!nvn_through_opengl", string.Empty },
+ { "nv!nv_pllcx_always_on", string.Empty },
+ { "nv!nv_pllcx_safe_div", string.Empty },
+ { "nv!nvrm_gpu_channel_interleave", string.Empty },
+ { "nv!nvrm_gpu_channel_priority", string.Empty },
+ { "nv!nvrm_gpu_channel_timeslice", string.Empty },
+ { "nv!nvrm_gpu_default_device_index", string.Empty },
+ { "nv!nvrm_gpu_dummy", string.Empty },
+ { "nv!nvrm_gpu_help", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_disable", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_do_nfa_partial_map", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_ecc_overrides", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_as_get_va_regions", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_channel_abort", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_cyclestats", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_fixed", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_gpu_characteristics", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_ioctl_mutex", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_map_buffer_ex", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_robustness", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_sparse", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_syncpoints", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_tsg", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_zbc", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_no_zcull", string.Empty },
+ { "nv!nvrm_gpu_nvgpu_wrap_channels_in_tsgs", string.Empty },
+ { "nv!nvrm_gpu_prevent_use", string.Empty },
+ { "nv!nvrm_gpu_trace", string.Empty },
+ { "nv!nvsched_debug_mask", string.Empty },
+ { "nv!nvsched_force_enable", string.Empty },
+ { "nv!nvsched_force_log", string.Empty },
+ { "nv!nv_usb_plls_hw_ctrl", string.Empty },
+ { "nv!nv_winsys", string.Empty },
+ { "nv!nvwsi_dump", string.Empty },
+ { "nv!nvwsi_fill", string.Empty },
+ { "nv!ogl_", string.Empty },
+ { "nv!ogl_0356afd0", string.Empty },
+ { "nv!ogl_0356afd1", string.Empty },
+ { "nv!ogl_0356afd2", string.Empty },
+ { "nv!ogl_0356afd3", string.Empty },
+ { "nv!ogl_0x923dc0", string.Empty },
+ { "nv!ogl_0x923dc1", string.Empty },
+ { "nv!ogl_0x923dc2", string.Empty },
+ { "nv!ogl_0x923dc3", string.Empty },
+ { "nv!ogl_0x923dc4", string.Empty },
+ { "nv!ogl_0x923dd3", string.Empty },
+ { "nv!ogl_0x9abdc5", string.Empty },
+ { "nv!ogl_0x9abdc6", string.Empty },
+ { "nv!ogl_0xbd10fb", string.Empty },
+ { "nv!ogl_0xce2348", string.Empty },
+ { "nv!ogl_10261989", string.Empty },
+ { "nv!ogl_1042d483", string.Empty },
+ { "nv!ogl_10572898", string.Empty },
+ { "nv!ogl_115631", string.Empty },
+ { "nv!ogl_12950094", string.Empty },
+ { "nv!ogl_1314f311", string.Empty },
+ { "nv!ogl_1314f312", string.Empty },
+ { "nv!ogl_13279512", string.Empty },
+ { "nv!ogl_13813496", string.Empty },
+ { "nv!ogl_14507179", string.Empty },
+ { "nv!ogl_15694569", string.Empty },
+ { "nv!ogl_16936964", string.Empty },
+ { "nv!ogl_17aa230c", string.Empty },
+ { "nv!ogl_182054", string.Empty },
+ { "nv!ogl_18273275", string.Empty },
+ { "nv!ogl_18273276", string.Empty },
+ { "nv!ogl_1854d03b", string.Empty },
+ { "nv!ogl_18add00d", string.Empty },
+ { "nv!ogl_19156670", string.Empty },
+ { "nv!ogl_19286545", string.Empty },
+ { "nv!ogl_1a298e9f", string.Empty },
+ { "nv!ogl_1acf43fe", string.Empty },
+ { "nv!ogl_1bda43fe", string.Empty },
+ { "nv!ogl_1c3b92", string.Empty },
+ { "nv!ogl_21509920", string.Empty },
+ { "nv!ogl_215323457", string.Empty },
+ { "nv!ogl_2165ad", string.Empty },
+ { "nv!ogl_2165ae", string.Empty },
+ { "nv!ogl_21be9c", string.Empty },
+ { "nv!ogl_233264316", string.Empty },
+ { "nv!ogl_234557580", string.Empty },
+ { "nv!ogl_23cd0e", string.Empty },
+ { "nv!ogl_24189123", string.Empty },
+ { "nv!ogl_2443266", string.Empty },
+ { "nv!ogl_25025519", string.Empty },
+ { "nv!ogl_255e39", string.Empty },
+ { "nv!ogl_2583364", string.Empty },
+ { "nv!ogl_2888c1", string.Empty },
+ { "nv!ogl_28ca3e", string.Empty },
+ { "nv!ogl_29871243", string.Empty },
+ { "nv!ogl_2a1f64", string.Empty },
+ { "nv!ogl_2dc432", string.Empty },
+ { "nv!ogl_2de437", string.Empty },
+ { "nv!ogl_2f3bb89c", string.Empty },
+ { "nv!ogl_2fd652", string.Empty },
+ { "nv!ogl_3001ac", string.Empty },
+ { "nv!ogl_31298772", string.Empty },
+ { "nv!ogl_313233", string.Empty },
+ { "nv!ogl_31f7d603", string.Empty },
+ { "nv!ogl_320ce4", string.Empty },
+ { "nv!ogl_32153248", string.Empty },
+ { "nv!ogl_32153249", string.Empty },
+ { "nv!ogl_335bca", string.Empty },
+ { "nv!ogl_342abb", string.Empty },
+ { "nv!ogl_34dfe6", string.Empty },
+ { "nv!ogl_34dfe7", string.Empty },
+ { "nv!ogl_34dfe8", string.Empty },
+ { "nv!ogl_34dfe9", string.Empty },
+ { "nv!ogl_35201578", string.Empty },
+ { "nv!ogl_359278", string.Empty },
+ { "nv!ogl_37f53a", string.Empty },
+ { "nv!ogl_38144972", string.Empty },
+ { "nv!ogl_38542646", string.Empty },
+ { "nv!ogl_3b74c9", string.Empty },
+ { "nv!ogl_3c136f", string.Empty },
+ { "nv!ogl_3cf72823", string.Empty },
+ { "nv!ogl_3d7af029", string.Empty },
+ { "nv!ogl_3ff34782", string.Empty },
+ { "nv!ogl_4129618", string.Empty },
+ { "nv!ogl_4189fac3", string.Empty },
+ { "nv!ogl_420bd4", string.Empty },
+ { "nv!ogl_42a699", string.Empty },
+ { "nv!ogl_441369", string.Empty },
+ { "nv!ogl_4458713e", string.Empty },
+ { "nv!ogl_4554b6", string.Empty },
+ { "nv!ogl_457425", string.Empty },
+ { "nv!ogl_4603b207", string.Empty },
+ { "nv!ogl_46574957", string.Empty },
+ { "nv!ogl_46574958", string.Empty },
+ { "nv!ogl_46813529", string.Empty },
+ { "nv!ogl_46f1e13d", string.Empty },
+ { "nv!ogl_47534c43", string.Empty },
+ { "nv!ogl_48550336", string.Empty },
+ { "nv!ogl_48576893", string.Empty },
+ { "nv!ogl_48576894", string.Empty },
+ { "nv!ogl_4889ac02", string.Empty },
+ { "nv!ogl_49005740", string.Empty },
+ { "nv!ogl_49867584", string.Empty },
+ { "nv!ogl_49960973", string.Empty },
+ { "nv!ogl_4a5341", string.Empty },
+ { "nv!ogl_4f4e48", string.Empty },
+ { "nv!ogl_4f8a0a", string.Empty },
+ { "nv!ogl_50299698", string.Empty },
+ { "nv!ogl_50299699", string.Empty },
+ { "nv!ogl_50361291", string.Empty },
+ { "nv!ogl_5242ae", string.Empty },
+ { "nv!ogl_53d30c", string.Empty },
+ { "nv!ogl_56347a", string.Empty },
+ { "nv!ogl_563a95f1", string.Empty },
+ { "nv!ogl_573823", string.Empty },
+ { "nv!ogl_58027529", string.Empty },
+ { "nv!ogl_5d2d63", string.Empty },
+ { "nv!ogl_5f7e3b", string.Empty },
+ { "nv!ogl_60461793", string.Empty },
+ { "nv!ogl_60d355", string.Empty },
+ { "nv!ogl_616627aa", string.Empty },
+ { "nv!ogl_62317182", string.Empty },
+ { "nv!ogl_6253fa2e", string.Empty },
+ { "nv!ogl_64100768", string.Empty },
+ { "nv!ogl_64100769", string.Empty },
+ { "nv!ogl_64100770", string.Empty },
+ { "nv!ogl_647395", string.Empty },
+ { "nv!ogl_66543234", string.Empty },
+ { "nv!ogl_67674763", string.Empty },
+ { "nv!ogl_67739784", string.Empty },
+ { "nv!ogl_68fb9c", string.Empty },
+ { "nv!ogl_69801276", string.Empty },
+ { "nv!ogl_6af9fa2f", string.Empty },
+ { "nv!ogl_6af9fa3f", string.Empty },
+ { "nv!ogl_6af9fa4f", string.Empty },
+ { "nv!ogl_6bd8c7", string.Empty },
+ { "nv!ogl_6c7691", string.Empty },
+ { "nv!ogl_6d4296ce", string.Empty },
+ { "nv!ogl_6dd7e7", string.Empty },
+ { "nv!ogl_6dd7e8", string.Empty },
+ { "nv!ogl_6fe11ec1", string.Empty },
+ { "nv!ogl_716511763", string.Empty },
+ { "nv!ogl_72504593", string.Empty },
+ { "nv!ogl_73304097", string.Empty },
+ { "nv!ogl_73314098", string.Empty },
+ { "nv!ogl_74095213", string.Empty },
+ { "nv!ogl_74095213a", string.Empty },
+ { "nv!ogl_74095213b", string.Empty },
+ { "nv!ogl_74095214", string.Empty },
+ { "nv!ogl_748f9649", string.Empty },
+ { "nv!ogl_75494732", string.Empty },
+ { "nv!ogl_78452832", string.Empty },
+ { "nv!ogl_784561", string.Empty },
+ { "nv!ogl_78e16b9c", string.Empty },
+ { "nv!ogl_79251225", string.Empty },
+ { "nv!ogl_7c128b", string.Empty },
+ { "nv!ogl_7ccd93", string.Empty },
+ { "nv!ogl_7df8d1", string.Empty },
+ { "nv!ogl_800c2310", string.Empty },
+ { "nv!ogl_80546710", string.Empty },
+ { "nv!ogl_80772310", string.Empty },
+ { "nv!ogl_808ee280", string.Empty },
+ { "nv!ogl_81131154", string.Empty },
+ { "nv!ogl_81274457", string.Empty },
+ { "nv!ogl_8292291f", string.Empty },
+ { "nv!ogl_83498426", string.Empty },
+ { "nv!ogl_84993794", string.Empty },
+ { "nv!ogl_84995585", string.Empty },
+ { "nv!ogl_84a0a0", string.Empty },
+ { "nv!ogl_852142", string.Empty },
+ { "nv!ogl_85612309", string.Empty },
+ { "nv!ogl_85612310", string.Empty },
+ { "nv!ogl_85612311", string.Empty },
+ { "nv!ogl_85612312", string.Empty },
+ { "nv!ogl_8623ff27", string.Empty },
+ { "nv!ogl_87364952", string.Empty },
+ { "nv!ogl_87f6275666", string.Empty },
+ { "nv!ogl_886748", string.Empty },
+ { "nv!ogl_89894423", string.Empty },
+ { "nv!ogl_8ad8a75", string.Empty },
+ { "nv!ogl_8ad8ad00", string.Empty },
+ { "nv!ogl_8bb815", string.Empty },
+ { "nv!ogl_8bb817", string.Empty },
+ { "nv!ogl_8bb818", string.Empty },
+ { "nv!ogl_8bb819", string.Empty },
+ { "nv!ogl_8e640cd1", string.Empty },
+ { "nv!ogl_8f34971a", string.Empty },
+ { "nv!ogl_8f773984", string.Empty },
+ { "nv!ogl_8f7a7d", string.Empty },
+ { "nv!ogl_902486209", string.Empty },
+ { "nv!ogl_90482571", string.Empty },
+ { "nv!ogl_91214835", string.Empty },
+ { "nv!ogl_912848290", string.Empty },
+ { "nv!ogl_915e56", string.Empty },
+ { "nv!ogl_92179063", string.Empty },
+ { "nv!ogl_92179064", string.Empty },
+ { "nv!ogl_92179065", string.Empty },
+ { "nv!ogl_92179066", string.Empty },
+ { "nv!ogl_92350358", string.Empty },
+ { "nv!ogl_92809063", string.Empty },
+ { "nv!ogl_92809064", string.Empty },
+ { "nv!ogl_92809065", string.Empty },
+ { "nv!ogl_92809066", string.Empty },
+ { "nv!ogl_92920143", string.Empty },
+ { "nv!ogl_93a89b12", string.Empty },
+ { "nv!ogl_93a89c0b", string.Empty },
+ { "nv!ogl_94812574", string.Empty },
+ { "nv!ogl_95282304", string.Empty },
+ { "nv!ogl_95394027", string.Empty },
+ { "nv!ogl_959b1f", string.Empty },
+ { "nv!ogl_9638af", string.Empty },
+ { "nv!ogl_96fd59", string.Empty },
+ { "nv!ogl_97f6275666", string.Empty },
+ { "nv!ogl_97f6275667", string.Empty },
+ { "nv!ogl_97f6275668", string.Empty },
+ { "nv!ogl_97f6275669", string.Empty },
+ { "nv!ogl_97f627566a", string.Empty },
+ { "nv!ogl_97f627566b", string.Empty },
+ { "nv!ogl_97f627566d", string.Empty },
+ { "nv!ogl_97f627566e", string.Empty },
+ { "nv!ogl_97f627566f", string.Empty },
+ { "nv!ogl_97f6275670", string.Empty },
+ { "nv!ogl_97f6275671", string.Empty },
+ { "nv!ogl_97f727566e", string.Empty },
+ { "nv!ogl_98480775", string.Empty },
+ { "nv!ogl_98480776", string.Empty },
+ { "nv!ogl_98480777", string.Empty },
+ { "nv!ogl_992431", string.Empty },
+ { "nv!ogl_9aa29065", string.Empty },
+ { "nv!ogl_9af32c", string.Empty },
+ { "nv!ogl_9af32d", string.Empty },
+ { "nv!ogl_9af32e", string.Empty },
+ { "nv!ogl_9c108b71", string.Empty },
+ { "nv!ogl_9f279065", string.Empty },
+ { "nv!ogl_a01bc728", string.Empty },
+ { "nv!ogl_a13b46c80", string.Empty },
+ { "nv!ogl_a22eb0", string.Empty },
+ { "nv!ogl_a2fb451e", string.Empty },
+ { "nv!ogl_a3456abe", string.Empty },
+ { "nv!ogl_a7044887", string.Empty },
+ { "nv!ogl_a7149200", string.Empty },
+ { "nv!ogl_a766215670", string.Empty },
+ { "nv!ogl_aalinegamma", string.Empty },
+ { "nv!ogl_aalinetweaks", string.Empty },
+ { "nv!ogl_ab34ee01", string.Empty },
+ { "nv!ogl_ab34ee02", string.Empty },
+ { "nv!ogl_ab34ee03", string.Empty },
+ { "nv!ogl_ac0274", string.Empty },
+ { "nv!ogl_af73c63e", string.Empty },
+ { "nv!ogl_af73c63f", string.Empty },
+ { "nv!ogl_af9927", string.Empty },
+ { "nv!ogl_afoverride", string.Empty },
+ { "nv!ogl_allocdeviceevents", string.Empty },
+ { "nv!ogl_applicationkey", string.Empty },
+ { "nv!ogl_appreturnonlybasicglsltype", string.Empty },
+ { "nv!ogl_app_softimage", string.Empty },
+ { "nv!ogl_app_supportbits2", string.Empty },
+ { "nv!ogl_assumetextureismipmappedatcreation", string.Empty },
+ { "nv!ogl_b1fb0f01", string.Empty },
+ { "nv!ogl_b3edd5", string.Empty },
+ { "nv!ogl_b40d9e03d", string.Empty },
+ { "nv!ogl_b7f6275666", string.Empty },
+ { "nv!ogl_b812c1", string.Empty },
+ { "nv!ogl_ba14ba1a", string.Empty },
+ { "nv!ogl_ba14ba1b", string.Empty },
+ { "nv!ogl_bd7559", string.Empty },
+ { "nv!ogl_bd755a", string.Empty },
+ { "nv!ogl_bd755c", string.Empty },
+ { "nv!ogl_bd755d", string.Empty },
+ { "nv!ogl_be58bb", string.Empty },
+ { "nv!ogl_be92cb", string.Empty },
+ { "nv!ogl_beefcba3", string.Empty },
+ { "nv!ogl_beefcba4", string.Empty },
+ { "nv!ogl_c023777f", string.Empty },
+ { "nv!ogl_c09dc8", string.Empty },
+ { "nv!ogl_c0d340", string.Empty },
+ { "nv!ogl_c2ff374c", string.Empty },
+ { "nv!ogl_c5e9d7a3", string.Empty },
+ { "nv!ogl_c5e9d7a4", string.Empty },
+ { "nv!ogl_c5e9d7b4", string.Empty },
+ { "nv!ogl_c618f9", string.Empty },
+ { "nv!ogl_ca345840", string.Empty },
+ { "nv!ogl_cachedisable", string.Empty },
+ { "nv!ogl_channelpriorityoverride", string.Empty },
+ { "nv!ogl_cleardatastorevidmem", string.Empty },
+ { "nv!ogl_cmdbufmemoryspaceenables", string.Empty },
+ { "nv!ogl_cmdbufminwords", string.Empty },
+ { "nv!ogl_cmdbufsizewords", string.Empty },
+ { "nv!ogl_conformantblitframebufferscissor", string.Empty },
+ { "nv!ogl_conformantincompletetextures", string.Empty },
+ { "nv!ogl_copybuffermethod", string.Empty },
+ { "nv!ogl_cubemapaniso", string.Empty },
+ { "nv!ogl_cubemapfiltering", string.Empty },
+ { "nv!ogl_d0e9a4d7", string.Empty },
+ { "nv!ogl_d13733f12", string.Empty },
+ { "nv!ogl_d1b399", string.Empty },
+ { "nv!ogl_d2983c32", string.Empty },
+ { "nv!ogl_d2983c33", string.Empty },
+ { "nv!ogl_d2e71b", string.Empty },
+ { "nv!ogl_d377dc", string.Empty },
+ { "nv!ogl_d377dd", string.Empty },
+ { "nv!ogl_d489f4", string.Empty },
+ { "nv!ogl_d4bce1", string.Empty },
+ { "nv!ogl_d518cb", string.Empty },
+ { "nv!ogl_d518cd", string.Empty },
+ { "nv!ogl_d518ce", string.Empty },
+ { "nv!ogl_d518d0", string.Empty },
+ { "nv!ogl_d518d1", string.Empty },
+ { "nv!ogl_d518d2", string.Empty },
+ { "nv!ogl_d518d3", string.Empty },
+ { "nv!ogl_d518d4", string.Empty },
+ { "nv!ogl_d518d5", string.Empty },
+ { "nv!ogl_d59eda", string.Empty },
+ { "nv!ogl_d83cbd", string.Empty },
+ { "nv!ogl_d8e777", string.Empty },
+ { "nv!ogl_debug_level", string.Empty },
+ { "nv!ogl_debug_mask", string.Empty },
+ { "nv!ogl_debug_options", string.Empty },
+ { "nv!ogl_devshmpageableallocations", string.Empty },
+ { "nv!ogl_df1f9812", string.Empty },
+ { "nv!ogl_df783c", string.Empty },
+ { "nv!ogl_diagenable", string.Empty },
+ { "nv!ogl_disallowcemask", string.Empty },
+ { "nv!ogl_disallowz16", string.Empty },
+ { "nv!ogl_dlmemoryspaceenables", string.Empty },
+ { "nv!ogl_e0bfec", string.Empty },
+ { "nv!ogl_e433456d", string.Empty },
+ { "nv!ogl_e435563f", string.Empty },
+ { "nv!ogl_e4cd9c", string.Empty },
+ { "nv!ogl_e5c972", string.Empty },
+ { "nv!ogl_e639ef", string.Empty },
+ { "nv!ogl_e802af", string.Empty },
+ { "nv!ogl_eae964", string.Empty },
+ { "nv!ogl_earlytexturehwallocation", string.Empty },
+ { "nv!ogl_eb92a3", string.Empty },
+ { "nv!ogl_ebca56", string.Empty },
+ { "nv!ogl_expert_detail_level", string.Empty },
+ { "nv!ogl_expert_output_mask", string.Empty },
+ { "nv!ogl_expert_report_mask", string.Empty },
+ { "nv!ogl_extensionstringnvarch", string.Empty },
+ { "nv!ogl_extensionstringversion", string.Empty },
+ { "nv!ogl_f00f1938", string.Empty },
+ { "nv!ogl_f10736", string.Empty },
+ { "nv!ogl_f1846870", string.Empty },
+ { "nv!ogl_f33bc370", string.Empty },
+ { "nv!ogl_f392a874", string.Empty },
+ { "nv!ogl_f49ae8", string.Empty },
+ { "nv!ogl_fa345cce", string.Empty },
+ { "nv!ogl_fa35cc4", string.Empty },
+ { "nv!ogl_faa14a", string.Empty },
+ { "nv!ogl_faf8a723", string.Empty },
+ { "nv!ogl_fastgs", string.Empty },
+ { "nv!ogl_fbf4ac45", string.Empty },
+ { "nv!ogl_fbo_blit_ignore_srgb", string.Empty },
+ { "nv!ogl_fc64c7", string.Empty },
+ { "nv!ogl_ff54ec97", string.Empty },
+ { "nv!ogl_ff54ec98", string.Empty },
+ { "nv!ogl_forceexitprocessdetach", string.Empty },
+ { "nv!ogl_forcerequestedesversion", string.Empty },
+ { "nv!ogl_glsynctovblank", string.Empty },
+ { "nv!ogl_gvitimeoutcontrol", string.Empty },
+ { "nv!ogl_hcctrl", string.Empty },
+ { "nv!ogl_hwstate_per_ctx", string.Empty },
+ { "nv!ogl_machinecachelimit", string.Empty },
+ { "nv!ogl_maxframesallowed", string.Empty },
+ { "nv!ogl_memmgrcachedalloclimit", string.Empty },
+ { "nv!ogl_memmgrcachedalloclimitratio", string.Empty },
+ { "nv!ogl_memmgrsysheapalloclimit", string.Empty },
+ { "nv!ogl_memmgrsysheapalloclimitratio", string.Empty },
+ { "nv!ogl_memmgrvidheapalloclimit", string.Empty },
+ { "nv!ogl_mosaic_clip_to_subdev", string.Empty },
+ { "nv!ogl_mosaic_clip_to_subdev_h_overlap", string.Empty },
+ { "nv!ogl_mosaic_clip_to_subdev_v_overlap", string.Empty },
+ { "nv!ogl_overlaymergeblittimerms", string.Empty },
+ { "nv!ogl_perfmon_mode", string.Empty },
+ { "nv!ogl_pixbar_mode", string.Empty },
+ { "nv!ogl_qualityenhancements", string.Empty },
+ { "nv!ogl_r27s18q28", string.Empty },
+ { "nv!ogl_r2d7c1d8", string.Empty },
+ { "nv!ogl_renderer", string.Empty },
+ { "nv!ogl_renderqualityflags", string.Empty },
+ { "nv!ogl_s3tcquality", string.Empty },
+ { "nv!ogl_shaderatomics", string.Empty },
+ { "nv!ogl_shadercacheinitsize", string.Empty },
+ { "nv!ogl_shader_disk_cache_path", string.Empty },
+ { "nv!ogl_shader_disk_cache_read_only", string.Empty },
+ { "nv!ogl_shaderobjects", string.Empty },
+ { "nv!ogl_shaderportabilitywarnings", string.Empty },
+ { "nv!ogl_shaderwarningsaserrors", string.Empty },
+ { "nv!ogl_skiptexturehostcopies", string.Empty },
+ { "nv!ogl_sli_dli_control", string.Empty },
+ { "nv!ogl_sparsetexture", string.Empty },
+ { "nv!ogl_spinlooptimeout", string.Empty },
+ { "nv!ogl_sync_to_vblank", string.Empty },
+ { "nv!ogl_sysheapreuseratio", string.Empty },
+ { "nv!ogl_sysmemtexturepromotion", string.Empty },
+ { "nv!ogl_targetflushcount", string.Empty },
+ { "nv!ogl_tearingfreeswappresent", string.Empty },
+ { "nv!ogl_texclampbehavior", string.Empty },
+ { "nv!ogl_texlodbias", string.Empty },
+ { "nv!ogl_texmemoryspaceenables", string.Empty },
+ { "nv!ogl_textureprecache", string.Empty },
+ { "nv!ogl_threadcontrol", string.Empty },
+ { "nv!ogl_threadcontrol2", string.Empty },
+ { "nv!ogl_usegvievents", string.Empty },
+ { "nv!ogl_vbomemoryspaceenables", string.Empty },
+ { "nv!ogl_vertexlimit", string.Empty },
+ { "nv!ogl_vidheapreuseratio", string.Empty },
+ { "nv!ogl_vpipe", string.Empty },
+ { "nv!ogl_vpipeformatbloatlimit", string.Empty },
+ { "nv!ogl_wglmessageboxonabort", string.Empty },
+ { "nv!ogl_writeinfolog", string.Empty },
+ { "nv!ogl_writeprogramobjectassembly", string.Empty },
+ { "nv!ogl_writeprogramobjectsource", string.Empty },
+ { "nv!ogl_xnvadapterpresent", string.Empty },
+ { "nv!ogl_yield", string.Empty },
+ { "nv!ogl_yieldfunction", string.Empty },
+ { "nv!ogl_yieldfunctionfast", string.Empty },
+ { "nv!ogl_yieldfunctionslow", string.Empty },
+ { "nv!ogl_yieldfunctionwaitfordcqueue", string.Empty },
+ { "nv!ogl_yieldfunctionwaitforframe", string.Empty },
+ { "nv!ogl_yieldfunctionwaitforgpu", string.Empty },
+ { "nv!ogl_zbctableaddhysteresis", string.Empty },
+ { "nv!overlaymergeblittimerms", string.Empty },
+ { "nv!perfmon_mode", string.Empty },
+ { "nv!persist.sys.display.resolution", string.Empty },
+ { "nv!persist.tegra.composite.fallb", string.Empty },
+ { "nv!persist.tegra.composite.policy", string.Empty },
+ { "nv!persist.tegra.composite.range", string.Empty },
+ { "nv!persist.tegra.compositor", string.Empty },
+ { "nv!persist.tegra.compositor.virt", string.Empty },
+ { "nv!persist.tegra.compression", string.Empty },
+ { "nv!persist.tegra.cursor.enable", string.Empty },
+ { "nv!persist.tegra.didim.enable", string.Empty },
+ { "nv!persist.tegra.didim.normal", string.Empty },
+ { "nv!persist.tegra.didim.video", string.Empty },
+ { "nv!persist.tegra.disp.heads", string.Empty },
+ { "nv!persist.tegra.gamma_correction", string.Empty },
+ { "nv!persist.tegra.gpu_mapping_cache", string.Empty },
+ { "nv!persist.tegra.grlayout", string.Empty },
+ { "nv!persist.tegra.hdmi.2020.10", string.Empty },
+ { "nv!persist.tegra.hdmi.2020.fake", string.Empty },
+ { "nv!persist.tegra.hdmi.2020.force", string.Empty },
+ { "nv!persist.tegra.hdmi.autorotate", string.Empty },
+ { "nv!persist.tegra.hdmi.hdr.fake", string.Empty },
+ { "nv!persist.tegra.hdmi.ignore_ratio", string.Empty },
+ { "nv!persist.tegra.hdmi.limit.clock", string.Empty },
+ { "nv!persist.tegra.hdmi.only_16_9", string.Empty },
+ { "nv!persist.tegra.hdmi.range", string.Empty },
+ { "nv!persist.tegra.hdmi.resolution", string.Empty },
+ { "nv!persist.tegra.hdmi.underscan", string.Empty },
+ { "nv!persist.tegra.hdmi.yuv.422", string.Empty },
+ { "nv!persist.tegra.hdmi.yuv.444", string.Empty },
+ { "nv!persist.tegra.hdmi.yuv.enable", string.Empty },
+ { "nv!persist.tegra.hdmi.yuv.force", string.Empty },
+ { "nv!persist.tegra.hwc.nvdc", string.Empty },
+ { "nv!persist.tegra.idle.minimum_fps", string.Empty },
+ { "nv!persist.tegra.panel.rotation", string.Empty },
+ { "nv!persist.tegra.scan_props", string.Empty },
+ { "nv!persist.tegra.stb.mode", string.Empty },
+ { "nv!persist.tegra.zbc_override", string.Empty },
+ { "nv!pixbar_mode", string.Empty },
+ { "nv!qualityenhancements", string.Empty },
+ { "nv!r27s18q28", string.Empty },
+ { "nv!r2d7c1d8", string.Empty },
+ { "nv!renderer", string.Empty },
+ { "nv!renderqualityflags", string.Empty },
+ { "nv!rmos_debug_mask", string.Empty },
+ { "nv!rmos_set_production_mode", string.Empty },
+ { "nv!s3tcquality", string.Empty },
+ { "nv!shaderatomics", string.Empty },
+ { "nv!shadercacheinitsize", string.Empty },
+ { "nv!shader_disk_cache_path", string.Empty },
+ { "nv!shader_disk_cache_read_only", string.Empty },
+ { "nv!shaderobjects", string.Empty },
+ { "nv!shaderportabilitywarnings", string.Empty },
+ { "nv!shaderwarningsaserrors", string.Empty },
+ { "nv!skiptexturehostcopies", string.Empty },
+ { "nv!sli_dli_control", string.Empty },
+ { "nv!sparsetexture", string.Empty },
+ { "nv!spinlooptimeout", string.Empty },
+ { "nv!sync_to_vblank", string.Empty },
+ { "nv!sysheapreuseratio", string.Empty },
+ { "nv!sysmemtexturepromotion", string.Empty },
+ { "nv!targetflushcount", string.Empty },
+ { "nv!tearingfreeswappresent", string.Empty },
+ { "nv!tegra.refresh", string.Empty },
+ { "nv!texclampbehavior", string.Empty },
+ { "nv!texlodbias", string.Empty },
+ { "nv!texmemoryspaceenables", string.Empty },
+ { "nv!textureprecache", string.Empty },
+ { "nv!threadcontrol", string.Empty },
+ { "nv!threadcontrol2", string.Empty },
+ { "nv!tvmr.avp.logs", string.Empty },
+ { "nv!tvmr.buffer.logs", string.Empty },
+ { "nv!tvmr.dec.prof", string.Empty },
+ { "nv!tvmr.deint.logs", string.Empty },
+ { "nv!tvmr.dfs.logs", string.Empty },
+ { "nv!tvmr.ffprof.logs", string.Empty },
+ { "nv!tvmr.game.stream", string.Empty },
+ { "nv!tvmr.general.logs", string.Empty },
+ { "nv!tvmr.input.dump", string.Empty },
+ { "nv!tvmr.seeking.logs", string.Empty },
+ { "nv!tvmr.ts_pulldown", string.Empty },
+ { "nv!usegvievents", string.Empty },
+ { "nv!vbomemoryspaceenables", string.Empty },
+ { "nv!vcc_debug_ip", string.Empty },
+ { "nv!vcc_verbose_level", string.Empty },
+ { "nv!vertexlimit", string.Empty },
+ { "nv!viccomposer.filter", string.Empty },
+ { "nv!videostats-enable", string.Empty },
+ { "nv!vidheapreuseratio", string.Empty },
+ { "nv!vpipe", string.Empty },
+ { "nv!vpipeformatbloatlimit", string.Empty },
+ { "nv!wglmessageboxonabort", string.Empty },
+ { "nv!writeinfolog", string.Empty },
+ { "nv!writeprogramobjectassembly", string.Empty },
+ { "nv!writeprogramobjectsource", string.Empty },
+ { "nv!xnvadapterpresent", string.Empty },
+ { "nv!yield", string.Empty },
+ { "nv!yieldfunction", string.Empty },
+ { "nv!yieldfunctionfast", string.Empty },
+ { "nv!yieldfunctionslow", string.Empty },
+ { "nv!yieldfunctionwaitfordcqueue", string.Empty },
+ { "nv!yieldfunctionwaitforframe", string.Empty },
+ { "nv!yieldfunctionwaitforgpu", string.Empty },
+ { "nv!zbctableaddhysteresis", string.Empty },
{ "pcm!enable", true },
{ "pctl!intermittent_task_interval_seconds", 21600 },
{ "prepo!devmenu_prepo_page_view", false },
@@ -1611,7 +1611,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
{ "systempowerstate!always_reboot", false },
{ "systempowerstate!power_state_message_emulation_trigger_time", 0 },
{ "systempowerstate!power_state_message_to_emulate", 0 },
- { "target_manager!device_name", "" },
+ { "target_manager!device_name", string.Empty },
{ "vulnerability!needs_update_vulnerability_policy", 0 },
{ "apm!performance_mode_policy", "auto" },
{ "apm!sdev_throttling_enabled", true },
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
index 21d48288e..3a40a4ac5 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
@@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
}
}
- ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol)
+ ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
{
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
};
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs
index c9b811cf5..981fe0a8f 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs
@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System;
using System.Collections.Generic;
@@ -21,21 +22,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public bool Blocking { get => Socket.Blocking; set => Socket.Blocking = value; }
- public nint Handle => Socket.Handle;
+ public nint Handle => IntPtr.Zero;
public IPEndPoint RemoteEndPoint => Socket.RemoteEndPoint as IPEndPoint;
public IPEndPoint LocalEndPoint => Socket.LocalEndPoint as IPEndPoint;
- public Socket Socket { get; }
+ public ISocketImpl Socket { get; }
- public ManagedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
+ public ManagedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string lanInterfaceId)
{
- Socket = new Socket(addressFamily, socketType, protocolType);
+ Socket = SocketHelpers.CreateSocket(addressFamily, socketType, protocolType, lanInterfaceId);
Refcount = 1;
}
- private ManagedSocket(Socket socket)
+ private ManagedSocket(ISocketImpl socket)
{
Socket = socket;
Refcount = 1;
@@ -185,6 +186,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
}
+ bool hasEmittedBlockingWarning = false;
+
public LinuxError Receive(out int receiveSize, Span buffer, BsdSocketFlags flags)
{
LinuxError result;
@@ -199,6 +202,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true;
}
+ if (Blocking && !hasEmittedBlockingWarning)
+ {
+ Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
+ hasEmittedBlockingWarning = true;
+ }
+
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
result = LinuxError.SUCCESS;
@@ -236,6 +245,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true;
}
+ if (Blocking && !hasEmittedBlockingWarning)
+ {
+ Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
+ hasEmittedBlockingWarning = true;
+ }
+
if (!Socket.IsBound)
{
receiveSize = -1;
@@ -313,7 +328,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt Option: {option} Level: {level}");
optionValue.Clear();
- return LinuxError.SUCCESS;
+ return LinuxError.EOPNOTSUPP;
}
byte[] tempOptionValue = new byte[optionValue.Length];
@@ -347,7 +362,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt Option: {option} Level: {level}");
- return LinuxError.SUCCESS;
+ return LinuxError.EOPNOTSUPP;
}
int value = optionValue.Length >= 4 ? MemoryMarshal.Read(optionValue) : MemoryMarshal.Read(optionValue);
@@ -493,7 +508,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
try
{
- int receiveSize = Socket.Receive(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
+ int receiveSize = (Socket as DefaultSocket).BaseSocket.Receive(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
if (receiveSize > 0)
{
@@ -531,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
try
{
- int sendSize = Socket.Send(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
+ int sendSize = (Socket as DefaultSocket).BaseSocket.Send(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
if (sendSize > 0)
{
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs
index d0db44086..e870e8aea 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs
@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System.Collections.Generic;
using System.Net.Sockets;
@@ -26,45 +27,46 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public LinuxError Poll(List events, int timeoutMilliseconds, out int updatedCount)
{
- List readEvents = new();
- List writeEvents = new();
- List errorEvents = new();
+ List readEvents = new();
+ List writeEvents = new();
+ List errorEvents = new();
updatedCount = 0;
foreach (PollEvent evnt in events)
{
- ManagedSocket socket = (ManagedSocket)evnt.FileDescriptor;
-
- bool isValidEvent = evnt.Data.InputEvents == 0;
-
- errorEvents.Add(socket.Socket);
-
- if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
+ if (evnt.FileDescriptor is ManagedSocket ms)
{
- readEvents.Add(socket.Socket);
+ bool isValidEvent = evnt.Data.InputEvents == 0;
- isValidEvent = true;
- }
+ errorEvents.Add(ms.Socket);
- if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0)
- {
- readEvents.Add(socket.Socket);
+ if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
+ {
+ readEvents.Add(ms.Socket);
- isValidEvent = true;
- }
+ isValidEvent = true;
+ }
- if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0)
- {
- writeEvents.Add(socket.Socket);
+ if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0)
+ {
+ readEvents.Add(ms.Socket);
- isValidEvent = true;
- }
+ isValidEvent = true;
+ }
- if (!isValidEvent)
- {
- Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}");
- return LinuxError.EINVAL;
+ if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0)
+ {
+ writeEvents.Add(ms.Socket);
+
+ isValidEvent = true;
+ }
+
+ if (!isValidEvent)
+ {
+ Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}");
+ return LinuxError.EINVAL;
+ }
}
}
@@ -72,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{
int actualTimeoutMicroseconds = timeoutMilliseconds == -1 ? -1 : timeoutMilliseconds * 1000;
- Socket.Select(readEvents, writeEvents, errorEvents, actualTimeoutMicroseconds);
+ SocketHelpers.Select(readEvents, writeEvents, errorEvents, actualTimeoutMicroseconds);
}
catch (SocketException exception)
{
@@ -81,34 +83,37 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
foreach (PollEvent evnt in events)
{
- Socket socket = ((ManagedSocket)evnt.FileDescriptor).Socket;
-
- PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents;
-
- if (errorEvents.Contains(socket))
+ if (evnt.FileDescriptor is ManagedSocket ms)
{
- outputEvents |= PollEventTypeMask.Error;
+ ISocketImpl socket = ms.Socket;
- if (!socket.Connected || !socket.IsBound)
+ PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents;
+
+ if (errorEvents.Contains(ms.Socket))
{
- outputEvents |= PollEventTypeMask.Disconnected;
- }
- }
+ outputEvents |= PollEventTypeMask.Error;
- if (readEvents.Contains(socket))
- {
- if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
+ if (!socket.Connected || !socket.IsBound)
+ {
+ outputEvents |= PollEventTypeMask.Disconnected;
+ }
+ }
+
+ if (readEvents.Contains(ms.Socket))
{
- outputEvents |= PollEventTypeMask.Input;
+ if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
+ {
+ outputEvents |= PollEventTypeMask.Input;
+ }
}
- }
- if (writeEvents.Contains(socket))
- {
- outputEvents |= PollEventTypeMask.Output;
- }
+ if (writeEvents.Contains(ms.Socket))
+ {
+ outputEvents |= PollEventTypeMask.Output;
+ }
- evnt.Data.OutputEvents = outputEvents;
+ evnt.Data.OutputEvents = outputEvents;
+ }
}
updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count;
@@ -118,53 +123,55 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public LinuxError Select(List events, int timeout, out int updatedCount)
{
- List readEvents = new();
- List writeEvents = new();
- List errorEvents = new();
+ List readEvents = new();
+ List writeEvents = new();
+ List errorEvents = new();
updatedCount = 0;
foreach (PollEvent pollEvent in events)
{
- ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor;
-
- if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Input))
+ if (pollEvent.FileDescriptor is ManagedSocket ms)
{
- readEvents.Add(socket.Socket);
- }
+ if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Input))
+ {
+ readEvents.Add(ms.Socket);
+ }
- if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Output))
- {
- writeEvents.Add(socket.Socket);
- }
+ if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Output))
+ {
+ writeEvents.Add(ms.Socket);
+ }
- if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Error))
- {
- errorEvents.Add(socket.Socket);
+ if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Error))
+ {
+ errorEvents.Add(ms.Socket);
+ }
}
}
- Socket.Select(readEvents, writeEvents, errorEvents, timeout);
+ SocketHelpers.Select(readEvents, writeEvents, errorEvents, timeout);
updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count;
foreach (PollEvent pollEvent in events)
{
- ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor;
-
- if (readEvents.Contains(socket.Socket))
+ if (pollEvent.FileDescriptor is ManagedSocket ms)
{
- pollEvent.Data.OutputEvents |= PollEventTypeMask.Input;
- }
+ if (readEvents.Contains(ms.Socket))
+ {
+ pollEvent.Data.OutputEvents |= PollEventTypeMask.Input;
+ }
- if (writeEvents.Contains(socket.Socket))
- {
- pollEvent.Data.OutputEvents |= PollEventTypeMask.Output;
- }
+ if (writeEvents.Contains(ms.Socket))
+ {
+ pollEvent.Data.OutputEvents |= PollEventTypeMask.Output;
+ }
- if (errorEvents.Contains(socket.Socket))
- {
- pollEvent.Data.OutputEvents |= PollEventTypeMask.Error;
+ if (errorEvents.Contains(ms.Socket))
+ {
+ pollEvent.Data.OutputEvents |= PollEventTypeMask.Error;
+ }
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/DefaultSocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/DefaultSocket.cs
new file mode 100644
index 000000000..f1040e799
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/DefaultSocket.cs
@@ -0,0 +1,178 @@
+using Ryujinx.Common.Utilities;
+using System;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+
+namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
+{
+ class DefaultSocket : ISocketImpl
+ {
+ public Socket BaseSocket { get; }
+
+ public EndPoint RemoteEndPoint => BaseSocket.RemoteEndPoint;
+
+ public EndPoint LocalEndPoint => BaseSocket.LocalEndPoint;
+
+ public bool Connected => BaseSocket.Connected;
+
+ public bool IsBound => BaseSocket.IsBound;
+
+ public AddressFamily AddressFamily => BaseSocket.AddressFamily;
+
+ public SocketType SocketType => BaseSocket.SocketType;
+
+ public ProtocolType ProtocolType => BaseSocket.ProtocolType;
+
+ public bool Blocking { get => BaseSocket.Blocking; set => BaseSocket.Blocking = value; }
+
+ public int Available => BaseSocket.Available;
+
+ private readonly string _lanInterfaceId;
+
+ public DefaultSocket(Socket baseSocket, string lanInterfaceId)
+ {
+ _lanInterfaceId = lanInterfaceId;
+
+ BaseSocket = baseSocket;
+ }
+
+ public DefaultSocket(AddressFamily domain, SocketType type, ProtocolType protocol, string lanInterfaceId)
+ {
+ _lanInterfaceId = lanInterfaceId;
+
+ BaseSocket = new Socket(domain, type, protocol);
+ }
+
+ private void EnsureNetworkInterfaceBound()
+ {
+ if (_lanInterfaceId != "0" && !BaseSocket.IsBound)
+ {
+ (_, UnicastIPAddressInformation ipInfo) = NetworkHelpers.GetLocalInterface(_lanInterfaceId);
+
+ BaseSocket.Bind(new IPEndPoint(ipInfo.Address, 0));
+ }
+ }
+
+ public ISocketImpl Accept()
+ {
+ return new DefaultSocket(BaseSocket.Accept(), _lanInterfaceId);
+ }
+
+ public void Bind(EndPoint localEP)
+ {
+ // NOTE: The guest is able to receive on 0.0.0.0 without it being limited to the chosen network interface.
+ // This is because it must get loopback traffic as well. This could allow other network traffic to leak in.
+
+ BaseSocket.Bind(localEP);
+ }
+
+ public void Close()
+ {
+ BaseSocket.Close();
+ }
+
+ public void Connect(EndPoint remoteEP)
+ {
+ EnsureNetworkInterfaceBound();
+
+ BaseSocket.Connect(remoteEP);
+ }
+
+ public void Disconnect(bool reuseSocket)
+ {
+ BaseSocket.Disconnect(reuseSocket);
+ }
+
+ public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue)
+ {
+ BaseSocket.GetSocketOption(optionLevel, optionName, optionValue);
+ }
+
+ public void Listen(int backlog)
+ {
+ BaseSocket.Listen(backlog);
+ }
+
+ public int Receive(Span buffer)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.Receive(buffer);
+ }
+
+ public int Receive(Span buffer, SocketFlags flags)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.Receive(buffer, flags);
+ }
+
+ public int Receive(Span buffer, SocketFlags flags, out SocketError socketError)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.Receive(buffer, flags, out socketError);
+ }
+
+ public int ReceiveFrom(Span buffer, SocketFlags flags, ref EndPoint remoteEP)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.ReceiveFrom(buffer, flags, ref remoteEP);
+ }
+
+ public int Send(ReadOnlySpan buffer)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.Send(buffer);
+ }
+
+ public int Send(ReadOnlySpan buffer, SocketFlags flags)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.Send(buffer, flags);
+ }
+
+ public int Send(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.Send(buffer, flags, out socketError);
+ }
+
+ public int SendTo(ReadOnlySpan buffer, SocketFlags flags, EndPoint remoteEP)
+ {
+ EnsureNetworkInterfaceBound();
+
+ return BaseSocket.SendTo(buffer, flags, remoteEP);
+ }
+
+ public bool Poll(int microSeconds, SelectMode mode)
+ {
+ return BaseSocket.Poll(microSeconds, mode);
+ }
+
+ public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
+ {
+ BaseSocket.SetSocketOption(optionLevel, optionName, optionValue);
+ }
+
+ public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue)
+ {
+ BaseSocket.SetSocketOption(optionLevel, optionName, optionValue);
+ }
+
+ public void Shutdown(SocketShutdown how)
+ {
+ BaseSocket.Shutdown(how);
+ }
+
+ public void Dispose()
+ {
+ BaseSocket.Dispose();
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/ISocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/ISocket.cs
new file mode 100644
index 000000000..b7055f08b
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/ISocket.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+
+namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
+{
+ interface ISocketImpl : IDisposable
+ {
+ EndPoint RemoteEndPoint { get; }
+ EndPoint LocalEndPoint { get; }
+ bool Connected { get; }
+ bool IsBound { get; }
+
+ AddressFamily AddressFamily { get; }
+ SocketType SocketType { get; }
+ ProtocolType ProtocolType { get; }
+
+ bool Blocking { get; set; }
+ int Available { get; }
+
+ int Receive(Span buffer);
+ int Receive(Span buffer, SocketFlags flags);
+ int Receive(Span buffer, SocketFlags flags, out SocketError socketError);
+ int ReceiveFrom(Span buffer, SocketFlags flags, ref EndPoint remoteEP);
+
+ int Send(ReadOnlySpan buffer);
+ int Send(ReadOnlySpan buffer, SocketFlags flags);
+ int Send(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError);
+ int SendTo(ReadOnlySpan buffer, SocketFlags flags, EndPoint remoteEP);
+
+ bool Poll(int microSeconds, SelectMode mode);
+
+ ISocketImpl Accept();
+
+ void Bind(EndPoint localEP);
+ void Connect(EndPoint remoteEP);
+ void Listen(int backlog);
+
+ void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue);
+ void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue);
+ void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue);
+
+ void Shutdown(SocketShutdown how);
+ void Disconnect(bool reuseSocket);
+ void Close();
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs
new file mode 100644
index 000000000..485a7f86b
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs
@@ -0,0 +1,74 @@
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+
+namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
+{
+ static class SocketHelpers
+ {
+ private static LdnProxy _proxy;
+
+ public static void Select(List readEvents, List writeEvents, List errorEvents, int timeout)
+ {
+ var readDefault = readEvents.Select(x => (x as DefaultSocket)?.BaseSocket).Where(x => x != null).ToList();
+ var writeDefault = writeEvents.Select(x => (x as DefaultSocket)?.BaseSocket).Where(x => x != null).ToList();
+ var errorDefault = errorEvents.Select(x => (x as DefaultSocket)?.BaseSocket).Where(x => x != null).ToList();
+
+ if (readDefault.Count != 0 || writeDefault.Count != 0 || errorDefault.Count != 0)
+ {
+ Socket.Select(readDefault, writeDefault, errorDefault, timeout);
+ }
+
+ void FilterSockets(List removeFrom, List selectedSockets, Func ldnCheck)
+ {
+ removeFrom.RemoveAll(socket =>
+ {
+ switch (socket)
+ {
+ case DefaultSocket dsocket:
+ return !selectedSockets.Contains(dsocket.BaseSocket);
+ case LdnProxySocket psocket:
+ return !ldnCheck(psocket);
+ default:
+ throw new NotImplementedException();
+ }
+ });
+ };
+
+ FilterSockets(readEvents, readDefault, (socket) => socket.Readable);
+ FilterSockets(writeEvents, writeDefault, (socket) => socket.Writable);
+ FilterSockets(errorEvents, errorDefault, (socket) => socket.Error);
+ }
+
+ public static void RegisterProxy(LdnProxy proxy)
+ {
+ if (_proxy != null)
+ {
+ UnregisterProxy();
+ }
+
+ _proxy = proxy;
+ }
+
+ public static void UnregisterProxy()
+ {
+ _proxy?.Dispose();
+ _proxy = null;
+ }
+
+ public static ISocketImpl CreateSocket(AddressFamily domain, SocketType type, ProtocolType protocol, string lanInterfaceId)
+ {
+ if (_proxy != null)
+ {
+ if (_proxy.Supported(domain, type, protocol))
+ {
+ return new LdnProxySocket(domain, type, protocol, _proxy);
+ }
+ }
+
+ return new DefaultSocket(domain, type, protocol, lanInterfaceId);
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
index 2ec0f744e..3ff48b883 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
// TODO: Load Environment from the savedata.
address = address.Replace("%", IManager.NsdSettings.Environment);
- resolvedAddress = "";
+ resolvedAddress = string.Empty;
if (IManager.NsdSettings == null)
{
diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
index 39af90383..5b2de13f0 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
@@ -292,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
{
string host = MemoryHelper.ReadAsciiString(context.Memory, inputBufferPosition, (int)inputBufferSize);
- if (!context.Device.Configuration.EnableInternetAccess)
+ if (host != "localhost" && !context.Device.Configuration.EnableInternetAccess)
{
Logger.Info?.Print(LogClass.ServiceSfdnsres, $"Guest network access disabled, DNS Blocked: {host}");
diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs
index 8cc761baf..dc33dd6a5 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs
@@ -1,5 +1,6 @@
using Ryujinx.HLE.HOS.Services.Sockets.Bsd;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl;
+using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy;
using Ryujinx.HLE.HOS.Services.Ssl.Types;
using System;
using System.IO;
@@ -116,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
public ResultCode Handshake(string hostName)
{
StartSslOperation();
- _stream = new SslStream(new NetworkStream(((ManagedSocket)Socket).Socket, false), false, null, null);
+ _stream = new SslStream(new NetworkStream(((DefaultSocket)((ManagedSocket)Socket).Socket).BaseSocket, false), false, null, null);
hostName = RetrieveHostName(hostName);
_stream.AuthenticateAsClient(hostName, null, TranslateSslVersion(_sslVersion), false);
EndSslOperation();
diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs
index 1df280dce..25c89baec 100644
--- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs
+++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs
@@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
if (size < 0)
{
- return "";
+ return string.Empty;
}
ReadOnlySpan data = ReadInPlace((size + 1) * 2);
diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
index a2b1fb524..edb441a0a 100644
--- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
@@ -166,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
private ResultCode OpenDisplayImpl(ServiceCtx context, string name)
{
- if (name == "")
+ if (name == string.Empty)
{
return ResultCode.InvalidValue;
}
diff --git a/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs
index 83ad5d7e8..1caedb51e 100644
--- a/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs
+++ b/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.Loaders.Executables
Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer);
if (fsSdkMatch.Success)
{
- stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", "")}");
+ stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", string.Empty)}");
}
MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer);
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs
index 3904d660e..cd215781f 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs
@@ -89,7 +89,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
}
- string programName = "";
+ string programName = string.Empty;
if (!isHomebrew && programId > 0x010000000000FFFF)
{
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs
index 6c2a19894..e3ae9bf5f 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.Loaders.Processes
var nacpData = new BlitStruct(1);
ulong programId = metaLoader.GetProgramId();
- device.Configuration.VirtualFileSystem.ModLoader.CollectMods(new[] { programId });
+ device.Configuration.VirtualFileSystem.ModLoader.CollectMods([programId]);
if (programId != 0)
{
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
index 12d9c8bd9..a0e7e0fa1 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
@@ -145,7 +145,7 @@ namespace Ryujinx.HLE.Loaders.Processes
IFileSystem dummyExeFs = null;
Stream romfsStream = null;
- string programName = "";
+ string programName = string.Empty;
ulong programId = 0000000000000000;
// Load executable.
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
index cf4eb416e..33aee1c4c 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
@@ -255,7 +255,7 @@ namespace Ryujinx.HLE.Loaders.Processes
{
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()),
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId),
- _ => "",
+ _ => string.Empty
}).ToUpper());
ulong[] nsoBase = new ulong[executables.Length];
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs
index 10561a5a1..e187b2360 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs
@@ -85,8 +85,8 @@ namespace Ryujinx.HLE.Loaders.Processes
}
// TODO: LibHac npdm currently doesn't support version field.
- string version = ProgramId > 0x0100000000007FFF
- ? DisplayVersion
+ string version = ProgramId > 0x0100000000007FFF
+ ? DisplayVersion
: device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?";
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]");
diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
index a7bb3cd7f..5f7f6db69 100644
--- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -29,6 +29,7 @@
+
diff --git a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
index ce52f84d9..8c4854a11 100644
--- a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
+++ b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
@@ -117,8 +117,9 @@ namespace Ryujinx.Headless.SDL2.OpenGL
GraphicsDebugLevel glLogLevel,
AspectRatio aspectRatio,
bool enableMouse,
- HideCursorMode hideCursorMode)
- : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode)
+ HideCursorMode hideCursorMode,
+ bool ignoreControllerApplet)
+ : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet)
{
_glLogLevel = glLogLevel;
}
diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs
index 2f86f3ebf..8078ca5e4 100644
--- a/src/Ryujinx.Headless.SDL2/Options.cs
+++ b/src/Ryujinx.Headless.SDL2/Options.cs
@@ -225,6 +225,9 @@ namespace Ryujinx.Headless.SDL2
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")]
public bool IgnoreMissingServices { get; set; }
+
+ [Option("ignore-controller-applet", Required = false, Default = false, HelpText = "Enable ignoring the controller applet when your game loses connection to your controller.")]
+ public bool IgnoreControllerApplet { get; set; }
// Values
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index b6ccb2ac4..e3bbd1e51 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -444,8 +444,7 @@ namespace Ryujinx.Headless.SDL2
{
Logger.AddTarget(new AsyncLogTargetWrapper(
new FileLogTarget("file", logFile),
- 1000,
- AsyncLogTargetOverflowAction.Block
+ 1000
));
}
else
@@ -506,8 +505,8 @@ namespace Ryujinx.Headless.SDL2
private static WindowBase CreateWindow(Options options)
{
return options.GraphicsBackend == GraphicsBackend.Vulkan
- ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode)
- : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode);
+ ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet)
+ : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet);
}
private static IRenderer CreateRenderer(Options options, WindowBase window)
@@ -578,7 +577,10 @@ namespace Ryujinx.Headless.SDL2
options.AudioVolume,
options.UseHypervisor ?? true,
options.MultiplayerLanInterfaceId,
- Common.Configuration.Multiplayer.MultiplayerMode.Disabled);
+ Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
+ false,
+ "",
+ "");
return new Switch(configuration);
}
diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
index 610229544..ebda97b46 100644
--- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
+++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
@@ -1,4 +1,4 @@
-
+
net8.0
@@ -17,7 +17,7 @@
-
+
diff --git a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
index fb73ca335..b88e0fe83 100644
--- a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
+++ b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
@@ -17,8 +17,9 @@ namespace Ryujinx.Headless.SDL2.Vulkan
GraphicsDebugLevel glLogLevel,
AspectRatio aspectRatio,
bool enableMouse,
- HideCursorMode hideCursorMode)
- : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode)
+ HideCursorMode hideCursorMode,
+ bool ignoreControllerApplet)
+ : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet)
{
_glLogLevel = glLogLevel;
}
diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs
index 4addf62b5..6d681e100 100644
--- a/src/Ryujinx.Headless.SDL2/WindowBase.cs
+++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs
@@ -1,3 +1,4 @@
+using Humanizer;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
@@ -85,13 +86,15 @@ namespace Ryujinx.Headless.SDL2
private readonly AspectRatio _aspectRatio;
private readonly bool _enableMouse;
+ private readonly bool _ignoreControllerApplet;
public WindowBase(
InputManager inputManager,
GraphicsDebugLevel glLogLevel,
AspectRatio aspectRatio,
bool enableMouse,
- HideCursorMode hideCursorMode)
+ HideCursorMode hideCursorMode,
+ bool ignoreControllerApplet)
{
MouseDriver = new SDL2MouseDriver(hideCursorMode);
_inputManager = inputManager;
@@ -107,6 +110,7 @@ namespace Ryujinx.Headless.SDL2
_gpuDoneEvent = new ManualResetEvent(false);
_aspectRatio = aspectRatio;
_enableMouse = enableMouse;
+ _ignoreControllerApplet = ignoreControllerApplet;
HostUITheme = new HeadlessHostUiTheme();
SDL2Driver.Instance.Initialize();
@@ -483,12 +487,14 @@ namespace Ryujinx.Headless.SDL2
public bool DisplayMessageDialog(ControllerAppletUIArgs args)
{
+ if (_ignoreControllerApplet) return false;
+
string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}";
- string message = $"Application requests {playerCount} player(s) with:\n\n"
+ string message = $"Application requests {playerCount} {"player".ToQuantity(args.PlayerCountMin + args.PlayerCountMax, ShowQuantityAs.None)} with:\n\n"
+ $"TYPES: {args.SupportedStyles}\n\n"
+ $"PLAYERS: {string.Join(", ", args.SupportedPlayers)}\n\n"
- + (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : "")
+ + (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : string.Empty)
+ "Please reconfigure Input now and then press OK.";
return DisplayMessageDialog("Controller Applet", message);
diff --git a/src/Ryujinx.Horizon.Common/ResultNames.cs b/src/Ryujinx.Horizon.Common/ResultNames.cs
index 55a33d680..25d04b308 100644
--- a/src/Ryujinx.Horizon.Common/ResultNames.cs
+++ b/src/Ryujinx.Horizon.Common/ResultNames.cs
@@ -1235,14 +1235,14 @@ namespace Ryujinx.Horizon.Common
{ 0x412, "NotFound" },
{ 0x612, "NotEnoughBuffer" },
{ 0xCA12, "Cancelled" },
- { 0x7FE12, "" },
- { 0xFA212, "" },
+ { 0x7FE12, string.Empty },
+ { 0xFA212, string.Empty },
{ 0xFA612, "InvalidTaskId" },
{ 0xFB612, "InvalidSize" },
{ 0xFCA12, "TaskCancelled" },
{ 0xFCC12, "TaskNotCompleted" },
{ 0xFCE12, "TaskQueueNotAvailable" },
- { 0x106A12, "" },
+ { 0x106A12, string.Empty },
{ 0x106C12, "OutOfRpcTask" },
{ 0x109612, "InvalidCategory" },
{ 0x214, "OutOfKeyResource" },
diff --git a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs
index 6acb9be97..03f61c218 100644
--- a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs
+++ b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs
@@ -221,7 +221,7 @@ namespace Ryujinx.Horizon.Sdk.Ngc.Detail
if (includeMultiWord)
{
int lastMultiWordIndex = 0;
- string multiWord = "";
+ string multiWord = string.Empty;
while (_multiWordMap.Has(nodePlainIndex))
{
diff --git a/src/Ryujinx.UI.Common/App/ApplicationData.cs b/src/Ryujinx.UI.Common/App/ApplicationData.cs
index b1e346291..7aa0dccaa 100644
--- a/src/Ryujinx.UI.Common/App/ApplicationData.cs
+++ b/src/Ryujinx.UI.Common/App/ApplicationData.cs
@@ -27,6 +27,8 @@ namespace Ryujinx.UI.App.Common
public ulong Id { get; set; }
public string Developer { get; set; } = "Unknown";
public string Version { get; set; } = "0";
+ public int PlayerCount { get; set; }
+ public int GameCount { get; set; }
public TimeSpan TimePlayed { get; set; }
public DateTime? LastPlayed { get; set; }
public string FileExtension { get; set; }
@@ -162,7 +164,7 @@ namespace Ryujinx.UI.App.Common
NsoReader reader = new();
reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure();
- return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16];
+ return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", string.Empty).ToUpper()[..16];
}
}
}
diff --git a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs
index 044eccbea..174db51ad 100644
--- a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs
+++ b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs
@@ -12,6 +12,7 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
@@ -27,10 +28,12 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading;
+using System.Threading.Tasks;
using ContentType = LibHac.Ncm.ContentType;
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
using Path = System.IO.Path;
@@ -41,8 +44,10 @@ namespace Ryujinx.UI.App.Common
{
public class ApplicationLibrary
{
+ public static string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
public Language DesiredLanguage { get; set; }
public event EventHandler ApplicationCountUpdated;
+ public event EventHandler LdnGameDataReceived;
public readonly IObservableCache Applications;
public readonly IObservableCache<(TitleUpdateModel TitleUpdate, bool IsSelected), TitleUpdateModel> TitleUpdates;
@@ -62,6 +67,7 @@ namespace Ryujinx.UI.App.Common
private readonly SourceCache<(DownloadableContentModel Dlc, bool IsEnabled), DownloadableContentModel> _downloadableContents = new(it => it.Dlc);
private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
+ private static readonly LdnGameDataSerializerContext _ldnDataSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public ApplicationLibrary(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel)
{
@@ -687,7 +693,7 @@ namespace Ryujinx.UI.App.Common
(Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.UI.ShownFileTypes.PFS0) ||
(Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.UI.ShownFileTypes.XCI) ||
(Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.UI.ShownFileTypes.NCA) ||
- (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.UI.ShownFileTypes.NRO) ||
+ (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.UI.ShownFileTypes.NRO) ||
(Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.UI.ShownFileTypes.NSO)
);
@@ -719,6 +725,7 @@ namespace Ryujinx.UI.App.Common
}
}
+
// Loops through applications list, creating a struct and then firing an event containing the struct for each application
foreach (string applicationPath in applicationPaths)
{
@@ -775,6 +782,46 @@ namespace Ryujinx.UI.App.Common
}
}
+ public async Task RefreshLdn()
+ {
+
+ if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu)
+ {
+ try
+ {
+ string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer;
+ if (string.IsNullOrEmpty(ldnWebHost))
+ {
+ ldnWebHost = DefaultLanPlayWebHost;
+ }
+ IEnumerable ldnGameDataArray = Array.Empty();
+ using HttpClient httpClient = new HttpClient();
+ string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
+ ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
+ var evt = new LdnGameDataReceivedEventArgs
+ {
+ LdnData = ldnGameDataArray
+ };
+ LdnGameDataReceived?.Invoke(null, evt);
+ }
+ 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}");
+ LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs()
+ {
+ LdnData = Array.Empty()
+ });
+ }
+ }
+ else
+ {
+ LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs()
+ {
+ LdnData = Array.Empty()
+ });
+ }
+ }
+
// Replace the currently stored DLC state for the game with the provided DLC state.
public void SaveDownloadableContentsForGame(ApplicationData application, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
{
diff --git a/src/Ryujinx.UI.Common/App/LdnGameData.cs b/src/Ryujinx.UI.Common/App/LdnGameData.cs
new file mode 100644
index 000000000..6c784c991
--- /dev/null
+++ b/src/Ryujinx.UI.Common/App/LdnGameData.cs
@@ -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 Players { get; set; }
+ }
+}
diff --git a/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs b/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs
new file mode 100644
index 000000000..7c7454411
--- /dev/null
+++ b/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.UI.App.Common
+{
+ public class LdnGameDataReceivedEventArgs : EventArgs
+ {
+ public IEnumerable LdnData { get; set; }
+ }
+}
diff --git a/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs b/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs
new file mode 100644
index 000000000..ce8edcdb6
--- /dev/null
+++ b/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Ryujinx.UI.App.Common
+{
+ [JsonSerializable(typeof(IEnumerable))]
+ internal partial class LdnGameDataSerializerContext : JsonSerializerContext
+ {
+
+ }
+}
diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs
index 1eb970016..80ba1b186 100644
--- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs
+++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs
@@ -16,7 +16,7 @@ namespace Ryujinx.UI.Common.Configuration
///
/// The current version of the file format
///
- public const int CurrentVersion = 55;
+ public const int CurrentVersion = 56;
///
/// Version of the configuration file format
@@ -173,6 +173,11 @@ namespace Ryujinx.UI.Common.Configuration
///
public bool RememberWindowState { get; set; }
+ ///
+ /// Enables or disables the redesigned title bar
+ ///
+ public bool ShowTitleBar { get; set; }
+
///
/// Enables hardware-accelerated rendering for Avalonia
///
@@ -293,16 +298,6 @@ namespace Ryujinx.UI.Common.Configuration
///
public string LanguageCode { get; set; }
- ///
- /// Enable or disable custom themes in the GUI
- ///
- public bool EnableCustomTheme { get; set; }
-
- ///
- /// Path to custom GUI theme
- ///
- public string CustomThemePath { get; set; }
-
///
/// Chooses the base style // Not Used
///
@@ -397,6 +392,21 @@ namespace Ryujinx.UI.Common.Configuration
///
public string MultiplayerLanInterfaceId { get; set; }
+ ///
+ /// Disable P2p Toggle
+ ///
+ public bool MultiplayerDisableP2p { get; set; }
+
+ ///
+ /// Local network passphrase, for private networks.
+ ///
+ public string MultiplayerLdnPassphrase { get; set; }
+
+ ///
+ /// Custom LDN Server
+ ///
+ public string LdnServer { get; set; }
+
///
/// Uses Hypervisor over JIT if available
///
diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs
new file mode 100644
index 000000000..65dd88106
--- /dev/null
+++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs
@@ -0,0 +1,718 @@
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Configuration.Hid.Controller;
+using Ryujinx.Common.Configuration.Hid.Keyboard;
+using Ryujinx.Common.Configuration.Multiplayer;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE;
+using Ryujinx.UI.Common.Configuration.System;
+using Ryujinx.UI.Common.Configuration.UI;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.UI.Common.Configuration
+{
+ public partial class ConfigurationState
+ {
+ public void Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath)
+ {
+ bool configurationFileUpdated = false;
+
+ if (configurationFileFormat.Version is < 0 or > ConfigurationFileFormat.CurrentVersion)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
+
+ LoadDefault();
+ }
+
+ if (configurationFileFormat.Version < 2)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 2.");
+
+ configurationFileFormat.SystemRegion = Region.USA;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 3)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 3.");
+
+ configurationFileFormat.SystemTimeZone = "UTC";
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 4)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 4.");
+
+ configurationFileFormat.MaxAnisotropy = -1;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 5)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 5.");
+
+ configurationFileFormat.SystemTimeOffset = 0;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 8)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 8.");
+
+ configurationFileFormat.EnablePtc = true;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 9)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 9.");
+
+ configurationFileFormat.ColumnSort = new ColumnSort
+ {
+ SortColumnId = 0,
+ SortAscending = false,
+ };
+
+ configurationFileFormat.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.F1,
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 10)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 10.");
+
+ configurationFileFormat.AudioBackend = AudioBackend.OpenAl;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 11)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 11.");
+
+ configurationFileFormat.ResScale = 1;
+ configurationFileFormat.ResScaleCustom = 1.0f;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 12)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 12.");
+
+ configurationFileFormat.LoggingGraphicsDebugLevel = GraphicsDebugLevel.None;
+
+ configurationFileUpdated = true;
+ }
+
+ // configurationFileFormat.Version == 13 -> LDN1
+
+ if (configurationFileFormat.Version < 14)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 14.");
+
+ configurationFileFormat.CheckUpdatesOnStart = true;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 16)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 16.");
+
+ configurationFileFormat.EnableShaderCache = true;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 17)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 17.");
+
+ configurationFileFormat.StartFullscreen = false;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 18)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 18.");
+
+ configurationFileFormat.AspectRatio = AspectRatio.Fixed16x9;
+
+ configurationFileUpdated = true;
+ }
+
+ // configurationFileFormat.Version == 19 -> LDN2
+
+ if (configurationFileFormat.Version < 20)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 20.");
+
+ configurationFileFormat.ShowConfirmExit = true;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 21)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 21.");
+
+ // Initialize network config.
+
+ configurationFileFormat.MultiplayerMode = MultiplayerMode.Disabled;
+ configurationFileFormat.MultiplayerLanInterfaceId = "0";
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 22)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 22.");
+
+ configurationFileFormat.HideCursor = HideCursorMode.Never;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 24)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 24.");
+
+ configurationFileFormat.InputConfig = new List
+ {
+ new StandardKeyboardInputConfig
+ {
+ Version = InputConfig.CurrentVersion,
+ Backend = InputBackendType.WindowKeyboard,
+ Id = "0",
+ PlayerIndex = PlayerIndex.Player1,
+ ControllerType = ControllerType.ProController,
+ LeftJoycon = new LeftJoyconCommonConfig
+ {
+ DpadUp = Key.Up,
+ DpadDown = Key.Down,
+ DpadLeft = Key.Left,
+ DpadRight = Key.Right,
+ ButtonMinus = Key.Minus,
+ ButtonL = Key.E,
+ ButtonZl = Key.Q,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound,
+ },
+ LeftJoyconStick = new JoyconConfigKeyboardStick
+ {
+ StickUp = Key.W,
+ StickDown = Key.S,
+ StickLeft = Key.A,
+ StickRight = Key.D,
+ StickButton = Key.F,
+ },
+ RightJoycon = new RightJoyconCommonConfig
+ {
+ ButtonA = Key.Z,
+ ButtonB = Key.X,
+ ButtonX = Key.C,
+ ButtonY = Key.V,
+ ButtonPlus = Key.Plus,
+ ButtonR = Key.U,
+ ButtonZr = Key.O,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound,
+ },
+ RightJoyconStick = new JoyconConfigKeyboardStick
+ {
+ StickUp = Key.I,
+ StickDown = Key.K,
+ StickLeft = Key.J,
+ StickRight = Key.L,
+ StickButton = Key.H,
+ },
+ },
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 25)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 25.");
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 26)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 26.");
+
+ configurationFileFormat.MemoryManagerMode = MemoryManagerMode.HostMappedUnsafe;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 27)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 27.");
+
+ configurationFileFormat.EnableMouse = false;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 28)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 28.");
+
+ configurationFileFormat.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.F1,
+ Screenshot = Key.F8,
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 29)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 29.");
+
+ configurationFileFormat.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.F1,
+ Screenshot = Key.F8,
+ ShowUI = Key.F4,
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 30)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 30.");
+
+ foreach (InputConfig config in configurationFileFormat.InputConfig)
+ {
+ if (config is StandardControllerInputConfig controllerConfig)
+ {
+ controllerConfig.Rumble = new RumbleConfigController
+ {
+ EnableRumble = false,
+ StrongRumble = 1f,
+ WeakRumble = 1f,
+ };
+ }
+ }
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 31)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 31.");
+
+ configurationFileFormat.BackendThreading = BackendThreading.Auto;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 32)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 32.");
+
+ configurationFileFormat.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
+ Screenshot = configurationFileFormat.Hotkeys.Screenshot,
+ ShowUI = configurationFileFormat.Hotkeys.ShowUI,
+ Pause = Key.F5,
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 33)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 33.");
+
+ configurationFileFormat.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
+ Screenshot = configurationFileFormat.Hotkeys.Screenshot,
+ ShowUI = configurationFileFormat.Hotkeys.ShowUI,
+ Pause = configurationFileFormat.Hotkeys.Pause,
+ ToggleMute = Key.F2,
+ };
+
+ configurationFileFormat.AudioVolume = 1;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 34)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 34.");
+
+ configurationFileFormat.EnableInternetAccess = false;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 35)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 35.");
+
+ foreach (InputConfig config in configurationFileFormat.InputConfig)
+ {
+ if (config is StandardControllerInputConfig controllerConfig)
+ {
+ controllerConfig.RangeLeft = 1.0f;
+ controllerConfig.RangeRight = 1.0f;
+ }
+ }
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 36)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 36.");
+
+ configurationFileFormat.LoggingEnableTrace = false;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 37)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 37.");
+
+ configurationFileFormat.ShowConsole = true;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 38)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 38.");
+
+ configurationFileFormat.BaseStyle = "Dark";
+ configurationFileFormat.GameListViewMode = 0;
+ configurationFileFormat.ShowNames = true;
+ configurationFileFormat.GridSize = 2;
+ configurationFileFormat.LanguageCode = "en_US";
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 39)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 39.");
+
+ configurationFileFormat.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
+ Screenshot = configurationFileFormat.Hotkeys.Screenshot,
+ ShowUI = configurationFileFormat.Hotkeys.ShowUI,
+ Pause = configurationFileFormat.Hotkeys.Pause,
+ ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
+ ResScaleUp = Key.Unbound,
+ ResScaleDown = Key.Unbound,
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 40)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 40.");
+
+ configurationFileFormat.GraphicsBackend = GraphicsBackend.OpenGl;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 41)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 41.");
+
+ configurationFileFormat.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
+ Screenshot = configurationFileFormat.Hotkeys.Screenshot,
+ ShowUI = configurationFileFormat.Hotkeys.ShowUI,
+ Pause = configurationFileFormat.Hotkeys.Pause,
+ ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
+ ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp,
+ ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown,
+ VolumeUp = Key.Unbound,
+ VolumeDown = Key.Unbound,
+ };
+ }
+
+ if (configurationFileFormat.Version < 42)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42.");
+
+ configurationFileFormat.EnableMacroHLE = true;
+ }
+
+ if (configurationFileFormat.Version < 43)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 43.");
+
+ configurationFileFormat.UseHypervisor = true;
+ }
+
+ if (configurationFileFormat.Version < 44)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 44.");
+
+ configurationFileFormat.AntiAliasing = AntiAliasing.None;
+ configurationFileFormat.ScalingFilter = ScalingFilter.Bilinear;
+ configurationFileFormat.ScalingFilterLevel = 80;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 45)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 45.");
+
+ configurationFileFormat.ShownFileTypes = new ShownFileTypes
+ {
+ NSP = true,
+ PFS0 = true,
+ XCI = true,
+ NCA = true,
+ NRO = true,
+ NSO = true,
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 46)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 46.");
+
+ configurationFileFormat.MultiplayerLanInterfaceId = "0";
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 47)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 47.");
+
+ configurationFileFormat.WindowStartup = new WindowStartup
+ {
+ WindowPositionX = 0,
+ WindowPositionY = 0,
+ WindowSizeHeight = 760,
+ WindowSizeWidth = 1280,
+ WindowMaximized = false,
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 48)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 48.");
+
+ configurationFileFormat.EnableColorSpacePassthrough = false;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 49)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 49.");
+
+ if (OperatingSystem.IsMacOS())
+ {
+ AppDataManager.FixMacOSConfigurationFolders();
+ }
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 50)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 50.");
+
+ configurationFileFormat.EnableHardwareAcceleration = true;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 51)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 51.");
+
+ configurationFileFormat.RememberWindowState = true;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 52)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 52.");
+
+ configurationFileFormat.AutoloadDirs = [];
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 53)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 53.");
+
+ configurationFileFormat.EnableLowPowerPtc = false;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 54)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 54.");
+
+ configurationFileFormat.DramSize = MemoryConfiguration.MemoryConfiguration4GiB;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 55)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 55.");
+
+ configurationFileFormat.IgnoreApplet = false;
+
+ configurationFileUpdated = true;
+ }
+
+ if (configurationFileFormat.Version < 56)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 56.");
+
+ configurationFileFormat.ShowTitleBar = !OperatingSystem.IsWindows();
+
+ configurationFileUpdated = true;
+ }
+
+ Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
+ Graphics.ResScale.Value = configurationFileFormat.ResScale;
+ Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
+ Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy;
+ Graphics.AspectRatio.Value = configurationFileFormat.AspectRatio;
+ Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath;
+ Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
+ Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend;
+ Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu;
+ Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing;
+ Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter;
+ Graphics.ScalingFilterLevel.Value = configurationFileFormat.ScalingFilterLevel;
+ Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug;
+ Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub;
+ Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo;
+ Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn;
+ Logger.EnableError.Value = configurationFileFormat.LoggingEnableError;
+ Logger.EnableTrace.Value = configurationFileFormat.LoggingEnableTrace;
+ Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest;
+ Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog;
+ Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses;
+ Logger.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel;
+ System.Language.Value = configurationFileFormat.SystemLanguage;
+ System.Region.Value = configurationFileFormat.SystemRegion;
+ System.TimeZone.Value = configurationFileFormat.SystemTimeZone;
+ System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset;
+ System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
+ EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
+ CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
+ ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit;
+ IgnoreApplet.Value = configurationFileFormat.IgnoreApplet;
+ RememberWindowState.Value = configurationFileFormat.RememberWindowState;
+ ShowTitleBar.Value = configurationFileFormat.ShowTitleBar;
+ EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration;
+ HideCursor.Value = configurationFileFormat.HideCursor;
+ Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
+ Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
+ Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression;
+ Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE;
+ Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough;
+ System.EnablePtc.Value = configurationFileFormat.EnablePtc;
+ System.EnableLowPowerPtc.Value = configurationFileFormat.EnableLowPowerPtc;
+ System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess;
+ System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
+ System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
+ System.AudioBackend.Value = configurationFileFormat.AudioBackend;
+ System.AudioVolume.Value = configurationFileFormat.AudioVolume;
+ System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode;
+ System.DramSize.Value = configurationFileFormat.DramSize;
+ System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;
+ System.UseHypervisor.Value = configurationFileFormat.UseHypervisor;
+ UI.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn;
+ UI.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn;
+ UI.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn;
+ UI.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn;
+ UI.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn;
+ UI.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn;
+ UI.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn;
+ UI.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn;
+ UI.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn;
+ UI.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn;
+ UI.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId;
+ UI.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending;
+ UI.GameDirs.Value = configurationFileFormat.GameDirs;
+ UI.AutoloadDirs.Value = configurationFileFormat.AutoloadDirs ?? [];
+ UI.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP;
+ UI.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0;
+ UI.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI;
+ UI.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA;
+ UI.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO;
+ UI.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO;
+ UI.LanguageCode.Value = configurationFileFormat.LanguageCode;
+ UI.BaseStyle.Value = configurationFileFormat.BaseStyle;
+ UI.GameListViewMode.Value = configurationFileFormat.GameListViewMode;
+ UI.ShowNames.Value = configurationFileFormat.ShowNames;
+ UI.IsAscendingOrder.Value = configurationFileFormat.IsAscendingOrder;
+ UI.GridSize.Value = configurationFileFormat.GridSize;
+ UI.ApplicationSort.Value = configurationFileFormat.ApplicationSort;
+ UI.StartFullscreen.Value = configurationFileFormat.StartFullscreen;
+ UI.ShowConsole.Value = configurationFileFormat.ShowConsole;
+ UI.WindowStartup.WindowSizeWidth.Value = configurationFileFormat.WindowStartup.WindowSizeWidth;
+ UI.WindowStartup.WindowSizeHeight.Value = configurationFileFormat.WindowStartup.WindowSizeHeight;
+ UI.WindowStartup.WindowPositionX.Value = configurationFileFormat.WindowStartup.WindowPositionX;
+ UI.WindowStartup.WindowPositionY.Value = configurationFileFormat.WindowStartup.WindowPositionY;
+ UI.WindowStartup.WindowMaximized.Value = configurationFileFormat.WindowStartup.WindowMaximized;
+ Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
+ Hid.EnableMouse.Value = configurationFileFormat.EnableMouse;
+ Hid.Hotkeys.Value = configurationFileFormat.Hotkeys;
+ Hid.InputConfig.Value = configurationFileFormat.InputConfig ?? [];
+
+ Multiplayer.LanInterfaceId.Value = configurationFileFormat.MultiplayerLanInterfaceId;
+ Multiplayer.Mode.Value = configurationFileFormat.MultiplayerMode;
+ Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p;
+ Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase;
+ Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;
+
+ if (configurationFileUpdated)
+ {
+ ToFileFormat().SaveConfig(configurationFilePath);
+
+ Ryujinx.Common.Logging.Logger.Notice.Print(LogClass.Application, $"Configuration file updated to version {ConfigurationFileFormat.CurrentVersion}");
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs
new file mode 100644
index 000000000..9be8f4df7
--- /dev/null
+++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs
@@ -0,0 +1,700 @@
+using ARMeilleure;
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Configuration.Multiplayer;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE;
+using Ryujinx.UI.Common.Configuration.System;
+using Ryujinx.UI.Common.Helper;
+using System.Collections.Generic;
+
+namespace Ryujinx.UI.Common.Configuration
+{
+ public partial class ConfigurationState
+ {
+ ///
+ /// UI configuration section
+ ///
+ public class UISection
+ {
+ public class Columns
+ {
+ public ReactiveObject FavColumn { get; private set; }
+ public ReactiveObject IconColumn { get; private set; }
+ public ReactiveObject AppColumn { get; private set; }
+ public ReactiveObject DevColumn { get; private set; }
+ public ReactiveObject VersionColumn { get; private set; }
+ public ReactiveObject LdnInfoColumn { get; private set; }
+ public ReactiveObject TimePlayedColumn { get; private set; }
+ public ReactiveObject LastPlayedColumn { get; private set; }
+ public ReactiveObject FileExtColumn { get; private set; }
+ public ReactiveObject FileSizeColumn { get; private set; }
+ public ReactiveObject PathColumn { get; private set; }
+
+ public Columns()
+ {
+ FavColumn = new ReactiveObject();
+ IconColumn = new ReactiveObject();
+ AppColumn = new ReactiveObject();
+ DevColumn = new ReactiveObject();
+ VersionColumn = new ReactiveObject