mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-06-28 11:26:24 +02:00
Compare commits
No commits in common. "XC-ios-ht" and "1.4.0" have entirely different histories.
1358 changed files with 25476 additions and 64642 deletions
|
@ -17,8 +17,8 @@ tab_width = 4
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Markdown, JSON, YAML, props and csproj files
|
# JSON files
|
||||||
[*.{md,json,yml,props,csproj}]
|
[*.json]
|
||||||
|
|
||||||
# Indentation and spacing
|
# Indentation and spacing
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
@ -259,14 +259,14 @@ dotnet_diagnostic.CA1861.severity = none
|
||||||
# Disable "Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'"
|
# Disable "Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'"
|
||||||
dotnet_diagnostic.CA1862.severity = none
|
dotnet_diagnostic.CA1862.severity = none
|
||||||
|
|
||||||
[src/Ryujinx/UI/ViewModels/**.cs]
|
|
||||||
# Disable "mark members as static" rule for ViewModels
|
|
||||||
dotnet_diagnostic.CA1822.severity = none
|
|
||||||
|
|
||||||
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
||||||
# Disable "mark members as static" rule for services
|
# Disable "mark members as static" rule for services
|
||||||
dotnet_diagnostic.CA1822.severity = none
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
|
|
||||||
|
[src/Ryujinx.Ava/UI/ViewModels/**.cs]
|
||||||
|
# Disable "mark members as static" rule for ViewModels
|
||||||
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
|
|
||||||
[src/Ryujinx.Tests/Cpu/*.cs]
|
[src/Ryujinx.Tests/Cpu/*.cs]
|
||||||
# Disable naming rules for CPU tests
|
# Disable naming rules for CPU tests
|
||||||
dotnet_diagnostic.IDE1006.severity = none
|
dotnet_diagnostic.IDE1006.severity = none
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
name: Update apps.json on new release
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update:
|
|
||||||
runs-on: debian-trixie
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get install -y jq
|
|
||||||
|
|
||||||
- name: Extract release data
|
|
||||||
id: release
|
|
||||||
run: |
|
|
||||||
echo "VERSION=${GITEA_REF_NAME}" >> $GITHUB_OUTPUT
|
|
||||||
echo "DESCRIPTION=$(echo '${GITEA_EVENT_RELEASE_BODY}' | jq -Rs .)" >> $GITHUB_OUTPUT
|
|
||||||
echo "DATE=$(date '+%Y-%m-%d')" >> $GITHUB_OUTPUT
|
|
||||||
IPA_URL=$(echo '${GITEA_EVENT_RELEASE_ASSETS}' | jq -r '.[0].browser_download_url')
|
|
||||||
echo "DOWNLOAD_URL=$IPA_URL" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Update apps.json
|
|
||||||
run: |
|
|
||||||
jq --arg version "${{ steps.release.outputs.VERSION }}" \
|
|
||||||
--arg buildVersion "1" \
|
|
||||||
--arg date "${{ steps.release.outputs.DATE }}" \
|
|
||||||
--arg localizedDescription "${{ steps.release.outputs.DESCRIPTION }}" \
|
|
||||||
--arg downloadURL "${{ steps.release.outputs.DOWNLOAD_URL }}" \
|
|
||||||
'.apps[0].versions |= [{"version": $version, "buildVersion": $buildVersion, "date": $date, "localizedDescription": $localizedDescription, "downloadURL": $downloadURL, "minOSVersion": "15.0"}]' \
|
|
||||||
apps.json > tmp.json && mv tmp.json apps.json
|
|
||||||
|
|
||||||
- name: Commit and push
|
|
||||||
run: |
|
|
||||||
git config user.name "gitea-actions"
|
|
||||||
git config user.email "gitea-actions@localhost"
|
|
||||||
git add apps.json
|
|
||||||
git commit -m "Update apps.json for release ${{ steps.release.outputs.VERSION }}"
|
|
||||||
git push
|
|
||||||
env:
|
|
||||||
GIT_AUTHOR_NAME: gitea-actions
|
|
||||||
GIT_AUTHOR_EMAIL: gitea-actions@localhost
|
|
||||||
GIT_COMMITTER_NAME: gitea-actions
|
|
||||||
GIT_COMMITTER_EMAIL: gitea-actions@localhost
|
|
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -23,7 +23,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Log file
|
label: Log file
|
||||||
description: A log file will help our developers to better diagnose and fix the issue.
|
description: A log file will help our developers to better diagnose and fix the issue.
|
||||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste).
|
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
|
|
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -1,7 +1,6 @@
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Suggest a new feature for Ryujinx.
|
description: Suggest a new feature for Ryujinx.
|
||||||
title: "[Feature Request]"
|
title: "[Feature Request]"
|
||||||
labels: enhancement
|
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: overview
|
id: overview
|
||||||
|
|
22
.github/dependabot.yml
vendored
22
.github/dependabot.yml
vendored
|
@ -7,34 +7,18 @@ updates:
|
||||||
labels:
|
labels:
|
||||||
- "infra"
|
- "infra"
|
||||||
reviewers:
|
reviewers:
|
||||||
- TSRBerry
|
- marysaka
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "ci"
|
prefix: "ci"
|
||||||
|
|
||||||
- package-ecosystem: nuget
|
- package-ecosystem: nuget
|
||||||
directory: /
|
directory: /
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 5
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
labels:
|
labels:
|
||||||
- "infra"
|
- "infra"
|
||||||
reviewers:
|
reviewers:
|
||||||
- TSRBerry
|
- marysaka
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: nuget
|
prefix: nuget
|
||||||
groups:
|
|
||||||
Avalonia:
|
|
||||||
patterns:
|
|
||||||
- "*Avalonia*"
|
|
||||||
Silk.NET:
|
|
||||||
patterns:
|
|
||||||
- "Silk.NET*"
|
|
||||||
OpenTK:
|
|
||||||
patterns:
|
|
||||||
- "OpenTK*"
|
|
||||||
SixLabors:
|
|
||||||
patterns:
|
|
||||||
- "SixLabors*"
|
|
||||||
NUnit:
|
|
||||||
patterns:
|
|
||||||
- "NUnit*"
|
|
||||||
|
|
2
.github/labeler.yml
vendored
2
.github/labeler.yml
vendored
|
@ -20,7 +20,7 @@ gpu:
|
||||||
|
|
||||||
gui:
|
gui:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**', 'src/Ryujinx.Gtk3/**']
|
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.Ui.Common/**', 'src/Ryujinx.Ui.LocaleGenerator/**', 'src/Ryujinx.Ava/**']
|
||||||
|
|
||||||
horizon:
|
horizon:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|
7
.github/reviewers.yml
vendored
7
.github/reviewers.yml
vendored
|
@ -1,24 +1,31 @@
|
||||||
|
audio:
|
||||||
|
- marysaka
|
||||||
|
|
||||||
cpu:
|
cpu:
|
||||||
- gdkchan
|
- gdkchan
|
||||||
- riperiperi
|
- riperiperi
|
||||||
|
- marysaka
|
||||||
- LDj3SNuD
|
- LDj3SNuD
|
||||||
|
|
||||||
gpu:
|
gpu:
|
||||||
- gdkchan
|
- gdkchan
|
||||||
- riperiperi
|
- riperiperi
|
||||||
|
- marysaka
|
||||||
|
|
||||||
gui:
|
gui:
|
||||||
- Ack77
|
- Ack77
|
||||||
- emmauss
|
- emmauss
|
||||||
- TSRBerry
|
- TSRBerry
|
||||||
|
- marysaka
|
||||||
|
|
||||||
horizon:
|
horizon:
|
||||||
- gdkchan
|
- gdkchan
|
||||||
- Ack77
|
- Ack77
|
||||||
|
- marysaka
|
||||||
- TSRBerry
|
- TSRBerry
|
||||||
|
|
||||||
infra:
|
infra:
|
||||||
|
- marysaka
|
||||||
- TSRBerry
|
- TSRBerry
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
85
.github/workflows/build.yml
vendored
85
.github/workflows/build.yml
vendored
|
@ -10,17 +10,28 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.platform.name }} (${{ matrix.configuration }})
|
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||||
configuration: [Debug, Release]
|
configuration: [Debug, Release]
|
||||||
platform:
|
include:
|
||||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
- os: ubuntu-latest
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
OS_NAME: Linux x64
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||||
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
|
- os: macOS-latest
|
||||||
|
OS_NAME: macOS x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: osx_x64
|
||||||
|
|
||||||
|
- os: windows-latest
|
||||||
|
OS_NAME: Windows x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
|
@ -38,16 +49,6 @@ jobs:
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Change config filename
|
|
||||||
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
|
||||||
|
|
||||||
- name: Change config filename for macOS
|
|
||||||
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'macos-13'
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||||
|
|
||||||
|
@ -57,47 +58,46 @@ jobs:
|
||||||
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
|
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
retry-codes: 139
|
retry-codes: 139
|
||||||
if: matrix.platform.name != 'linux-arm64'
|
|
||||||
|
|
||||||
- name: Publish Ryujinx
|
- name: Publish Ryujinx
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
- name: Publish Ryujinx.Headless.SDL2
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Gtk3
|
- name: Publish Ryujinx.Ava
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_gtk -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Gtk3 --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Set executable bit
|
- name: Set executable bit
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
||||||
chmod +x ./publish_gtk/Ryujinx.Gtk3 ./publish_gtk/Ryujinx.sh
|
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest'
|
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish
|
path: publish
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish_sdl2_headless
|
path: publish_sdl2_headless
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Gtk3 artifact
|
- name: Upload Ryujinx.Ava artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish_gtk
|
path: publish_ava
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
build_macos:
|
build_macos:
|
||||||
name: macOS Universal (${{ matrix.configuration }})
|
name: macOS Universal (${{ matrix.configuration }})
|
||||||
|
@ -135,24 +135,19 @@ jobs:
|
||||||
id: git_short_hash
|
id: git_short_hash
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Change config filename
|
- name: Publish macOS Ryujinx.Ava
|
||||||
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx
|
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
- name: Publish macOS Ryujinx.Headless.SDL2
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx.Ava artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||||
path: "publish/*.tar.gz"
|
path: "publish_ava/*.tar.gz"
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||||
|
|
2
.github/workflows/checks.yml
vendored
2
.github/workflows/checks.yml
vendored
|
@ -8,7 +8,7 @@ on:
|
||||||
- '!.github/**'
|
- '!.github/**'
|
||||||
- '!*.yml'
|
- '!*.yml'
|
||||||
- '!*.config'
|
- '!*.config'
|
||||||
- '!*.md'
|
- '!README.md'
|
||||||
- '.github/workflows/*.yml'
|
- '.github/workflows/*.yml'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|
72
.github/workflows/flatpak.yml
vendored
72
.github/workflows/flatpak.yml
vendored
|
@ -51,76 +51,38 @@ jobs:
|
||||||
- name: Restore Nuget packages
|
- name: Restore Nuget packages
|
||||||
# With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing.
|
# With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing.
|
||||||
# So we just publish to grab the dependencies
|
# So we just publish to grab the dependencies
|
||||||
run: |
|
run: dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
||||||
dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
|
||||||
dotnet publish -c Release -r linux-arm64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
|
||||||
|
|
||||||
- name: Generate nuget_sources.json
|
- name: Generate nuget_sources.json
|
||||||
shell: python
|
shell: python
|
||||||
run: |
|
run: |
|
||||||
import hashlib
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
sources = []
|
sources = []
|
||||||
|
|
||||||
|
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||||
|
name = path.parent.parent.name
|
||||||
|
version = path.parent.name
|
||||||
|
filename = '{}.{}.nupkg'.format(name, version)
|
||||||
|
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||||
|
|
||||||
def create_source_from_external(name, version):
|
with path.open() as fp:
|
||||||
full_dir_path = Path(os.environ["NUGET_PACKAGES"]).joinpath(name).joinpath(version)
|
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||||
os.makedirs(full_dir_path, exist_ok=True)
|
|
||||||
|
|
||||||
filename = "{}.{}.nupkg".format(name, version)
|
sources.append({
|
||||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
'type': 'file',
|
||||||
name, version, filename
|
'url': url,
|
||||||
)
|
'sha512': sha512,
|
||||||
|
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||||
|
'dest-filename': filename,
|
||||||
|
})
|
||||||
|
|
||||||
print(f"Processing {url}...")
|
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||||
response = urllib.request.urlopen(url)
|
json.dump(sources, fp, indent=4)
|
||||||
sha512 = hashlib.sha512(response.read()).hexdigest()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"type": "file",
|
|
||||||
"url": url,
|
|
||||||
"sha512": sha512,
|
|
||||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
|
||||||
"dest-filename": filename,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
has_added_x64_apphost = False
|
|
||||||
|
|
||||||
for path in Path(os.environ["NUGET_PACKAGES"]).glob("**/*.nupkg.sha512"):
|
|
||||||
name = path.parent.parent.name
|
|
||||||
version = path.parent.name
|
|
||||||
filename = "{}.{}.nupkg".format(name, version)
|
|
||||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
|
||||||
name, version, filename
|
|
||||||
)
|
|
||||||
|
|
||||||
with path.open() as fp:
|
|
||||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode("ascii")
|
|
||||||
|
|
||||||
sources.append(
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"url": url,
|
|
||||||
"sha512": sha512,
|
|
||||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
|
||||||
"dest-filename": filename,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# .NET will not add current installed application host to the list, force inject it here.
|
|
||||||
if not has_added_x64_apphost and name.startswith('microsoft.netcore.app.host'):
|
|
||||||
sources.append(create_source_from_external("microsoft.netcore.app.host.linux-x64", version))
|
|
||||||
has_added_x64_apphost = True
|
|
||||||
|
|
||||||
with open("flathub/nuget_sources.json", "w") as fp:
|
|
||||||
json.dump(sources, fp, indent=4)
|
|
||||||
|
|
||||||
- name: Update flatpak metadata
|
- name: Update flatpak metadata
|
||||||
id: metadata
|
id: metadata
|
||||||
|
|
41
.github/workflows/mako.yml
vendored
41
.github/workflows/mako.yml
vendored
|
@ -1,41 +0,0 @@
|
||||||
name: Mako
|
|
||||||
on:
|
|
||||||
discussion:
|
|
||||||
types: [created, edited, answered, unanswered, category_changed]
|
|
||||||
discussion_comment:
|
|
||||||
types: [created, edited]
|
|
||||||
gollum:
|
|
||||||
issue_comment:
|
|
||||||
types: [created, edited]
|
|
||||||
issues:
|
|
||||||
types: [opened, edited, reopened, pinned, milestoned, demilestoned, assigned, unassigned, labeled, unlabeled]
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, edited, reopened, synchronize, ready_for_review, assigned, unassigned]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
tasks:
|
|
||||||
name: Run Ryujinx tasks
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
discussions: write
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
with:
|
|
||||||
# Ensure we pin the source origin as pull_request_target run under forks.
|
|
||||||
fetch-depth: 0
|
|
||||||
repository: Ryujinx/Ryujinx
|
|
||||||
ref: master
|
|
||||||
|
|
||||||
- name: Run Mako command
|
|
||||||
uses: Ryujinx/Ryujinx-Mako@master
|
|
||||||
with:
|
|
||||||
command: exec-ryujinx-tasks
|
|
||||||
args: --event-name "${{ github.event_name }}" --event-path "${{ github.event_path }}" -w "${{ github.workspace }}" "${{ github.repository }}" "${{ github.run_id }}"
|
|
||||||
app_id: ${{ secrets.MAKO_APP_ID }}
|
|
||||||
private_key: ${{ secrets.MAKO_PRIVATE_KEY }}
|
|
||||||
installation_id: ${{ secrets.MAKO_INSTALLATION_ID }}
|
|
10
.github/workflows/nightly_pr_comment.yml
vendored
10
.github/workflows/nightly_pr_comment.yml
vendored
|
@ -39,24 +39,24 @@ jobs:
|
||||||
return core.error(`No artifacts found`);
|
return core.error(`No artifacts found`);
|
||||||
}
|
}
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
let body = `Download the artifacts for this pull request:\n`;
|
||||||
let hidden_gtk_artifacts = `\n\n <details><summary>Old GUI (GTK3)</summary>\n`;
|
let hidden_avalonia_artifacts = `\n\n <details><summary>Experimental GUI (Avalonia)</summary>\n`;
|
||||||
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||||
for (const art of artifacts) {
|
for (const art of artifacts) {
|
||||||
if(art.name.includes('Debug')) {
|
if(art.name.includes('Debug')) {
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else if(art.name.includes('gtk-ryujinx')) {
|
} else if(art.name.includes('ava-ryujinx')) {
|
||||||
hidden_gtk_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_avalonia_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
||||||
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else {
|
} else {
|
||||||
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hidden_gtk_artifacts += `\n</details>`;
|
hidden_avalonia_artifacts += `\n</details>`;
|
||||||
hidden_headless_artifacts += `\n</details>`;
|
hidden_headless_artifacts += `\n</details>`;
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
hidden_debug_artifacts += `\n</details>`;
|
||||||
body += hidden_gtk_artifacts;
|
body += hidden_avalonia_artifacts;
|
||||||
body += hidden_headless_artifacts;
|
body += hidden_headless_artifacts;
|
||||||
body += hidden_debug_artifacts;
|
body += hidden_debug_artifacts;
|
||||||
|
|
||||||
|
|
19
.github/workflows/pr_triage.yml
vendored
19
.github/workflows/pr_triage.yml
vendored
|
@ -21,8 +21,27 @@ jobs:
|
||||||
repository: Ryujinx/Ryujinx
|
repository: Ryujinx/Ryujinx
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
|
- name: Checkout Ryujinx-Mako
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: Ryujinx/Ryujinx-Mako
|
||||||
|
ref: master
|
||||||
|
path: '.ryujinx-mako'
|
||||||
|
|
||||||
|
- name: Setup Ryujinx-Mako
|
||||||
|
uses: ./.ryujinx-mako/.github/actions/setup-mako
|
||||||
|
|
||||||
- name: Update labels based on changes
|
- name: Update labels based on changes
|
||||||
uses: actions/labeler@v5
|
uses: actions/labeler@v5
|
||||||
with:
|
with:
|
||||||
sync-labels: true
|
sync-labels: true
|
||||||
dot: true
|
dot: true
|
||||||
|
|
||||||
|
- name: Assign reviewers
|
||||||
|
run: |
|
||||||
|
poetry -n -C .ryujinx-mako run ryujinx-mako update-reviewers ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
MAKO_APP_ID: ${{ secrets.MAKO_APP_ID }}
|
||||||
|
MAKO_PRIVATE_KEY: ${{ secrets.MAKO_PRIVATE_KEY }}
|
||||||
|
MAKO_INSTALLATION_ID: ${{ secrets.MAKO_INSTALLATION_ID }}
|
||||||
|
|
72
.github/workflows/release.yml
vendored
72
.github/workflows/release.yml
vendored
|
@ -10,7 +10,7 @@ on:
|
||||||
- '*.yml'
|
- '*.yml'
|
||||||
- '*.json'
|
- '*.json'
|
||||||
- '*.config'
|
- '*.config'
|
||||||
- '*.md'
|
- 'README.md'
|
||||||
|
|
||||||
concurrency: release
|
concurrency: release
|
||||||
|
|
||||||
|
@ -44,27 +44,23 @@ jobs:
|
||||||
sha: context.sha
|
sha: context.sha
|
||||||
})
|
})
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
uses: ncipollo/release-action@v1
|
|
||||||
with:
|
|
||||||
name: ${{ steps.version_info.outputs.build_version }}
|
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
|
||||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
|
||||||
omitBodyDuringUpdate: true
|
|
||||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
|
||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Release for ${{ matrix.platform.name }}
|
name: Release ${{ matrix.OS_NAME }}
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
os: [ ubuntu-latest, windows-latest ]
|
||||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
include:
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- os: ubuntu-latest
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
OS_NAME: Linux x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
|
- os: windows-latest
|
||||||
|
OS_NAME: Windows x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
@ -89,7 +85,6 @@ jobs:
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create output dir
|
- name: Create output dir
|
||||||
|
@ -97,36 +92,42 @@ jobs:
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_gtk/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
||||||
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish_gtk
|
||||||
cp publish/Ryujinx.exe publish/Ryujinx.Ava.exe
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
|
||||||
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd publish_ava
|
||||||
|
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
if: matrix.platform.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish_gtk
|
||||||
cp publish/Ryujinx publish/Ryujinx.Ava
|
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava publish/Ryujinx
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
|
||||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd publish_ava
|
||||||
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||||
|
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
@ -185,10 +186,9 @@ jobs:
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx.Ava
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||||
|
|
||||||
|
|
35
Compile.md
35
Compile.md
|
@ -12,16 +12,23 @@ Before you begin, ensure you have the following installed:
|
||||||
|
|
||||||
### 1. Clone the Repository and Build Ryujinx
|
### 1. Clone the Repository and Build Ryujinx
|
||||||
|
|
||||||
Open a terminal and run (your password will not be shown in the 2nd command):
|
Open a terminal and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://git.743378673.xyz/MeloNX/MeloNX.git
|
git clone https://git.743378673.xyz/MeloNX/MeloNX.git
|
||||||
|
cd MeloNX
|
||||||
|
./compile.sh
|
||||||
|
```
|
||||||
|
You may need to run this command if compilation fails, then run the `./compile.sh` command again (You will need to put in your user password. Your password will not be shown at all.)
|
||||||
|
```
|
||||||
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
||||||
```
|
```
|
||||||
|
|
||||||
However, if you only need to update MeloNX, make sure you have cd into the directory then run this
|
|
||||||
|
However, if you only need to update MeloNX, make sure you have cd into the directory then run this then skip to step 5
|
||||||
```
|
```
|
||||||
git pull
|
git pull
|
||||||
|
./compile.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Open the Xcode Project
|
### 2. Open the Xcode Project
|
||||||
|
@ -34,7 +41,13 @@ src/MeloNX/MeloNX.xcodeproj
|
||||||
|
|
||||||
Double-click to open it in **Xcode**.
|
Double-click to open it in **Xcode**.
|
||||||
|
|
||||||
### 3. Configure Signing & Capabilities
|
### 3. Configure the Project Settings
|
||||||
|
|
||||||
|
- In **Xcode**, select the **MeloNX** project.
|
||||||
|
- Under the **General** tab, find `Ryujinx.Headless.SDL2.dylib`.
|
||||||
|
- Set its **Embed setting** to **"Embed & Sign"**.
|
||||||
|
|
||||||
|
### 4. Configure Signing & Capabilities
|
||||||
|
|
||||||
- In **Xcode**, go to **Signing & Capabilities**.
|
- In **Xcode**, go to **Signing & Capabilities**.
|
||||||
- Set the **Team** to your **Apple Developer account** (free or paid).
|
- Set the **Team** to your **Apple Developer account** (free or paid).
|
||||||
|
@ -46,31 +59,23 @@ Double-click to open it in **Xcode**.
|
||||||
|
|
||||||
*(Replace `<your-name>` with your actual name or identifier.)*
|
*(Replace `<your-name>` with your actual name or identifier.)*
|
||||||
|
|
||||||
### 4. Connect Your Device
|
### 5. Connect Your Device
|
||||||
|
|
||||||
Ensure your **iPhone/iPad** is **connected** and **selected** (Next to MeloNX with the arrow) in Xcode.
|
Ensure your **iPhone/iPad** is **connected** and **selected** (Next to MeloNX with the arrow) in Xcode.
|
||||||
- You may need to install the iOS SDK. it will say next to MeloNX with the arrow saying "iOS XX Not Installed (GET)"
|
- You may need to install the iOS SDK. it will say next to MeloNX with the arrow saying "iOS XX Not Installed (GET)"
|
||||||
- You will be need to press GET and wait for it to finish downloading and installing
|
- You will be need to press GET and wait for it to finish downloading and installing
|
||||||
- Then you will be able to select your device and Build and Run.
|
- Then you will be able to select your device and Build and Run.
|
||||||
|
|
||||||
### Make Sure you do **NOT** select the Simulator. (Which is the Generic names and the ones with the non-coloured icons, e.g. "iPhone 16 Pro")
|
Make Sure you do **NOT** select the Simulator. (Which is the Generic names and the ones with the non-coloured icons, e.g. "iPhone 16 Pro")
|
||||||
|
|
||||||
### 6. Configure the Project Settings
|
### 6. Build and Run
|
||||||
|
|
||||||
Click the **Run (▶️) button** in Xcode to compile MeloNX and wait it will fail with Undefined Symbol(s) the first time, Thats normal.
|
|
||||||
- When it fails the first time, do this:
|
|
||||||
- In **Xcode**, select the **MeloNX** project.
|
|
||||||
- Under the **General** tab, find `Ryujinx.Headless.SDL2.dylib`.
|
|
||||||
- Set its **Embed setting** to **"Embed & Sign"**.
|
|
||||||
|
|
||||||
### 5. Build and Run
|
|
||||||
|
|
||||||
Click the **Run (▶️) button** in Xcode to compile and launch MeloNX.
|
Click the **Run (▶️) button** in Xcode to compile and launch MeloNX.
|
||||||
- When running on your device, Click the **Spray Can Button** below the Run button
|
- When running on your device, Click the **Spray Can Button** below the Run button
|
||||||
- Right Click where it says "> MeloNX PID XXXX"
|
- Right Click where it says "> MeloNX PID XXXX"
|
||||||
- Press Detach in the Context Menu.
|
- Press Detach in the Context Menu.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Now you're all set! 🚀 If you encounter issues, please join the discord at https://melonx.org
|
Now you're all set! 🚀 If you encounter issues, please join the discord at https://melonx.org
|
||||||
|
```
|
|
@ -3,48 +3,50 @@
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.0.10" />
|
<PackageVersion Include="Avalonia" Version="11.0.5" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.5" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.0.5" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.5" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.5" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.3" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.3" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="2.2.0" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.4" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="7.0.0" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Core" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.1" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.3" />
|
||||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.28.1-build28" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
|
<PackageVersion Include="System.Drawing.Common" Version="8.0.0" />
|
||||||
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
Currently licensed under the GNU AFFERO GENERAL PUBLIC LICENSE version 3, or any later version, at your choice.
|
|
||||||
You may obtain a copy of the license at <https://gnu.org/>
|
|
||||||
|
|
||||||
Copyright (c) Rhajune Park and contributors, 2025
|
|
||||||
|
|
||||||
For copyright infringement claims, please contact abuse@pythonplayer123.dev for expedited processing
|
|
||||||
|
|
||||||
Previously licensed under the MeloNX License.
|
|
||||||
|
|
||||||
MeloNX License
|
MeloNX License
|
||||||
|
|
||||||
Copyright (c) MeloNX Team and Contributors
|
Copyright (c) MeloNX Team and Contributors
|
||||||
|
|
90
README.md
90
README.md
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<h1 align="center">MeloNX</h1>
|
<h1 align="center">MeloNX</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
MeloNX enables Nintendo Switch game emulation on iOS using the Ryujinx iOS code base.
|
MeloNX enables Nintendo Switch game emulation on iOS using the Ryujinx iOS code base.
|
||||||
</p>
|
</p>
|
||||||
|
@ -22,16 +24,13 @@ MeloNX works on iPhone XS/XR and later and iPad 8th Gen and later. Check out the
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
- MeloNX is made for iOS 17+, on iOS 15 - 16 MeloNX can be installed but may have issues or not work at all.
|
- MeloNX is made for iOS 17+, on iOS 15 - 16 MeloNX can be installed but will have issues or may not work at all.
|
||||||
- MeloNX cannot be Sideloaded normally and requires the use of the following Installation Guide(s).
|
- MeloNX needs Xcode or a Paid Apple Developer Account. SideStore support may come soon (SideStore Side Issue)
|
||||||
- MeloNX requires JIT
|
- MeloNX needs JIT
|
||||||
- Recommended Device: iPhone 15 Pro or newer.
|
- Recommended Device: iPhone 15 Pro or newer.
|
||||||
- Low-End Recommended Device: iPhone 13 Pro.
|
- Low-End Recommended Device**: iPhone 13 Pro.
|
||||||
|
- Lowest Supported Device: iPhone XR
|
||||||
|
|
||||||
## Discord Server
|
|
||||||
|
|
||||||
We have a discord server!
|
|
||||||
- https://discord.gg/melonx
|
|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
|
@ -52,67 +51,14 @@ We have a discord server!
|
||||||
|
|
||||||
4. **Enable JIT**
|
4. **Enable JIT**
|
||||||
- Use your preferred method to enable Just-In-Time (JIT) compilation.
|
- Use your preferred method to enable Just-In-Time (JIT) compilation.
|
||||||
- We reccomend using [StikDebug](https://apps.apple.com/us/app/stikdebug/id6744045754)
|
|
||||||
|
|
||||||
5. **Add Necessary Files**
|
5. **Add Necessary Files**
|
||||||
|
|
||||||
If having Issues installing firmware (Make sure your Keys are installed first)
|
If having Issues installing firmware (Make sure your Keys are installed first)
|
||||||
- If needed, install firmware and keys from **Ryujinx Desktop** (or forks).
|
- If needed, install firmware and keys from **Ryujinx Desktop**.
|
||||||
- Copy the **bis** and **system** folders
|
- Copy the **bis** and **system** folders
|
||||||
|
|
||||||
### Free Developer Account (Experimental)
|
### Xcode
|
||||||
|
|
||||||
1. **Sideload MeloNX**
|
|
||||||
- Use [SideStore](https://sidestore.io/) or [AltStore](https://altstore.io/) (**NOT** AltStore PAL).
|
|
||||||
|
|
||||||
2. **Sideload the Entitlement App**
|
|
||||||
- Install [this app](https://github.com/hugeBlack/GetMoreRam/releases/download/nightly/Entitlement.ipa) using [SideStore](https://sidestore.io/) or [AltStore](https://altstore.io/) (**NOT** AltStore PAL).
|
|
||||||
|
|
||||||
3. **Sign In to Your Account**
|
|
||||||
- Open **Settings** in the entitlement app and sign in with your Apple ID.
|
|
||||||
|
|
||||||
4. **Refresh App IDs**
|
|
||||||
- Navigate to the **App IDs** page.
|
|
||||||
- Tap **Refresh** to update the list.
|
|
||||||
|
|
||||||
5. **Enable Increased Memory Limit**
|
|
||||||
- Select **MeloNX** (should be like "com.stossy11.MeloNX" or some variation) from the list.
|
|
||||||
- Tap **Add Increased Memory Limit**.
|
|
||||||
|
|
||||||
6. **Reinstall MeloNX**
|
|
||||||
- Delete the existing installation.
|
|
||||||
- Sideload the app again using SideStore or AltStore.
|
|
||||||
|
|
||||||
7. **Verify Increased Memory Limit**
|
|
||||||
- Open MeloNX and check if the **Increased Memory Limit** is enabled.
|
|
||||||
|
|
||||||
8. **Add Necessary Files**
|
|
||||||
|
|
||||||
If having Issues installing firmware (Make sure your keys are installed first)
|
|
||||||
- If needed, install firmware and keys from **Ryujinx Desktop** (or forks).
|
|
||||||
- Copy the **bis** and **system** folders
|
|
||||||
|
|
||||||
9. **Enable JIT**
|
|
||||||
- Use your preferred method to enable Just-In-Time (JIT) compilation.
|
|
||||||
- We recommend using [StikDebug](https://apps.apple.com/us/app/stikdebug/id6744045754)
|
|
||||||
|
|
||||||
|
|
||||||
### TrollStore
|
|
||||||
As Said in FAQ:
|
|
||||||
> MeloNX is made for iOS 17+, on iOS 15 - 16 MeloNX can be installed but may have issues or not work at all.
|
|
||||||
|
|
||||||
1. **Install MeloNX with TrollStore**
|
|
||||||
|
|
||||||
2. **Add Necessary Files**
|
|
||||||
|
|
||||||
3. **Enable TrollStore JIT**
|
|
||||||
- MeloNX includes automatic JIT using the TrollStore URL Scheme
|
|
||||||
- Open MeloNX Settings
|
|
||||||
- Scroll down and enable the "TrollStore JIT" toggle
|
|
||||||
- Profit
|
|
||||||
|
|
||||||
|
|
||||||
### Free Developer Account (Xcode)
|
|
||||||
|
|
||||||
**NOTE: These Xcode builds are nightly and may have unfinished features.**
|
**NOTE: These Xcode builds are nightly and may have unfinished features.**
|
||||||
|
|
||||||
|
@ -121,8 +67,8 @@ As Said in FAQ:
|
||||||
|
|
||||||
2. **Add Necessary Files**
|
2. **Add Necessary Files**
|
||||||
|
|
||||||
If having Issues installing firmware (Make sure your keys are installed first)
|
If having Issues installing firmware (Make sure your Keys are installed first)
|
||||||
- If needed, install firmware and keys from **Ryujinx Desktop** (or forks).
|
- If needed, install firmware and keys from **Ryujinx Desktop**.
|
||||||
- Copy the **bis** and **system** folders
|
- Copy the **bis** and **system** folders
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
@ -145,12 +91,13 @@ If having Issues installing firmware (Make sure your keys are installed first)
|
||||||
|
|
||||||
- **GPU**
|
- **GPU**
|
||||||
|
|
||||||
The GPU emulator emulates the Switch's Maxwell GPU using Metal (via MoltenVK) APIs through a custom build of Silk.NET.
|
The GPU emulator emulates the Switch's Maxwell GPU using Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
We currently have support for keyboard, touch input, JoyCon input support, and nearly all MFI controllers.
|
We currently have support for keyboard, touch input, JoyCon input support, and nearly all controllers.
|
||||||
Motion controls are natively supported in most cases, however JoyCons do not have motion support doe to an iOS limitation.
|
Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
||||||
|
In all scenarios, you can set up everything inside the input configuration menu.
|
||||||
|
|
||||||
- **DLC & Modifications**
|
- **DLC & Modifications**
|
||||||
|
|
||||||
|
@ -161,13 +108,14 @@ If having Issues installing firmware (Make sure your keys are installed first)
|
||||||
|
|
||||||
The emulator has settings for enabling or disabling some logging, remapping controllers, and more.
|
The emulator has settings for enabling or disabling some logging, remapping controllers, and more.
|
||||||
|
|
||||||
# License
|
## License
|
||||||
|
|
||||||
This software is licensed under the terms of the [MeloNX license](LICENSE.txt).
|
This software is licensed under the terms of the [MeloNX license (Based on MIT License)](LICENSE.txt).
|
||||||
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
|
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
|
||||||
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
|
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
|
||||||
|
|
||||||
# Credits
|
## Credits
|
||||||
|
|
||||||
- [Ryujinx](https://github.com/ryujinx-mirror/ryujinx) is used for the base of this emulator. (link is to ryujinx-mirror since they were supportive)
|
- [Ryujinx](https://github.com/ryujinx-mirror/ryujinx) is used for the base of this emulator. (link is to ryujinx-mirror since they were supportive)
|
||||||
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
||||||
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
|
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
|
||||||
|
|
14
Ryujinx.sln
14
Ryujinx.sln
|
@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.1.32228.430
|
VisualStudioVersion = 17.1.32228.430
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Gtk3", "src\Ryujinx.Gtk3\Ryujinx.Gtk3.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -69,9 +69,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "sr
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "src\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -79,7 +79,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.LocaleGenerator", "src\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "src\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -87,8 +87,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -251,10 +249,6 @@ Global
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
|
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></Policy></s:String>
|
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
# Define the destination directory (hardcoded)
|
# Define the destination directory (hardcoded)
|
||||||
DESTINATION_DIR="src/MeloNX/Dependencies/Dynamic\ Libraries/Ryujinx.Headless.SDL2.dylib"
|
DESTINATION_DIR="src/MeloNX/Dependencies/Dynamic\ Libraries/Ryujinx.Headless.SDL2.dylib"
|
||||||
|
|
||||||
dotnet clean
|
|
||||||
|
|
||||||
# Restore the project
|
# Restore the project
|
||||||
dotnet restore
|
dotnet restore
|
||||||
|
|
||||||
# Build the project with the specified version
|
# Build the project with the specified version
|
||||||
# dotnet build -c Release
|
dotnet build -c Release
|
||||||
|
|
||||||
# Publish the project with the specified runtime and settings
|
# Publish the project with the specified runtime and settings
|
||||||
dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
|
@ -1,33 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# XCCONFIG_FILE="${SRCROOT}/MeloNX.xcconfig"
|
|
||||||
|
|
||||||
SEARCH_PATHS=(
|
|
||||||
"/usr/local/share/dotnet"
|
|
||||||
"/usr/local/bin"
|
|
||||||
"/usr/bin"
|
|
||||||
"/bin"
|
|
||||||
"/opt"
|
|
||||||
"/Library/Frameworks"
|
|
||||||
"$HOME/.dotnet"
|
|
||||||
"$HOME/Developer"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DOTNET_PATH=""
|
|
||||||
|
|
||||||
for path in "${SEARCH_PATHS[@]}"; do
|
|
||||||
if [ -d "$path" ]; then
|
|
||||||
DOTNET_PATH=$(find "$path" -name dotnet -type f -print -quit 2>/dev/null)
|
|
||||||
if [ -n "$DOTNET_PATH" ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$DOTNET_PATH" ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$DOTNET_PATH"
|
|
|
@ -1,38 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
GITEA_URL="https://git.743378673.xyz/"
|
|
||||||
REPO="MeloNX"
|
|
||||||
XCCONFIG_FILE="${SRCROOT}/MeloNX.xcconfig"
|
|
||||||
|
|
||||||
INCREMENT_PATCH=false
|
|
||||||
|
|
||||||
# Check for --patch argument
|
|
||||||
if [[ "$1" == "--patch" ]]; then
|
|
||||||
INCREMENT_PATCH=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fetch latest tag from Gitea
|
|
||||||
LATEST_VERSION=$(curl -s "${GITEA_URL}/api/v1/repos/${REPO}/${REPO}/tags" | jq -r '.[].name' | sort -V | tail -n1)
|
|
||||||
|
|
||||||
if [ -z "$LATEST_VERSION" ]; then
|
|
||||||
echo "Error: Could not fetch latest tag from Gitea"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Latest version: $LATEST_VERSION"
|
|
||||||
|
|
||||||
# Split version into major, minor, and patch
|
|
||||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$LATEST_VERSION"
|
|
||||||
|
|
||||||
# Increment version based on argument
|
|
||||||
if $INCREMENT_PATCH; then
|
|
||||||
NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
|
|
||||||
else
|
|
||||||
NEW_VERSION="$MAJOR.$((MINOR + 1)).0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "New version: $NEW_VERSION"
|
|
||||||
|
|
||||||
sed -i '' "s/^VERSION = $LATEST_VERSION$/VERSION = $NEW_VERSION/g" "$XCCONFIG_FILE"
|
|
||||||
|
|
||||||
echo "Updated MeloNX.xcconfig with version $NEW_VERSION"
|
|
|
@ -1,21 +0,0 @@
|
||||||
dotnet_output=$(./distribution/ios/get_dotnet.sh)
|
|
||||||
exit_code=$?
|
|
||||||
|
|
||||||
if [ $exit_code -eq 0 ]; then
|
|
||||||
dotnet="$dotnet_output"
|
|
||||||
else
|
|
||||||
echo "error: .NET not found, Please follow the compilation instructions on the gitea." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
$dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "warning: Compiling MeloNX failed! Running dotnet clean + restore then Retrying..."
|
|
||||||
|
|
||||||
$dotnet clean
|
|
||||||
|
|
||||||
$dotnet restore
|
|
||||||
|
|
||||||
$dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
|
||||||
fi
|
|
|
@ -4,7 +4,7 @@ Name=Ryujinx
|
||||||
Type=Application
|
Type=Application
|
||||||
Icon=Ryujinx
|
Icon=Ryujinx
|
||||||
Exec=Ryujinx.sh %f
|
Exec=Ryujinx.sh %f
|
||||||
Comment=A Nintendo Switch Emulator
|
Comment=Plays Nintendo Switch applications
|
||||||
GenericName=Nintendo Switch Emulator
|
GenericName=Nintendo Switch Emulator
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=Game;Emulator;
|
Categories=Game;Emulator;
|
||||||
|
|
15
distribution/linux/Ryujinx.sh
Executable file → Normal file
15
distribution/linux/Ryujinx.sh
Executable file → Normal file
|
@ -1,23 +1,20 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||||
|
RYUJINX_BIN="Ryujinx"
|
||||||
|
|
||||||
|
if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then
|
||||||
|
RYUJINX_BIN="Ryujinx.Ava"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/Ryujinx" ]; then
|
|
||||||
RYUJINX_BIN="Ryujinx"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$RYUJINX_BIN" ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
||||||
|
|
||||||
if command -v gamemoderun > /dev/null 2>&1; then
|
if command -v gamemoderun > /dev/null 2>&1; then
|
||||||
COMMAND="$COMMAND gamemoderun"
|
COMMAND="$COMMAND gamemoderun"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec $COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
|
@ -14,8 +14,8 @@ mkdir "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
||||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS"
|
mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS"
|
||||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
||||||
|
|
||||||
# Copy executable and nsure executable can be executed
|
# Copy executables first
|
||||||
cp "$PUBLISH_DIRECTORY/Ryujinx" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
cp "$PUBLISH_DIRECTORY/Ryujinx.Ava" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||||
chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||||
|
|
||||||
# Then all libraries
|
# Then all libraries
|
||||||
|
|
|
@ -22,9 +22,9 @@ EXTRA_ARGS=$8
|
||||||
|
|
||||||
if [ "$VERSION" == "1.1.0" ];
|
if [ "$VERSION" == "1.1.0" ];
|
||||||
then
|
then
|
||||||
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||||
else
|
else
|
||||||
RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_universal.app.tar
|
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$VERSION-macos_universal.app.tar
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
|
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
|
||||||
|
@ -38,9 +38,9 @@ mkdir -p "$TEMP_DIRECTORY"
|
||||||
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
|
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
|
||||||
|
|
||||||
dotnet restore
|
dotnet restore
|
||||||
dotnet build -c "$CONFIGURATION" src/Ryujinx
|
dotnet build -c "$CONFIGURATION" src/Ryujinx.Ava
|
||||||
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
|
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava
|
||||||
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
|
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava
|
||||||
|
|
||||||
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
||||||
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
||||||
|
@ -108,13 +108,6 @@ tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf "$RELEASE_TAR_FILE_NAME"
|
||||||
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
|
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
|
||||||
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||||
rm "$RELEASE_TAR_FILE_NAME"
|
rm "$RELEASE_TAR_FILE_NAME"
|
||||||
|
|
||||||
# Create legacy update package for Avalonia to not left behind old testers.
|
|
||||||
if [ "$VERSION" != "1.1.0" ];
|
|
||||||
then
|
|
||||||
cp $RELEASE_TAR_FILE_NAME.gz test-ava-ryujinx-$VERSION-macos_universal.app.tar.gz
|
|
||||||
fi
|
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
echo "Done"
|
echo "Done"
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
launch_arch="$(uname -m)"
|
|
||||||
if [ "$(sysctl -in sysctl.proc_translated)" = "1" ]
|
|
||||||
then
|
|
||||||
launch_arch="arm64"
|
|
||||||
fi
|
|
||||||
|
|
||||||
arch -$launch_arch {0} {1}
|
|
|
@ -33,3 +33,8 @@ Project Docs
|
||||||
=================
|
=================
|
||||||
|
|
||||||
To be added. Many project files will contain basic XML docs for key functions and classes in the meantime.
|
To be added. Many project files will contain basic XML docs for key functions and classes in the meantime.
|
||||||
|
|
||||||
|
Other Information
|
||||||
|
=================
|
||||||
|
|
||||||
|
- N/A
|
||||||
|
|
49
source.json
49
source.json
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"name": "MeloNX",
|
|
||||||
"subtitle": "A source for the MeloNX Application",
|
|
||||||
"description": "Welcome to the MeloNX source! The latest download for MeloNX.",
|
|
||||||
"iconURL": "https://git.743378673.xyz/CycloKid/assets/media/branch/main/Melo/AppIcons/MeloNX.png",
|
|
||||||
"headerURL": "https://cdn.discordapp.com/attachments/1320760161836466257/1331670540447912090/melon-x-not-melo-nx-amiright-guys.png?ex=67f556d6&is=67f40556&hm=71be8f109a14f1c47d8f4965aa017bccb5617962b7a9f5cdfb936a5a8135dad7&",
|
|
||||||
"website": "https://MeloNX.org",
|
|
||||||
"tintColor": "#AE34EB",
|
|
||||||
"featuredApps": [
|
|
||||||
"com.stossy11.MeloNX"
|
|
||||||
],
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"name": "MeloNX",
|
|
||||||
"bundleIdentifier": "com.stossy11.MeloNX",
|
|
||||||
"developerName": "Stossy11",
|
|
||||||
"subtitle": "An NX Emulator.",
|
|
||||||
"localizedDescription": "MeloNX is an iOS Nintendo Switch emulator based on Ryujinx, written primarily in C#. Designed to bring accurate performance and a user-friendly interface to iOS, MeloNX makes Switch games accessible on Apple devices. Developed from the ground up, MeloNX is open-source and available on a custom Gitea server under the MeloNX license (Based on MIT) (requires increased memory limit)",
|
|
||||||
"iconURL": "https://git.743378673.xyz/CycloKid/assets/media/branch/main/Melo/AppIcons/MeloNX.png",
|
|
||||||
"tintColor": "#AE34EB",
|
|
||||||
"category": "games",
|
|
||||||
"screenshots": [
|
|
||||||
"https://git.743378673.xyz/stossy11/screenshots/raw/branch/main/IMG_0380.PNG",
|
|
||||||
"https://git.743378673.xyz/stossy11/screenshots/raw/branch/main/IMG_0381.PNG"
|
|
||||||
],
|
|
||||||
"versions": [
|
|
||||||
{
|
|
||||||
"version": "1.7.0",
|
|
||||||
"buildVersion": "1",
|
|
||||||
"date": "2025-04-08",
|
|
||||||
"localizedDescription": "First AltStore release!",
|
|
||||||
"downloadURL": "https://git.743378673.xyz/MeloNX/MeloNX/releases/download/1.7.0/MeloNX.ipa",
|
|
||||||
"size": 79821,
|
|
||||||
"minOSVersion": "15.0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"appPermissions": {
|
|
||||||
"entitlements": [
|
|
||||||
"get-task-allow",
|
|
||||||
"com.apple.developer.kernel.increased-memory-limit"
|
|
||||||
],
|
|
||||||
"privacy": {
|
|
||||||
"NSPhotoLibraryAddUsageDescription": "MeloNX needs access to your Photo Library in order to save screenshots."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"news": []
|
|
||||||
}
|
|
|
@ -237,7 +237,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||||
long originalPosition = _stream.Position;
|
long originalPosition = _stream.Position;
|
||||||
|
|
||||||
_stream.Seek(0, SeekOrigin.Begin);
|
_stream.Seek(0, SeekOrigin.Begin);
|
||||||
_stream.ReadExactly(code, 0, code.Length);
|
_stream.Read(code, 0, code.Length);
|
||||||
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
RelocInfo relocInfo;
|
RelocInfo relocInfo;
|
||||||
|
|
|
@ -251,20 +251,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a copy destination variable, we prefer the register used for the copy source.
|
int selectedReg = GetHighestValueIndex(freePositions);
|
||||||
// If the register is available, then the copy can be eliminated later as both source
|
|
||||||
// and destination will use the same register.
|
|
||||||
int selectedReg;
|
|
||||||
|
|
||||||
if (current.TryGetCopySourceRegister(out int preferredReg) && freePositions[preferredReg] >= current.GetEnd())
|
|
||||||
{
|
|
||||||
selectedReg = preferredReg;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
selectedReg = GetHighestValueIndex(freePositions);
|
|
||||||
}
|
|
||||||
|
|
||||||
int selectedNextUse = freePositions[selectedReg];
|
int selectedNextUse = freePositions[selectedReg];
|
||||||
|
|
||||||
// Intervals starts and ends at odd positions, unless they span an entire
|
// Intervals starts and ends at odd positions, unless they span an entire
|
||||||
|
@ -444,7 +431,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetHighestValueIndex(ReadOnlySpan<int> span)
|
private static int GetHighestValueIndex(Span<int> span)
|
||||||
{
|
{
|
||||||
int highest = int.MinValue;
|
int highest = int.MinValue;
|
||||||
|
|
||||||
|
@ -811,12 +798,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
// The "visited" state is stored in the MSB of the local's value.
|
// The "visited" state is stored in the MSB of the local's value.
|
||||||
const ulong VisitedMask = 1ul << 63;
|
const ulong VisitedMask = 1ul << 63;
|
||||||
|
|
||||||
static bool IsVisited(Operand local)
|
bool IsVisited(Operand local)
|
||||||
{
|
{
|
||||||
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetVisited(Operand local)
|
void SetVisited(Operand local)
|
||||||
{
|
{
|
||||||
local.GetValueUnsafe() |= VisitedMask;
|
local.GetValueUnsafe() |= VisitedMask;
|
||||||
}
|
}
|
||||||
|
@ -839,25 +826,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
{
|
{
|
||||||
dest.NumberLocal(_intervals.Count);
|
dest.NumberLocal(_intervals.Count);
|
||||||
|
|
||||||
LiveInterval interval = new LiveInterval(dest);
|
_intervals.Add(new LiveInterval(dest));
|
||||||
_intervals.Add(interval);
|
|
||||||
|
|
||||||
SetVisited(dest);
|
SetVisited(dest);
|
||||||
|
|
||||||
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
|
||||||
// This is used for register preferencing later on, which allows the copy to be eliminated
|
|
||||||
// in some cases.
|
|
||||||
if (node.Instruction == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32)
|
|
||||||
{
|
|
||||||
Operand source = node.GetSource(0);
|
|
||||||
|
|
||||||
if (source.Kind == OperandKind.LocalVariable &&
|
|
||||||
source.GetLocalNumber() > 0 &&
|
|
||||||
(node.Instruction == Instruction.Copy || source.Type == OperandType.I32))
|
|
||||||
{
|
|
||||||
interval.SetCopySource(_intervals[source.GetLocalNumber()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
public LiveRange CurrRange;
|
public LiveRange CurrRange;
|
||||||
|
|
||||||
public LiveInterval Parent;
|
public LiveInterval Parent;
|
||||||
public LiveInterval CopySource;
|
|
||||||
|
|
||||||
public UseList Uses;
|
public UseList Uses;
|
||||||
public LiveIntervalList Children;
|
public LiveIntervalList Children;
|
||||||
|
@ -38,7 +37,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
private ref LiveRange CurrRange => ref _data->CurrRange;
|
private ref LiveRange CurrRange => ref _data->CurrRange;
|
||||||
private ref LiveRange PrevRange => ref _data->PrevRange;
|
private ref LiveRange PrevRange => ref _data->PrevRange;
|
||||||
private ref LiveInterval Parent => ref _data->Parent;
|
private ref LiveInterval Parent => ref _data->Parent;
|
||||||
private ref LiveInterval CopySource => ref _data->CopySource;
|
|
||||||
private ref UseList Uses => ref _data->Uses;
|
private ref UseList Uses => ref _data->Uses;
|
||||||
private ref LiveIntervalList Children => ref _data->Children;
|
private ref LiveIntervalList Children => ref _data->Children;
|
||||||
|
|
||||||
|
@ -80,25 +78,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
Register = register;
|
Register = register;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCopySource(LiveInterval copySource)
|
|
||||||
{
|
|
||||||
CopySource = copySource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetCopySourceRegister(out int copySourceRegIndex)
|
|
||||||
{
|
|
||||||
if (CopySource._data != null)
|
|
||||||
{
|
|
||||||
copySourceRegIndex = CopySource.Register.Index;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
copySourceRegIndex = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
PrevRange = default;
|
PrevRange = default;
|
||||||
|
|
|
@ -1444,7 +1444,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
|
|
||||||
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
||||||
|
|
||||||
_stream.ReadExactly(buffer);
|
_stream.Read(buffer);
|
||||||
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
||||||
|
|
||||||
codeStream.Write(buffer);
|
codeStream.Write(buffer);
|
||||||
|
|
|
@ -517,10 +517,7 @@ namespace ARMeilleure.Decoders
|
||||||
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", InstName.Sqrshrn_V, InstEmit.Sqrshrn_V, OpCodeSimdShImm.Create);
|
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", InstName.Sqrshrn_V, InstEmit.Sqrshrn_V, OpCodeSimdShImm.Create);
|
||||||
SetA64("0111111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_S, InstEmit.Sqrshrun_S, OpCodeSimdShImm.Create);
|
SetA64("0111111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_S, InstEmit.Sqrshrun_S, OpCodeSimdShImm.Create);
|
||||||
SetA64("0x10111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_V, InstEmit.Sqrshrun_V, OpCodeSimdShImm.Create);
|
SetA64("0x10111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_V, InstEmit.Sqrshrun_V, OpCodeSimdShImm.Create);
|
||||||
SetA64("010111110>>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Si, InstEmit.Sqshl_Si, OpCodeSimdShImm.Create);
|
|
||||||
SetA64("0>001110<<1xxxxx010011xxxxxxxxxx", InstName.Sqshl_V, InstEmit.Sqshl_V, OpCodeSimdReg.Create);
|
SetA64("0>001110<<1xxxxx010011xxxxxxxxxx", InstName.Sqshl_V, InstEmit.Sqshl_V, OpCodeSimdReg.Create);
|
||||||
SetA64("0000111100>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Vi, InstEmit.Sqshl_Vi, OpCodeSimdShImm.Create);
|
|
||||||
SetA64("010011110>>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Vi, InstEmit.Sqshl_Vi, OpCodeSimdShImm.Create);
|
|
||||||
SetA64("0101111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_S, InstEmit.Sqshrn_S, OpCodeSimdShImm.Create);
|
SetA64("0101111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_S, InstEmit.Sqshrn_S, OpCodeSimdShImm.Create);
|
||||||
SetA64("0x00111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_V, InstEmit.Sqshrn_V, OpCodeSimdShImm.Create);
|
SetA64("0x00111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_V, InstEmit.Sqshrn_V, OpCodeSimdShImm.Create);
|
||||||
SetA64("0111111100>>>xxx100001xxxxxxxxxx", InstName.Sqshrun_S, InstEmit.Sqshrun_S, OpCodeSimdShImm.Create);
|
SetA64("0111111100>>>xxx100001xxxxxxxxxx", InstName.Sqshrun_S, InstEmit.Sqshrun_S, OpCodeSimdShImm.Create);
|
||||||
|
@ -746,7 +743,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
|
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
|
||||||
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
||||||
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
|
||||||
SetA32("<<<<01100010xxxxxxxx11110001xxxx", InstName.Qadd16, InstEmit32.Qadd16, OpCode32AluReg.Create);
|
|
||||||
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
|
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
|
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
|
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
|
||||||
|
@ -823,10 +819,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
||||||
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
||||||
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
|
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
|
||||||
SetA32("<<<<01100110xxxxxxxx11110001xxxx", InstName.Uqadd16, InstEmit32.Uqadd16, OpCode32AluReg.Create);
|
|
||||||
SetA32("<<<<01100110xxxxxxxx11111001xxxx", InstName.Uqadd8, InstEmit32.Uqadd8, OpCode32AluReg.Create);
|
|
||||||
SetA32("<<<<01100110xxxxxxxx11110111xxxx", InstName.Uqsub16, InstEmit32.Uqsub16, OpCode32AluReg.Create);
|
|
||||||
SetA32("<<<<01100110xxxxxxxx11111111xxxx", InstName.Uqsub8, InstEmit32.Uqsub8, OpCode32AluReg.Create);
|
|
||||||
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
|
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
|
||||||
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
||||||
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
||||||
|
@ -880,7 +872,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetVfp("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
SetVfp("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||||
SetVfp("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110110xxxx101x01x0xxxx", InstName.Vrintr, InstEmit32.Vrintr_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
|
||||||
SetVfp("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create, OpCode32SimdSel.CreateT32);
|
SetVfp("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create, OpCode32SimdSel.CreateT32);
|
||||||
|
@ -1001,7 +992,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32);
|
SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32);
|
||||||
SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100111x11<<00xxxx0110xxx0xxxx", InstName.Vpadal, InstEmit32.Vpadal, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
|
||||||
SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||||
SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
@ -1012,8 +1002,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||||
SetAsimd("111100110x01xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
|
||||||
SetAsimd("111100110x10xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
|
@ -1035,10 +1023,8 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
||||||
SetAsimd("111100111x11<<10xxxx001100x0xxxx", InstName.Vshll, InstEmit32.Vshll2, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); // A2 encoding.
|
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||||
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||||
|
@ -1063,7 +1049,6 @@ namespace ARMeilleure.Decoders
|
||||||
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
|
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
|
||||||
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
|
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
|
||||||
SetAsimd("111100111x110010xxxx00000xx0xxxx", InstName.Vswp, InstEmit32.Vswp, OpCode32Simd.Create, OpCode32Simd.CreateT32);
|
|
||||||
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
|
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||||
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
|
|
@ -2,8 +2,6 @@ using ARMeilleure.Decoders;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||||
|
@ -21,12 +19,6 @@ namespace ARMeilleure.Instructions
|
||||||
Operand n = GetAluN(context);
|
Operand n = GetAluN(context);
|
||||||
Operand m = GetAluM(context, setCarry: false);
|
Operand m = GetAluM(context, setCarry: false);
|
||||||
|
|
||||||
if (op.Rn == RegisterAlias.Aarch32Pc && op is OpCodeT32AluImm12)
|
|
||||||
{
|
|
||||||
// For ADR, PC is always 4 bytes aligned, even in Thumb mode.
|
|
||||||
n = context.BitwiseAnd(n, Const(~3u));
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand res = context.Add(n, m);
|
Operand res = context.Add(n, m);
|
||||||
|
|
||||||
if (ShouldSetFlags(context))
|
if (ShouldSetFlags(context))
|
||||||
|
@ -292,16 +284,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitAluStore(context, res);
|
EmitAluStore(context, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Qadd16(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rd, EmitSigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
|
||||||
{
|
|
||||||
EmitSaturateRange(context, d, context.Add(n, m), 16, unsigned: false, setQ: false);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Rbit(ArmEmitterContext context)
|
public static void Rbit(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
Operand m = GetAluM(context);
|
Operand m = GetAluM(context);
|
||||||
|
@ -485,12 +467,6 @@ namespace ARMeilleure.Instructions
|
||||||
Operand n = GetAluN(context);
|
Operand n = GetAluN(context);
|
||||||
Operand m = GetAluM(context, setCarry: false);
|
Operand m = GetAluM(context, setCarry: false);
|
||||||
|
|
||||||
if (op.Rn == RegisterAlias.Aarch32Pc && op is OpCodeT32AluImm12)
|
|
||||||
{
|
|
||||||
// For ADR, PC is always 4 bytes aligned, even in Thumb mode.
|
|
||||||
n = context.BitwiseAnd(n, Const(~3u));
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand res = context.Subtract(n, m);
|
Operand res = context.Subtract(n, m);
|
||||||
|
|
||||||
if (ShouldSetFlags(context))
|
if (ShouldSetFlags(context))
|
||||||
|
@ -570,46 +546,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitHsub8(context, unsigned: true);
|
EmitHsub8(context, unsigned: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Uqadd16(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
|
||||||
{
|
|
||||||
EmitSaturateUqadd(context, d, context.Add(n, m), 16);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Uqadd8(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
|
||||||
{
|
|
||||||
EmitSaturateUqadd(context, d, context.Add(n, m), 8);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Uqsub16(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
|
||||||
{
|
|
||||||
EmitSaturateUqsub(context, d, context.Subtract(n, m), 16);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Uqsub8(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
|
||||||
{
|
|
||||||
EmitSaturateUqsub(context, d, context.Subtract(n, m), 8);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Usat(ArmEmitterContext context)
|
public static void Usat(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||||
|
@ -986,251 +922,6 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitSaturateRange(ArmEmitterContext context, Operand result, Operand value, uint saturateTo, bool unsigned, bool setQ = true)
|
|
||||||
{
|
|
||||||
Debug.Assert(saturateTo <= 32);
|
|
||||||
Debug.Assert(!unsigned || saturateTo < 32);
|
|
||||||
|
|
||||||
if (!unsigned && saturateTo == 32)
|
|
||||||
{
|
|
||||||
// No saturation possible for this case.
|
|
||||||
|
|
||||||
context.Copy(result, value);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (saturateTo == 0)
|
|
||||||
{
|
|
||||||
// Result is always zero if we saturate 0 bits.
|
|
||||||
|
|
||||||
context.Copy(result, Const(0));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand satValue;
|
|
||||||
|
|
||||||
if (unsigned)
|
|
||||||
{
|
|
||||||
// Negative values always saturate (to zero).
|
|
||||||
// So we must always ignore the sign bit when masking, so that the truncated value will differ from the original one.
|
|
||||||
|
|
||||||
satValue = context.BitwiseAnd(value, Const((int)(uint.MaxValue >> (32 - (int)saturateTo))));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
satValue = context.ShiftLeft(value, Const(32 - (int)saturateTo));
|
|
||||||
satValue = context.ShiftRightSI(satValue, Const(32 - (int)saturateTo));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the result is 0, the values are equal and we don't need saturation.
|
|
||||||
Operand lblNoSat = Label();
|
|
||||||
context.BranchIfFalse(lblNoSat, context.Subtract(value, satValue));
|
|
||||||
|
|
||||||
// Saturate and set Q flag.
|
|
||||||
if (unsigned)
|
|
||||||
{
|
|
||||||
if (saturateTo == 31)
|
|
||||||
{
|
|
||||||
// Only saturation case possible when going from 32 bits signed to 32 or 31 bits unsigned
|
|
||||||
// is when the signed input is negative, as all positive values are representable on a 31 bits range.
|
|
||||||
|
|
||||||
satValue = Const(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
satValue = context.ShiftRightSI(value, Const(31));
|
|
||||||
satValue = context.BitwiseNot(satValue);
|
|
||||||
satValue = context.ShiftRightUI(satValue, Const(32 - (int)saturateTo));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (saturateTo == 1)
|
|
||||||
{
|
|
||||||
satValue = context.ShiftRightSI(value, Const(31));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
satValue = Const(uint.MaxValue >> (33 - (int)saturateTo));
|
|
||||||
satValue = context.BitwiseExclusiveOr(satValue, context.ShiftRightSI(value, Const(31)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setQ)
|
|
||||||
{
|
|
||||||
SetFlag(context, PState.QFlag, Const(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(result, satValue);
|
|
||||||
|
|
||||||
Operand lblExit = Label();
|
|
||||||
context.Branch(lblExit);
|
|
||||||
|
|
||||||
context.MarkLabel(lblNoSat);
|
|
||||||
|
|
||||||
context.Copy(result, value);
|
|
||||||
|
|
||||||
context.MarkLabel(lblExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
|
||||||
{
|
|
||||||
Debug.Assert(saturateTo <= 32);
|
|
||||||
|
|
||||||
if (saturateTo == 32)
|
|
||||||
{
|
|
||||||
// No saturation possible for this case.
|
|
||||||
|
|
||||||
context.Copy(result, value);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (saturateTo == 0)
|
|
||||||
{
|
|
||||||
// Result is always zero if we saturate 0 bits.
|
|
||||||
|
|
||||||
context.Copy(result, Const(0));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the result is 0, the values are equal and we don't need saturation.
|
|
||||||
Operand lblNoSat = Label();
|
|
||||||
context.BranchIfFalse(lblNoSat, context.ShiftRightUI(value, Const((int)saturateTo)));
|
|
||||||
|
|
||||||
// Saturate.
|
|
||||||
context.Copy(result, Const(uint.MaxValue >> (32 - (int)saturateTo)));
|
|
||||||
|
|
||||||
Operand lblExit = Label();
|
|
||||||
context.Branch(lblExit);
|
|
||||||
|
|
||||||
context.MarkLabel(lblNoSat);
|
|
||||||
|
|
||||||
context.Copy(result, value);
|
|
||||||
|
|
||||||
context.MarkLabel(lblExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSaturateUqsub(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
|
||||||
{
|
|
||||||
Debug.Assert(saturateTo <= 32);
|
|
||||||
|
|
||||||
if (saturateTo == 32)
|
|
||||||
{
|
|
||||||
// No saturation possible for this case.
|
|
||||||
|
|
||||||
context.Copy(result, value);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (saturateTo == 0)
|
|
||||||
{
|
|
||||||
// Result is always zero if we saturate 0 bits.
|
|
||||||
|
|
||||||
context.Copy(result, Const(0));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the result is 0, the values are equal and we don't need saturation.
|
|
||||||
Operand lblNoSat = Label();
|
|
||||||
context.BranchIf(lblNoSat, value, Const(0), Comparison.GreaterOrEqual);
|
|
||||||
|
|
||||||
// Saturate.
|
|
||||||
// Assumes that the value can only underflow, since this is only used for unsigned subtraction.
|
|
||||||
context.Copy(result, Const(0));
|
|
||||||
|
|
||||||
Operand lblExit = Label();
|
|
||||||
context.Branch(lblExit);
|
|
||||||
|
|
||||||
context.MarkLabel(lblNoSat);
|
|
||||||
|
|
||||||
context.Copy(result, value);
|
|
||||||
|
|
||||||
context.MarkLabel(lblExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitSigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
|
||||||
{
|
|
||||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
|
||||||
|
|
||||||
Operand tempN = context.SignExtend16(OperandType.I32, rn);
|
|
||||||
Operand tempM = context.SignExtend16(OperandType.I32, rm);
|
|
||||||
elementAction(tempD, tempN, tempM);
|
|
||||||
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
|
|
||||||
|
|
||||||
tempN = context.ShiftRightSI(rn, Const(16));
|
|
||||||
tempM = context.ShiftRightSI(rm, Const(16));
|
|
||||||
elementAction(tempD, tempN, tempM);
|
|
||||||
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
|
||||||
{
|
|
||||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
|
||||||
|
|
||||||
Operand tempN = context.ZeroExtend16(OperandType.I32, rn);
|
|
||||||
Operand tempM = context.ZeroExtend16(OperandType.I32, rm);
|
|
||||||
elementAction(tempD, tempN, tempM);
|
|
||||||
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
|
|
||||||
|
|
||||||
tempN = context.ShiftRightUI(rn, Const(16));
|
|
||||||
tempM = context.ShiftRightUI(rm, Const(16));
|
|
||||||
elementAction(tempD, tempN, tempM);
|
|
||||||
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitSigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
|
||||||
{
|
|
||||||
return Emit8BitPair(context, rn, rm, elementAction, unsigned: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitUnsigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
|
||||||
{
|
|
||||||
return Emit8BitPair(context, rn, rm, elementAction, unsigned: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand Emit8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction, bool unsigned)
|
|
||||||
{
|
|
||||||
Operand tempD = context.AllocateLocal(OperandType.I32);
|
|
||||||
Operand result = default;
|
|
||||||
|
|
||||||
for (int b = 0; b < 4; b++)
|
|
||||||
{
|
|
||||||
Operand nByte = b != 0 ? context.ShiftRightUI(rn, Const(b * 8)) : rn;
|
|
||||||
Operand mByte = b != 0 ? context.ShiftRightUI(rm, Const(b * 8)) : rm;
|
|
||||||
|
|
||||||
if (unsigned)
|
|
||||||
{
|
|
||||||
nByte = context.ZeroExtend8(OperandType.I32, nByte);
|
|
||||||
mByte = context.ZeroExtend8(OperandType.I32, mByte);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nByte = context.SignExtend8(OperandType.I32, nByte);
|
|
||||||
mByte = context.SignExtend8(OperandType.I32, mByte);
|
|
||||||
}
|
|
||||||
|
|
||||||
elementAction(tempD, nByte, mByte);
|
|
||||||
|
|
||||||
if (b == 0)
|
|
||||||
{
|
|
||||||
result = context.ZeroExtend8(OperandType.I32, tempD);
|
|
||||||
}
|
|
||||||
else if (b < 3)
|
|
||||||
{
|
|
||||||
result = context.BitwiseOr(result, context.ShiftLeft(context.ZeroExtend8(OperandType.I32, tempD), Const(b * 8)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = context.BitwiseOr(result, context.ShiftLeft(tempD, Const(24)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||||
{
|
{
|
||||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||||
|
|
|
@ -403,25 +403,19 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
return EmitHostMappedPointer(context, address);
|
return EmitHostMappedPointer(context, address);
|
||||||
}
|
}
|
||||||
else if (context.Memory.Type.IsHostTracked())
|
else if (context.Memory.Type == MemoryManagerType.HostTracked)
|
||||||
{
|
{
|
||||||
if (address.Type == OperandType.I32)
|
|
||||||
{
|
|
||||||
address = context.ZeroExtend32(OperandType.I64, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Memory.Type == MemoryManagerType.HostTracked)
|
|
||||||
{
|
|
||||||
Operand mask = Const(ulong.MaxValue >> (64 - context.Memory.AddressSpaceBits));
|
|
||||||
address = context.BitwiseAnd(address, mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand ptBase = !context.HasPtc
|
Operand ptBase = !context.HasPtc
|
||||||
? Const(context.Memory.PageTablePointer.ToInt64())
|
? Const(context.Memory.PageTablePointer.ToInt64())
|
||||||
: Const(context.Memory.PageTablePointer.ToInt64(), Ptc.PageTableSymbol);
|
: Const(context.Memory.PageTablePointer.ToInt64(), Ptc.PageTableSymbol);
|
||||||
|
|
||||||
Operand ptOffset = context.ShiftRightUI(address, Const(PageBits));
|
Operand ptOffset = context.ShiftRightUI(address, Const(PageBits));
|
||||||
|
|
||||||
|
if (ptOffset.Type == OperandType.I32)
|
||||||
|
{
|
||||||
|
ptOffset = context.ZeroExtend32(OperandType.I64, ptOffset);
|
||||||
|
}
|
||||||
|
|
||||||
return context.Add(address, context.Load(OperandType.I64, context.Add(ptBase, context.ShiftLeft(ptOffset, Const(3)))));
|
return context.Add(address, context.Load(OperandType.I64, context.Add(ptBase, context.ShiftLeft(ptOffset, Const(3)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2426,11 +2426,7 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
|
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
|
||||||
{
|
{
|
||||||
// RSQRTSS handles subnormals as zero, which differs from Arm, so we can't use it here.
|
Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtss, GetVec(op.Rn)), scalar: true);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Sqrtss, GetVec(op.Rn));
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Rcpss, res);
|
|
||||||
res = EmitSse41Round32Exp8OpF(context, res, scalar: true);
|
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
|
@ -2455,11 +2451,7 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
|
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
|
||||||
{
|
{
|
||||||
// RSQRTPS handles subnormals as zero, which differs from Arm, so we can't use it here.
|
Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtps, GetVec(op.Rn)), scalar: false);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Sqrtps, GetVec(op.Rn));
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Rcpps, res);
|
|
||||||
res = EmitSse41Round32Exp8OpF(context, res, scalar: false);
|
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1115,13 +1115,6 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Vpadal(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
EmitVectorPairwiseTernaryLongOpI32(context, (op1, op2, op3) => context.Add(context.Add(op1, op2), op3), op.Opc != 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vpaddl(ArmEmitterContext context)
|
public static void Vpaddl(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||||
|
@ -1246,33 +1239,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Vqrdmulh(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
|
||||||
int eSize = 8 << op.Size;
|
|
||||||
|
|
||||||
EmitVectorBinaryOpI32(context, (op1, op2) =>
|
|
||||||
{
|
|
||||||
if (op.Size == 2)
|
|
||||||
{
|
|
||||||
op1 = context.SignExtend32(OperandType.I64, op1);
|
|
||||||
op2 = context.SignExtend32(OperandType.I64, op2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand res = context.Multiply(op1, op2);
|
|
||||||
res = context.Add(res, Const(res.Type, 1L << (eSize - 2)));
|
|
||||||
res = context.ShiftRightSI(res, Const(eSize - 1));
|
|
||||||
res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
|
|
||||||
|
|
||||||
if (op.Size == 2)
|
|
||||||
{
|
|
||||||
res = context.ConvertI64ToI32(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}, signed: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vqsub(ArmEmitterContext context)
|
public static void Vqsub(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||||
|
|
|
@ -578,22 +578,6 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VRINTR (floating-point).
|
|
||||||
public static void Vrintr_S(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseAdvSimd)
|
|
||||||
{
|
|
||||||
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintiS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitScalarUnaryOpF32(context, (op1) =>
|
|
||||||
{
|
|
||||||
return EmitRoundByRMode(context, op1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VRINTZ (floating-point).
|
// VRINTZ (floating-point).
|
||||||
public static void Vrint_Z(ArmEmitterContext context)
|
public static void Vrint_Z(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
|
|
|
@ -673,35 +673,6 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVectorPairwiseTernaryLongOpI32(ArmEmitterContext context, Func3I emit, bool signed)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
int elems = op.GetBytesCount() >> op.Size;
|
|
||||||
int pairs = elems >> 1;
|
|
||||||
|
|
||||||
Operand res = GetVecA32(op.Qd);
|
|
||||||
|
|
||||||
for (int index = 0; index < pairs; index++)
|
|
||||||
{
|
|
||||||
int pairIndex = index * 2;
|
|
||||||
Operand m1 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex, op.Size, signed);
|
|
||||||
Operand m2 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex + 1, op.Size, signed);
|
|
||||||
|
|
||||||
if (op.Size == 2)
|
|
||||||
{
|
|
||||||
m1 = signed ? context.SignExtend32(OperandType.I64, m1) : context.ZeroExtend32(OperandType.I64, m1);
|
|
||||||
m2 = signed ? context.SignExtend32(OperandType.I64, m2) : context.ZeroExtend32(OperandType.I64, m2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand d1 = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size + 1, signed);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, emit(m1, m2, d1), op.Id + index, op.Size + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Narrow
|
// Narrow
|
||||||
|
|
||||||
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit, bool signed = false)
|
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit, bool signed = false)
|
||||||
|
|
|
@ -191,26 +191,6 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Vswp(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
if (op.Q)
|
|
||||||
{
|
|
||||||
Operand temp = context.Copy(GetVecA32(op.Qd));
|
|
||||||
|
|
||||||
context.Copy(GetVecA32(op.Qd), GetVecA32(op.Qm));
|
|
||||||
context.Copy(GetVecA32(op.Qm), temp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand temp = ExtractScalar(context, OperandType.I64, op.Vd);
|
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, ExtractScalar(context, OperandType.I64, op.Vm));
|
|
||||||
InsertScalar(context, op.Vm, temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vtbl(ArmEmitterContext context)
|
public static void Vtbl(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
||||||
|
|
|
@ -116,7 +116,7 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
else if (shift >= eSize)
|
else if (shift >= eSize)
|
||||||
{
|
{
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if ((op.RegisterSize == RegisterSize.Simd64))
|
||||||
{
|
{
|
||||||
Operand res = context.VectorZeroUpper64(GetVec(op.Rd));
|
Operand res = context.VectorZeroUpper64(GetVec(op.Rd));
|
||||||
|
|
||||||
|
@ -359,16 +359,6 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Sqshl_Si(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitShlImmOp(context, signedDst: true, ShlRegFlags.Signed | ShlRegFlags.Scalar | ShlRegFlags.Saturating);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sqshl_Vi(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitShlImmOp(context, signedDst: true, ShlRegFlags.Signed | ShlRegFlags.Saturating);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sqshrn_S(ArmEmitterContext context)
|
public static void Sqshrn_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.UseAdvSimd)
|
if (Optimizations.UseAdvSimd)
|
||||||
|
@ -1603,99 +1593,6 @@ namespace ARMeilleure.Instructions
|
||||||
Saturating = 1 << 3,
|
Saturating = 1 << 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitShlImmOp(ArmEmitterContext context, bool signedDst, ShlRegFlags flags = ShlRegFlags.None)
|
|
||||||
{
|
|
||||||
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
|
||||||
bool signed = flags.HasFlag(ShlRegFlags.Signed);
|
|
||||||
bool saturating = flags.HasFlag(ShlRegFlags.Saturating);
|
|
||||||
|
|
||||||
OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp;
|
|
||||||
|
|
||||||
Operand res = context.VectorZero();
|
|
||||||
|
|
||||||
int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
|
|
||||||
|
|
||||||
for (int index = 0; index < elems; index++)
|
|
||||||
{
|
|
||||||
Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size, signed);
|
|
||||||
|
|
||||||
Operand e = !saturating
|
|
||||||
? EmitShlImm(context, ne, GetImmShl(op), op.Size)
|
|
||||||
: EmitShlImmSatQ(context, ne, GetImmShl(op), op.Size, signed, signedDst);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, e, index, op.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitShlImm(ArmEmitterContext context, Operand op, int shiftLsB, int size)
|
|
||||||
{
|
|
||||||
int eSize = 8 << size;
|
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
|
||||||
|
|
||||||
Operand res = context.AllocateLocal(OperandType.I64);
|
|
||||||
|
|
||||||
if (shiftLsB >= eSize)
|
|
||||||
{
|
|
||||||
Operand shl = context.ShiftLeft(op, Const(shiftLsB));
|
|
||||||
context.Copy(res, shl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand zeroL = Const(0L);
|
|
||||||
context.Copy(res, zeroL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitShlImmSatQ(ArmEmitterContext context, Operand op, int shiftLsB, int size, bool signedSrc, bool signedDst)
|
|
||||||
{
|
|
||||||
int eSize = 8 << size;
|
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
|
||||||
|
|
||||||
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op);
|
|
||||||
|
|
||||||
if (shiftLsB >= eSize)
|
|
||||||
{
|
|
||||||
context.Copy(res, signedSrc
|
|
||||||
? EmitSignedSignSatQ(context, op, size)
|
|
||||||
: EmitUnsignedSignSatQ(context, op, size));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand shl = context.ShiftLeft(op, Const(shiftLsB));
|
|
||||||
if (eSize == 64)
|
|
||||||
{
|
|
||||||
Operand sarOrShr = signedSrc
|
|
||||||
? context.ShiftRightSI(shl, Const(shiftLsB))
|
|
||||||
: context.ShiftRightUI(shl, Const(shiftLsB));
|
|
||||||
context.Copy(res, shl);
|
|
||||||
context.BranchIf(lblEnd, sarOrShr, op, Comparison.Equal);
|
|
||||||
context.Copy(res, signedSrc
|
|
||||||
? EmitSignedSignSatQ(context, op, size)
|
|
||||||
: EmitUnsignedSignSatQ(context, op, size));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Copy(res, signedSrc
|
|
||||||
? EmitSignedSrcSatQ(context, shl, size, signedDst)
|
|
||||||
: EmitUnsignedSrcSatQ(context, shl, size, signedDst));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.MarkLabel(lblEnd);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitShlRegOp(ArmEmitterContext context, ShlRegFlags flags = ShlRegFlags.None)
|
private static void EmitShlRegOp(ArmEmitterContext context, ShlRegFlags flags = ShlRegFlags.None)
|
||||||
{
|
{
|
||||||
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
||||||
|
|
|
@ -106,38 +106,6 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Vshll2(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
Operand res = context.VectorZero();
|
|
||||||
|
|
||||||
int elems = op.GetBytesCount() >> op.Size;
|
|
||||||
|
|
||||||
for (int index = 0; index < elems; index++)
|
|
||||||
{
|
|
||||||
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
|
|
||||||
|
|
||||||
if (op.Size == 2)
|
|
||||||
{
|
|
||||||
if (op.U)
|
|
||||||
{
|
|
||||||
me = context.ZeroExtend32(OperandType.I64, me);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
me = context.SignExtend32(OperandType.I64, me);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
me = context.ShiftLeft(me, Const(8 << op.Size));
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vshr(ArmEmitterContext context)
|
public static void Vshr(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||||
|
@ -162,36 +130,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Vsli_I(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
|
||||||
int shift = op.Shift;
|
|
||||||
int eSize = 8 << op.Size;
|
|
||||||
|
|
||||||
ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL;
|
|
||||||
|
|
||||||
Operand res = GetVec(op.Qd);
|
|
||||||
|
|
||||||
int elems = op.GetBytesCount() >> op.Size;
|
|
||||||
|
|
||||||
for (int index = 0; index < elems; index++)
|
|
||||||
{
|
|
||||||
Operand me = EmitVectorExtractZx(context, op.Qm, op.Im + index, op.Size);
|
|
||||||
|
|
||||||
Operand neShifted = context.ShiftLeft(me, Const(shift));
|
|
||||||
|
|
||||||
Operand de = EmitVectorExtractZx(context, op.Qd, op.Id + index, op.Size);
|
|
||||||
|
|
||||||
Operand deMasked = context.BitwiseAnd(de, Const(mask));
|
|
||||||
|
|
||||||
Operand e = context.BitwiseOr(neShifted, deMasked);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, e, op.Id + index, op.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVec(op.Qd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vsra(ArmEmitterContext context)
|
public static void Vsra(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||||
|
|
|
@ -384,9 +384,7 @@ namespace ARMeilleure.Instructions
|
||||||
Sqrshrn_V,
|
Sqrshrn_V,
|
||||||
Sqrshrun_S,
|
Sqrshrun_S,
|
||||||
Sqrshrun_V,
|
Sqrshrun_V,
|
||||||
Sqshl_Si,
|
|
||||||
Sqshl_V,
|
Sqshl_V,
|
||||||
Sqshl_Vi,
|
|
||||||
Sqshrn_S,
|
Sqshrn_S,
|
||||||
Sqshrn_V,
|
Sqshrn_V,
|
||||||
Sqshrun_S,
|
Sqshrun_S,
|
||||||
|
@ -527,7 +525,6 @@ namespace ARMeilleure.Instructions
|
||||||
Pld,
|
Pld,
|
||||||
Pop,
|
Pop,
|
||||||
Push,
|
Push,
|
||||||
Qadd16,
|
|
||||||
Rev,
|
Rev,
|
||||||
Revsh,
|
Revsh,
|
||||||
Rsb,
|
Rsb,
|
||||||
|
@ -572,10 +569,6 @@ namespace ARMeilleure.Instructions
|
||||||
Umaal,
|
Umaal,
|
||||||
Umlal,
|
Umlal,
|
||||||
Umull,
|
Umull,
|
||||||
Uqadd16,
|
|
||||||
Uqadd8,
|
|
||||||
Uqsub16,
|
|
||||||
Uqsub8,
|
|
||||||
Usat,
|
Usat,
|
||||||
Usat16,
|
Usat16,
|
||||||
Usub8,
|
Usub8,
|
||||||
|
@ -642,7 +635,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vorn,
|
Vorn,
|
||||||
Vorr,
|
Vorr,
|
||||||
Vpadd,
|
Vpadd,
|
||||||
Vpadal,
|
|
||||||
Vpaddl,
|
Vpaddl,
|
||||||
Vpmax,
|
Vpmax,
|
||||||
Vpmin,
|
Vpmin,
|
||||||
|
@ -650,7 +642,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vqdmulh,
|
Vqdmulh,
|
||||||
Vqmovn,
|
Vqmovn,
|
||||||
Vqmovun,
|
Vqmovun,
|
||||||
Vqrdmulh,
|
|
||||||
Vqrshrn,
|
Vqrshrn,
|
||||||
Vqrshrun,
|
Vqrshrun,
|
||||||
Vqshrn,
|
Vqshrn,
|
||||||
|
@ -663,7 +654,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vrintm,
|
Vrintm,
|
||||||
Vrintn,
|
Vrintn,
|
||||||
Vrintp,
|
Vrintp,
|
||||||
Vrintr,
|
|
||||||
Vrintx,
|
Vrintx,
|
||||||
Vrshr,
|
Vrshr,
|
||||||
Vrshrn,
|
Vrshrn,
|
||||||
|
@ -672,7 +662,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vshll,
|
Vshll,
|
||||||
Vshr,
|
Vshr,
|
||||||
Vshrn,
|
Vshrn,
|
||||||
Vsli,
|
|
||||||
Vst1,
|
Vst1,
|
||||||
Vst2,
|
Vst2,
|
||||||
Vst3,
|
Vst3,
|
||||||
|
@ -689,7 +678,6 @@ namespace ARMeilleure.Instructions
|
||||||
Vsub,
|
Vsub,
|
||||||
Vsubl,
|
Vsubl,
|
||||||
Vsubw,
|
Vsubw,
|
||||||
Vswp,
|
|
||||||
Vtbl,
|
Vtbl,
|
||||||
Vtrn,
|
Vtrn,
|
||||||
Vtst,
|
Vtst,
|
||||||
|
|
|
@ -91,54 +91,54 @@ namespace ARMeilleure.Instructions
|
||||||
#region "Read"
|
#region "Read"
|
||||||
public static byte ReadByte(ulong address)
|
public static byte ReadByte(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<byte>(address);
|
return GetMemoryManager().ReadTracked<byte>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ushort ReadUInt16(ulong address)
|
public static ushort ReadUInt16(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<ushort>(address);
|
return GetMemoryManager().ReadTracked<ushort>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint ReadUInt32(ulong address)
|
public static uint ReadUInt32(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<uint>(address);
|
return GetMemoryManager().ReadTracked<uint>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong ReadUInt64(ulong address)
|
public static ulong ReadUInt64(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<ulong>(address);
|
return GetMemoryManager().ReadTracked<ulong>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static V128 ReadVector128(ulong address)
|
public static V128 ReadVector128(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<V128>(address);
|
return GetMemoryManager().ReadTracked<V128>(address);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Write"
|
#region "Write"
|
||||||
public static void WriteByte(ulong address, byte value)
|
public static void WriteByte(ulong address, byte value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().Write(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteUInt16(ulong address, ushort value)
|
public static void WriteUInt16(ulong address, ushort value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().Write(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteUInt32(ulong address, uint value)
|
public static void WriteUInt32(ulong address, uint value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().Write(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteUInt64(ulong address, ulong value)
|
public static void WriteUInt64(ulong address, ulong value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().Write(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteVector128(ulong address, V128 value)
|
public static void WriteVector128(ulong address, V128 value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().Write(address, value);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,7 @@ namespace ARMeilleure.Memory
|
||||||
{
|
{
|
||||||
IJitMemoryBlock Allocate(ulong size);
|
IJitMemoryBlock Allocate(ulong size);
|
||||||
IJitMemoryBlock Reserve(ulong size);
|
IJitMemoryBlock Reserve(ulong size);
|
||||||
|
|
||||||
|
ulong GetPageSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,17 +28,6 @@ namespace ARMeilleure.Memory
|
||||||
/// <returns>The data</returns>
|
/// <returns>The data</returns>
|
||||||
T ReadTracked<T>(ulong va) where T : unmanaged;
|
T ReadTracked<T>(ulong va) where T : unmanaged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads data from CPU mapped memory, from guest code. (with read tracking)
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data being read</typeparam>
|
|
||||||
/// <param name="va">Virtual address of the data in memory</param>
|
|
||||||
/// <returns>The data</returns>
|
|
||||||
T ReadGuest<T>(ulong va) where T : unmanaged
|
|
||||||
{
|
|
||||||
return ReadTracked<T>(va);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes data to CPU mapped memory.
|
/// Writes data to CPU mapped memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -47,17 +36,6 @@ namespace ARMeilleure.Memory
|
||||||
/// <param name="value">Data to be written</param>
|
/// <param name="value">Data to be written</param>
|
||||||
void Write<T>(ulong va, T value) where T : unmanaged;
|
void Write<T>(ulong va, T value) where T : unmanaged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes data to CPU mapped memory, from guest code.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data being written</typeparam>
|
|
||||||
/// <param name="va">Virtual address to write the data into</param>
|
|
||||||
/// <param name="value">Data to be written</param>
|
|
||||||
void WriteGuest<T>(ulong va, T value) where T : unmanaged
|
|
||||||
{
|
|
||||||
Write(va, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a read-only span of data from CPU mapped memory.
|
/// Gets a read-only span of data from CPU mapped memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -35,29 +35,18 @@ namespace ARMeilleure.Memory
|
||||||
/// Allows invalid access from JIT code to the rest of the program, but is faster.
|
/// Allows invalid access from JIT code to the rest of the program, but is faster.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
HostMappedUnsafe,
|
HostMappedUnsafe,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// High level implementation using a software flat page table for address translation
|
|
||||||
/// without masking the address and no support for handling invalid or non-contiguous memory access.
|
|
||||||
/// </summary>
|
|
||||||
HostTrackedUnsafe,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MemoryManagerTypeExtensions
|
static class MemoryManagerTypeExtensions
|
||||||
{
|
{
|
||||||
public static bool IsHostMapped(this MemoryManagerType type)
|
public static bool IsHostMapped(this MemoryManagerType type)
|
||||||
{
|
{
|
||||||
return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
|
return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsHostTracked(this MemoryManagerType type)
|
|
||||||
{
|
|
||||||
return type == MemoryManagerType.HostTracked || type == MemoryManagerType.HostTrackedUnsafe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
||||||
{
|
{
|
||||||
return type.IsHostMapped() || type.IsHostTracked();
|
return type == MemoryManagerType.HostTracked || type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,63 @@
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
|
using ARMeilleure.Translation.Cache;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
namespace ARMeilleure.Signal
|
||||||
{
|
{
|
||||||
public static class NativeSignalHandlerGenerator
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
struct SignalHandlerRange
|
||||||
{
|
{
|
||||||
public const int MaxTrackedRanges = 8;
|
public int IsActive;
|
||||||
|
public nuint RangeAddress;
|
||||||
|
public nuint RangeEndAddress;
|
||||||
|
public IntPtr ActionPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
struct SignalHandlerConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
|
||||||
|
/// </summary>
|
||||||
|
public int StructAddressOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
|
||||||
|
/// </summary>
|
||||||
|
public int StructWriteOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sigaction handler that was registered before this one. (unix only)
|
||||||
|
/// </summary>
|
||||||
|
public nuint UnixOldSigaction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of the previous sigaction. True for the 3 argument variant. (unix only)
|
||||||
|
/// </summary>
|
||||||
|
public int UnixOldSigaction3Arg;
|
||||||
|
|
||||||
|
public SignalHandlerRange Range0;
|
||||||
|
public SignalHandlerRange Range1;
|
||||||
|
public SignalHandlerRange Range2;
|
||||||
|
public SignalHandlerRange Range3;
|
||||||
|
public SignalHandlerRange Range4;
|
||||||
|
public SignalHandlerRange Range5;
|
||||||
|
public SignalHandlerRange Range6;
|
||||||
|
public SignalHandlerRange Range7;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NativeSignalHandler
|
||||||
|
{
|
||||||
|
private delegate void UnixExceptionHandler(int sig, IntPtr info, IntPtr ucontext);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||||
|
private delegate int VectoredExceptionHandler(IntPtr exceptionInfo);
|
||||||
|
|
||||||
|
private const int MaxTrackedRanges = 8;
|
||||||
|
|
||||||
private const int StructAddressOffset = 0;
|
private const int StructAddressOffset = 0;
|
||||||
private const int StructWriteOffset = 4;
|
private const int StructWriteOffset = 4;
|
||||||
|
@ -21,7 +70,124 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
||||||
|
|
||||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize)
|
private static ulong _pageSize;
|
||||||
|
private static ulong _pageMask;
|
||||||
|
|
||||||
|
private static readonly IntPtr _handlerConfig;
|
||||||
|
private static IntPtr _signalHandlerPtr;
|
||||||
|
private static IntPtr _signalHandlerHandle;
|
||||||
|
|
||||||
|
private static readonly object _lock = new();
|
||||||
|
private static bool _initialized;
|
||||||
|
|
||||||
|
static NativeSignalHandler()
|
||||||
|
{
|
||||||
|
_handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
|
||||||
|
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||||
|
|
||||||
|
config = new SignalHandlerConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
|
{
|
||||||
|
JitCache.Initialize(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
|
||||||
|
{
|
||||||
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pageSize = pageSize;
|
||||||
|
_pageMask = pageSize - 1;
|
||||||
|
|
||||||
|
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||||
|
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||||
|
{
|
||||||
|
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
||||||
|
|
||||||
|
if (customSignalHandlerFactory != null)
|
||||||
|
{
|
||||||
|
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||||
|
|
||||||
|
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
|
||||||
|
config.UnixOldSigaction3Arg = old.sa_flags & 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.StructAddressOffset = 40; // ExceptionInformation1
|
||||||
|
config.StructWriteOffset = 32; // ExceptionInformation0
|
||||||
|
|
||||||
|
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateWindowsSignalHandler(_handlerConfig));
|
||||||
|
|
||||||
|
if (customSignalHandlerFactory != null)
|
||||||
|
{
|
||||||
|
_signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe ref SignalHandlerConfig GetConfigRef()
|
||||||
|
{
|
||||||
|
return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
|
||||||
|
{
|
||||||
|
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
||||||
|
|
||||||
|
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||||
|
{
|
||||||
|
if (ranges[i].IsActive == 0)
|
||||||
|
{
|
||||||
|
ranges[i].RangeAddress = address;
|
||||||
|
ranges[i].RangeEndAddress = endAddress;
|
||||||
|
ranges[i].ActionPointer = action;
|
||||||
|
ranges[i].IsActive = 1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe bool RemoveTrackedRegion(nuint address)
|
||||||
|
{
|
||||||
|
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
||||||
|
|
||||||
|
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||||
|
{
|
||||||
|
if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
|
||||||
|
{
|
||||||
|
ranges[i].IsActive = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite)
|
||||||
{
|
{
|
||||||
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
||||||
context.Copy(inRegionLocal, Const(0));
|
context.Copy(inRegionLocal, Const(0));
|
||||||
|
@ -30,7 +196,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||||
{
|
{
|
||||||
ulong rangeBaseOffset = (ulong)(RangeOffset + i * rangeStructSize);
|
ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf<SignalHandlerRange>());
|
||||||
|
|
||||||
Operand nextLabel = Label();
|
Operand nextLabel = Label();
|
||||||
|
|
||||||
|
@ -44,12 +210,13 @@ namespace ARMeilleure.Signal
|
||||||
// Is the fault address within this tracked region?
|
// Is the fault address within this tracked region?
|
||||||
Operand inRange = context.BitwiseAnd(
|
Operand inRange = context.BitwiseAnd(
|
||||||
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
|
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
|
||||||
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI));
|
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI)
|
||||||
|
);
|
||||||
|
|
||||||
// Only call tracking if in range.
|
// Only call tracking if in range.
|
||||||
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
|
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
|
||||||
|
|
||||||
Operand offset = context.Subtract(faultAddress, rangeAddress);
|
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~_pageMask));
|
||||||
|
|
||||||
// Call the tracking action, with the pointer's relative offset to the base address.
|
// Call the tracking action, with the pointer's relative offset to the base address.
|
||||||
Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20));
|
Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20));
|
||||||
|
@ -60,10 +227,8 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
// Tracking action should be non-null to call it, otherwise assume false return.
|
// Tracking action should be non-null to call it, otherwise assume false return.
|
||||||
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
||||||
Operand result = context.Call(trackingActionPtr, OperandType.I64, offset, Const(1UL), isWrite);
|
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite);
|
||||||
context.Copy(inRegionLocal, context.ICompareNotEqual(result, Const(0UL)));
|
context.Copy(inRegionLocal, result);
|
||||||
|
|
||||||
GenerateFaultAddressPatchCode(context, faultAddress, result);
|
|
||||||
|
|
||||||
context.MarkLabel(skipActionLabel);
|
context.MarkLabel(skipActionLabel);
|
||||||
|
|
||||||
|
@ -104,7 +269,8 @@ namespace ARMeilleure.Signal
|
||||||
Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset)));
|
Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset)));
|
||||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||||
}
|
}
|
||||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
|
||||||
|
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
{
|
{
|
||||||
const ulong ErrOffset = 4; // __es.__err
|
const ulong ErrOffset = 4; // __es.__err
|
||||||
Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset)));
|
Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset)));
|
||||||
|
@ -144,7 +310,8 @@ namespace ARMeilleure.Signal
|
||||||
Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul)));
|
Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul)));
|
||||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||||
}
|
}
|
||||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
|
||||||
|
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
{
|
{
|
||||||
const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR]
|
const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR]
|
||||||
Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset)));
|
Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset)));
|
||||||
|
@ -155,7 +322,7 @@ namespace ARMeilleure.Signal
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize)
|
private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
@ -168,7 +335,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||||
|
|
||||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize);
|
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
||||||
|
|
||||||
Operand endLabel = Label();
|
Operand endLabel = Label();
|
||||||
|
|
||||||
|
@ -200,10 +367,10 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
|
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<UnixExceptionHandler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize)
|
private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
@ -232,7 +399,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||||
|
|
||||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize);
|
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
||||||
|
|
||||||
Operand endLabel = Label();
|
Operand endLabel = Label();
|
||||||
|
|
||||||
|
@ -254,88 +421,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<VectoredExceptionHandler>();
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateFaultAddressPatchCode(EmitterContext context, Operand faultAddress, Operand newAddress)
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
|
||||||
{
|
|
||||||
if (SupportsFaultAddressPatchingForHostOs())
|
|
||||||
{
|
|
||||||
Operand lblSkip = Label();
|
|
||||||
|
|
||||||
context.BranchIf(lblSkip, faultAddress, newAddress, Comparison.Equal);
|
|
||||||
|
|
||||||
Operand ucontextPtr = context.LoadArgument(OperandType.I64, 2);
|
|
||||||
Operand pcCtxAddress = default;
|
|
||||||
ulong baseRegsOffset = 0;
|
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
pcCtxAddress = context.Add(ucontextPtr, Const(440UL));
|
|
||||||
baseRegsOffset = 184UL;
|
|
||||||
}
|
|
||||||
else if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
|
||||||
{
|
|
||||||
ucontextPtr = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(48UL)));
|
|
||||||
|
|
||||||
pcCtxAddress = context.Add(ucontextPtr, Const(272UL));
|
|
||||||
baseRegsOffset = 16UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand pc = context.Load(OperandType.I64, pcCtxAddress);
|
|
||||||
|
|
||||||
Operand reg = GetAddressRegisterFromArm64Instruction(context, pc);
|
|
||||||
Operand reg64 = context.ZeroExtend32(OperandType.I64, reg);
|
|
||||||
Operand regCtxAddress = context.Add(ucontextPtr, context.Add(context.ShiftLeft(reg64, Const(3)), Const(baseRegsOffset)));
|
|
||||||
Operand regAddress = context.Load(OperandType.I64, regCtxAddress);
|
|
||||||
|
|
||||||
Operand addressDelta = context.Subtract(regAddress, faultAddress);
|
|
||||||
|
|
||||||
context.Store(regCtxAddress, context.Add(newAddress, addressDelta));
|
|
||||||
|
|
||||||
context.MarkLabel(lblSkip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand GetAddressRegisterFromArm64Instruction(EmitterContext context, Operand pc)
|
|
||||||
{
|
|
||||||
Operand inst = context.Load(OperandType.I32, pc);
|
|
||||||
Operand reg = context.AllocateLocal(OperandType.I32);
|
|
||||||
|
|
||||||
Operand isSysInst = context.ICompareEqual(context.BitwiseAnd(inst, Const(0xFFF80000)), Const(0xD5080000));
|
|
||||||
|
|
||||||
Operand lblSys = Label();
|
|
||||||
Operand lblEnd = Label();
|
|
||||||
|
|
||||||
context.BranchIfTrue(lblSys, isSysInst, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
context.Copy(reg, context.BitwiseAnd(context.ShiftRightUI(inst, Const(5)), Const(0x1F)));
|
|
||||||
context.Branch(lblEnd);
|
|
||||||
|
|
||||||
context.MarkLabel(lblSys);
|
|
||||||
context.Copy(reg, context.BitwiseAnd(inst, Const(0x1F)));
|
|
||||||
|
|
||||||
context.MarkLabel(lblEnd);
|
|
||||||
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool SupportsFaultAddressPatchingForHost()
|
|
||||||
{
|
|
||||||
return SupportsFaultAddressPatchingForHostArch() && SupportsFaultAddressPatchingForHostOs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool SupportsFaultAddressPatchingForHostArch()
|
|
||||||
{
|
|
||||||
return RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool SupportsFaultAddressPatchingForHostOs()
|
|
||||||
{
|
|
||||||
return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Signal
|
namespace ARMeilleure.Signal
|
||||||
{
|
{
|
||||||
static partial class UnixSignalHandlerRegistration
|
static partial class UnixSignalHandlerRegistration
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
namespace ARMeilleure.Signal
|
||||||
|
@ -10,28 +10,8 @@ namespace ARMeilleure.Signal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Methods to handle signals caused by partial unmaps. See the structs for C# implementations of the methods.
|
/// Methods to handle signals caused by partial unmaps. See the structs for C# implementations of the methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static partial class WindowsPartialUnmapHandler
|
internal static class WindowsPartialUnmapHandler
|
||||||
{
|
{
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
|
||||||
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
|
||||||
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
|
||||||
|
|
||||||
private static IntPtr _getCurrentThreadIdPtr;
|
|
||||||
|
|
||||||
public static IntPtr GetCurrentThreadIdFunc()
|
|
||||||
{
|
|
||||||
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
IntPtr handle = LoadLibrary("kernel32.dll");
|
|
||||||
|
|
||||||
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _getCurrentThreadIdPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
||||||
{
|
{
|
||||||
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
||||||
|
@ -40,7 +20,7 @@ namespace ARMeilleure.Signal
|
||||||
// Get the lock first.
|
// Get the lock first.
|
||||||
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||||
|
|
||||||
IntPtr getCurrentThreadId = GetCurrentThreadIdFunc();
|
IntPtr getCurrentThreadId = WindowsSignalHandlerRegistration.GetCurrentThreadIdFunc();
|
||||||
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
||||||
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
||||||
|
|
||||||
|
@ -157,6 +137,17 @@ namespace ARMeilleure.Signal
|
||||||
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable IDE0051 // Remove unused private member
|
||||||
|
private static void EmitThreadLocalMapIntRelease(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand index)
|
||||||
|
{
|
||||||
|
Operand offset = context.Multiply(index, Const(sizeof(int)));
|
||||||
|
Operand idsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.ThreadIdsOffset));
|
||||||
|
Operand idPtr = context.Add(idsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||||
|
|
||||||
|
context.CompareAndSwap(idPtr, threadId, Const(0));
|
||||||
|
}
|
||||||
|
#pragma warning restore IDE0051
|
||||||
|
|
||||||
private static void EmitAtomicAddI32(EmitterContext context, Operand ptr, Operand additive)
|
private static void EmitAtomicAddI32(EmitterContext context, Operand ptr, Operand additive)
|
||||||
{
|
{
|
||||||
Operand loop = Label();
|
Operand loop = Label();
|
||||||
|
|
44
src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs
Normal file
44
src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Signal
|
||||||
|
{
|
||||||
|
unsafe partial class WindowsSignalHandlerRegistration
|
||||||
|
{
|
||||||
|
[LibraryImport("kernel32.dll")]
|
||||||
|
private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
||||||
|
|
||||||
|
[LibraryImport("kernel32.dll")]
|
||||||
|
private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
||||||
|
|
||||||
|
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||||
|
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||||
|
|
||||||
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
|
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||||
|
|
||||||
|
private static IntPtr _getCurrentThreadIdPtr;
|
||||||
|
|
||||||
|
public static IntPtr RegisterExceptionHandler(IntPtr action)
|
||||||
|
{
|
||||||
|
return AddVectoredExceptionHandler(1, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool RemoveExceptionHandler(IntPtr handle)
|
||||||
|
{
|
||||||
|
return RemoveVectoredExceptionHandler(handle) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr GetCurrentThreadIdFunc()
|
||||||
|
{
|
||||||
|
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
IntPtr handle = LoadLibrary("kernel32.dll");
|
||||||
|
|
||||||
|
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _getCurrentThreadIdPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -117,15 +117,8 @@ namespace ARMeilleure.Translation.Cache
|
||||||
if (OperatingSystem.IsIOS())
|
if (OperatingSystem.IsIOS())
|
||||||
{
|
{
|
||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||||
if (deferProtect)
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
{
|
JitSupportDarwinAot.Invalidate(funcPtr, (ulong)code.Length);
|
||||||
_deferredRxProtect.Enqueue((funcOffset, code.Length));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
|
||||||
JitSupportDarwinAot.Invalidate(funcPtr, (ulong)code.Length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsMacOS()&& RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
else if (OperatingSystem.IsMacOS()&& RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
|
||||||
private int[] _postOrderMap;
|
private int[] _postOrderMap;
|
||||||
|
|
||||||
public int LocalsCount { get; private set; }
|
public int LocalsCount { get; private set; }
|
||||||
public BasicBlock Entry { get; private set; }
|
public BasicBlock Entry { get; }
|
||||||
public IntrusiveList<BasicBlock> Blocks { get; }
|
public IntrusiveList<BasicBlock> Blocks { get; }
|
||||||
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
||||||
public int[] PostOrderMap => _postOrderMap;
|
public int[] PostOrderMap => _postOrderMap;
|
||||||
|
@ -34,15 +34,6 @@ namespace ARMeilleure.Translation
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateEntry(BasicBlock newEntry)
|
|
||||||
{
|
|
||||||
newEntry.AddSuccessor(Entry);
|
|
||||||
|
|
||||||
Entry = newEntry;
|
|
||||||
Blocks.AddFirst(newEntry);
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
RemoveUnreachableBlocks(Blocks);
|
RemoveUnreachableBlocks(Blocks);
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 5518; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@ -858,14 +858,8 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
foreach (var thread in threads)
|
threads.ForEach((thread) => thread.Start());
|
||||||
{
|
threads.ForEach((thread) => thread.Join());
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
foreach (var thread in threads)
|
|
||||||
{
|
|
||||||
thread.Join();
|
|
||||||
}
|
|
||||||
|
|
||||||
threads.Clear();
|
threads.Clear();
|
||||||
|
|
||||||
|
|
|
@ -89,17 +89,6 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
||||||
{
|
{
|
||||||
if (cfg.Entry.Predecessors.Count != 0)
|
|
||||||
{
|
|
||||||
// We expect the entry block to have no predecessors.
|
|
||||||
// This is required because we have a implicit context load at the start of the function,
|
|
||||||
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
|
||||||
// Here we insert a new entry block that will jump to the existing entry block.
|
|
||||||
BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count);
|
|
||||||
|
|
||||||
cfg.UpdateEntry(newEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute local register inputs and outputs used inside blocks.
|
// Compute local register inputs and outputs used inside blocks.
|
||||||
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
||||||
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||||
|
@ -212,7 +201,7 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
// The only block without any predecessor should be the entry block.
|
// The only block without any predecessor should be the entry block.
|
||||||
// It always needs a context load as it is the first block to run.
|
// It always needs a context load as it is the first block to run.
|
||||||
if (block == cfg.Entry || hasContextLoad)
|
if (block.Predecessors.Count == 0 || hasContextLoad)
|
||||||
{
|
{
|
||||||
long vecMask = globalInputs[block.Index].VecMask;
|
long vecMask = globalInputs[block.Index].VecMask;
|
||||||
long intMask = globalInputs[block.Index].IntMask;
|
long intMask = globalInputs[block.Index].IntMask;
|
||||||
|
|
|
@ -57,6 +57,9 @@ namespace ARMeilleure.Translation
|
||||||
private Thread[] _backgroundTranslationThreads;
|
private Thread[] _backgroundTranslationThreads;
|
||||||
private volatile int _threadCount;
|
private volatile int _threadCount;
|
||||||
|
|
||||||
|
// FIXME: Remove this once the init logic of the emulator will be redone.
|
||||||
|
public static readonly ManualResetEvent IsReadyForTranslation = new(false);
|
||||||
|
|
||||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
||||||
{
|
{
|
||||||
_allocator = allocator;
|
_allocator = allocator;
|
||||||
|
@ -76,6 +79,11 @@ namespace ARMeilleure.Translation
|
||||||
Stubs = new TranslatorStubs(FunctionTable);
|
Stubs = new TranslatorStubs(FunctionTable);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
|
|
||||||
|
if (memory.Type.IsHostMappedOrTracked())
|
||||||
|
{
|
||||||
|
NativeSignalHandler.InitializeSignalHandler(allocator.GetPageSize());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||||
|
@ -97,6 +105,8 @@ namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
if (Interlocked.Increment(ref _threadCount) == 1)
|
||||||
{
|
{
|
||||||
|
IsReadyForTranslation.WaitOne();
|
||||||
|
|
||||||
if (_ptc.State == PtcState.Enabled)
|
if (_ptc.State == PtcState.Enabled)
|
||||||
{
|
{
|
||||||
Debug.Assert(Functions.Count == 0);
|
Debug.Assert(Functions.Count == 0);
|
||||||
|
|
|
@ -80,10 +80,7 @@ namespace ARMeilleure.Translation
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_disposed)
|
Monitor.Wait(Sync);
|
||||||
{
|
|
||||||
Monitor.Wait(Sync);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
//
|
|
||||||
// MeloNX.xcconfig
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 06/03/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Configuration settings file format documentation can be found at:
|
|
||||||
// https://help.apple.com/xcode/#/dev745c5c974
|
|
||||||
|
|
||||||
VERSION = 2.0.1
|
|
|
@ -24,8 +24,7 @@
|
||||||
/* End PBXAggregateTarget section */
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
4549A31C2DD8795900EC8D88 /* CocoaAsyncSocket in Frameworks */ = {isa = PBXBuildFile; productRef = 4549A31B2DD8795900EC8D88 /* CocoaAsyncSocket */; };
|
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */ = {isa = PBXBuildFile; productRef = 4E0DED332D05695D00FEF007 /* SwiftUIJoystick */; };
|
||||||
4E12B23C2D797CFA00FB2271 /* MeloNX.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */; };
|
|
||||||
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; };
|
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; };
|
||||||
4EA5AE822D16807500AD0B9F /* SwiftSVG in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA5AE812D16807500AD0B9F /* SwiftSVG */; };
|
4EA5AE822D16807500AD0B9F /* SwiftSVG in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA5AE812D16807500AD0B9F /* SwiftSVG */; };
|
||||||
CA8F9C322D3F5AB200D7E586 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; };
|
CA8F9C322D3F5AB200D7E586 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; };
|
||||||
|
@ -46,13 +45,6 @@
|
||||||
remoteGlobalIDString = 4E80A98C2CD6F54500029585;
|
remoteGlobalIDString = 4E80A98C2CD6F54500029585;
|
||||||
remoteInfo = MeloNX;
|
remoteInfo = MeloNX;
|
||||||
};
|
};
|
||||||
4EFFCD182DFB766F00F78EA6 /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = 4E80A9852CD6F54500029585 /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = BD43C6212D1B248D003BBC42;
|
|
||||||
remoteInfo = com.Stossy11.MeloNX.RyujinxAg;
|
|
||||||
};
|
|
||||||
BD43C6252D1B249E003BBC42 /* PBXContainerItemProxy */ = {
|
BD43C6252D1B249E003BBC42 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 4E80A9852CD6F54500029585 /* Project object */;
|
containerPortal = 4E80A9852CD6F54500029585 /* Project object */;
|
||||||
|
@ -86,12 +78,12 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = MeloNX.xcconfig; sourceTree = "<group>"; };
|
|
||||||
4E7023A52D5A98E2002C7183 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
4E7023A52D5A98E2002C7183 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||||
4E80A98D2CD6F54500029585 /* MeloNX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MeloNX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
4E80A98D2CD6F54500029585 /* MeloNX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MeloNX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4E80A99D2CD6F54700029585 /* MeloNXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeloNXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
4E80A99D2CD6F54700029585 /* MeloNXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeloNXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4E80A9A72CD6F54700029585 /* MeloNXUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeloNXUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
4E80A9A72CD6F54700029585 /* MeloNXUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeloNXUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4E80AA622CD7122800029585 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
|
4E80AA622CD7122800029585 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
|
||||||
|
5650564A2D2A758600C8BB1E /* dotnet.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = dotnet.xcconfig; sourceTree = "<group>"; };
|
||||||
BD43C6282D1B2514003BBC42 /* Ryujinx.Headless.SDL2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = Ryujinx.Headless.SDL2.dylib; path = "MeloNX/Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib"; sourceTree = "<group>"; };
|
BD43C6282D1B2514003BBC42 /* Ryujinx.Headless.SDL2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = Ryujinx.Headless.SDL2.dylib; path = "MeloNX/Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
@ -116,11 +108,7 @@
|
||||||
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = (
|
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = (
|
||||||
CodeSignOnCopy,
|
CodeSignOnCopy,
|
||||||
);
|
);
|
||||||
"Dependencies/Dynamic Libraries/RyujinxHelper.framework" = (
|
"Dependencies/Dynamic Libraries/RyujinxKeyboard.framework" = (
|
||||||
CodeSignOnCopy,
|
|
||||||
RemoveHeadersOnCopy,
|
|
||||||
);
|
|
||||||
"Dependencies/Dynamic Libraries/StosJIT.framework" = (
|
|
||||||
CodeSignOnCopy,
|
CodeSignOnCopy,
|
||||||
RemoveHeadersOnCopy,
|
RemoveHeadersOnCopy,
|
||||||
);
|
);
|
||||||
|
@ -133,6 +121,10 @@
|
||||||
"Dependencies/Dynamic Libraries/libavutil.dylib" = (
|
"Dependencies/Dynamic Libraries/libavutil.dylib" = (
|
||||||
CodeSignOnCopy,
|
CodeSignOnCopy,
|
||||||
);
|
);
|
||||||
|
Dependencies/XCFrameworks/MoltenVK.xcframework = (
|
||||||
|
CodeSignOnCopy,
|
||||||
|
RemoveHeadersOnCopy,
|
||||||
|
);
|
||||||
Dependencies/XCFrameworks/SDL2.xcframework = (
|
Dependencies/XCFrameworks/SDL2.xcframework = (
|
||||||
CodeSignOnCopy,
|
CodeSignOnCopy,
|
||||||
RemoveHeadersOnCopy,
|
RemoveHeadersOnCopy,
|
||||||
|
@ -177,8 +169,7 @@
|
||||||
"Dependencies/Dynamic Libraries/libavutil.dylib",
|
"Dependencies/Dynamic Libraries/libavutil.dylib",
|
||||||
"Dependencies/Dynamic Libraries/libMoltenVK.dylib",
|
"Dependencies/Dynamic Libraries/libMoltenVK.dylib",
|
||||||
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib",
|
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib",
|
||||||
"Dependencies/Dynamic Libraries/RyujinxHelper.framework",
|
"Dependencies/Dynamic Libraries/RyujinxKeyboard.framework",
|
||||||
"Dependencies/Dynamic Libraries/StosJIT.framework",
|
|
||||||
Dependencies/XCFrameworks/libavcodec.xcframework,
|
Dependencies/XCFrameworks/libavcodec.xcframework,
|
||||||
Dependencies/XCFrameworks/libavfilter.xcframework,
|
Dependencies/XCFrameworks/libavfilter.xcframework,
|
||||||
Dependencies/XCFrameworks/libavformat.xcframework,
|
Dependencies/XCFrameworks/libavformat.xcframework,
|
||||||
|
@ -187,6 +178,7 @@
|
||||||
Dependencies/XCFrameworks/libswresample.xcframework,
|
Dependencies/XCFrameworks/libswresample.xcframework,
|
||||||
Dependencies/XCFrameworks/libswscale.xcframework,
|
Dependencies/XCFrameworks/libswscale.xcframework,
|
||||||
Dependencies/XCFrameworks/libteakra.xcframework,
|
Dependencies/XCFrameworks/libteakra.xcframework,
|
||||||
|
Dependencies/XCFrameworks/MoltenVK.xcframework,
|
||||||
Dependencies/XCFrameworks/SDL2.xcframework,
|
Dependencies/XCFrameworks/SDL2.xcframework,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -203,8 +195,8 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */,
|
||||||
CA8F9C322D3F5AB200D7E586 /* GameController.framework in Frameworks */,
|
CA8F9C322D3F5AB200D7E586 /* GameController.framework in Frameworks */,
|
||||||
4549A31C2DD8795900EC8D88 /* CocoaAsyncSocket in Frameworks */,
|
|
||||||
4EA5AE822D16807500AD0B9F /* SwiftSVG in Frameworks */,
|
4EA5AE822D16807500AD0B9F /* SwiftSVG in Frameworks */,
|
||||||
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */,
|
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
|
@ -230,7 +222,7 @@
|
||||||
4E80A9842CD6F54500029585 = {
|
4E80A9842CD6F54500029585 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */,
|
5650564A2D2A758600C8BB1E /* dotnet.xcconfig */,
|
||||||
BD43C6282D1B2514003BBC42 /* Ryujinx.Headless.SDL2.dylib */,
|
BD43C6282D1B2514003BBC42 /* Ryujinx.Headless.SDL2.dylib */,
|
||||||
4E80A98F2CD6F54500029585 /* MeloNX */,
|
4E80A98F2CD6F54500029585 /* MeloNX */,
|
||||||
4E80A9A02CD6F54700029585 /* MeloNXTests */,
|
4E80A9A02CD6F54700029585 /* MeloNXTests */,
|
||||||
|
@ -264,12 +256,12 @@
|
||||||
/* Begin PBXLegacyTarget section */
|
/* Begin PBXLegacyTarget section */
|
||||||
BD43C61D2D1B23AB003BBC42 /* Ryujinx */ = {
|
BD43C61D2D1B23AB003BBC42 /* Ryujinx */ = {
|
||||||
isa = PBXLegacyTarget;
|
isa = PBXLegacyTarget;
|
||||||
buildArgumentsString = "./distribution/ios/xc-compile.sh";
|
buildArgumentsString = "publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true";
|
||||||
buildConfigurationList = BD43C61E2D1B23AB003BBC42 /* Build configuration list for PBXLegacyTarget "Ryujinx" */;
|
buildConfigurationList = BD43C61E2D1B23AB003BBC42 /* Build configuration list for PBXLegacyTarget "Ryujinx" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
);
|
);
|
||||||
buildToolPath = /bin/sh;
|
buildToolPath = /usr/local/share/dotnet/dotnet;
|
||||||
buildWorkingDirectory = "$(SRCROOT)/../../";
|
buildWorkingDirectory = "$(SRCROOT)/../..";
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = Ryujinx;
|
name = Ryujinx;
|
||||||
|
@ -294,15 +286,14 @@
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
4EFFCD192DFB766F00F78EA6 /* PBXTargetDependency */,
|
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
4E80A98F2CD6F54500029585 /* MeloNX */,
|
4E80A98F2CD6F54500029585 /* MeloNX */,
|
||||||
);
|
);
|
||||||
name = MeloNX;
|
name = MeloNX;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
|
4E0DED332D05695D00FEF007 /* SwiftUIJoystick */,
|
||||||
4EA5AE812D16807500AD0B9F /* SwiftSVG */,
|
4EA5AE812D16807500AD0B9F /* SwiftSVG */,
|
||||||
4549A31B2DD8795900EC8D88 /* CocoaAsyncSocket */,
|
|
||||||
);
|
);
|
||||||
productName = MeloNX;
|
productName = MeloNX;
|
||||||
productReference = 4E80A98D2CD6F54500029585 /* MeloNX.app */;
|
productReference = 4E80A98D2CD6F54500029585 /* MeloNX.app */;
|
||||||
|
@ -362,7 +353,7 @@
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastSwiftUpdateCheck = 1620;
|
LastSwiftUpdateCheck = 1620;
|
||||||
LastUpgradeCheck = 1620;
|
LastUpgradeCheck = 1610;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
4E80A98C2CD6F54500029585 = {
|
4E80A98C2CD6F54500029585 = {
|
||||||
CreatedOnToolsVersion = 16.1;
|
CreatedOnToolsVersion = 16.1;
|
||||||
|
@ -393,8 +384,8 @@
|
||||||
mainGroup = 4E80A9842CD6F54500029585;
|
mainGroup = 4E80A9842CD6F54500029585;
|
||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
|
4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */,
|
||||||
4EA5AE802D16807500AD0B9F /* XCRemoteSwiftPackageReference "SwiftSVG" */,
|
4EA5AE802D16807500AD0B9F /* XCRemoteSwiftPackageReference "SwiftSVG" */,
|
||||||
4549A31A2DD8795900EC8D88 /* XCRemoteSwiftPackageReference "CocoaAsyncSocket" */,
|
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 56;
|
preferredProjectObjectVersion = 56;
|
||||||
productRefGroup = 4E80A98E2CD6F54500029585 /* Products */;
|
productRefGroup = 4E80A98E2CD6F54500029585 /* Products */;
|
||||||
|
@ -415,7 +406,6 @@
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
4E12B23C2D797CFA00FB2271 /* MeloNX.xcconfig in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -453,7 +443,7 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "cd ../..\nmv src/Ryujinx.Headless.SDL2/bin/Release/net8.0/ios-arm64/publish/Ryujinx.Headless.SDL2.dylib src/MeloNX/MeloNX/Dependencies/Dynamic\\ Libraries/Ryujinx.Headless.SDL2.dylib\n";
|
shellScript = "cd ../..\nmv src/Ryujinx.Headless.SDL2/bin/Release/net8.0/ios-arm64/native/Ryujinx.Headless.SDL2.dylib src/MeloNX/MeloNX/Dependencies/Dynamic\\ Libraries/Ryujinx.Headless.SDL2.dylib\n";
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
@ -492,11 +482,6 @@
|
||||||
target = 4E80A98C2CD6F54500029585 /* MeloNX */;
|
target = 4E80A98C2CD6F54500029585 /* MeloNX */;
|
||||||
targetProxy = 4E80A9A82CD6F54700029585 /* PBXContainerItemProxy */;
|
targetProxy = 4E80A9A82CD6F54700029585 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
4EFFCD192DFB766F00F78EA6 /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = BD43C6212D1B248D003BBC42 /* com.Stossy11.MeloNX.RyujinxAg */;
|
|
||||||
targetProxy = 4EFFCD182DFB766F00F78EA6 /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
BD43C6262D1B249E003BBC42 /* PBXTargetDependency */ = {
|
BD43C6262D1B249E003BBC42 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = BD43C61D2D1B23AB003BBC42 /* Ryujinx */;
|
target = BD43C61D2D1B23AB003BBC42 /* Ryujinx */;
|
||||||
|
@ -507,7 +492,6 @@
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
4E80A9AF2CD6F54700029585 /* Debug */ = {
|
4E80A9AF2CD6F54700029585 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
@ -570,7 +554,6 @@
|
||||||
ONLY_ACTIVE_ARCH = NO;
|
ONLY_ACTIVE_ARCH = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
||||||
SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only";
|
SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
};
|
};
|
||||||
|
@ -578,7 +561,6 @@
|
||||||
};
|
};
|
||||||
4E80A9B02CD6F54700029585 /* Release */ = {
|
4E80A9B02CD6F54700029585 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
@ -637,7 +619,6 @@
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_DISABLE_SAFETY_CHECKS = YES;
|
SWIFT_DISABLE_SAFETY_CHECKS = YES;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
||||||
SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only";
|
SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
|
@ -645,18 +626,14 @@
|
||||||
};
|
};
|
||||||
4E80A9B22CD6F54700029585 /* Debug */ = {
|
4E80A9B22CD6F54700029585 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = PixelAppIcon;
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
|
||||||
CODE_SIGN_ENTITLEMENTS = MeloNX/MeloNX.entitlements;
|
CODE_SIGN_ENTITLEMENTS = MeloNX/MeloNX.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||||
EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = NO;
|
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_TESTABILITY = NO;
|
ENABLE_TESTABILITY = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -703,96 +680,8 @@
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
);
|
);
|
||||||
GCC_OPTIMIZATION_LEVEL = z;
|
GCC_OPTIMIZATION_LEVEL = fast;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = MeloNX/Info.plist;
|
INFOPLIST_FILE = MeloNX/Info.plist;
|
||||||
INFOPLIST_KEY_GCSupportsControllerUserInteraction = YES;
|
INFOPLIST_KEY_GCSupportsControllerUserInteraction = YES;
|
||||||
|
@ -803,10 +692,10 @@
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIRequiresFullScreen = NO;
|
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -890,168 +779,10 @@
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(VERSION)";
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
|
||||||
SUPPORTS_MACCATALYST = NO;
|
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
@ -1061,18 +792,14 @@
|
||||||
};
|
};
|
||||||
4E80A9B32CD6F54700029585 /* Release */ = {
|
4E80A9B32CD6F54700029585 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = PixelAppIcon;
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
|
||||||
CODE_SIGN_ENTITLEMENTS = MeloNX/MeloNX.entitlements;
|
CODE_SIGN_ENTITLEMENTS = MeloNX/MeloNX.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||||
EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = NO;
|
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -1119,96 +846,8 @@
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
);
|
);
|
||||||
GCC_OPTIMIZATION_LEVEL = z;
|
GCC_OPTIMIZATION_LEVEL = fast;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = MeloNX/Info.plist;
|
INFOPLIST_FILE = MeloNX/Info.plist;
|
||||||
INFOPLIST_KEY_GCSupportsControllerUserInteraction = YES;
|
INFOPLIST_KEY_GCSupportsControllerUserInteraction = YES;
|
||||||
|
@ -1219,10 +858,10 @@
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIRequiresFullScreen = NO;
|
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1306,168 +945,10 @@
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(VERSION)";
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
|
||||||
SUPPORTS_MACCATALYST = NO;
|
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
@ -1477,7 +958,6 @@
|
||||||
};
|
};
|
||||||
4E80A9B52CD6F54700029585 /* Debug */ = {
|
4E80A9B52CD6F54700029585 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
@ -1497,7 +977,6 @@
|
||||||
};
|
};
|
||||||
4E80A9B62CD6F54700029585 /* Release */ = {
|
4E80A9B62CD6F54700029585 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
@ -1517,7 +996,6 @@
|
||||||
};
|
};
|
||||||
4E80A9B82CD6F54700029585 /* Debug */ = {
|
4E80A9B82CD6F54700029585 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
@ -1535,7 +1013,6 @@
|
||||||
};
|
};
|
||||||
4E80A9B92CD6F54700029585 /* Release */ = {
|
4E80A9B92CD6F54700029585 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
@ -1553,9 +1030,7 @@
|
||||||
};
|
};
|
||||||
BD43C61F2D1B23AB003BBC42 /* Debug */ = {
|
BD43C61F2D1B23AB003BBC42 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEBUGGING_SYMBOLS = YES;
|
DEBUGGING_SYMBOLS = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
@ -1571,9 +1046,7 @@
|
||||||
};
|
};
|
||||||
BD43C6202D1B23AB003BBC42 /* Release */ = {
|
BD43C6202D1B23AB003BBC42 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||||
|
@ -1585,7 +1058,6 @@
|
||||||
};
|
};
|
||||||
BD43C6232D1B248D003BBC42 /* Debug */ = {
|
BD43C6232D1B248D003BBC42 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||||
|
@ -1595,7 +1067,6 @@
|
||||||
};
|
};
|
||||||
BD43C6242D1B248D003BBC42 /* Release */ = {
|
BD43C6242D1B248D003BBC42 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||||
|
@ -1663,12 +1134,12 @@
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
4549A31A2DD8795900EC8D88 /* XCRemoteSwiftPackageReference "CocoaAsyncSocket" */ = {
|
4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/robbiehanson/CocoaAsyncSocket";
|
repositoryURL = "https://github.com/michael94ellis/SwiftUIJoystick";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = upToNextMajorVersion;
|
kind = upToNextMajorVersion;
|
||||||
minimumVersion = 7.6.5;
|
minimumVersion = 1.5.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
4EA5AE802D16807500AD0B9F /* XCRemoteSwiftPackageReference "SwiftSVG" */ = {
|
4EA5AE802D16807500AD0B9F /* XCRemoteSwiftPackageReference "SwiftSVG" */ = {
|
||||||
|
@ -1682,10 +1153,10 @@
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
4549A31B2DD8795900EC8D88 /* CocoaAsyncSocket */ = {
|
4E0DED332D05695D00FEF007 /* SwiftUIJoystick */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 4549A31A2DD8795900EC8D88 /* XCRemoteSwiftPackageReference "CocoaAsyncSocket" */;
|
package = 4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */;
|
||||||
productName = CocoaAsyncSocket;
|
productName = SwiftUIJoystick;
|
||||||
};
|
};
|
||||||
4EA5AE812D16807500AD0B9F /* SwiftSVG */ = {
|
4EA5AE812D16807500AD0B9F /* SwiftSVG */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
{
|
{
|
||||||
"originHash" : "b4a593815773c4e9eedb98cabe88f41620776314bffb6c39d5a41cb743e4d390",
|
"originHash" : "d611b071fbe94fdc9900a07a218340eab4ce2c3c7168bf6542f2830c0400a72b",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
|
||||||
"identity" : "cocoaasyncsocket",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/robbiehanson/CocoaAsyncSocket",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "dbdc00669c1ced63b27c3c5f052ee4d28f10150c",
|
|
||||||
"version" : "7.6.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "swiftsvg",
|
"identity" : "swiftsvg",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
@ -18,6 +9,15 @@
|
||||||
"branch" : "master",
|
"branch" : "master",
|
||||||
"revision" : "88b9ee086b29019e35f6f49c8e30e5552eb8fa9d"
|
"revision" : "88b9ee086b29019e35f6f49c8e30e5552eb8fa9d"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swiftuijoystick",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/michael94ellis/SwiftUIJoystick",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "5bd303cdafb369a70a45c902538b42dd3c5f4d65",
|
||||||
|
"version" : "1.5.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version" : 3
|
"version" : 3
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<array/>
|
||||||
|
</plist>
|
Binary file not shown.
Binary file not shown.
BIN
src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/ls.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/ls.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1620"
|
LastUpgradeVersion = "1610"
|
||||||
version = "2.0">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES"
|
buildImplicitDependencies = "YES"
|
||||||
|
@ -58,19 +58,14 @@
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
disableMainThreadChecker = "YES"
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugXPCServices = "NO"
|
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
enableGPUValidationMode = "1"
|
enableGPUValidationMode = "1"
|
||||||
allowLocationSimulation = "YES"
|
showGraphicsOverview = "Yes"
|
||||||
queueDebuggingEnabled = "No"
|
allowLocationSimulation = "YES">
|
||||||
consoleMode = "0"
|
|
||||||
structuredConsoleMode = "2"
|
|
||||||
disablePerformanceAntipatternChecker = "YES">
|
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
|
@ -105,24 +100,5 @@
|
||||||
<ArchiveAction
|
<ArchiveAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
revealArchiveInOrganizer = "YES">
|
revealArchiveInOrganizer = "YES">
|
||||||
<PreActions>
|
|
||||||
<ExecutionAction
|
|
||||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
|
||||||
<ActionContent
|
|
||||||
title = "Run Script"
|
|
||||||
scriptText = "REPO_DIR="$(cd "${SRCROOT}/../../" && pwd)" SCRIPT_PATH="$REPO_DIR/distribution/ios/set_current_version.sh" echo "hi" sh "${SCRIPT_PATH}" "
|
|
||||||
shellToInvoke = "/bin/bash">
|
|
||||||
<EnvironmentBuildable>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "4E80A98C2CD6F54500029585"
|
|
||||||
BuildableName = "MeloNX.app"
|
|
||||||
BlueprintName = "MeloNX"
|
|
||||||
ReferencedContainer = "container:MeloNX.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</EnvironmentBuildable>
|
|
||||||
</ActionContent>
|
|
||||||
</ExecutionAction>
|
|
||||||
</PreActions>
|
|
||||||
</ArchiveAction>
|
</ArchiveAction>
|
||||||
</Scheme>
|
</Scheme>
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1620"
|
|
||||||
version = "1.7">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES"
|
|
||||||
buildArchitectures = "Automatic">
|
|
||||||
<PreActions>
|
|
||||||
<ExecutionAction
|
|
||||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
|
||||||
<ActionContent
|
|
||||||
title = "Run Script"
|
|
||||||
scriptText = "REPO_DIR="$(cd "${SRCROOT}/../../" && pwd)" SCRIPT_PATH="$REPO_DIR/distribution/ios/get_dotnet.sh" echo "Xcode is located at: $DEVELOPER_DIR" sh "${SCRIPT_PATH}" "
|
|
||||||
shellToInvoke = "/bin/bash">
|
|
||||||
<EnvironmentBuildable>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BD43C61D2D1B23AB003BBC42"
|
|
||||||
BuildableName = "Ryujinx"
|
|
||||||
BlueprintName = "Ryujinx"
|
|
||||||
ReferencedContainer = "container:MeloNX.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</EnvironmentBuildable>
|
|
||||||
</ActionContent>
|
|
||||||
</ExecutionAction>
|
|
||||||
</PreActions>
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BD43C61D2D1B23AB003BBC42"
|
|
||||||
BuildableName = "Ryujinx"
|
|
||||||
BlueprintName = "Ryujinx"
|
|
||||||
ReferencedContainer = "container:MeloNX.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
shouldAutocreateTestPlan = "YES">
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BD43C61D2D1B23AB003BBC42"
|
|
||||||
BuildableName = "Ryujinx"
|
|
||||||
BlueprintName = "Ryujinx"
|
|
||||||
ReferencedContainer = "container:MeloNX.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
|
@ -17,20 +17,11 @@ func SecTaskCopyValueForEntitlement(
|
||||||
_ error: NSErrorPointer
|
_ error: NSErrorPointer
|
||||||
) -> CFTypeRef?
|
) -> CFTypeRef?
|
||||||
|
|
||||||
@_silgen_name("SecTaskCopyTeamIdentifier")
|
|
||||||
func SecTaskCopyTeamIdentifier(
|
|
||||||
_ task: SecTaskRef,
|
|
||||||
_ error: NSErrorPointer
|
|
||||||
) -> NSString?
|
|
||||||
|
|
||||||
@_silgen_name("SecTaskCreateFromSelf")
|
@_silgen_name("SecTaskCreateFromSelf")
|
||||||
func SecTaskCreateFromSelf(
|
func SecTaskCreateFromSelf(
|
||||||
_ allocator: CFAllocator?
|
_ allocator: CFAllocator?
|
||||||
) -> SecTaskRef?
|
) -> SecTaskRef?
|
||||||
|
|
||||||
@_silgen_name("CFRelease")
|
|
||||||
func CFRelease(_ cf: CFTypeRef)
|
|
||||||
|
|
||||||
@_silgen_name("SecTaskCopyValuesForEntitlements")
|
@_silgen_name("SecTaskCopyValuesForEntitlements")
|
||||||
func SecTaskCopyValuesForEntitlements(
|
func SecTaskCopyValuesForEntitlements(
|
||||||
_ task: SecTaskRef,
|
_ task: SecTaskRef,
|
||||||
|
@ -38,43 +29,30 @@ func SecTaskCopyValuesForEntitlements(
|
||||||
_ error: UnsafeMutablePointer<Unmanaged<CFError>?>?
|
_ error: UnsafeMutablePointer<Unmanaged<CFError>?>?
|
||||||
) -> CFDictionary?
|
) -> CFDictionary?
|
||||||
|
|
||||||
func releaseSecTask(_ task: SecTaskRef) {
|
|
||||||
let cf = unsafeBitCast(task, to: CFTypeRef.self)
|
|
||||||
CFRelease(cf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAppEntitlements(_ ents: [String]) -> [String: Any] {
|
func checkAppEntitlements(_ ents: [String]) -> [String: Any] {
|
||||||
guard let task = SecTaskCreateFromSelf(nil) else {
|
guard let task = SecTaskCreateFromSelf(nil) else {
|
||||||
|
print("Failed to create SecTask")
|
||||||
return [:]
|
return [:]
|
||||||
}
|
}
|
||||||
defer {
|
|
||||||
releaseSecTask(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let entitlements = SecTaskCopyValuesForEntitlements(task, ents as CFArray, nil) else {
|
guard let entitlements = SecTaskCopyValuesForEntitlements(task, ents as CFArray, nil) else {
|
||||||
|
print("Failed to get entitlements")
|
||||||
return [:]
|
return [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return (entitlements as NSDictionary) as? [String: Any] ?? [:]
|
return (entitlements as? [String: Any]) ?? [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAppEntitlement(_ ent: String) -> Bool {
|
func checkAppEntitlement(_ ent: String) -> Bool {
|
||||||
guard let task = SecTaskCreateFromSelf(nil) else {
|
guard let task = SecTaskCreateFromSelf(nil) else {
|
||||||
return false
|
print("Failed to create SecTask")
|
||||||
}
|
|
||||||
defer {
|
|
||||||
releaseSecTask(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let entitlement = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if let number = entitlement as? NSNumber {
|
guard let entitlements = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else {
|
||||||
return number.boolValue
|
print("Failed to get entitlements")
|
||||||
} else if let bool = entitlement as? Bool {
|
return false
|
||||||
return bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return entitlements.boolValue != nil && entitlements.boolValue
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,11 @@
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <SDL2/SDL_syswm.h>
|
#include <SDL2/SDL_syswm.h>
|
||||||
#include <StosJIT/StosJIT-Swift.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct GameInfo {
|
struct GameInfo {
|
||||||
long FileSize;
|
long FileSize;
|
||||||
char TitleName[512];
|
char TitleName[512];
|
||||||
|
@ -43,10 +40,6 @@ struct DlcNcaList {
|
||||||
struct DlcNcaListItem* items;
|
struct DlcNcaListItem* items;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (^SwiftCallback)(NSString *result);
|
|
||||||
|
|
||||||
void RegisterCallback(NSString *identifier, SwiftCallback callback);
|
|
||||||
|
|
||||||
extern struct GameInfo get_game_info(int, char*);
|
extern struct GameInfo get_game_info(int, char*);
|
||||||
|
|
||||||
extern struct DlcNcaList get_dlc_nca_list(const char* titleIdPtr, const char* pathPtr);
|
extern struct DlcNcaList get_dlc_nca_list(const char* titleIdPtr, const char* pathPtr);
|
||||||
|
@ -57,16 +50,12 @@ char* installed_firmware_version();
|
||||||
|
|
||||||
void set_native_window(void *layerPtr);
|
void set_native_window(void *layerPtr);
|
||||||
|
|
||||||
void pause_emulation(bool shouldPause);
|
|
||||||
|
|
||||||
void stop_emulation();
|
void stop_emulation();
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
int main_ryujinx_sdl(int argc, char **argv);
|
int main_ryujinx_sdl(int argc, char **argv);
|
||||||
|
|
||||||
int update_settings_external(int argc, char **argv);
|
|
||||||
|
|
||||||
int get_current_fps();
|
int get_current_fps();
|
||||||
|
|
||||||
void touch_began(float x, float y, int index);
|
void touch_began(float x, float y, int index);
|
||||||
|
|
|
@ -20,14 +20,6 @@ func isJITEnabled() -> Bool {
|
||||||
return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0 ? allocateTest() : false
|
return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0 ? allocateTest() : false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDebugged() -> Bool {
|
|
||||||
var flags: Int = 0
|
|
||||||
if checkAppEntitlement("dynamic-codesigning") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkMemoryPermissions(at address: UnsafeRawPointer) -> Bool {
|
func checkMemoryPermissions(at address: UnsafeRawPointer) -> Bool {
|
||||||
var region: vm_address_t = vm_address_t(UInt(bitPattern: address))
|
var region: vm_address_t = vm_address_t(UInt(bitPattern: address))
|
||||||
var regionSize: vm_size_t = 0
|
var regionSize: vm_size_t = 0
|
||||||
|
@ -42,7 +34,7 @@ func checkMemoryPermissions(at address: UnsafeRawPointer) -> Bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if result != KERN_SUCCESS {
|
if result != KERN_SUCCESS {
|
||||||
// print("Failed to reach \(address)")
|
print("Failed to reach \(address)")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,122 +6,39 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Network
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
func enableJITEB() {
|
func enableJITEB() {
|
||||||
if UserDefaults.standard.bool(forKey: "waitForVPN") {
|
guard let bundleID = Bundle.main.bundleIdentifier else {
|
||||||
waitForVPNConnection { connected in
|
|
||||||
if connected {
|
|
||||||
enableJITEBRequest()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
enableJITEBRequest()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableJITEBRequest() {
|
|
||||||
let pid = Int(getpid())
|
|
||||||
// print(pid)
|
|
||||||
|
|
||||||
let address = URL(string: "http://[fd00::]:9172/attach/\(pid)")!
|
|
||||||
var request = URLRequest(url: address)
|
|
||||||
request.httpMethod = "POST"
|
|
||||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
||||||
|
|
||||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
||||||
if let error = error {
|
|
||||||
presentAlert(title: "Request Error", message: error.localizedDescription)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if let data = data, let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
|
||||||
showLaunchAppAlert(jsonData: data, in: windowScene.windows.last!.rootViewController!)
|
|
||||||
} else {
|
|
||||||
fatalError("Unable to get Window")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForVPNConnection(timeout: TimeInterval = 30, interval: TimeInterval = 1, _ completion: @escaping (Bool) -> Void) {
|
|
||||||
let startTime = Date()
|
|
||||||
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .background))
|
|
||||||
|
|
||||||
timer.schedule(deadline: .now(), repeating: interval)
|
|
||||||
|
|
||||||
timer.setEventHandler {
|
|
||||||
pingSite { connected in
|
|
||||||
if connected {
|
|
||||||
timer.cancel()
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion(true)
|
|
||||||
}
|
|
||||||
} else if Date().timeIntervalSince(startTime) > timeout {
|
|
||||||
timer.cancel()
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timer.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func pingSite(host: String = "http://[fd00::]:9172/hello", completion: @escaping (Bool) -> Void) {
|
|
||||||
guard let url = URL(string: host) else {
|
|
||||||
completion(false)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = URLSessionConfiguration.default
|
let address = URL(string: "http://[fd00::]:9172/launch_app/\(bundleID)")!
|
||||||
config.timeoutIntervalForRequest = 2.0
|
|
||||||
config.timeoutIntervalForResource = 2.0
|
|
||||||
|
|
||||||
let session = URLSession(configuration: config)
|
let task = URLSession.shared.dataTask(with: address) { data, response, error in
|
||||||
|
if error != nil {
|
||||||
var request = URLRequest(url: url)
|
return
|
||||||
request.httpMethod = "GET"
|
|
||||||
|
|
||||||
let task = session.dataTask(with: request) { _, response, error in
|
|
||||||
if let error = error {
|
|
||||||
// print("Ping failed: \(error.localizedDescription)")
|
|
||||||
completion(false)
|
|
||||||
} else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
|
|
||||||
completion(true)
|
|
||||||
} else {
|
|
||||||
let httpResponse = response as? HTTPURLResponse
|
|
||||||
completion(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
showLaunchAppAlert(jsonData: data!, in: UIApplication.shared.windows.last!.rootViewController!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
task.resume()
|
task.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func presentAlert(title: String, message: String, completion: (() -> Void)? = nil) {
|
|
||||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
||||||
let lastWindow = windowScene.windows.last {
|
|
||||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
|
|
||||||
completion?()
|
|
||||||
})
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
lastWindow.rootViewController?.present(alert, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct LaunchApp: Codable {
|
struct LaunchApp: Codable {
|
||||||
let success: Bool
|
let ok: Bool
|
||||||
let message: String
|
let error: String?
|
||||||
|
let launching: Bool
|
||||||
|
let position: Int?
|
||||||
|
let mounting: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func showLaunchAppAlert(jsonData: Data, in viewController: UIViewController) {
|
func showLaunchAppAlert(jsonData: Data, in viewController: UIViewController) {
|
||||||
|
@ -130,23 +47,28 @@ func showLaunchAppAlert(jsonData: Data, in viewController: UIViewController) {
|
||||||
|
|
||||||
var message = ""
|
var message = ""
|
||||||
|
|
||||||
if !result.success {
|
if let error = result.error {
|
||||||
message += "\n\(result.message)"
|
message = "Error: \(error)"
|
||||||
|
} else if result.mounting {
|
||||||
|
message = "App is mounting..."
|
||||||
let alert = UIAlertController(title: "JIT Error", message: message, preferredStyle: .alert)
|
} else if result.launching {
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
message = "App is launching..."
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
viewController.present(alert, animated: true)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// print("Hopefully JIT is enabled now...")
|
message = "App launch status unknown."
|
||||||
Ryujinx.shared.ryuIsJITEnabled()
|
}
|
||||||
|
|
||||||
|
if let position = result.position {
|
||||||
|
message += "\nPosition: \(position)"
|
||||||
|
}
|
||||||
|
|
||||||
|
let alert = UIAlertController(title: "Launch Status", message: message, preferredStyle: .alert)
|
||||||
|
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
viewController.present(alert, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
// print(String(data: jsonData, encoding: .utf8))
|
|
||||||
let alert = UIAlertController(title: "Decoding Error", message: error.localizedDescription, preferredStyle: .alert)
|
let alert = UIAlertController(title: "Decoding Error", message: error.localizedDescription, preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
//
|
|
||||||
// EnableJIT.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 10/02/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Network
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
func stikJITorStikDebug() -> Int {
|
|
||||||
let teamid = SecTaskCopyTeamIdentifier(SecTaskCreateFromSelf(nil)!, nil)
|
|
||||||
|
|
||||||
if checkifappinstalled("com.stik.sj") {
|
|
||||||
return 1 // StikDebug
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkifappinstalled("com.stik.sj.\(String(teamid ?? ""))") {
|
|
||||||
return 2 // StikJIT
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0 // Not Found
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkforOld() -> Bool {
|
|
||||||
let teamid = SecTaskCopyTeamIdentifier(SecTaskCreateFromSelf(nil)!, nil)
|
|
||||||
|
|
||||||
if checkifappinstalled(changeAppUI("Y29tLnN0b3NzeTExLlBvbWVsbw==") ?? "") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkifappinstalled(changeAppUI("Y29tLnN0b3NzeTExLlBvbWVsbw==") ?? "" + ".\(String(teamid ?? ""))") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkifappinstalled((Bundle.main.bundleIdentifier ?? "").replacingOccurrences(of: "MeloNX", with: changeAppUI("UG9tZWxv") ?? "")) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func checkifappinstalled(_ id: String) -> Bool {
|
|
||||||
guard let handle = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_LAZY) else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias SBSLaunchApplicationWithIdentifierFunc = @convention(c) (CFString, Bool) -> Int32
|
|
||||||
guard let sym = dlsym(handle, "SBSLaunchApplicationWithIdentifier") else {
|
|
||||||
if let error = dlerror() {
|
|
||||||
print(String(cString: error))
|
|
||||||
}
|
|
||||||
dlclose(handle)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let bundleID: CFString = id as CFString
|
|
||||||
let suspended: Bool = false
|
|
||||||
|
|
||||||
|
|
||||||
let SBSLaunchApplicationWithIdentifier = unsafeBitCast(sym, to: SBSLaunchApplicationWithIdentifierFunc.self)
|
|
||||||
let result = SBSLaunchApplicationWithIdentifier(bundleID, suspended)
|
|
||||||
|
|
||||||
return result == 9
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableJITStik() {
|
|
||||||
let urlScheme = "stikjit://enable-jit?bundle-id=\(Bundle.main.bundleIdentifier ?? "wow")"
|
|
||||||
if let launchURL = URL(string: urlScheme), !isJITEnabled() {
|
|
||||||
UIApplication.shared.open(launchURL, options: [:], completionHandler: nil)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
//
|
|
||||||
// BaseController.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by MediaMoots on 5/17/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- Base Controller Protocol
|
|
||||||
|
|
||||||
/// Base Controller with motion related functions
|
|
||||||
protocol BaseController: AnyObject {
|
|
||||||
func tryRegisterMotion(slot: UInt8)
|
|
||||||
func tryGetMotionProvider() -> DSUMotionProvider?
|
|
||||||
}
|
|
|
@ -1,178 +0,0 @@
|
||||||
//
|
|
||||||
// DSUMotionProviders.swift
|
|
||||||
//
|
|
||||||
// Multi-source Cemuhook-compatible DSU server.
|
|
||||||
// Created by MediaMoots on 5/17/2025.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreMotion
|
|
||||||
import GameController // GCController
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- Providers
|
|
||||||
|
|
||||||
/// iPhone / iPad IMU
|
|
||||||
final class DeviceMotionProvider: DSUMotionProvider {
|
|
||||||
|
|
||||||
// ───── DSUMotionProvider conformance
|
|
||||||
let slot: UInt8
|
|
||||||
let mac: [UInt8] = [0xAB,0x12,0xCD,0x34,0xEF,0x56]
|
|
||||||
let connectionType: UInt8 = 2
|
|
||||||
let batteryLevel: UInt8 = 5
|
|
||||||
let motionRate: Double = 60.0 // 60 Hz
|
|
||||||
|
|
||||||
// ───── Internals
|
|
||||||
private let mm = CMMotionManager()
|
|
||||||
|
|
||||||
// Thread Safety
|
|
||||||
private let dataLock = NSLock()
|
|
||||||
private var _latest: CMDeviceMotion?
|
|
||||||
private var latest: CMDeviceMotion? {
|
|
||||||
get { dataLock.lock(); defer { dataLock.unlock() }; return _latest }
|
|
||||||
set { dataLock.lock(); _latest = newValue; dataLock.unlock() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private var orientation: UIDeviceOrientation =
|
|
||||||
UIDevice.current.orientation == .unknown ? .landscapeLeft : UIDevice.current.orientation
|
|
||||||
|
|
||||||
init(slot: UInt8) {
|
|
||||||
precondition(slot < 8, "DSU only supports slots 0…7")
|
|
||||||
self.slot = slot
|
|
||||||
|
|
||||||
// ── start Core Motion
|
|
||||||
mm.deviceMotionUpdateInterval = 1.0 / motionRate
|
|
||||||
mm.startDeviceMotionUpdates(to: .main) { [weak self] m, _ in
|
|
||||||
guard let self = self, let m = m else { return }
|
|
||||||
self.latest = m
|
|
||||||
if let sample = self.nextSample() {
|
|
||||||
DSUServer.shared.pushSample(sample, from: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── track orientation changes (ignore flat)
|
|
||||||
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(orientationDidChange),
|
|
||||||
name: UIDevice.orientationDidChangeNotification,
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func orientationDidChange() {
|
|
||||||
let o = UIDevice.current.orientation
|
|
||||||
if o.isFlat { return } // ignore face-up / face-down
|
|
||||||
orientation = o
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextSample() -> DSUMotionSample? {
|
|
||||||
guard let m = latest else { return nil }
|
|
||||||
|
|
||||||
// Raw values
|
|
||||||
let gx = Float(m.rotationRate.x)
|
|
||||||
let gy = Float(m.rotationRate.y)
|
|
||||||
let gz = Float(m.rotationRate.z)
|
|
||||||
let ax = Float(m.gravity.x + m.userAcceleration.x)
|
|
||||||
let ay = Float(m.gravity.y + m.userAcceleration.y)
|
|
||||||
let az = Float(m.gravity.z + m.userAcceleration.z)
|
|
||||||
|
|
||||||
// Rotate axes to match Cemuhook's "landscape-left as neutral" convention
|
|
||||||
let a: SIMD3<Float>
|
|
||||||
let g: SIMD3<Float>
|
|
||||||
|
|
||||||
switch orientation {
|
|
||||||
case .portrait:
|
|
||||||
a = SIMD3( ax, az, -ay)
|
|
||||||
g = SIMD3( gx, -gz, gy)
|
|
||||||
case .landscapeRight:
|
|
||||||
a = SIMD3( ay, az, ax)
|
|
||||||
g = SIMD3( gy, -gz, -gx)
|
|
||||||
case .portraitUpsideDown:
|
|
||||||
a = SIMD3( -ax, az, ay)
|
|
||||||
g = SIMD3( -gx, -gz, -gy)
|
|
||||||
case .landscapeLeft, .unknown, .faceUp, .faceDown:
|
|
||||||
a = SIMD3( -ay, az, -ax)
|
|
||||||
g = SIMD3( -gy, -gz, gx)
|
|
||||||
@unknown default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert gyro rad/s → °/s here so the server doesn't have to.
|
|
||||||
let gDeg = g * (180 / .pi)
|
|
||||||
|
|
||||||
return DSUMotionSample(timestampUS: currentUS(),
|
|
||||||
accel: a,
|
|
||||||
gyroDeg: gDeg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any Switch Pro / DualSense controller that exposes `GCMotion`
|
|
||||||
final class ControllerMotionProvider: DSUMotionProvider {
|
|
||||||
|
|
||||||
// DSUMotionProvider
|
|
||||||
let slot: UInt8
|
|
||||||
let mac: [UInt8] = [0xAB,0x12,0xCD,0x34,0xEF,0x56]
|
|
||||||
let connectionType: UInt8 = 2
|
|
||||||
var batteryLevel: UInt8 {
|
|
||||||
UInt8((pad.battery?.batteryLevel ?? 0.3) * 5).clamped(to: 0...5)
|
|
||||||
}
|
|
||||||
|
|
||||||
private let pad: GCController
|
|
||||||
|
|
||||||
// Thread Safety
|
|
||||||
private let dataLock = NSLock()
|
|
||||||
private var _latest: GCMotion?
|
|
||||||
private var latest: GCMotion? {
|
|
||||||
get { dataLock.lock(); defer { dataLock.unlock() }; return _latest }
|
|
||||||
set { dataLock.lock(); _latest = newValue; dataLock.unlock() }
|
|
||||||
}
|
|
||||||
|
|
||||||
init(controller: GCController, slot: UInt8) {
|
|
||||||
self.pad = controller
|
|
||||||
self.slot = slot
|
|
||||||
pad.motion?.sensorsActive = true
|
|
||||||
pad.motion?.valueChangedHandler = { [weak self] motion in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.latest = motion
|
|
||||||
if let sample = self.nextSample() {
|
|
||||||
DSUServer.shared.pushSample(sample, from: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextSample() -> DSUMotionSample? {
|
|
||||||
guard let m = latest else { return nil }
|
|
||||||
|
|
||||||
// Extract and convert acceleration to SIMD3<Float>
|
|
||||||
let a = SIMD3<Float>(
|
|
||||||
Float(m.acceleration.x),
|
|
||||||
Float(m.acceleration.z),
|
|
||||||
-Float(m.acceleration.y)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Extract, transform, and convert rotation rate to SIMD3<Float> (in radians/s)
|
|
||||||
let g = SIMD3<Float>(
|
|
||||||
Float(m.rotationRate.x),
|
|
||||||
-Float(m.rotationRate.z),
|
|
||||||
Float(m.rotationRate.y)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Convert gyro rotation rate from rad/s to degrees/s
|
|
||||||
let gDeg = g * (180 / .pi)
|
|
||||||
|
|
||||||
return DSUMotionSample(
|
|
||||||
timestampUS: currentUS(),
|
|
||||||
accel: a,
|
|
||||||
gyroDeg: gDeg
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- Helper funcs / ext
|
|
||||||
|
|
||||||
private func uint64US(_ time: TimeInterval) -> UInt64 { UInt64(time * 1_000_000) }
|
|
||||||
private func currentUS() -> UInt64 { uint64US(CACurrentMediaTime()) }
|
|
||||||
|
|
||||||
private extension Comparable {
|
|
||||||
func clamped(to r: ClosedRange<Self>) -> Self { min(max(self, r.lowerBound), r.upperBound) }
|
|
||||||
}
|
|
|
@ -1,217 +0,0 @@
|
||||||
//
|
|
||||||
// DSUServer.swift
|
|
||||||
//
|
|
||||||
// Multi-source Cemuhook-compatible DSU server.
|
|
||||||
// Created by MediaMoots on 5/17/2025.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CocoaAsyncSocket // GCDAsyncUdpSocket
|
|
||||||
import zlib // CRC-32
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- DSU Motion protocol
|
|
||||||
|
|
||||||
/// One motion source == one DSU *slot* (0-7).
|
|
||||||
protocol DSUMotionProvider: AnyObject {
|
|
||||||
var slot: UInt8 { get } // unique, 0-7
|
|
||||||
var mac: [UInt8] { get } // 6-byte ID
|
|
||||||
var connectionType: UInt8 { get } // 0 = USB, 2 = BT
|
|
||||||
var batteryLevel: UInt8 { get } // 0-5 (Cemuhook)
|
|
||||||
|
|
||||||
func nextSample() -> DSUMotionSample?
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Raw motion payload returned by providers.
|
|
||||||
struct DSUMotionSample {
|
|
||||||
var timestampUS: UInt64 // µs
|
|
||||||
var accel: SIMD3<Float> // G's
|
|
||||||
var gyroDeg: SIMD3<Float> // °/s
|
|
||||||
}
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- Server constants
|
|
||||||
|
|
||||||
private enum C {
|
|
||||||
static let port: UInt16 = 26_760
|
|
||||||
static let protocolVersion: UInt16 = 1_001
|
|
||||||
static let headerMagic = "DSUS"
|
|
||||||
}
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- Server core
|
|
||||||
|
|
||||||
final class DSUServer: NSObject {
|
|
||||||
|
|
||||||
// Singleton for convenience
|
|
||||||
static let shared = DSUServer()
|
|
||||||
private override init() {
|
|
||||||
serverID = UInt32.random(in: .min ... .max)
|
|
||||||
super.init()
|
|
||||||
configureSocket()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Public API ─────────────────────────────────────────────
|
|
||||||
func register(_ provider: DSUMotionProvider) { providers[provider.slot] = provider }
|
|
||||||
func unregister(slot: UInt8) { providers.removeValue(forKey: slot) }
|
|
||||||
|
|
||||||
/// 🔸 providers push fresh samples here.
|
|
||||||
func pushSample(_ sample: DSUMotionSample, from provider: DSUMotionProvider) {
|
|
||||||
guard let addr = lastClientAddress else { return } // no subscriber → drop
|
|
||||||
sendPadData(sample: sample, from: provider, to: addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
private let serverID: UInt32
|
|
||||||
private var socket: GCDAsyncUdpSocket?
|
|
||||||
private var lastClientAddress: Data?
|
|
||||||
|
|
||||||
private var providers = [UInt8 : DSUMotionProvider]() // slot→provider
|
|
||||||
private var packetNumber = [UInt8 : UInt32]() // per-slot counter
|
|
||||||
|
|
||||||
// ───────── UDP setup
|
|
||||||
private func configureSocket() {
|
|
||||||
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: .main)
|
|
||||||
do {
|
|
||||||
try socket?.bind(toPort: C.port)
|
|
||||||
try socket?.beginReceiving()
|
|
||||||
//print("🟢 DSU server listening on UDP \(C.port)")
|
|
||||||
} catch {
|
|
||||||
//print("❌ DSU socket error:", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- UDP delegate
|
|
||||||
|
|
||||||
extension DSUServer: GCDAsyncUdpSocketDelegate {
|
|
||||||
|
|
||||||
func udpSocket(_ sock: GCDAsyncUdpSocket,
|
|
||||||
didReceive data: Data,
|
|
||||||
fromAddress addr: Data,
|
|
||||||
withFilterContext ctx: Any?) {
|
|
||||||
|
|
||||||
lastClientAddress = addr
|
|
||||||
|
|
||||||
// Light validation
|
|
||||||
guard data.count >= 20,
|
|
||||||
String(decoding: data[0..<4], as: UTF8.self) == C.headerMagic,
|
|
||||||
data.readUInt16LE(at: 4) == C.protocolVersion
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
let type = data.readUInt32LE(at: 16)
|
|
||||||
switch type {
|
|
||||||
case 0x100001: sendPortInfo(to: addr) // client asks for port list
|
|
||||||
case 0x100002: break // subscription acknowledged
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func udpSocketDidClose(_ sock: GCDAsyncUdpSocket, withError err: Error?) {
|
|
||||||
//print("UDP closed:", err?.localizedDescription ?? "nil")
|
|
||||||
lastClientAddress = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- Packet helpers
|
|
||||||
|
|
||||||
private extension DSUServer {
|
|
||||||
|
|
||||||
// ── Header (16 bytes)
|
|
||||||
func appendHeader(into d: inout Data, payloadSize: UInt16) {
|
|
||||||
d.append(C.headerMagic.data(using: .utf8)!) // "DSUS"
|
|
||||||
d.append(C.protocolVersion.leData) // Protocol Version
|
|
||||||
d.append(payloadSize.leData) // Payload Size
|
|
||||||
d.append(Data(repeating: 0, count: 4)) // CRC-stub
|
|
||||||
d.append(serverID.leData) // Server ID
|
|
||||||
}
|
|
||||||
func patchCRC32(of packet: inout Data) {
|
|
||||||
let crc = packet.withUnsafeBytes { ptr in
|
|
||||||
crc32(0, ptr.baseAddress, uInt(packet.count))
|
|
||||||
}.littleEndian
|
|
||||||
let crcLE = UInt32(crc).littleEndian
|
|
||||||
let crcData = withUnsafeBytes(of: crcLE) { Data($0) }
|
|
||||||
packet.replaceSubrange(8..<12, with: crcData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 0x100001 DSUSPortInfo
|
|
||||||
func sendPortInfo(to addr: Data) {
|
|
||||||
for p in providers.values {
|
|
||||||
var pkt = Data()
|
|
||||||
appendHeader(into: &pkt, payloadSize: 12)
|
|
||||||
pkt.append(UInt32(0x100001).leData)
|
|
||||||
|
|
||||||
pkt.append(p.slot)
|
|
||||||
pkt.append(UInt8(2)) // connected
|
|
||||||
pkt.append(UInt8(2)) // full gyro
|
|
||||||
pkt.append(p.connectionType)
|
|
||||||
pkt.append(p.mac, count: 6)
|
|
||||||
pkt.append(p.batteryLevel)
|
|
||||||
pkt.append(UInt8(0)) // padding
|
|
||||||
|
|
||||||
patchCRC32(of: &pkt)
|
|
||||||
socket?.send(pkt, toAddress: addr, withTimeout: -1, tag: 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 0x100002 DSUSPadDataRsp
|
|
||||||
func sendPadData(sample s: DSUMotionSample,
|
|
||||||
from p: DSUMotionProvider,
|
|
||||||
to addr: Data) {
|
|
||||||
|
|
||||||
var pkt = Data()
|
|
||||||
appendHeader(into: &pkt, payloadSize: 84)
|
|
||||||
pkt.append(UInt32(0x100002).leData)
|
|
||||||
|
|
||||||
pkt.append(p.slot)
|
|
||||||
pkt.append(UInt8(2)) // connected
|
|
||||||
pkt.append(UInt8(2)) // full gyro
|
|
||||||
pkt.append(p.connectionType)
|
|
||||||
pkt.append(p.mac, count: 6)
|
|
||||||
pkt.append(p.batteryLevel)
|
|
||||||
pkt.append(UInt8(1)) // is connected
|
|
||||||
|
|
||||||
let num = packetNumber[p.slot, default: 0]
|
|
||||||
pkt.append(num.leData)
|
|
||||||
packetNumber[p.slot] = num &+ 1
|
|
||||||
|
|
||||||
pkt.append(UInt16(0).leData) // buttons
|
|
||||||
pkt.append(contentsOf: [0,0]) // HOME / Touch
|
|
||||||
pkt.append(contentsOf: [128,128,128,128]) // sticks
|
|
||||||
pkt.append(Data(repeating: 0, count: 12)) // d-pad / face / trig
|
|
||||||
pkt.append(Data(repeating: 0, count: 12)) // touch 1 & 2
|
|
||||||
pkt.append(s.timestampUS.leData)
|
|
||||||
|
|
||||||
pkt.append(s.accel.x.leData)
|
|
||||||
pkt.append(s.accel.y.leData)
|
|
||||||
pkt.append(s.accel.z.leData)
|
|
||||||
|
|
||||||
pkt.append(s.gyroDeg.x.leData)
|
|
||||||
pkt.append(s.gyroDeg.y.leData)
|
|
||||||
pkt.append(s.gyroDeg.z.leData)
|
|
||||||
|
|
||||||
patchCRC32(of: &pkt)
|
|
||||||
socket?.send(pkt, toAddress: addr, withTimeout: -1, tag: 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//──────────────────────────────────────────────────────────────────────── MARK:- Helper funcs / ext
|
|
||||||
|
|
||||||
private extension FixedWidthInteger {
|
|
||||||
var leData: Data {
|
|
||||||
var v = self.littleEndian
|
|
||||||
return Data(bytes: &v, count: MemoryLayout<Self>.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private extension Float {
|
|
||||||
var leData: Data {
|
|
||||||
var v = self
|
|
||||||
return Data(bytes: &v, count: MemoryLayout<Self>.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private extension Data {
|
|
||||||
func readUInt16LE(at offset: Int) -> UInt16 {
|
|
||||||
self[offset..<offset+2].withUnsafeBytes { $0.load(as: UInt16.self) }.littleEndian
|
|
||||||
}
|
|
||||||
func readUInt32LE(at offset: Int) -> UInt32 {
|
|
||||||
self[offset..<offset+4].withUnsafeBytes { $0.load(as: UInt32.self) }.littleEndian
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,36 +8,18 @@
|
||||||
import CoreHaptics
|
import CoreHaptics
|
||||||
import GameController
|
import GameController
|
||||||
|
|
||||||
class NativeController: Hashable, BaseController {
|
class NativeController: Hashable {
|
||||||
private var instanceID: SDL_JoystickID = -1
|
private var instanceID: SDL_JoystickID = -1
|
||||||
private var controller: OpaquePointer?
|
private var controller: OpaquePointer?
|
||||||
private var nativeController: GCController
|
private var nativeController: GCController
|
||||||
private var controllerMotionProvider: DSUMotionProvider?
|
|
||||||
|
|
||||||
private let controllerHaptics: CHHapticEngine?
|
private let controllerHaptics: CHHapticEngine?
|
||||||
private let rumbleController: RumbleController?
|
|
||||||
|
|
||||||
public var controllername: String { "GC - \(nativeController.vendorName ?? "Unknown")" }
|
public var controllername: String { "GC - \(nativeController.vendorName ?? "Unknown")" }
|
||||||
|
|
||||||
init(_ controller: GCController) {
|
init(_ controller: GCController) {
|
||||||
nativeController = controller
|
nativeController = controller
|
||||||
var ncontrollerHaptics = nativeController.haptics?.createEngine(withLocality: .default)
|
controllerHaptics = nativeController.haptics?.createEngine(withLocality: .default)
|
||||||
|
try? controllerHaptics?.start()
|
||||||
let vendorName = nativeController.vendorName ?? "Unknown"
|
|
||||||
var usesdeviceHaptics = (ncontrollerHaptics == nil || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")
|
|
||||||
controllerHaptics = usesdeviceHaptics ? ncontrollerHaptics : try? CHHapticEngine()
|
|
||||||
|
|
||||||
// Make sure the haptic engine exists before attempting to start it or initialize the controller.
|
|
||||||
if let hapticsEngine = controllerHaptics {
|
|
||||||
do {
|
|
||||||
try hapticsEngine.start()
|
|
||||||
rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: 1.2)
|
|
||||||
} catch {
|
|
||||||
rumbleController = nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rumbleController = nil
|
|
||||||
}
|
|
||||||
setupHandheldController()
|
setupHandheldController()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,21 +27,6 @@ class NativeController: Hashable, BaseController {
|
||||||
cleanup()
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func tryRegisterMotion(slot: UInt8) {
|
|
||||||
// Setup Motion
|
|
||||||
let dsuServer = DSUServer.shared
|
|
||||||
let vendorName = nativeController.vendorName ?? "Unknown"
|
|
||||||
var usesdevicemotion = (vendorName.lowercased() == "Joy-Con (l/R)".lowercased() || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")
|
|
||||||
|
|
||||||
controllerMotionProvider = usesdevicemotion ? DeviceMotionProvider(slot: slot) : ControllerMotionProvider(controller: nativeController, slot: slot)
|
|
||||||
|
|
||||||
if let provider = controllerMotionProvider {
|
|
||||||
dsuServer.register(provider)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func tryGetMotionProvider() -> DSUMotionProvider? { return controllerMotionProvider }
|
|
||||||
|
|
||||||
private func setupHandheldController() {
|
private func setupHandheldController() {
|
||||||
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
||||||
SDL_InitSubSystem(Uint32(SDL_INIT_GAMECONTROLLER))
|
SDL_InitSubSystem(Uint32(SDL_INIT_GAMECONTROLLER))
|
||||||
|
@ -82,40 +49,41 @@ class NativeController: Hashable, BaseController {
|
||||||
// Update joystick state here
|
// Update joystick state here
|
||||||
},
|
},
|
||||||
SetPlayerIndex: { userdata, playerIndex in
|
SetPlayerIndex: { userdata, playerIndex in
|
||||||
// print("Player index set to \(playerIndex)")
|
print("Player index set to \(playerIndex)")
|
||||||
guard let userdata, let player = GCControllerPlayerIndex(rawValue: Int(playerIndex)) else { return }
|
|
||||||
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
|
||||||
_self.nativeController.playerIndex = player
|
|
||||||
},
|
},
|
||||||
Rumble: { userdata, lowFreq, highFreq in
|
Rumble: { userdata, lowFreq, highFreq in
|
||||||
|
print("Rumble with \(lowFreq), \(highFreq)")
|
||||||
guard let userdata else { return 0 }
|
guard let userdata else { return 0 }
|
||||||
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
_self.rumbleController?.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq))
|
VirtualController.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq), engine: _self.controllerHaptics)
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
||||||
|
print("Trigger rumble with \(leftRumble), \(rightRumble)")
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
SetLED: { userdata, red, green, blue in
|
SetLED: { userdata, red, green, blue in
|
||||||
guard let userdata else { return 0 }
|
print("Set LED to RGB(\(red), \(green), \(blue))")
|
||||||
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
|
||||||
guard let light = _self.nativeController.light else { return 0 }
|
|
||||||
light.color = .init(red: Float(red), green: Float(green), blue: Float(blue))
|
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
SendEffect: { userdata, data, size in
|
SendEffect: { userdata, data, size in
|
||||||
|
print("Effect sent with size \(size)")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
instanceID = SDL_JoystickAttachVirtualEx(&joystickDesc)
|
instanceID = SDL_JoystickAttachVirtualEx(&joystickDesc)// SDL_JoystickAttachVirtual(SDL_JoystickType(SDL_JOYSTICK_TYPE_GAMECONTROLLER.rawValue), 6, 15, 1)
|
||||||
if instanceID < 0 {
|
if instanceID < 0 {
|
||||||
|
print("Failed to create virtual joystick: \(String(cString: SDL_GetError()))")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open a game controller for the virtual joystick
|
||||||
|
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
||||||
controller = SDL_GameControllerOpen(Int32(instanceID))
|
controller = SDL_GameControllerOpen(Int32(instanceID))
|
||||||
|
|
||||||
if controller == nil {
|
if controller == nil {
|
||||||
|
print("Failed to create virtual controller: \(String(cString: SDL_GetError()))")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,10 +91,10 @@ class NativeController: Hashable, BaseController {
|
||||||
guard let gamepad = nativeController.extendedGamepad
|
guard let gamepad = nativeController.extendedGamepad
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
setupButtonChangeListener(gamepad.buttonA, for: UserDefaults.standard.bool(forKey: "swapBandA") ? .B : .A)
|
setupButtonChangeListener(gamepad.buttonA, for: .A)
|
||||||
setupButtonChangeListener(gamepad.buttonB, for: UserDefaults.standard.bool(forKey: "swapBandA") ? .A : .B)
|
setupButtonChangeListener(gamepad.buttonB, for: .B)
|
||||||
setupButtonChangeListener(gamepad.buttonX, for: UserDefaults.standard.bool(forKey: "swapBandA") ? .Y : .X)
|
setupButtonChangeListener(gamepad.buttonX, for: .X)
|
||||||
setupButtonChangeListener(gamepad.buttonY, for: UserDefaults.standard.bool(forKey: "swapBandA") ? .X : .Y)
|
setupButtonChangeListener(gamepad.buttonY, for: .Y)
|
||||||
|
|
||||||
setupButtonChangeListener(gamepad.dpad.up, for: .dPadUp)
|
setupButtonChangeListener(gamepad.dpad.up, for: .dPadUp)
|
||||||
setupButtonChangeListener(gamepad.dpad.down, for: .dPadDown)
|
setupButtonChangeListener(gamepad.dpad.down, for: .dPadDown)
|
||||||
|
@ -173,13 +141,49 @@ class NativeController: Hashable, BaseController {
|
||||||
|
|
||||||
func setupTriggerChangeListener(_ button: GCControllerButtonInput, for key: ThumbstickType) {
|
func setupTriggerChangeListener(_ button: GCControllerButtonInput, for key: ThumbstickType) {
|
||||||
button.valueChangedHandler = { [unowned self] _, value, pressed in
|
button.valueChangedHandler = { [unowned self] _, value, pressed in
|
||||||
// // print("Value: \(value), Is pressed: \(pressed)")
|
// print("Value: \(value), Is pressed: \(pressed)")
|
||||||
let axis: SDL_GameControllerAxis = (key == .left) ? SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
let axis: SDL_GameControllerAxis = (key == .left) ? SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||||
let scaledValue = Sint16(value * 32767.0)
|
let scaledValue = Sint16(value * 32767.0)
|
||||||
updateAxisValue(value: scaledValue, forAxis: axis)
|
updateAxisValue(value: scaledValue, forAxis: axis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func rumble(lowFreq: Float, highFreq: Float) {
|
||||||
|
do {
|
||||||
|
// Low-frequency haptic pattern
|
||||||
|
let lowFreqPattern = try CHHapticPattern(events: [
|
||||||
|
CHHapticEvent(eventType: .hapticTransient, parameters: [
|
||||||
|
CHHapticEventParameter(parameterID: .hapticIntensity, value: lowFreq),
|
||||||
|
CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5)
|
||||||
|
], relativeTime: 0, duration: 0.2)
|
||||||
|
], parameters: [])
|
||||||
|
|
||||||
|
// High-frequency haptic pattern
|
||||||
|
let highFreqPattern = try CHHapticPattern(events: [
|
||||||
|
CHHapticEvent(eventType: .hapticTransient, parameters: [
|
||||||
|
CHHapticEventParameter(parameterID: .hapticIntensity, value: highFreq),
|
||||||
|
CHHapticEventParameter(parameterID: .hapticSharpness, value: 1.0)
|
||||||
|
], relativeTime: 0.2, duration: 0.2)
|
||||||
|
], parameters: [])
|
||||||
|
|
||||||
|
// Create and start the haptic engine
|
||||||
|
let engine = try CHHapticEngine()
|
||||||
|
try engine.start()
|
||||||
|
|
||||||
|
// Create and play the low-frequency player
|
||||||
|
let lowFreqPlayer = try engine.makePlayer(with: lowFreqPattern)
|
||||||
|
try lowFreqPlayer.start(atTime: 0)
|
||||||
|
|
||||||
|
// Create and play the high-frequency player after a short delay
|
||||||
|
let highFreqPlayer = try engine.makePlayer(with: highFreqPattern)
|
||||||
|
try highFreqPlayer.start(atTime: 0.2)
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Error creating haptic patterns: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
||||||
guard controller != nil else { return }
|
guard controller != nil else { return }
|
||||||
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
||||||
|
@ -204,6 +208,7 @@ class NativeController: Hashable, BaseController {
|
||||||
func setButtonState(_ state: Uint8, for button: VirtualControllerButton) {
|
func setButtonState(_ state: Uint8, for button: VirtualControllerButton) {
|
||||||
guard controller != nil else { return }
|
guard controller != nil else { return }
|
||||||
|
|
||||||
|
// print("Button: \(button.rawValue) {state: \(state)}")
|
||||||
if (button == .leftTrigger || button == .rightTrigger) && (state == 1 || state == 0) {
|
if (button == .leftTrigger || button == .rightTrigger) && (state == 1 || state == 0) {
|
||||||
let axis: SDL_GameControllerAxis = (button == .leftTrigger) ? SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
let axis: SDL_GameControllerAxis = (button == .leftTrigger) ? SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||||
let value: Int = (state == 1) ? 32767 : 0
|
let value: Int = (state == 1) ? 32767 : 0
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
//
|
|
||||||
// RumbleController.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by MediaMoots on 2025/5/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreHaptics
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class RumbleController {
|
|
||||||
|
|
||||||
private var engine: CHHapticEngine?
|
|
||||||
private var lowHapticPlayer: CHHapticPatternPlayer?
|
|
||||||
private var highHapticPlayer: CHHapticPatternPlayer?
|
|
||||||
private var rumbleMultiplier: Float = 1.0
|
|
||||||
|
|
||||||
// The duration of each continuous haptic event.
|
|
||||||
// We'll restart the players before this duration expires.
|
|
||||||
private let hapticEventDuration: TimeInterval = 20
|
|
||||||
|
|
||||||
// Timer to schedule player restarts
|
|
||||||
private var playerRestartTimer: Timer?
|
|
||||||
|
|
||||||
// Interval before the haptic event duration runs out to restart
|
|
||||||
private let restartGracePeriod: TimeInterval = 1.0
|
|
||||||
|
|
||||||
init (engine: CHHapticEngine?, rumbleMultiplier: Float) {
|
|
||||||
self.engine = engine
|
|
||||||
self.rumbleMultiplier = rumbleMultiplier
|
|
||||||
|
|
||||||
createPlayers()
|
|
||||||
setupPlayerRestartTimer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deinitializer to clean up the timer and stop players when the controller is deallocated
|
|
||||||
deinit {
|
|
||||||
playerRestartTimer?.invalidate() // Stop the timer
|
|
||||||
playerRestartTimer = nil
|
|
||||||
|
|
||||||
// Optionally stop the haptic players immediately
|
|
||||||
try? lowHapticPlayer?.stop(atTime: CHHapticTimeImmediate)
|
|
||||||
try? highHapticPlayer?.stop(atTime: CHHapticTimeImmediate)
|
|
||||||
|
|
||||||
// print("RumbleController deinitialized.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private Methods for Player Management
|
|
||||||
private func createPlayers() {
|
|
||||||
// Ensure the engine is available before proceeding
|
|
||||||
guard let engine = self.engine else {
|
|
||||||
// print("CHHapticEngine is nil. Cannot initialize RumbleController.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let baseIntensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0)
|
|
||||||
|
|
||||||
let lowSharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.0)
|
|
||||||
let highSharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 1)
|
|
||||||
|
|
||||||
// Create continuous haptic events with the defined duration
|
|
||||||
let lowContinuousEvent = CHHapticEvent(eventType: .hapticContinuous, parameters: [baseIntensity, lowSharpness], relativeTime: 0, duration: hapticEventDuration)
|
|
||||||
let highContinuousEvent = CHHapticEvent(eventType: .hapticContinuous, parameters: [baseIntensity, highSharpness], relativeTime: 0, duration: hapticEventDuration)
|
|
||||||
|
|
||||||
// Create patterns from the continuous haptic events.
|
|
||||||
let lowPattern = try CHHapticPattern(events: [lowContinuousEvent], parameters: [])
|
|
||||||
let highPattern = try CHHapticPattern(events: [highContinuousEvent], parameters: [])
|
|
||||||
|
|
||||||
// Make players from the patterns
|
|
||||||
lowHapticPlayer = try engine.makePlayer(with: lowPattern)
|
|
||||||
highHapticPlayer = try engine.makePlayer(with: highPattern)
|
|
||||||
|
|
||||||
rumble(lowFreq: 0, highFreq: 0)
|
|
||||||
|
|
||||||
// Start players initially
|
|
||||||
try lowHapticPlayer?.start(atTime: 0)
|
|
||||||
try highHapticPlayer?.start(atTime: 0)
|
|
||||||
} catch {
|
|
||||||
// print("Error initializing RumbleController or setting up haptic player: \(error.localizedDescription)")
|
|
||||||
|
|
||||||
// Clean up if setup fails
|
|
||||||
lowHapticPlayer = nil
|
|
||||||
highHapticPlayer = nil
|
|
||||||
playerRestartTimer?.invalidate()
|
|
||||||
playerRestartTimer = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupPlayerRestartTimer() {
|
|
||||||
// Invalidate any existing timer to prevent multiple timers if init is called multiple times
|
|
||||||
playerRestartTimer?.invalidate()
|
|
||||||
|
|
||||||
// Calculate the interval for restarting: 1 second before the haptic event duration ends
|
|
||||||
let restartInterval = hapticEventDuration - restartGracePeriod
|
|
||||||
|
|
||||||
guard restartInterval > 0 else {
|
|
||||||
// print("Warning: hapticEventDuration (\(hapticEventDuration)s) is too short for scheduled restart with grace period (\(restartGracePeriod)s). Timer will not be set.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schedule a repeating timer that calls restartPlayers()
|
|
||||||
playerRestartTimer = Timer.scheduledTimer(withTimeInterval: restartInterval, repeats: true) { [weak self] _ in
|
|
||||||
self?.createPlayers()
|
|
||||||
}
|
|
||||||
// Ensure the timer is added to the current run loop in its default mode
|
|
||||||
RunLoop.current.add(playerRestartTimer!, forMode: .default)
|
|
||||||
|
|
||||||
// print("Haptic Players restart timer scheduled to fire every \(restartInterval) seconds.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Public Rumble Control
|
|
||||||
|
|
||||||
public func rumble(lowFreq: Float, highFreq: Float) {
|
|
||||||
|
|
||||||
// Normalize SDL values (0-65535) to CoreHaptics range (0.0-1.0)
|
|
||||||
let normalizedLow = min(1.0, max(0.0, lowFreq * rumbleMultiplier / 65535.0))
|
|
||||||
let normalizedHigh = min(1.0, max(0.0, highFreq * rumbleMultiplier / 65535.0))
|
|
||||||
|
|
||||||
// Create dynamic parameters to control intensity
|
|
||||||
let lowIntensityParameter = CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: normalizedLow, relativeTime: 0)
|
|
||||||
let highIntensityParameter = CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: normalizedHigh, relativeTime: 0)
|
|
||||||
|
|
||||||
// Send parameters to the players
|
|
||||||
do {
|
|
||||||
try lowHapticPlayer?.sendParameters([lowIntensityParameter], atTime: 0)
|
|
||||||
try highHapticPlayer?.sendParameters([highIntensityParameter], atTime: 0)
|
|
||||||
} catch {
|
|
||||||
// print("Error sending haptic parameters: \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,49 +9,16 @@ import Foundation
|
||||||
import CoreHaptics
|
import CoreHaptics
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class VirtualController : BaseController {
|
class VirtualController {
|
||||||
private var instanceID: SDL_JoystickID = -1
|
private var instanceID: SDL_JoystickID = -1
|
||||||
private var controller: OpaquePointer?
|
private var controller: OpaquePointer?
|
||||||
private let hapticEngine: CHHapticEngine?
|
|
||||||
private let rumbleController: RumbleController?
|
|
||||||
private var deviceMotionProvider: DeviceMotionProvider?
|
|
||||||
|
|
||||||
public let controllername = "MeloNX Touch Controller"
|
public let controllername = "MeloNX Touch Controller"
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Setup Haptics
|
|
||||||
hapticEngine = try? CHHapticEngine()
|
|
||||||
if let hapticsEngine = hapticEngine {
|
|
||||||
do {
|
|
||||||
try hapticsEngine.start()
|
|
||||||
rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: 2.0)
|
|
||||||
|
|
||||||
// print("CHHapticEngine started and RumbleController initialized.")
|
|
||||||
} catch {
|
|
||||||
// print("Error starting CHHapticEngine: \(error.localizedDescription)")
|
|
||||||
rumbleController = nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// print("CHHapticEngine is nil. Cannot initialize RumbleController.")
|
|
||||||
rumbleController = nil
|
|
||||||
}
|
|
||||||
setupVirtualController()
|
setupVirtualController()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func tryRegisterMotion(slot: UInt8) {
|
|
||||||
// Setup Motion
|
|
||||||
let dsuServer = DSUServer.shared
|
|
||||||
|
|
||||||
deviceMotionProvider = DeviceMotionProvider(slot: slot)
|
|
||||||
if let provider = deviceMotionProvider {
|
|
||||||
dsuServer.register(provider)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func tryGetMotionProvider() -> DSUMotionProvider? {
|
|
||||||
return deviceMotionProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupVirtualController() {
|
private func setupVirtualController() {
|
||||||
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
||||||
SDL_InitSubSystem(Uint32(SDL_INIT_GAMECONTROLLER))
|
SDL_InitSubSystem(Uint32(SDL_INIT_GAMECONTROLLER))
|
||||||
|
@ -69,50 +36,96 @@ class VirtualController : BaseController {
|
||||||
button_mask: 0,
|
button_mask: 0,
|
||||||
axis_mask: 0,
|
axis_mask: 0,
|
||||||
name: controllername.withCString { $0 },
|
name: controllername.withCString { $0 },
|
||||||
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
userdata: nil,
|
||||||
Update: { userdata in
|
Update: { userdata in
|
||||||
// Update joystick state here
|
// Update joystick state here
|
||||||
},
|
},
|
||||||
SetPlayerIndex: { userdata, playerIndex in
|
SetPlayerIndex: { userdata, playerIndex in
|
||||||
// print("Player index set to \(playerIndex)")
|
print("Player index set to \(playerIndex)")
|
||||||
},
|
},
|
||||||
Rumble: { userdata, lowFreq, highFreq in
|
Rumble: { userdata, lowFreq, highFreq in
|
||||||
// print("Rumble with \(lowFreq), \(highFreq)")
|
print("Rumble with \(lowFreq), \(highFreq)")
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
guard let userdata else { return 0 }
|
VirtualController.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq))
|
||||||
let _self = Unmanaged<VirtualController>.fromOpaque(userdata).takeUnretainedValue()
|
|
||||||
_self.rumbleController?.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq))
|
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
||||||
// print("Trigger rumble with \(leftRumble), \(rightRumble)")
|
print("Trigger rumble with \(leftRumble), \(rightRumble)")
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
SetLED: { userdata, red, green, blue in
|
SetLED: { userdata, red, green, blue in
|
||||||
// print("Set LED to RGB(\(red), \(green), \(blue))")
|
print("Set LED to RGB(\(red), \(green), \(blue))")
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
SendEffect: { userdata, data, size in
|
SendEffect: { userdata, data, size in
|
||||||
// print("Effect sent with size \(size)")
|
print("Effect sent with size \(size)")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
instanceID = SDL_JoystickAttachVirtualEx(&joystickDesc)// SDL_JoystickAttachVirtual(SDL_JoystickType(SDL_JOYSTICK_TYPE_GAMECONTROLLER.rawValue), 6, 15, 1)
|
instanceID = SDL_JoystickAttachVirtualEx(&joystickDesc)// SDL_JoystickAttachVirtual(SDL_JoystickType(SDL_JOYSTICK_TYPE_GAMECONTROLLER.rawValue), 6, 15, 1)
|
||||||
if instanceID < 0 {
|
if instanceID < 0 {
|
||||||
// print("Failed to create virtual joystick: \(String(cString: SDL_GetError()))")
|
print("Failed to create virtual joystick: \(String(cString: SDL_GetError()))")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open a game controller for the virtual joystick
|
||||||
|
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
||||||
controller = SDL_GameControllerOpen(Int32(instanceID))
|
controller = SDL_GameControllerOpen(Int32(instanceID))
|
||||||
|
|
||||||
if controller == nil {
|
if controller == nil {
|
||||||
// print("Failed to create virtual controller: \(String(cString: SDL_GetError()))")
|
print("Failed to create virtual controller: \(String(cString: SDL_GetError()))")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func rumble(lowFreq: Float, highFreq: Float, engine: CHHapticEngine? = nil) {
|
||||||
|
do {
|
||||||
|
let lowFreqPattern = try CHHapticPattern(events: [
|
||||||
|
CHHapticEvent(eventType: .hapticTransient, parameters: [
|
||||||
|
CHHapticEventParameter(parameterID: .hapticIntensity, value: lowFreq),
|
||||||
|
CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5)
|
||||||
|
], relativeTime: 0, duration: 0.2)
|
||||||
|
], parameters: [])
|
||||||
|
|
||||||
|
|
||||||
|
let highFreqPattern = try CHHapticPattern(events: [
|
||||||
|
CHHapticEvent(eventType: .hapticTransient, parameters: [
|
||||||
|
CHHapticEventParameter(parameterID: .hapticIntensity, value: highFreq),
|
||||||
|
CHHapticEventParameter(parameterID: .hapticSharpness, value: 1.0)
|
||||||
|
], relativeTime: 0.2, duration: 0.2)
|
||||||
|
], parameters: [])
|
||||||
|
|
||||||
|
var engine = engine
|
||||||
|
|
||||||
|
if engine == nil {
|
||||||
|
if hapticEngine == nil {
|
||||||
|
hapticEngine = try CHHapticEngine()
|
||||||
|
try hapticEngine?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
engine = hapticEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let engine else {
|
||||||
|
return print("Error creating haptic patterns: hapticEngine is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
let lowFreqPlayer = try engine.makePlayer(with: lowFreqPattern)
|
||||||
|
try lowFreqPlayer.start(atTime: 0)
|
||||||
|
|
||||||
|
let highFreqPlayer = try engine.makePlayer(with: highFreqPattern)
|
||||||
|
try highFreqPlayer.start(atTime: 0)
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Error creating haptic patterns: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var hapticEngine: CHHapticEngine?
|
||||||
|
|
||||||
|
|
||||||
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
||||||
guard controller != nil else { return }
|
guard controller != nil else { return }
|
||||||
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
||||||
|
@ -120,8 +133,10 @@ class VirtualController : BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func thumbstickMoved(_ stick: ThumbstickType, x: Double, y: Double) {
|
func thumbstickMoved(_ stick: ThumbstickType, x: Double, y: Double) {
|
||||||
let scaledX = Int16(min(32767.0, max(-32768.0, x * 32767.0)))
|
let scaleFactor = 32767.0 / 160
|
||||||
let scaledY = Int16(min(32767.0, max(-32768.0, y * 32767.0)))
|
|
||||||
|
let scaledX = Int16(min(32767.0, max(-32768.0, x * scaleFactor)))
|
||||||
|
let scaledY = Int16(min(32767.0, max(-32768.0, y * scaleFactor)))
|
||||||
|
|
||||||
if stick == .right {
|
if stick == .right {
|
||||||
updateAxisValue(value: scaledX, forAxis: SDL_GameControllerAxis(SDL_CONTROLLER_AXIS_RIGHTX.rawValue))
|
updateAxisValue(value: scaledX, forAxis: SDL_GameControllerAxis(SDL_CONTROLLER_AXIS_RIGHTX.rawValue))
|
||||||
|
@ -135,7 +150,7 @@ class VirtualController : BaseController {
|
||||||
func setButtonState(_ state: Uint8, for button: VirtualControllerButton) {
|
func setButtonState(_ state: Uint8, for button: VirtualControllerButton) {
|
||||||
guard controller != nil else { return }
|
guard controller != nil else { return }
|
||||||
|
|
||||||
// // print("Button: \(button.rawValue) {state: \(state)}")
|
print("Button: \(button.rawValue) {state: \(state)}")
|
||||||
if (button == .leftTrigger || button == .rightTrigger) && (state == 1 || state == 0) {
|
if (button == .leftTrigger || button == .rightTrigger) && (state == 1 || state == 0) {
|
||||||
let axis: SDL_GameControllerAxis = (button == .leftTrigger) ? SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
let axis: SDL_GameControllerAxis = (button == .leftTrigger) ? SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||||
let value: Int = (state == 1) ? 32767 : 0
|
let value: Int = (state == 1) ? 32767 : 0
|
||||||
|
@ -159,10 +174,10 @@ class VirtualController : BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VirtualControllerButton: Int {
|
enum VirtualControllerButton: Int {
|
||||||
case A
|
|
||||||
case B
|
case B
|
||||||
case X
|
case A
|
||||||
case Y
|
case Y
|
||||||
|
case X
|
||||||
case back
|
case back
|
||||||
case guide
|
case guide
|
||||||
case start
|
case start
|
||||||
|
@ -176,24 +191,6 @@ enum VirtualControllerButton: Int {
|
||||||
case dPadRight
|
case dPadRight
|
||||||
case leftTrigger
|
case leftTrigger
|
||||||
case rightTrigger
|
case rightTrigger
|
||||||
|
|
||||||
var isTrigger: Bool {
|
|
||||||
switch self {
|
|
||||||
case .leftTrigger, .rightTrigger, .leftShoulder, .rightShoulder:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isSmall: Bool {
|
|
||||||
switch self {
|
|
||||||
case .back, .start, .guide:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ThumbstickType: Int {
|
enum ThumbstickType: Int {
|
||||||
|
|
|
@ -13,7 +13,7 @@ class MemoryUsageMonitor: ObservableObject {
|
||||||
private var timer: Timer?
|
private var timer: Timer?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { [weak self] _ in
|
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
||||||
self?.updateMemoryUsage()
|
self?.updateMemoryUsage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,12 +32,11 @@ class MemoryUsageMonitor: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
if result == KERN_SUCCESS {
|
if result == KERN_SUCCESS {
|
||||||
memoryUsage = 0
|
|
||||||
memoryUsage = taskInfo.phys_footprint
|
memoryUsage = taskInfo.phys_footprint
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// print("Error with task_info(): " +
|
print("Error with task_info(): " +
|
||||||
// (String(cString: mach_error_string(result), encoding: String.Encoding.ascii) ?? "unknown error"))
|
(String(cString: mach_error_string(result), encoding: String.Encoding.ascii) ?? "unknown error"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +46,7 @@ class MemoryUsageMonitor: ObservableObject {
|
||||||
formatter.countStyle = .memory
|
formatter.countStyle = .memory
|
||||||
return formatter.string(fromByteCount: Int64(bytes))
|
return formatter.string(fromByteCount: Int64(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// Untitled.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 21/12/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PerformanceOverlayView: View {
|
||||||
|
@StateObject private var memorymonitor = MemoryUsageMonitor()
|
||||||
|
|
||||||
|
@StateObject private var fpsmonitor = FPSMonitor()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Text("\(fpsmonitor.formatFPS())")
|
||||||
|
Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,28 +6,26 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class MTLHud: ObservableObject {
|
|
||||||
@Published var canMetalHud: Bool = false
|
|
||||||
|
|
||||||
@AppStorage("MTL_HUD_ENABLED") var metalHudEnabled: Bool = false {
|
class MTLHud {
|
||||||
didSet {
|
|
||||||
if metalHudEnabled {
|
var canMetalHud: Bool {
|
||||||
enable()
|
return openMetalDylib()
|
||||||
} else {
|
|
||||||
disable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isEnabled: Bool {
|
||||||
|
if let getenv = getenv("MTL_HUD_ENABLED") {
|
||||||
|
return String(cString: getenv).contains("1")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
static let shared = MTLHud()
|
static let shared = MTLHud()
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
canMetalHud = openMetalDylib() // i'm fixing the warnings just because you said i suck at coding Autumn (propenchiefer, https://youtu.be/tc65SNOTMz4 7:23)
|
openMetalDylib()
|
||||||
|
if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") {
|
||||||
if metalHudEnabled {
|
|
||||||
enable()
|
enable()
|
||||||
} else {
|
} else {
|
||||||
disable()
|
disable()
|
||||||
|
@ -37,9 +35,16 @@ class MTLHud: ObservableObject {
|
||||||
func openMetalDylib() -> Bool {
|
func openMetalDylib() -> Bool {
|
||||||
let path = "/usr/lib/libMTLHud.dylib"
|
let path = "/usr/lib/libMTLHud.dylib"
|
||||||
|
|
||||||
|
// Load the dynamic library
|
||||||
if dlopen(path, RTLD_NOW) != nil {
|
if dlopen(path, RTLD_NOW) != nil {
|
||||||
|
// Library loaded successfully
|
||||||
|
print("Library loaded from \(path)")
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
// Handle error
|
||||||
|
if let error = String(validatingUTF8: dlerror()) {
|
||||||
|
print("Error loading library: \(error)")
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,94 +10,6 @@ import SwiftUI
|
||||||
import GameController
|
import GameController
|
||||||
import MetalKit
|
import MetalKit
|
||||||
import Metal
|
import Metal
|
||||||
import Darwin
|
|
||||||
|
|
||||||
class LogCapture {
|
|
||||||
static let shared = LogCapture()
|
|
||||||
|
|
||||||
private var stdoutPipe: Pipe?
|
|
||||||
private var stderrPipe: Pipe?
|
|
||||||
private let originalStdout: Int32
|
|
||||||
private let originalStderr: Int32
|
|
||||||
|
|
||||||
var capturedLogs: [String] = [] {
|
|
||||||
didSet {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
NotificationCenter.default.post(name: .newLogCaptured, object: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private init() {
|
|
||||||
originalStdout = dup(STDOUT_FILENO)
|
|
||||||
originalStderr = dup(STDERR_FILENO)
|
|
||||||
startCapturing()
|
|
||||||
}
|
|
||||||
|
|
||||||
func startCapturing() {
|
|
||||||
stdoutPipe = Pipe()
|
|
||||||
stderrPipe = Pipe()
|
|
||||||
|
|
||||||
redirectOutput(to: stdoutPipe!, fileDescriptor: STDOUT_FILENO)
|
|
||||||
redirectOutput(to: stderrPipe!, fileDescriptor: STDERR_FILENO)
|
|
||||||
|
|
||||||
setupReadabilityHandler(for: stdoutPipe!, isStdout: true)
|
|
||||||
setupReadabilityHandler(for: stderrPipe!, isStdout: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stopCapturing() {
|
|
||||||
dup2(originalStdout, STDOUT_FILENO)
|
|
||||||
dup2(originalStderr, STDERR_FILENO)
|
|
||||||
|
|
||||||
stdoutPipe?.fileHandleForReading.readabilityHandler = nil
|
|
||||||
stderrPipe?.fileHandleForReading.readabilityHandler = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
private func redirectOutput(to pipe: Pipe, fileDescriptor: Int32) {
|
|
||||||
dup2(pipe.fileHandleForWriting.fileDescriptor, fileDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupReadabilityHandler(for pipe: Pipe, isStdout: Bool) {
|
|
||||||
pipe.fileHandleForReading.readabilityHandler = { [weak self] fileHandle in
|
|
||||||
let data = fileHandle.availableData
|
|
||||||
let originalFD = isStdout ? self?.originalStdout : self?.originalStderr
|
|
||||||
write(originalFD ?? STDOUT_FILENO, (data as NSData).bytes, data.count)
|
|
||||||
|
|
||||||
if let logString = String(data: data, encoding: .utf8),
|
|
||||||
let cleanedLog = self?.cleanLog(logString), !cleanedLog.isEmpty {
|
|
||||||
self?.capturedLogs.append(cleanedLog)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func cleanLog(_ raw: String) -> String? {
|
|
||||||
let lines = raw.split(separator: "\n")
|
|
||||||
let filteredLines = lines.filter { line in
|
|
||||||
!line.contains("SwiftUI") &&
|
|
||||||
!line.contains("ForEach") &&
|
|
||||||
!line.contains("VStack") &&
|
|
||||||
!line.contains("Invalid frame dimension (negative or non-finite).")
|
|
||||||
}
|
|
||||||
|
|
||||||
let cleaned = filteredLines.map { line -> String in
|
|
||||||
if let tabRange = line.range(of: "\t") {
|
|
||||||
return line[tabRange.upperBound...].trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
}
|
|
||||||
return line.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
}.joined(separator: "\n")
|
|
||||||
|
|
||||||
return cleaned.isEmpty ? nil : cleaned.replacingOccurrences(of: "\n\n", with: "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
stopCapturing()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension Notification.Name {
|
|
||||||
static let newLogCaptured = Notification.Name("newLogCaptured")
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Controller: Identifiable, Hashable {
|
struct Controller: Identifiable, Hashable {
|
||||||
var id: String
|
var id: String
|
||||||
|
@ -118,106 +30,61 @@ struct iOSNav<Content: View>: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func threadEntry(_ arg: () -> Void) -> UnsafeMutableRawPointer? {
|
|
||||||
arg()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
class Ryujinx {
|
||||||
class Ryujinx : ObservableObject {
|
private var isRunning = false
|
||||||
@Published var isRunning = false
|
|
||||||
|
|
||||||
let virtualController = VirtualController()
|
let virtualController = VirtualController()
|
||||||
|
|
||||||
@Published var controllerMap: [Controller] = []
|
@Published var controllerMap: [Controller] = []
|
||||||
@Published var metalLayer: CAMetalLayer? = nil
|
@Published var metalLayer: CAMetalLayer? = nil
|
||||||
@Published var isPortrait = false
|
|
||||||
@Published var firmwareversion = "0"
|
@Published var firmwareversion = "0"
|
||||||
@Published var emulationUIView: MeloMTKView? = nil
|
@Published var emulationUIView: MeloMTKView? = nil
|
||||||
@Published var config: Ryujinx.Arguments? = nil
|
@Published var config: Ryujinx.Configuration? = nil
|
||||||
@Published var games: [Game] = []
|
@Published var games: [Game] = []
|
||||||
|
|
||||||
@Published var defMLContentSize: CGFloat?
|
@Published var defMLContentSize: CGFloat?
|
||||||
|
|
||||||
var thread: pthread_t? = nil
|
|
||||||
|
|
||||||
@Published var jitenabled = false
|
|
||||||
|
|
||||||
var shouldMetal: Bool {
|
var shouldMetal: Bool {
|
||||||
metalLayer == nil
|
metalLayer == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
static let shared = Ryujinx()
|
static let shared = Ryujinx()
|
||||||
|
|
||||||
func addGames() {
|
private init() {
|
||||||
self.games = loadGames()
|
self.games = loadGames()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runloop(_ cool: @escaping () -> Void) {
|
public struct Configuration : Codable, Equatable {
|
||||||
if UserDefaults.standard.bool(forKey: "runOnMainThread") {
|
|
||||||
RunLoop.main.perform {
|
|
||||||
cool()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Box the closure
|
|
||||||
let boxed = Unmanaged.passRetained(ClosureBox(cool)).toOpaque()
|
|
||||||
|
|
||||||
var thread: pthread_t?
|
|
||||||
let result = pthread_create(&thread, nil, { arg in
|
|
||||||
let unmanaged = Unmanaged<ClosureBox>.fromOpaque(arg)
|
|
||||||
let box = unmanaged.takeRetainedValue()
|
|
||||||
box.closure()
|
|
||||||
return nil
|
|
||||||
}, boxed)
|
|
||||||
|
|
||||||
if result == 0 {
|
|
||||||
pthread_detach(thread!)
|
|
||||||
} else {
|
|
||||||
print("Failed to create thread: \(result)")
|
|
||||||
Unmanaged<ClosureBox>.fromOpaque(boxed).release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ClosureBox {
|
|
||||||
let closure: () -> Void
|
|
||||||
init(_ closure: @escaping () -> Void) {
|
|
||||||
self.closure = closure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Arguments : Observable, Codable, Equatable {
|
|
||||||
var gamepath: String
|
var gamepath: String
|
||||||
var inputids: [String]
|
var inputids: [String]
|
||||||
var inputDSUServers: [String]
|
var resscale: Float
|
||||||
var resscale: Float = 1.0
|
var debuglogs: Bool
|
||||||
var debuglogs: Bool = false
|
var tracelogs: Bool
|
||||||
var tracelogs: Bool = false
|
var nintendoinput: Bool
|
||||||
var nintendoinput: Bool = true
|
var enableInternet: Bool
|
||||||
var enableInternet: Bool = false
|
var listinputids: Bool
|
||||||
var listinputids: Bool = false
|
var aspectRatio: AspectRatio
|
||||||
var aspectRatio: AspectRatio = .fixed16x9
|
var memoryManagerMode: String
|
||||||
var memoryManagerMode: String = "HostMappedUnsafe"
|
var disableShaderCache: Bool
|
||||||
var disableShaderCache: Bool = false
|
var hypervisor: Bool
|
||||||
var hypervisor: Bool = false
|
var disableDockedMode: Bool
|
||||||
var disableDockedMode: Bool = false
|
var enableTextureRecompression: Bool
|
||||||
var enableTextureRecompression: Bool = true
|
var additionalArgs: [String]
|
||||||
var additionalArgs: [String] = []
|
var maxAnisotropy: Float
|
||||||
var maxAnisotropy: Float = 1.0
|
var macroHLE: Bool
|
||||||
var macroHLE: Bool = true
|
var ignoreMissingServices: Bool
|
||||||
var ignoreMissingServices: Bool = false
|
var expandRam: Bool
|
||||||
var expandRam: Bool = false
|
var dfsIntegrityChecks: Bool
|
||||||
var dfsIntegrityChecks: Bool = false
|
var disablePTC: Bool
|
||||||
var disablePTC: Bool = false
|
var disablevsync: Bool
|
||||||
var disablevsync: Bool = false
|
var language: SystemLanguage
|
||||||
var language: SystemLanguage = .americanEnglish
|
var regioncode: SystemRegionCode
|
||||||
var regioncode: SystemRegionCode = .usa
|
var handHeldController: Bool
|
||||||
var handHeldController: Bool = true
|
|
||||||
|
|
||||||
|
|
||||||
init(gamepath: String = "",
|
init(gamepath: String,
|
||||||
inputids: [String] = [],
|
inputids: [String] = [],
|
||||||
inputDSUServers: [String] = [],
|
|
||||||
debuglogs: Bool = false,
|
debuglogs: Bool = false,
|
||||||
tracelogs: Bool = false,
|
tracelogs: Bool = false,
|
||||||
listinputids: Bool = false,
|
listinputids: Bool = false,
|
||||||
|
@ -240,11 +107,10 @@ class Ryujinx : ObservableObject {
|
||||||
disablevsync: Bool = false,
|
disablevsync: Bool = false,
|
||||||
language: SystemLanguage = .americanEnglish,
|
language: SystemLanguage = .americanEnglish,
|
||||||
regioncode: SystemRegionCode = .usa,
|
regioncode: SystemRegionCode = .usa,
|
||||||
handHeldController: Bool = false,
|
handHeldController: Bool = false
|
||||||
) {
|
) {
|
||||||
self.gamepath = gamepath
|
self.gamepath = gamepath
|
||||||
self.inputids = inputids
|
self.inputids = inputids
|
||||||
self.inputDSUServers = inputDSUServers
|
|
||||||
self.debuglogs = debuglogs
|
self.debuglogs = debuglogs
|
||||||
self.tracelogs = tracelogs
|
self.tracelogs = tracelogs
|
||||||
self.listinputids = listinputids
|
self.listinputids = listinputids
|
||||||
|
@ -269,71 +135,17 @@ class Ryujinx : ObservableObject {
|
||||||
self.regioncode = regioncode
|
self.regioncode = regioncode
|
||||||
self.handHeldController = handHeldController
|
self.handHeldController = handHeldController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static func == (lhs: Arguments, rhs: Arguments) -> Bool {
|
|
||||||
return lhs.resscale == rhs.resscale &&
|
|
||||||
lhs.debuglogs == rhs.debuglogs &&
|
|
||||||
lhs.tracelogs == rhs.tracelogs &&
|
|
||||||
lhs.nintendoinput == rhs.nintendoinput &&
|
|
||||||
lhs.enableInternet == rhs.enableInternet &&
|
|
||||||
lhs.listinputids == rhs.listinputids &&
|
|
||||||
lhs.aspectRatio == rhs.aspectRatio &&
|
|
||||||
lhs.memoryManagerMode == rhs.memoryManagerMode &&
|
|
||||||
lhs.disableShaderCache == rhs.disableShaderCache &&
|
|
||||||
lhs.hypervisor == rhs.hypervisor &&
|
|
||||||
lhs.disableDockedMode == rhs.disableDockedMode &&
|
|
||||||
lhs.enableTextureRecompression == rhs.enableTextureRecompression &&
|
|
||||||
lhs.additionalArgs == rhs.additionalArgs &&
|
|
||||||
lhs.maxAnisotropy == rhs.maxAnisotropy &&
|
|
||||||
lhs.macroHLE == rhs.macroHLE &&
|
|
||||||
lhs.ignoreMissingServices == rhs.ignoreMissingServices &&
|
|
||||||
lhs.expandRam == rhs.expandRam &&
|
|
||||||
lhs.dfsIntegrityChecks == rhs.dfsIntegrityChecks &&
|
|
||||||
lhs.disablePTC == rhs.disablePTC &&
|
|
||||||
lhs.disablevsync == rhs.disablevsync &&
|
|
||||||
lhs.language == rhs.language &&
|
|
||||||
lhs.regioncode == rhs.regioncode &&
|
|
||||||
lhs.handHeldController == rhs.handHeldController
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func start(with config: Arguments) throws {
|
func start(with config: Configuration) throws {
|
||||||
guard !isRunning else {
|
guard !isRunning else {
|
||||||
throw RyujinxError.alreadyRunning
|
throw RyujinxError.alreadyRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
|
RunLoop.current.perform { [self] in
|
||||||
if UserDefaults.standard.bool(forKey: "lockInApp") {
|
|
||||||
let cool = Thread {
|
|
||||||
while true {
|
|
||||||
if UserDefaults.standard.bool(forKey: "lockInApp") {
|
|
||||||
if let workspaceClass = NSClassFromString("LSApplicationWorkspace") as? NSObject.Type,
|
|
||||||
let workspace = workspaceClass.perform(NSSelectorFromString("defaultWorkspace"))?.takeUnretainedValue() {
|
|
||||||
|
|
||||||
let selector = NSSelectorFromString("openApplicationWithBundleID:")
|
|
||||||
|
|
||||||
if workspace.responds(to: selector) {
|
|
||||||
workspace.perform(selector, with: Bundle.main.bundleIdentifier ?? "")
|
|
||||||
} else {
|
|
||||||
print("Selector not found or not responding.")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print("Could not get LSApplicationWorkspace class.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cool.qualityOfService = .userInteractive
|
|
||||||
cool.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
runloop { [self] in
|
|
||||||
|
|
||||||
isRunning = true
|
isRunning = true
|
||||||
|
|
||||||
|
@ -353,9 +165,7 @@ class Ryujinx : ObservableObject {
|
||||||
let result = main_ryujinx_sdl(Int32(args.count), &argvPtrs)
|
let result = main_ryujinx_sdl(Int32(args.count), &argvPtrs)
|
||||||
|
|
||||||
if result != 0 {
|
if result != 0 {
|
||||||
DispatchQueue.main.async {
|
self.isRunning = false
|
||||||
self.isRunning = false
|
|
||||||
}
|
|
||||||
if let accessing, accessing {
|
if let accessing, accessing {
|
||||||
url!.stopAccessingSecurityScopedResource()
|
url!.stopAccessingSecurityScopedResource()
|
||||||
}
|
}
|
||||||
|
@ -364,102 +174,12 @@ class Ryujinx : ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
DispatchQueue.main.async {
|
self.isRunning = false
|
||||||
self.isRunning = false
|
Self.log("Emulation failed to start: \(error)")
|
||||||
}
|
|
||||||
Thread.sleep(forTimeInterval: 0.3)
|
|
||||||
let logs = LogCapture.shared.capturedLogs
|
|
||||||
let parsedLogs = extractExceptionInfo(logs)
|
|
||||||
if let parsedLogs {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let result = Array(logs.suffix(from: parsedLogs.lineIndex))
|
|
||||||
|
|
||||||
LogCapture.shared.capturedLogs = Array(LogCapture.shared.capturedLogs.prefix(upTo: parsedLogs.lineIndex))
|
|
||||||
let dateFormatter = DateFormatter()
|
|
||||||
dateFormatter.dateFormat = "yyyy-MM-dd_HH-mm-ss"
|
|
||||||
let currentDate = Date()
|
|
||||||
let dateString = dateFormatter.string(from: currentDate)
|
|
||||||
let path = URL.documentsDirectory.appendingPathComponent("StackTrace").appendingPathComponent("StackTrace-\(dateString).txt").path
|
|
||||||
|
|
||||||
self.saveArrayAsTextFile(strings: result, filePath: path)
|
|
||||||
|
|
||||||
|
|
||||||
presentAlert(title: "MeloNX Crashed!", message: parsedLogs.exceptionType + ": " + parsedLogs.message) {
|
|
||||||
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
||||||
exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
presentAlert(title: "MeloNX Crashed!", message: "Unknown Error") {
|
|
||||||
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
||||||
exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveArrayAsTextFile(strings: [String], filePath: String) {
|
|
||||||
let text = strings.joined(separator: "\n")
|
|
||||||
|
|
||||||
let path = URL.documentsDirectory.appendingPathComponent("StackTrace").path
|
|
||||||
|
|
||||||
do {
|
|
||||||
try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: false)
|
|
||||||
} catch {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
try text.write(to: URL(fileURLWithPath: filePath), atomically: true, encoding: .utf8)
|
|
||||||
print("File saved successfully.")
|
|
||||||
} catch {
|
|
||||||
print("Error saving file: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ExceptionInfo {
|
|
||||||
let exceptionType: String
|
|
||||||
let message: String
|
|
||||||
let lineIndex: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractExceptionInfo(_ logs: [String]) -> ExceptionInfo? {
|
|
||||||
for i in (0..<logs.count).reversed() {
|
|
||||||
let line = logs[i]
|
|
||||||
let pattern = "([\\w\\.]+Exception): ([^\\s]+(?:\\s+[^\\s]+)*)"
|
|
||||||
|
|
||||||
guard let regex = try? NSRegularExpression(pattern: pattern, options: []),
|
|
||||||
let match = regex.firstMatch(in: line, options: [], range: NSRange(location: 0, length: line.count)) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract exception type and message if pattern matches
|
|
||||||
if let exceptionTypeRange = Range(match.range(at: 1), in: line),
|
|
||||||
let messageRange = Range(match.range(at: 2), in: line) {
|
|
||||||
|
|
||||||
let exceptionType = String(line[exceptionTypeRange])
|
|
||||||
|
|
||||||
var message = String(line[messageRange])
|
|
||||||
if let atIndex = message.range(of: "\\s+at\\s+", options: .regularExpression) {
|
|
||||||
message = String(message[..<atIndex.lowerBound])
|
|
||||||
}
|
|
||||||
|
|
||||||
message = message.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
|
|
||||||
return ExceptionInfo(exceptionType: exceptionType, message: message, lineIndex: i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func stop() throws {
|
func stop() throws {
|
||||||
guard isRunning else {
|
guard isRunning else {
|
||||||
|
@ -468,12 +188,14 @@ class Ryujinx : ObservableObject {
|
||||||
|
|
||||||
isRunning = false
|
isRunning = false
|
||||||
|
|
||||||
UserDefaults.standard.set(false, forKey: "lockInApp")
|
|
||||||
|
|
||||||
self.emulationUIView = nil
|
self.emulationUIView = nil
|
||||||
self.metalLayer = nil
|
self.metalLayer = nil
|
||||||
|
|
||||||
|
stop_emulation()
|
||||||
|
}
|
||||||
|
|
||||||
|
var running: Bool {
|
||||||
|
return isRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -487,7 +209,7 @@ class Ryujinx : ObservableObject {
|
||||||
do {
|
do {
|
||||||
try fileManager.createDirectory(at: romsDirectory, withIntermediateDirectories: true, attributes: nil)
|
try fileManager.createDirectory(at: romsDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||||
} catch {
|
} catch {
|
||||||
// print("Failed to create roms directory: \(error)")
|
print("Failed to create roms directory: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var games: [Game] = []
|
var games: [Game] = []
|
||||||
|
@ -512,18 +234,19 @@ class Ryujinx : ObservableObject {
|
||||||
|
|
||||||
games.append(game)
|
games.append(game)
|
||||||
} catch {
|
} catch {
|
||||||
// print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return games
|
return games
|
||||||
} catch {
|
} catch {
|
||||||
// print("Error loading games from roms folder: \(error)")
|
print("Error loading games from roms folder: \(error)")
|
||||||
return games
|
return games
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildCommandLineArgs(from config: Arguments) -> [String] {
|
private func buildCommandLineArgs(from config: Configuration) -> [String] {
|
||||||
var args: [String] = []
|
var args: [String] = []
|
||||||
|
|
||||||
// Add the game path
|
// Add the game path
|
||||||
|
@ -535,41 +258,18 @@ class Ryujinx : ObservableObject {
|
||||||
|
|
||||||
args.append(contentsOf: ["--memory-manager-mode", config.memoryManagerMode])
|
args.append(contentsOf: ["--memory-manager-mode", config.memoryManagerMode])
|
||||||
|
|
||||||
args.append(contentsOf: ["--exclusive-fullscreen", String(true)])
|
// args.append(contentsOf: ["--exclusive-fullscreen", String(true)])
|
||||||
args.append(contentsOf: ["--exclusive-fullscreen-width", "\(Int(UIScreen.main.bounds.width))"])
|
// args.append(contentsOf: ["--exclusive-fullscreen-width", "\(Int(UIScreen.main.bounds.width))"])
|
||||||
args.append(contentsOf: ["--exclusive-fullscreen-height", "\(Int(UIScreen.main.bounds.height))"])
|
// args.append(contentsOf: ["--exclusive-fullscreen-height", "\(Int(UIScreen.main.bounds.height))"])
|
||||||
// We don't need this. Ryujinx should handle it fine :3
|
// We don't need this. Ryujinx should handle it fine :3
|
||||||
// this also causes crashes in some games :3
|
// this also causes crashes in some games :3
|
||||||
|
|
||||||
var model = ""
|
|
||||||
|
|
||||||
var systemInfo = utsname()
|
|
||||||
uname(&systemInfo)
|
|
||||||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
|
||||||
model = machineMirror.children.reduce("") { identifier, element in
|
|
||||||
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
|
||||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
args.append(contentsOf: ["--device-model", model])
|
|
||||||
|
|
||||||
args.append(contentsOf: ["--device-display-name", UIDevice.modelName])
|
|
||||||
|
|
||||||
if checkAppEntitlement("com.apple.developer.kernel.increased-memory-limit") {
|
|
||||||
args.append("--has-memory-entitlement")
|
|
||||||
}
|
|
||||||
|
|
||||||
args.append(contentsOf: ["--system-language", config.language.rawValue])
|
args.append(contentsOf: ["--system-language", config.language.rawValue])
|
||||||
|
|
||||||
args.append(contentsOf: ["--system-region", config.regioncode.rawValue])
|
args.append(contentsOf: ["--system-region", config.regioncode.rawValue])
|
||||||
|
|
||||||
args.append(contentsOf: ["--aspect-ratio", config.aspectRatio.rawValue])
|
args.append(contentsOf: ["--aspect-ratio", config.aspectRatio.rawValue])
|
||||||
|
|
||||||
args.append(contentsOf: ["--system-timezone", TimeZone.current.identifier])
|
|
||||||
|
|
||||||
// args.append(contentsOf: ["--system-time-offset", String(TimeZone.current.secondsFromGMT())])
|
|
||||||
|
|
||||||
|
|
||||||
if config.nintendoinput {
|
if config.nintendoinput {
|
||||||
args.append("--correct-controller")
|
args.append("--correct-controller")
|
||||||
}
|
}
|
||||||
|
@ -647,16 +347,6 @@ class Ryujinx : ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the input dsu servers (limit to 8 (used to be 4) just in case)
|
|
||||||
if !config.inputDSUServers.isEmpty {
|
|
||||||
config.inputDSUServers.prefix(8).enumerated().forEach { index, inputDSUServer in
|
|
||||||
if index == 0 {
|
|
||||||
args.append(contentsOf: ["--input-dsu-server-handheld", inputDSUServer])
|
|
||||||
}
|
|
||||||
args.append(contentsOf: ["--input-dsu-server-\(index + 1)", inputDSUServer])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args.append(contentsOf: config.additionalArgs)
|
args.append(contentsOf: config.additionalArgs)
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
@ -670,13 +360,18 @@ class Ryujinx : ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchFirmwareVersion() -> String {
|
func fetchFirmwareVersion() -> String {
|
||||||
let firmwareVersionPointer = installed_firmware_version()
|
do {
|
||||||
if let pointer = firmwareVersionPointer {
|
let firmwareVersionPointer = installed_firmware_version()
|
||||||
let firmwareVersion = String(cString: pointer)
|
if let pointer = firmwareVersionPointer {
|
||||||
DispatchQueue.main.async {
|
let firmwareVersion = String(cString: pointer)
|
||||||
self.firmwareversion = firmwareVersion
|
DispatchQueue.main.async {
|
||||||
|
self.firmwareversion = firmwareVersion
|
||||||
|
}
|
||||||
|
return firmwareVersion
|
||||||
}
|
}
|
||||||
return firmwareVersion
|
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "0"
|
return "0"
|
||||||
|
@ -684,7 +379,7 @@ class Ryujinx : ObservableObject {
|
||||||
|
|
||||||
func installFirmware(firmwarePath: String) {
|
func installFirmware(firmwarePath: String) {
|
||||||
guard let cString = firmwarePath.cString(using: .utf8) else {
|
guard let cString = firmwarePath.cString(using: .utf8) else {
|
||||||
// print("Invalid firmware path")
|
print("Invalid firmware path")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,12 +395,12 @@ class Ryujinx : ObservableObject {
|
||||||
guard let titleIdCString = titleId.cString(using: .utf8),
|
guard let titleIdCString = titleId.cString(using: .utf8),
|
||||||
let pathCString = path.cString(using: .utf8)
|
let pathCString = path.cString(using: .utf8)
|
||||||
else {
|
else {
|
||||||
// print("Invalid path")
|
print("Invalid path")
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let listPointer = get_dlc_nca_list(titleIdCString, pathCString)
|
let listPointer = get_dlc_nca_list(titleIdCString, pathCString)
|
||||||
// print("DLC parcing success: \(listPointer.success)")
|
print("DLC parcing success: \(listPointer.success)")
|
||||||
guard listPointer.success else { return [] }
|
guard listPointer.success else { return [] }
|
||||||
|
|
||||||
let list = Array(UnsafeBufferPointer(start: listPointer.items, count: Int(listPointer.size)))
|
let list = Array(UnsafeBufferPointer(start: listPointer.items, count: Int(listPointer.size)))
|
||||||
|
@ -757,7 +452,7 @@ class Ryujinx : ObservableObject {
|
||||||
let guid = generateGamepadId(joystickIndex: i)
|
let guid = generateGamepadId(joystickIndex: i)
|
||||||
let name = String(cString: SDL_GameControllerName(controller))
|
let name = String(cString: SDL_GameControllerName(controller))
|
||||||
|
|
||||||
// print("Controller \(i): \(name), GUID: \(guid ?? "")")
|
print("Controller \(i): \(name), GUID: \(guid ?? "")")
|
||||||
|
|
||||||
guard let guid else {
|
guard let guid else {
|
||||||
SDL_GameControllerClose(controller)
|
SDL_GameControllerClose(controller)
|
||||||
|
@ -788,163 +483,84 @@ class Ryujinx : ObservableObject {
|
||||||
do {
|
do {
|
||||||
if fileManager.fileExists(atPath: registeredFolder) {
|
if fileManager.fileExists(atPath: registeredFolder) {
|
||||||
try fileManager.removeItem(atPath: registeredFolder)
|
try fileManager.removeItem(atPath: registeredFolder)
|
||||||
// print("Folder removed successfully.")
|
print("Folder removed successfully.")
|
||||||
let version = fetchFirmwareVersion()
|
let version = fetchFirmwareVersion()
|
||||||
|
|
||||||
if version.isEmpty {
|
if version.isEmpty {
|
||||||
self.firmwareversion = "0"
|
self.firmwareversion = "0"
|
||||||
} else {
|
} else {
|
||||||
// print("Firmware eeeeee \(version)")
|
print("Firmware eeeeee \(version)")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// print("Folder does not exist.")
|
print("Folder does not exist.")
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// print("Error removing folder: \(error)")
|
print("Error removing folder: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func repeatuntilfindLayer() {
|
||||||
|
Task { @MainActor in
|
||||||
|
while self.metalLayer == nil {
|
||||||
|
let layer = self.getMetalLayer(nil)
|
||||||
|
|
||||||
|
if layer != nil {
|
||||||
|
self.metalLayer = layer
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.sleep(forTimeInterval: 0.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func getMetalLayer(_ window: OpaquePointer?) -> CAMetalLayer? {
|
||||||
|
var window = window
|
||||||
|
if window == nil {
|
||||||
|
window = SDL_GetWindowFromID(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowInfo = SDL_SysWMinfo()
|
||||||
|
SDL_GetWindowWMInfo(window, &windowInfo)
|
||||||
|
|
||||||
|
|
||||||
|
guard let uiWindow = windowInfo.info.uikit.window,
|
||||||
|
let rootView = uiWindow.takeUnretainedValue().rootViewController?.view else {
|
||||||
|
print("Unable to get root view")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMetalLayer(in view: UIView) -> CAMetalLayer? {
|
||||||
|
if let metalLayer = view.layer as? CAMetalLayer {
|
||||||
|
return metalLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
for subview in view.subviews {
|
||||||
|
if let metalLayer = findMetalLayer(in: subview) {
|
||||||
|
return metalLayer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let existingLayer = findMetalLayer(in: rootView) {
|
||||||
|
print("Found Metal Layer")
|
||||||
|
return existingLayer
|
||||||
|
}
|
||||||
|
print("found nothing")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static func log(_ message: String) {
|
static func log(_ message: String) {
|
||||||
// print("[Ryujinx] \(message)")
|
print("[Ryujinx] \(message)")
|
||||||
}
|
|
||||||
|
|
||||||
public func updateOrientation() -> Bool {
|
|
||||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
||||||
let window = windowScene.windows.first {
|
|
||||||
return (window.bounds.size.height > window.bounds.size.width)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ryuIsJITEnabled() {
|
|
||||||
jitenabled = isJITEnabled()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public extension UIDevice {
|
|
||||||
static let modelName: String = {
|
|
||||||
var systemInfo = utsname()
|
|
||||||
uname(&systemInfo)
|
|
||||||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
|
||||||
let identifier = machineMirror.children.reduce("") { identifier, element in
|
|
||||||
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
|
||||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity
|
|
||||||
#if os(iOS)
|
|
||||||
switch identifier {
|
|
||||||
case "iPod5,1": return "iPod touch (5th generation)"
|
|
||||||
case "iPod7,1": return "iPod touch (6th generation)"
|
|
||||||
case "iPod9,1": return "iPod touch (7th generation)"
|
|
||||||
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4"
|
|
||||||
case "iPhone4,1": return "iPhone 4s"
|
|
||||||
case "iPhone5,1", "iPhone5,2": return "iPhone 5"
|
|
||||||
case "iPhone5,3", "iPhone5,4": return "iPhone 5c"
|
|
||||||
case "iPhone6,1", "iPhone6,2": return "iPhone 5s"
|
|
||||||
case "iPhone7,2": return "iPhone 6"
|
|
||||||
case "iPhone7,1": return "iPhone 6 Plus"
|
|
||||||
case "iPhone8,1": return "iPhone 6s"
|
|
||||||
case "iPhone8,2": return "iPhone 6s Plus"
|
|
||||||
case "iPhone9,1", "iPhone9,3": return "iPhone 7"
|
|
||||||
case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus"
|
|
||||||
case "iPhone10,1", "iPhone10,4": return "iPhone 8"
|
|
||||||
case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
|
|
||||||
case "iPhone10,3", "iPhone10,6": return "iPhone X"
|
|
||||||
case "iPhone11,2": return "iPhone XS"
|
|
||||||
case "iPhone11,4", "iPhone11,6": return "iPhone XS Max"
|
|
||||||
case "iPhone11,8": return "iPhone XR"
|
|
||||||
case "iPhone12,1": return "iPhone 11"
|
|
||||||
case "iPhone12,3": return "iPhone 11 Pro"
|
|
||||||
case "iPhone12,5": return "iPhone 11 Pro Max"
|
|
||||||
case "iPhone13,1": return "iPhone 12 mini"
|
|
||||||
case "iPhone13,2": return "iPhone 12"
|
|
||||||
case "iPhone13,3": return "iPhone 12 Pro"
|
|
||||||
case "iPhone13,4": return "iPhone 12 Pro Max"
|
|
||||||
case "iPhone14,4": return "iPhone 13 mini"
|
|
||||||
case "iPhone14,5": return "iPhone 13"
|
|
||||||
case "iPhone14,2": return "iPhone 13 Pro"
|
|
||||||
case "iPhone14,3": return "iPhone 13 Pro Max"
|
|
||||||
case "iPhone14,7": return "iPhone 14"
|
|
||||||
case "iPhone14,8": return "iPhone 14 Plus"
|
|
||||||
case "iPhone15,2": return "iPhone 14 Pro"
|
|
||||||
case "iPhone15,3": return "iPhone 14 Pro Max"
|
|
||||||
case "iPhone15,4": return "iPhone 15"
|
|
||||||
case "iPhone15,5": return "iPhone 15 Plus"
|
|
||||||
case "iPhone16,1": return "iPhone 15 Pro"
|
|
||||||
case "iPhone16,2": return "iPhone 15 Pro Max"
|
|
||||||
case "iPhone17,3": return "iPhone 16"
|
|
||||||
case "iPhone17,4": return "iPhone 16 Plus"
|
|
||||||
case "iPhone17,1": return "iPhone 16 Pro"
|
|
||||||
case "iPhone17,2": return "iPhone 16 Pro Max"
|
|
||||||
case "iPhone17,5": return "iPhone 16e"
|
|
||||||
case "iPhone8,4": return "iPhone SE"
|
|
||||||
case "iPhone12,8": return "iPhone SE (2nd generation)"
|
|
||||||
case "iPhone14,6": return "iPhone SE (3rd generation)"
|
|
||||||
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2"
|
|
||||||
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad (3rd generation)"
|
|
||||||
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad (4th generation)"
|
|
||||||
case "iPad6,11", "iPad6,12": return "iPad (5th generation)"
|
|
||||||
case "iPad7,5", "iPad7,6": return "iPad (6th generation)"
|
|
||||||
case "iPad7,11", "iPad7,12": return "iPad (7th generation)"
|
|
||||||
case "iPad11,6", "iPad11,7": return "iPad (8th generation)"
|
|
||||||
case "iPad12,1", "iPad12,2": return "iPad (9th generation)"
|
|
||||||
case "iPad13,18", "iPad13,19": return "iPad (10th generation)"
|
|
||||||
case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air"
|
|
||||||
case "iPad5,3", "iPad5,4": return "iPad Air 2"
|
|
||||||
case "iPad11,3", "iPad11,4": return "iPad Air (3rd generation)"
|
|
||||||
case "iPad13,1", "iPad13,2": return "iPad Air (4th generation)"
|
|
||||||
case "iPad13,16", "iPad13,17": return "iPad Air (5th generation)"
|
|
||||||
case "iPad14,8", "iPad14,9": return "iPad Air (11-inch) (M2)"
|
|
||||||
case "iPad14,10", "iPad14,11": return "iPad Air (13-inch) (M2)"
|
|
||||||
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini"
|
|
||||||
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad mini 2"
|
|
||||||
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad mini 3"
|
|
||||||
case "iPad5,1", "iPad5,2": return "iPad mini 4"
|
|
||||||
case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)"
|
|
||||||
case "iPad14,1", "iPad14,2": return "iPad mini (6th generation)"
|
|
||||||
case "iPad16,1", "iPad16,2": return "iPad mini (A17 Pro)"
|
|
||||||
case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)"
|
|
||||||
case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)"
|
|
||||||
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return "iPad Pro (11-inch) (1st generation)"
|
|
||||||
case "iPad8,9", "iPad8,10": return "iPad Pro (11-inch) (2nd generation)"
|
|
||||||
case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": return "iPad Pro (11-inch) (3rd generation)"
|
|
||||||
case "iPad14,3", "iPad14,4": return "iPad Pro (11-inch) (4th generation)"
|
|
||||||
case "iPad16,3", "iPad16,4": return "iPad Pro (11-inch) (M4)"
|
|
||||||
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch) (1st generation)"
|
|
||||||
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)"
|
|
||||||
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return "iPad Pro (12.9-inch) (3rd generation)"
|
|
||||||
case "iPad8,11", "iPad8,12": return "iPad Pro (12.9-inch) (4th generation)"
|
|
||||||
case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11":return "iPad Pro (12.9-inch) (5th generation)"
|
|
||||||
case "iPad14,5", "iPad14,6": return "iPad Pro (12.9-inch) (6th generation)"
|
|
||||||
case "iPad16,5", "iPad16,6": return "iPad Pro (13-inch) (M4)"
|
|
||||||
case "AppleTV5,3": return "Apple TV"
|
|
||||||
case "AppleTV6,2": return "Apple TV 4K"
|
|
||||||
case "AudioAccessory1,1": return "HomePod"
|
|
||||||
case "AudioAccessory5,1": return "HomePod mini"
|
|
||||||
case "i386", "x86_64", "arm64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))"
|
|
||||||
default: return identifier
|
|
||||||
}
|
|
||||||
#elseif os(tvOS)
|
|
||||||
switch identifier {
|
|
||||||
case "AppleTV5,3": return "Apple TV 4"
|
|
||||||
case "AppleTV6,2", "AppleTV11,1", "AppleTV14,1": return "Apple TV 4K"
|
|
||||||
case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))"
|
|
||||||
default: return identifier
|
|
||||||
}
|
|
||||||
#elseif os(visionOS)
|
|
||||||
switch identifier {
|
|
||||||
case "RealityDevice14,1": return "Apple Vision Pro"
|
|
||||||
default: return identifier
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapToDevice(identifier: identifier)
|
|
||||||
}()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,10 +32,10 @@ struct LaunchGameIntentDef: AppIntent {
|
||||||
|
|
||||||
let ryujinx = Ryujinx.shared.games
|
let ryujinx = Ryujinx.shared.games
|
||||||
|
|
||||||
let name = findClosestGameName(input: gameName, games: ryujinx.compactMap(\.titleName))
|
let name = findClosestGameName(input: gameName, games: ryujinx.flatMap(\.titleName))
|
||||||
|
|
||||||
let urlString = "melonx://game?name=\(name ?? gameName)"
|
let urlString = "melonx://game?name=\(name ?? gameName)"
|
||||||
// print(urlString)
|
print(urlString)
|
||||||
if let url = URL(string: urlString) {
|
if let url = URL(string: urlString) {
|
||||||
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,21 +57,28 @@ public struct Game: Identifiable, Equatable, Hashable {
|
||||||
|
|
||||||
gameTemp.icon = UIImage(data: imageData)
|
gameTemp.icon = UIImage(data: imageData)
|
||||||
} else {
|
} else {
|
||||||
// print("Invalid image size.")
|
print("Invalid image size.")
|
||||||
}
|
}
|
||||||
return gameTemp
|
return gameTemp
|
||||||
}
|
}
|
||||||
|
|
||||||
func createImage(from gameInfo: GameInfo) -> UIImage? {
|
func createImage(from gameInfo: GameInfo) -> UIImage? {
|
||||||
|
// Access the struct
|
||||||
let gameInfoValue = gameInfo
|
let gameInfoValue = gameInfo
|
||||||
|
|
||||||
|
// Get the image data
|
||||||
let imageSize = Int(gameInfoValue.ImageSize)
|
let imageSize = Int(gameInfoValue.ImageSize)
|
||||||
guard imageSize > 0, imageSize <= 1024 * 1024 else {
|
guard imageSize > 0, imageSize <= 1024 * 1024 else {
|
||||||
// print("Invalid image size.")
|
print("Invalid image size.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert the ImageData byte array to Swift's Data
|
||||||
let imageData = Data(bytes: gameInfoValue.ImageData, count: imageSize)
|
let imageData = Data(bytes: gameInfoValue.ImageData, count: imageSize)
|
||||||
|
|
||||||
|
// Create a UIImage (or NSImage on macOS)
|
||||||
|
print(imageData)
|
||||||
|
|
||||||
return UIImage(data: imageData)
|
return UIImage(data: imageData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
//
|
|
||||||
// LatestVersionResponse.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by Bella on 12/03/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
struct LatestVersionResponse: Codable {
|
|
||||||
let version_number: String
|
|
||||||
let version_number_stripped: String
|
|
||||||
let changelog: String
|
|
||||||
let download_link: String
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
static let example1 = LatestVersionResponse(
|
|
||||||
version_number: "1.0.0",
|
|
||||||
version_number_stripped: "100",
|
|
||||||
changelog: """
|
|
||||||
- Rewrite Display Code (SDL isn't used for display anymore)
|
|
||||||
- Add New Onboarding / Setup
|
|
||||||
- Better Performance
|
|
||||||
- Remove "SDL Window" option in settings
|
|
||||||
- Fix JIT Cache Regions
|
|
||||||
- Fix how JIT is detected in Settings
|
|
||||||
- Fix ABYX being swapped on controller.
|
|
||||||
- Settings are now a config.json file
|
|
||||||
- Fix Performance Overlay not showing when Virtual Controller is hidden
|
|
||||||
- Add displaying logs when Loading or in-game
|
|
||||||
- Fix Launching games from outside of the roms folder
|
|
||||||
- Add Waiting for JIT popup
|
|
||||||
- Fix spesific Games
|
|
||||||
- Added Back Herobrine ("You were supposed to be the hero, Bryan")
|
|
||||||
""",
|
|
||||||
download_link: "https://example.com"
|
|
||||||
)
|
|
||||||
#endif
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
//
|
|
||||||
// ToggleButtonsState.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 12/04/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
struct ToggleButtonsState: Codable, Equatable {
|
|
||||||
var toggle1: Bool
|
|
||||||
var toggle2: Bool
|
|
||||||
var toggle3: Bool
|
|
||||||
var toggle4: Bool
|
|
||||||
|
|
||||||
init() {
|
|
||||||
self = .default
|
|
||||||
}
|
|
||||||
|
|
||||||
init(toggle1: Bool, toggle2: Bool, toggle3: Bool, toggle4: Bool) {
|
|
||||||
self.toggle1 = toggle1
|
|
||||||
self.toggle2 = toggle2
|
|
||||||
self.toggle3 = toggle3
|
|
||||||
self.toggle4 = toggle4
|
|
||||||
}
|
|
||||||
|
|
||||||
static let `default` = ToggleButtonsState(toggle1: false, toggle2: false, toggle3: false, toggle4: false)
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
//
|
|
||||||
// AppCodableStorage.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 12/04/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
@propertyWrapper
|
|
||||||
struct AppCodableStorage<Value: Codable & Equatable>: DynamicProperty {
|
|
||||||
@State private var value: Value
|
|
||||||
|
|
||||||
private let key: String
|
|
||||||
private let defaultValue: Value
|
|
||||||
private let storage: UserDefaults
|
|
||||||
|
|
||||||
init(wrappedValue defaultValue: Value, _ key: String, store: UserDefaults = .standard) {
|
|
||||||
self._value = State(initialValue: {
|
|
||||||
if let data = store.data(forKey: key),
|
|
||||||
let decoded = try? JSONDecoder().decode(Value.self, from: data) {
|
|
||||||
return decoded
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}())
|
|
||||||
self.key = key
|
|
||||||
self.defaultValue = defaultValue
|
|
||||||
self.storage = store
|
|
||||||
}
|
|
||||||
|
|
||||||
var wrappedValue: Value {
|
|
||||||
get { value }
|
|
||||||
nonmutating set {
|
|
||||||
value = newValue
|
|
||||||
if let data = try? JSONEncoder().encode(newValue) {
|
|
||||||
storage.set(data, forKey: key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var projectedValue: Binding<Value> {
|
|
||||||
Binding(
|
|
||||||
get: { self.wrappedValue },
|
|
||||||
set: { newValue in self.wrappedValue = newValue }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
389
src/MeloNX/MeloNX/App/Views/Main/ContentView.swift
Normal file
389
src/MeloNX/MeloNX/App/Views/Main/ContentView.swift
Normal file
|
@ -0,0 +1,389 @@
|
||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 3/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
// import SDL2
|
||||||
|
import GameController
|
||||||
|
import Darwin
|
||||||
|
import UIKit
|
||||||
|
import MetalKit
|
||||||
|
// import SDL
|
||||||
|
|
||||||
|
struct MoltenVKSettings: Codable, Hashable {
|
||||||
|
let string: String
|
||||||
|
var value: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
// Games
|
||||||
|
@State private var game: Game?
|
||||||
|
|
||||||
|
// Controllers
|
||||||
|
@State private var controllersList: [Controller] = []
|
||||||
|
@State private var currentControllers: [Controller] = []
|
||||||
|
@State var onscreencontroller: Controller = Controller(id: "", name: "")
|
||||||
|
@State var nativeControllers: [GCController: NativeController] = [:]
|
||||||
|
@State private var isVirtualControllerActive: Bool = false
|
||||||
|
@AppStorage("isVirtualController") var isVCA: Bool = true
|
||||||
|
|
||||||
|
// Settings and Configuration
|
||||||
|
@State private var config: Ryujinx.Configuration
|
||||||
|
@State var settings: [MoltenVKSettings]
|
||||||
|
@AppStorage("useTrollStore") var useTrollStore: Bool = false
|
||||||
|
|
||||||
|
// JIT
|
||||||
|
@AppStorage("jitStreamerEB") var jitStreamerEB: Bool = false
|
||||||
|
|
||||||
|
// Other Configuration
|
||||||
|
@State var isMK8: Bool = false
|
||||||
|
@AppStorage("quit") var quit: Bool = false
|
||||||
|
@State var quits: Bool = false
|
||||||
|
@AppStorage("MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS") var mVKPreFillBuffer: Bool = true
|
||||||
|
@AppStorage("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS") var syncqsubmits: Bool = true
|
||||||
|
@AppStorage("ignoreJIT") var ignoreJIT: Bool = false
|
||||||
|
|
||||||
|
// Loading Animation
|
||||||
|
@AppStorage("showlogsloading") var showlogsloading: Bool = true
|
||||||
|
@State private var clumpOffset: CGFloat = -100
|
||||||
|
private let clumpWidth: CGFloat = 100
|
||||||
|
private let animationDuration: Double = 1.0
|
||||||
|
@State private var isAnimating = false
|
||||||
|
@State var isLoading = true
|
||||||
|
@State var jitNotEnabled = false
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
init() {
|
||||||
|
var defaultConfig = loadSettings()
|
||||||
|
if defaultConfig == nil {
|
||||||
|
saveSettings(config: .init(gamepath: ""))
|
||||||
|
|
||||||
|
defaultConfig = loadSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_config = State(initialValue: defaultConfig!)
|
||||||
|
|
||||||
|
let defaultSettings: [MoltenVKSettings] = [ // Default MoltenVK Settings.
|
||||||
|
MoltenVKSettings(string: "MVK_USE_METAL_PRIVATE_API", value: "1"),
|
||||||
|
MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_PRIVATE_API", value: "1"),
|
||||||
|
MoltenVKSettings(string: "MVK_DEBUG", value: "0"),
|
||||||
|
MoltenVKSettings(string: "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", value: "0"),
|
||||||
|
MoltenVKSettings(string: "MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS", value: "0"),
|
||||||
|
// Uses more ram but makes performance higher, may add an option in settings to change or enable / disable this value (default 64)
|
||||||
|
MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "512"),
|
||||||
|
]
|
||||||
|
|
||||||
|
_settings = State(initialValue: defaultSettings)
|
||||||
|
|
||||||
|
initializeSDL()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Body
|
||||||
|
var body: some View {
|
||||||
|
if game != nil, !jitNotEnabled {
|
||||||
|
// This is when the game starts to stop the animation
|
||||||
|
ZStack {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
EmulationView(startgame: $game)
|
||||||
|
.persistentSystemOverlays(.hidden)
|
||||||
|
} else {
|
||||||
|
EmulationView(startgame: $game)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isLoading {
|
||||||
|
ZStack {
|
||||||
|
Color.black
|
||||||
|
.opacity(0.8)
|
||||||
|
emulationView
|
||||||
|
.ignoresSafeArea(.all)
|
||||||
|
}
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.ignoresSafeArea(.all)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if game != nil, ignoreJIT {
|
||||||
|
ZStack {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
EmulationView(startgame: $game)
|
||||||
|
.persistentSystemOverlays(.hidden)
|
||||||
|
} else {
|
||||||
|
EmulationView(startgame: $game)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isLoading {
|
||||||
|
ZStack {
|
||||||
|
Color.black
|
||||||
|
.opacity(0.8)
|
||||||
|
emulationView
|
||||||
|
.ignoresSafeArea(.all)
|
||||||
|
}
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.ignoresSafeArea(.all)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if game != nil {
|
||||||
|
Text("")
|
||||||
|
.sheet(isPresented: $jitNotEnabled) {
|
||||||
|
JITPopover() {
|
||||||
|
jitNotEnabled = false
|
||||||
|
}
|
||||||
|
.interactiveDismissDisabled()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is the main menu view that includes the Settings and the Game Selector
|
||||||
|
mainMenuView
|
||||||
|
.onAppear() {
|
||||||
|
quits = false
|
||||||
|
|
||||||
|
loadSettings()
|
||||||
|
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
initControllerObservers() // This initializes the Controller Observers that refreshes the controller list when a new controller connecvts.
|
||||||
|
}
|
||||||
|
.onOpenURL() { url in
|
||||||
|
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||||
|
components.host == "game" {
|
||||||
|
if let text = components.queryItems?.first(where: { $0.name == "id" })?.value {
|
||||||
|
|
||||||
|
game = Ryujinx.shared.games.first(where: { $0.titleId == text })
|
||||||
|
} else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value {
|
||||||
|
game = Ryujinx.shared.games.first(where: { $0.titleName == text })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func initControllerObservers() {
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
forName: .GCControllerDidConnect,
|
||||||
|
object: nil,
|
||||||
|
queue: .main) { notification in
|
||||||
|
if let controller = notification.object as? GCController {
|
||||||
|
print("Controller connected: \(controller.productCategory)")
|
||||||
|
nativeControllers[controller] = .init(controller)
|
||||||
|
refreshControllersList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
forName: .GCControllerDidDisconnect,
|
||||||
|
object: nil,
|
||||||
|
queue: .main) { notification in
|
||||||
|
if let controller = notification.object as? GCController {
|
||||||
|
print("Controller disconnected: \(controller.productCategory)")
|
||||||
|
nativeControllers[controller]?.cleanup()
|
||||||
|
nativeControllers[controller] = nil
|
||||||
|
refreshControllersList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - View Components
|
||||||
|
private var emulationView: some View {
|
||||||
|
GeometryReader { screenGeometry in
|
||||||
|
ZStack {
|
||||||
|
HStack(spacing: screenGeometry.size.width * 0.04) {
|
||||||
|
if let icon = game?.icon {
|
||||||
|
Image(uiImage: icon)
|
||||||
|
.resizable()
|
||||||
|
.frame(
|
||||||
|
width: min(screenGeometry.size.width * 0.25, 250),
|
||||||
|
height: min(screenGeometry.size.width * 0.25, 250)
|
||||||
|
)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||||
|
.shadow(color: .black.opacity(0.5), radius: 10, x: 0, y: 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: screenGeometry.size.height * 0.015) {
|
||||||
|
Text("Loading \(game?.titleName ?? "Game")")
|
||||||
|
.font(.system(size: min(screenGeometry.size.width * 0.04, 32)))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
|
||||||
|
GeometryReader { geometry in
|
||||||
|
let containerWidth = min(screenGeometry.size.width * 0.35, 350)
|
||||||
|
|
||||||
|
ZStack(alignment: .leading) {
|
||||||
|
Rectangle()
|
||||||
|
.cornerRadius(10)
|
||||||
|
.frame(width: containerWidth, height: min(screenGeometry.size.height * 0.015, 12))
|
||||||
|
.foregroundColor(.gray.opacity(0.3))
|
||||||
|
.shadow(color: .black.opacity(0.2), radius: 4, x: 0, y: 2)
|
||||||
|
|
||||||
|
Rectangle()
|
||||||
|
.cornerRadius(10)
|
||||||
|
.frame(width: clumpWidth, height: min(screenGeometry.size.height * 0.015, 12))
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
.shadow(color: .blue.opacity(0.5), radius: 4, x: 0, y: 2)
|
||||||
|
.offset(x: isAnimating ? containerWidth : -clumpWidth)
|
||||||
|
.animation(
|
||||||
|
Animation.linear(duration: 1.0)
|
||||||
|
.repeatForever(autoreverses: false),
|
||||||
|
value: isAnimating
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||||
|
.onAppear {
|
||||||
|
isAnimating = true
|
||||||
|
|
||||||
|
setupEmulation()
|
||||||
|
|
||||||
|
|
||||||
|
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
|
||||||
|
if get_current_fps() != 0 {
|
||||||
|
withAnimation {
|
||||||
|
isLoading = false
|
||||||
|
|
||||||
|
isAnimating = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
timer.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: min(screenGeometry.size.height * 0.015, 12))
|
||||||
|
.frame(width: min(screenGeometry.size.width * 0.35, 350))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, screenGeometry.size.width * 0.06)
|
||||||
|
.padding(.vertical, screenGeometry.size.height * 0.05)
|
||||||
|
.position(
|
||||||
|
x: screenGeometry.size.width / 2,
|
||||||
|
y: screenGeometry.size.height * 0.5
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if showlogsloading {
|
||||||
|
LogFileView(isfps: true)
|
||||||
|
.frame(alignment: .topLeading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mainMenuView: some View {
|
||||||
|
MainTabView(startemu: $game, config: $config, MVKconfig: $settings, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller)
|
||||||
|
.onAppear() {
|
||||||
|
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { timer in
|
||||||
|
refreshControllersList()
|
||||||
|
}
|
||||||
|
|
||||||
|
Air.play(AnyView(
|
||||||
|
VStack {
|
||||||
|
Image(systemName: "gamecontroller")
|
||||||
|
.font(.system(size: 300))
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
Text("Select Game")
|
||||||
|
.font(.system(size: 150))
|
||||||
|
.bold()
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
jitNotEnabled = !isJITEnabled()
|
||||||
|
if jitNotEnabled {
|
||||||
|
useTrollStore ? askForJIT() : jitStreamerEB ? enableJITEB() : print("no JIT")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helper Methods
|
||||||
|
var SdlInitFlags: uint = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO; // Initialises SDL2 for Events, Game Controller, Joystick, Audio and Video.
|
||||||
|
private func initializeSDL() {
|
||||||
|
setMoltenVKSettings()
|
||||||
|
SDL_SetMainReady() // Sets SDL Ready
|
||||||
|
SDL_iPhoneSetEventPump(SDL_TRUE) // Set iOS Event Pump to true
|
||||||
|
SDL_Init(SdlInitFlags) // Initialises SDL2
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupEmulation() {
|
||||||
|
isVCA = (currentControllers.first(where: { $0 == onscreencontroller }) != nil)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
start(displayid: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func refreshControllersList() {
|
||||||
|
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||||
|
|
||||||
|
if let onscreen = controllersList.first(where: { $0.name == Ryujinx.shared.virtualController.controllername }) {
|
||||||
|
self.onscreencontroller = onscreen
|
||||||
|
}
|
||||||
|
|
||||||
|
controllersList.removeAll(where: { $0.id == "0" || (!$0.name.starts(with: "GC - ") && $0 != onscreencontroller) })
|
||||||
|
controllersList.mutableForEach { $0.name = $0.name.replacingOccurrences(of: "GC - ", with: "") }
|
||||||
|
|
||||||
|
currentControllers = []
|
||||||
|
|
||||||
|
if controllersList.count == 1 {
|
||||||
|
let controller = controllersList[0]
|
||||||
|
currentControllers.append(controller)
|
||||||
|
} else if (controllersList.count - 1) >= 1 {
|
||||||
|
for controller in controllersList {
|
||||||
|
if controller.id != onscreencontroller.id && !currentControllers.contains(where: { $0.id == controller.id }) {
|
||||||
|
currentControllers.append(controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private func start(displayid: UInt32) {
|
||||||
|
guard let game else { return }
|
||||||
|
|
||||||
|
config.gamepath = game.fileURL.path
|
||||||
|
config.inputids = Array(Set(currentControllers.map(\.id)))
|
||||||
|
|
||||||
|
if mVKPreFillBuffer {
|
||||||
|
let setting = MoltenVKSettings(string: "MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS", value: "2")
|
||||||
|
setenv(setting.string, setting.value, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if syncqsubmits {
|
||||||
|
let setting = MoltenVKSettings(string: "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", value: "2")
|
||||||
|
setenv(setting.string, setting.value, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.inputids.isEmpty {
|
||||||
|
config.inputids.append("0")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do {
|
||||||
|
try Ryujinx.shared.start(with: config)
|
||||||
|
} catch {
|
||||||
|
print("Error: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Sets MoltenVK Environment Variables
|
||||||
|
private func setMoltenVKSettings() {
|
||||||
|
settings.forEach { setting in
|
||||||
|
setenv(setting.string, setting.value, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Array {
|
||||||
|
@inlinable public mutating func mutableForEach(_ body: (inout Element) throws -> Void) rethrows {
|
||||||
|
for index in self.indices {
|
||||||
|
try body(&self[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
//
|
||||||
|
// ControllerView.swift
|
||||||
|
// Pomelo-V2
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 16/7/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import GameController
|
||||||
|
import SwiftUIJoystick
|
||||||
|
import CoreMotion
|
||||||
|
|
||||||
|
struct ControllerView: View {
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { geometry in
|
||||||
|
if geometry.size.height > geometry.size.width && UIDevice.current.userInterfaceIdiom != .pad {
|
||||||
|
VStack {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
VStack {
|
||||||
|
ShoulderButtonsViewLeft()
|
||||||
|
ZStack {
|
||||||
|
Joystick()
|
||||||
|
DPadView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
VStack {
|
||||||
|
ShoulderButtonsViewRight()
|
||||||
|
ZStack {
|
||||||
|
Joystick(iscool: true) // hope this works
|
||||||
|
ABXYView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
ButtonView(button: .start) // Adding the + button
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
ButtonView(button: .back) // Adding the - button
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// could be landscape
|
||||||
|
VStack {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
|
||||||
|
// gotta fuckin add + and - now
|
||||||
|
VStack {
|
||||||
|
ShoulderButtonsViewLeft()
|
||||||
|
ZStack {
|
||||||
|
Joystick()
|
||||||
|
DPadView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
// Spacer()
|
||||||
|
VStack {
|
||||||
|
// Spacer()
|
||||||
|
ButtonView(button: .back) // Adding the - button
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
VStack {
|
||||||
|
// Spacer()
|
||||||
|
ButtonView(button: .start) // Adding the + button
|
||||||
|
}
|
||||||
|
// Spacer()
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
ShoulderButtonsViewRight()
|
||||||
|
ZStack {
|
||||||
|
Joystick(iscool: true) // hope this work s
|
||||||
|
ABXYView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// .padding(.bottom, geometry.size.height / 11) // also extremally broken (
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ShoulderButtonsViewLeft: View {
|
||||||
|
@State var width: CGFloat = 160
|
||||||
|
@State var height: CGFloat = 20
|
||||||
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
ButtonView(button: .leftTrigger)
|
||||||
|
.padding(.horizontal)
|
||||||
|
ButtonView(button: .leftShoulder)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
.frame(width: width, height: height)
|
||||||
|
.onAppear() {
|
||||||
|
if UIDevice.current.systemName.contains("iPadOS") {
|
||||||
|
width *= 1.2
|
||||||
|
height *= 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
width *= CGFloat(controllerScale)
|
||||||
|
height *= CGFloat(controllerScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ShoulderButtonsViewRight: View {
|
||||||
|
@State var width: CGFloat = 160
|
||||||
|
@State var height: CGFloat = 20
|
||||||
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
ButtonView(button: .rightShoulder)
|
||||||
|
.padding(.horizontal)
|
||||||
|
ButtonView(button: .rightTrigger)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
.frame(width: width, height: height)
|
||||||
|
.onAppear() {
|
||||||
|
if UIDevice.current.systemName.contains("iPadOS") {
|
||||||
|
width *= 1.2
|
||||||
|
height *= 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
width *= CGFloat(controllerScale)
|
||||||
|
height *= CGFloat(controllerScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DPadView: View {
|
||||||
|
@State var size: CGFloat = 145
|
||||||
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
ButtonView(button: .dPadUp)
|
||||||
|
HStack {
|
||||||
|
ButtonView(button: .dPadLeft)
|
||||||
|
Spacer(minLength: 20)
|
||||||
|
ButtonView(button: .dPadRight)
|
||||||
|
}
|
||||||
|
ButtonView(button: .dPadDown)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
.frame(width: size, height: size)
|
||||||
|
.onAppear() {
|
||||||
|
if UIDevice.current.systemName.contains("iPadOS") {
|
||||||
|
size *= 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
size *= CGFloat(controllerScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ABXYView: View {
|
||||||
|
@State var size: CGFloat = 145
|
||||||
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
ButtonView(button: .X)
|
||||||
|
HStack {
|
||||||
|
ButtonView(button: .Y)
|
||||||
|
Spacer(minLength: 20)
|
||||||
|
ButtonView(button: .A)
|
||||||
|
}
|
||||||
|
ButtonView(button: .B)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
.frame(width: size, height: size)
|
||||||
|
.onAppear() {
|
||||||
|
if UIDevice.current.systemName.contains("iPadOS") {
|
||||||
|
size *= 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
size *= CGFloat(controllerScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ButtonView: View {
|
||||||
|
var button: VirtualControllerButton
|
||||||
|
@State var width: CGFloat = 45
|
||||||
|
@State var height: CGFloat = 45
|
||||||
|
@State var isPressed = false
|
||||||
|
@AppStorage("onscreenhandheld") var onscreenjoy: Bool = false
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@Environment(\.presentationMode) var presentationMode
|
||||||
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Image(systemName: buttonText)
|
||||||
|
.resizable()
|
||||||
|
.frame(width: width, height: height)
|
||||||
|
.foregroundColor(colorScheme == .dark ? Color.gray : Color.gray)
|
||||||
|
.opacity(isPressed ? 0.4 : 0.7)
|
||||||
|
.gesture(
|
||||||
|
DragGesture(minimumDistance: 0)
|
||||||
|
.onChanged { _ in
|
||||||
|
if !self.isPressed {
|
||||||
|
self.isPressed = true
|
||||||
|
Ryujinx.shared.virtualController.setButtonState(1, for: button)
|
||||||
|
Haptics.shared.play(.heavy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onEnded { _ in
|
||||||
|
self.isPressed = false
|
||||||
|
Ryujinx.shared.virtualController.setButtonState(0, for: button)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onAppear() {
|
||||||
|
if button == .leftTrigger || button == .rightTrigger || button == .leftShoulder || button == .rightShoulder {
|
||||||
|
width = 65
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if button == .back || button == .start || button == .guide {
|
||||||
|
width = 35
|
||||||
|
height = 35
|
||||||
|
}
|
||||||
|
|
||||||
|
if UIDevice.current.systemName.contains("iPadOS") {
|
||||||
|
width *= 1.2
|
||||||
|
height *= 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
width *= CGFloat(controllerScale)
|
||||||
|
height *= CGFloat(controllerScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private var buttonText: String {
|
||||||
|
switch button {
|
||||||
|
case .A:
|
||||||
|
return "a.circle.fill"
|
||||||
|
case .B:
|
||||||
|
return "b.circle.fill"
|
||||||
|
case .X:
|
||||||
|
return "x.circle.fill"
|
||||||
|
case .Y:
|
||||||
|
return "y.circle.fill"
|
||||||
|
case .dPadUp:
|
||||||
|
return "arrowtriangle.up.circle.fill"
|
||||||
|
case .dPadDown:
|
||||||
|
return "arrowtriangle.down.circle.fill"
|
||||||
|
case .dPadLeft:
|
||||||
|
return "arrowtriangle.left.circle.fill"
|
||||||
|
case .dPadRight:
|
||||||
|
return "arrowtriangle.right.circle.fill"
|
||||||
|
case .leftTrigger:
|
||||||
|
return"zl.rectangle.roundedtop.fill"
|
||||||
|
case .rightTrigger:
|
||||||
|
return "zr.rectangle.roundedtop.fill"
|
||||||
|
case .leftShoulder:
|
||||||
|
return "l.rectangle.roundedbottom.fill"
|
||||||
|
case .rightShoulder:
|
||||||
|
return "r.rectangle.roundedbottom.fill"
|
||||||
|
case .start:
|
||||||
|
return "plus.circle.fill" // System symbol for +
|
||||||
|
case .back:
|
||||||
|
return "minus.circle.fill" // System symbol for -
|
||||||
|
case .guide:
|
||||||
|
return "house.circle.fill"
|
||||||
|
// This should be all the cases
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ class Haptics {
|
||||||
private init() { }
|
private init() { }
|
||||||
|
|
||||||
func play(_ feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle) {
|
func play(_ feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle) {
|
||||||
|
print("haptics")
|
||||||
UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred()
|
UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// JoystickView.swift
|
||||||
|
// Pomelo
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 30/9/2024.
|
||||||
|
// Copyright © 2024 Stossy11. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftUIJoystick
|
||||||
|
|
||||||
|
public struct Joystick: View {
|
||||||
|
@State var iscool: Bool? = nil
|
||||||
|
|
||||||
|
@ObservedObject public var joystickMonitor = JoystickMonitor()
|
||||||
|
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||||
|
var dragDiameter: CGFloat {
|
||||||
|
var selfs = CGFloat(160)
|
||||||
|
selfs *= controllerScale
|
||||||
|
if UIDevice.current.systemName.contains("iPadOS") {
|
||||||
|
return selfs * 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
return selfs
|
||||||
|
}
|
||||||
|
private let shape: JoystickShape = .circle
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
VStack{
|
||||||
|
JoystickBuilder(
|
||||||
|
monitor: self.joystickMonitor,
|
||||||
|
width: self.dragDiameter,
|
||||||
|
shape: .circle,
|
||||||
|
background: {
|
||||||
|
Text("")
|
||||||
|
.hidden()
|
||||||
|
},
|
||||||
|
foreground: {
|
||||||
|
Circle().fill(Color.gray)
|
||||||
|
.opacity(0.7)
|
||||||
|
},
|
||||||
|
locksInPlace: false)
|
||||||
|
.onChange(of: self.joystickMonitor.xyPoint) { newValue in
|
||||||
|
let scaledX = Float(newValue.x)
|
||||||
|
let scaledY = Float(newValue.y) // my dumbass broke this by having -y instead of y :/
|
||||||
|
print("Joystick Position: (\(scaledX), \(scaledY))")
|
||||||
|
|
||||||
|
if iscool != nil {
|
||||||
|
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: newValue.x, y: newValue.y)
|
||||||
|
} else {
|
||||||
|
Ryujinx.shared.virtualController.thumbstickMoved(.left, x: newValue.x, y: newValue.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,125 +0,0 @@
|
||||||
//
|
|
||||||
// FileImporter.swift
|
|
||||||
// MeloNX
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 17/04/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
class FileImporterManager: ObservableObject {
|
|
||||||
static let shared = FileImporterManager()
|
|
||||||
|
|
||||||
private init() {}
|
|
||||||
|
|
||||||
func importFiles(types: [UTType], allowMultiple: Bool = false, completion: @escaping (Result<[URL], Error>) -> Void) {
|
|
||||||
let id = "\(Unmanaged.passUnretained(completion as AnyObject).toOpaque())"
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
NotificationCenter.default.post(
|
|
||||||
name: .importFiles,
|
|
||||||
object: nil,
|
|
||||||
userInfo: [
|
|
||||||
"id": id,
|
|
||||||
"types": types,
|
|
||||||
"allowMultiple": allowMultiple,
|
|
||||||
"completion": completion
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Notification.Name {
|
|
||||||
static let importFiles = Notification.Name("importFiles")
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FileImporterView: ViewModifier {
|
|
||||||
@State private var isImporterPresented: [String: Bool] = [:]
|
|
||||||
@State private var activeImporters: [String: ImporterConfig] = [:]
|
|
||||||
|
|
||||||
struct ImporterConfig {
|
|
||||||
let types: [UTType]
|
|
||||||
let allowMultiple: Bool
|
|
||||||
let completion: (Result<[URL], Error>) -> Void
|
|
||||||
}
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
content
|
|
||||||
.background(
|
|
||||||
ForEach(Array(activeImporters.keys), id: \.self) { id in
|
|
||||||
if let config = activeImporters[id] {
|
|
||||||
FileImporterWrapper(
|
|
||||||
isPresented: Binding(
|
|
||||||
get: { isImporterPresented[id] ?? false },
|
|
||||||
set: { isImporterPresented[id] = $0 }
|
|
||||||
),
|
|
||||||
id: id,
|
|
||||||
config: config,
|
|
||||||
onCompletion: { success in
|
|
||||||
if success {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
activeImporters.removeValue(forKey: id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .importFiles)) { notification in
|
|
||||||
guard let userInfo = notification.userInfo,
|
|
||||||
let id = userInfo["id"] as? String,
|
|
||||||
let types = userInfo["types"] as? [UTType],
|
|
||||||
let allowMultiple = userInfo["allowMultiple"] as? Bool,
|
|
||||||
let completion = userInfo["completion"] as? ((Result<[URL], Error>) -> Void) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let config = ImporterConfig(
|
|
||||||
types: types,
|
|
||||||
allowMultiple: allowMultiple,
|
|
||||||
completion: completion
|
|
||||||
)
|
|
||||||
|
|
||||||
activeImporters[id] = config
|
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
isImporterPresented[id] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FileImporterWrapper: View {
|
|
||||||
@Binding var isPresented: Bool
|
|
||||||
let id: String
|
|
||||||
let config: FileImporterView.ImporterConfig
|
|
||||||
let onCompletion: (Bool) -> Void
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Text("wow")
|
|
||||||
.hidden()
|
|
||||||
.fileImporter(
|
|
||||||
isPresented: $isPresented,
|
|
||||||
allowedContentTypes: config.types,
|
|
||||||
allowsMultipleSelection: config.allowMultiple
|
|
||||||
) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let urls):
|
|
||||||
config.completion(.success(urls))
|
|
||||||
case .failure(let error):
|
|
||||||
config.completion(.failure(error))
|
|
||||||
}
|
|
||||||
onCompletion(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension View {
|
|
||||||
func withFileImporter() -> some View {
|
|
||||||
self.modifier(FileImporterView())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -58,7 +58,7 @@ public class Air {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func didConnect(sender: NSNotification) {
|
@objc func didConnect(sender: NSNotification) {
|
||||||
// print("AirKit - Connect")
|
print("AirKit - Connect")
|
||||||
self.connected = true
|
self.connected = true
|
||||||
guard let screen: UIScreen = sender.object as? UIScreen else { return }
|
guard let screen: UIScreen = sender.object as? UIScreen else { return }
|
||||||
add(screen: screen) { success in
|
add(screen: screen) { success in
|
||||||
|
@ -69,35 +69,35 @@ public class Air {
|
||||||
|
|
||||||
func add(screen: UIScreen, completion: @escaping (Bool) -> ()) {
|
func add(screen: UIScreen, completion: @escaping (Bool) -> ()) {
|
||||||
|
|
||||||
// print("AirKit - Add Screen")
|
print("AirKit - Add Screen")
|
||||||
|
|
||||||
airScreen = screen
|
airScreen = screen
|
||||||
|
|
||||||
airWindow = UIWindow(frame: airScreen!.bounds)
|
airWindow = UIWindow(frame: airScreen!.bounds)
|
||||||
|
|
||||||
guard let viewController: UIViewController = hostingController else {
|
guard let viewController: UIViewController = hostingController else {
|
||||||
// print("AirKit - Add - Failed: Hosting Controller Not Found")
|
print("AirKit - Add - Failed: Hosting Controller Not Found")
|
||||||
completion(false)
|
completion(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
findWindowScene(for: airScreen!) { windowScene in
|
findWindowScene(for: airScreen!) { windowScene in
|
||||||
guard let airWindowScene: UIWindowScene = windowScene else {
|
guard let airWindowScene: UIWindowScene = windowScene else {
|
||||||
// print("AirKit - Add - Failed: Window Scene Not Found")
|
print("AirKit - Add - Failed: Window Scene Not Found")
|
||||||
completion(false)
|
completion(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.airWindow?.rootViewController = viewController
|
self.airWindow?.rootViewController = viewController
|
||||||
self.airWindow?.windowScene = airWindowScene
|
self.airWindow?.windowScene = airWindowScene
|
||||||
self.airWindow?.isHidden = false
|
self.airWindow?.isHidden = false
|
||||||
// print("AirKit - Add Screen - Done")
|
print("AirKit - Add Screen - Done")
|
||||||
completion(true)
|
completion(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findWindowScene(for screen: UIScreen, shouldRecurse: Bool = true, completion: @escaping (UIWindowScene?) -> ()) {
|
func findWindowScene(for screen: UIScreen, shouldRecurse: Bool = true, completion: @escaping (UIWindowScene?) -> ()) {
|
||||||
// print("AirKit - Find Window Scene")
|
print("AirKit - Find Window Scene")
|
||||||
var matchingWindowScene: UIWindowScene? = nil
|
var matchingWindowScene: UIWindowScene? = nil
|
||||||
let scenes = UIApplication.shared.connectedScenes
|
let scenes = UIApplication.shared.connectedScenes
|
||||||
for scene in scenes {
|
for scene in scenes {
|
||||||
|
@ -120,23 +120,23 @@ public class Air {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func didDisconnect() {
|
@objc func didDisconnect() {
|
||||||
// print("AirKit - Disconnect")
|
print("AirKit - Disconnect")
|
||||||
remove()
|
remove()
|
||||||
connected = false
|
connected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove() {
|
func remove() {
|
||||||
// print("AirKit - Remove")
|
print("AirKit - Remove")
|
||||||
airWindow = nil
|
airWindow = nil
|
||||||
airScreen = nil
|
airScreen = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func didBecomeActive() {
|
@objc func didBecomeActive() {
|
||||||
// print("AirKit - App Active")
|
print("AirKit - App Active")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func willResignActive() {
|
@objc func willResignActive() {
|
||||||
// print("AirKit - App Inactive")
|
print("AirKit - App Inactive")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import SwiftUI
|
||||||
public extension View {
|
public extension View {
|
||||||
|
|
||||||
func airPlay() -> some View {
|
func airPlay() -> some View {
|
||||||
// print("AirKit - airPlay")
|
print("AirKit - airPlay")
|
||||||
Air.play(AnyView(self))
|
Air.play(AnyView(self))
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,558 +0,0 @@
|
||||||
//
|
|
||||||
// ControllerView.swift
|
|
||||||
// Pomelo-V2
|
|
||||||
//
|
|
||||||
// Created by Stossy11 on 16/7/2024.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import GameController
|
|
||||||
import CoreMotion
|
|
||||||
|
|
||||||
struct ControllerView: View {
|
|
||||||
// MARK: - Properties
|
|
||||||
@AppStorage("On-ScreenControllerScale") private var controllerScale: Double = 1.0
|
|
||||||
@AppStorage("stick-button") private var stickButton = false
|
|
||||||
@State private var isPortrait = true
|
|
||||||
@State var hideDpad = false
|
|
||||||
@State var hideABXY = false
|
|
||||||
@Environment(\.verticalSizeClass) var verticalSizeClass
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Body
|
|
||||||
var body: some View {
|
|
||||||
Group {
|
|
||||||
let isPad = UIDevice.current.userInterfaceIdiom == .pad
|
|
||||||
|
|
||||||
if isPortrait && !isPad {
|
|
||||||
portraitLayout
|
|
||||||
} else {
|
|
||||||
landscapeLayout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.onChange(of: verticalSizeClass) { _ in
|
|
||||||
updateOrientation()
|
|
||||||
}
|
|
||||||
.onAppear(perform: updateOrientation)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Layouts
|
|
||||||
private var portraitLayout: some View {
|
|
||||||
VStack {
|
|
||||||
Spacer()
|
|
||||||
VStack(spacing: 20) {
|
|
||||||
HStack(spacing: 30) {
|
|
||||||
VStack(spacing: 15) {
|
|
||||||
ShoulderButtonsViewLeft()
|
|
||||||
.padding(.vertical)
|
|
||||||
ZStack {
|
|
||||||
JoystickController(showBackground: $hideDpad)
|
|
||||||
DPadView()
|
|
||||||
.opacity(hideDpad ? 0 : 1)
|
|
||||||
.allowsHitTesting(!hideDpad)
|
|
||||||
.animation(.easeInOut(duration: 0.2), value: hideDpad)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(spacing: 15) {
|
|
||||||
ShoulderButtonsViewRight()
|
|
||||||
.padding(.vertical)
|
|
||||||
ZStack {
|
|
||||||
JoystickController(iscool: true, showBackground: $hideABXY)
|
|
||||||
ABXYView()
|
|
||||||
.opacity(hideABXY ? 0 : 1)
|
|
||||||
.allowsHitTesting(!hideABXY)
|
|
||||||
.animation(.easeInOut(duration: 0.2), value: hideABXY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack(spacing: 60) {
|
|
||||||
HStack {
|
|
||||||
ButtonView(button: .leftStick)
|
|
||||||
.padding()
|
|
||||||
ButtonView(button: .back)
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
ButtonView(button: .start)
|
|
||||||
ButtonView(button: .rightStick)
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var landscapeLayout: some View {
|
|
||||||
VStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
VStack(spacing: 20) {
|
|
||||||
ShoulderButtonsViewLeft()
|
|
||||||
.padding(.vertical)
|
|
||||||
ZStack {
|
|
||||||
JoystickController(showBackground: $hideDpad)
|
|
||||||
DPadView()
|
|
||||||
.opacity(hideDpad ? 0 : 1)
|
|
||||||
.allowsHitTesting(!hideDpad)
|
|
||||||
.animation(.easeInOut(duration: 0.2), value: hideDpad)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
centerButtons
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
VStack(spacing: 20) {
|
|
||||||
ShoulderButtonsViewRight()
|
|
||||||
.padding(.vertical)
|
|
||||||
ZStack {
|
|
||||||
JoystickController(iscool: true, showBackground: $hideABXY)
|
|
||||||
ABXYView()
|
|
||||||
.opacity(hideABXY ? 0 : 1)
|
|
||||||
.allowsHitTesting(!hideABXY)
|
|
||||||
.animation(.easeInOut(duration: 0.2), value: hideABXY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var centerButtons: some View {
|
|
||||||
Group {
|
|
||||||
if stickButton {
|
|
||||||
VStack {
|
|
||||||
HStack(spacing: 50) {
|
|
||||||
ButtonView(button: .leftStick)
|
|
||||||
.padding()
|
|
||||||
Spacer()
|
|
||||||
ButtonView(button: .rightStick)
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
.padding(.top, 30)
|
|
||||||
|
|
||||||
HStack(spacing: 50) {
|
|
||||||
ButtonView(button: .back)
|
|
||||||
Spacer()
|
|
||||||
ButtonView(button: .start)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
} else {
|
|
||||||
HStack(spacing: 50) {
|
|
||||||
ButtonView(button: .back)
|
|
||||||
Spacer()
|
|
||||||
ButtonView(button: .start)
|
|
||||||
}
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Methods
|
|
||||||
|
|
||||||
private func updateOrientation() {
|
|
||||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
||||||
let window = windowScene.windows.first {
|
|
||||||
isPortrait = window.bounds.size.height > window.bounds.size.width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct ShoulderButtonsViewLeft: View {
|
|
||||||
@State private var width: CGFloat = 160
|
|
||||||
@State private var height: CGFloat = 20
|
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
ButtonView(button: .leftTrigger)
|
|
||||||
ButtonView(button: .leftShoulder)
|
|
||||||
}
|
|
||||||
.frame(width: width, height: height)
|
|
||||||
.onAppear {
|
|
||||||
if UIDevice.current.systemName.contains("iPadOS") {
|
|
||||||
width *= 1.2
|
|
||||||
height *= 1.2
|
|
||||||
}
|
|
||||||
|
|
||||||
width *= CGFloat(controllerScale)
|
|
||||||
height *= CGFloat(controllerScale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ShoulderButtonsViewRight: View {
|
|
||||||
@State private var width: CGFloat = 160
|
|
||||||
@State private var height: CGFloat = 20
|
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
ButtonView(button: .rightShoulder)
|
|
||||||
ButtonView(button: .rightTrigger)
|
|
||||||
}
|
|
||||||
.frame(width: width, height: height)
|
|
||||||
.onAppear {
|
|
||||||
if UIDevice.current.systemName.contains("iPadOS") {
|
|
||||||
width *= 1.2
|
|
||||||
height *= 1.2
|
|
||||||
}
|
|
||||||
|
|
||||||
width *= CGFloat(controllerScale)
|
|
||||||
height *= CGFloat(controllerScale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DPadView: View {
|
|
||||||
@State private var size: CGFloat = 145
|
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack(spacing: 7) {
|
|
||||||
ButtonView(button: .dPadUp)
|
|
||||||
HStack(spacing: 22) {
|
|
||||||
ButtonView(button: .dPadLeft)
|
|
||||||
Spacer(minLength: 22)
|
|
||||||
ButtonView(button: .dPadRight)
|
|
||||||
}
|
|
||||||
ButtonView(button: .dPadDown)
|
|
||||||
}
|
|
||||||
.frame(width: size, height: size)
|
|
||||||
.onAppear {
|
|
||||||
if UIDevice.current.systemName.contains("iPadOS") {
|
|
||||||
size *= 1.2
|
|
||||||
}
|
|
||||||
|
|
||||||
size *= CGFloat(controllerScale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ABXYView: View {
|
|
||||||
@State private var size: CGFloat = 145
|
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack(spacing: 7) {
|
|
||||||
ButtonView(button: .X)
|
|
||||||
HStack(spacing: 22) {
|
|
||||||
ButtonView(button: .Y)
|
|
||||||
Spacer(minLength: 22)
|
|
||||||
ButtonView(button: .A)
|
|
||||||
}
|
|
||||||
ButtonView(button: .B)
|
|
||||||
}
|
|
||||||
.frame(width: size, height: size)
|
|
||||||
.onAppear {
|
|
||||||
if UIDevice.current.systemName.contains("iPadOS") {
|
|
||||||
size *= 1.2
|
|
||||||
}
|
|
||||||
|
|
||||||
size *= CGFloat(controllerScale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct ButtonView: View {
|
|
||||||
var button: VirtualControllerButton
|
|
||||||
|
|
||||||
@AppStorage("onscreenhandheld") var onscreenjoy: Bool = false
|
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
|
||||||
@Environment(\.presentationMode) var presentationMode
|
|
||||||
|
|
||||||
@AppCodableStorage("toggleButtons") var toggleButtons = ToggleButtonsState()
|
|
||||||
@State private var istoggle = false
|
|
||||||
|
|
||||||
@State private var isPressed = false
|
|
||||||
@State private var toggleState = false
|
|
||||||
|
|
||||||
@State private var size: CGSize = .zero
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Circle()
|
|
||||||
.foregroundStyle(.clear.opacity(0))
|
|
||||||
.overlay {
|
|
||||||
Image(systemName: buttonConfig.iconName)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: size.width, height: size.height)
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.opacity(isPressed ? 0.6 : 0.8)
|
|
||||||
.allowsHitTesting(false)
|
|
||||||
}
|
|
||||||
.frame(width: size.width, height: size.height)
|
|
||||||
.background(
|
|
||||||
buttonBackground
|
|
||||||
)
|
|
||||||
.gesture(
|
|
||||||
DragGesture(minimumDistance: 0)
|
|
||||||
.onChanged { _ in handleButtonPress() }
|
|
||||||
.onEnded { _ in handleButtonRelease() }
|
|
||||||
)
|
|
||||||
.onAppear {
|
|
||||||
istoggle = (toggleButtons.toggle1 && button == .A) || (toggleButtons.toggle2 && button == .B) || (toggleButtons.toggle3 && button == .X) || (toggleButtons.toggle4 && button == .Y)
|
|
||||||
size = calculateButtonSize()
|
|
||||||
}
|
|
||||||
.onChange(of: controllerScale) { _ in
|
|
||||||
size = calculateButtonSize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var buttonBackground: some View {
|
|
||||||
Group {
|
|
||||||
if !button.isTrigger && button != .leftStick && button != .rightStick {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.gray.opacity(0.4))
|
|
||||||
.frame(width: size.width * 1.25, height: size.height * 1.25)
|
|
||||||
} else if button == .leftStick || button == .rightStick {
|
|
||||||
Image(systemName: buttonConfig.iconName)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: size.width * 1.25, height: size.height * 1.25)
|
|
||||||
.foregroundColor(Color.gray.opacity(0.4))
|
|
||||||
} else if button.isTrigger {
|
|
||||||
Image(systemName: convertTriggerIconToButton(buttonConfig.iconName))
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: size.width * 1.25, height: size.height * 1.25)
|
|
||||||
.foregroundColor(Color.gray.opacity(0.4))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func convertTriggerIconToButton(_ iconName: String) -> String {
|
|
||||||
if iconName.hasPrefix("zl") || iconName.hasPrefix("zr") {
|
|
||||||
var converted = String(iconName.dropFirst(3))
|
|
||||||
converted = converted.replacingOccurrences(of: "rectangle", with: "button")
|
|
||||||
converted = converted.replacingOccurrences(of: ".fill", with: ".horizontal.fill")
|
|
||||||
return converted
|
|
||||||
} else {
|
|
||||||
var converted = String(iconName.dropFirst(2))
|
|
||||||
converted = converted.replacingOccurrences(of: "rectangle", with: "button")
|
|
||||||
converted = converted.replacingOccurrences(of: ".fill", with: ".horizontal.fill")
|
|
||||||
return converted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleButtonPress() {
|
|
||||||
guard !isPressed || istoggle else { return }
|
|
||||||
|
|
||||||
if istoggle {
|
|
||||||
toggleState.toggle()
|
|
||||||
isPressed = toggleState
|
|
||||||
let value = toggleState ? 1 : 0
|
|
||||||
Ryujinx.shared.virtualController.setButtonState(Uint8(value), for: button)
|
|
||||||
Haptics.shared.play(.medium)
|
|
||||||
} else {
|
|
||||||
isPressed = true
|
|
||||||
Ryujinx.shared.virtualController.setButtonState(1, for: button)
|
|
||||||
Haptics.shared.play(.medium)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleButtonRelease() {
|
|
||||||
if istoggle { return }
|
|
||||||
|
|
||||||
guard isPressed else { return }
|
|
||||||
|
|
||||||
isPressed = false
|
|
||||||
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.05) {
|
|
||||||
Ryujinx.shared.virtualController.setButtonState(0, for: button)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func calculateButtonSize() -> CGSize {
|
|
||||||
let baseWidth: CGFloat
|
|
||||||
let baseHeight: CGFloat
|
|
||||||
|
|
||||||
if button.isTrigger {
|
|
||||||
baseWidth = 70
|
|
||||||
baseHeight = 40
|
|
||||||
} else if button.isSmall {
|
|
||||||
baseWidth = 35
|
|
||||||
baseHeight = 35
|
|
||||||
} else {
|
|
||||||
baseWidth = 45
|
|
||||||
baseHeight = 45
|
|
||||||
}
|
|
||||||
|
|
||||||
let deviceMultiplier = UIDevice.current.userInterfaceIdiom == .pad ? 1.2 : 1.0
|
|
||||||
let scaleMultiplier = CGFloat(controllerScale)
|
|
||||||
|
|
||||||
return CGSize(
|
|
||||||
width: baseWidth * deviceMultiplier * scaleMultiplier,
|
|
||||||
height: baseHeight * deviceMultiplier * scaleMultiplier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Centralized button configuration
|
|
||||||
private var buttonConfig: ButtonConfiguration {
|
|
||||||
switch button {
|
|
||||||
case .A:
|
|
||||||
return ButtonConfiguration(iconName: "a.circle.fill")
|
|
||||||
case .B:
|
|
||||||
return ButtonConfiguration(iconName: "b.circle.fill")
|
|
||||||
case .X:
|
|
||||||
return ButtonConfiguration(iconName: "x.circle.fill")
|
|
||||||
case .Y:
|
|
||||||
return ButtonConfiguration(iconName: "y.circle.fill")
|
|
||||||
case .leftStick:
|
|
||||||
return ButtonConfiguration(iconName: "l.joystick.press.down.fill")
|
|
||||||
case .rightStick:
|
|
||||||
return ButtonConfiguration(iconName: "r.joystick.press.down.fill")
|
|
||||||
case .dPadUp:
|
|
||||||
return ButtonConfiguration(iconName: "arrowtriangle.up.circle.fill")
|
|
||||||
case .dPadDown:
|
|
||||||
return ButtonConfiguration(iconName: "arrowtriangle.down.circle.fill")
|
|
||||||
case .dPadLeft:
|
|
||||||
return ButtonConfiguration(iconName: "arrowtriangle.left.circle.fill")
|
|
||||||
case .dPadRight:
|
|
||||||
return ButtonConfiguration(iconName: "arrowtriangle.right.circle.fill")
|
|
||||||
case .leftTrigger:
|
|
||||||
return ButtonConfiguration(iconName: "zl.rectangle.roundedtop.fill")
|
|
||||||
case .rightTrigger:
|
|
||||||
return ButtonConfiguration(iconName: "zr.rectangle.roundedtop.fill")
|
|
||||||
case .leftShoulder:
|
|
||||||
return ButtonConfiguration(iconName: "l.rectangle.roundedbottom.fill")
|
|
||||||
case .rightShoulder:
|
|
||||||
return ButtonConfiguration(iconName: "r.rectangle.roundedbottom.fill")
|
|
||||||
case .start:
|
|
||||||
return ButtonConfiguration(iconName: "plus.circle.fill")
|
|
||||||
case .back:
|
|
||||||
return ButtonConfiguration(iconName: "minus.circle.fill")
|
|
||||||
case .guide:
|
|
||||||
return ButtonConfiguration(iconName: "house.circle.fill")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ButtonConfiguration {
|
|
||||||
let iconName: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct ExtButtonIconView: View {
|
|
||||||
var button: VirtualControllerButton
|
|
||||||
var opacity = 0.8
|
|
||||||
|
|
||||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
|
||||||
@State private var size: CGSize = .zero
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Circle()
|
|
||||||
.foregroundStyle(.clear.opacity(0))
|
|
||||||
.overlay {
|
|
||||||
Image(systemName: buttonConfig.iconName)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: size.width / 1.5, height: size.height / 1.5)
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.opacity(opacity)
|
|
||||||
.allowsHitTesting(false)
|
|
||||||
}
|
|
||||||
.frame(width: size.width, height: size.height)
|
|
||||||
.background(
|
|
||||||
buttonBackground
|
|
||||||
)
|
|
||||||
.onAppear {
|
|
||||||
size = calculateButtonSize()
|
|
||||||
}
|
|
||||||
.onChange(of: controllerScale) { _ in
|
|
||||||
size = calculateButtonSize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var buttonBackground: some View {
|
|
||||||
Group {
|
|
||||||
if !button.isTrigger && button != .leftStick && button != .rightStick {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.gray.opacity(0.3))
|
|
||||||
.frame(width: size.width * 1.25, height: size.height * 1.25)
|
|
||||||
} else if button == .leftStick || button == .rightStick {
|
|
||||||
Image(systemName: buttonConfig.iconName)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: size.width * 1.25, height: size.height * 1.25)
|
|
||||||
.foregroundColor(Color.gray.opacity(0.4))
|
|
||||||
} else if button.isTrigger {
|
|
||||||
Image(systemName: convertTriggerIconToButton(buttonConfig.iconName))
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: size.width * 1.25, height: size.height * 1.25)
|
|
||||||
.foregroundColor(Color.gray.opacity(0.4))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func convertTriggerIconToButton(_ iconName: String) -> String {
|
|
||||||
var converted = iconName
|
|
||||||
if iconName.hasPrefix("zl") || iconName.hasPrefix("zr") {
|
|
||||||
converted = String(iconName.dropFirst(3))
|
|
||||||
} else {
|
|
||||||
converted = String(iconName.dropFirst(2))
|
|
||||||
}
|
|
||||||
converted = converted
|
|
||||||
.replacingOccurrences(of: "rectangle", with: "button")
|
|
||||||
.replacingOccurrences(of: ".fill", with: ".horizontal.fill")
|
|
||||||
return converted
|
|
||||||
}
|
|
||||||
|
|
||||||
private func calculateButtonSize() -> CGSize {
|
|
||||||
let baseWidth: CGFloat
|
|
||||||
let baseHeight: CGFloat
|
|
||||||
|
|
||||||
if button.isTrigger {
|
|
||||||
baseWidth = 70
|
|
||||||
baseHeight = 40
|
|
||||||
} else if button.isSmall {
|
|
||||||
baseWidth = 35
|
|
||||||
baseHeight = 35
|
|
||||||
} else {
|
|
||||||
baseWidth = 45
|
|
||||||
baseHeight = 45
|
|
||||||
}
|
|
||||||
|
|
||||||
let deviceMultiplier = UIDevice.current.userInterfaceIdiom == .pad ? 1.2 : 1.0
|
|
||||||
let scaleMultiplier = CGFloat(controllerScale)
|
|
||||||
|
|
||||||
return CGSize(
|
|
||||||
width: baseWidth * deviceMultiplier * scaleMultiplier,
|
|
||||||
height: baseHeight * deviceMultiplier * scaleMultiplier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var buttonConfig: ButtonConfiguration {
|
|
||||||
switch button {
|
|
||||||
case .A: return .init(iconName: "a.circle.fill")
|
|
||||||
case .B: return .init(iconName: "b.circle.fill")
|
|
||||||
case .X: return .init(iconName: "x.circle.fill")
|
|
||||||
case .Y: return .init(iconName: "y.circle.fill")
|
|
||||||
case .leftStick: return .init(iconName: "l.joystick.press.down.fill")
|
|
||||||
case .rightStick: return .init(iconName: "r.joystick.press.down.fill")
|
|
||||||
case .dPadUp: return .init(iconName: "arrowtriangle.up.circle.fill")
|
|
||||||
case .dPadDown: return .init(iconName: "arrowtriangle.down.circle.fill")
|
|
||||||
case .dPadLeft: return .init(iconName: "arrowtriangle.left.circle.fill")
|
|
||||||
case .dPadRight: return .init(iconName: "arrowtriangle.right.circle.fill")
|
|
||||||
case .leftTrigger: return .init(iconName: "zl.rectangle.roundedtop.fill")
|
|
||||||
case .rightTrigger: return .init(iconName: "zr.rectangle.roundedtop.fill")
|
|
||||||
case .leftShoulder: return .init(iconName: "l.rectangle.roundedbottom.fill")
|
|
||||||
case .rightShoulder: return .init(iconName: "r.rectangle.roundedbottom.fill")
|
|
||||||
case .start: return .init(iconName: "plus.circle.fill")
|
|
||||||
case .back: return .init(iconName: "minus.circle.fill")
|
|
||||||
case .guide: return .init(iconName: "gearshape.fill")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ButtonConfiguration {
|
|
||||||
let iconName: String
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue