Compare commits

...

71 Commits

Author SHA1 Message Date
KeatonTheBot
1adc7fa1d4 Fix LedInputView 2026-02-19 11:04:05 -06:00
KeatonTheBot
1973b01c67 UI: Restore FluentAvaloniaUI package, disable animations on app initialization
* Avalonia's built-in color picker is now used when selecting a firmware avatar

* Update Avalonia to 11.3.11, Svg.Controls.Avalonia group to 11.3.9.2, FluentAvalonia to 2.5.0
2026-02-19 11:04:05 -06:00
BowedCascade
d1205dc95d Fix backslash key not mappable in controller settings (ryubing/ryujinx!265)
See merge request ryubing/ryujinx!265
2026-02-18 18:13:15 -06:00
Awesomeangotti
6f95172bb6 Compatability Data Update (ryubing/ryujinx!264)
See merge request ryubing/ryujinx!264
2026-02-17 19:24:01 -06:00
Princess Piplup
8208d43d9e compatiblity/2026-02-17 (ryubing/ryujinx!263)
See merge request ryubing/ryujinx!263
2026-02-17 18:57:50 -06:00
shinyoyo
1260f93aaf Updated ‌Simplified Chinese‌ translation. (ryubing/ryujinx!260)
See merge request ryubing/ryujinx!260
2026-02-09 01:07:22 -06:00
LotP
1b3bf1473d Fix Dual Joy-Con driver and InputView (ryubing/ryujinx!259)
See merge request ryubing/ryujinx!259
2026-01-31 23:12:29 -06:00
LotP
081cdcab0c remap joy-cons (ryubing/ryujinx!258)
See merge request ryubing/ryujinx!258
2026-01-31 17:58:31 -06:00
Coxxs
922775664c audio: Fix crash due to invalid Splitter size (ryubing/ryujinx!257)
See merge request ryubing/ryujinx!257
2026-01-31 11:22:14 -06:00
sh0inx
478b66fd49 HLE: Stubbed IUserLocalCommuniationService SetProtocol (106) (ryubing/ryujinx!253)
See merge request ryubing/ryujinx!253
2026-01-30 20:48:41 -06:00
Coxxs
a16a072155 HLE: Implement 10106 and 10107 in IPrepoService (ryubing/ryujinx!254)
See merge request ryubing/ryujinx!254
2026-01-29 13:45:35 -06:00
Babib3l
a4a0fcd4da General translations updates + fixes (ryubing/ryujinx!248)
See merge request ryubing/ryujinx!248
2026-01-28 07:01:39 -06:00
GreemDev
cc5b60bbca fix AppleHardwareDeviceDriver.IsSupported (no fancy check is needed; it's on any macOS version 10.5 (Leopard) and above) 2026-01-28 00:05:02 -06:00
GreemDev
5ed94c365b add a stack trace for the catch branch of AppleHardwareDeviceDriver.IsSupported 2026-01-27 17:52:45 -06:00
GreemDev
fef93a453a [ci skip] replace all usages of IntPtr with nint 2026-01-27 17:41:46 -06:00
GreemDev
82074eb191 audio backend projects code cleanup 2026-01-27 17:34:51 -06:00
GreemDev
bd388cf4f9 Expose AudioToolkit in UI 2026-01-27 17:28:59 -06:00
Stossy11
d271abe19a [ci skip] Add macOS native Audio Backend (ryubing/ryujinx!252)
See merge request ryubing/ryujinx!252

THIS IS CURRENTLY NOT EXPOSED BY THE UI OR HANDLED BY THE EMULATOR. Expect a commit later to add it to configs, UI, etc.
2026-01-27 17:03:59 -06:00
Hack茶ん
c154f66f26 Update Korean translation (ryubing/ryujinx!251)
See merge request ryubing/ryujinx!251
2026-01-21 18:23:34 -06:00
GreemDev
f556e8b8fb add offline update server catch branch 2026-01-20 13:19:44 -06:00
Babib3l
99feaafbe6 French and Spanish Translations updates on RenderDoc (ryubing/ryujinx!246)
See merge request ryubing/ryujinx!246
2026-01-03 07:23:11 -06:00
GreemDev
fa55608587 RenderDoc API support (ryubing/ryujinx!242)
See merge request ryubing/ryujinx!242
2026-01-01 00:10:21 -06:00
LotP
4c64300576 fix new locale files data loading (ryubing/ryujinx!245)
See merge request ryubing/ryujinx!245
2025-12-31 20:21:35 -06:00
LotP
0a3db19b28 fix language switching 2 (ryubing/ryujinx!244)
See merge request ryubing/ryujinx!244
2025-12-31 10:30:35 -06:00
LotP
453b246faa fix (ryubing/ryujinx!243)
See merge request ryubing/ryujinx!243
2025-12-31 09:15:40 -06:00
LotP
45193dcc8d Fractured Locales Support (ryubing/ryujinx!238)
See merge request ryubing/ryujinx!238
2025-12-27 14:07:56 -06:00
GreemDev
9ebf444644 [ci skip] Code comment 2025-12-25 23:48:10 -06:00
GreemDev
f585b36263 Use new retry flag for uploading built artifacts in CI
(I'm tired of the GitLab randomly HTTP 500ing and causing the entire CI to fail)
2025-12-23 02:16:01 -06:00
GreemDev
a96f20dca5 Removed TypedStringEnumConverter; it exists in .NET now.
As per the remark XMLdoc on the type: Get rid of this converter if dotnet supports similar functionality out of the box.
2025-12-23 01:42:28 -06:00
GreemDev
1e1bcb4a5b storing commit string in github output causes weird CI failures
so let's just not bother, it didn't show anything more than the UI already did anyways
2025-12-23 00:02:02 -06:00
Coxxs
ca76bacd22 gdb: add monitor get mapping (ryubing/ryujinx!215)
See merge request ryubing/ryujinx!215
2025-12-21 22:34:20 -06:00
GreemDev
66f339d265 CI 2.0 (ryubing/ryujinx!237)
See merge request ryubing/ryujinx!237
2025-12-18 22:56:50 -06:00
GreemDev
6cdbdfd329 [ci skip] Pin GitLabCli to 1.4.1 in CI scripts so I can test v2.0 2025-12-18 03:27:43 -06:00
GreemDev
9f817d60d5 oops 2025-12-18 03:05:42 -06:00
GreemDev
5cffc95be6 Make all OSes build on Linux (7zip has a linux version) 2025-12-18 03:01:22 -06:00
LotP
2c0977f6b3 fix pre-action crash (ryubing/ryujinx!236)
See merge request ryubing/ryujinx!236
2025-12-12 14:28:54 -06:00
LotP
3a593b6084 Fix kaddressarbiter crash (ryubing/ryujinx!235)
See merge request ryubing/ryujinx!235
2025-12-06 20:16:43 -06:00
LotP
c3155fcadb Memory Changes 3.2 (ryubing/ryujinx!234)
See merge request ryubing/ryujinx!234
2025-12-06 17:19:19 -06:00
LotP
fd7554425a Update BiquadFilterEffectParameter2.cs (ryubing/ryujinx!233)
See merge request ryubing/ryujinx!233
2025-12-05 07:53:09 -06:00
Princess Piplup
52700f71dc Fix SaveCurrentScreenshot (ryubing/ryujinx!230)
See merge request ryubing/ryujinx!230
2025-12-04 17:35:17 -06:00
Alula
b018a44ff0 Disable coredumps by default on Linux + other minor fixes (ryubing/ryujinx!204)
See merge request ryubing/ryujinx!204
2025-12-04 17:32:26 -06:00
GreemDev
d522bfef62 fix: Force the key install helper to delete key files before copying (not sure why the overwrite boolean does nothing for File.Copy) 2025-12-02 21:23:06 -06:00
KeatonTheBot
39f55b2af3 cpu: Protect against stack overflow caused by deep recursion (ryubing/ryujinx!111)
See merge request ryubing/ryujinx!111
2025-11-19 20:50:23 -06:00
Goodfeat
6126e3dc1e fix: UI: Custom setting was reset to global when changed during gameplay. (ryubing/ryujinx!164)
See merge request ryubing/ryujinx!164
2025-11-19 20:33:35 -06:00
GreemDev
e1c829f91d We no longer offer a dedicated headless build. This script could have been deleted like a year ago lol 2025-11-17 22:47:23 -06:00
Babib3l
6c7dc7646b Translation updates (ryubing/ryujinx!221)
See merge request ryubing/ryujinx!221
2025-11-17 22:34:54 -06:00
GreemDev
862a686c5e UI: Improve "Show Changelog" button in the updater
Now it no longer causes the dialog to disappear (which then promptly re-appears so you can click yes/no to accept/deny the update)
2025-11-17 00:15:58 -06:00
GreemDev
e8751e1c40 more C# 14 partial properties 2025-11-16 19:14:26 -06:00
GreemDev
09748b140a Use the new C# 14 null propagation setter 2025-11-16 19:14:26 -06:00
Coxxs
456db46065 Stub IWriterForApplication: 0 (CreateContextRegistrar) (ryubing/ryujinx!183)
See merge request ryubing/ryujinx!183
2025-11-16 18:24:41 -06:00
GreemDev
7b39ff36c0 docs: compat: ZA LDN works and has for a while 2025-11-16 00:30:37 -06:00
Maki
10476771d3 fix: detect face button layout for gamepads (ryubing/ryujinx!219)
See merge request ryubing/ryujinx!219
2025-11-14 12:51:53 -06:00
Maki
c5082ac85a fix: make controller GUIDs match old SDL2 GUIDs (ryubing/ryujinx!218)
See merge request ryubing/ryujinx!218
2025-11-14 11:52:42 -06:00
GreemDev
5e86ad83cc feature: macOS Liquid Glass dynamic icon
No more squircle jail! Thanks @transistor.exe in the Ryubing discord for dropping the asset files for this to happen.
2025-11-13 20:47:05 -06:00
Coxxs
1baaa1c365 Preserve and rename the configuration file when it is deemed invalid (ryubing/ryujinx!216)
See merge request ryubing/ryujinx!216
2025-11-12 08:23:05 -06:00
LotP
e8225ce7aa Memory changes 3.1 (ryubing/ryujinx!212)
See merge request ryubing/ryujinx!212
2025-11-11 13:24:57 -06:00
GreemDev
6b814fb973 feature: .NET 10 (ryubing/ryujinx!214)
See merge request ryubing/ryujinx!214
2025-11-11 12:55:36 -06:00
GreemDev
49c70efdd5 UI: App Library: automatically remove nonexistent autoload/game dirs from the configuration upon load. 2025-11-10 19:14:29 -06:00
Babib3l
4677b749b1 fr_FR and es_ES small translation update (ryubing/ryujinx!172)
See merge request ryubing/ryujinx!172
2025-11-10 17:35:17 -06:00
GreemDev
ed32cd6999 fix nested project error when building 2025-11-10 13:32:40 -06:00
GreemDev
d7e2d4534a [ci skip] fix new SDL projects showing up in an src folder in the IDE (idk how I didn't see that before) 2025-11-10 00:12:39 -06:00
LotP
3c3e14c819 Update .gitattributes (ryubing/ryujinx!213)
See merge request ryubing/ryujinx!213
2025-11-09 19:03:01 -06:00
Godzilaa4
dd9ba05e36 Updated PT_BR translation (ryubing/ryujinx!208)
See merge request ryubing/ryujinx!208
2025-11-09 14:36:44 -06:00
Maki
a49822470c fix: crash when connecting a joycon (ryubing/ryujinx!211)
See merge request ryubing/ryujinx!211
2025-11-09 13:35:16 -06:00
GreemDev
58be57bf73 chore: [ci skip] rename InputElement_OnGotFocus/OnLostFocus to fit what it's listening to better 2025-11-08 03:21:54 -06:00
shinyoyo
17d5d6e65a Updated Simplified Chinese translation. (ryubing/ryujinx!209)
See merge request ryubing/ryujinx!209
2025-11-08 03:16:17 -06:00
GreemDev
1dac06e394 misc: [ci skip] remove duplicate log when setting audio backend 2025-11-08 00:18:23 -06:00
GreemDev
ed89ffd3f8 fix: add back compat functionality to the AudioBackend enum as well, and add missing migration comment from config version 70 2025-11-07 22:23:13 -06:00
GreemDev
844d7a9cfe fix: broken arrow in log 2025-11-07 22:14:35 -06:00
GreemDev
cf6acba416 chore: remove debug logging from SDL update 2025-11-07 20:44:25 -06:00
GreemDev
5a9d5ee664 chore: [ci skip] Add more misc logging similar to the "loading content archive" line for loading a base game dump, to updates and DLC. 2025-11-07 15:14:43 -06:00
348 changed files with 10637 additions and 8461 deletions

1
.gitattributes vendored
View File

@@ -2,3 +2,4 @@
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto eol=lf
*.json text eol=lf

View File

@@ -1,4 +1,4 @@
name: Canary release job
name: Canary CI
on:
workflow_dispatch:
@@ -19,7 +19,6 @@ concurrency: release
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.3"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
RELEASE: 1
@@ -30,8 +29,8 @@ jobs:
strategy:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:
@@ -44,11 +43,25 @@ jobs:
- name: Overwrite csc problem matcher
run: echo "::add-matcher::.github/csc.json"
- name: Install 7zip
run: |
sudo apt install -y 7zip
- name: Install gli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $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 "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
@@ -69,33 +82,20 @@ jobs:
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
- name: Packing Windows builds
if: matrix.platform.os == 'windows-latest'
if: contains(matrix.platform.name, 'win')
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd
gh release download -R GreemDev/GLI -O gli.exe -p 'GitLabCli-win_x64.exe'
./gli.exe --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install GitLabCli
if: matrix.platform.os == 'ubuntu-latest'
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Packing Linux builds
if: matrix.platform.os == 'ubuntu-latest'
if: contains(matrix.platform.name, 'linux')
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
@@ -103,11 +103,11 @@ jobs:
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
shell: bash
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
if: contains(matrix.platform.name, 'linux')
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
@@ -139,8 +139,8 @@ jobs:
pushd publish_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
popd
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
shell: bash
macos_release:
@@ -159,10 +159,10 @@ jobs:
chmod +x llvm.sh
sudo ./llvm.sh 17
- name: Install GitLabCli
- name: Install gli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
@@ -183,9 +183,10 @@ jobs:
- 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 "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Configure for release
run: |
@@ -200,7 +201,7 @@ jobs:
- 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 1
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
create_gitlab_release:
name: Create GitLab Release
@@ -210,37 +211,41 @@ jobs:
- release
steps:
- uses: actions/checkout@v4
- 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: Install GitLabCli
- name: Install gli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info
id: version_info
run: |
echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Create tag
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateTag "Canary-${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}"
gli create-tag -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Canary-${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }}
- name: Create release
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=CreateReleaseFromGenericPackageFiles "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|main|Canary ${{ steps.version_info.outputs.build_version }}|**Full Changelog:** [${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
gli create-release-from-generic-package-files -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r main -t "Canary ${{ steps.version_info.outputs.build_version }}" -b "**Full Changelog:** [${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
- name: Send notification webhook
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|FF4500|${{ secrets.CANARY_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
gli send-update-message -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -t ${{ steps.version_info.outputs.build_version }} -c FF4500 -w ${{ secrets.CANARY_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4
- name: Notify update server of new builds
run: |
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=canary' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'
gli refresh-version-cache -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Canary
- name: Advance to the next version
run: |
gli increment-version -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Canary

View File

@@ -1,224 +0,0 @@
name: Release job (Debug)
on:
workflow_dispatch:
inputs: {}
concurrency: release
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.3"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
RELEASE: 1
jobs:
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: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { 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 }} + 10))" >> $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_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 -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
- name: Packing Windows builds
if: matrix.platform.os == 'windows-latest'
run: |
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
gh release download -R GreemDev/GLI -O gli.exe -p 'GitLabCli-win_x64.exe'
./gli.exe --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip"
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install GitLabCli
if: matrix.platform.os == 'ubuntu-latest'
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Packing Linux builds
if: matrix.platform.os == 'ubuntu-latest'
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
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
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
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
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
shell: bash
macos_release:
name: Release MacOS universal
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- name: Setup LLVM 17
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 17
- name: Install GitLabCli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- 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 }} + 10))" >> $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_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 ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
create_gitlab_release:
name: Create GitLab Release
runs-on: ubuntu-24.04
needs:
- macos_release
- release
steps:
- uses: actions/checkout@v4
- name: Get version info
id: version_info
run: |
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} + 10))" >> $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: Install GitLabCli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create release
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateReleaseFromGenericPackageFiles "Ryubing|${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}|test|THIS IS NOT INTENDED FOR END USER USAGE"

View File

@@ -1,15 +1,18 @@
name: Release job
name: Stable CI
on:
workflow_dispatch:
inputs: {}
inputs:
is_bugfix_release:
description: "Bug fix release: If checked, this will increment the third number for only Stable, and leave the Major version alone for both Stable and Canary."
required: true
type: boolean
concurrency: release
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.3"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
RELEASE: 1
@@ -20,8 +23,8 @@ jobs:
strategy:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:
@@ -33,12 +36,30 @@ jobs:
- name: Overwrite csc problem matcher
run: echo "::add-matcher::.github/csc.json"
- name: Install 7zip
run: |
sudo apt install -y 7zip
- name: Install gli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $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
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
else
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
fi
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
@@ -58,47 +79,34 @@ jobs:
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
- name: Packing Windows builds
if: matrix.platform.os == 'windows-latest'
if: contains(matrix.platform.name, 'win')
run: |
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
gh release download -R GreemDev/GLI -O gli.exe -p 'GitLabCli-win_x64.exe'
./gli.exe --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install GitLabCli
if: matrix.platform.os == 'ubuntu-latest'
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Packing Linux builds
if: matrix.platform.os == 'ubuntu-latest'
if: contains(matrix.platform.name, 'linux')
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
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
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
if: contains(matrix.platform.name, 'linux')
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
@@ -131,7 +139,7 @@ jobs:
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
popd
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
shell: bash
macos_release:
@@ -150,10 +158,10 @@ jobs:
chmod +x llvm.sh
sudo ./llvm.sh 17
- name: Install GitLabCli
- name: Install gli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
@@ -174,9 +182,14 @@ jobs:
- 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
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
else
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
fi
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Configure for release
run: |
@@ -189,7 +202,8 @@ jobs:
- name: Publish macOS Ryujinx
run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
create_gitlab_release:
name: Create GitLab Release
@@ -200,32 +214,45 @@ jobs:
steps:
- uses: actions/checkout@v4
- 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: Install GitLabCli
- name: Install gli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info
id: version_info
run: |
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
else
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
fi
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
echo "commit_message=$(git log -1 --pretty=%B)" >> $GITHUB_OUTPUT
shell: bash
- name: Create release
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateReleaseFromGenericPackageFiles "Ryubing|${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}|${{ steps.version_info.outputs.build_version }}|msd:${{ steps.version_info.outputs.build_version }}"
gli create-release-from-generic-package-files -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }} -t "${{ steps.version_info.outputs.build_version }}" -b "msd:${{ steps.version_info.outputs.build_version }}"
- name: Send notification webhook
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|32cd32|${{ secrets.STABLE_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
gli send-update-message -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -t ${{ steps.version_info.outputs.build_version }} -c 32cd32 -w ${{ secrets.STABLE_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4
- name: Notify update server of new builds
run: |
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=stable' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'
gli refresh-version-cache -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Stable
- name: Advance to the next version
run: |
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
gli advance-version -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}
else
gli increment-version -T ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }} -c Stable
fi

3
.gitignore vendored
View File

@@ -18,6 +18,8 @@ build/
[Oo]bj/
AppDir/
publish_appimage/
publish_ava/
publish_tmp_ava/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
@@ -98,6 +100,7 @@ DocProject/Help/html
# Click-Once directory
publish/
RyubingMaintainerTools/
# Publish Web Output
*.Publish.xml

View File

@@ -5,7 +5,7 @@ If you wish to build the emulator yourself, follow these steps:
### Step 1
Install the [.NET 9.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/9.0).
Install the [.NET 10.0 (or higher) SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0).
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
### Step 2

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
</Project>

View File

@@ -3,13 +3,13 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="11.3.6" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.6" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.6.2" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.6.2" />
<PackageVersion Include="Avalonia" Version="11.3.11" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.11" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.11" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.11" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.11" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.2" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.2" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
@@ -22,7 +22,7 @@
<PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="DynamicData" Version="9.4.1" />
<PackageVersion Include="FluentAvaloniaUI.NoAnim" Version="2.4.0-build3" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.0" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
@@ -59,4 +59,4 @@
<PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>
</Project>

View File

@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.GAL", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.OpenGL", "src\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj", "{9558FB96-075D-4219-8FFF-401979DC0B69}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.RenderDoc", "src\Ryujinx.Graphics.RenderDocApi\Ryujinx.Graphics.RenderDocApi.csproj", "{D58FA894-27D5-4EAA-9042-AD422AD82931}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Texture", "src\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj", "{E1B1AD28-289D-47B7-A106-326972240207}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "src\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}"
@@ -45,12 +47,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "src\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.Apple", "src\Ryujinx.Audio.Backends.Apple\Ryujinx.Audio.Backends.Apple.csproj", "{AC26EFF0-8593-4184-9A09-98E37EFFB32E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL3", "src\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj", "{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "src\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "src\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "src\Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL3", "src\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj", "{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
@@ -73,24 +81,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL3.Common", "src\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj", "{F6F9826A-BC58-4D78-A700-F358A66B2B06}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.github\workflows\build.yml = .github\workflows\build.yml
.github\workflows\canary.yml = .github\workflows\canary.yml
Directory.Packages.props = Directory.Packages.props
Directory.Build.props = Directory.Build.props
.github\workflows\release.yml = .github\workflows\release.yml
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.SDL3.Common", "src\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj", "{F6F9826A-BC58-4D78-A700-F358A66B2B06}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input.SDL3", "src\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj", "{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Audio.Backends.SDL3", "src\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj", "{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -556,15 +559,24 @@ Global
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|x64.Build.0 = Release|Any CPU
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|x86.ActiveCfg = Release|Any CPU
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|x86.Build.0 = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Debug|x64.ActiveCfg = Debug|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Debug|x64.Build.0 = Debug|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Debug|x86.ActiveCfg = Debug|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Debug|x86.Build.0 = Debug|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|Any CPU.Build.0 = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x64.ActiveCfg = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x64.Build.0 = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.ActiveCfg = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.Build.0 = Release|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F6F9826A-BC58-4D78-A700-F358A66B2B06} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A}
EndGlobalSection

24
assets/Languages.json Normal file
View File

@@ -0,0 +1,24 @@
{
"Languages": {
"ar_SA": "اَلْعَرَبِيَّةُ",
"de_DE": "Deutsch",
"el_GR": "Ελληνικά",
"en_US": "English (US)",
"es_ES": "Español (ES)",
"fr_FR": "Français (FR)",
"he_IL": "עִברִית",
"it_IT": "Italiano",
"ja_JP": "日本語",
"ko_KR": "한국어",
"no_NO": "Norsk",
"pl_PL": "Polski",
"pt_BR": "Português (BR)",
"ru_RU": "Русский",
"sv_SE": "Svenska",
"th_TH": "ภาษาไทย",
"tr_TR": "Türkçe",
"uk_UA": "Українська",
"zh_CN": "简体中文",
"zh_TW": "繁體中文 (台灣)"
}
}

60
assets/Locales.md Normal file
View File

@@ -0,0 +1,60 @@
# Ryubing Locales
Ryubing Locales uses a custom format, which uses a file for defining the supported languages and a folder of json files for the locales themselves.
Each json file holds the locales for a specific part of the emulator, e.g. the Setup Wizard locales are in `SetupWizard.json`, and each locale entry in the file includes all the supported languages in the same place.
## Languages
in the `/assets/` folder you will find the `Languages.json` file, which defines all the languages supported by the emulator.
The file includes a table of the langauge codes and their langauge names.
#Example of the format for Languages.json
{
"Languages": {
"ar_SA": "اَلْعَرَبِيَّةُ",
"en_US": "English (US)",
...
"zh_TW": "繁體中文 (台灣)"
}
}
## Locales
in the `/assets/Locales/` folder you will find the json files, which define all the locales supported by the emulator.
Each json file holds locales for a specific part of the emulator in a large array of locale objects.
Each locale is made up an ID used for lookup and a list of the languages and their matching translations.
Any empty string or null value will automatically use the English translation instead in the emulator.
### Format
When adding a new locale, you just need to add the ID and the en_US language translation, then the validation system will add default values for the rest of languages automatically, when rebuilding the project.
If you want to signal that a translation is supposed to match the English translation, you just have to replace the empty string with `null`.
When you want to check what translations are missing for a language just search for `"<lang_code>": ""`, e.g: `"en_US": ""` (but with any other language, as English will never be missing translations).
### Legacy file (Root.json)
Currently all older locales are stored in `Root.json`, but they are slowly being moved into newer, more descriptive json files, to make the locale system more accessible.
Do **not** add new locales to `Root.json`.
If no json file exists for the specific part of the emulator you're working on, you should instead add a new json file for that part.
#Example of the format for Root.json
{
"Locales": [
{
"ID": "MenuBarActionsOpenMiiEditor",
"Translations": {
"ar_SA": "",
"en_US": "Mii Editor",
...
"zh_TW": "Mii 編輯器"
}
},
{
"ID": "KeyNumber9",
"Translations": {
"ar_SA": "٩",
"en_US": "9",
...
"zh_TW": null
}
}
]
}

View File

@@ -0,0 +1,104 @@
{
"Locales": [
{
"ID": "MenuBarActions_StartCapture",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Start RenderDoc Frame Capture",
"es_ES": "Iniciar una captura de fotograma de RenderDoc",
"fr_FR": "Démarrer une capture de trame RenderDoc",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "RenderDoc 프레임 캡처 시작",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "启动 RenderDoc 帧捕获",
"zh_TW": ""
}
},
{
"ID": "MenuBarActions_EndCapture",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "End RenderDoc Frame Capture",
"es_ES": "Detener la captura de fotograma de RenderDoc",
"fr_FR": "Arrêter la capture de trame RenderDoc",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "RenderDoc 프레임 캡처 종료",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "结束 RenderDoc 帧捕获",
"zh_TW": ""
}
},
{
"ID": "MenuBarActions_DiscardCapture",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Discard RenderDoc Frame Capture",
"es_ES": "Descartar la captura de fotograma de RenderDoc",
"fr_FR": "Supprimer la capture de trame RenderDoc",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "RenderDoc 프레임 캡처 폐기",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "丢弃 RenderDoc 帧捕获",
"zh_TW": ""
}
},
{
"ID": "MenuBarActions_DiscardCapture_ToolTip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Ends the currently active RenderDoc Frame Capture, immediately discarding its result.",
"es_ES": "Finaliza la captura de fotograma de RenderDoc actualmente activa y descarta inmediatamente su resultado.",
"fr_FR": "Met fin à la capture de trame RenderDoc en cours, en supprimant immédiatement son résultat.",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "현재 활성화된 RenderDoc 프레임 캡처를 종료하고 결과를 즉시 폐기합니다.",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "结束当前正在进行的 RenderDoc 帧捕获,并立即丢弃其结果。",
"zh_TW": ""
}
}
]
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -10,6 +10,8 @@
<string>Ryujinx</string>
<key>CFBundleIconFile</key>
<string>Ryujinx.icns</string>
<key>CFBundleIconName</key>
<string>Ryujinx</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
@@ -40,7 +42,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2</string>
<string>1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -25,6 +25,7 @@ cp "$PUBLISH_DIRECTORY"/*.dylib "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents"
cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns"
cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh"
cp Assets.car "$APP_BUNDLE_DIRECTORY/Contents/Resources/Assets.car"
cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources"
echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"

View File

@@ -1,124 +0,0 @@
#!/bin/bash
set -e
if [ "$#" -lt 8 ]; then
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <CANARY>"
exit 1
fi
mkdir -p "$1"
mkdir -p "$2"
mkdir -p "$3"
BASE_DIR=$(readlink -f "$1")
TEMP_DIRECTORY=$(readlink -f "$2")
OUTPUT_DIRECTORY=$(readlink -f "$3")
ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
VERSION=$5
SOURCE_REVISION_ID=$6
CONFIGURATION=$7
CANARY=$8
if [[ "$(uname)" == "Darwin" ]]; then
echo "Clearing xattr on all dot undercsore files"
find "$BASE_DIR" -type f -name "._*" -exec sh -c '
for f; do
dir=$(dirname "$f")
base=$(basename "$f")
orig="$dir/${base#._}"
[ -f "$orig" ] && xattr -c "$orig" || true
done
' sh {} +
fi
if [ "$CANARY" == "1" ]; then
RELEASE_TAR_FILE_NAME=nogui-ryujinx-canary-$VERSION-macos_universal.tar
elif [ "$VERSION" == "1.1.0" ]; then
RELEASE_TAR_FILE_NAME=nogui-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.tar
else
RELEASE_TAR_FILE_NAME=nogui-ryujinx-$VERSION-macos_universal.tar
fi
ARM64_OUTPUT="$TEMP_DIRECTORY/publish_arm64"
X64_OUTPUT="$TEMP_DIRECTORY/publish_x64"
UNIVERSAL_OUTPUT="$OUTPUT_DIRECTORY/publish"
EXECUTABLE_SUB_PATH=Ryujinx.Headless.SDL3
rm -rf "$TEMP_DIRECTORY"
mkdir -p "$TEMP_DIRECTORY"
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
dotnet restore
dotnet build -c "$CONFIGURATION" src/Ryujinx.Headless.SDL3
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL3
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL3
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
# Get rid of libsoundio from arm64 builds as we don't have a arm64 variant
# TODO: remove this once done
rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib"
rm -rf "$OUTPUT_DIRECTORY"
mkdir -p "$OUTPUT_DIRECTORY"
# Let's copy one of the two different outputs and remove the executable
cp -R "$ARM64_OUTPUT/" "$UNIVERSAL_OUTPUT"
rm "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH"
# Make its libraries universal
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_OUTPUT" "$X64_OUTPUT" "$UNIVERSAL_OUTPUT" "**/*.dylib"
if ! [ -x "$(command -v lipo)" ];
then
if ! [ -x "$(command -v llvm-lipo-17)" ];
then
LIPO=llvm-lipo
else
LIPO=llvm-lipo-17
fi
else
LIPO=lipo
fi
# Make the executable universal
$LIPO "$ARM64_OUTPUT/$EXECUTABLE_SUB_PATH" "$X64_OUTPUT/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH" -create
# Now sign it
if ! [ -x "$(command -v codesign)" ];
then
if ! [ -x "$(command -v rcodesign)" ];
then
echo "Cannot find rcodesign on your system, please install rcodesign."
exit 1
fi
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
echo "Using rcodesign for ad-hoc signing"
for FILE in "$UNIVERSAL_OUTPUT"/*; do
if [[ $(file "$FILE") == *"Mach-O"* ]]; then
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$FILE"
fi
done
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 -s - "$FILE"
fi
done
fi
echo "Creating archive"
pushd "$OUTPUT_DIRECTORY"
tar --exclude "publish/Ryujinx.Headless.SDL3" -cvf "$RELEASE_TAR_FILE_NAME" publish 1> /dev/null
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "publish/Ryujinx.Headless.SDL3" "publish/Ryujinx.Headless.SDL3"
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
rm "$RELEASE_TAR_FILE_NAME"
popd
echo "Done"

View File

@@ -2050,7 +2050,9 @@
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
0100C9A00ECE6000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
010057D00ECE4000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
0100e0601c632000,"Nintendo 64™ Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
010037A0170D2000,"NINTENDO 64™ Nintendo Switch Online 18+",,ingame,2025-02-03 22:27:00
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
@@ -2275,12 +2277,12 @@
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
0100F43008C44000,"Pokémon™ Legends: Z-A",gpu;crash;ldn-works,ingame,2025-11-16 00:30:00
01005D100807A000,"Pokémon™ Quest",,playable,2022-02-22 16:12:32
0100A3D008C5C000,"Pokémon™ Scarlet",gpu;nvdec;ldn-works;amd-vendor-bug,ingame,2023-12-14 13:18:29
01008F6008C5E000,"Pokémon™ Violet",gpu;nvdec;ldn-works;amd-vendor-bug;mac-bug,ingame,2024-07-30 02:51:48
0100187003A36000,"Pokémon™: Lets Go, Eevee!",crash;nvdec;online-broken;ldn-broken,ingame,2024-06-01 15:03:04
010003F003A34000,"Pokémon™: Lets Go, Pikachu!",crash;nvdec;online-broken;ldn-broken,ingame,2024-03-15 07:55:41
0100F43008C44000,"Pokémon Legends: Z-A",gpu;crash;ldn-broken,ingame,2025-10-16 19:13:00
0100B3F000BE2000,"Pokkén Tournament™ DX",nvdec;ldn-works;opengl-backend-bug;LAN;amd-vendor-bug;intel-vendor-bug,playable,2024-07-18 23:11:08
010030D005AE6000,"Pokkén Tournament™ DX Demo",demo;opengl-backend-bug,playable,2022-08-10 12:03:19
0100A3500B4EC000,"Polandball: Can Into Space",,playable,2020-06-25 15:13:26
@@ -2638,6 +2640,7 @@
0100B16009C10000,"SINNER: Sacrifice for Redemption",nvdec;UE4;vulkan-backend-bug,playable,2022-08-12 20:37:33
0100E9201410E000,"Sir Lovelot",,playable,2021-04-05 16:21:46
0100134011E32000,"Skate City",,playable,2022-11-04 11:37:39
0100a8501b66e000,"Skateboard Drifting with Maxwell Cat: The Game Simulator",,playable,2026-02-17 19:05:00
0100B2F008BD8000,"Skee-Ball",,playable,2020-11-16 04:44:07
01001A900F862000,"Skelattack",,playable,2021-06-09 15:26:26
01008E700F952000,"Skelittle: A Giant Party!",,playable,2021-06-09 19:08:34
@@ -3307,6 +3310,7 @@
0100AFA011068000,"Voxel Pirates",,playable,2022-09-28 22:55:02
0100BFB00D1F4000,"Voxel Sword",,playable,2022-08-30 14:57:27
01004E90028A2000,"Vroom in the night sky",Needs Update;vulkan-backend-bug,playable,2023-02-20 02:32:29
0100BFC01D976000,"Virtual Boy Nintendo Classics",services,nothing,2026-02-17 11:26:59
0100C7C00AE6C000,"VSR: Void Space Racing",,playable,2021-01-27 14:08:59
0100B130119D0000,"Waifu Uncovered",crash,ingame,2023-02-27 01:17:46
0100E29010A4A000,"Wanba Warriors",,playable,2020-10-04 17:56:22
1 title_id game_name labels status last_updated
2050 010003C00B868000 Ninjin: Clash of Carrots online-broken playable 2024-07-10 05:12:26
2051 0100746010E4C000 NinNinDays playable 2022-11-20 15:17:29
2052 0100C9A00ECE6000 Nintendo 64™ – Nintendo Switch Online gpu;vulkan ingame 2024-04-23 20:21:07
2053 010057D00ECE4000 Nintendo 64™ – Nintendo Switch Online gpu;vulkan ingame 2024-04-23 20:21:07
2054 0100e0601c632000 Nintendo 64™ – Nintendo Switch Online: MATURE 17+ ingame 2025-02-03 22:27:00
2055 010037A0170D2000 NINTENDO 64™ – Nintendo Switch Online 18+ ingame 2025-02-03 22:27:00
2056 0100D870045B6000 Nintendo Entertainment System™ - Nintendo Switch Online online playable 2022-07-01 15:45:06
2057 0100C4B0034B2000 Nintendo Labo Toy-Con 01 Variety Kit gpu ingame 2022-08-07 12:56:07
2058 01001E9003502000 Nintendo Labo Toy-Con 03 Vehicle Kit services;crash menus 2022-08-03 17:20:11
2277 010018E011D92000 Pokémon™ Shining Pearl gpu;ldn-works ingame 2024-08-28 13:26:35
2278 010015F008C54000 Pokémon™ HOME Needs Update;crash;services menus 2020-12-06 06:01:51
2279 01001F5010DFA000 Pokémon™ Legends: Arceus gpu;Needs Update;ldn-works ingame 2024-09-19 10:02:02
2280 0100F43008C44000 Pokémon™ Legends: Z-A gpu;crash;ldn-works ingame 2025-11-16 00:30:00
2281 01005D100807A000 Pokémon™ Quest playable 2022-02-22 16:12:32
2282 0100A3D008C5C000 Pokémon™ Scarlet gpu;nvdec;ldn-works;amd-vendor-bug ingame 2023-12-14 13:18:29
2283 01008F6008C5E000 Pokémon™ Violet gpu;nvdec;ldn-works;amd-vendor-bug;mac-bug ingame 2024-07-30 02:51:48
2284 0100187003A36000 Pokémon™: Let’s Go, Eevee! crash;nvdec;online-broken;ldn-broken ingame 2024-06-01 15:03:04
2285 010003F003A34000 Pokémon™: Let’s Go, Pikachu! crash;nvdec;online-broken;ldn-broken ingame 2024-03-15 07:55:41
0100F43008C44000 Pokémon Legends: Z-A gpu;crash;ldn-broken ingame 2025-10-16 19:13:00
2286 0100B3F000BE2000 Pokkén Tournament™ DX nvdec;ldn-works;opengl-backend-bug;LAN;amd-vendor-bug;intel-vendor-bug playable 2024-07-18 23:11:08
2287 010030D005AE6000 Pokkén Tournament™ DX Demo demo;opengl-backend-bug playable 2022-08-10 12:03:19
2288 0100A3500B4EC000 Polandball: Can Into Space playable 2020-06-25 15:13:26
2640 0100B16009C10000 SINNER: Sacrifice for Redemption nvdec;UE4;vulkan-backend-bug playable 2022-08-12 20:37:33
2641 0100E9201410E000 Sir Lovelot playable 2021-04-05 16:21:46
2642 0100134011E32000 Skate City playable 2022-11-04 11:37:39
2643 0100a8501b66e000 Skateboard Drifting with Maxwell Cat: The Game Simulator playable 2026-02-17 19:05:00
2644 0100B2F008BD8000 Skee-Ball playable 2020-11-16 04:44:07
2645 01001A900F862000 Skelattack playable 2021-06-09 15:26:26
2646 01008E700F952000 Skelittle: A Giant Party! playable 2021-06-09 19:08:34
3310 0100AFA011068000 Voxel Pirates playable 2022-09-28 22:55:02
3311 0100BFB00D1F4000 Voxel Sword playable 2022-08-30 14:57:27
3312 01004E90028A2000 Vroom in the night sky Needs Update;vulkan-backend-bug playable 2023-02-20 02:32:29
3313 0100BFC01D976000 Virtual Boy – Nintendo Classics services nothing 2026-02-17 11:26:59
3314 0100C7C00AE6C000 VSR: Void Space Racing playable 2021-01-27 14:08:59
3315 0100B130119D0000 Waifu Uncovered crash ingame 2023-02-27 01:17:46
3316 0100E29010A4A000 Wanba Warriors playable 2020-10-04 17:56:22

View File

@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.100",
"version": "10.0.100",
"rollForward": "latestFeature"
}
}

View File

@@ -25,9 +25,9 @@ namespace ARMeilleure.CodeGen.Arm64
static class ComparisonArm64Extensions
{
public static ArmCondition ToArmCondition(this Comparison comp)
extension(Comparison comparison)
{
return comp switch
public ArmCondition Arm => comparison switch
{
#pragma warning disable IDE0055 // Disable formatting
Comparison.Equal => ArmCondition.Eq,
@@ -42,7 +42,7 @@ namespace ARMeilleure.CodeGen.Arm64
Comparison.LessUI => ArmCondition.LtUn,
#pragma warning restore IDE0055
_ => throw new ArgumentException(null, nameof(comp)),
_ => throw new ArgumentException(null, nameof(comparison))
};
}
}

View File

@@ -181,10 +181,10 @@ namespace ARMeilleure.CodeGen.Arm64
public void Fmov(Operand rd, Operand rn, bool topHalf)
{
Debug.Assert(rd.Type.IsInteger() != rn.Type.IsInteger());
Debug.Assert(rd.Type.IsInteger != rn.Type.IsInteger);
Debug.Assert(rd.Type == OperandType.I64 || rn.Type == OperandType.I64 || !topHalf);
uint opcode = rd.Type.IsInteger() ? 0b110u : 0b111u;
uint opcode = rd.Type.IsInteger ? 0b110u : 0b111u;
uint rmode = topHalf ? 1u << 19 : 0u;
uint ftype = rd.Type == OperandType.FP64 || rn.Type == OperandType.FP64 ? 1u << 22 : 0u;
@@ -411,7 +411,7 @@ namespace ARMeilleure.CodeGen.Arm64
public void Mov(Operand rd, Operand rn)
{
if (rd.Type.IsInteger())
if (rd.Type.IsInteger)
{
Orr(rd, Factory.Register(ZrRegister, RegisterType.Integer, rd.Type), rn);
}
@@ -973,7 +973,7 @@ namespace ARMeilleure.CodeGen.Arm64
uint instruction;
int scale;
if (type.IsInteger())
if (type.IsInteger)
{
instruction = intInst;
@@ -1009,7 +1009,7 @@ namespace ARMeilleure.CodeGen.Arm64
{
uint instruction;
if (type.IsInteger())
if (type.IsInteger)
{
instruction = intInst;

View File

@@ -250,7 +250,7 @@ namespace ARMeilleure.CodeGen.Arm64
// ValidateBinOp(dest, src1, src2);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Add(dest, src1, src2);
}
@@ -268,7 +268,7 @@ namespace ARMeilleure.CodeGen.Arm64
ValidateBinOp(dest, src1, src2);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.And(dest, src1, src2);
}
@@ -281,7 +281,7 @@ namespace ARMeilleure.CodeGen.Arm64
ValidateBinOp(dest, src1, src2);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Eor(dest, src1, src2);
}
@@ -298,7 +298,7 @@ namespace ARMeilleure.CodeGen.Arm64
ValidateUnOp(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Mvn(dest, source);
}
@@ -311,7 +311,7 @@ namespace ARMeilleure.CodeGen.Arm64
ValidateBinOp(dest, src1, src2);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Orr(dest, src1, src2);
}
@@ -322,7 +322,7 @@ namespace ARMeilleure.CodeGen.Arm64
Debug.Assert(comp.Kind == OperandKind.Constant);
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
ArmCondition cond = ((Comparison)comp.AsInt32()).Arm;
GenerateCompareCommon(context, operation);
@@ -336,7 +336,7 @@ namespace ARMeilleure.CodeGen.Arm64
ValidateUnOp(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Rev(dest, source);
}
@@ -354,7 +354,7 @@ namespace ARMeilleure.CodeGen.Arm64
Debug.Assert(dest.Type == OperandType.I32);
Debug.Assert(comp.Kind == OperandKind.Constant);
ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition();
ArmCondition cond = ((Comparison)comp.AsInt32()).Arm;
GenerateCompareCommon(context, operation);
@@ -428,7 +428,7 @@ namespace ARMeilleure.CodeGen.Arm64
EnsureSameType(src1, src2);
Debug.Assert(src1.Type.IsInteger());
Debug.Assert(src1.Type.IsInteger);
context.Assembler.Cmp(src1, src2);
}
@@ -442,7 +442,7 @@ namespace ARMeilleure.CodeGen.Arm64
EnsureSameType(dest, src2, src3);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
Debug.Assert(src1.Type == OperandType.I32);
context.Assembler.Cmp(src1, Const(src1.Type, 0));
@@ -468,7 +468,7 @@ namespace ARMeilleure.CodeGen.Arm64
Debug.Assert(dest.Type != source.Type);
Debug.Assert(source.Type != OperandType.V128);
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
context.Assembler.ScvtfScalar(dest, source);
}
@@ -485,7 +485,7 @@ namespace ARMeilleure.CodeGen.Arm64
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
Debug.Assert(dest.Type != source.Type);
Debug.Assert(source.Type.IsInteger());
Debug.Assert(source.Type.IsInteger);
context.Assembler.UcvtfScalar(dest, source);
}
@@ -497,7 +497,7 @@ namespace ARMeilleure.CodeGen.Arm64
EnsureSameType(dest, source);
Debug.Assert(dest.Type.IsInteger() || source.Kind != OperandKind.Constant);
Debug.Assert(dest.Type.IsInteger || source.Kind != OperandKind.Constant);
// Moves to the same register are useless.
if (dest.Kind == source.Kind && dest.Value == source.Value)
@@ -529,7 +529,7 @@ namespace ARMeilleure.CodeGen.Arm64
EnsureSameType(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Clz(dest, source);
}
@@ -542,7 +542,7 @@ namespace ARMeilleure.CodeGen.Arm64
ValidateBinOp(dest, dividend, divisor);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Sdiv(dest, dividend, divisor);
}
@@ -576,7 +576,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand value = operation.Destination;
Operand address = operation.GetSource(0);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.LdrhRiUn(value, address, 0);
}
@@ -586,7 +586,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand value = operation.Destination;
Operand address = operation.GetSource(0);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.LdrbRiUn(value, address, 0);
}
@@ -604,7 +604,7 @@ namespace ARMeilleure.CodeGen.Arm64
EnsureSameType(dest, src1, src2);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Mul(dest, src1, src2);
}
@@ -647,7 +647,7 @@ namespace ARMeilleure.CodeGen.Arm64
ValidateUnOp(dest, source);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Neg(dest, source);
}
@@ -732,7 +732,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Sxth(dest, source);
}
@@ -742,7 +742,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Sxtw(dest, source);
}
@@ -752,7 +752,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Sxtb(dest, source);
}
@@ -823,7 +823,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand value = operation.GetSource(1);
Operand address = operation.GetSource(0);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.StrhRiUn(value, address, 0);
}
@@ -833,7 +833,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand value = operation.GetSource(1);
Operand address = operation.GetSource(0);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.StrbRiUn(value, address, 0);
}
@@ -858,7 +858,7 @@ namespace ARMeilleure.CodeGen.Arm64
// ValidateBinOp(dest, src1, src2);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Sub(dest, src1, src2);
}
@@ -882,7 +882,7 @@ namespace ARMeilleure.CodeGen.Arm64
if (dest != default)
{
Debug.Assert(!dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger && source.Type.IsInteger);
OperandType destType = source.Type == OperandType.I64 ? OperandType.FP64 : OperandType.FP32;
@@ -901,9 +901,9 @@ namespace ARMeilleure.CodeGen.Arm64
byte index = src2.AsByte();
Debug.Assert(index < OperandType.V128.GetSizeInBytes() / dest.Type.GetSizeInBytes());
Debug.Assert(index < OperandType.V128.ByteSize / dest.Type.ByteSize);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Umov(dest, src1, index, dest.Type == OperandType.I64 ? 3 : 2);
}
@@ -959,7 +959,7 @@ namespace ARMeilleure.CodeGen.Arm64
byte index = src3.AsByte();
if (src2.Type.IsInteger())
if (src2.Type.IsInteger)
{
context.Assembler.Ins(dest, src2, index, src2.Type == OperandType.I64 ? 3 : 2);
}
@@ -1007,7 +1007,7 @@ namespace ARMeilleure.CodeGen.Arm64
{
Operand dest = operation.Destination;
Debug.Assert(!dest.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger);
context.Assembler.CmeqVector(dest, dest, dest, 2);
}
@@ -1016,7 +1016,7 @@ namespace ARMeilleure.CodeGen.Arm64
{
Operand dest = operation.Destination;
Debug.Assert(!dest.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger);
context.Assembler.EorVector(dest, dest, dest);
}
@@ -1046,7 +1046,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Uxth(dest, source);
}
@@ -1056,7 +1056,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
// We can eliminate the move if source is already 32-bit and the registers are the same.
if (dest.Value == source.Value && source.Type == OperandType.I32)
@@ -1072,7 +1072,7 @@ namespace ARMeilleure.CodeGen.Arm64
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Uxtb(dest, source);
}
@@ -1169,7 +1169,7 @@ namespace ARMeilleure.CodeGen.Arm64
context.Assembler.StrRiPre(Register(reg, type), Register(SpRegister), -calleeSaveRegionSize);
}
offset += type.GetSizeInBytes();
offset += type.ByteSize;
}
while (mask != 0)
@@ -1195,7 +1195,7 @@ namespace ARMeilleure.CodeGen.Arm64
context.Assembler.StpRiPre(Register(reg, type), Register(reg2, type), Register(SpRegister), -calleeSaveRegionSize);
}
offset += type.GetSizeInBytes() * 2;
offset += type.ByteSize * 2;
}
}
@@ -1273,7 +1273,7 @@ namespace ARMeilleure.CodeGen.Arm64
mask &= ~(1 << reg2);
offset -= type.GetSizeInBytes() * 2;
offset -= type.ByteSize * 2;
if (offset != 0)
{
@@ -1286,7 +1286,7 @@ namespace ARMeilleure.CodeGen.Arm64
}
else
{
offset -= type.GetSizeInBytes();
offset -= type.ByteSize;
if (offset != 0)
{
@@ -1435,12 +1435,12 @@ namespace ARMeilleure.CodeGen.Arm64
OperandType valueType = GetMemOpValueType(currentOp);
if (valueType != GetMemOpValueType(nextOp) || op1Offset + valueType.GetSizeInBytes() != op2Offset)
if (valueType != GetMemOpValueType(nextOp) || op1Offset + valueType.ByteSize != op2Offset)
{
return false;
}
if (!CodeGenCommon.ConstFitsOnSImm7(op1Offset, valueType.GetSizeInBytesLog2()))
if (!CodeGenCommon.ConstFitsOnSImm7(op1Offset, valueType.ByteSizeLog2))
{
return false;
}
@@ -1549,7 +1549,7 @@ namespace ARMeilleure.CodeGen.Arm64
// EnsureSameReg (dest, src1);
EnsureSameType(dest, src1);
Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32);
Debug.Assert(dest.Type.IsInteger && src2.Type == OperandType.I32);
}
private static void EnsureSameReg(Operand op1, Operand op2)

View File

@@ -462,7 +462,7 @@ namespace ARMeilleure.CodeGen.Arm64
{
instruction |= (sz << 22);
if (rd.Type.IsInteger())
if (rd.Type.IsInteger)
{
context.Assembler.WriteInstructionAuto(instruction, rd, rn);
}
@@ -490,7 +490,7 @@ namespace ARMeilleure.CodeGen.Arm64
instruction |= (sz << 22);
instruction |= (64 - fBits) << 10;
if (rd.Type.IsInteger())
if (rd.Type.IsInteger)
{
Debug.Assert(rd.Type != OperandType.I32 || fBits <= 32);

View File

@@ -112,7 +112,7 @@ namespace ARMeilleure.CodeGen.Arm64
if (src1.Kind == OperandKind.Constant)
{
if (!src1.Type.IsInteger())
if (!src1.Type.IsInteger)
{
// Handle non-integer types (FP32, FP64 and V128).
// For instructions without an immediate operand, we do the following:
@@ -161,7 +161,7 @@ namespace ARMeilleure.CodeGen.Arm64
if (src2.Kind == OperandKind.Constant)
{
if (!src2.Type.IsInteger())
if (!src2.Type.IsInteger)
{
src2 = AddFloatConstantCopy(constants, nodes, node, src2);
@@ -191,7 +191,7 @@ namespace ARMeilleure.CodeGen.Arm64
if (src.Kind == OperandKind.Constant)
{
if (!src.Type.IsInteger())
if (!src.Type.IsInteger)
{
src = AddFloatConstantCopy(constants, nodes, node, src);
@@ -282,7 +282,7 @@ namespace ARMeilleure.CodeGen.Arm64
bool passOnReg;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
passOnReg = intCount < intMax;
}
@@ -309,7 +309,7 @@ namespace ARMeilleure.CodeGen.Arm64
if (passOnReg)
{
Operand argReg = source.Type.IsInteger()
Operand argReg = source.Type.IsInteger
? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
@@ -327,7 +327,7 @@ namespace ARMeilleure.CodeGen.Arm64
InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, spillOp));
stackOffset += source.Type.GetSizeInBytes();
stackOffset += source.Type.ByteSize;
}
}
@@ -345,7 +345,7 @@ namespace ARMeilleure.CodeGen.Arm64
}
else
{
Operand retReg = dest.Type.IsInteger()
Operand retReg = dest.Type.IsInteger
? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type)
: Xmm(CallingConvention.GetVecReturnRegister(), dest.Type);
@@ -385,7 +385,7 @@ namespace ARMeilleure.CodeGen.Arm64
bool passOnReg;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
passOnReg = intCount + 1 < intMax;
}
@@ -408,7 +408,7 @@ namespace ARMeilleure.CodeGen.Arm64
if (passOnReg)
{
Operand argReg = source.Type.IsInteger()
Operand argReg = source.Type.IsInteger
? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
@@ -521,7 +521,7 @@ namespace ARMeilleure.CodeGen.Arm64
}
else
{
Operand retReg = source.Type.IsInteger()
Operand retReg = source.Type.IsInteger
? Gpr(CallingConvention.GetIntReturnRegister(), source.Type)
: Xmm(CallingConvention.GetVecReturnRegister(), source.Type);
@@ -551,7 +551,7 @@ namespace ARMeilleure.CodeGen.Arm64
{
OperandType argType = cctx.FuncArgTypes[cIndex];
if (argType.IsInteger())
if (argType.IsInteger)
{
intCount++;
}
@@ -567,7 +567,7 @@ namespace ARMeilleure.CodeGen.Arm64
bool passOnReg;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
passOnReg = intCount < CallingConvention.GetArgumentsOnRegsCount();
}
@@ -606,7 +606,7 @@ namespace ARMeilleure.CodeGen.Arm64
{
Operand pArg = Local(dest.Type);
Operand argReg = dest.Type.IsInteger()
Operand argReg = dest.Type.IsInteger
? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type)
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type);

View File

@@ -51,7 +51,7 @@ namespace ARMeilleure.CodeGen.Optimizations
if (trueSucc == block.ListNext)
{
Comparison comp = (Comparison)branchOp.GetSource(2).AsInt32();
Comparison compInv = comp.Invert();
Comparison compInv = comp.Inverse;
branchOp.SetSource(2, Const((int)compInv));

View File

@@ -161,7 +161,7 @@ namespace ARMeilleure.CodeGen.Optimizations
}
else if (otherCompType == Comparison.Equal)
{
propCompType = compType.Invert();
propCompType = compType.Inverse;
}
else
{

View File

@@ -105,7 +105,7 @@ namespace ARMeilleure.CodeGen.Optimizations
Operand x = operation.GetSource(0);
Operand y = operation.GetSource(1);
if (x == y && x.Type.IsInteger())
if (x == y && x.Type.IsInteger)
{
operation.TurnIntoCopy(Const(x.Type, 0));
}
@@ -161,7 +161,7 @@ namespace ARMeilleure.CodeGen.Optimizations
private static bool IsConstEqual(Operand operand, ulong comparand)
{
if (operand.Kind != OperandKind.Constant || !operand.Type.IsInteger())
if (operand.Kind != OperandKind.Constant || !operand.Type.IsInteger)
{
return false;
}

View File

@@ -98,7 +98,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
OperandType type = types[copyDest];
type = type.IsInteger() ? OperandType.I64 : OperandType.V128;
type = type.IsInteger ? OperandType.I64 : OperandType.V128;
EmitXorSwap(sequence, GetRegister(copyDest, type), GetRegister(copySource, type));

View File

@@ -178,7 +178,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
}
else if (dest.Kind == OperandKind.Register)
{
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
intFixedRegisters |= 1 << dest.GetRegister().Index;
}
@@ -236,7 +236,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
Register reg = info.Register.GetRegister();
if (local.Type.IsInteger())
if (local.Type.IsInteger)
{
intLocalFreeRegisters |= 1 << reg.Index;
}
@@ -254,7 +254,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
if (temp == default || info.Sequence != sequence)
{
temp = local.Type.IsInteger()
temp = local.Type.IsInteger
? GetSpillTemp(local, intSpillTempRegisters, ref intLocalUse)
: GetSpillTemp(local, vecSpillTempRegisters, ref vecLocalUse);
@@ -335,7 +335,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
if (info.UsesAllocated == 0)
{
int mask = dest.Type.IsInteger()
int mask = dest.Type.IsInteger
? intLocalFreeRegisters
: vecLocalFreeRegisters;
@@ -343,9 +343,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
int selectedReg = BitOperations.TrailingZeroCount(mask);
info.Register = Register(selectedReg, info.Type.ToRegisterType(), info.Type);
info.Register = Register(selectedReg, info.Type.Register, info.Type);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
intLocalFreeRegisters &= ~(1 << selectedReg);
intUsedRegisters |= 1 << selectedReg;
@@ -359,7 +359,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
else
{
info.Register = default;
info.SpillOffset = Const(stackAlloc.Allocate(dest.Type.GetSizeInBytes()));
info.SpillOffset = Const(stackAlloc.Allocate(dest.Type.ByteSize));
}
}
@@ -377,7 +377,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
if (temp == default || info.Sequence != sequence)
{
temp = dest.Type.IsInteger()
temp = dest.Type.IsInteger
? GetSpillTemp(dest, intSpillTempRegisters, ref intLocalAsg)
: GetSpillTemp(dest, vecSpillTempRegisters, ref vecLocalAsg);
@@ -443,7 +443,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
useMask |= 1 << selectedReg;
return Register(selectedReg, local.Type.ToRegisterType(), local.Type);
return Register(selectedReg, local.Type.Register, local.Type);
}
private static int UsesCount(Operand local)

View File

@@ -208,7 +208,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
private bool TryAllocateRegWithoutSpill(AllocationContext context, LiveInterval current, int cIndex, int registersCount)
{
RegisterType regType = current.Local.Type.ToRegisterType();
RegisterType regType = current.Local.Type.Register;
Span<int> freePositions = stackalloc int[registersCount];
@@ -318,7 +318,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
private void AllocateRegWithSpill(AllocationContext context, LiveInterval current, int cIndex, int registersCount)
{
RegisterType regType = current.Local.Type.ToRegisterType();
RegisterType regType = current.Local.Type.Register;
Span<int> usePositions = stackalloc int[registersCount];
Span<int> blockedPositions = stackalloc int[registersCount];

View File

@@ -10,7 +10,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
public int Allocate(OperandType type)
{
return Allocate(type.GetSizeInBytes());
return Allocate(type.ByteSize);
}
public int Allocate(int sizeInBytes)

View File

@@ -385,7 +385,7 @@ namespace ARMeilleure.CodeGen.X86
{
ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Movd];
if (source.Type.IsInteger() || source.Kind == OperandKind.Memory)
if (source.Type.IsInteger || source.Kind == OperandKind.Memory)
{
WriteOpCode(dest, default, source, OperandType.None, info.Flags, info.OpRRM, rrm: true);
}
@@ -416,11 +416,11 @@ namespace ARMeilleure.CodeGen.X86
InstructionFlags flags = info.Flags | InstructionFlags.RexW;
if (source.Type.IsInteger() || source.Kind == OperandKind.Memory)
if (source.Type.IsInteger || source.Kind == OperandKind.Memory)
{
WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRRM, rrm: true);
}
else if (dest.Type.IsInteger() || dest.Kind == OperandKind.Memory)
else if (dest.Type.IsInteger || dest.Kind == OperandKind.Memory)
{
WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRMR);
}

View File

@@ -289,7 +289,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameType(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Popcnt(dest, source, dest.Type);
@@ -303,7 +303,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameType(dest, source);
Debug.Assert(!dest.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger);
context.Assembler.WriteInstruction(info.Inst, dest, source);
@@ -315,7 +315,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && !source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && !source.Type.IsInteger);
if (operation.Intrinsic == Intrinsic.X86Cvtsi2si)
{
@@ -349,8 +349,8 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameReg(dest, src1);
}
Debug.Assert(!dest.Type.IsInteger());
Debug.Assert(!src2.Type.IsInteger() || src2.Kind == OperandKind.Constant);
Debug.Assert(!dest.Type.IsInteger);
Debug.Assert(!src2.Type.IsInteger || src2.Kind == OperandKind.Constant);
context.Assembler.WriteInstruction(info.Inst, dest, src1, src2);
@@ -370,7 +370,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameReg(dest, src1);
}
Debug.Assert(!dest.Type.IsInteger() && src2.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger && src2.Type.IsInteger);
context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src2.Type);
@@ -385,7 +385,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameReg(dest, src1);
Debug.Assert(dest.Type.IsInteger() && src1.Type.IsInteger() && src2.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && src1.Type.IsInteger && src2.Type.IsInteger);
context.Assembler.WriteInstruction(info.Inst, dest, src2, dest.Type);
@@ -405,7 +405,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameReg(dest, src1);
}
Debug.Assert(!dest.Type.IsInteger() && src2.Kind == OperandKind.Constant);
Debug.Assert(!dest.Type.IsInteger && src2.Kind == OperandKind.Constant);
context.Assembler.WriteInstruction(info.Inst, dest, src1, src2.AsByte());
@@ -421,7 +421,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameType(dest, src1, src2, src3);
Debug.Assert(!dest.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger);
if (info.Inst == X86Instruction.Blendvpd && HardwareCapabilities.SupportsVexEncoding)
{
@@ -461,7 +461,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameReg(dest, src1);
}
Debug.Assert(!dest.Type.IsInteger() && src3.Kind == OperandKind.Constant);
Debug.Assert(!dest.Type.IsInteger && src3.Kind == OperandKind.Constant);
context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src3.AsByte());
@@ -512,7 +512,7 @@ namespace ARMeilleure.CodeGen.X86
Operand src1 = operation.GetSource(0);
Operand src2 = operation.GetSource(1);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
// If Destination and Source 1 Operands are the same, perform a standard add as there are no benefits to using LEA.
if (dest.Kind == src1.Kind && dest.Value == src1.Value)
@@ -567,7 +567,7 @@ namespace ARMeilleure.CodeGen.X86
ValidateBinOp(dest, src1, src2);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
// Note: GenerateCompareCommon makes the assumption that BitwiseAnd will emit only a single `and`
// instruction.
@@ -582,7 +582,7 @@ namespace ARMeilleure.CodeGen.X86
ValidateBinOp(dest, src1, src2);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Xor(dest, src2, dest.Type);
}
@@ -599,7 +599,7 @@ namespace ARMeilleure.CodeGen.X86
ValidateUnOp(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Not(dest);
}
@@ -612,7 +612,7 @@ namespace ARMeilleure.CodeGen.X86
ValidateBinOp(dest, src1, src2);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Or(dest, src2, dest.Type);
}
@@ -623,7 +623,7 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(comp.Kind == OperandKind.Constant);
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
X86Condition cond = ((Comparison)comp.AsInt32()).X86;
GenerateCompareCommon(context, operation);
@@ -637,7 +637,7 @@ namespace ARMeilleure.CodeGen.X86
ValidateUnOp(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Bswap(dest);
}
@@ -661,7 +661,7 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(dest.Type == OperandType.I32);
Debug.Assert(comp.Kind == OperandKind.Constant);
X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition();
X86Condition cond = ((Comparison)comp.AsInt32()).X86;
GenerateCompareCommon(context, operation);
@@ -676,7 +676,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameType(src1, src2);
Debug.Assert(src1.Type.IsInteger());
Debug.Assert(src1.Type.IsInteger);
if (src2.Kind == OperandKind.Constant && src2.Value == 0)
{
@@ -766,7 +766,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameReg(dest, src3);
EnsureSameType(dest, src2, src3);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
Debug.Assert(src1.Type == OperandType.I32);
context.Assembler.Test(src1, src1, src1.Type);
@@ -792,9 +792,9 @@ namespace ARMeilleure.CodeGen.X86
if (dest.Type == OperandType.FP32)
{
Debug.Assert(source.Type.IsInteger() || source.Type == OperandType.FP64);
Debug.Assert(source.Type.IsInteger || source.Type == OperandType.FP64);
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
context.Assembler.Xorps(dest, dest, dest);
context.Assembler.Cvtsi2ss(dest, dest, source, source.Type);
@@ -808,9 +808,9 @@ namespace ARMeilleure.CodeGen.X86
}
else /* if (dest.Type == OperandType.FP64) */
{
Debug.Assert(source.Type.IsInteger() || source.Type == OperandType.FP32);
Debug.Assert(source.Type.IsInteger || source.Type == OperandType.FP32);
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
context.Assembler.Xorps(dest, dest, dest);
context.Assembler.Cvtsi2sd(dest, dest, source, source.Type);
@@ -831,7 +831,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameType(dest, source);
Debug.Assert(dest.Type.IsInteger() || source.Kind != OperandKind.Constant);
Debug.Assert(dest.Type.IsInteger || source.Kind != OperandKind.Constant);
// Moves to the same register are useless.
if (dest.Kind == source.Kind && dest.Value == source.Value)
@@ -845,7 +845,7 @@ namespace ARMeilleure.CodeGen.X86
// Assemble "mov reg, 0" as "xor reg, reg" as the later is more efficient.
context.Assembler.Xor(dest, dest, OperandType.I32);
}
else if (dest.Type.IsInteger())
else if (dest.Type.IsInteger)
{
context.Assembler.Mov(dest, source, dest.Type);
}
@@ -862,7 +862,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameType(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Bsr(dest, source, dest.Type);
@@ -894,12 +894,12 @@ namespace ARMeilleure.CodeGen.X86
Operand dividend = operation.GetSource(0);
Operand divisor = operation.GetSource(1);
if (!dest.Type.IsInteger())
if (!dest.Type.IsInteger)
{
ValidateBinOp(dest, dividend, divisor);
}
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
divisor = operation.GetSource(2);
@@ -932,7 +932,7 @@ namespace ARMeilleure.CodeGen.X86
Operand rdx = Register(X86Register.Rdx);
Debug.Assert(divisor.Type.IsInteger());
Debug.Assert(divisor.Type.IsInteger);
context.Assembler.Xor(rdx, rdx, OperandType.I32);
context.Assembler.Div(divisor);
@@ -967,7 +967,7 @@ namespace ARMeilleure.CodeGen.X86
Operand value = operation.Destination;
Operand address = Memory(operation.GetSource(0), value.Type);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.Movzx16(value, address, value.Type);
}
@@ -977,7 +977,7 @@ namespace ARMeilleure.CodeGen.X86
Operand value = operation.Destination;
Operand address = Memory(operation.GetSource(0), value.Type);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.Movzx8(value, address, value.Type);
}
@@ -1000,7 +1000,7 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameType(dest, src1, src2);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
if (src2.Kind == OperandKind.Constant)
{
@@ -1046,7 +1046,7 @@ namespace ARMeilleure.CodeGen.X86
ValidateUnOp(dest, source);
Debug.Assert(dest.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger);
context.Assembler.Neg(dest);
}
@@ -1107,7 +1107,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Movsx16(dest, source, dest.Type);
}
@@ -1117,7 +1117,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Movsx32(dest, source, dest.Type);
}
@@ -1127,7 +1127,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Movsx8(dest, source, dest.Type);
}
@@ -1187,7 +1187,7 @@ namespace ARMeilleure.CodeGen.X86
Operand value = operation.GetSource(1);
Operand address = Memory(operation.GetSource(0), value.Type);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.Mov16(address, value);
}
@@ -1197,7 +1197,7 @@ namespace ARMeilleure.CodeGen.X86
Operand value = operation.GetSource(1);
Operand address = Memory(operation.GetSource(0), value.Type);
Debug.Assert(value.Type.IsInteger());
Debug.Assert(value.Type.IsInteger);
context.Assembler.Mov8(address, value);
}
@@ -1210,7 +1210,7 @@ namespace ARMeilleure.CodeGen.X86
ValidateBinOp(dest, src1, src2);
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
context.Assembler.Sub(dest, src2, dest.Type);
}
@@ -1236,7 +1236,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(!dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger && source.Type.IsInteger);
if (source.Type == OperandType.I32)
{
@@ -1259,7 +1259,7 @@ namespace ARMeilleure.CodeGen.X86
byte index = src2.AsByte();
Debug.Assert(index < OperandType.V128.GetSizeInBytes() / dest.Type.GetSizeInBytes());
Debug.Assert(index < OperandType.V128.ByteSize / dest.Type.ByteSize);
if (dest.Type == OperandType.I32)
{
@@ -1541,7 +1541,7 @@ namespace ARMeilleure.CodeGen.X86
{
Operand dest = operation.Destination;
Debug.Assert(!dest.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger);
context.Assembler.Pcmpeqw(dest, dest, dest);
}
@@ -1550,7 +1550,7 @@ namespace ARMeilleure.CodeGen.X86
{
Operand dest = operation.Destination;
Debug.Assert(!dest.Type.IsInteger());
Debug.Assert(!dest.Type.IsInteger);
context.Assembler.Xorps(dest, dest, dest);
}
@@ -1580,7 +1580,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Movzx16(dest, source, OperandType.I32);
}
@@ -1590,7 +1590,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
// We can eliminate the move if source is already 32-bit and the registers are the same.
if (dest.Value == source.Value && source.Type == OperandType.I32)
@@ -1606,7 +1606,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = operation.Destination;
Operand source = operation.GetSource(0);
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger);
context.Assembler.Movzx8(dest, source, OperandType.I32);
}
@@ -1713,12 +1713,12 @@ namespace ARMeilleure.CodeGen.X86
EnsureSameReg(dest, src1);
EnsureSameType(dest, src1);
Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32);
Debug.Assert(dest.Type.IsInteger && src2.Type == OperandType.I32);
}
private static void EnsureSameReg(Operand op1, Operand op2)
{
if (!op1.Type.IsInteger() && HardwareCapabilities.SupportsVexEncoding)
if (!op1.Type.IsInteger && HardwareCapabilities.SupportsVexEncoding)
{
return;
}

View File

@@ -86,7 +86,7 @@ namespace ARMeilleure.CodeGen.X86
break;
case Instruction.Negate:
if (!node.GetSource(0).Type.IsInteger())
if (!node.GetSource(0).Type.IsInteger)
{
GenerateNegate(block.Operations, node);
}
@@ -159,7 +159,7 @@ namespace ARMeilleure.CodeGen.X86
if (src1.Kind == OperandKind.Constant)
{
if (!src1.Type.IsInteger())
if (!src1.Type.IsInteger)
{
// Handle non-integer types (FP32, FP64 and V128).
// For instructions without an immediate operand, we do the following:
@@ -208,7 +208,7 @@ namespace ARMeilleure.CodeGen.X86
if (src2.Kind == OperandKind.Constant)
{
if (!src2.Type.IsInteger())
if (!src2.Type.IsInteger)
{
src2 = AddXmmCopy(nodes, node, src2);
@@ -298,7 +298,7 @@ namespace ARMeilleure.CodeGen.X86
// - The dividend is always in RDX:RAX.
// - The result is always in RAX.
// - Additionally it also writes the remainder in RDX.
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
Operand src1 = node.GetSource(0);
@@ -466,7 +466,7 @@ namespace ARMeilleure.CodeGen.X86
Operand dest = node.Destination;
Operand source = node.GetSource(0);
Debug.Assert(source.Type.IsInteger(), $"Invalid source type \"{source.Type}\".");
Debug.Assert(source.Type.IsInteger, $"Invalid source type \"{source.Type}\".");
Operation currentNode = node;
@@ -654,10 +654,10 @@ namespace ARMeilleure.CodeGen.X86
switch (operation.Instruction)
{
case Instruction.Add:
return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger();
return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger;
case Instruction.Multiply:
case Instruction.Subtract:
return !HardwareCapabilities.SupportsVexEncoding || operation.Destination.Type.IsInteger();
return !HardwareCapabilities.SupportsVexEncoding || operation.Destination.Type.IsInteger;
case Instruction.BitwiseAnd:
case Instruction.BitwiseExclusiveOr:
@@ -672,7 +672,7 @@ namespace ARMeilleure.CodeGen.X86
return true;
case Instruction.Divide:
return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger();
return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger;
case Instruction.VectorInsert:
case Instruction.VectorInsert16:

View File

@@ -35,7 +35,7 @@ namespace ARMeilleure.CodeGen.X86
bool passOnReg;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
passOnReg = intCount < intMax;
}
@@ -62,7 +62,7 @@ namespace ARMeilleure.CodeGen.X86
if (passOnReg)
{
Operand argReg = source.Type.IsInteger()
Operand argReg = source.Type.IsInteger
? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
@@ -80,7 +80,7 @@ namespace ARMeilleure.CodeGen.X86
InsertConstantRegCopies(nodes, nodes.AddBefore(node, spillOp));
stackOffset += source.Type.GetSizeInBytes();
stackOffset += source.Type.ByteSize;
}
}
@@ -102,7 +102,7 @@ namespace ARMeilleure.CodeGen.X86
}
else
{
Operand retReg = dest.Type.IsInteger()
Operand retReg = dest.Type.IsInteger
? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type)
: Xmm(CallingConvention.GetVecReturnRegister(), dest.Type);
@@ -137,7 +137,7 @@ namespace ARMeilleure.CodeGen.X86
bool passOnReg;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
passOnReg = intCount + 1 < intMax;
}
@@ -160,7 +160,7 @@ namespace ARMeilleure.CodeGen.X86
if (passOnReg)
{
Operand argReg = source.Type.IsInteger()
Operand argReg = source.Type.IsInteger
? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
@@ -210,7 +210,7 @@ namespace ARMeilleure.CodeGen.X86
{
OperandType argType = cctx.FuncArgTypes[cIndex];
if (argType.IsInteger())
if (argType.IsInteger)
{
intCount++;
}
@@ -226,7 +226,7 @@ namespace ARMeilleure.CodeGen.X86
bool passOnReg;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
passOnReg = intCount < CallingConvention.GetIntArgumentsOnRegsCount();
}
@@ -265,7 +265,7 @@ namespace ARMeilleure.CodeGen.X86
{
Operand pArg = Local(dest.Type);
Operand argReg = dest.Type.IsInteger()
Operand argReg = dest.Type.IsInteger
? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type)
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type);
@@ -320,7 +320,7 @@ namespace ARMeilleure.CodeGen.X86
}
else
{
Operand retReg = source.Type.IsInteger()
Operand retReg = source.Type.IsInteger
? Gpr(CallingConvention.GetIntReturnRegister(), source.Type)
: Xmm(CallingConvention.GetVecReturnRegister(), source.Type);

View File

@@ -40,7 +40,7 @@ namespace ARMeilleure.CodeGen.X86
if (dest != default && dest.Type == OperandType.V128)
{
int stackOffset = AllocateOnStack(dest.Type.GetSizeInBytes());
int stackOffset = AllocateOnStack(dest.Type.ByteSize);
arg0Reg = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64);
@@ -76,7 +76,7 @@ namespace ARMeilleure.CodeGen.X86
{
Operand stackAddr = Local(OperandType.I64);
int stackOffset = AllocateOnStack(source.Type.GetSizeInBytes());
int stackOffset = AllocateOnStack(source.Type.ByteSize);
nodes.AddBefore(node, Operation(Instruction.StackAlloc, stackAddr, Const(stackOffset)));
@@ -96,7 +96,7 @@ namespace ARMeilleure.CodeGen.X86
int argIndex = index + retArgs;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
argReg = Gpr(CallingConvention.GetIntArgumentRegister(argIndex), source.Type);
}
@@ -140,7 +140,7 @@ namespace ARMeilleure.CodeGen.X86
}
else
{
Operand retReg = dest.Type.IsInteger()
Operand retReg = dest.Type.IsInteger
? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type)
: Xmm(CallingConvention.GetVecReturnRegister(), dest.Type);
@@ -171,7 +171,7 @@ namespace ARMeilleure.CodeGen.X86
for (int index = 0; index < argsCount; index++)
{
Operand source = node.GetSource(1 + index);
Operand argReg = source.Type.IsInteger()
Operand argReg = source.Type.IsInteger
? Gpr(CallingConvention.GetIntArgumentRegister(index), source.Type)
: Xmm(CallingConvention.GetVecArgumentRegister(index), source.Type);
@@ -219,7 +219,7 @@ namespace ARMeilleure.CodeGen.X86
{
Operand argReg, pArg;
if (dest.Type.IsInteger())
if (dest.Type.IsInteger)
{
argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), dest.Type);
pArg = Local(dest.Type);
@@ -283,7 +283,7 @@ namespace ARMeilleure.CodeGen.X86
Operand source = node.GetSource(0);
Operand retReg;
if (source.Type.IsInteger())
if (source.Type.IsInteger)
{
retReg = Gpr(CallingConvention.GetIntReturnRegister(), source.Type);
}

View File

@@ -25,9 +25,9 @@ namespace ARMeilleure.CodeGen.X86
static class ComparisonX86Extensions
{
public static X86Condition ToX86Condition(this Comparison comp)
extension(Comparison comparison)
{
return comp switch
public X86Condition X86 => comparison switch
{
#pragma warning disable IDE0055 // Disable formatting
Comparison.Equal => X86Condition.Equal,
@@ -42,7 +42,7 @@ namespace ARMeilleure.CodeGen.X86
Comparison.LessUI => X86Condition.Below,
#pragma warning restore IDE0055
_ => throw new ArgumentException(null, nameof(comp)),
_ => throw new ArgumentException(null, nameof(comparison))
};
}
}

View File

@@ -168,7 +168,7 @@ namespace ARMeilleure.Common
{
_allocated.Dispose();
foreach (IntPtr page in _pages.Values)
foreach (nint page in _pages.Values)
{
NativeAllocator.Instance.Free((void*)page);
}

View File

@@ -22,11 +22,11 @@ namespace ARMeilleure.Decoders
static class ConditionExtensions
{
public static Condition Invert(this Condition cond)
extension(Condition condition)
{
// Bit 0 of all conditions is basically a negation bit, so
// inverting this bit has the effect of inverting the condition.
return (Condition)((int)cond ^ 1);
public Condition Inverse => (Condition)((int)condition ^ 1);
}
}
}

View File

@@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions
context.LoadFromContext();
context.Return(Const(op.Address));
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
}
public static void Svc(ArmEmitterContext context)
@@ -49,7 +49,7 @@ namespace ARMeilleure.Instructions
context.LoadFromContext();
context.Return(Const(op.Address));
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
}
}
}

View File

@@ -33,7 +33,7 @@ namespace ARMeilleure.Instructions
context.LoadFromContext();
context.Return(Const(context.CurrOp.Address));
InstEmitFlowHelper.EmitReturn(context, Const(context.CurrOp.Address));
}
}
}

View File

@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
{
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
context.Return(GetIntOrZR(context, op.Rn));
EmitReturn(context, GetIntOrZR(context, op.Rn));
}
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);

View File

@@ -13,6 +13,10 @@ namespace ARMeilleure.Instructions
{
static class InstEmitFlowHelper
{
// How many calls we can have in our call stack before we give up and return to the dispatcher.
// This prevents stack overflows caused by deep recursive calls.
private const int MaxCallDepth = 200;
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
{
if (cond != Condition.Al)
@@ -182,12 +186,7 @@ namespace ARMeilleure.Instructions
{
if (isReturn || context.IsSingleStep)
{
if (target.Type == OperandType.I32)
{
target = context.ZeroExtend32(OperandType.I64, target);
}
context.Return(target);
EmitReturn(context, target);
}
else
{
@@ -195,6 +194,19 @@ namespace ARMeilleure.Instructions
}
}
public static void EmitReturn(ArmEmitterContext context, Operand target)
{
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
DecreaseCallDepth(context, nativeContext);
if (target.Type == OperandType.I32)
{
target = context.ZeroExtend32(OperandType.I64, target);
}
context.Return(target);
}
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
{
context.StoreToContext();
@@ -257,6 +269,8 @@ namespace ARMeilleure.Instructions
if (isJump)
{
DecreaseCallDepth(context, nativeContext);
context.Tailcall(hostAddress, nativeContext);
}
else
@@ -278,8 +292,42 @@ namespace ARMeilleure.Instructions
Operand lblContinue = context.GetLabel(nextAddr.Value);
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
DecreaseCallDepth(context, nativeContext);
context.Return(returnAddress);
}
}
public static void EmitCallDepthCheckAndIncrement(EmitterContext context, Operand guestAddress)
{
if (!Optimizations.EnableDeepCallRecursionProtection)
{
return;
}
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
Operand lblDoCall = Label();
context.BranchIf(lblDoCall, currentCallDepth, Const(MaxCallDepth), Comparison.LessUI);
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
context.Return(guestAddress);
context.MarkLabel(lblDoCall);
context.Store(callDepthAddr, context.Add(currentCallDepth, Const(1)));
}
private static void DecreaseCallDepth(EmitterContext context, Operand nativeContext)
{
if (!Optimizations.EnableDeepCallRecursionProtection)
{
return;
}
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
}
}
}

View File

@@ -16,7 +16,7 @@ namespace ARMeilleure.Instructions
public static Operand EmitCrc32(ArmEmitterContext context, Operand crc, Operand value, int size, bool castagnoli)
{
Debug.Assert(crc.Type.IsInteger() && value.Type.IsInteger());
Debug.Assert(crc.Type.IsInteger && value.Type.IsInteger);
Debug.Assert(size is >= 0 and < 4);
Debug.Assert((size < 3) || (value.Type == OperandType.I64));

View File

@@ -157,7 +157,7 @@ namespace ARMeilleure.Instructions
context.Copy(temp, value);
if (!context.Memory.Type.IsHostMappedOrTracked())
if (!context.Memory.Type.IsHostMappedOrTracked)
{
context.Branch(lblEnd);
@@ -198,7 +198,7 @@ namespace ARMeilleure.Instructions
SetInt(context, rt, value);
if (!context.Memory.Type.IsHostMappedOrTracked())
if (!context.Memory.Type.IsHostMappedOrTracked)
{
context.Branch(lblEnd);
@@ -265,7 +265,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(rt), value);
if (!context.Memory.Type.IsHostMappedOrTracked())
if (!context.Memory.Type.IsHostMappedOrTracked)
{
context.Branch(lblEnd);
@@ -312,7 +312,7 @@ namespace ARMeilleure.Instructions
break;
}
if (!context.Memory.Type.IsHostMappedOrTracked())
if (!context.Memory.Type.IsHostMappedOrTracked)
{
context.Branch(lblEnd);
@@ -385,7 +385,7 @@ namespace ARMeilleure.Instructions
break;
}
if (!context.Memory.Type.IsHostMappedOrTracked())
if (!context.Memory.Type.IsHostMappedOrTracked)
{
context.Branch(lblEnd);
@@ -399,11 +399,11 @@ namespace ARMeilleure.Instructions
public static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblSlowPath, bool write, int size)
{
if (context.Memory.Type.IsHostMapped())
if (context.Memory.Type.IsHostMapped)
{
return EmitHostMappedPointer(context, address);
}
else if (context.Memory.Type.IsHostTracked())
else if (context.Memory.Type.IsHostTracked)
{
if (address.Type == OperandType.I32)
{

View File

@@ -1,692 +0,0 @@
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class SoftFallback
{
#region "ShrImm64"
[UnmanagedCallersOnly]
public static long SignedShrImm64(long value, long roundConst, int shift)
{
if (roundConst == 0L)
{
if (shift <= 63)
{
return value >> shift;
}
else /* if (shift == 64) */
{
if (value < 0L)
{
return -1L;
}
else /* if (value >= 0L) */
{
return 0L;
}
}
}
else /* if (roundConst == 1L << (shift - 1)) */
{
if (shift <= 63)
{
long add = value + roundConst;
if ((~value & (value ^ add)) < 0L)
{
return (long)((ulong)add >> shift);
}
else
{
return add >> shift;
}
}
else /* if (shift == 64) */
{
return 0L;
}
}
}
[UnmanagedCallersOnly]
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
{
if (roundConst == 0L)
{
if (shift <= 63)
{
return value >> shift;
}
else /* if (shift == 64) */
{
return 0UL;
}
}
else /* if (roundConst == 1L << (shift - 1)) */
{
ulong add = value + (ulong)roundConst;
if ((add < value) && (add < (ulong)roundConst))
{
if (shift <= 63)
{
return (add >> shift) | (0x8000000000000000UL >> (shift - 1));
}
else /* if (shift == 64) */
{
return 1UL;
}
}
else
{
if (shift <= 63)
{
return add >> shift;
}
else /* if (shift == 64) */
{
return 0UL;
}
}
}
}
#endregion
#region "Saturation"
[UnmanagedCallersOnly]
public static int SatF32ToS32(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= int.MaxValue ? int.MaxValue :
value <= int.MinValue ? int.MinValue : (int)value;
}
[UnmanagedCallersOnly]
public static long SatF32ToS64(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= long.MaxValue ? long.MaxValue :
value <= long.MinValue ? long.MinValue : (long)value;
}
[UnmanagedCallersOnly]
public static uint SatF32ToU32(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= uint.MaxValue ? uint.MaxValue :
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[UnmanagedCallersOnly]
public static ulong SatF32ToU64(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= ulong.MaxValue ? ulong.MaxValue :
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
}
[UnmanagedCallersOnly]
public static int SatF64ToS32(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= int.MaxValue ? int.MaxValue :
value <= int.MinValue ? int.MinValue : (int)value;
}
[UnmanagedCallersOnly]
public static long SatF64ToS64(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= long.MaxValue ? long.MaxValue :
value <= long.MinValue ? long.MinValue : (long)value;
}
[UnmanagedCallersOnly]
public static uint SatF64ToU32(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= uint.MaxValue ? uint.MaxValue :
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[UnmanagedCallersOnly]
public static ulong SatF64ToU64(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= ulong.MaxValue ? ulong.MaxValue :
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
}
#endregion
#region "Count"
[UnmanagedCallersOnly]
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
value ^= value >> 1;
int highBit = size - 2;
for (int bit = highBit; bit >= 0; bit--)
{
if (((int)(value >> bit) & 0b1) != 0)
{
return (ulong)(highBit - bit);
}
}
return (ulong)(size - 1);
}
private static ReadOnlySpan<byte> ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
[UnmanagedCallersOnly]
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
if (value == 0ul)
{
return (ulong)size;
}
int nibbleIdx = size;
int preCount, count = 0;
do
{
nibbleIdx -= 4;
preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111];
count += preCount;
}
while (preCount == 4);
return (ulong)count;
}
#endregion
#region "Table"
[UnmanagedCallersOnly]
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
{
return TblOrTbx(default, vector, bytes, tb0);
}
[UnmanagedCallersOnly]
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
{
return TblOrTbx(default, vector, bytes, tb0, tb1);
}
[UnmanagedCallersOnly]
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
{
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
}
[UnmanagedCallersOnly]
public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
{
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3);
}
[UnmanagedCallersOnly]
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
{
return TblOrTbx(dest, vector, bytes, tb0);
}
[UnmanagedCallersOnly]
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1);
}
[UnmanagedCallersOnly]
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
}
[UnmanagedCallersOnly]
public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
}
private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params ReadOnlySpan<V128> tb)
{
byte[] res = new byte[16];
if (dest != default)
{
Buffer.BlockCopy(dest.ToArray(), 0, res, 0, bytes);
}
byte[] table = new byte[tb.Length * 16];
for (byte index = 0; index < tb.Length; index++)
{
Buffer.BlockCopy(tb[index].ToArray(), 0, table, index * 16, 16);
}
byte[] v = vector.ToArray();
for (byte index = 0; index < bytes; index++)
{
byte tblIndex = v[index];
if (tblIndex < table.Length)
{
res[index] = table[tblIndex];
}
}
return new V128(res);
}
#endregion
#region "Crc32"
private const uint Crc32RevPoly = 0xedb88320;
private const uint Crc32cRevPoly = 0x82f63b78;
[UnmanagedCallersOnly]
public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
private static uint Crc32h(uint crc, uint poly, ushort val)
{
crc = Crc32(crc, poly, (byte)(val >> 0));
crc = Crc32(crc, poly, (byte)(val >> 8));
return crc;
}
private static uint Crc32w(uint crc, uint poly, uint val)
{
crc = Crc32(crc, poly, (byte)(val >> 0));
crc = Crc32(crc, poly, (byte)(val >> 8));
crc = Crc32(crc, poly, (byte)(val >> 16));
crc = Crc32(crc, poly, (byte)(val >> 24));
return crc;
}
private static uint Crc32x(uint crc, uint poly, ulong val)
{
crc = Crc32(crc, poly, (byte)(val >> 0));
crc = Crc32(crc, poly, (byte)(val >> 8));
crc = Crc32(crc, poly, (byte)(val >> 16));
crc = Crc32(crc, poly, (byte)(val >> 24));
crc = Crc32(crc, poly, (byte)(val >> 32));
crc = Crc32(crc, poly, (byte)(val >> 40));
crc = Crc32(crc, poly, (byte)(val >> 48));
crc = Crc32(crc, poly, (byte)(val >> 56));
return crc;
}
private static uint Crc32(uint crc, uint poly, byte val)
{
crc ^= val;
for (int bit = 7; bit >= 0; bit--)
{
uint mask = (uint)(-(int)(crc & 1));
crc = (crc >> 1) ^ (poly & mask);
}
return crc;
}
#endregion
#region "Aes"
[UnmanagedCallersOnly]
public static V128 Decrypt(V128 value, V128 roundKey)
{
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
}
[UnmanagedCallersOnly]
public static V128 Encrypt(V128 value, V128 roundKey)
{
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
}
[UnmanagedCallersOnly]
public static V128 InverseMixColumns(V128 value)
{
return CryptoHelper.AesInvMixColumns(value);
}
[UnmanagedCallersOnly]
public static V128 MixColumns(V128 value)
{
return CryptoHelper.AesMixColumns(value);
}
#endregion
#region "Sha1"
[UnmanagedCallersOnly]
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
{
uint t = ShaChoose(hash_abcd.Extract<uint>(1),
hash_abcd.Extract<uint>(2),
hash_abcd.Extract<uint>(3));
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
t = Rol(hash_abcd.Extract<uint>(1), 30);
hash_abcd.Insert(1, t);
Rol32_160(ref hash_e, ref hash_abcd);
}
return hash_abcd;
}
[UnmanagedCallersOnly]
public static uint FixedRotate(uint hash_e)
{
return hash_e.Rol(30);
}
[UnmanagedCallersOnly]
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
{
uint t = ShaMajority(hash_abcd.Extract<uint>(1),
hash_abcd.Extract<uint>(2),
hash_abcd.Extract<uint>(3));
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
t = Rol(hash_abcd.Extract<uint>(1), 30);
hash_abcd.Insert(1, t);
Rol32_160(ref hash_e, ref hash_abcd);
}
return hash_abcd;
}
[UnmanagedCallersOnly]
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
{
uint t = ShaParity(hash_abcd.Extract<uint>(1),
hash_abcd.Extract<uint>(2),
hash_abcd.Extract<uint>(3));
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
t = Rol(hash_abcd.Extract<uint>(1), 30);
hash_abcd.Insert(1, t);
Rol32_160(ref hash_e, ref hash_abcd);
}
return hash_abcd;
}
[UnmanagedCallersOnly]
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
{
ulong t2 = w4_7.Extract<ulong>(0);
ulong t1 = w0_3.Extract<ulong>(1);
V128 result = new(t1, t2);
return result ^ (w0_3 ^ w8_11);
}
[UnmanagedCallersOnly]
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
{
V128 t = tw0_3 ^ (w12_15 >> 32);
uint tE0 = t.Extract<uint>(0);
uint tE1 = t.Extract<uint>(1);
uint tE2 = t.Extract<uint>(2);
uint tE3 = t.Extract<uint>(3);
return new V128(tE0.Rol(1), tE1.Rol(1), tE2.Rol(1), tE3.Rol(1) ^ tE0.Rol(2));
}
private static void Rol32_160(ref uint y, ref V128 x)
{
uint xE3 = x.Extract<uint>(3);
x <<= 32;
x.Insert(0, y);
y = xE3;
}
private static uint ShaChoose(uint x, uint y, uint z)
{
return ((y ^ z) & x) ^ z;
}
private static uint ShaMajority(uint x, uint y, uint z)
{
return (x & y) | ((x | y) & z);
}
private static uint ShaParity(uint x, uint y, uint z)
{
return x ^ y ^ z;
}
private static uint Rol(this uint value, int count)
{
return (value << count) | (value >> (32 - count));
}
#endregion
#region "Sha256"
[UnmanagedCallersOnly]
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
{
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
}
[UnmanagedCallersOnly]
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
{
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
}
[UnmanagedCallersOnly]
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
{
V128 result = new();
for (int e = 0; e <= 3; e++)
{
uint elt = (e <= 2 ? w0_3 : w4_7).Extract<uint>(e <= 2 ? e + 1 : 0);
elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3);
elt += w0_3.Extract<uint>(e);
result.Insert(e, elt);
}
return result;
}
[UnmanagedCallersOnly]
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
{
V128 result = new();
ulong t1 = w12_15.Extract<ulong>(1);
for (int e = 0; e <= 1; e++)
{
uint elt = t1.ULongPart(e);
elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10);
elt += w0_3.Extract<uint>(e) + w8_11.Extract<uint>(e + 1);
result.Insert(e, elt);
}
t1 = result.Extract<ulong>(0);
for (int e = 2; e <= 3; e++)
{
uint elt = t1.ULongPart(e - 2);
elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10);
elt += w0_3.Extract<uint>(e) + (e == 2 ? w8_11 : w12_15).Extract<uint>(e == 2 ? 3 : 0);
result.Insert(e, elt);
}
return result;
}
private static V128 Sha256Hash(V128 x, V128 y, V128 w, bool part1)
{
for (int e = 0; e <= 3; e++)
{
uint chs = ShaChoose(y.Extract<uint>(0),
y.Extract<uint>(1),
y.Extract<uint>(2));
uint maj = ShaMajority(x.Extract<uint>(0),
x.Extract<uint>(1),
x.Extract<uint>(2));
uint t1 = y.Extract<uint>(3) + ShaHashSigma1(y.Extract<uint>(0)) + chs + w.Extract<uint>(e);
uint t2 = t1 + x.Extract<uint>(3);
x.Insert(3, t2);
t2 = t1 + ShaHashSigma0(x.Extract<uint>(0)) + maj;
y.Insert(3, t2);
Rol32_256(ref y, ref x);
}
return part1 ? x : y;
}
private static void Rol32_256(ref V128 y, ref V128 x)
{
uint yE3 = y.Extract<uint>(3);
uint xE3 = x.Extract<uint>(3);
y <<= 32;
x <<= 32;
y.Insert(0, xE3);
x.Insert(0, yE3);
}
private static uint ShaHashSigma0(uint x)
{
return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22);
}
private static uint ShaHashSigma1(uint x)
{
return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25);
}
private static uint Ror(this uint value, int count)
{
return (value >> count) | (value << (32 - count));
}
private static uint Lsr(this uint value, int count)
{
return value >> count;
}
private static uint ULongPart(this ulong value, int part)
{
return part == 0
? (uint)(value & 0xFFFFFFFFUL)
: (uint)(value >> 32);
}
#endregion
[UnmanagedCallersOnly]
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
{
V128 result = V128.Zero;
V128 op2_128 = new(op2, 0);
for (int i = 0; i < 64; i++)
{
if (((op1 >> i) & 1) == 1)
{
result ^= op2_128 << i;
}
}
return result;
}
}
}

View File

@@ -0,0 +1,32 @@
using ARMeilleure.State;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static V128 Decrypt(V128 value, V128 roundKey)
{
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
}
[UnmanagedCallersOnly]
public static V128 Encrypt(V128 value, V128 roundKey)
{
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
}
[UnmanagedCallersOnly]
public static V128 InverseMixColumns(V128 value)
{
return CryptoHelper.AesInvMixColumns(value);
}
[UnmanagedCallersOnly]
public static V128 MixColumns(V128 value)
{
return CryptoHelper.AesMixColumns(value);
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
value ^= value >> 1;
int highBit = size - 2;
for (int bit = highBit; bit >= 0; bit--)
{
if (((int)(value >> bit) & 0b1) != 0)
{
return (ulong)(highBit - bit);
}
}
return (ulong)(size - 1);
}
private static ReadOnlySpan<byte> ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
[UnmanagedCallersOnly]
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
if (value == 0ul)
{
return (ulong)size;
}
int nibbleIdx = size;
int preCount, count = 0;
do
{
nibbleIdx -= 4;
preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111];
count += preCount;
}
while (preCount == 4);
return (ulong)count;
}
}
}

View File

@@ -0,0 +1,74 @@
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
private const uint Crc32RevPoly = 0xedb88320;
private const uint Crc32cRevPoly = 0x82f63b78;
[UnmanagedCallersOnly]
public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
private static uint Crc32h(uint crc, uint poly, ushort val)
{
crc = Crc32(crc, poly, (byte)(val >> 0));
crc = Crc32(crc, poly, (byte)(val >> 8));
return crc;
}
private static uint Crc32w(uint crc, uint poly, uint val)
{
crc = Crc32(crc, poly, (byte)(val >> 0));
crc = Crc32(crc, poly, (byte)(val >> 8));
crc = Crc32(crc, poly, (byte)(val >> 16));
crc = Crc32(crc, poly, (byte)(val >> 24));
return crc;
}
private static uint Crc32x(uint crc, uint poly, ulong val)
{
crc = Crc32(crc, poly, (byte)(val >> 0));
crc = Crc32(crc, poly, (byte)(val >> 8));
crc = Crc32(crc, poly, (byte)(val >> 16));
crc = Crc32(crc, poly, (byte)(val >> 24));
crc = Crc32(crc, poly, (byte)(val >> 32));
crc = Crc32(crc, poly, (byte)(val >> 40));
crc = Crc32(crc, poly, (byte)(val >> 48));
crc = Crc32(crc, poly, (byte)(val >> 56));
return crc;
}
private static uint Crc32(uint crc, uint poly, byte val)
{
crc ^= val;
for (int bit = 7; bit >= 0; bit--)
{
uint mask = (uint)(-(int)(crc & 1));
crc = (crc >> 1) ^ (poly & mask);
}
return crc;
}
}
}

View File

@@ -0,0 +1,103 @@
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static int SatF32ToS32(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= int.MaxValue ? int.MaxValue :
value <= int.MinValue ? int.MinValue : (int)value;
}
[UnmanagedCallersOnly]
public static long SatF32ToS64(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= long.MaxValue ? long.MaxValue :
value <= long.MinValue ? long.MinValue : (long)value;
}
[UnmanagedCallersOnly]
public static uint SatF32ToU32(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= uint.MaxValue ? uint.MaxValue :
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[UnmanagedCallersOnly]
public static ulong SatF32ToU64(float value)
{
if (float.IsNaN(value))
{
return 0;
}
return value >= ulong.MaxValue ? ulong.MaxValue :
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
}
[UnmanagedCallersOnly]
public static int SatF64ToS32(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= int.MaxValue ? int.MaxValue :
value <= int.MinValue ? int.MinValue : (int)value;
}
[UnmanagedCallersOnly]
public static long SatF64ToS64(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= long.MaxValue ? long.MaxValue :
value <= long.MinValue ? long.MinValue : (long)value;
}
[UnmanagedCallersOnly]
public static uint SatF64ToU32(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= uint.MaxValue ? uint.MaxValue :
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[UnmanagedCallersOnly]
public static ulong SatF64ToU64(double value)
{
if (double.IsNaN(value))
{
return 0;
}
return value >= ulong.MaxValue ? ulong.MaxValue :
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
}
}
}

View File

@@ -0,0 +1,131 @@
using ARMeilleure.State;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
{
uint t = ShaChoose(hash_abcd.Extract<uint>(1),
hash_abcd.Extract<uint>(2),
hash_abcd.Extract<uint>(3));
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
t = Rol(hash_abcd.Extract<uint>(1), 30);
hash_abcd.Insert(1, t);
Rol32_160(ref hash_e, ref hash_abcd);
}
return hash_abcd;
}
[UnmanagedCallersOnly]
public static uint FixedRotate(uint hash_e)
{
return hash_e.Rol(30);
}
[UnmanagedCallersOnly]
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
{
uint t = ShaMajority(hash_abcd.Extract<uint>(1),
hash_abcd.Extract<uint>(2),
hash_abcd.Extract<uint>(3));
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
t = Rol(hash_abcd.Extract<uint>(1), 30);
hash_abcd.Insert(1, t);
Rol32_160(ref hash_e, ref hash_abcd);
}
return hash_abcd;
}
[UnmanagedCallersOnly]
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
{
uint t = ShaParity(hash_abcd.Extract<uint>(1),
hash_abcd.Extract<uint>(2),
hash_abcd.Extract<uint>(3));
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
t = Rol(hash_abcd.Extract<uint>(1), 30);
hash_abcd.Insert(1, t);
Rol32_160(ref hash_e, ref hash_abcd);
}
return hash_abcd;
}
[UnmanagedCallersOnly]
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
{
ulong t2 = w4_7.Extract<ulong>(0);
ulong t1 = w0_3.Extract<ulong>(1);
V128 result = new(t1, t2);
return result ^ (w0_3 ^ w8_11);
}
[UnmanagedCallersOnly]
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
{
V128 t = tw0_3 ^ (w12_15 >> 32);
uint tE0 = t.Extract<uint>(0);
uint tE1 = t.Extract<uint>(1);
uint tE2 = t.Extract<uint>(2);
uint tE3 = t.Extract<uint>(3);
return new V128(tE0.Rol(1), tE1.Rol(1), tE2.Rol(1), tE3.Rol(1) ^ tE0.Rol(2));
}
private static void Rol32_160(ref uint y, ref V128 x)
{
uint xE3 = x.Extract<uint>(3);
x <<= 32;
x.Insert(0, y);
y = xE3;
}
private static uint ShaChoose(uint x, uint y, uint z)
{
return ((y ^ z) & x) ^ z;
}
private static uint ShaMajority(uint x, uint y, uint z)
{
return (x & y) | ((x | y) & z);
}
private static uint ShaParity(uint x, uint y, uint z)
{
return x ^ y ^ z;
}
private static uint Rol(this uint value, int count)
{
return (value << count) | (value >> (32 - count));
}
}
}

View File

@@ -0,0 +1,140 @@
using ARMeilleure.State;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
{
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
}
[UnmanagedCallersOnly]
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
{
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
}
[UnmanagedCallersOnly]
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
{
V128 result = new();
for (int e = 0; e <= 3; e++)
{
uint elt = (e <= 2 ? w0_3 : w4_7).Extract<uint>(e <= 2 ? e + 1 : 0);
elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3);
elt += w0_3.Extract<uint>(e);
result.Insert(e, elt);
}
return result;
}
[UnmanagedCallersOnly]
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
{
V128 result = new();
ulong t1 = w12_15.Extract<ulong>(1);
for (int e = 0; e <= 1; e++)
{
uint elt = t1.ULongPart(e);
elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10);
elt += w0_3.Extract<uint>(e) + w8_11.Extract<uint>(e + 1);
result.Insert(e, elt);
}
t1 = result.Extract<ulong>(0);
for (int e = 2; e <= 3; e++)
{
uint elt = t1.ULongPart(e - 2);
elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10);
elt += w0_3.Extract<uint>(e) + (e == 2 ? w8_11 : w12_15).Extract<uint>(e == 2 ? 3 : 0);
result.Insert(e, elt);
}
return result;
}
private static V128 Sha256Hash(V128 x, V128 y, V128 w, bool part1)
{
for (int e = 0; e <= 3; e++)
{
uint chs = ShaChoose(y.Extract<uint>(0),
y.Extract<uint>(1),
y.Extract<uint>(2));
uint maj = ShaMajority(x.Extract<uint>(0),
x.Extract<uint>(1),
x.Extract<uint>(2));
uint t1 = y.Extract<uint>(3) + ShaHashSigma1(y.Extract<uint>(0)) + chs + w.Extract<uint>(e);
uint t2 = t1 + x.Extract<uint>(3);
x.Insert(3, t2);
t2 = t1 + ShaHashSigma0(x.Extract<uint>(0)) + maj;
y.Insert(3, t2);
Rol32_256(ref y, ref x);
}
return part1 ? x : y;
}
private static void Rol32_256(ref V128 y, ref V128 x)
{
uint yE3 = y.Extract<uint>(3);
uint xE3 = x.Extract<uint>(3);
y <<= 32;
x <<= 32;
y.Insert(0, xE3);
x.Insert(0, yE3);
}
private static uint ShaHashSigma0(uint x)
{
return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22);
}
private static uint ShaHashSigma1(uint x)
{
return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25);
}
private static uint Ror(this uint value, int count)
{
return (value >> count) | (value << (32 - count));
}
private static uint Lsr(this uint value, int count)
{
return value >> count;
}
private static uint ULongPart(this ulong value, int part)
{
return part == 0
? (uint)(value & 0xFFFFFFFFUL)
: (uint)(value >> 32);
}
}
}

View File

@@ -0,0 +1,93 @@
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static long SignedShrImm64(long value, long roundConst, int shift)
{
if (roundConst == 0L)
{
if (shift <= 63)
{
return value >> shift;
}
else /* if (shift == 64) */
{
if (value < 0L)
{
return -1L;
}
else /* if (value >= 0L) */
{
return 0L;
}
}
}
else /* if (roundConst == 1L << (shift - 1)) */
{
if (shift <= 63)
{
long add = value + roundConst;
if ((~value & (value ^ add)) < 0L)
{
return (long)((ulong)add >> shift);
}
else
{
return add >> shift;
}
}
else /* if (shift == 64) */
{
return 0L;
}
}
}
[UnmanagedCallersOnly]
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
{
if (roundConst == 0L)
{
if (shift <= 63)
{
return value >> shift;
}
else /* if (shift == 64) */
{
return 0UL;
}
}
else /* if (roundConst == 1L << (shift - 1)) */
{
ulong add = value + (ulong)roundConst;
if ((add < value) && (add < (ulong)roundConst))
{
if (shift <= 63)
{
return (add >> shift) | (0x8000000000000000UL >> (shift - 1));
}
else /* if (shift == 64) */
{
return 1UL;
}
}
else
{
if (shift <= 63)
{
return add >> shift;
}
else /* if (shift == 64) */
{
return 0UL;
}
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
{
return TblOrTbx(default, vector, bytes, tb0);
}
[UnmanagedCallersOnly]
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
{
return TblOrTbx(default, vector, bytes, tb0, tb1);
}
[UnmanagedCallersOnly]
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
{
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
}
[UnmanagedCallersOnly]
public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
{
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3);
}
[UnmanagedCallersOnly]
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
{
return TblOrTbx(dest, vector, bytes, tb0);
}
[UnmanagedCallersOnly]
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1);
}
[UnmanagedCallersOnly]
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
}
[UnmanagedCallersOnly]
public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
}
private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params ReadOnlySpan<V128> tb)
{
byte[] res = new byte[16];
if (dest != default)
{
Buffer.BlockCopy(dest.ToArray(), 0, res, 0, bytes);
}
byte[] table = new byte[tb.Length * 16];
for (byte index = 0; index < tb.Length; index++)
{
Buffer.BlockCopy(tb[index].ToArray(), 0, table, index * 16, 16);
}
byte[] v = vector.ToArray();
for (byte index = 0; index < bytes; index++)
{
byte tblIndex = v[index];
if (tblIndex < table.Length)
{
res[index] = table[tblIndex];
}
}
return new V128(res);
}
}
}

View File

@@ -0,0 +1,26 @@
using ARMeilleure.State;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static partial class SoftFallback
{
[UnmanagedCallersOnly]
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
{
V128 result = V128.Zero;
V128 op2_128 = new(op2, 0);
for (int i = 0; i < 64; i++)
{
if (((op1 >> i) & 1) == 1)
{
result ^= op2_128 << i;
}
}
return result;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
using ARMeilleure.State;
using System;
using System.Diagnostics;
namespace ARMeilleure.Instructions
{
static class SoftFloat
{
static SoftFloat()
{
RecipEstimateTable = BuildRecipEstimateTable();
RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable();
}
public static readonly byte[] RecipEstimateTable;
public static readonly byte[] RecipSqrtEstimateTable;
private static byte[] BuildRecipEstimateTable()
{
byte[] tbl = new byte[256];
for (int idx = 0; idx < 256; idx++)
{
uint src = (uint)idx + 256u;
Debug.Assert(src is >= 256u and < 512u);
src = (src << 1) + 1u;
uint aux = (1u << 19) / src;
uint dst = (aux + 1u) >> 1;
Debug.Assert(dst is >= 256u and < 512u);
tbl[idx] = (byte)(dst - 256u);
}
return tbl;
}
private static byte[] BuildRecipSqrtEstimateTable()
{
byte[] tbl = new byte[384];
for (int idx = 0; idx < 384; idx++)
{
uint src = (uint)idx + 128u;
Debug.Assert(src is >= 128u and < 512u);
if (src < 256u)
{
src = (src << 1) + 1u;
}
else
{
src = (src >> 1) << 1;
src = (src + 1u) << 1;
}
uint aux = 512u;
while (src * (aux + 1u) * (aux + 1u) < (1u << 28))
{
aux++;
}
uint dst = (aux + 1u) >> 1;
Debug.Assert(dst is >= 256u and < 512u);
tbl[idx] = (byte)(dst - 256u);
}
return tbl;
}
public static void FPProcessException(FPException exc, ExecutionContext context)
{
FPProcessException(exc, context, context.Fpcr);
}
public static void FPProcessException(FPException exc, ExecutionContext context, FPCR fpcr)
{
int enable = (int)exc + 8;
if ((fpcr & (FPCR)(1 << enable)) != 0)
{
throw new NotImplementedException("Floating-point trap handling.");
}
else
{
context.Fpsr |= (FPSR)(1 << (int)exc);
}
}
extension(FPCR fpcr)
{
public FPRoundingMode RoundingMode
{
get
{
const int RModeShift = 22;
return (FPRoundingMode)(((uint)fpcr >> RModeShift) & 3u);
}
}
}
}
}

View File

@@ -0,0 +1,212 @@
using ARMeilleure.State;
using System;
namespace ARMeilleure.Instructions
{
static class SoftFloat16
{
public static ushort FPDefaultNaN()
{
return (ushort)0x7E00u;
}
public static ushort FPInfinity(bool sign)
{
return sign ? (ushort)0xFC00u : (ushort)0x7C00u;
}
public static ushort FPZero(bool sign)
{
return sign ? (ushort)0x8000u : (ushort)0x0000u;
}
public static ushort FPMaxNormal(bool sign)
{
return sign ? (ushort)0xFBFFu : (ushort)0x7BFFu;
}
public static double FPUnpackCv(
this ushort valueBits,
out FPType type,
out bool sign,
ExecutionContext context)
{
sign = (~(uint)valueBits & 0x8000u) == 0u;
uint exp16 = ((uint)valueBits & 0x7C00u) >> 10;
uint frac16 = (uint)valueBits & 0x03FFu;
double real;
if (exp16 == 0u)
{
if (frac16 == 0u)
{
type = FPType.Zero;
real = 0d;
}
else
{
type = FPType.Nonzero; // Subnormal.
real = Math.Pow(2d, -14) * ((double)frac16 * Math.Pow(2d, -10));
}
}
else if (exp16 == 0x1Fu && (context.Fpcr & FPCR.Ahp) == 0)
{
if (frac16 == 0u)
{
type = FPType.Infinity;
real = Math.Pow(2d, 1000);
}
else
{
type = (~frac16 & 0x0200u) == 0u ? FPType.QNaN : FPType.SNaN;
real = 0d;
}
}
else
{
type = FPType.Nonzero; // Normal.
real = Math.Pow(2d, (int)exp16 - 15) * (1d + (double)frac16 * Math.Pow(2d, -10));
}
return sign ? -real : real;
}
public static ushort FPRoundCv(double real, ExecutionContext context)
{
const int MinimumExp = -14;
const int E = 5;
const int F = 10;
bool sign;
double mantissa;
if (real < 0d)
{
sign = true;
mantissa = -real;
}
else
{
sign = false;
mantissa = real;
}
int exponent = 0;
while (mantissa < 1d)
{
mantissa *= 2d;
exponent--;
}
while (mantissa >= 2d)
{
mantissa /= 2d;
exponent++;
}
uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0);
if (biasedExp == 0u)
{
mantissa /= Math.Pow(2d, MinimumExp - exponent);
}
uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, F));
double error = mantissa * Math.Pow(2d, F) - (double)intMant;
if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0))
{
SoftFloat.FPProcessException(FPException.Underflow, context);
}
bool overflowToInf;
bool roundUp;
switch (context.Fpcr.RoundingMode)
{
case FPRoundingMode.ToNearest:
roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u));
overflowToInf = true;
break;
case FPRoundingMode.TowardsPlusInfinity:
roundUp = (error != 0d && !sign);
overflowToInf = !sign;
break;
case FPRoundingMode.TowardsMinusInfinity:
roundUp = (error != 0d && sign);
overflowToInf = sign;
break;
case FPRoundingMode.TowardsZero:
roundUp = false;
overflowToInf = false;
break;
default:
throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.RoundingMode}\".");
}
if (roundUp)
{
intMant++;
if (intMant == 1u << F)
{
biasedExp = 1u;
}
if (intMant == 1u << (F + 1))
{
biasedExp++;
intMant >>= 1;
}
}
ushort resultBits;
if ((context.Fpcr & FPCR.Ahp) == 0)
{
if (biasedExp >= (1u << E) - 1u)
{
resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
SoftFloat.FPProcessException(FPException.Overflow, context);
error = 1d;
}
else
{
resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu));
}
}
else
{
if (biasedExp >= 1u << E)
{
resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu);
SoftFloat.FPProcessException(FPException.InvalidOp, context);
error = 0d;
}
else
{
resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu));
}
}
if (error != 0d)
{
SoftFloat.FPProcessException(FPException.Inexact, context);
}
return resultBits;
}
}
}

View File

@@ -0,0 +1,182 @@
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class SoftFloat16_32
{
[UnmanagedCallersOnly]
public static float FPConvert(ushort valueBits)
{
ExecutionContext context = NativeInterface.GetContext();
double real = valueBits.FPUnpackCv(out FPType type, out bool sign, context);
float result;
if (type is FPType.SNaN or FPType.QNaN)
{
if ((context.Fpcr & FPCR.Dn) != 0)
{
result = SoftFloat32.FPDefaultNaN();
}
else
{
result = FPConvertNaN(valueBits);
}
if (type == FPType.SNaN)
{
SoftFloat.FPProcessException(FPException.InvalidOp, context);
}
}
else if (type == FPType.Infinity)
{
result = SoftFloat32.FPInfinity(sign);
}
else if (type == FPType.Zero)
{
result = SoftFloat32.FPZero(sign);
}
else
{
result = FPRoundCv(real, context);
}
return result;
}
private static float FPRoundCv(double real, ExecutionContext context)
{
const int MinimumExp = -126;
const int E = 8;
const int F = 23;
bool sign;
double mantissa;
if (real < 0d)
{
sign = true;
mantissa = -real;
}
else
{
sign = false;
mantissa = real;
}
int exponent = 0;
while (mantissa < 1d)
{
mantissa *= 2d;
exponent--;
}
while (mantissa >= 2d)
{
mantissa /= 2d;
exponent++;
}
if ((context.Fpcr & FPCR.Fz) != 0 && exponent < MinimumExp)
{
context.Fpsr |= FPSR.Ufc;
return SoftFloat32.FPZero(sign);
}
uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0);
if (biasedExp == 0u)
{
mantissa /= Math.Pow(2d, MinimumExp - exponent);
}
uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, F));
double error = mantissa * Math.Pow(2d, F) - (double)intMant;
if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0))
{
SoftFloat.FPProcessException(FPException.Underflow, context);
}
bool overflowToInf;
bool roundUp;
switch (context.Fpcr.RoundingMode)
{
case FPRoundingMode.ToNearest:
roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u));
overflowToInf = true;
break;
case FPRoundingMode.TowardsPlusInfinity:
roundUp = (error != 0d && !sign);
overflowToInf = !sign;
break;
case FPRoundingMode.TowardsMinusInfinity:
roundUp = (error != 0d && sign);
overflowToInf = sign;
break;
case FPRoundingMode.TowardsZero:
roundUp = false;
overflowToInf = false;
break;
default:
throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.RoundingMode}\".");
}
if (roundUp)
{
intMant++;
if (intMant == 1u << F)
{
biasedExp = 1u;
}
if (intMant == 1u << (F + 1))
{
biasedExp++;
intMant >>= 1;
}
}
float result;
if (biasedExp >= (1u << E) - 1u)
{
result = overflowToInf ? SoftFloat32.FPInfinity(sign) : SoftFloat32.FPMaxNormal(sign);
SoftFloat.FPProcessException(FPException.Overflow, context);
error = 1d;
}
else
{
result = BitConverter.Int32BitsToSingle(
(int)((sign ? 1u : 0u) << 31 | (biasedExp & 0xFFu) << 23 | (intMant & 0x007FFFFFu)));
}
if (error != 0d)
{
SoftFloat.FPProcessException(FPException.Inexact, context);
}
return result;
}
private static float FPConvertNaN(ushort valueBits)
{
return BitConverter.Int32BitsToSingle(
(int)(((uint)valueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)valueBits & 0x01FFu) << 13));
}
}
}

View File

@@ -0,0 +1,182 @@
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class SoftFloat16_64
{
[UnmanagedCallersOnly]
public static double FPConvert(ushort valueBits)
{
ExecutionContext context = NativeInterface.GetContext();
double real = valueBits.FPUnpackCv(out FPType type, out bool sign, context);
double result;
if (type is FPType.SNaN or FPType.QNaN)
{
if ((context.Fpcr & FPCR.Dn) != 0)
{
result = SoftFloat64.FPDefaultNaN();
}
else
{
result = FPConvertNaN(valueBits);
}
if (type == FPType.SNaN)
{
SoftFloat.FPProcessException(FPException.InvalidOp, context);
}
}
else if (type == FPType.Infinity)
{
result = SoftFloat64.FPInfinity(sign);
}
else if (type == FPType.Zero)
{
result = SoftFloat64.FPZero(sign);
}
else
{
result = FPRoundCv(real, context);
}
return result;
}
private static double FPRoundCv(double real, ExecutionContext context)
{
const int MinimumExp = -1022;
const int E = 11;
const int F = 52;
bool sign;
double mantissa;
if (real < 0d)
{
sign = true;
mantissa = -real;
}
else
{
sign = false;
mantissa = real;
}
int exponent = 0;
while (mantissa < 1d)
{
mantissa *= 2d;
exponent--;
}
while (mantissa >= 2d)
{
mantissa /= 2d;
exponent++;
}
if ((context.Fpcr & FPCR.Fz) != 0 && exponent < MinimumExp)
{
context.Fpsr |= FPSR.Ufc;
return SoftFloat64.FPZero(sign);
}
uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0);
if (biasedExp == 0u)
{
mantissa /= Math.Pow(2d, MinimumExp - exponent);
}
ulong intMant = (ulong)Math.Floor(mantissa * Math.Pow(2d, F));
double error = mantissa * Math.Pow(2d, F) - (double)intMant;
if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0))
{
SoftFloat.FPProcessException(FPException.Underflow, context);
}
bool overflowToInf;
bool roundUp;
switch (context.Fpcr.RoundingMode)
{
case FPRoundingMode.ToNearest:
roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u));
overflowToInf = true;
break;
case FPRoundingMode.TowardsPlusInfinity:
roundUp = (error != 0d && !sign);
overflowToInf = !sign;
break;
case FPRoundingMode.TowardsMinusInfinity:
roundUp = (error != 0d && sign);
overflowToInf = sign;
break;
case FPRoundingMode.TowardsZero:
roundUp = false;
overflowToInf = false;
break;
default:
throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.RoundingMode}\".");
}
if (roundUp)
{
intMant++;
if (intMant == 1ul << F)
{
biasedExp = 1u;
}
if (intMant == 1ul << (F + 1))
{
biasedExp++;
intMant >>= 1;
}
}
double result;
if (biasedExp >= (1u << E) - 1u)
{
result = overflowToInf ? SoftFloat64.FPInfinity(sign) : SoftFloat64.FPMaxNormal(sign);
SoftFloat.FPProcessException(FPException.Overflow, context);
error = 1d;
}
else
{
result = BitConverter.Int64BitsToDouble(
(long)((sign ? 1ul : 0ul) << 63 | (biasedExp & 0x7FFul) << 52 | (intMant & 0x000FFFFFFFFFFFFFul)));
}
if (error != 0d)
{
SoftFloat.FPProcessException(FPException.Inexact, context);
}
return result;
}
private static double FPConvertNaN(ushort valueBits)
{
return BitConverter.Int64BitsToDouble(
(long)(((ulong)valueBits & 0x8000ul) << 48 | 0x7FF8000000000000ul | ((ulong)valueBits & 0x01FFul) << 42));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class SoftFloat32_16
{
[UnmanagedCallersOnly]
public static ushort FPConvert(float value)
{
ExecutionContext context = NativeInterface.GetContext();
double real = value.FPUnpackCv(out FPType type, out bool sign, out uint valueBits, context);
bool altHp = (context.Fpcr & FPCR.Ahp) != 0;
ushort resultBits;
if (type is FPType.SNaN or FPType.QNaN)
{
if (altHp)
{
resultBits = SoftFloat16.FPZero(sign);
}
else if ((context.Fpcr & FPCR.Dn) != 0)
{
resultBits = SoftFloat16.FPDefaultNaN();
}
else
{
resultBits = FPConvertNaN(valueBits);
}
if (type == FPType.SNaN || altHp)
{
SoftFloat.FPProcessException(FPException.InvalidOp, context);
}
}
else if (type == FPType.Infinity)
{
if (altHp)
{
resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu);
SoftFloat.FPProcessException(FPException.InvalidOp, context);
}
else
{
resultBits = SoftFloat16.FPInfinity(sign);
}
}
else if (type == FPType.Zero)
{
resultBits = SoftFloat16.FPZero(sign);
}
else
{
resultBits = SoftFloat16.FPRoundCv(real, context);
}
return resultBits;
}
private static double FPUnpackCv(
this float value,
out FPType type,
out bool sign,
out uint valueBits,
ExecutionContext context)
{
valueBits = (uint)BitConverter.SingleToInt32Bits(value);
sign = (~valueBits & 0x80000000u) == 0u;
uint exp32 = (valueBits & 0x7F800000u) >> 23;
uint frac32 = valueBits & 0x007FFFFFu;
double real;
if (exp32 == 0u)
{
if (frac32 == 0u || (context.Fpcr & FPCR.Fz) != 0)
{
type = FPType.Zero;
real = 0d;
if (frac32 != 0u)
{
SoftFloat.FPProcessException(FPException.InputDenorm, context);
}
}
else
{
type = FPType.Nonzero; // Subnormal.
real = Math.Pow(2d, -126) * ((double)frac32 * Math.Pow(2d, -23));
}
}
else if (exp32 == 0xFFu)
{
if (frac32 == 0u)
{
type = FPType.Infinity;
real = Math.Pow(2d, 1000);
}
else
{
type = (~frac32 & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN;
real = 0d;
}
}
else
{
type = FPType.Nonzero; // Normal.
real = Math.Pow(2d, (int)exp32 - 127) * (1d + (double)frac32 * Math.Pow(2d, -23));
}
return sign ? -real : real;
}
private static ushort FPConvertNaN(uint valueBits)
{
return (ushort)((valueBits & 0x80000000u) >> 16 | 0x7E00u | (valueBits & 0x003FE000u) >> 13);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,127 @@
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class SoftFloat64_16
{
[UnmanagedCallersOnly]
public static ushort FPConvert(double value)
{
ExecutionContext context = NativeInterface.GetContext();
double real = value.FPUnpackCv(out FPType type, out bool sign, out ulong valueBits, context);
bool altHp = (context.Fpcr & FPCR.Ahp) != 0;
ushort resultBits;
if (type is FPType.SNaN or FPType.QNaN)
{
if (altHp)
{
resultBits = SoftFloat16.FPZero(sign);
}
else if ((context.Fpcr & FPCR.Dn) != 0)
{
resultBits = SoftFloat16.FPDefaultNaN();
}
else
{
resultBits = FPConvertNaN(valueBits);
}
if (type == FPType.SNaN || altHp)
{
SoftFloat.FPProcessException(FPException.InvalidOp, context);
}
}
else if (type == FPType.Infinity)
{
if (altHp)
{
resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu);
SoftFloat.FPProcessException(FPException.InvalidOp, context);
}
else
{
resultBits = SoftFloat16.FPInfinity(sign);
}
}
else if (type == FPType.Zero)
{
resultBits = SoftFloat16.FPZero(sign);
}
else
{
resultBits = SoftFloat16.FPRoundCv(real, context);
}
return resultBits;
}
private static double FPUnpackCv(
this double value,
out FPType type,
out bool sign,
out ulong valueBits,
ExecutionContext context)
{
valueBits = (ulong)BitConverter.DoubleToInt64Bits(value);
sign = (~valueBits & 0x8000000000000000ul) == 0u;
ulong exp64 = (valueBits & 0x7FF0000000000000ul) >> 52;
ulong frac64 = valueBits & 0x000FFFFFFFFFFFFFul;
double real;
if (exp64 == 0u)
{
if (frac64 == 0u || (context.Fpcr & FPCR.Fz) != 0)
{
type = FPType.Zero;
real = 0d;
if (frac64 != 0u)
{
SoftFloat.FPProcessException(FPException.InputDenorm, context);
}
}
else
{
type = FPType.Nonzero; // Subnormal.
real = Math.Pow(2d, -1022) * ((double)frac64 * Math.Pow(2d, -52));
}
}
else if (exp64 == 0x7FFul)
{
if (frac64 == 0u)
{
type = FPType.Infinity;
real = Math.Pow(2d, 1000000);
}
else
{
type = (~frac64 & 0x0008000000000000ul) == 0u ? FPType.QNaN : FPType.SNaN;
real = 0d;
}
}
else
{
type = FPType.Nonzero; // Normal.
real = Math.Pow(2d, (int)exp64 - 1023) * (1d + (double)frac64 * Math.Pow(2d, -52));
}
return sign ? -real : real;
}
private static ushort FPConvertNaN(ulong valueBits)
{
return (ushort)((valueBits & 0x8000000000000000ul) >> 48 | 0x7E00u |
(valueBits & 0x0007FC0000000000ul) >> 42);
}
}
}

View File

@@ -16,9 +16,9 @@ namespace ARMeilleure.IntermediateRepresentation
static class ComparisonExtensions
{
public static Comparison Invert(this Comparison comp)
extension(Comparison comparison)
{
return (Comparison)((int)comp ^ 1);
public Comparison Inverse => (Comparison)((int)comparison ^ 1);
}
}
}

View File

@@ -14,48 +14,38 @@ namespace ARMeilleure.IntermediateRepresentation
static class OperandTypeExtensions
{
public static bool IsInteger(this OperandType type)
extension(OperandType type)
{
return type is OperandType.I32 or
OperandType.I64;
}
public static RegisterType ToRegisterType(this OperandType type)
{
return type switch
public bool IsInteger => type is OperandType.I32 or OperandType.I64;
public RegisterType Register => type switch
{
OperandType.FP32 => RegisterType.Vector,
OperandType.FP64 => RegisterType.Vector,
OperandType.I32 => RegisterType.Integer,
OperandType.I64 => RegisterType.Integer,
OperandType.V128 => RegisterType.Vector,
_ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."),
_ => throw new InvalidOperationException($"Invalid operand type \"{type}\".")
};
}
public static int GetSizeInBytes(this OperandType type)
{
return type switch
public int ByteSize => type switch
{
OperandType.FP32 => 4,
OperandType.FP64 => 8,
OperandType.I32 => 4,
OperandType.I64 => 8,
OperandType.V128 => 16,
_ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."),
_ => throw new InvalidOperationException($"Invalid operand type \"{type}\".")
};
}
public static int GetSizeInBytesLog2(this OperandType type)
{
return type switch
public int ByteSizeLog2 => type switch
{
OperandType.FP32 => 2,
OperandType.FP64 => 3,
OperandType.I32 => 2,
OperandType.I64 => 3,
OperandType.V128 => 4,
_ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."),
_ => throw new InvalidOperationException($"Invalid operand type \"{type}\".")
};
}
}

View File

@@ -45,19 +45,12 @@ namespace ARMeilleure.Memory
public static class MemoryManagerTypeExtensions
{
public static bool IsHostMapped(this MemoryManagerType type)
extension(MemoryManagerType type)
{
return type is MemoryManagerType.HostMapped or MemoryManagerType.HostMappedUnsafe;
}
public static bool IsHostTracked(this MemoryManagerType type)
{
return type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe;
}
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
{
return type.IsHostMapped() || type.IsHostTracked();
public bool IsHostMapped => type is MemoryManagerType.HostMapped or MemoryManagerType.HostMappedUnsafe;
public bool IsHostTracked => type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe;
public bool IsHostMappedOrTracked => type.IsHostMapped || type.IsHostTracked;
}
}
}

View File

@@ -13,6 +13,7 @@ namespace ARMeilleure
public static bool AllowLcqInFunctionTable { get; set; } = true;
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
public static bool EnableDebugging { get; set; } = false;
public static bool EnableDeepCallRecursionProtection { get; set; } = true;
public static bool UseAdvSimdIfAvailable { get; set; } = true;
public static bool UseArm64AesIfAvailable { get; set; } = true;

View File

@@ -134,6 +134,11 @@ namespace ARMeilleure.State
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
internal void ResetCallDepth()
{
_nativeContext.ResetCallDepth();
}
internal void CheckInterrupt()
{
if (Interrupted)

View File

@@ -22,6 +22,7 @@ namespace ARMeilleure.State
public ulong ExclusiveValueHigh;
public int Running;
public long Tpidr2El0;
public int CallDepth;
/// <summary>
/// Precise PC value used for debugging.
@@ -199,6 +200,8 @@ namespace ARMeilleure.State
public bool GetRunning() => GetStorage().Running != 0;
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
public void ResetCallDepth() => GetStorage().CallDepth = 0;
public unsafe static int GetRegisterOffset(Register reg)
{
if (reg.Type == RegisterType.Integer)
@@ -284,6 +287,11 @@ namespace ARMeilleure.State
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DebugPrecisePc);
}
public static int GetCallDepthOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallDepth);
}
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
{
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);

View File

@@ -361,10 +361,7 @@ namespace ARMeilleure.Translation
IntervalTreeNode<TK, TV> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
if (tmp != null)
{
tmp.Parent = ParentOf(replacementNode);
}
tmp?.Parent = ParentOf(replacementNode);
if (ParentOf(replacementNode) == null)
{
@@ -582,10 +579,7 @@ namespace ARMeilleure.Translation
{
IntervalTreeNode<TK, TV> right = RightOf(node);
node.Right = LeftOf(right);
if (node.Right != null)
{
node.Right.Parent = node;
}
node.Right?.Parent = node;
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
right.Parent = nodeParent;
@@ -615,10 +609,7 @@ namespace ARMeilleure.Translation
{
IntervalTreeNode<TK, TV> left = LeftOf(node);
node.Left = RightOf(left);
if (node.Left != null)
{
node.Left.Parent = node;
}
node.Left?.Parent = node;
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
left.Parent = nodeParent;
@@ -667,10 +658,7 @@ namespace ARMeilleure.Translation
/// <param name="color">Color (Boolean)</param>
private static void SetColor(IntervalTreeNode<TK, TV> node, bool color)
{
if (node != null)
{
node.Color = color;
}
node?.Color = color;
}
/// <summary>

View File

@@ -33,7 +33,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 7009; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 7010; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View File

@@ -186,6 +186,7 @@ namespace ARMeilleure.Translation
Statistics.StartTimer();
context.ResetCallDepth();
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
Statistics.StopTimer(address);
@@ -260,6 +261,7 @@ namespace ARMeilleure.Translation
Logger.StartPass(PassName.Translation);
InstEmitFlowHelper.EmitCallDepthCheckAndIncrement(context, Const(address));
EmitSynchronization(context);
if (blocks[0].Address != address)
@@ -412,7 +414,7 @@ namespace ARMeilleure.Translation
{
context.SyncQcFlag();
if (block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address)
if (block.Branch is { Exit: false } && block.Branch.Address <= block.Address)
{
EmitSynchronization(context);
}
@@ -429,14 +431,14 @@ namespace ARMeilleure.Translation
{
lblPredicateSkip = Label();
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Invert());
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Inverse);
}
if (opCode is OpCode32 op && op.Cond < Condition.Al)
if (opCode is OpCode32 { Cond: < Condition.Al } op)
{
lblPredicateSkip = Label();
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Invert());
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Inverse);
}
if (opCode.Instruction.Emitter != null)

View File

@@ -262,10 +262,18 @@ namespace ARMeilleure.Translation
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
Operand callDepthAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
EmitSyncFpContext(context, nativeContext, true);
context.MarkLabel(beginLbl);
if (Optimizations.EnableDeepCallRecursionProtection)
{
// Reset the call depth counter, since this is our first guest function call.
context.Store(callDepthAddress, Const(0));
}
context.Store(dispatchAddress, guestAddress);
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
context.BranchIfFalse(endLbl, guestAddress);

View File

@@ -0,0 +1,16 @@
namespace Ryujinx.Audio.Backends.Apple
{
class AppleAudioBuffer
{
public readonly ulong DriverIdentifier;
public readonly ulong SampleCount;
public ulong SamplePlayed;
public AppleAudioBuffer(ulong driverIdentifier, ulong sampleCount)
{
DriverIdentifier = driverIdentifier;
SampleCount = sampleCount;
SamplePlayed = 0;
}
}
}

View File

@@ -0,0 +1,196 @@
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.Versioning;
using Ryujinx.Audio.Backends.Apple.Native;
using static Ryujinx.Audio.Backends.Apple.Native.AudioToolbox;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace Ryujinx.Audio.Backends.Apple
{
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
public sealed class AppleHardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent;
private readonly ConcurrentDictionary<AppleHardwareDeviceSession, byte> _sessions;
private readonly bool _supportSurroundConfiguration;
public float Volume { get; set; }
public AppleHardwareDeviceDriver()
{
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
_sessions = new ConcurrentDictionary<AppleHardwareDeviceSession, byte>();
_supportSurroundConfiguration = TestSurroundSupport();
Volume = 1f;
}
private bool TestSurroundSupport()
{
try
{
AudioStreamBasicDescription format =
GetAudioFormat(SampleFormat.PcmFloat, Constants.TargetSampleRate, 6);
int result = AudioQueueNewOutput(
ref format,
nint.Zero,
nint.Zero,
nint.Zero,
nint.Zero,
0,
out nint testQueue);
if (result == 0)
{
AudioChannelLayout layout = new AudioChannelLayout
{
AudioChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A,
AudioChannelBitmap = 0,
NumberChannelDescriptions = 0
};
int layoutResult = AudioQueueSetProperty(
testQueue,
kAudioQueueProperty_ChannelLayout,
ref layout,
(uint)Marshal.SizeOf<AudioChannelLayout>());
if (layoutResult == 0)
{
AudioQueueDispose(testQueue, true);
return true;
}
AudioQueueDispose(testQueue, true);
}
return false;
}
catch
{
return false;
}
}
public static bool IsSupported => OperatingSystem.IsMacOSVersionAtLeast(10, 5);
public ManualResetEvent GetUpdateRequiredEvent()
=> _updateRequiredEvent;
public ManualResetEvent GetPauseEvent()
=> _pauseEvent;
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager,
SampleFormat sampleFormat, uint sampleRate, uint channelCount)
{
if (channelCount == 0)
{
channelCount = 2;
}
if (sampleRate == 0)
{
sampleRate = Constants.TargetSampleRate;
}
if (direction != Direction.Output)
{
throw new NotImplementedException("Input direction is currently not implemented on Apple backend!");
}
AppleHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
_sessions.TryAdd(session, 0);
return session;
}
internal bool Unregister(AppleHardwareDeviceSession session)
=> _sessions.TryRemove(session, out _);
internal static AudioStreamBasicDescription GetAudioFormat(SampleFormat sampleFormat, uint sampleRate,
uint channelCount)
{
uint formatFlags;
uint bitsPerChannel;
switch (sampleFormat)
{
case SampleFormat.PcmInt8:
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
bitsPerChannel = 8;
break;
case SampleFormat.PcmInt16:
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
bitsPerChannel = 16;
break;
case SampleFormat.PcmInt32:
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
bitsPerChannel = 32;
break;
case SampleFormat.PcmFloat:
formatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
bitsPerChannel = 32;
break;
default:
throw new ArgumentException($"Unsupported sample format {sampleFormat}");
}
uint bytesPerFrame = (bitsPerChannel / 8) * channelCount;
return new AudioStreamBasicDescription
{
SampleRate = sampleRate,
FormatID = kAudioFormatLinearPCM,
FormatFlags = formatFlags,
BytesPerPacket = bytesPerFrame,
FramesPerPacket = 1,
BytesPerFrame = bytesPerFrame,
ChannelsPerFrame = channelCount,
BitsPerChannel = bitsPerChannel,
Reserved = 0
};
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
foreach (AppleHardwareDeviceSession session in _sessions.Keys)
{
session.Dispose();
}
_pauseEvent.Dispose();
}
}
public bool SupportsDirection(Direction direction)
=> direction != Direction.Input;
public bool SupportsSampleRate(uint sampleRate) => true;
public bool SupportsSampleFormat(SampleFormat sampleFormat)
=> sampleFormat != SampleFormat.PcmInt24;
public bool SupportsChannelCount(uint channelCount)
=> channelCount != 6 || _supportSurroundConfiguration;
}
}

View File

@@ -0,0 +1,285 @@
using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Common;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.Versioning;
using static Ryujinx.Audio.Backends.Apple.Native.AudioToolbox;
namespace Ryujinx.Audio.Backends.Apple
{
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
class AppleHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private const int NumBuffers = 3;
private readonly AppleHardwareDeviceDriver _driver;
private readonly ConcurrentQueue<AppleAudioBuffer> _queuedBuffers = new();
private readonly DynamicRingBuffer _ringBuffer = new();
private readonly ManualResetEvent _updateRequiredEvent;
private readonly AudioQueueOutputCallback _callbackDelegate;
private readonly GCHandle _gcHandle;
private nint _audioQueue;
private readonly nint[] _audioQueueBuffers = new nint[NumBuffers];
private readonly int[] _bufferBytesFilled = new int[NumBuffers];
private readonly int _bytesPerFrame;
private ulong _playedSampleCount;
private bool _started;
private float _volume = 1f;
private readonly object _lock = new();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void AudioQueueOutputCallback(
nint userData,
nint audioQueue,
nint buffer);
public AppleHardwareDeviceSession(
AppleHardwareDeviceDriver driver,
IVirtualMemoryManager memoryManager,
SampleFormat requestedSampleFormat,
uint requestedSampleRate,
uint requestedChannelCount)
: base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
{
_driver = driver;
_updateRequiredEvent = driver.GetUpdateRequiredEvent();
_callbackDelegate = OutputCallback;
_bytesPerFrame = BackendHelper.GetSampleSize(requestedSampleFormat) * (int)requestedChannelCount;
_gcHandle = GCHandle.Alloc(this, GCHandleType.Normal);
SetupAudioQueue();
}
private void SetupAudioQueue()
{
lock (_lock)
{
AudioStreamBasicDescription format = AppleHardwareDeviceDriver.GetAudioFormat(
RequestedSampleFormat,
RequestedSampleRate,
RequestedChannelCount);
nint callbackPtr = Marshal.GetFunctionPointerForDelegate(_callbackDelegate);
nint userData = GCHandle.ToIntPtr(_gcHandle);
int result = AudioQueueNewOutput(
ref format,
callbackPtr,
userData,
nint.Zero,
nint.Zero,
0,
out _audioQueue);
if (result != 0)
{
throw new InvalidOperationException($"AudioQueueNewOutput failed: {result}");
}
uint framesPerBuffer = RequestedSampleRate / 100;
uint bufferSize = framesPerBuffer * (uint)_bytesPerFrame;
for (int i = 0; i < NumBuffers; i++)
{
AudioQueueAllocateBuffer(_audioQueue, bufferSize, out _audioQueueBuffers[i]);
_bufferBytesFilled[i] = 0;
PrimeBuffer(_audioQueueBuffers[i], i);
}
}
}
private unsafe void PrimeBuffer(nint bufferPtr, int bufferIndex)
{
AudioQueueBuffer* buffer = (AudioQueueBuffer*)bufferPtr;
int capacityBytes = (int)buffer->AudioDataBytesCapacity;
int framesPerBuffer = capacityBytes / _bytesPerFrame;
int availableFrames = _ringBuffer.Length / _bytesPerFrame;
int framesToRead = Math.Min(availableFrames, framesPerBuffer);
int bytesToRead = framesToRead * _bytesPerFrame;
Span<byte> dst = new((void*)buffer->AudioData, capacityBytes);
dst.Clear();
if (bytesToRead > 0)
{
Span<byte> audio = dst.Slice(0, bytesToRead);
_ringBuffer.Read(audio, 0, bytesToRead);
ApplyVolume(buffer->AudioData, bytesToRead);
}
buffer->AudioDataByteSize = (uint)capacityBytes;
_bufferBytesFilled[bufferIndex] = bytesToRead;
AudioQueueEnqueueBuffer(_audioQueue, bufferPtr, 0, nint.Zero);
}
private void OutputCallback(nint userData, nint audioQueue, nint bufferPtr)
{
if (!_started || bufferPtr == nint.Zero)
return;
int bufferIndex = Array.IndexOf(_audioQueueBuffers, bufferPtr);
if (bufferIndex < 0)
return;
int bytesPlayed = _bufferBytesFilled[bufferIndex];
if (bytesPlayed > 0)
{
ProcessPlayedSamples(bytesPlayed);
}
PrimeBuffer(bufferPtr, bufferIndex);
}
private void ProcessPlayedSamples(int bytesPlayed)
{
ulong samplesPlayed = GetSampleCount(bytesPlayed);
ulong remaining = samplesPlayed;
bool needUpdate = false;
while (remaining > 0 && _queuedBuffers.TryPeek(out AppleAudioBuffer buffer))
{
ulong needed = buffer.SampleCount - Interlocked.Read(ref buffer.SamplePlayed);
ulong take = Math.Min(needed, remaining);
ulong played = Interlocked.Add(ref buffer.SamplePlayed, take);
remaining -= take;
if (played == buffer.SampleCount)
{
_queuedBuffers.TryDequeue(out _);
needUpdate = true;
}
Interlocked.Add(ref _playedSampleCount, take);
}
if (needUpdate)
{
_updateRequiredEvent.Set();
}
}
private unsafe void ApplyVolume(nint dataPtr, int byteSize)
{
float volume = Math.Clamp(_volume * _driver.Volume, 0f, 1f);
if (volume >= 0.999f)
return;
int sampleCount = byteSize / BackendHelper.GetSampleSize(RequestedSampleFormat);
switch (RequestedSampleFormat)
{
case SampleFormat.PcmInt16:
short* s16 = (short*)dataPtr;
for (int i = 0; i < sampleCount; i++)
s16[i] = (short)(s16[i] * volume);
break;
case SampleFormat.PcmFloat:
float* f32 = (float*)dataPtr;
for (int i = 0; i < sampleCount; i++)
f32[i] *= volume;
break;
case SampleFormat.PcmInt32:
int* s32 = (int*)dataPtr;
for (int i = 0; i < sampleCount; i++)
s32[i] = (int)(s32[i] * volume);
break;
case SampleFormat.PcmInt8:
sbyte* s8 = (sbyte*)dataPtr;
for (int i = 0; i < sampleCount; i++)
s8[i] = (sbyte)(s8[i] * volume);
break;
}
}
public override void QueueBuffer(AudioBuffer buffer)
{
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
_queuedBuffers.Enqueue(new AppleAudioBuffer(buffer.DataPointer, GetSampleCount(buffer)));
}
public override void Start()
{
lock (_lock)
{
if (_started)
return;
_started = true;
AudioQueueStart(_audioQueue, nint.Zero);
}
}
public override void Stop()
{
lock (_lock)
{
if (!_started)
return;
_started = false;
AudioQueuePause(_audioQueue);
}
}
public override ulong GetPlayedSampleCount()
=> Interlocked.Read(ref _playedSampleCount);
public override float GetVolume() => _volume;
public override void SetVolume(float volume) => _volume = volume;
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
{
if (!_queuedBuffers.TryPeek(out AppleAudioBuffer driverBuffer))
return true;
return driverBuffer.DriverIdentifier != buffer.DataPointer;
}
public override void PrepareToClose() { }
public override void UnregisterBuffer(AudioBuffer buffer) { }
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Stop();
if (_audioQueue != nint.Zero)
{
AudioQueueStop(_audioQueue, true);
AudioQueueDispose(_audioQueue, true);
_audioQueue = nint.Zero;
}
if (_gcHandle.IsAllocated)
{
_gcHandle.Free();
}
}
}
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -0,0 +1,102 @@
using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming
namespace Ryujinx.Audio.Backends.Apple.Native
{
public static partial class AudioToolbox
{
[StructLayout(LayoutKind.Sequential)]
internal struct AudioStreamBasicDescription
{
public double SampleRate;
public uint FormatID;
public uint FormatFlags;
public uint BytesPerPacket;
public uint FramesPerPacket;
public uint BytesPerFrame;
public uint ChannelsPerFrame;
public uint BitsPerChannel;
public uint Reserved;
}
[StructLayout(LayoutKind.Sequential)]
internal struct AudioChannelLayout
{
public uint AudioChannelLayoutTag;
public uint AudioChannelBitmap;
public uint NumberChannelDescriptions;
}
internal const uint kAudioFormatLinearPCM = 0x6C70636D;
internal const uint kAudioQueueProperty_ChannelLayout = 0x6171636c;
internal const uint kAudioChannelLayoutTag_MPEG_5_1_A = 0x650006;
internal const uint kAudioFormatFlagIsFloat = (1 << 0);
internal const uint kAudioFormatFlagIsSignedInteger = (1 << 2);
internal const uint kAudioFormatFlagIsPacked = (1 << 3);
internal const uint kAudioFormatFlagIsBigEndian = (1 << 1);
internal const uint kAudioFormatFlagIsAlignedHigh = (1 << 4);
internal const uint kAudioFormatFlagIsNonInterleaved = (1 << 5);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueNewOutput(
ref AudioStreamBasicDescription format,
nint callback,
nint userData,
nint callbackRunLoop,
nint callbackRunLoopMode,
uint flags,
out nint audioQueue);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueSetProperty(
nint audioQueue,
uint propertyID,
ref AudioChannelLayout layout,
uint layoutSize);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueDispose(nint audioQueue, [MarshalAs(UnmanagedType.I1)] bool immediate);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueAllocateBuffer(
nint audioQueue,
uint bufferByteSize,
out nint buffer);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueStart(nint audioQueue, nint startTime);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueuePause(nint audioQueue);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueStop(nint audioQueue, [MarshalAs(UnmanagedType.I1)] bool immediate);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueSetParameter(
nint audioQueue,
uint parameterID,
float value);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueEnqueueBuffer(
nint audioQueue,
nint buffer,
uint numPacketDescs,
nint packetDescs);
[StructLayout(LayoutKind.Sequential)]
internal struct AudioQueueBuffer
{
public uint AudioDataBytesCapacity;
public nint AudioData;
public uint AudioDataByteSize;
public nint UserData;
public uint PacketDescriptionCapacity;
public nint PacketDescriptions;
public uint PacketDescriptionCount;
}
internal const uint kAudioQueueParam_Volume = 1;
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -10,7 +10,8 @@ using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace Ryujinx.Audio.Backends.OpenAL
{
public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
// ReSharper disable once InconsistentNaming
public sealed class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly ALDevice _device;
private readonly ALContext _context;
@@ -148,7 +149,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
Dispose(true);
}
protected virtual void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (disposing)
{

View File

@@ -9,7 +9,8 @@ using System.Threading;
namespace Ryujinx.Audio.Backends.OpenAL
{
class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase
// ReSharper disable once InconsistentNaming
sealed class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private readonly OpenALHardwareDeviceDriver _driver;
private readonly int _sourceId;
@@ -190,7 +191,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
}
}
protected virtual void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (disposing && _driver.Unregister(this))
{

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Backends.SDL3
using unsafe SDL_AudioStreamCallbackPointer = delegate* unmanaged[Cdecl]<nint, SDL_AudioStream*, int, int, void>;
public class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
public sealed class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent;
@@ -135,7 +135,6 @@ namespace Ryujinx.Audio.Backends.SDL3
// From SDL 3 and on, SDL requires us to set this as a hint
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, $"{sampleCount}");
SDL_AudioStream* device = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &got, pCallback, 0);
Console.WriteLine(got.freq);
if (device == null)
{
@@ -163,7 +162,7 @@ namespace Ryujinx.Audio.Backends.SDL3
Dispose(true);
}
protected virtual void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (disposing)
{

View File

@@ -12,10 +12,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Backends.SDL3
{
unsafe class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
sealed unsafe class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private readonly SDL3HardwareDeviceDriver _driver;
private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers;
@@ -226,7 +223,7 @@ namespace Ryujinx.Audio.Backends.SDL3
return driverBuffer.DriverIdentifier != buffer.DataPointer;
}
protected virtual void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (disposing && _driver.Unregister(this))
{

View File

@@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
unsafe
{
int* frameCountPtr = &nativeFrameCount;
IntPtr* arenasPtr = &arenas;
nint* arenasPtr = &arenas;
CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr));
frameCount = *frameCountPtr;

View File

@@ -10,7 +10,7 @@ using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace Ryujinx.Audio.Backends.SoundIo
{
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
public sealed class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly SoundIoContext _audioContext;
private readonly SoundIoDeviceContext _audioDevice;
@@ -227,7 +227,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
}
}
protected virtual void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (disposing)
{

View File

@@ -11,7 +11,7 @@ using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
namespace Ryujinx.Audio.Backends.SoundIo
{
class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase
sealed class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private readonly SoundIoHardwareDeviceDriver _driver;
private readonly ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers;
@@ -428,7 +428,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
}
}
protected virtual void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (disposing && _driver.Unregister(this))
{

View File

@@ -58,16 +58,16 @@ namespace Ryujinx.Audio.Backends.CompatLayer
switch (realSampleFormat)
{
case SampleFormat.PcmInt8:
PcmHelper.ConvertSampleToPcm8(MemoryMarshal.Cast<byte, sbyte>(convertedSamples), samples);
PcmHelper.ConvertSampleToPcm8(MemoryMarshal.Cast<byte, sbyte>(new Span<byte>(convertedSamples)), samples);
break;
case SampleFormat.PcmInt24:
PcmHelper.ConvertSampleToPcm24(convertedSamples, samples);
break;
case SampleFormat.PcmInt32:
PcmHelper.ConvertSampleToPcm32(MemoryMarshal.Cast<byte, int>(convertedSamples), samples);
PcmHelper.ConvertSampleToPcm32(MemoryMarshal.Cast<byte, int>(new Span<byte>(convertedSamples)), samples);
break;
case SampleFormat.PcmFloat:
PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast<byte, float>(convertedSamples), samples);
PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast<byte, float>(new Span<byte>(convertedSamples)), samples);
break;
default:
throw new NotImplementedException($"Sample format conversion from {_userSampleFormat} to {realSampleFormat} not implemented.");

View File

@@ -27,7 +27,7 @@ namespace Ryujinx.Audio.Integration
public void AppendBuffer(ReadOnlySpan<short> data, uint channelCount)
{
data.CopyTo(MemoryMarshal.Cast<byte, short>(_buffer));
data.CopyTo(MemoryMarshal.Cast<byte, short>(new Span<byte>(_buffer)));
_session.QueueBuffer(new AudioBuffer
{

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Common
public uint MixesSize;
public uint SinksSize;
public uint PerformanceBufferSize;
public uint Unknown24;
public uint SplitterSize;
public uint RenderInfoSize;
#pragma warning disable IDE0051, CS0169 // Remove unused field

View File

@@ -92,6 +92,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
}
short[] outputBuffer = ArrayPool<short>.Shared.Rent((int)inputCount * SampleCount);
Array.Fill(outputBuffer, (short)0, 0, (int)inputCount * SampleCount);
for (int i = 0; i < bufferCount; i++)
{

View File

@@ -19,6 +19,11 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// The output channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
/// </summary>
public Array6<byte> Output;
/// <summary>
/// Reserved/unused.
/// </summary>
private readonly uint _padding;
/// <summary>
/// Biquad filter numerator (b0, b1, b2).

View File

@@ -433,8 +433,12 @@ namespace Ryujinx.Audio.Renderer.Server
public ResultCode UpdateSplitter(SplitterContext context)
{
long initialInputConsumed = _inputReader.Consumed;
if (context.Update(ref _inputReader))
{
_inputReader.SetConsumed(initialInputConsumed + _inputHeader.SplitterSize);
return ResultCode.Success;
}

View File

@@ -11,9 +11,7 @@ namespace Ryujinx.BuildValidationTasks
{
static readonly JsonSerializerOptions _jsonOptions = new()
{
WriteIndented = true,
NewLine = "\n",
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
WriteIndented = true, NewLine = "\n", Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
public LocalesValidationTask() { }
@@ -22,77 +20,116 @@ namespace Ryujinx.BuildValidationTasks
{
Console.WriteLine("Running Locale Validation Task...");
string path = projectPath + "assets/locales.json";
bool encounteredIssue = false;
string langPath = projectPath + "assets/Languages.json";
string data;
using (StreamReader sr = new(path))
using (StreamReader sr = new(langPath))
{
data = sr.ReadToEnd();
}
LocalesJson json;
if (isGitRunner && data.Contains("\r\n"))
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, rebuild locally to fix...");
throw new FormatException("Languages.json is using CRLF line endings! It should be using LF line endings, rebuild locally to fix...");
LanguagesJson langJson;
try
{
json = JsonSerializer.Deserialize<LocalesJson>(data);
langJson = JsonSerializer.Deserialize<LanguagesJson>(data);
}
catch (JsonException e)
{
throw new JsonException(e.Message); //shorter and easier stacktrace
}
bool encounteredIssue = false;
for (int i = 0; i < json.Locales.Count; i++)
foreach ((string code, string lang) in langJson.Languages)
{
LocalesEntry locale = json.Locales[i];
foreach (string langCode in json.Languages.Where(lang => !locale.Translations.ContainsKey(lang)))
if (string.IsNullOrEmpty(lang))
{
encounteredIssue = true;
if (!isGitRunner)
{
locale.Translations.Add(langCode, string.Empty);
Console.WriteLine($"Added '{langCode}' to Locale '{locale.ID}'");
}
else
{
Console.WriteLine($"Missing '{langCode}' in Locale '{locale.ID}'!");
}
throw new JsonException($"{code} language name missing!");
}
foreach (string langCode in json.Languages.Where(lang => locale.Translations.ContainsKey(lang) && lang != "en_US" && locale.Translations[lang] == locale.Translations["en_US"]))
{
encounteredIssue = true;
if (!isGitRunner)
{
locale.Translations[langCode] = string.Empty;
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
}
else
{
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
}
}
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
json.Locales[i] = locale;
}
if (isGitRunner && encounteredIssue)
throw new JsonException("1 or more locales are invalid! Rebuild locally to fix...");
string folderPath = projectPath + "assets/Locales/";
string jsonString = JsonSerializer.Serialize(json, _jsonOptions);
string[] paths = Directory.GetFiles(folderPath, "*.json", SearchOption.AllDirectories);
using (StreamWriter sw = new(path))
foreach (string path in paths)
{
sw.Write(jsonString);
using (StreamReader sr = new(path))
{
data = sr.ReadToEnd();
}
if (isGitRunner && data.Contains("\r\n"))
throw new FormatException($"{Path.GetFileName(path)} is using CRLF line endings! It should be using LF line endings, rebuild locally to fix...");
LocalesJson json;
try
{
json = JsonSerializer.Deserialize<LocalesJson>(data);
}
catch (JsonException e)
{
throw new JsonException(e.Message); //shorter and easier stacktrace
}
for (int i = 0; i < json.Locales.Count; i++)
{
LocalesEntry locale = json.Locales[i];
foreach (string langCode in
langJson.Languages.Keys.Where(lang => !locale.Translations.ContainsKey(lang)))
{
encounteredIssue = true;
if (!isGitRunner)
{
locale.Translations.Add(langCode, string.Empty);
Console.WriteLine($"Added '{langCode}' to Locale '{locale.ID}'");
}
else
{
Console.WriteLine($"Missing '{langCode}' in Locale '{locale.ID}'!");
}
}
foreach (string langCode in langJson.Languages.Keys.Where(lang =>
locale.Translations.ContainsKey(lang) && lang != "en_US" &&
locale.Translations[lang] == locale.Translations["en_US"]))
{
encounteredIssue = true;
if (!isGitRunner)
{
locale.Translations[langCode] = string.Empty;
Console.WriteLine(
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
}
else
{
Console.WriteLine(
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
}
}
locale.Translations = locale.Translations.OrderBy(pair => pair.Key)
.ToDictionary(pair => pair.Key, pair => pair.Value);
json.Locales[i] = locale;
}
if (isGitRunner && encounteredIssue)
throw new JsonException("1 or more locales are invalid! Rebuild locally to fix...");
string jsonString = JsonSerializer.Serialize(json, _jsonOptions);
using (StreamWriter sw = new(path))
{
sw.Write(jsonString);
}
}
Console.WriteLine("Finished Locale Validation Task!");
@@ -100,10 +137,13 @@ namespace Ryujinx.BuildValidationTasks
return true;
}
struct LanguagesJson
{
public Dictionary<string, string> Languages { get; set; }
}
struct LocalesJson
{
public Dictionary<string, string> Info { get; set; }
public List<string> Languages { get; set; }
public List<LocalesEntry> Locales { get; set; }
}

View File

@@ -386,10 +386,7 @@ namespace Ryujinx.Common.Collections
IntervalTreeNode<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
if (tmp != null)
{
tmp.Parent = ParentOf(replacementNode);
}
tmp?.Parent = ParentOf(replacementNode);
if (ParentOf(replacementNode) == null)
{

View File

@@ -235,10 +235,7 @@ namespace Ryujinx.Common.Collections
parent = ParentOf(element);
color = ColorOf(element);
if (child != null)
{
child.Parent = parent;
}
child?.Parent = parent;
if (parent == null)
{
@@ -258,8 +255,7 @@ namespace Ryujinx.Common.Collections
element.Right = old.Right;
element.Parent = old.Parent;
element.Predecessor = old.Predecessor;
if (element.Predecessor != null)
element.Predecessor.Successor = element;
element.Predecessor?.Successor = element;
if (ParentOf(old) == null)
{
@@ -292,10 +288,7 @@ namespace Ryujinx.Common.Collections
parent = ParentOf(nodeToDelete);
color = ColorOf(nodeToDelete);
if (child != null)
{
child.Parent = parent;
}
child?.Parent = parent;
if (parent == null)
{
@@ -314,11 +307,9 @@ namespace Ryujinx.Common.Collections
{
RestoreBalanceAfterRemoval(child);
}
if (old.Successor != null)
old.Successor.Predecessor = old.Predecessor;
if (old.Predecessor != null)
old.Predecessor.Successor = old.Successor;
old.Successor?.Predecessor = old.Predecessor;
old.Predecessor?.Successor = old.Successor;
return old;
}

View File

@@ -250,10 +250,7 @@ namespace Ryujinx.Common.Collections
{
T right = RightOf(node);
node.Right = LeftOf(right);
if (node.Right != null)
{
node.Right.Parent = node;
}
node.Right?.Parent = node;
T nodeParent = ParentOf(node);
right.Parent = nodeParent;
@@ -281,10 +278,7 @@ namespace Ryujinx.Common.Collections
{
T left = LeftOf(node);
node.Left = RightOf(left);
if (node.Left != null)
{
node.Left.Parent = node;
}
node.Left?.Parent = node;
T nodeParent = ParentOf(node);
left.Parent = nodeParent;
@@ -329,10 +323,7 @@ namespace Ryujinx.Common.Collections
/// <param name="color">Color (Boolean)</param>
protected static void SetColor(T node, bool color)
{
if (node != null)
{
node.Color = color;
}
node?.Color = color;
}
/// <summary>

View File

@@ -328,10 +328,7 @@ namespace Ryujinx.Common.Collections
Node<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
if (tmp != null)
{
tmp.Parent = ParentOf(replacementNode);
}
tmp?.Parent = ParentOf(replacementNode);
if (ParentOf(replacementNode) == null)
{

View File

@@ -1,9 +1,8 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{
[JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))]
[JsonConverter(typeof(JsonStringEnumConverter<AntiAliasing>))]
public enum AntiAliasing
{
None,

View File

@@ -1,9 +1,8 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
[JsonConverter(typeof(JsonStringEnumConverter<AspectRatio>))]
public enum AspectRatio
{
Fixed4x3,

View File

@@ -1,9 +1,8 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
[JsonConverter(typeof(JsonStringEnumConverter<BackendThreading>))]
public enum BackendThreading
{
Auto,

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