mirror of
https://git.citron-emu.org/citron/emu
synced 2025-05-13 11:20:36 +01:00
Compare commits
60 commits
v0.6.1-can
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7a668a98d7 | ||
![]() |
f45d818a64 | ||
![]() |
ac850bdf90 | ||
![]() |
7e58599d69 | ||
![]() |
6969005c54 | ||
![]() |
f3f18cede9 | ||
![]() |
03aab9becc | ||
![]() |
a5f722b6d9 | ||
![]() |
ba98d0f15c | ||
![]() |
fc88c06769 | ||
![]() |
7d213efca8 | ||
![]() |
791b95822d | ||
![]() |
f706427815 | ||
![]() |
6c10e0034f | ||
![]() |
44dfea60a9 | ||
![]() |
46abf95383 | ||
![]() |
5d3e75c25f | ||
![]() |
1bf27ebbb9 | ||
![]() |
eec0f34204 | ||
![]() |
257aad2431 | ||
![]() |
65dfe3234f | ||
![]() |
57cf5a0daf | ||
![]() |
020492f1fa | ||
![]() |
21ca0b3119 | ||
![]() |
58401f5b39 | ||
![]() |
1ad69f3545 | ||
![]() |
48eed78d1a | ||
![]() |
5f962dd1c6 | ||
![]() |
25abfe36a3 | ||
![]() |
2f57a35d2d | ||
![]() |
66bdd6ed27 | ||
![]() |
ff9c61e7c7 | ||
![]() |
e72d695115 | ||
![]() |
0cdd546152 | ||
![]() |
3205c9b691 | ||
![]() |
f1e169e060 | ||
![]() |
278486d059 | ||
![]() |
bbd3253169 | ||
![]() |
a1f3414bde | ||
![]() |
175a427c27 | ||
![]() |
18def48dfe | ||
![]() |
a4088f3a1e | ||
![]() |
b66b3ca639 | ||
![]() |
3a1c178711 | ||
![]() |
964bbf489a | ||
![]() |
19febba866 | ||
![]() |
0dac3c1dbd | ||
![]() |
5d952717ff | ||
![]() |
b25c7653e6 | ||
![]() |
edfb500ee7 | ||
![]() |
ebfc9d8347 | ||
![]() |
1fd5fefcb1 | ||
![]() |
55dc3f8ec1 | ||
![]() |
7edbccbdc9 | ||
![]() |
e06526cbbc | ||
![]() |
0448d8146f | ||
![]() |
98c515871e | ||
![]() |
278ac75a37 | ||
![]() |
ec402a0510 | ||
![]() |
8cb6e6d5d4 |
147 changed files with 4334 additions and 1300 deletions
14
.ci/citron-canary-step1.yml
Executable file
14
.ci/citron-canary-step1.yml
Executable file
|
@ -0,0 +1,14 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
stages:
|
||||
- stage: merge
|
||||
displayName: 'merge'
|
||||
jobs:
|
||||
- template: ./templates/merge-private.yml
|
59
.ci/citron-canary-step2.yml
Executable file
59
.ci/citron-canary-step2.yml
Executable file
|
@ -0,0 +1,59 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
variables:
|
||||
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
|
||||
|
||||
stages:
|
||||
- stage: build
|
||||
displayName: 'build'
|
||||
jobs:
|
||||
- job: linux
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'linux'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
matrix:
|
||||
linux:
|
||||
BuildSuffix: 'linux'
|
||||
ScriptFolder: 'linux'
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./templates/build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: $(parameters.cache)
|
||||
version: $(DisplayVersion)
|
||||
- job: msvc
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'windows'
|
||||
pool:
|
||||
vmImage: windows-2022
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./templates/build-msvc.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: $(parameters.cache)
|
||||
version: $(DisplayVersion)
|
||||
- stage: release
|
||||
displayName: 'release'
|
||||
dependsOn: build
|
||||
jobs:
|
||||
- job: release
|
||||
displayName: 'source'
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- template: ./templates/release-private-tag.yml
|
106
.ci/citron-pipeline.yml
Normal file
106
.ci/citron-pipeline.yml
Normal file
|
@ -0,0 +1,106 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
variables:
|
||||
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
|
||||
BuildName: 'stable'
|
||||
|
||||
stages:
|
||||
- stage: build
|
||||
displayName: 'build'
|
||||
jobs:
|
||||
- job: build
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'standard'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
matrix:
|
||||
linux:
|
||||
BuildSuffix: 'linux'
|
||||
ScriptFolder: 'linux'
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./templates/build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: 'true'
|
||||
version: $(DisplayVersion)
|
||||
# Make tokens available to scripts
|
||||
- task: Bash@3
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
echo "##vso[task.setvariable variable=EARLY_ACCESS_TOKEN]$(EARLY_ACCESS_TOKEN)"
|
||||
echo "##vso[task.setvariable variable=CI_RELEASE_TOKEN]$(CI_RELEASE_TOKEN)"
|
||||
|
||||
- stage: build_win
|
||||
displayName: 'build-windows'
|
||||
jobs:
|
||||
- job: build
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'msvc'
|
||||
pool:
|
||||
vmImage: windows-2022
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./templates/build-msvc.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: 'true'
|
||||
version: $(DisplayVersion)
|
||||
# Make tokens available to scripts
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
Write-Host "##vso[task.setvariable variable=EARLY_ACCESS_TOKEN]$(EARLY_ACCESS_TOKEN)"
|
||||
Write-Host "##vso[task.setvariable variable=CI_RELEASE_TOKEN]$(CI_RELEASE_TOKEN)"
|
||||
|
||||
- stage: build_android
|
||||
displayName: 'build-android'
|
||||
jobs:
|
||||
- job: build
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'android'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- task: Bash@3
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
export USE_DEBUG_KEYSTORE=true
|
||||
chmod +x ./.ci/scripts/android/build.sh
|
||||
./.ci/scripts/android/build.sh
|
||||
- publish: artifacts
|
||||
artifact: 'citron-$(BuildName)-android'
|
||||
displayName: 'Upload Android Artifacts'
|
||||
|
||||
- stage: release
|
||||
displayName: 'release'
|
||||
dependsOn:
|
||||
- build
|
||||
- build_win
|
||||
- build_android
|
||||
jobs:
|
||||
- job: github
|
||||
displayName: 'github'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- template: ./templates/release-github.yml
|
22
.ci/citron-repo-sync.yml
Executable file
22
.ci/citron-repo-sync.yml
Executable file
|
@ -0,0 +1,22 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
- job: copy
|
||||
displayName: 'Sync Repository'
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- script: echo 'https://$(GitUsername):$(GitAccessToken)@dev.azure.com' > $HOME/.git-credentials
|
||||
displayName: 'Load Credentials'
|
||||
- script: git config --global credential.helper store
|
||||
displayName: 'Register Credential Helper'
|
||||
- script: git remote add other $(GitRepoPushChangesURL)
|
||||
displayName: 'Register Repository'
|
||||
- script: git push --force other HEAD:$(GitPushBranch)
|
||||
displayName: 'Update Code'
|
||||
- script: rm -rf $HOME/.git-credentials
|
||||
displayName: 'Clear Cached Credentials'
|
14
.ci/citron-stable-step1.yml
Executable file
14
.ci/citron-stable-step1.yml
Executable file
|
@ -0,0 +1,14 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
stages:
|
||||
- stage: merge
|
||||
displayName: 'merge'
|
||||
jobs:
|
||||
- template: ./templates/merge.yml
|
64
.ci/citron-stable-step2.yml
Executable file
64
.ci/citron-stable-step2.yml
Executable file
|
@ -0,0 +1,64 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
variables:
|
||||
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
|
||||
|
||||
stages:
|
||||
- stage: build
|
||||
displayName: 'build'
|
||||
jobs:
|
||||
- job: build
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'standard'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
matrix:
|
||||
linux:
|
||||
BuildSuffix: 'linux'
|
||||
ScriptFolder: 'linux'
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./templates/build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: 'true'
|
||||
version: $(DisplayVersion)
|
||||
- stage: build_win
|
||||
displayName: 'build-windows'
|
||||
jobs:
|
||||
- job: build
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'msvc'
|
||||
pool:
|
||||
vmImage: windows-2022
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./templates/build-msvc.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: 'true'
|
||||
version: $(DisplayVersion)
|
||||
- stage: release
|
||||
displayName: 'release'
|
||||
dependsOn:
|
||||
- build
|
||||
- build_win
|
||||
jobs:
|
||||
- job: github
|
||||
displayName: 'github'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- template: ./templates/release-github.yml
|
23
.ci/citron-verify.yml
Executable file
23
.ci/citron-verify.yml
Executable file
|
@ -0,0 +1,23 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
stages:
|
||||
- stage: format
|
||||
displayName: 'format'
|
||||
jobs:
|
||||
- job: format
|
||||
displayName: 'clang'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- template: ./templates/format-check.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
- stage: build
|
||||
displayName: 'build'
|
||||
dependsOn: format
|
||||
jobs:
|
||||
- template: ./templates/build-standard.yml
|
||||
parameters:
|
||||
cache: 'false'
|
||||
- template: ./templates/build-testing.yml
|
28
.ci/scripts/android/build.sh
Executable file
28
.ci/scripts/android/build.sh
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
export NDK_CCACHE="$(which ccache)"
|
||||
ccache -s
|
||||
|
||||
BUILD_FLAVOR="stable"
|
||||
|
||||
mkdir -p "artifacts"
|
||||
|
||||
BUILD_TYPE="release"
|
||||
if [ "${GITHUB_REPOSITORY}" == "Citron-Project/Cit" ]; then
|
||||
BUILD_TYPE="relWithDebInfo"
|
||||
fi
|
||||
|
||||
# We'll use the Android debug keystore for builds
|
||||
# This removes the requirement for storing a keystore in secrets
|
||||
# For production builds, this should be replaced with a proper signing configuration
|
||||
export USE_DEBUG_KEYSTORE=true
|
||||
|
||||
cd src/android
|
||||
chmod +x ./gradlew
|
||||
./gradlew clean
|
||||
./gradlew app:assemble${BUILD_FLAVOR}${BUILD_TYPE}
|
||||
|
||||
ccache -s
|
21
.ci/scripts/android/canarybuild.sh
Executable file
21
.ci/scripts/android/canarybuild.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
export NDK_CCACHE="$(which ccache)"
|
||||
ccache -s
|
||||
|
||||
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
|
||||
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/service-account-key.json"
|
||||
|
||||
base64 --decode <<< "${CANARY_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
|
||||
chmod 600 "${ANDROID_KEYSTORE_FILE}"
|
||||
|
||||
mkdir -p "$(dirname "${SERVICE_ACCOUNT_KEY_PATH}")"
|
||||
base64 --decode <<< "${CANARY_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
|
||||
./gradlew "publishCanaryReleaseBundle"
|
||||
|
||||
ccache -s
|
||||
|
||||
rm "${ANDROID_KEYSTORE_FILE}" "${SERVICE_ACCOUNT_KEY_PATH}"
|
21
.ci/scripts/android/eabuild.sh
Executable file
21
.ci/scripts/android/eabuild.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
export NDK_CCACHE="$(which ccache)"
|
||||
ccache -s
|
||||
|
||||
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
|
||||
base64 --decode <<< "${EA_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
|
||||
export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
|
||||
export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
|
||||
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
|
||||
base64 --decode <<< "${EA_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
|
||||
./gradlew "publishEaReleaseBundle"
|
||||
|
||||
ccache -s
|
||||
|
||||
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
|
||||
rm "${ANDROID_KEYSTORE_FILE}"
|
||||
fi
|
21
.ci/scripts/android/stablebuild.sh
Executable file
21
.ci/scripts/android/stablebuild.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
export NDK_CCACHE="$(which ccache)"
|
||||
ccache -s
|
||||
|
||||
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
|
||||
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/service-account-key.json"
|
||||
|
||||
base64 --decode <<< "${STABLE_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
|
||||
chmod 600 "${ANDROID_KEYSTORE_FILE}"
|
||||
|
||||
mkdir -p "$(dirname "${SERVICE_ACCOUNT_KEY_PATH}")"
|
||||
base64 --decode <<< "${STABLE_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
|
||||
./gradlew "publishStableReleaseBundle"
|
||||
|
||||
ccache -s
|
||||
|
||||
rm "${ANDROID_KEYSTORE_FILE}" "${SERVICE_ACCOUNT_KEY_PATH}"
|
41
.ci/scripts/android/upload.sh
Executable file
41
.ci/scripts/android/upload.sh
Executable file
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
APPNAME="org.citron.citron"
|
||||
|
||||
REV_NAME="${APPNAME}-${GITDATE}-${GITREV}"
|
||||
|
||||
APK="${REV_NAME}.apk"
|
||||
|
||||
if [ "${GITHUB_REPOSITORY}" == "Citron-Project/Cit" ]; then
|
||||
cd src/android
|
||||
./gradlew app:bundleRelease
|
||||
# RELEASE_BODY gets reset here, but its fine since we're uploading after the GitHub Release
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
|
||||
sudo apt-get install -y jq
|
||||
|
||||
ls app/build/outputs/bundle/release/
|
||||
|
||||
echo "Downloading CI scripts bundle"
|
||||
ARTIFACTS_DIR="/tmp/artifact-pool"
|
||||
mkdir -p "$ARTIFACTS_DIR"
|
||||
fi
|
||||
|
||||
BUILD_FLAVOR="stable"
|
||||
|
||||
BUILD_TYPE_LOWER="release"
|
||||
BUILD_TYPE_UPPER="Release"
|
||||
if [ "${GITHUB_REPOSITORY}" == "Citron-Project/Cit" ]; then
|
||||
BUILD_TYPE_LOWER="relWithDebInfo"
|
||||
BUILD_TYPE_UPPER="RelWithDebInfo"
|
||||
fi
|
||||
|
||||
cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/${BUILD_TYPE_LOWER}/app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.apk" \
|
||||
"artifacts/${REV_NAME}.apk"
|
||||
cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}${BUILD_TYPE_UPPER}"/"app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.aab" \
|
||||
"artifacts/${REV_NAME}.aab"
|
32
.ci/scripts/clang/docker.sh
Executable file
32
.ci/scripts/clang/docker.sh
Executable file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2021 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ \
|
||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/clang \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DDISPLAY_VERSION=$1 \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DCITRON_CRASH_DUMPS=ON \
|
||||
-DCITRON_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DCITRON_USE_BUNDLED_FFMPEG=ON \
|
||||
-GNinja
|
||||
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
11
.ci/scripts/clang/exec.sh
Executable file
11
.ci/scripts/clang/exec.sh
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2021 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/clang/docker.sh
|
||||
# the UID for the container citron user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/citron/ccache -v "$(pwd):/citron" -w /citron yuzu-emu-mirror/build-environments:linux-fresh /bin/bash /citron/.ci/scripts/clang/docker.sh "$1"
|
||||
sudo chown -R $UID ./
|
23
.ci/scripts/clang/upload.sh
Executable file
23
.ci/scripts/clang/upload.sh
Executable file
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
REV_NAME="citron-linux-clang-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "stable" ]; then
|
||||
DIR_NAME="${REV_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir -p $DIR_NAME
|
||||
|
||||
cp build/bin/citron "$DIR_NAME"
|
||||
cp build/bin/citron-cmd "$DIR_NAME"
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
20
.ci/scripts/common/post-upload.sh
Executable file
20
.ci/scripts/common/post-upload.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Copy documentation
|
||||
cp LICENSE.txt "$DIR_NAME"
|
||||
cp README.md "$DIR_NAME"
|
||||
|
||||
if [[ -z "${NO_SOURCE_PACK}" ]]; then
|
||||
git clone --depth 1 file://$(readlink -e .) ${REV_NAME}-source
|
||||
tar -cJvf "${REV_NAME}-source.tar.xz" ${REV_NAME}-source
|
||||
cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME"
|
||||
cp -v "${REV_NAME}-source.tar.xz" "${ARTIFACTS_DIR}/"
|
||||
fi
|
||||
|
||||
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
|
||||
|
||||
# move the compiled archive into the artifacts directory to be uploaded by travis releases
|
||||
mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/"
|
10
.ci/scripts/common/pre-upload.sh
Executable file
10
.ci/scripts/common/pre-upload.sh
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||
GITREV="`git show -s --format='%h'`"
|
||||
ARTIFACTS_DIR="$PWD/artifacts"
|
||||
|
||||
mkdir -p "${ARTIFACTS_DIR}/"
|
9
.ci/scripts/format/docker.sh
Executable file
9
.ci/scripts/format/docker.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Run clang-format
|
||||
cd /citron
|
||||
chmod a+x ./.ci/scripts/format/script.sh
|
||||
./.ci/scripts/format/script.sh
|
10
.ci/scripts/format/exec.sh
Executable file
10
.ci/scripts/format/exec.sh
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
chmod a+x ./.ci/scripts/format/docker.sh
|
||||
# the UID for the container citron user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -v "$(pwd):/citron" -w /citron yuzu-emu-mirror/build-environments:linux-clang-format /bin/bash -ex /citron/.ci/scripts/format/docker.sh
|
||||
sudo chown -R $UID ./
|
37
.ci/scripts/format/script.sh
Executable file
37
.ci/scripts/format/script.sh
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
shopt -s nullglob globstar
|
||||
|
||||
if git grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop dist/*.svg dist/*.xml; then
|
||||
echo Trailing whitespace found, aborting
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Default clang-format points to default 3.5 version one
|
||||
CLANG_FORMAT="${CLANG_FORMAT:-clang-format-15}"
|
||||
"$CLANG_FORMAT" --version
|
||||
|
||||
# Turn off tracing for this because it's too verbose
|
||||
set +x
|
||||
|
||||
# Check everything for branch pushes
|
||||
FILES_TO_LINT="$(find src/ -name '*.cpp' -or -name '*.h')"
|
||||
|
||||
for f in $FILES_TO_LINT; do
|
||||
echo "$f"
|
||||
"$CLANG_FORMAT" -i "$f"
|
||||
done
|
||||
|
||||
DIFF=$(git -c core.fileMode=false diff)
|
||||
|
||||
if [ ! -z "$DIFF" ]; then
|
||||
echo "!!! Not compliant to coding style, here is the fix:"
|
||||
echo "$DIFF"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd src/android
|
||||
./gradlew ktlintCheck
|
79
.ci/scripts/linux/docker.sh
Executable file
79
.ci/scripts/linux/docker.sh
Executable file
|
@ -0,0 +1,79 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. \
|
||||
-DBoost_USE_STATIC_LIBS=ON \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DDISPLAY_VERSION=$1 \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DCITRON_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DCITRON_USE_BUNDLED_FFMPEG=ON \
|
||||
-DCITRON_ENABLE_LTO=ON \
|
||||
-DCITRON_CRASH_DUMPS=ON \
|
||||
-GNinja
|
||||
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
# Separate debug symbols from specified executables
|
||||
for EXE in citron; do
|
||||
EXE_PATH="bin/$EXE"
|
||||
# Copy debug symbols out
|
||||
objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug
|
||||
# Add debug link and strip debug symbols
|
||||
objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out
|
||||
# Overwrite original with stripped copy
|
||||
mv $EXE_PATH.out $EXE_PATH
|
||||
done
|
||||
# Strip debug symbols from all executables
|
||||
find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';'
|
||||
|
||||
DESTDIR="$PWD/AppDir" ninja install
|
||||
rm -vf AppDir/usr/bin/citron-cmd AppDir/usr/bin/citron-tester
|
||||
|
||||
# Download tools needed to build an AppImage
|
||||
wget -nc https://raw.githubusercontent.com/yuzu-emu-mirror/ext-linux-bin/main/appimage/deploy-linux.sh || wget -nc https://raw.githubusercontent.com/AppImage/AppImageKit/master/appimagetool/deploy-linux.sh
|
||||
wget -nc https://raw.githubusercontent.com/yuzu-emu-mirror/AppImageKit-checkrt/old/AppRun.sh || wget -nc https://raw.githubusercontent.com/darealshinji/AppImageKit-checkrt/master/AppRun.sh
|
||||
wget -nc https://github.com/yuzu-emu-mirror/ext-linux-bin/raw/main/appimage/exec-x86_64.so || wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so
|
||||
# Set executable bit
|
||||
chmod 755 \
|
||||
deploy-linux.sh \
|
||||
AppRun.sh \
|
||||
exec-x86_64.so
|
||||
|
||||
# Workaround for https://github.com/AppImage/AppImageKit/issues/828
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
|
||||
mkdir -p AppDir/usr/optional
|
||||
mkdir -p AppDir/usr/optional/libstdc++
|
||||
mkdir -p AppDir/usr/optional/libgcc_s
|
||||
|
||||
# Deploy citron's needed dependencies
|
||||
DEPLOY_QT=1 ./deploy-linux.sh AppDir/usr/bin/citron AppDir
|
||||
|
||||
# Workaround for libQt5MultimediaGstTools indirectly requiring libwayland-client and breaking Vulkan usage on end-user systems
|
||||
find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print
|
||||
|
||||
# Workaround for building citron with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al.
|
||||
# See https://github.com/darealshinji/AppImageKit-checkrt
|
||||
cp exec-x86_64.so AppDir/usr/optional/exec.so
|
||||
cp AppRun.sh AppDir/AppRun
|
||||
cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
|
||||
cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
|
16
.ci/scripts/linux/exec.sh
Executable file
16
.ci/scripts/linux/exec.sh
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/linux/docker.sh
|
||||
# the UID for the container citron user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
|
||||
# The environment variables listed below:
|
||||
# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
|
||||
# are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps
|
||||
|
||||
docker run -e AZURECIREPO -e TITLEBARFORMATIDLE -e TITLEBARFORMATRUNNING -e DISPLAYVERSION -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/citron/ccache -v "$(pwd):/citron" -w /citron yuzu-emu-mirror/build-environments:linux-fresh /bin/bash /citron/.ci/scripts/linux/docker.sh "$1"
|
||||
sudo chown -R $UID ./
|
67
.ci/scripts/linux/upload.sh
Executable file
67
.ci/scripts/linux/upload.sh
Executable file
|
@ -0,0 +1,67 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
APPIMAGE_NAME="citron-${RELEASE_NAME}-${GITDATE}-${GITREV}.AppImage"
|
||||
BASE_NAME="citron-linux"
|
||||
REV_NAME="${BASE_NAME}-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "stable" ] || [ "${RELEASE_NAME}" = "canary" ]; then
|
||||
DIR_NAME="${BASE_NAME}-${RELEASE_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}-${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
|
||||
cp build/bin/citron-cmd "$DIR_NAME"
|
||||
if [ "${RELEASE_NAME}" != "canary" ] && [ "${RELEASE_NAME}" != "stable" ]; then
|
||||
cp build/bin/citron "$DIR_NAME"
|
||||
fi
|
||||
|
||||
# Build an AppImage
|
||||
cd build
|
||||
|
||||
wget -nc https://github.com/yuzu-emu-mirror/ext-linux-bin/raw/main/appimage/appimagetool-x86_64.AppImage || wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
chmod 755 appimagetool-x86_64.AppImage
|
||||
|
||||
# if FUSE is not available, then fallback to extract and run
|
||||
if ! ./appimagetool-x86_64.AppImage --version; then
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
fi
|
||||
|
||||
# Don't let AppImageLauncher ask to integrate EA
|
||||
if [ "${RELEASE_NAME}" = "stable" ] || [ "${RELEASE_NAME}" = "canary" ]; then
|
||||
echo "X-AppImage-Integrate=false" >> AppDir/org.citron_emu.citron.desktop
|
||||
fi
|
||||
|
||||
if [ "${RELEASE_NAME}" = "stable" ]; then
|
||||
# Generate update information if releasing to stable
|
||||
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|Citron-Project|Cit|latest|citron-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
|
||||
else
|
||||
./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}"
|
||||
fi
|
||||
cd ..
|
||||
|
||||
# Copy the AppImage and update info to the artifacts directory and avoid compressing it
|
||||
cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/"
|
||||
if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
|
||||
cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
|
||||
fi
|
||||
|
||||
# Copy the AppImage to the general release directory and remove git revision info
|
||||
if [ "${RELEASE_NAME}" = "stable" ] || [ "${RELEASE_NAME}" = "canary" ]; then
|
||||
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/citron-${RELEASE_NAME}.AppImage"
|
||||
fi
|
||||
|
||||
# Copy debug symbols to artifacts
|
||||
cd build/bin
|
||||
tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug
|
||||
cd -
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
19
.ci/scripts/merge/apply-patches-by-label-private.py
Executable file
19
.ci/scripts/merge/apply-patches-by-label-private.py
Executable file
|
@ -0,0 +1,19 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Apply patches that are tagged with a specific label
|
||||
# Current labels include:
|
||||
# "canary merge benefit"
|
||||
|
||||
import os
|
||||
import sys
|
||||
org = os.getenv("PRIVATEMERGEORG", "Citron-Project")
|
||||
base = os.getenv("PRIVATEMERGEBASE", "Cit")
|
||||
head = os.getenv("PRIVATEMERGEBASE", "Cit-canary")
|
||||
|
||||
# We use different files for the 3 PR checkers.
|
||||
check_label_presence_file = ".ci/scripts/merge/check-label-presence.py"
|
||||
check_reviews_file = ".ci/scripts/merge/check-reviews-private.py"
|
||||
|
||||
# python3 python_file org repo pr# label
|
||||
os.system(f"python3 .ci/scripts/merge/apply-patches-by-label.py {sys.argv[1]} {org} {base} {head} {check_label_presence_file} {check_reviews_file}")
|
60
.ci/scripts/merge/apply-patches-by-label.py
Executable file
60
.ci/scripts/merge/apply-patches-by-label.py
Executable file
|
@ -0,0 +1,60 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Apply patches that are tagged with a specific label
|
||||
# Current labels include:
|
||||
# "canary merge benefit"
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
from urllib import request
|
||||
|
||||
|
||||
def get_pull_request_list(page=1):
|
||||
url = f"https://api.github.com/repos/Citron-Project/Cit/pulls?page={page}"
|
||||
headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||
token = os.environ.get('EARLY_ACCESS_TOKEN')
|
||||
if token:
|
||||
headers['Authorization'] = f'token {token}'
|
||||
req = request.Request(url, headers=headers)
|
||||
response = request.urlopen(req)
|
||||
result = json.loads(response.read().decode('utf-8'))
|
||||
if len(result) > 0:
|
||||
return result + get_pull_request_list(page + 1)
|
||||
return []
|
||||
|
||||
|
||||
seen = {}
|
||||
|
||||
for pr in get_pull_request_list():
|
||||
pn = pr['number']
|
||||
if pn in seen:
|
||||
continue
|
||||
seen[pn] = True
|
||||
print(subprocess.check_output(["git", "fetch", "https://github.com/Citron-Project/Cit.git", f"pull/{pn}/head:pr-{pn}", "-f", "--no-recurse-submodules"]))
|
||||
out = subprocess.check_output(["python3", ".ci/scripts/merge/check-label-presence.py", str(pn), sys.argv[1]])
|
||||
if b'true' in out:
|
||||
print(f'Applying PR #{pn} matching label {sys.argv[1]}...')
|
||||
try:
|
||||
if b'changes requested' in subprocess.check_output(["python3", ".ci/scripts/merge/check-reviews.py", str(pn)]):
|
||||
print(f'PR #{pn} has requested changes, skipping')
|
||||
continue
|
||||
else:
|
||||
print(f'PR #{pn} has approving reviews, applying')
|
||||
except:
|
||||
print('Could not find any valid reviews, applying anyway')
|
||||
try:
|
||||
print(subprocess.check_output(["git", "merge", "--squash", f"pr-{pn}", "-m", f"Merge PR #{pn} tagged with: {sys.argv[1]}"]))
|
||||
except:
|
||||
print("Failed to merge cleanly, are there conflicts?")
|
||||
exit(0)
|
||||
|
||||
# Apply a tag to the EA release
|
||||
try:
|
||||
print(subprocess.check_output(["git", "tag", "-f", f"canary/{sys.argv[1]}"]))
|
||||
except:
|
||||
print("Failed to apply canary tag")
|
||||
exit(0)
|
27
.ci/scripts/merge/check-label-presence.py
Executable file
27
.ci/scripts/merge/check-label-presence.py
Executable file
|
@ -0,0 +1,27 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Check if a pull request has a specific label
|
||||
# Usage: python3 check-label-presence.py <PR Number> <Label Text>
|
||||
|
||||
import json, sys
|
||||
from urllib import request
|
||||
import os
|
||||
|
||||
url = 'https://api.github.com/repos/Citron-Project/Cit/issues/%s' % sys.argv[1]
|
||||
headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||
token = os.environ.get('EARLY_ACCESS_TOKEN')
|
||||
if token:
|
||||
headers['Authorization'] = f'token {token}'
|
||||
req = request.Request(url, headers=headers)
|
||||
try:
|
||||
response = request.urlopen(req)
|
||||
j = json.loads(response.read().decode('utf-8'))
|
||||
for label in j['labels']:
|
||||
if label['name'] == sys.argv[2]:
|
||||
print('true')
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f'Error: {e}')
|
||||
print('false')
|
||||
sys.exit(1)
|
5
.ci/scripts/merge/citronbot-git-config.sh
Executable file
5
.ci/scripts/merge/citronbot-git-config.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
git config --global user.email "zephyron@citron-emu.org"
|
||||
git config --global user.name "Zephyron"
|
19
.ci/scripts/transifex/docker.sh
Executable file
19
.ci/scripts/transifex/docker.sh
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
# SPDX-FileCopyrightText: 2021 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set -x
|
||||
|
||||
echo -e "\e[1m\e[33mBuild tools information:\e[0m"
|
||||
cmake --version
|
||||
gcc -v
|
||||
tx --version
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF -DCITRON_TESTS=OFF -DCITRON_USE_BUNDLED_VCPKG=ON
|
||||
make translation
|
||||
cd ..
|
||||
|
||||
cd dist/languages
|
||||
tx push -s
|
66
.ci/scripts/windows/docker.sh
Executable file
66
.ci/scripts/windows/docker.sh
Executable file
|
@ -0,0 +1,66 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set -e
|
||||
|
||||
#cd /citron
|
||||
|
||||
ccache -sv
|
||||
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWCross.cmake" \
|
||||
-DDISPLAY_VERSION="$1" \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_CCACHE=ON \
|
||||
-DCITRON_USE_BUNDLED_SDL2=OFF \
|
||||
-DCITRON_USE_EXTERNAL_SDL2=OFF \
|
||||
-DCITRON_TESTS=OFF \
|
||||
-GNinja
|
||||
ninja citron citron-cmd
|
||||
|
||||
ccache -sv
|
||||
|
||||
echo "Tests skipped"
|
||||
#ctest -VV -C Release
|
||||
|
||||
echo 'Prepare binaries...'
|
||||
cd ..
|
||||
mkdir package
|
||||
|
||||
if [ -d "/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/" ]; then
|
||||
QT_PLUGINS_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins'
|
||||
else
|
||||
#fallback to qt
|
||||
QT_PLUGINS_PATH='/usr/x86_64-w64-mingw32/lib/qt/plugins'
|
||||
fi
|
||||
|
||||
find build/ -name "citron*.exe" -exec cp {} 'package' \;
|
||||
|
||||
# copy Qt plugins
|
||||
mkdir package/platforms
|
||||
cp -v "${QT_PLUGINS_PATH}/platforms/qwindows.dll" package/platforms/
|
||||
cp -rv "${QT_PLUGINS_PATH}/mediaservice/" package/
|
||||
cp -rv "${QT_PLUGINS_PATH}/imageformats/" package/
|
||||
cp -rv "${QT_PLUGINS_PATH}/styles/" package/
|
||||
rm -f package/mediaservice/*d.dll
|
||||
|
||||
for i in package/*.exe; do
|
||||
# we need to process pdb here, however, cv2pdb
|
||||
# does not work here, so we just simply strip all the debug symbols
|
||||
x86_64-w64-mingw32-strip "${i}"
|
||||
done
|
||||
|
||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
|
||||
|
||||
# copy FFmpeg libraries
|
||||
EXTERNALS_PATH="$(pwd)/build/externals"
|
||||
FFMPEG_DLL_PATH="$(find "${EXTERNALS_PATH}" -maxdepth 1 -type d | grep 'ffmpeg-')/bin"
|
||||
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -nv {} package/ ';'
|
||||
|
||||
# copy libraries from citron.exe path
|
||||
find "$(pwd)/build/bin/" -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
|
11
.ci/scripts/windows/exec.sh
Executable file
11
.ci/scripts/windows/exec.sh
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/windows/docker.sh
|
||||
# the UID for the container citron user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e CCACHE_DIR=/citron/ccache -v "$(pwd):/citron" -w /citron yuzu-emu-mirror/build-environments:linux-mingw /bin/bash -ex /citron/.ci/scripts/windows/docker.sh "$1"
|
||||
sudo chown -R $UID ./
|
33
.ci/scripts/windows/install-vulkan-sdk.ps1
Executable file
33
.ci/scripts/windows/install-vulkan-sdk.ps1
Executable file
|
@ -0,0 +1,33 @@
|
|||
# SPDX-FileCopyrightText: 2023 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$VulkanSDKVer = "1.3.250.1"
|
||||
$ExeFile = "VulkanSDK-$VulkanSDKVer-Installer.exe"
|
||||
$Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile"
|
||||
$Destination = "./$ExeFile"
|
||||
|
||||
echo "Downloading Vulkan SDK $VulkanSDKVer from $Uri"
|
||||
$WebClient = New-Object System.Net.WebClient
|
||||
$WebClient.DownloadFile($Uri, $Destination)
|
||||
echo "Finished downloading $ExeFile"
|
||||
|
||||
$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer"
|
||||
$Arguments = "--root `"$VULKAN_SDK`" --accept-licenses --default-answer --confirm-command install"
|
||||
|
||||
echo "Installing Vulkan SDK $VulkanSDKVer"
|
||||
$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments
|
||||
$ExitCode = $InstallProcess.ExitCode
|
||||
|
||||
if ($ExitCode -ne 0) {
|
||||
echo "Error installing Vulkan SDK $VulkanSDKVer (Error: $ExitCode)"
|
||||
Exit $ExitCode
|
||||
}
|
||||
|
||||
echo "Finished installing Vulkan SDK $VulkanSDKVer"
|
||||
|
||||
if ("$env:GITHUB_ACTIONS" -eq "true") {
|
||||
echo "VULKAN_SDK=$VULKAN_SDK" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "$VULKAN_SDK/Bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
}
|
109
.ci/scripts/windows/scan_dll.py
Executable file
109
.ci/scripts/windows/scan_dll.py
Executable file
|
@ -0,0 +1,109 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import pefile
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import queue
|
||||
import shutil
|
||||
|
||||
# constant definitions
|
||||
KNOWN_SYS_DLLS = ['WINMM.DLL', 'MSVCRT.DLL', 'VERSION.DLL', 'MPR.DLL',
|
||||
'DWMAPI.DLL', 'UXTHEME.DLL', 'DNSAPI.DLL', 'IPHLPAPI.DLL']
|
||||
# below is for Ubuntu 18.04 with specified PPA enabled, if you are using
|
||||
# other distro or different repositories, change the following accordingly
|
||||
DLL_PATH = [
|
||||
'/usr/x86_64-w64-mingw32/bin/',
|
||||
'/usr/x86_64-w64-mingw32/lib/',
|
||||
'/usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/'
|
||||
]
|
||||
|
||||
missing = []
|
||||
|
||||
|
||||
def parse_imports(file_name):
|
||||
results = []
|
||||
pe = pefile.PE(file_name, fast_load=True)
|
||||
pe.parse_data_directories()
|
||||
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
current = entry.dll.decode()
|
||||
current_u = current.upper() # b/c Windows is often case insensitive
|
||||
# here we filter out system dlls
|
||||
# dll w/ names like *32.dll are likely to be system dlls
|
||||
if current_u.upper() not in KNOWN_SYS_DLLS and not re.match(string=current_u, pattern=r'.*32\.DLL'):
|
||||
results.append(current)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def parse_imports_recursive(file_name, path_list=[]):
|
||||
q = queue.Queue() # create a FIFO queue
|
||||
# file_name can be a string or a list for the convenience
|
||||
if isinstance(file_name, str):
|
||||
q.put(file_name)
|
||||
elif isinstance(file_name, list):
|
||||
for i in file_name:
|
||||
q.put(i)
|
||||
full_list = []
|
||||
while q.qsize():
|
||||
current = q.get_nowait()
|
||||
print('> %s' % current)
|
||||
deps = parse_imports(current)
|
||||
# if this dll does not have any import, ignore it
|
||||
if not deps:
|
||||
continue
|
||||
for dep in deps:
|
||||
# the dependency already included in the list, skip
|
||||
if dep in full_list:
|
||||
continue
|
||||
# find the requested dll in the provided paths
|
||||
full_path = find_dll(dep)
|
||||
if not full_path:
|
||||
missing.append(dep)
|
||||
continue
|
||||
full_list.append(dep)
|
||||
q.put(full_path)
|
||||
path_list.append(full_path)
|
||||
return full_list
|
||||
|
||||
|
||||
def find_dll(name):
|
||||
for path in DLL_PATH:
|
||||
for root, _, files in os.walk(path):
|
||||
for f in files:
|
||||
if name.lower() == f.lower():
|
||||
return os.path.join(root, f)
|
||||
|
||||
|
||||
def deploy(name, dst, dry_run=False):
|
||||
dlls_path = []
|
||||
parse_imports_recursive(name, dlls_path)
|
||||
for dll_entry in dlls_path:
|
||||
if not dry_run:
|
||||
shutil.copy(dll_entry, dst)
|
||||
else:
|
||||
print('[Dry-Run] Copy %s to %s' % (dll_entry, dst))
|
||||
print('Deploy completed.')
|
||||
return dlls_path
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print('Usage: %s [files to examine ...] [target deploy directory]')
|
||||
return 1
|
||||
to_deploy = sys.argv[1:-1]
|
||||
tgt_dir = sys.argv[-1]
|
||||
if not os.path.isdir(tgt_dir):
|
||||
print('%s is not a directory.' % tgt_dir)
|
||||
return 1
|
||||
print('Scanning dependencies...')
|
||||
deploy(to_deploy, tgt_dir)
|
||||
if missing:
|
||||
print('Following DLLs are not found: %s' % ('\n'.join(missing)))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
118
.ci/scripts/windows/upload.ps1
Executable file
118
.ci/scripts/windows/upload.ps1
Executable file
|
@ -0,0 +1,118 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
param($BUILD_NAME)
|
||||
|
||||
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-", ""
|
||||
$GITREV = $(git show -s --format='%h')
|
||||
|
||||
if ("$BUILD_NAME" -eq "stable") {
|
||||
$RELEASE_DIST = "citron-stable-msvc"
|
||||
}
|
||||
else {
|
||||
$RELEASE_DIST = "citron-windows-msvc-$BUILD_NAME"
|
||||
}
|
||||
|
||||
$MSVC_BUILD_ZIP = "citron-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
|
||||
$MSVC_BUILD_PDB = "citron-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", ""
|
||||
$MSVC_SEVENZIP = "citron-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
|
||||
$MSVC_TAR = "citron-windows-msvc-$GITDATE-$GITREV.tar" -replace " ", ""
|
||||
$MSVC_TARXZ = "citron-windows-msvc-$GITDATE-$GITREV.tar.xz" -replace " ", ""
|
||||
$MSVC_SOURCE = "citron-windows-msvc-source-$GITDATE-$GITREV" -replace " ", ""
|
||||
$MSVC_SOURCE_TAR = "$MSVC_SOURCE.tar"
|
||||
$MSVC_SOURCE_TARXZ = "$MSVC_SOURCE_TAR.xz"
|
||||
|
||||
$env:BUILD_ZIP = $MSVC_BUILD_ZIP
|
||||
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
|
||||
$env:BUILD_UPDATE = $MSVC_SEVENZIP
|
||||
|
||||
if (Test-Path -Path ".\build\bin\Release") {
|
||||
$BUILD_DIR = ".\build\bin\Release"
|
||||
} else {
|
||||
$BUILD_DIR = ".\build\bin\"
|
||||
}
|
||||
|
||||
# Cleanup unneeded data in submodules
|
||||
git submodule foreach git clean -fxd
|
||||
|
||||
# Upload debugging symbols
|
||||
mkdir pdb
|
||||
Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
|
||||
7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
|
||||
rm "$BUILD_DIR\*.pdb"
|
||||
|
||||
# Create artifact directories
|
||||
mkdir $RELEASE_DIST
|
||||
mkdir $MSVC_SOURCE
|
||||
mkdir "artifacts"
|
||||
|
||||
$CURRENT_DIR = Convert-Path .
|
||||
|
||||
# Build a tar.xz for the source of the release
|
||||
git clone --depth 1 file://$CURRENT_DIR $MSVC_SOURCE
|
||||
7z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE
|
||||
7z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR
|
||||
|
||||
# Following section is quick hack to package artifacts differently for GitHub Actions
|
||||
if ("$env:GITHUB_ACTIONS" -eq "true") {
|
||||
echo "Hello GitHub Actions"
|
||||
|
||||
# With vcpkg we now have a few more dll files
|
||||
ls .\build\bin\*.dll
|
||||
cp .\build\bin\*.dll .\artifacts\
|
||||
|
||||
# Hopefully there is an exe in either .\build\bin or .\build\bin\Release
|
||||
cp .\build\bin\citron*.exe .\artifacts\
|
||||
Copy-Item "$BUILD_DIR\*" -Destination "artifacts" -Recurse
|
||||
Remove-Item .\artifacts\tests.exe -ErrorAction ignore
|
||||
|
||||
# None of the other GHA builds are including source, so commenting out today
|
||||
#Copy-Item $MSVC_SOURCE_TARXZ -Destination "artifacts"
|
||||
|
||||
# Debugging symbols
|
||||
cp .\build\bin\citron*.pdb .\artifacts\
|
||||
|
||||
# Write out a tag BUILD_TAG to environment for the Upload step
|
||||
# We're getting ${{ github.event.number }} as $env:PR_NUMBER"
|
||||
echo "env:PR_NUMBER: $env:PR_NUMBER"
|
||||
if (Test-Path env:PR_NUMBER) {
|
||||
$PR_NUMBER = $env:PR_NUMBER.Substring(2) -as [int]
|
||||
$PR_NUMBER_TAG = "pr"+([string]$PR_NUMBER).PadLeft(5,'0')
|
||||
if ($PR_NUMBER -gt 1){
|
||||
$BUILD_TAG="verify-$PR_NUMBER_TAG-$GITDATE-$GITREV"
|
||||
} else {
|
||||
$BUILD_TAG = "verify-$GITDATE-$GITREV"
|
||||
}
|
||||
} else {
|
||||
# If env:PR_NUMBER isn't set, we should still write out a variable
|
||||
$BUILD_TAG = "verify-$GITDATE-$GITREV"
|
||||
}
|
||||
echo "BUILD_TAG=$BUILD_TAG"
|
||||
echo "BUILD_TAG=$BUILD_TAG" >> $env:GITHUB_ENV
|
||||
|
||||
# For extra job, just the exe
|
||||
$INDIVIDUAL_EXE = "citron-msvc-$BUILD_TAG.exe"
|
||||
echo "INDIVIDUAL_EXE=$INDIVIDUAL_EXE"
|
||||
echo "INDIVIDUAL_EXE=$INDIVIDUAL_EXE" >> $env:GITHUB_ENV
|
||||
echo "Just the exe: $INDIVIDUAL_EXE"
|
||||
cp .\artifacts\citron.exe .\$INDIVIDUAL_EXE
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
# Build the final release artifacts
|
||||
Copy-Item $MSVC_SOURCE_TARXZ -Destination $RELEASE_DIST
|
||||
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
|
||||
rm "$RELEASE_DIST\*.exe"
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "citron*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MSVC_SEVENZIP $RELEASE_DIST
|
||||
|
||||
7z a -r -ttar $MSVC_TAR $RELEASE_DIST
|
||||
7z a -r -txz $MSVC_TARXZ $MSVC_TAR
|
||||
|
||||
Get-ChildItem . -Filter "*.zip" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.7z" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.tar.xz" | Copy-Item -destination "artifacts"
|
||||
}
|
28
.ci/scripts/windows/upload.sh
Executable file
28
.ci/scripts/windows/upload.sh
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
REV_NAME="citron-windows-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "stable" ]; then
|
||||
DIR_NAME="${REV_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
# get around the permission issues
|
||||
cp -r package/* "$DIR_NAME"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "stable" ]; then
|
||||
MSVC_BUILD_ZIP="citron-stable-msvc.zip"
|
||||
else
|
||||
MSVC_BUILD_ZIP="${REV_NAME}-msvc.zip"
|
||||
fi
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
8
.ci/templates/build-mock.yml
Executable file
8
.ci/templates/build-mock.yml
Executable file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
steps:
|
||||
- script: mkdir artifacts || echo 'X' > artifacts/T1.txt
|
||||
- publish: artifacts
|
||||
artifact: 'citron-$(BuildName)-mock'
|
||||
displayName: 'Upload Artifacts'
|
31
.ci/templates/build-msvc.yml
Executable file
31
.ci/templates/build-msvc.yml
Executable file
|
@ -0,0 +1,31 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
artifactSource: 'true'
|
||||
cache: 'false'
|
||||
version: ''
|
||||
|
||||
steps:
|
||||
- task: Powershell@2
|
||||
displayName: 'Install Vulkan SDK'
|
||||
inputs:
|
||||
targetType: 'filePath'
|
||||
filePath: './.ci/scripts/windows/install-vulkan-sdk.ps1'
|
||||
- script: refreshenv && glslangValidator --version && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DCITRON_ENABLE_LTO=ON -DCITRON_USE_BUNDLED_QT=1 -DCITRON_USE_BUNDLED_SDL2=1 -DCITRON_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCITRON_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DCITRON_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DCITRON_CRASH_DUMPS=ON .. && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
inputs:
|
||||
solution: 'build/citron.sln'
|
||||
maximumCpuCount: true
|
||||
configuration: release
|
||||
- task: PowerShell@2
|
||||
displayName: 'Package Artifacts'
|
||||
inputs:
|
||||
targetType: 'filePath'
|
||||
filePath: './.ci/scripts/windows/upload.ps1'
|
||||
arguments: '$(BuildName)'
|
||||
- publish: artifacts
|
||||
artifact: 'citron-$(BuildName)-windows-msvc'
|
||||
displayName: 'Upload Artifacts'
|
26
.ci/templates/build-single.yml
Executable file
26
.ci/templates/build-single.yml
Executable file
|
@ -0,0 +1,26 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
artifactSource: 'true'
|
||||
cache: 'false'
|
||||
version: ''
|
||||
|
||||
steps:
|
||||
- task: DockerInstaller@0
|
||||
displayName: 'Prepare Environment'
|
||||
inputs:
|
||||
dockerVersion: '17.09.0-ce'
|
||||
- task: CacheBeta@0
|
||||
displayName: 'Cache Build System'
|
||||
inputs:
|
||||
key: citron-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
|
||||
path: $(System.DefaultWorkingDirectory)/ccache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh ${{ parameters['version'] }}
|
||||
displayName: 'Build'
|
||||
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
|
||||
displayName: 'Package Artifacts'
|
||||
- publish: artifacts
|
||||
artifact: 'citron-$(BuildName)-$(BuildSuffix)'
|
||||
displayName: 'Upload Artifacts'
|
33
.ci/templates/build-standard.yml
Executable file
33
.ci/templates/build-standard.yml
Executable file
|
@ -0,0 +1,33 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
version: ''
|
||||
|
||||
jobs:
|
||||
- job: build
|
||||
displayName: 'standard'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
matrix:
|
||||
windows:
|
||||
BuildSuffix: 'windows-mingw'
|
||||
ScriptFolder: 'windows'
|
||||
clang:
|
||||
BuildSuffix: 'clang'
|
||||
ScriptFolder: 'clang'
|
||||
linux:
|
||||
BuildSuffix: 'linux'
|
||||
ScriptFolder: 'linux'
|
||||
steps:
|
||||
- template: ./sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: $(parameters.cache)
|
||||
version: $(parameters.version)
|
40
.ci/templates/build-testing.yml
Executable file
40
.ci/templates/build-testing.yml
Executable file
|
@ -0,0 +1,40 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
version: ''
|
||||
|
||||
jobs:
|
||||
- job: build_test
|
||||
displayName: 'testing'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
maxParallel: 5
|
||||
matrix:
|
||||
windows:
|
||||
BuildSuffix: 'windows-testing'
|
||||
ScriptFolder: 'windows'
|
||||
steps:
|
||||
- script: sudo apt-get update && sudo apt-get --only-upgrade -y install python3-pip && pip install requests urllib3
|
||||
displayName: 'Prepare Environment'
|
||||
- task: PythonScript@0
|
||||
condition: eq(variables['Build.Reason'], 'PullRequest')
|
||||
displayName: 'Determine Testing Status'
|
||||
inputs:
|
||||
scriptSource: 'filePath'
|
||||
scriptPath: '.ci/scripts/merge/check-label-presence.py'
|
||||
arguments: '$(System.PullRequest.PullRequestNumber) create-testing-build'
|
||||
- ${{ if eq(variables.enabletesting, 'true') }}:
|
||||
- template: ./sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./mergebot.yml
|
||||
parameters:
|
||||
matchLabel: 'testing-merge'
|
||||
- template: ./build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: 'false'
|
||||
version: $(parameters.version)
|
17
.ci/templates/format-check.yml
Executable file
17
.ci/templates/format-check.yml
Executable file
|
@ -0,0 +1,17 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
artifactSource: 'true'
|
||||
|
||||
steps:
|
||||
- template: ./sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'false'
|
||||
- task: DockerInstaller@0
|
||||
displayName: 'Prepare Environment'
|
||||
inputs:
|
||||
dockerVersion: '17.09.0-ce'
|
||||
- script: chmod a+x ./.ci/scripts/format/exec.sh && ./.ci/scripts/format/exec.sh
|
||||
displayName: 'Verify Formatting'
|
44
.ci/templates/merge-private.yml
Executable file
44
.ci/templates/merge-private.yml
Executable file
|
@ -0,0 +1,44 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
jobs:
|
||||
- job: merge
|
||||
displayName: 'pull requests'
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: recursive
|
||||
- template: ./mergebot-private.yml
|
||||
parameters:
|
||||
matchLabel: '$(BuildName)-merge'
|
||||
matchLabelPublic: '$(PublicBuildName)-merge'
|
||||
- task: ArchiveFiles@2
|
||||
displayName: 'Package Source'
|
||||
inputs:
|
||||
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
|
||||
includeRootFolder: false
|
||||
archiveType: '7z'
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/citron-$(BuildName)-source.7z'
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Upload Artifacts'
|
||||
inputs:
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)/citron-$(BuildName)-source.7z'
|
||||
artifact: 'citron-$(BuildName)-source'
|
||||
replaceExistingArchive: true
|
||||
- job: upload_source
|
||||
displayName: 'upload'
|
||||
dependsOn: merge
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- template: ./sync-source.yml
|
||||
parameters:
|
||||
artifactSource: 'true'
|
||||
needSubmodules: 'true'
|
||||
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh
|
||||
displayName: 'Apply Git Configuration'
|
||||
- script: git remote add other $(GitRepoPushChangesURL)
|
||||
displayName: 'Register Repository'
|
||||
- script: git push --force other HEAD:$(GitPushBranch)
|
||||
displayName: 'Update Code'
|
43
.ci/templates/merge.yml
Executable file
43
.ci/templates/merge.yml
Executable file
|
@ -0,0 +1,43 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
jobs:
|
||||
- job: merge
|
||||
displayName: 'pull requests'
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: recursive
|
||||
- template: ./mergebot.yml
|
||||
parameters:
|
||||
matchLabel: '$(BuildName)-merge'
|
||||
- task: ArchiveFiles@2
|
||||
displayName: 'Package Source'
|
||||
inputs:
|
||||
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
|
||||
includeRootFolder: false
|
||||
archiveType: '7z'
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/citron-$(BuildName)-source.7z'
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Upload Artifacts'
|
||||
inputs:
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)/citron-$(BuildName)-source.7z'
|
||||
artifact: 'citron-$(BuildName)-source'
|
||||
replaceExistingArchive: true
|
||||
- job: upload_source
|
||||
displayName: 'upload'
|
||||
dependsOn: merge
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- template: ./sync-source.yml
|
||||
parameters:
|
||||
artifactSource: 'true'
|
||||
needSubmodules: 'true'
|
||||
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh
|
||||
displayName: 'Apply Git Configuration'
|
||||
- script: git remote add other $(GitRepoPushChangesURL)
|
||||
displayName: 'Register Repository'
|
||||
- script: git push --force other HEAD:$(GitPushBranch)
|
||||
displayName: 'Update Code'
|
41
.ci/templates/mergebot-private.yml
Executable file
41
.ci/templates/mergebot-private.yml
Executable file
|
@ -0,0 +1,41 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
matchLabel: 'dummy-merge'
|
||||
matchLabelPublic: 'dummy-merge'
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
submodules: false
|
||||
- task: PythonScript@0
|
||||
displayName: 'Generate Merge Config'
|
||||
inputs:
|
||||
scriptSource: 'inline'
|
||||
script: '${{ parameters.createMergeConfigScript }}'
|
||||
- task: PythonScript@0
|
||||
displayName: 'Discover, Download, and Apply Patches (Stable)'
|
||||
inputs:
|
||||
scriptSource: 'filePath'
|
||||
scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
|
||||
arguments: $(publicLabel)
|
||||
workingDirectory: '$(Build.SourcesDirectory)'
|
||||
- task: PythonScript@0
|
||||
displayName: 'Discover, Download, and Apply Patches (Canary Public)'
|
||||
inputs:
|
||||
scriptSource: 'filePath'
|
||||
scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
|
||||
arguments: $(supporterPublicLabel)
|
||||
workingDirectory: '$(Build.SourcesDirectory)'
|
||||
- task: PythonScript@0
|
||||
displayName: 'Discover, Download, and Apply Patches (Canary Private)'
|
||||
inputs:
|
||||
scriptSource: 'filePath'
|
||||
scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
|
||||
arguments: $(supporterPrivateLabel)
|
||||
workingDirectory: '$(Build.SourcesDirectory)'
|
||||
- script: |
|
||||
echo '##vso[task.setvariable variable=DisplayPrefix]$(tagPrefix)'
|
||||
git rev-parse HEAD
|
||||
displayName: 'Set Version Number'
|
18
.ci/templates/mergebot.yml
Executable file
18
.ci/templates/mergebot.yml
Executable file
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
matchLabel: 'dummy-merge'
|
||||
|
||||
steps:
|
||||
- script: mkdir $(System.DefaultWorkingDirectory)/patches && pip install requests urllib3
|
||||
displayName: 'Prepare Environment'
|
||||
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh
|
||||
displayName: 'Apply Git Configuration'
|
||||
- task: PythonScript@0
|
||||
displayName: 'Discover, Download, and Apply Patches'
|
||||
inputs:
|
||||
scriptSource: 'filePath'
|
||||
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
|
||||
arguments: '${{ parameters.matchLabel }} Tagged patches'
|
||||
workingDirectory: '$(System.DefaultWorkingDirectory)'
|
22
.ci/templates/release-download.yml
Executable file
22
.ci/templates/release-download.yml
Executable file
|
@ -0,0 +1,22 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Windows Release'
|
||||
inputs:
|
||||
artifactName: 'citron-$(BuildName)-windows-msvc'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Linux Release'
|
||||
inputs:
|
||||
artifactName: 'citron-$(BuildName)-linux'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Android Release'
|
||||
inputs:
|
||||
artifactName: 'citron-$(BuildName)-android'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
18
.ci/templates/release-github.yml
Executable file
18
.ci/templates/release-github.yml
Executable file
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
steps:
|
||||
- template: ./release-download.yml
|
||||
- task: GitHubRelease@0
|
||||
displayName: 'GitHub Release'
|
||||
inputs:
|
||||
action: 'create'
|
||||
title: '$(ReleasePrefix) $(DisplayVersion)'
|
||||
assets: '$(Build.ArtifactStagingDirectory)/*'
|
||||
gitHubConnection: CI_RELEASE_TOKEN
|
||||
repositoryName: 'Citron-Project/Cit'
|
||||
target: '$(Build.SourceVersion)'
|
||||
tagSource: manual
|
||||
tag: $(BuildName)-$(DisplayPrefix)-$(DisplayVersion)
|
||||
addChangeLog: true
|
||||
isDraft: false
|
12
.ci/templates/release-private-tag.yml
Executable file
12
.ci/templates/release-private-tag.yml
Executable file
|
@ -0,0 +1,12 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
steps:
|
||||
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/citronbot-git-config.sh
|
||||
displayName: 'Apply Git Configuration'
|
||||
- script: git tag -a $(BuildName)-$(DisplayPrefix)-$(DisplayVersion) -m "citron $(BuildName) $(Build.BuildNumber) $(Build.DefinitionName) $(DisplayPrefix)-$(DisplayVersion)"
|
||||
displayName: 'Tag Source'
|
||||
- script: git remote add other $(GitRepoPushChangesURL)
|
||||
displayName: 'Register Repository'
|
||||
- script: git push other $(BuildName)-$(DisplayPrefix)-$(DisplayVersion)
|
||||
displayName: 'Update Code'
|
13
.ci/templates/release-universal.yml
Executable file
13
.ci/templates/release-universal.yml
Executable file
|
@ -0,0 +1,13 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
steps:
|
||||
- template: ./release-download.yml
|
||||
- task: UniversalPackages@0
|
||||
displayName: Publish Artifacts
|
||||
inputs:
|
||||
command: publish
|
||||
publishDirectory: '$(Build.ArtifactStagingDirectory)'
|
||||
vstsFeedPublish: 'citron-$(BuildName)'
|
||||
vstsFeedPackagePublish: 'main'
|
||||
packagePublishDescription: 'Citron Windows and Linux Executable Packages'
|
19
.ci/templates/retrieve-artifact-source.yml
Executable file
19
.ci/templates/retrieve-artifact-source.yml
Executable file
|
@ -0,0 +1,19 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Source'
|
||||
inputs:
|
||||
artifactName: 'citron-$(BuildName)-source'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
- script: rm -rf $(System.DefaultWorkingDirectory) && mkdir $(System.DefaultWorkingDirectory)
|
||||
displayName: 'Clean Working Directory'
|
||||
- task: ExtractFiles@1
|
||||
displayName: 'Prepare Source'
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/*.7z'
|
||||
destinationFolder: '$(System.DefaultWorkingDirectory)'
|
||||
cleanDestinationFolder: false
|
14
.ci/templates/retrieve-master-source.yml
Executable file
14
.ci/templates/retrieve-master-source.yml
Executable file
|
@ -0,0 +1,14 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
parameters:
|
||||
needSubmodules: 'true'
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
displayName: 'Checkout Recursive'
|
||||
submodules: recursive
|
||||
# condition: eq(parameters.needSubmodules, 'true')
|
||||
#- checkout: self
|
||||
# displayName: 'Checkout Fast'
|
||||
# condition: ne(parameters.needSubmodules, 'true')
|
10
.ci/templates/sync-source.yml
Executable file
10
.ci/templates/sync-source.yml
Executable file
|
@ -0,0 +1,10 @@
|
|||
# SPDX-FileCopyrightText: 2019 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
steps:
|
||||
- ${{ if eq(parameters.artifactSource, 'true') }}:
|
||||
- template: ./retrieve-artifact-source.yml
|
||||
- ${{ if ne(parameters.artifactSource, 'true') }}:
|
||||
- template: ./retrieve-master-source.yml
|
||||
parameters:
|
||||
needSubmodules: $(parameters.needSubmodules)
|
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Executable file
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Executable file
|
@ -0,0 +1,10 @@
|
|||
name: New Issue (Developers Only)
|
||||
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Issue"
|
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Executable file
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Executable file
|
@ -0,0 +1,64 @@
|
|||
name: Bug Report
|
||||
description: File a bug report
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Affected Build(s)
|
||||
description: List the affected build(s) that this issue applies to.
|
||||
placeholder: Mainline 1234 / Early Access 1234
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: issue-desc
|
||||
attributes:
|
||||
label: Description of Issue
|
||||
description: A brief description of the issue encountered along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A brief description of how it is expected to work along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Log File
|
||||
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://yuzu-emu.org/help/reference/log-files).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: system-config
|
||||
attributes:
|
||||
label: System Configuration
|
||||
placeholder: |
|
||||
CPU: Intel i5-10400 / AMD Ryzen 5 3600
|
||||
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
|
||||
RAM: 16GB DDR4-3200
|
||||
OS: Windows 11 22H2 (Build 22621.819)
|
||||
value: |
|
||||
CPU:
|
||||
GPU/Driver:
|
||||
RAM:
|
||||
OS:
|
||||
validations:
|
||||
required: true
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Executable file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Executable file
|
@ -0,0 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: yuzu Discord
|
||||
url: https://discord.com/invite/u77vRWY
|
||||
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
|
||||
- name: Community forums
|
||||
url: https://community.citra-emu.org
|
||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Executable file
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Executable file
|
@ -0,0 +1,28 @@
|
|||
name: Feature Request
|
||||
description: File a feature request
|
||||
labels: "request"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the feature you are requesting.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-feature
|
||||
attributes:
|
||||
label: What feature are you suggesting?
|
||||
description: A brief description of the requested feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: why-feature
|
||||
attributes:
|
||||
label: Why would this feature be useful?
|
||||
description: A brief description of why this feature would make yuzu better.
|
||||
validations:
|
||||
required: true
|
80
.github/workflows/android-build.yml
vendored
Executable file
80
.github/workflows/android-build.yml
vendored
Executable file
|
@ -0,0 +1,80 @@
|
|||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
name: 'yuzu-android-build'
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ "*" ]
|
||||
|
||||
jobs:
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'yuzu-emu/yuzu-android' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
~/.ccache
|
||||
key: ${{ runner.os }}-android-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-android-
|
||||
- name: Query tag name
|
||||
uses: olegtarasov/get-tag@v2.1.2
|
||||
id: tagName
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||
- name: Build
|
||||
run: ./.ci/scripts/android/build.sh
|
||||
env:
|
||||
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
|
||||
- name: Copy artifacts
|
||||
run: ./.ci/scripts/android/upload.sh
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: android
|
||||
path: artifacts/
|
||||
# release steps
|
||||
release-android:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [android]
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
- name: Query tag name
|
||||
uses: olegtarasov/get-tag@v2.1.2
|
||||
id: tagName
|
||||
- name: Create release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.tagName.outputs.tag }}
|
||||
release_name: ${{ steps.tagName.outputs.tag }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Upload artifacts
|
||||
uses: alexellis/upload-assets@0.2.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
asset_paths: '["./**/*.apk","./**/*.aab"]'
|
66
.github/workflows/android-canary-play-release.yml
vendored
Executable file
66
.github/workflows/android-canary-play-release.yml
vendored
Executable file
|
@ -0,0 +1,66 @@
|
|||
# SPDX-FileCopyrightText: 2024 Citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: citron-android-canary-play-release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-track:
|
||||
description: 'Play store release track (internal/alpha/beta/production)'
|
||||
required: true
|
||||
default: 'alpha'
|
||||
|
||||
jobs:
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'Citron-Project/Cit' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
name: Checkout
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
token: ${{ secrets.CI_RELEASE_TOKEN }}
|
||||
- run: npm install execa@5
|
||||
- uses: actions/github-script@v5
|
||||
name: 'Merge and publish Android Canary changes'
|
||||
env:
|
||||
CI_RELEASE_TOKEN: ${{ secrets.CI_RELEASE_TOKEN }}
|
||||
BUILD_CANARY: true
|
||||
with:
|
||||
script: |
|
||||
const execa = require("execa");
|
||||
const mergebot = require('./.github/workflows/android-merge.js').mergebot;
|
||||
process.chdir('${{ github.workspace }}');
|
||||
mergebot(github, context, execa);
|
||||
- name: Get tag name
|
||||
run: echo "GIT_TAG_NAME=$(cat tag-name.txt)" >> $GITHUB_ENV
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||
- name: Build
|
||||
run: ./.ci/scripts/android/canarybuild.sh
|
||||
env:
|
||||
CANARY_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
|
||||
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
|
||||
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
|
||||
CANARY_SERVICE_ACCOUNT_KEY_B64: ${{ secrets.CANARY_SERVICE_ACCOUNT_KEY_B64 }}
|
||||
STORE_TRACK: ${{ github.event.inputs.release-track }}
|
||||
AUTO_VERSIONED: true
|
||||
BUILD_CANARY: true
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ env.CANARY_TAG_NAME }}
|
||||
name: ${{ env.CANARY_TAG_NAME }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
repository: Citron-Project/citron-android
|
||||
token: ${{ secrets.CI_RELEASE_TOKEN }}
|
318
.github/workflows/android-merge.js
vendored
Executable file
318
.github/workflows/android-merge.js
vendored
Executable file
|
@ -0,0 +1,318 @@
|
|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Note: This is a GitHub Actions script
|
||||
// It is not meant to be executed directly on your machine without modifications
|
||||
|
||||
const fs = require("fs");
|
||||
// which label to check for changes
|
||||
const CHANGE_LABEL_MAINLINE = 'android-merge';
|
||||
const CHANGE_LABEL_EA = 'android-ea-merge';
|
||||
// how far back in time should we consider the changes are "recent"? (default: 24 hours)
|
||||
const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000);
|
||||
const BUILD_EA = process.env.BUILD_EA == 'true';
|
||||
const MAINLINE_TAG = process.env.MAINLINE_TAG;
|
||||
|
||||
async function checkBaseChanges(github) {
|
||||
// query the commit date of the latest commit on this branch
|
||||
const query = `query($owner:String!, $name:String!, $ref:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
ref(qualifiedName:$ref) {
|
||||
target {
|
||||
... on Commit { id pushedDate oid }
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: 'yuzu-emu',
|
||||
name: 'yuzu',
|
||||
ref: 'refs/heads/master',
|
||||
};
|
||||
const result = await github.graphql(query, variables);
|
||||
const pushedAt = result.repository.ref.target.pushedDate;
|
||||
console.log(`Last commit pushed at ${pushedAt}.`);
|
||||
const delta = new Date() - new Date(pushedAt);
|
||||
if (delta <= DETECTION_TIME_FRAME) {
|
||||
console.info('New changes detected, triggering a new build.');
|
||||
return true;
|
||||
}
|
||||
console.info('No new changes detected.');
|
||||
return false;
|
||||
}
|
||||
|
||||
async function checkAndroidChanges(github) {
|
||||
if (checkBaseChanges(github)) return true;
|
||||
const pulls = getPulls(github, false);
|
||||
for (let i = 0; i < pulls.length; i++) {
|
||||
let pull = pulls[i];
|
||||
if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) {
|
||||
console.info(`${pull.number} updated at ${pull.headRepository.pushedAt}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
console.info("No changes detected in any tagged pull requests.");
|
||||
return false;
|
||||
}
|
||||
|
||||
async function tagAndPush(github, owner, repo, execa, commit=false) {
|
||||
let altToken = process.env.ALT_GITHUB_TOKEN;
|
||||
if (!altToken) {
|
||||
throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`;
|
||||
}
|
||||
const query = `query ($owner:String!, $name:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) {
|
||||
nodes { name }
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: owner,
|
||||
name: repo,
|
||||
};
|
||||
const tags = await github.graphql(query, variables);
|
||||
const tagList = tags.repository.refs.nodes;
|
||||
let lastTag = 'android-1';
|
||||
for (let i = 0; i < tagList.length; ++i) {
|
||||
if (tagList[i].name.includes('android-')) {
|
||||
lastTag = tagList[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
|
||||
const channel = repo.split('-')[1];
|
||||
const newTag = `${channel}-${tagNumber + 1}`;
|
||||
console.log(`New tag: ${newTag}`);
|
||||
if (commit) {
|
||||
let channelName = channel[0].toUpperCase() + channel.slice(1);
|
||||
console.info(`Committing pending commit as ${channelName} ${tagNumber + 1}`);
|
||||
await execa("git", ['commit', '-m', `${channelName} ${tagNumber + 1}`]);
|
||||
}
|
||||
console.info('Pushing tags to GitHub ...');
|
||||
await execa("git", ['tag', newTag]);
|
||||
await execa("git", ['remote', 'add', 'target', `https://${altToken}@github.com/${owner}/${repo}.git`]);
|
||||
await execa("git", ['push', 'target', 'master', '-f']);
|
||||
await execa("git", ['push', 'target', 'master', '--tags']);
|
||||
console.info('Successfully pushed new changes.');
|
||||
}
|
||||
|
||||
async function tagAndPushEA(github, owner, repo, execa) {
|
||||
let altToken = process.env.ALT_GITHUB_TOKEN;
|
||||
if (!altToken) {
|
||||
throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`;
|
||||
}
|
||||
const query = `query ($owner:String!, $name:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) {
|
||||
nodes { name }
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: owner,
|
||||
name: repo,
|
||||
};
|
||||
const tags = await github.graphql(query, variables);
|
||||
const tagList = tags.repository.refs.nodes;
|
||||
let lastTag = 'ea-1';
|
||||
for (let i = 0; i < tagList.length; ++i) {
|
||||
if (tagList[i].name.includes('ea-')) {
|
||||
lastTag = tagList[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
|
||||
const newTag = `ea-${tagNumber + 1}`;
|
||||
console.log(`New tag: ${newTag}`);
|
||||
console.info('Pushing tags to GitHub ...');
|
||||
await execa("git", ["remote", "add", "android", "https://github.com/yuzu-emu/yuzu-android.git"]);
|
||||
await execa("git", ["fetch", "android"]);
|
||||
|
||||
await execa("git", ['tag', newTag]);
|
||||
await execa("git", ['push', 'android', `${newTag}`]);
|
||||
|
||||
fs.writeFile('tag-name.txt', newTag, (err) => {
|
||||
if (err) throw 'Could not write tag name to file!'
|
||||
})
|
||||
|
||||
console.info('Successfully pushed new changes.');
|
||||
}
|
||||
|
||||
async function generateReadme(pulls, context, mergeResults, execa) {
|
||||
let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`;
|
||||
let output =
|
||||
"| Pull Request | Commit | Title | Author | Merged? |\n|----|----|----|----|----|\n";
|
||||
for (let pull of pulls) {
|
||||
let pr = pull.number;
|
||||
let result = mergeResults[pr];
|
||||
output += `| [${pr}](${baseUrl}/pull/${pr}) | [\`${result.rev || "N/A"}\`](${baseUrl}/pull/${pr}/files) | ${pull.title} | [${pull.author.login}](https://github.com/${pull.author.login}/) | ${result.success ? "Yes" : "No"} |\n`;
|
||||
}
|
||||
output +=
|
||||
"\n\nEnd of merge log. You can find the original README.md below the break.\n\n-----\n\n";
|
||||
output += fs.readFileSync("./README.md");
|
||||
fs.writeFileSync("./README.md", output);
|
||||
await execa("git", ["add", "README.md"]);
|
||||
}
|
||||
|
||||
async function fetchPullRequests(pulls, repoUrl, execa) {
|
||||
console.log("::group::Fetch pull requests");
|
||||
for (let pull of pulls) {
|
||||
let pr = pull.number;
|
||||
console.info(`Fetching PR ${pr} ...`);
|
||||
await execa("git", [
|
||||
"fetch",
|
||||
"-f",
|
||||
"--no-recurse-submodules",
|
||||
repoUrl,
|
||||
`pull/${pr}/head:pr-${pr}`,
|
||||
]);
|
||||
}
|
||||
console.log("::endgroup::");
|
||||
}
|
||||
|
||||
async function mergePullRequests(pulls, execa) {
|
||||
let mergeResults = {};
|
||||
console.log("::group::Merge pull requests");
|
||||
await execa("git", ["config", "--global", "user.name", "yuzubot"]);
|
||||
await execa("git", [
|
||||
"config",
|
||||
"--global",
|
||||
"user.email",
|
||||
"yuzu\x40yuzu-emu\x2eorg", // prevent email harvesters from scraping the address
|
||||
]);
|
||||
let hasFailed = false;
|
||||
for (let pull of pulls) {
|
||||
let pr = pull.number;
|
||||
console.info(`Merging PR ${pr} ...`);
|
||||
try {
|
||||
const process1 = execa("git", [
|
||||
"merge",
|
||||
"--squash",
|
||||
"--no-edit",
|
||||
`pr-${pr}`,
|
||||
]);
|
||||
process1.stdout.pipe(process.stdout);
|
||||
await process1;
|
||||
|
||||
const process2 = execa("git", ["commit", "-m", `Merge yuzu-emu#${pr}`]);
|
||||
process2.stdout.pipe(process.stdout);
|
||||
await process2;
|
||||
|
||||
const process3 = await execa("git", ["rev-parse", "--short", `pr-${pr}`]);
|
||||
mergeResults[pr] = {
|
||||
success: true,
|
||||
rev: process3.stdout,
|
||||
};
|
||||
} catch (err) {
|
||||
console.log(
|
||||
`::error title=#${pr} not merged::Failed to merge pull request: ${pr}: ${err}`
|
||||
);
|
||||
mergeResults[pr] = { success: false };
|
||||
hasFailed = true;
|
||||
await execa("git", ["reset", "--hard"]);
|
||||
}
|
||||
}
|
||||
console.log("::endgroup::");
|
||||
if (hasFailed) {
|
||||
throw 'There are merge failures. Aborting!';
|
||||
}
|
||||
return mergeResults;
|
||||
}
|
||||
|
||||
async function resetBranch(execa) {
|
||||
console.log("::group::Reset master branch");
|
||||
let hasFailed = false;
|
||||
try {
|
||||
await execa("git", ["remote", "add", "source", "https://github.com/yuzu-emu/yuzu.git"]);
|
||||
await execa("git", ["fetch", "source"]);
|
||||
const process1 = await execa("git", ["rev-parse", "source/master"]);
|
||||
const headCommit = process1.stdout;
|
||||
|
||||
await execa("git", ["reset", "--hard", headCommit]);
|
||||
} catch (err) {
|
||||
console.log(`::error title=Failed to reset master branch`);
|
||||
hasFailed = true;
|
||||
}
|
||||
console.log("::endgroup::");
|
||||
if (hasFailed) {
|
||||
throw 'Failed to reset the master branch. Aborting!';
|
||||
}
|
||||
}
|
||||
|
||||
async function getPulls(github) {
|
||||
const query = `query ($owner:String!, $name:String!, $label:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
pullRequests(labels: [$label], states: OPEN, first: 100) {
|
||||
nodes {
|
||||
number title author { login }
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const mainlineVariables = {
|
||||
owner: 'yuzu-emu',
|
||||
name: 'yuzu',
|
||||
label: CHANGE_LABEL_MAINLINE,
|
||||
};
|
||||
const mainlineResult = await github.graphql(query, mainlineVariables);
|
||||
const pulls = mainlineResult.repository.pullRequests.nodes;
|
||||
if (BUILD_EA) {
|
||||
const eaVariables = {
|
||||
owner: 'yuzu-emu',
|
||||
name: 'yuzu',
|
||||
label: CHANGE_LABEL_EA,
|
||||
};
|
||||
const eaResult = await github.graphql(query, eaVariables);
|
||||
const eaPulls = eaResult.repository.pullRequests.nodes;
|
||||
return pulls.concat(eaPulls);
|
||||
}
|
||||
return pulls;
|
||||
}
|
||||
|
||||
async function getMainlineTag(execa) {
|
||||
console.log(`::group::Getting mainline tag android-${MAINLINE_TAG}`);
|
||||
let hasFailed = false;
|
||||
try {
|
||||
await execa("git", ["remote", "add", "mainline", "https://github.com/yuzu-emu/yuzu-android.git"]);
|
||||
await execa("git", ["fetch", "mainline", "--tags"]);
|
||||
await execa("git", ["checkout", `tags/android-${MAINLINE_TAG}`]);
|
||||
await execa("git", ["submodule", "update", "--init", "--recursive"]);
|
||||
} catch (err) {
|
||||
console.log('::error title=Failed pull tag');
|
||||
hasFailed = true;
|
||||
}
|
||||
console.log("::endgroup::");
|
||||
if (hasFailed) {
|
||||
throw 'Failed pull mainline tag. Aborting!';
|
||||
}
|
||||
}
|
||||
|
||||
async function mergebot(github, context, execa) {
|
||||
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
|
||||
await resetBranch(execa);
|
||||
|
||||
const pulls = await getPulls(github);
|
||||
let displayList = [];
|
||||
for (let i = 0; i < pulls.length; i++) {
|
||||
let pull = pulls[i];
|
||||
displayList.push({ PR: pull.number, Title: pull.title });
|
||||
}
|
||||
console.info("The following pull requests will be merged:");
|
||||
console.table(displayList);
|
||||
await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
|
||||
const mergeResults = await mergePullRequests(pulls, execa);
|
||||
|
||||
if (BUILD_EA) {
|
||||
await tagAndPushEA(github, 'yuzu-emu', `yuzu-android`, execa);
|
||||
} else {
|
||||
await generateReadme(pulls, context, mergeResults, execa);
|
||||
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.mergebot = mergebot;
|
||||
module.exports.checkAndroidChanges = checkAndroidChanges;
|
||||
module.exports.tagAndPush = tagAndPush;
|
||||
module.exports.checkBaseChanges = checkBaseChanges;
|
||||
module.exports.getMainlineTag = getMainlineTag;
|
57
.github/workflows/android-publish.yml
vendored
Executable file
57
.github/workflows/android-publish.yml
vendored
Executable file
|
@ -0,0 +1,57 @@
|
|||
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: yuzu-android-publish
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '37 0 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
android:
|
||||
description: 'Whether to trigger an Android build (true/false/auto)'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }}
|
||||
steps:
|
||||
# this checkout is required to make sure the GitHub Actions scripts are available
|
||||
- uses: actions/checkout@v3
|
||||
name: Pre-checkout
|
||||
with:
|
||||
submodules: false
|
||||
- uses: actions/github-script@v6
|
||||
id: check-changes
|
||||
name: 'Check for new changes'
|
||||
env:
|
||||
# 24 hours
|
||||
DETECTION_TIME_FRAME: 86400000
|
||||
with:
|
||||
script: |
|
||||
if (context.payload.inputs && context.payload.inputs.android === 'true') return true;
|
||||
const checkAndroidChanges = require('./.github/workflows/android-merge.js').checkAndroidChanges;
|
||||
return checkAndroidChanges(github);
|
||||
- run: npm install execa@5
|
||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||
- uses: actions/checkout@v3
|
||||
name: Checkout
|
||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||
with:
|
||||
path: 'yuzu-merge'
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
token: ${{ secrets.ALT_GITHUB_TOKEN }}
|
||||
- uses: actions/github-script@v5
|
||||
name: 'Check and merge Android changes'
|
||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||
env:
|
||||
ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }}
|
||||
with:
|
||||
script: |
|
||||
const execa = require("execa");
|
||||
const mergebot = require('./.github/workflows/android-merge.js').mergebot;
|
||||
process.chdir('${{ github.workspace }}/yuzu-merge');
|
||||
mergebot(github, context, execa);
|
59
.github/workflows/android-stable-play-release.yml
vendored
Executable file
59
.github/workflows/android-stable-play-release.yml
vendored
Executable file
|
@ -0,0 +1,59 @@
|
|||
# SPDX-FileCopyrightText: 2024 Citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: citron-android-stable-play-release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-tag:
|
||||
description: 'Tag # from citron-android that you want to build and publish'
|
||||
required: true
|
||||
default: '100'
|
||||
release-track:
|
||||
description: 'Play store release track (internal/alpha/beta/production)'
|
||||
required: true
|
||||
default: 'alpha'
|
||||
|
||||
jobs:
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'Citron-Project/Cit' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
name: Checkout
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
token: ${{ secrets.CI_RELEASE_TOKEN }}
|
||||
- run: npm install execa@5
|
||||
- uses: actions/github-script@v5
|
||||
name: 'Pull stable tag'
|
||||
env:
|
||||
STABLE_TAG: ${{ github.event.inputs.release-tag }}
|
||||
with:
|
||||
script: |
|
||||
const execa = require("execa");
|
||||
const mergebot = require('./.github/workflows/android-merge.js').getStableTag;
|
||||
process.chdir('${{ github.workspace }}');
|
||||
mergebot(execa);
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||
- name: Build
|
||||
run: |
|
||||
echo "GIT_TAG_NAME=android-${{ github.event.inputs.releast-tag }}" >> $GITHUB_ENV
|
||||
./.ci/scripts/android/stablebuild.sh
|
||||
env:
|
||||
STABLE_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
|
||||
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
|
||||
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
|
||||
STABLE_SERVICE_ACCOUNT_KEY_B64: ${{ secrets.STABLE_SERVICE_ACCOUNT_KEY_B64 }}
|
||||
STORE_TRACK: ${{ github.event.inputs.release-track }}
|
||||
AUTO_VERSIONED: true
|
33
.github/workflows/ci.yml
vendored
Executable file
33
.github/workflows/ci.yml
vendored
Executable file
|
@ -0,0 +1,33 @@
|
|||
# SPDX-FileCopyrightText: 2024 Citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: citron-ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
tags: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
transifex:
|
||||
runs-on: ubuntu-latest
|
||||
container: yuzu-emu-mirror/build-environments:linux-transifex
|
||||
if: ${{ github.repository == 'Citron-Project/Cit' && !github.head_ref }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Update Translation
|
||||
run: ./.ci/scripts/transifex/docker.sh
|
||||
env:
|
||||
TX_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }}
|
||||
|
||||
reuse:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'Citron-Project/Cit' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: fsfe/reuse-action@v1
|
17
.github/workflows/codespell.yml
vendored
Executable file
17
.github/workflows/codespell.yml
vendored
Executable file
|
@ -0,0 +1,17 @@
|
|||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# GitHub Action to automate the identification of common misspellings in text files.
|
||||
# https://github.com/codespell-project/actions-codespell
|
||||
# https://github.com/codespell-project/codespell
|
||||
name: codespell
|
||||
on: pull_request
|
||||
permissions: {}
|
||||
jobs:
|
||||
codespell:
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: codespell-project/actions-codespell@master
|
190
.github/workflows/verify.yml
vendored
Executable file
190
.github/workflows/verify.yml
vendored
Executable file
|
@ -0,0 +1,190 @@
|
|||
# SPDX-FileCopyrightText: 2024 Citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: 'citron verify'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
env:
|
||||
PR_NUMBER: pr${{ github.event.number }}
|
||||
|
||||
jobs:
|
||||
format:
|
||||
name: 'verify format'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: false
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: 'Verify Formatting'
|
||||
run: bash -ex ./.ci/scripts/format/script.sh
|
||||
build:
|
||||
name: 'test build'
|
||||
needs: format
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- type: clang
|
||||
image: linux-fresh
|
||||
- type: linux
|
||||
image: linux-fresh
|
||||
- type: windows
|
||||
image: linux-mingw
|
||||
container:
|
||||
image: yuzu-emu-mirror/build-environments:${{ matrix.image }}
|
||||
options: -u 1001
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
id: ccache-restore
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ runner.os }}-${{ matrix.type }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.type }}-
|
||||
- name: Create ccache directory
|
||||
if: steps.ccache-restore.outputs.cache-hit != 'true'
|
||||
run: mkdir -p ~/.ccache
|
||||
- name: Build
|
||||
run: ./.ci/scripts/${{ matrix.type }}/docker.sh
|
||||
env:
|
||||
ENABLE_COMPATIBILITY_REPORTING: "ON"
|
||||
- name: Pack
|
||||
run: ./.ci/scripts/${{ matrix.type }}/upload.sh
|
||||
env:
|
||||
NO_SOURCE_PACK: "YES"
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.type }}
|
||||
path: artifacts/
|
||||
build-mac:
|
||||
name: 'test build (macos)'
|
||||
needs: format
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install autoconf automake boost ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
export Qt5_DIR="$(brew --prefix qt@5)/lib/cmake"
|
||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCITRON_USE_BUNDLED_VCPKG=OFF -DCITRON_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF
|
||||
ninja
|
||||
build-msvc:
|
||||
name: 'test build (windows, msvc)'
|
||||
needs: format
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.buildcache
|
||||
key: ${{ runner.os }}-msvc-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-msvc-
|
||||
- name: Install dependencies
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
$BuildCacheVer = "v0.28.4"
|
||||
$File = "buildcache-windows.zip"
|
||||
$Uri = "https://github.com/mbitsnbites/buildcache/releases/download/$BuildCacheVer/$File"
|
||||
$WebClient = New-Object System.Net.WebClient
|
||||
$WebClient.DownloadFile($Uri, $File)
|
||||
7z x $File
|
||||
$CurrentDir = Convert-Path .
|
||||
echo "$CurrentDir/buildcache/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: Install Vulkan SDK
|
||||
shell: pwsh
|
||||
run: .\.ci\scripts\windows\install-vulkan-sdk.ps1
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Configure
|
||||
env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
run: |
|
||||
glslangValidator --version
|
||||
mkdir build
|
||||
cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DCITRON_USE_BUNDLED_QT=1 -DCITRON_USE_BUNDLED_SDL2=1 -DCITRON_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCITRON_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DGIT_BRANCH=pr-verify -DCITRON_CRASH_DUMPS=ON
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
- name: Cache Summary
|
||||
run: buildcache -s
|
||||
- name: Pack
|
||||
shell: pwsh
|
||||
run: .\.ci\scripts\windows\upload.ps1
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: msvc
|
||||
path: artifacts/
|
||||
- name: Upload EXE
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.INDIVIDUAL_EXE }}
|
||||
path: ${{ env.INDIVIDUAL_EXE }}
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
needs: format
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
~/.ccache
|
||||
key: ${{ runner.os }}-android-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-android-
|
||||
- name: Query tag name
|
||||
uses: olegtarasov/get-tag@v2.1.2
|
||||
id: tagName
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||
- name: Build
|
||||
run: ./.ci/scripts/android/build.sh
|
||||
env:
|
||||
USE_DEBUG_KEYSTORE: "true"
|
||||
- name: Copy artifacts
|
||||
run: ./.ci/scripts/android/upload.sh
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: android
|
||||
path: artifacts/
|
12
.gitmodules
vendored
12
.gitmodules
vendored
|
@ -9,22 +9,22 @@
|
|||
url = https://github.com/mozilla/cubeb.git
|
||||
[submodule "dynarmic"]
|
||||
path = externals/dynarmic
|
||||
url = https://git.citron-emu.org/Citron/dynarmic.git
|
||||
url = https://github.com/yuzu-mirror/dynarmic.git
|
||||
[submodule "libusb"]
|
||||
path = externals/libusb/libusb
|
||||
url = https://github.com/libusb/libusb.git
|
||||
[submodule "discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://git.citron-emu.org/Citron/discord-rpc.git
|
||||
url = https://github.com/yuzu-mirror/discord-rpc.git
|
||||
[submodule "Vulkan-Headers"]
|
||||
path = externals/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||
[submodule "sirit"]
|
||||
path = externals/sirit
|
||||
url = https://git.citron-emu.org/Citron/sirit.git
|
||||
url = https://github.com/yuzu-mirror/sirit.git
|
||||
[submodule "mbedtls"]
|
||||
path = externals/mbedtls
|
||||
url = https://git.citron-emu.org/Citron/mbedtls.git
|
||||
url = https://github.com/yuzu-mirror/mbedtls.git
|
||||
[submodule "xbyak"]
|
||||
path = externals/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
|
@ -57,13 +57,13 @@
|
|||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||
[submodule "breakpad"]
|
||||
path = externals/breakpad
|
||||
url = https://git.citron-emu.org/Citron/breakpad.git
|
||||
url = https://github.com/yuzu-mirror/breakpad.git
|
||||
[submodule "simpleini"]
|
||||
path = externals/simpleini
|
||||
url = https://github.com/brofield/simpleini.git
|
||||
[submodule "oaknut"]
|
||||
path = externals/oaknut
|
||||
url = https://git.citron-emu.org/Citron/oaknut.git
|
||||
url = https://github.com/yuzu-mirror/oaknut
|
||||
[submodule "Vulkan-Utility-Libraries"]
|
||||
path = externals/Vulkan-Utility-Libraries
|
||||
url = https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git
|
||||
|
|
|
@ -137,7 +137,7 @@ endif()
|
|||
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
|
||||
|
||||
if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL)
|
||||
set(vvl_version "1.4.304.1")
|
||||
set(vvl_version "1.4.309.0")
|
||||
set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip")
|
||||
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/")
|
||||
set(vvl_final_lib "${vvl_lib_path}/libVkLayer_khronos_validation.so")
|
||||
|
@ -169,13 +169,23 @@ if (CITRON_USE_BUNDLED_VCPKG)
|
|||
|
||||
if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
|
||||
set(VCPKG_TARGET_TRIPLET "arm64-android")
|
||||
# Detect host system (Windows or Linux)
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(VCPKG_HOST_TRIPLET "x64-windows")
|
||||
else()
|
||||
set(VCPKG_HOST_TRIPLET "x64-linux")
|
||||
endif()
|
||||
# this is to avoid CMake using the host pkg-config to find the host
|
||||
# libraries when building for Android targets
|
||||
set(PKG_CONFIG_EXECUTABLE "aarch64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
|
||||
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
|
||||
set(VCPKG_TARGET_TRIPLET "x64-android")
|
||||
# Detect host system (Windows or Linux)
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(VCPKG_HOST_TRIPLET "x64-windows")
|
||||
else()
|
||||
set(VCPKG_HOST_TRIPLET "x64-linux")
|
||||
endif()
|
||||
set(PKG_CONFIG_EXECUTABLE "x86_64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported Android architecture ${CMAKE_ANDROID_ARCH_ABI}")
|
||||
|
@ -359,7 +369,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
|||
# Enforce the search mode of non-required packages for better and shorter failure messages
|
||||
find_package(Boost REQUIRED context)
|
||||
find_package(enet MODULE)
|
||||
find_package(fmt 11 REQUIRED)
|
||||
find_package(fmt 10 REQUIRED)
|
||||
if (CITRON_USE_LLVM_DEMANGLE)
|
||||
find_package(LLVM MODULE COMPONENTS Demangle)
|
||||
endif()
|
||||
|
@ -374,7 +384,7 @@ find_package(ZLIB REQUIRED)
|
|||
find_package(zstd REQUIRED)
|
||||
|
||||
if (NOT CITRON_USE_EXTERNAL_VULKAN_HEADERS)
|
||||
find_package(VulkanHeaders 1.4.307 REQUIRED)
|
||||
find_package(VulkanHeaders 1.4.313 REQUIRED)
|
||||
endif()
|
||||
|
||||
if (NOT CITRON_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
|
||||
|
@ -437,7 +447,7 @@ if (ENABLE_SDL2)
|
|||
if (CITRON_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1920) AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.32.0")
|
||||
set(SDL2_VER "SDL2-2.28.2")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRON_USE_BUNDLED_SDL2 and provide your own.")
|
||||
endif()
|
||||
|
@ -584,7 +594,7 @@ endif()
|
|||
# against all the src files. This should be used before making a pull request.
|
||||
# =======================================================================
|
||||
|
||||
set(CLANG_FORMAT_POSTFIX "-18")
|
||||
set(CLANG_FORMAT_POSTFIX "-15")
|
||||
find_program(CLANG_FORMAT
|
||||
NAMES clang-format${CLANG_FORMAT_POSTFIX}
|
||||
clang-format
|
||||
|
@ -595,7 +605,7 @@ if (NOT CLANG_FORMAT)
|
|||
message(STATUS "Clang format not found! Downloading...")
|
||||
set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
file(DOWNLOAD
|
||||
https://git.citron-emu.org/Citron/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
|
||||
https://github.com/yuzu-mirror/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
|
||||
"${CLANG_FORMAT}" SHOW_PROGRESS
|
||||
STATUS DOWNLOAD_SUCCESS)
|
||||
if (NOT DOWNLOAD_SUCCESS EQUAL 0)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
set(CURRENT_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
function(download_bundled_external remote_path lib_name prefix_var)
|
||||
|
||||
set(package_base_url "https://git.citron-emu.org/Citron/")
|
||||
set(package_base_url "https://github.com/yuzu-mirror/")
|
||||
set(package_repo "no_platform")
|
||||
set(package_extension "no_platform")
|
||||
if (WIN32)
|
||||
|
|
|
@ -14,13 +14,14 @@ Welcome to **Citron**, a cutting-edge Nintendo Homebrew emulator designed to del
|
|||
|
||||
## Download 🚀
|
||||
|
||||
Ready to experience Citron? [Download the latest version](https://git.citron-emu.org/Citron/Citron/releases) and dive into your favorite Nintendo homebrew titles!
|
||||
Ready to experience Citron? [Download the latest version](https://git.citron-emu.org/citron/emu/-/releases) and dive into your favorite Nintendo homebrew titles!
|
||||
|
||||
## Getting Started 💡
|
||||
|
||||
1. **Download and Install**: Head over to the [downloads page](https://git.citron-emu.org/Citron/Citron/releases) to grab the latest release.
|
||||
2. **Add Homebrew Games**: Citron is built to play homebrew games. Add them to your game directory and enjoy!
|
||||
3. **Configure Your Settings**: Customize your emulator settings to suit your performance needs.
|
||||
1. **Check the System Requirements** Head over to this link to check the requirements to run this emulator [system requirements page](https://evilperson1337.notion.site/Hardware-Requirements-1d957c2edaf680418e2bf2c7239170ce)
|
||||
2. **Download and Install**: Head over to the [downloads page](https://git.citron-emu.org/citron/emu/-/releases) to grab the latest release.
|
||||
3. **Add Homebrew Games**: Citron is built to play homebrew games. Add them to your game directory and enjoy!
|
||||
4. **Configure Your Settings**: Customize your emulator settings to suit your performance needs.
|
||||
|
||||
## Source Code 🔧
|
||||
|
||||
|
|
7
dist/72-citron-input.rules
vendored
7
dist/72-citron-input.rules
vendored
|
@ -1,4 +1,5 @@
|
|||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Allow systemd-logind to manage user access to hidraw with this file
|
||||
|
@ -7,13 +8,13 @@
|
|||
|
||||
# Switch Pro Controller (USB/Bluetooth)
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="2009", MODE="0660", TAG+="uaccess"
|
||||
KERNEL=="hidraw*", KERNELS=="*057e:2009*", MODE="0660", TAG+="uaccess"
|
||||
KERNEL=="hidraw*", KERNELS=="*057E:2009*", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Joy-Con L (Bluetooth)
|
||||
KERNEL=="hidraw*", KERNELS=="*057e:2006*", MODE="0660", TAG+="uaccess"
|
||||
KERNEL=="hidraw*", KERNELS=="*057E:2006*", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Joy-Con R (Bluetooth)
|
||||
KERNEL=="hidraw*", KERNELS=="*057e:2007*", MODE="0660", TAG+="uaccess"
|
||||
KERNEL=="hidraw*", KERNELS=="*057E:2007*", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Joy-Con Charging Grip (USB)
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="200e", MODE="0660", TAG+="uaccess"
|
||||
|
|
3
dist/org.citron_emu.citron.desktop
vendored
3
dist/org.citron_emu.citron.desktop
vendored
|
@ -1,4 +1,5 @@
|
|||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
[Desktop Entry]
|
||||
|
@ -13,4 +14,4 @@ Exec=citron %f
|
|||
Categories=Game;Emulator;Qt;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||
Keywords=Nintendo;Switch;
|
||||
StartupWMClass=citron
|
||||
StartupWMClass=org.citron_emu.citron
|
||||
|
|
2
externals/SDL
vendored
2
externals/SDL
vendored
|
@ -1 +1 @@
|
|||
Subproject commit cc016b0046d563287f0aa9f09b958b5e70d43696
|
||||
Subproject commit 2359383fc187386204c3bb22de89655a494cd128
|
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
|
@ -1 +1 @@
|
|||
Subproject commit cacef3039d277c448c89336290ec3937270b0996
|
||||
Subproject commit e2e53a724677f6eba8ff0ce1ccb64ee321785cbd
|
2
externals/Vulkan-Utility-Libraries
vendored
2
externals/Vulkan-Utility-Libraries
vendored
|
@ -1 +1 @@
|
|||
Subproject commit bc3a4d9fd9b46729651a3cec4f5226f6272b8684
|
||||
Subproject commit 4e246c56ec5afb5ad66b9b04374d39ac04675c8e
|
2
externals/VulkanMemoryAllocator
vendored
2
externals/VulkanMemoryAllocator
vendored
|
@ -1 +1 @@
|
|||
Subproject commit c788c52156f3ef7bc7ab769cb03c110a53ac8fcb
|
||||
Subproject commit 539c0a8d8e3733c9f25ea9a184c85c77504f1653
|
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
|
@ -1 +1 @@
|
|||
Subproject commit e40d24cb149dd138e7c11d490834fa2c81298b32
|
||||
Subproject commit 5a874b05fb981e2643fd0c1c9ad63e19dfe76b27
|
|
@ -55,7 +55,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
// TODO If this is ever modified, change application_id in strings.xml
|
||||
applicationId = "com.antutu.ABenchMark"
|
||||
applicationId = "org.citron.citron_emu"
|
||||
minSdk = 30
|
||||
//noinspection EditedTargetSdkVersion
|
||||
targetSdk = 35
|
||||
|
@ -76,9 +76,19 @@ android {
|
|||
buildConfigField("String", "BRANCH", "\"${getBranch()}\"")
|
||||
}
|
||||
|
||||
// Always use debug keystore for CI builds when USE_DEBUG_KEYSTORE is set
|
||||
val useDebugKeystore = System.getenv("USE_DEBUG_KEYSTORE") == "true"
|
||||
val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
|
||||
|
||||
signingConfigs {
|
||||
if (keystoreFile != null) {
|
||||
create("default") {
|
||||
storeFile = file("$projectDir/debug.keystore")
|
||||
storePassword = "android"
|
||||
keyAlias = "androiddebugkey"
|
||||
keyPassword = "android"
|
||||
}
|
||||
|
||||
if (keystoreFile != null && !useDebugKeystore) {
|
||||
create("release") {
|
||||
storeFile = file(keystoreFile)
|
||||
storePassword = System.getenv("ANDROID_KEYSTORE_PASS")
|
||||
|
@ -86,12 +96,6 @@ android {
|
|||
keyPassword = System.getenv("ANDROID_KEYSTORE_PASS")
|
||||
}
|
||||
}
|
||||
create("default") {
|
||||
storeFile = file("$projectDir/debug.keystore")
|
||||
storePassword = "android"
|
||||
keyAlias = "androiddebugkey"
|
||||
keyPassword = "android"
|
||||
}
|
||||
}
|
||||
|
||||
// Define build types, which are orthogonal to product flavors.
|
||||
|
@ -99,7 +103,7 @@ android {
|
|||
|
||||
// Signed by release key, allowing for upload to Play Store.
|
||||
release {
|
||||
signingConfig = if (keystoreFile != null) {
|
||||
signingConfig = if (keystoreFile != null && !useDebugKeystore) {
|
||||
signingConfigs.getByName("release")
|
||||
} else {
|
||||
signingConfigs.getByName("default")
|
||||
|
@ -161,7 +165,7 @@ android {
|
|||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = "3.31.6"
|
||||
version = "4.0.1"
|
||||
path = file("../../../CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +183,8 @@ android {
|
|||
"-DCITRON_USE_BUNDLED_FFMPEG=ON",
|
||||
"-DCITRON_ENABLE_LTO=ON",
|
||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
|
||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
|
||||
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
|
||||
)
|
||||
|
||||
abiFilters("arm64-v8a") // , "x86_64")
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<!-- SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.gamepad" android:required="false" />
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
|
||||
|
||||
<!-- Required Permissions for the app -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<!-- Permissions for NFC (only if needed) -->
|
||||
<uses-permission android:name="android.permission.NFC" android:required="false" />
|
||||
|
||||
<application
|
||||
android:name="org.citron.citron_emu.CitronApplication"
|
||||
android:label="@string/app_name_suffixed"
|
||||
|
@ -31,28 +31,37 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
<meta-data android:name="android.game_mode_config"
|
||||
<!-- Samsung Gaming Hub specific metadata -->
|
||||
<meta-data
|
||||
android:name="com.samsung.android.gamehub"
|
||||
android:value="true" />
|
||||
|
||||
<!-- Game Mode configuration -->
|
||||
<meta-data
|
||||
android:name="android.game_mode_config"
|
||||
android:resource="@xml/game_mode_config" />
|
||||
|
||||
<!-- Main Activity -->
|
||||
<activity
|
||||
android:name="org.citron.citron_emu.ui.main.MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Citron.Splash.Main">
|
||||
|
||||
<!-- This intentfilter marks this Activity as the one that gets launched from Home screen. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.GAME" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Settings Activity -->
|
||||
<activity
|
||||
android:name="org.citron.citron_emu.features.settings.ui.SettingsActivity"
|
||||
android:theme="@style/Theme.Citron.Main"
|
||||
android:label="@string/preferences_settings"/>
|
||||
android:label="@string/preferences_settings" />
|
||||
|
||||
<!-- Emulation Activity -->
|
||||
<activity
|
||||
android:name="org.citron.citron_emu.activities.EmulationActivity"
|
||||
android:theme="@style/Theme.Citron.Main"
|
||||
|
@ -72,7 +81,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data
|
||||
android:mimeType="application/octet-stream"
|
||||
android:scheme="content"/>
|
||||
android:scheme="content" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
|
@ -80,6 +89,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
android:resource="@xml/nfc_tech_filter" />
|
||||
</activity>
|
||||
|
||||
<!-- Document Provider -->
|
||||
<provider
|
||||
android:name=".features.DocumentProvider"
|
||||
android:authorities="${applicationId}.user"
|
||||
|
|
|
@ -18,6 +18,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
RENDERER_ENHANCED_SHADER_BUILDING("use_enhanced_shader_building"),
|
||||
PICTURE_IN_PICTURE("picture_in_picture"),
|
||||
USE_CUSTOM_RTC("custom_rtc_enabled"),
|
||||
BLACK_BACKGROUNDS("black_backgrounds"),
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.9.0" apply false
|
||||
id("com.android.library") version "8.9.0" apply false
|
||||
id("com.android.application") version "8.9.2" apply false
|
||||
id("com.android.library") version "8.9.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.20" apply false
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
#include <QComboBox>
|
||||
#include <QSpinBox>
|
||||
#include <QSlider>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
|
@ -38,7 +41,22 @@ ConfigureCpu::ConfigureCpu(const Core::System& system_,
|
|||
|
||||
ConfigureCpu::~ConfigureCpu() = default;
|
||||
|
||||
void ConfigureCpu::SetConfiguration() {}
|
||||
void ConfigureCpu::SetConfiguration() {
|
||||
// Set clock rate values from settings
|
||||
const u32 clock_rate_mhz = Settings::values.cpu_clock_rate.GetValue() / 1'000'000;
|
||||
ui->clock_rate_slider->setValue(static_cast<int>(clock_rate_mhz));
|
||||
ui->clock_rate_spinbox->setValue(static_cast<int>(clock_rate_mhz));
|
||||
|
||||
// Connect slider and spinbox signals to keep them in sync
|
||||
connect(ui->clock_rate_slider, &QSlider::valueChanged, this, [this](int value) {
|
||||
ui->clock_rate_spinbox->setValue(value);
|
||||
});
|
||||
|
||||
connect(ui->clock_rate_spinbox, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) {
|
||||
ui->clock_rate_slider->setValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
|
||||
auto* accuracy_layout = ui->widget_accuracy->layout();
|
||||
auto* backend_layout = ui->widget_backend->layout();
|
||||
|
@ -99,6 +117,9 @@ void ConfigureCpu::ApplyConfiguration() {
|
|||
for (const auto& apply_func : apply_funcs) {
|
||||
apply_func(is_powered_on);
|
||||
}
|
||||
|
||||
// Save the clock rate setting (convert from MHz to Hz)
|
||||
Settings::values.cpu_clock_rate = static_cast<u32>(ui->clock_rate_spinbox->value()) * 1'000'000;
|
||||
}
|
||||
|
||||
void ConfigureCpu::changeEvent(QEvent* event) {
|
||||
|
|
|
@ -126,6 +126,67 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="clock_rate_group">
|
||||
<property name="title">
|
||||
<string>CPU Clock Rate</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_clock_description">
|
||||
<property name="text">
|
||||
<string>CPU clock rate in MHz. Setting a higher clock rate will improve performance but may cause system instability. Default is 1020 MHz.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QSlider" name="clock_rate_slider">
|
||||
<property name="minimum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1785</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1020</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="clock_rate_spinbox">
|
||||
<property name="suffix">
|
||||
<string> MHz</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1785</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1020</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -423,6 +423,12 @@
|
|||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="use_auto_stub">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>This feature has been disabled.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Auto-Stub**</string>
|
||||
</property>
|
||||
|
@ -430,6 +436,12 @@
|
|||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="quest_flag">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>This feature has been disabled.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Kiosk (Quest) Mode</string>
|
||||
</property>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<property name="title">
|
||||
<string>citron Web Service</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutYuzuWebService">
|
||||
<layout class="QVBoxLayout" name="verticalLayoutCitronWebService">
|
||||
<item>
|
||||
<widget class="QLabel" name="web_credentials_disclaimer">
|
||||
<property name="text">
|
||||
|
@ -33,7 +33,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayoutYuzuUsername">
|
||||
<layout class="QGridLayout" name="gridLayoutCitronUsername">
|
||||
<item row="2" column="3">
|
||||
<widget class="QPushButton" name="button_reset_token">
|
||||
<property name="sizePolicy">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "citron/configuration/shared_translation.h"
|
||||
|
@ -146,6 +147,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
|||
INSERT(
|
||||
Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
|
||||
tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
|
||||
INSERT(
|
||||
Settings, respect_present_interval_zero, tr("Respect present interval 0 for unlocked FPS"),
|
||||
tr("When enabled, present interval 0 will be used for games requesting unlocked FPS.\n"
|
||||
"This matches console behavior more closely, but may cause higher battery usage and frame pacing issues.\n"
|
||||
"When disabled (default), present interval 0 is capped at 120FPS to conserve battery."));
|
||||
INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
|
||||
tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
|
||||
"decoding, or perform no decoding at all (black screen on videos).\n"
|
||||
|
|
|
@ -759,6 +759,7 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
|
|||
if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) {
|
||||
enable &= setting.UsingGlobal();
|
||||
}
|
||||
|
||||
this->setEnabled(enable);
|
||||
|
||||
this->setToolTip(tooltip);
|
||||
|
|
|
@ -36,7 +36,7 @@ system_
|
|||
DiscordEventHandlers handlers {};
|
||||
// The number is the client ID for citron, it's used for images and the
|
||||
// application name
|
||||
Discord_Initialize("1322413013248118888", & handlers, 1, nullptr);
|
||||
Discord_Initialize("1361252452329848892", & handlers, 1, nullptr);
|
||||
}
|
||||
|
||||
DiscordImpl::~DiscordImpl() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -89,6 +90,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||
SUB(Service, BGTC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, BSD) \
|
||||
SUB(Service, Capture) \
|
||||
SUB(Service, ERPT) \
|
||||
SUB(Service, ETicket) \
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
@ -57,6 +58,7 @@ enum class Class : u8 {
|
|||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
Service_BSD, ///< The BSD sockets service
|
||||
Service_Capture, ///< The capture service
|
||||
Service_ERPT, ///< The error reporting service
|
||||
Service_ETicket, ///< The ETicket service
|
||||
|
|
|
@ -198,6 +198,7 @@ struct Values {
|
|||
MemoryLayout::Memory_12Gb,
|
||||
"memory_layout_mode",
|
||||
Category::Core};
|
||||
SwitchableSetting<u32> cpu_clock_rate{linkage, 1'020'000'000, "cpu_clock_rate", Category::Cpu};
|
||||
SwitchableSetting<bool> use_speed_limit{
|
||||
linkage, true, "use_speed_limit", Category::Core, Specialization::Paired, false, true};
|
||||
SwitchableSetting<u16, true> speed_limit{linkage,
|
||||
|
@ -278,6 +279,8 @@ struct Values {
|
|||
Category::Renderer};
|
||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
||||
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
|
||||
SwitchableSetting<bool> respect_present_interval_zero{
|
||||
linkage, false, "respect_present_interval_zero", Category::Renderer};
|
||||
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
|
||||
#ifdef ANDROID
|
||||
AstcDecodeMode::Cpu,
|
||||
|
@ -392,11 +395,11 @@ struct Values {
|
|||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> async_presentation{linkage,
|
||||
#ifdef ANDROID
|
||||
false, // Disabled due to crashes
|
||||
true,
|
||||
#else
|
||||
false, // Disabled due to crashes
|
||||
false,
|
||||
#endif
|
||||
"async_presentation", Category::RendererAdvanced}; // Hide from UI
|
||||
"async_presentation", Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> use_reactive_flushing{linkage,
|
||||
|
@ -609,13 +612,17 @@ struct Values {
|
|||
Category::Network};
|
||||
|
||||
// WebService
|
||||
Setting<bool> enable_telemetry{linkage, true, "enable_telemetry", Category::WebService};
|
||||
Setting<std::string> web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url",
|
||||
Setting<bool> enable_telemetry{linkage, false, "enable_telemetry", Category::WebService};
|
||||
Setting<std::string> web_api_url{linkage, "https://api.ynet-fun.xyz", "web_api_url",
|
||||
Category::WebService};
|
||||
Setting<std::string> citron_username{linkage, std::string(), "citron_username",
|
||||
Category::WebService};
|
||||
Setting<std::string> citron_token{linkage, std::string(), "citron_token", Category::WebService};
|
||||
|
||||
// Memory
|
||||
Setting<bool> use_gpu_memory_manager{linkage, false, "use_gpu_memory_manager", Category::System};
|
||||
Setting<bool> enable_memory_snapshots{linkage, false, "enable_memory_snapshots", Category::System};
|
||||
|
||||
// Add-Ons
|
||||
std::map<u64, std::vector<std::string>> disabled_addons;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
@ -8,6 +9,7 @@
|
|||
#include <ratio>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
|
@ -15,7 +17,10 @@ class WallClock {
|
|||
public:
|
||||
static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
|
||||
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
|
||||
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
|
||||
// Changed from constexpr to function to get dynamic value from settings
|
||||
static inline u64 CPUTickFreq() {
|
||||
return Core::Hardware::BASE_CLOCK_RATE();
|
||||
} // T210/4 A57 CPU Tick Frequency from settings
|
||||
|
||||
virtual ~WallClock() = default;
|
||||
|
||||
|
@ -76,12 +81,28 @@ protected:
|
|||
using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
|
||||
using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
|
||||
|
||||
// Cycle Timing
|
||||
// Cycle Timing - using functions for dynamic values
|
||||
|
||||
using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
|
||||
using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
|
||||
using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
|
||||
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
|
||||
// Update these to use functions instead of constexpr
|
||||
struct CPUTickToNsRatio {
|
||||
static inline std::intmax_t num = std::nano::den;
|
||||
static inline std::intmax_t den = CPUTickFreq();
|
||||
};
|
||||
|
||||
struct CPUTickToUsRatio {
|
||||
static inline std::intmax_t num = std::micro::den;
|
||||
static inline std::intmax_t den = CPUTickFreq();
|
||||
};
|
||||
|
||||
struct CPUTickToCNTPCTRatio {
|
||||
static inline std::intmax_t num = CNTFRQ;
|
||||
static inline std::intmax_t den = CPUTickFreq();
|
||||
};
|
||||
|
||||
struct CPUTickToGPUTickRatio {
|
||||
static inline std::intmax_t num = GPUTickFreq;
|
||||
static inline std::intmax_t den = CPUTickFreq();
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<WallClock> CreateOptimalClock();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
add_library(core STATIC
|
||||
|
@ -776,8 +777,12 @@ add_library(core STATIC
|
|||
hle/service/ngc/ngc.h
|
||||
hle/service/nifm/nifm.cpp
|
||||
hle/service/nifm/nifm.h
|
||||
hle/service/nifm/nifm_utils.cpp
|
||||
hle/service/nifm/nifm_utils.h
|
||||
hle/service/nim/nim.cpp
|
||||
hle/service/nim/nim.h
|
||||
hle/service/nim/nim_utils.cpp
|
||||
hle/service/nim/nim_utils.h
|
||||
hle/service/npns/npns.cpp
|
||||
hle/service/npns/npns.h
|
||||
hle/service/ns/account_proxy_interface.cpp
|
||||
|
@ -1045,6 +1050,14 @@ add_library(core STATIC
|
|||
hle/service/sm/sm_controller.h
|
||||
hle/service/sockets/bsd.cpp
|
||||
hle/service/sockets/bsd.h
|
||||
hle/service/sockets/bsd_nu.cpp
|
||||
hle/service/sockets/bsd_nu.h
|
||||
hle/service/sockets/bsdcfg.cpp
|
||||
hle/service/sockets/bsdcfg.h
|
||||
hle/service/sockets/dns_priv.cpp
|
||||
hle/service/sockets/dns_priv.h
|
||||
hle/service/sockets/ethc.cpp
|
||||
hle/service/sockets/ethc.h
|
||||
hle/service/sockets/nsd.cpp
|
||||
hle/service/sockets/nsd.h
|
||||
hle/service/sockets/sfdnsres.cpp
|
||||
|
@ -1053,6 +1066,8 @@ add_library(core STATIC
|
|||
hle/service/sockets/sockets.h
|
||||
hle/service/sockets/sockets_translate.cpp
|
||||
hle/service/sockets/sockets_translate.h
|
||||
hle/service/sockets/socket_utils.cpp
|
||||
hle/service/sockets/socket_utils.h
|
||||
hle/service/spl/csrng.cpp
|
||||
hle/service/spl/csrng.h
|
||||
hle/service/spl/spl.cpp
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace Hardware {
|
||||
|
||||
constexpr u64 BASE_CLOCK_RATE = 1'020'000'000; // Default CPU Frequency = 1020 MHz
|
||||
inline u64 BASE_CLOCK_RATE() { return Settings::values.cpu_clock_rate.GetValue(); } // Default CPU Frequency set in settings, defaults to 1020 MHz
|
||||
constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
|
||||
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace HLE::ApiVersion {
|
|||
|
||||
// Horizon OS version constants.
|
||||
|
||||
constexpr u8 HOS_VERSION_MAJOR = 19;
|
||||
constexpr u8 HOS_VERSION_MAJOR = 20;
|
||||
constexpr u8 HOS_VERSION_MINOR = 0;
|
||||
constexpr u8 HOS_VERSION_MICRO = 1;
|
||||
|
||||
|
@ -22,9 +22,9 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
|
|||
constexpr u8 SDK_REVISION_MINOR = 0;
|
||||
|
||||
constexpr char PLATFORM_STRING[] = "NX";
|
||||
constexpr char VERSION_HASH[] = "835c78223df116284ef7e36e8441760edc81729c";
|
||||
constexpr char DISPLAY_VERSION[] = "19.0.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 19.0.1-1.0";
|
||||
constexpr char VERSION_HASH[] = "0b2540e5cd7498dd61f6caeca5136c73d9b1d21a";
|
||||
constexpr char DISPLAY_VERSION[] = "20.0.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 20.0.1-1.0";
|
||||
|
||||
// Atmosphere version constants.
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -81,8 +82,8 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
|
|||
|
||||
void Controller::SetClockSpeed(u32 mhz) {
|
||||
LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz);
|
||||
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
|
||||
// TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
|
||||
// Update the clock rate setting with the provided MHz value (convert to Hz)
|
||||
Settings::values.cpu_clock_rate = mhz * 1'000'000;
|
||||
}
|
||||
|
||||
} // namespace Service::APM
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <queue>
|
||||
|
@ -28,12 +29,12 @@ public:
|
|||
{10102, nullptr, "UpdateFriendInfo"},
|
||||
{10110, nullptr, "GetFriendProfileImage"},
|
||||
{10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"},
|
||||
{10121, nullptr, "EnsureFriendListAvailable"},
|
||||
{10121, &IFriendService::EnsureFriendListAvailable, "EnsureFriendListAvailable"},
|
||||
{10200, nullptr, "SendFriendRequestForApplication"},
|
||||
{10211, nullptr, "AddFacedFriendRequestForApplication"},
|
||||
{10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
|
||||
{10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
|
||||
{10421, nullptr, "EnsureBlockedUserListAvailable"},
|
||||
{10421, &IFriendService::EnsureBlockedUserListAvailable, "EnsureBlockedUserListAvailable"},
|
||||
{10500, nullptr, "GetProfileList"},
|
||||
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
|
||||
{10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
|
||||
|
@ -166,11 +167,27 @@ private:
|
|||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
// Signal the completion event to unblock any waiting threads
|
||||
completion_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
void EnsureFriendListAvailable(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) EnsureFriendListAvailable called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
// Signal the completion event to unblock any waiting threads
|
||||
completion_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetBlockedUserListIds(HLERequestContext& ctx) {
|
||||
// This is safe to stub, as there should be no adverse consequences from reporting no
|
||||
// blocked users.
|
||||
|
@ -186,11 +203,45 @@ private:
|
|||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
// Signal the completion event to unblock any waiting threads
|
||||
completion_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
void EnsureBlockedUserListAvailable(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) EnsureBlockedUserListAvailable called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
// Signal the completion event to unblock any waiting threads
|
||||
completion_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetReceivedFriendInvitationCountCache(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Friend, "(STUBBED) called, check in out");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(0); // Zero invitations
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Friend, "Cancel called - returning immediately");
|
||||
|
||||
// Signal the completion event to unblock any waiting threads
|
||||
completion_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void DeclareCloseOnlinePlaySession(HLERequestContext& ctx) {
|
||||
// Stub used by Splatoon 2
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called");
|
||||
|
@ -248,14 +299,6 @@ private:
|
|||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetReceivedFriendInvitationCountCache(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Friend, "(STUBBED) called, check in out");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(0);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* completion_event;
|
||||
|
@ -287,6 +330,9 @@ private:
|
|||
void GetEvent(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Friend, "called");
|
||||
|
||||
// Signal the notification event to unblock any waiting threads
|
||||
notification_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(notification_event->GetReadableEvent());
|
||||
|
@ -363,10 +409,11 @@ private:
|
|||
};
|
||||
|
||||
void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Friend, "CreateFriendService called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IFriendService>(system);
|
||||
LOG_DEBUG(Service_Friend, "called");
|
||||
}
|
||||
|
||||
void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
|
@ -6,6 +7,7 @@
|
|||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nifm/nifm_utils.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "network/network.h"
|
||||
|
||||
|
|
85
src/core/hle/service/nifm/nifm_utils.cpp
Normal file
85
src/core/hle/service/nifm/nifm_utils.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nifm/nifm_utils.h"
|
||||
|
||||
namespace Service::NIFM::nn::nifm {
|
||||
|
||||
// Simple implementation to track network requests
|
||||
namespace {
|
||||
std::mutex g_request_mutex;
|
||||
std::map<u32, NetworkRequest> g_requests;
|
||||
u32 g_next_request_id = 1;
|
||||
bool g_network_available = true; // Default to true for emulation
|
||||
}
|
||||
|
||||
bool IsNetworkAvailable() {
|
||||
// For emulation purposes, we'll just return the mocked availability
|
||||
std::lock_guard lock(g_request_mutex);
|
||||
return g_network_available;
|
||||
}
|
||||
|
||||
u32 SubmitNetworkRequest() {
|
||||
std::lock_guard lock(g_request_mutex);
|
||||
|
||||
if (!g_network_available) {
|
||||
LOG_WARNING(Service_NIFM, "Network request submitted but network is not available");
|
||||
}
|
||||
|
||||
u32 request_id = g_next_request_id++;
|
||||
|
||||
NetworkRequest request{
|
||||
.request_id = request_id,
|
||||
.is_pending = true,
|
||||
.result = NetworkRequestResult::Success // Assume immediate success for emulation
|
||||
};
|
||||
|
||||
g_requests[request_id] = request;
|
||||
|
||||
LOG_INFO(Service_NIFM, "Network request submitted with ID: {}", request_id);
|
||||
return request_id;
|
||||
}
|
||||
|
||||
NetworkRequestResult GetNetworkRequestResult(u32 request_id) {
|
||||
std::lock_guard lock(g_request_mutex);
|
||||
|
||||
auto it = g_requests.find(request_id);
|
||||
if (it == g_requests.end()) {
|
||||
LOG_ERROR(Service_NIFM, "Tried to get result for invalid request ID: {}", request_id);
|
||||
return NetworkRequestResult::Error;
|
||||
}
|
||||
|
||||
// For emulation, we'll mark the request as no longer pending once the result is checked
|
||||
it->second.is_pending = false;
|
||||
|
||||
return it->second.result;
|
||||
}
|
||||
|
||||
bool CancelNetworkRequest(u32 request_id) {
|
||||
std::lock_guard lock(g_request_mutex);
|
||||
|
||||
auto it = g_requests.find(request_id);
|
||||
if (it == g_requests.end()) {
|
||||
LOG_ERROR(Service_NIFM, "Tried to cancel invalid request ID: {}", request_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!it->second.is_pending) {
|
||||
LOG_WARNING(Service_NIFM, "Tried to cancel a request that is not pending, ID: {}", request_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
it->second.is_pending = false;
|
||||
it->second.result = NetworkRequestResult::Canceled;
|
||||
|
||||
LOG_INFO(Service_NIFM, "Network request canceled with ID: {}", request_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Service::NIFM::nn::nifm
|
44
src/core/hle/service/nifm/nifm_utils.h
Normal file
44
src/core/hle/service/nifm/nifm_utils.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::NIFM {
|
||||
|
||||
// Network request result codes
|
||||
enum class NetworkRequestResult {
|
||||
Success = 0,
|
||||
Error = 1,
|
||||
Canceled = 2,
|
||||
Timeout = 3,
|
||||
};
|
||||
|
||||
// Network request structure
|
||||
struct NetworkRequest {
|
||||
u32 request_id;
|
||||
bool is_pending;
|
||||
NetworkRequestResult result;
|
||||
};
|
||||
|
||||
namespace nn::nifm {
|
||||
|
||||
// Checks if network connectivity is available
|
||||
bool IsNetworkAvailable();
|
||||
|
||||
// Submits a network connection request
|
||||
// Returns the request ID or 0 if the request failed
|
||||
u32 SubmitNetworkRequest();
|
||||
|
||||
// Gets the status of a network request
|
||||
// Returns the request result
|
||||
NetworkRequestResult GetNetworkRequestResult(u32 request_id);
|
||||
|
||||
// Cancels a pending network request
|
||||
// Returns true if the request was successfully canceled
|
||||
bool CancelNetworkRequest(u32 request_id);
|
||||
|
||||
} // namespace nn::nifm
|
||||
|
||||
} // namespace Service::NIFM
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
|
@ -8,6 +9,7 @@
|
|||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/nim/nim_utils.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
|
|
124
src/core/hle/service/nim/nim_utils.cpp
Normal file
124
src/core/hle/service/nim/nim_utils.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nim/nim_utils.h"
|
||||
|
||||
namespace Service::NIM::nn::nim {
|
||||
|
||||
// Simple implementation to track installation tasks
|
||||
namespace {
|
||||
std::mutex g_task_mutex;
|
||||
std::map<u64, Task> g_tasks;
|
||||
u64 g_next_task_id = 1;
|
||||
bool g_service_available = true; // Default to true for emulation
|
||||
}
|
||||
|
||||
bool IsServiceAvailable() {
|
||||
std::lock_guard lock(g_task_mutex);
|
||||
return g_service_available;
|
||||
}
|
||||
|
||||
u64 CreateInstallTask(u64 application_id) {
|
||||
std::lock_guard lock(g_task_mutex);
|
||||
|
||||
if (!g_service_available) {
|
||||
LOG_WARNING(Service_NIM, "Installation task creation attempted but service is not available");
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 task_id = g_next_task_id++;
|
||||
|
||||
Task task{
|
||||
.task_id = task_id,
|
||||
.progress = {
|
||||
.downloaded_bytes = 0,
|
||||
.total_bytes = 1'000'000'000, // Fake 1GB download size
|
||||
.status = TaskStatus::None
|
||||
}
|
||||
};
|
||||
|
||||
g_tasks[task_id] = task;
|
||||
|
||||
LOG_INFO(Service_NIM, "Installation task created for application 0x{:016X} with ID: {}",
|
||||
application_id, task_id);
|
||||
return task_id;
|
||||
}
|
||||
|
||||
TaskProgress GetTaskProgress(u64 task_id) {
|
||||
std::lock_guard lock(g_task_mutex);
|
||||
|
||||
auto it = g_tasks.find(task_id);
|
||||
if (it == g_tasks.end()) {
|
||||
LOG_ERROR(Service_NIM, "Tried to get progress for invalid task ID: {}", task_id);
|
||||
return {0, 0, TaskStatus::Failed};
|
||||
}
|
||||
|
||||
// If task is in download state, simulate progress
|
||||
if (it->second.progress.status == TaskStatus::Downloading) {
|
||||
// Simulate download progress (add 10% of total size)
|
||||
auto& progress = it->second.progress;
|
||||
const u64 increment = progress.total_bytes / 10;
|
||||
|
||||
progress.downloaded_bytes += increment;
|
||||
if (progress.downloaded_bytes >= progress.total_bytes) {
|
||||
progress.downloaded_bytes = progress.total_bytes;
|
||||
progress.status = TaskStatus::Installing;
|
||||
LOG_INFO(Service_NIM, "Task ID {} download complete, now installing", task_id);
|
||||
}
|
||||
} else if (it->second.progress.status == TaskStatus::Installing) {
|
||||
// Simulate installation completion
|
||||
it->second.progress.status = TaskStatus::Complete;
|
||||
LOG_INFO(Service_NIM, "Task ID {} installation complete", task_id);
|
||||
}
|
||||
|
||||
return it->second.progress;
|
||||
}
|
||||
|
||||
bool StartInstallTask(u64 task_id) {
|
||||
std::lock_guard lock(g_task_mutex);
|
||||
|
||||
auto it = g_tasks.find(task_id);
|
||||
if (it == g_tasks.end()) {
|
||||
LOG_ERROR(Service_NIM, "Tried to start invalid task ID: {}", task_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it->second.progress.status != TaskStatus::None &&
|
||||
it->second.progress.status != TaskStatus::Pending) {
|
||||
LOG_WARNING(Service_NIM, "Tried to start task ID {} which is already in progress", task_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
it->second.progress.status = TaskStatus::Downloading;
|
||||
LOG_INFO(Service_NIM, "Started installation task ID: {}", task_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CancelInstallTask(u64 task_id) {
|
||||
std::lock_guard lock(g_task_mutex);
|
||||
|
||||
auto it = g_tasks.find(task_id);
|
||||
if (it == g_tasks.end()) {
|
||||
LOG_ERROR(Service_NIM, "Tried to cancel invalid task ID: {}", task_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it->second.progress.status == TaskStatus::Complete ||
|
||||
it->second.progress.status == TaskStatus::Failed ||
|
||||
it->second.progress.status == TaskStatus::Canceled) {
|
||||
LOG_WARNING(Service_NIM, "Tried to cancel task ID {} which is already in a final state", task_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
it->second.progress.status = TaskStatus::Canceled;
|
||||
LOG_INFO(Service_NIM, "Canceled installation task ID: {}", task_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Service::NIM::nn::nim
|
57
src/core/hle/service/nim/nim_utils.h
Normal file
57
src/core/hle/service/nim/nim_utils.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::NIM {
|
||||
|
||||
// Network installation task status
|
||||
enum class TaskStatus {
|
||||
None = 0,
|
||||
Pending = 1,
|
||||
Downloading = 2,
|
||||
Installing = 3,
|
||||
Complete = 4,
|
||||
Failed = 5,
|
||||
Canceled = 6,
|
||||
};
|
||||
|
||||
// Network installation task progress
|
||||
struct TaskProgress {
|
||||
u64 downloaded_bytes;
|
||||
u64 total_bytes;
|
||||
TaskStatus status;
|
||||
};
|
||||
|
||||
// Network installation task
|
||||
struct Task {
|
||||
u64 task_id;
|
||||
TaskProgress progress;
|
||||
};
|
||||
|
||||
namespace nn::nim {
|
||||
|
||||
// Checks if the NIM service is available
|
||||
bool IsServiceAvailable();
|
||||
|
||||
// Creates a new installation task
|
||||
// Returns the task ID or 0 if the task creation failed
|
||||
u64 CreateInstallTask(u64 application_id);
|
||||
|
||||
// Gets the progress of an installation task
|
||||
// Returns the task progress
|
||||
TaskProgress GetTaskProgress(u64 task_id);
|
||||
|
||||
// Starts an installation task
|
||||
// Returns true if the task was successfully started
|
||||
bool StartInstallTask(u64 task_id);
|
||||
|
||||
// Cancels an installation task
|
||||
// Returns true if the task was successfully canceled
|
||||
bool CancelInstallTask(u64 task_id);
|
||||
|
||||
} // namespace nn::nim
|
||||
|
||||
} // namespace Service::NIM
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue