Compare commits

..

No commits in common. "master" and "v0.6-canary-refresh" have entirely different histories.

151 changed files with 1218 additions and 4690 deletions

View File

@ -1,14 +0,0 @@
# 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

View File

@ -1,59 +0,0 @@
# 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

View File

@ -1,106 +0,0 @@
# 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

View File

@ -1,22 +0,0 @@
# 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'

View File

@ -1,14 +0,0 @@
# 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

View File

@ -1,64 +0,0 @@
# 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

View File

@ -1,23 +0,0 @@
# 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

View File

@ -1,28 +0,0 @@
#!/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

View File

@ -1,21 +0,0 @@
#!/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}"

View File

@ -1,21 +0,0 @@
#!/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

View File

@ -1,21 +0,0 @@
#!/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}"

View File

@ -1,41 +0,0 @@
#!/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"

View File

@ -1,32 +0,0 @@
#!/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

View File

@ -1,11 +0,0 @@
#!/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 ./

View File

@ -1,23 +0,0 @@
#!/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

View File

@ -1,20 +0,0 @@
#!/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}/"

View File

@ -1,10 +0,0 @@
#!/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}/"

View File

@ -1,9 +0,0 @@
#!/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

View File

@ -1,10 +0,0 @@
#!/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 ./

View File

@ -1,37 +0,0 @@
#!/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

View File

@ -1,79 +0,0 @@
#!/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

View File

@ -1,16 +0,0 @@
#!/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 ./

View File

@ -1,67 +0,0 @@
#!/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

View File

@ -1,19 +0,0 @@
# 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}")

View File

@ -1,60 +0,0 @@
# 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)

View File

@ -1,27 +0,0 @@
# 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)

View File

@ -1,5 +0,0 @@
# 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"

View File

@ -1,19 +0,0 @@
#!/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

View File

@ -1,66 +0,0 @@
#!/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/ ';'

View File

@ -1,11 +0,0 @@
#!/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 ./

View File

@ -1,33 +0,0 @@
# 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
}

View File

@ -1,109 +0,0 @@
# 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()

View File

@ -1,118 +0,0 @@
# 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"
}

View File

@ -1,28 +0,0 @@
#!/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

View File

@ -1,8 +0,0 @@
# 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'

View File

@ -1,31 +0,0 @@
# 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'

View File

@ -1,26 +0,0 @@
# 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'

View File

@ -1,33 +0,0 @@
# 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)

View File

@ -1,40 +0,0 @@
# 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)

View File

@ -1,17 +0,0 @@
# 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'

View File

@ -1,44 +0,0 @@
# 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'

View File

@ -1,43 +0,0 @@
# 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'

View File

@ -1,41 +0,0 @@
# 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'

View File

@ -1,18 +0,0 @@
# 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)'

View File

@ -1,22 +0,0 @@
# 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)'

View File

@ -1,18 +0,0 @@
# 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

View File

@ -1,12 +0,0 @@
# 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'

View File

@ -1,13 +0,0 @@
# 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'

View File

@ -1,19 +0,0 @@
# 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

View File

@ -1,14 +0,0 @@
# 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')

View File

@ -1,10 +0,0 @@
# 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)

View File

@ -1,10 +0,0 @@
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"

View File

@ -1,64 +0,0 @@
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

View File

@ -1,8 +0,0 @@
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.

View File

@ -1,28 +0,0 @@
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

View File

@ -1,80 +0,0 @@
# 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"]'

View File

@ -1,66 +0,0 @@
# 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 }}

View File

@ -1,318 +0,0 @@
// 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;

View File

@ -1,57 +0,0 @@
# 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);

View File

@ -1,59 +0,0 @@
# 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

View File

@ -1,33 +0,0 @@
# 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

View File

@ -1,17 +0,0 @@
# 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

View File

@ -1,190 +0,0 @@
# 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
View File

@ -9,22 +9,22 @@
url = https://github.com/mozilla/cubeb.git
[submodule "dynarmic"]
path = externals/dynarmic
url = https://github.com/yuzu-mirror/dynarmic.git
url = https://git.citron-emu.org/Citron/dynarmic.git
[submodule "libusb"]
path = externals/libusb/libusb
url = https://github.com/libusb/libusb.git
[submodule "discord-rpc"]
path = externals/discord-rpc
url = https://github.com/yuzu-mirror/discord-rpc.git
url = https://git.citron-emu.org/Citron/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://github.com/yuzu-mirror/sirit.git
url = https://git.citron-emu.org/Citron/sirit.git
[submodule "mbedtls"]
path = externals/mbedtls
url = https://github.com/yuzu-mirror/mbedtls.git
url = https://git.citron-emu.org/Citron/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://github.com/yuzu-mirror/breakpad.git
url = https://git.citron-emu.org/Citron/breakpad.git
[submodule "simpleini"]
path = externals/simpleini
url = https://github.com/brofield/simpleini.git
[submodule "oaknut"]
path = externals/oaknut
url = https://github.com/yuzu-mirror/oaknut
url = https://git.citron-emu.org/Citron/oaknut.git
[submodule "Vulkan-Utility-Libraries"]
path = externals/Vulkan-Utility-Libraries
url = https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git

View File

@ -17,45 +17,6 @@ if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
endif()
# PGO Configuration
option(CITRON_ENABLE_PGO_INSTRUMENT "Enable Profile-Guided Optimization instrumentation build" OFF)
option(CITRON_ENABLE_PGO_OPTIMIZE "Enable Profile-Guided Optimization optimization build" OFF)
if(MSVC)
if(CITRON_ENABLE_PGO_INSTRUMENT)
string(APPEND CMAKE_CXX_FLAGS_RELEASE " /GL /LTCG:PGINSTRUMENT")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " /LTCG:PGINSTRUMENT")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " /LTCG:PGINSTRUMENT")
elseif(CITRON_ENABLE_PGO_OPTIMIZE)
string(APPEND CMAKE_CXX_FLAGS_RELEASE " /GL /LTCG:PGOPTIMIZE")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " /LTCG:PGOPTIMIZE")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " /LTCG:PGOPTIMIZE")
endif()
else()
# GCC and Clang PGO flags
if(CITRON_ENABLE_PGO_INSTRUMENT)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-instr-generate")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-instr-generate")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-instr-generate")
else() # GCC
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-generate")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-generate")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-generate")
endif()
elseif(CITRON_ENABLE_PGO_OPTIMIZE)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-instr-use=default.profdata")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-instr-use=default.profdata")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-instr-use=default.profdata")
else() # GCC
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-use")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-use")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-use")
endif()
endif()
endif()
# Check if SDL2::SDL2 target exists; if not, create an alias
if (TARGET SDL2::SDL2-static)
add_library(SDL2::SDL2 ALIAS SDL2::SDL2-static)
@ -137,7 +98,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.309.0")
set(vvl_version "1.4.304.1")
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,23 +130,13 @@ 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()
set(VCPKG_HOST_TRIPLET "x64-windows")
# 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(VCPKG_HOST_TRIPLET "x64-windows")
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}")
@ -369,7 +320,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 10 REQUIRED)
find_package(fmt 11 REQUIRED)
if (CITRON_USE_LLVM_DEMANGLE)
find_package(LLVM MODULE COMPONENTS Demangle)
endif()
@ -384,7 +335,7 @@ find_package(ZLIB REQUIRED)
find_package(zstd REQUIRED)
if (NOT CITRON_USE_EXTERNAL_VULKAN_HEADERS)
find_package(VulkanHeaders 1.4.313 REQUIRED)
find_package(VulkanHeaders 1.4.307 REQUIRED)
endif()
if (NOT CITRON_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
@ -447,7 +398,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.28.2")
set(SDL2_VER "SDL2-2.32.0")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRON_USE_BUNDLED_SDL2 and provide your own.")
endif()
@ -594,7 +545,7 @@ endif()
# against all the src files. This should be used before making a pull request.
# =======================================================================
set(CLANG_FORMAT_POSTFIX "-15")
set(CLANG_FORMAT_POSTFIX "-18")
find_program(CLANG_FORMAT
NAMES clang-format${CLANG_FORMAT_POSTFIX}
clang-format
@ -605,7 +556,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://github.com/yuzu-mirror/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
https://git.citron-emu.org/Citron/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
"${CLANG_FORMAT}" SHOW_PROGRESS
STATUS DOWNLOAD_SUCCESS)
if (NOT DOWNLOAD_SUCCESS EQUAL 0)
@ -733,3 +684,42 @@ if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "dist/org.citron_emu.citron.metainfo.xml"
DESTINATION "share/metainfo")
endif()
# PGO Configuration
option(CITRON_ENABLE_PGO_INSTRUMENT "Enable Profile-Guided Optimization instrumentation build" OFF)
option(CITRON_ENABLE_PGO_OPTIMIZE "Enable Profile-Guided Optimization optimization build" OFF)
if(MSVC)
if(CITRON_ENABLE_PGO_INSTRUMENT)
string(APPEND CMAKE_CXX_FLAGS_RELEASE " /GL /LTCG:PGINSTRUMENT")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " /LTCG:PGINSTRUMENT")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " /LTCG:PGINSTRUMENT")
elseif(CITRON_ENABLE_PGO_OPTIMIZE)
string(APPEND CMAKE_CXX_FLAGS_RELEASE " /GL /LTCG:PGOPTIMIZE")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " /LTCG:PGOPTIMIZE")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " /LTCG:PGOPTIMIZE")
endif()
else()
# GCC and Clang PGO flags
if(CITRON_ENABLE_PGO_INSTRUMENT)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-instr-generate")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-instr-generate")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-instr-generate")
else() # GCC
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-generate")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-generate")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-generate")
endif()
elseif(CITRON_ENABLE_PGO_OPTIMIZE)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-instr-use=default.profdata")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-instr-use=default.profdata")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-instr-use=default.profdata")
else() # GCC
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -fprofile-use")
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " -fprofile-use")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fprofile-use")
endif()
endif()
endif()

View File

@ -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://github.com/yuzu-mirror/")
set(package_base_url "https://git.citron-emu.org/Citron/")
set(package_repo "no_platform")
set(package_extension "no_platform")
if (WIN32)

View File

@ -14,14 +14,13 @@ 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/emu/-/releases) and dive into your favorite Nintendo homebrew titles!
Ready to experience Citron? [Download the latest version](https://git.citron-emu.org/Citron/Citron/releases) and dive into your favorite Nintendo homebrew titles!
## Getting Started 💡
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.
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.
## Source Code 🔧

View File

@ -1,5 +1,4 @@
# 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
@ -8,13 +7,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"

View File

@ -1,5 +1,4 @@
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-FileCopyrightText: 2025 citron Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
[Desktop Entry]
@ -14,4 +13,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=org.citron_emu.citron
StartupWMClass=citron

2
externals/SDL vendored

@ -1 +1 @@
Subproject commit 2359383fc187386204c3bb22de89655a494cd128
Subproject commit cc016b0046d563287f0aa9f09b958b5e70d43696

@ -1 +1 @@
Subproject commit e2e53a724677f6eba8ff0ce1ccb64ee321785cbd
Subproject commit cacef3039d277c448c89336290ec3937270b0996

@ -1 +1 @@
Subproject commit 4e246c56ec5afb5ad66b9b04374d39ac04675c8e
Subproject commit bc3a4d9fd9b46729651a3cec4f5226f6272b8684

@ -1 +1 @@
Subproject commit 539c0a8d8e3733c9f25ea9a184c85c77504f1653
Subproject commit c788c52156f3ef7bc7ab769cb03c110a53ac8fcb

2
externals/vcpkg vendored

@ -1 +1 @@
Subproject commit 5a874b05fb981e2643fd0c1c9ad63e19dfe76b27
Subproject commit e40d24cb149dd138e7c11d490834fa2c81298b32

View File

@ -11,10 +11,10 @@ plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.9.20"
kotlin("plugin.serialization") version "2.1.20-RC2"
id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
id("com.github.triplet.play") version "3.8.6"
id("org.jlleitschuh.gradle.ktlint") version "12.2.0"
id("com.github.triplet.play") version "3.12.1"
}
/**
@ -55,7 +55,7 @@ android {
defaultConfig {
// TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.citron.citron_emu"
applicationId = "com.antutu.ABenchMark"
minSdk = 30
//noinspection EditedTargetSdkVersion
targetSdk = 35
@ -76,19 +76,9 @@ 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 {
create("default") {
storeFile = file("$projectDir/debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
}
if (keystoreFile != null && !useDebugKeystore) {
if (keystoreFile != null) {
create("release") {
storeFile = file(keystoreFile)
storePassword = System.getenv("ANDROID_KEYSTORE_PASS")
@ -96,6 +86,12 @@ 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.
@ -103,7 +99,7 @@ android {
// Signed by release key, allowing for upload to Play Store.
release {
signingConfig = if (keystoreFile != null && !useDebugKeystore) {
signingConfig = if (keystoreFile != null) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("default")
@ -165,7 +161,7 @@ android {
externalNativeBuild {
cmake {
version = "4.0.1"
version = "3.31.6"
path = file("../../../CMakeLists.txt")
}
}
@ -183,8 +179,7 @@ android {
"-DCITRON_USE_BUNDLED_FFMPEG=ON",
"-DCITRON_ENABLE_LTO=ON",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
)
abiFilters("arm64-v8a") // , "x86_64")
@ -208,7 +203,7 @@ tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() }
tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset")
ktlint {
version.set("0.47.1")
version.set("0.49.1")
android.set(true)
ignoreFailures.set(false)
disabledRules.set(
@ -233,23 +228,24 @@ play {
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.recyclerview:recyclerview:1.3.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.fragment:fragment-ktx:1.6.1")
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.recyclerview:recyclerview:1.4.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.fragment:fragment-ktx:1.8.6")
implementation("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
implementation("io.coil-kt:coil:2.7.0")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03")
implementation("androidx.window:window:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
implementation("androidx.navigation:navigation-fragment-ktx:2.8.8")
implementation("androidx.navigation:navigation-ui-ktx:2.8.8")
implementation("info.debatty:java-string-similarity:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
}
fun runGitCommand(command: List<String>): String {

View File

@ -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 -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!--
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,37 +31,28 @@
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
android:enableOnBackInvokedCallback="true">
<!-- 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"
<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"
@ -81,7 +72,7 @@
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="application/octet-stream"
android:scheme="content" />
android:scheme="content"/>
</intent-filter>
<meta-data
@ -89,7 +80,6 @@
android:resource="@xml/nfc_tech_filter" />
</activity>
<!-- Document Provider -->
<provider
android:name=".features.DocumentProvider"
android:authorities="${applicationId}.user"

View File

@ -18,7 +18,6 @@ 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"),

View File

@ -180,62 +180,6 @@ class SetupFragment : Fragment() {
}
)
)
// Add title.keys installation page
add(
SetupPage(
R.drawable.ic_key,
R.string.install_title_keys,
R.string.install_title_keys_description,
R.drawable.ic_add,
true,
R.string.select_keys,
{
titleKeyCallback = it
getTitleKey.launch(arrayOf("*/*"))
},
true,
R.string.install_title_keys_warning,
R.string.install_title_keys_warning_description,
R.string.install_title_keys_warning_help,
{
val file = File(DirectoryInitialization.userDirectory + "/keys/title.keys")
if (file.exists()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
}
)
)
// Add firmware installation page (mandatory)
add(
SetupPage(
R.drawable.ic_key,
R.string.install_firmware,
R.string.install_firmware_description,
R.drawable.ic_add,
true,
R.string.select_firmware,
{
firmwareCallback = it
getFirmware.launch(arrayOf("application/zip"))
},
true,
R.string.install_firmware_warning,
R.string.install_firmware_warning_description,
R.string.install_firmware_warning_help,
{
if (NativeLibrary.isFirmwareAvailable()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
}
)
)
add(
SetupPage(
R.drawable.ic_controller,
@ -324,18 +268,6 @@ class SetupFragment : Fragment() {
return@setOnClickListener
}
// Special handling for firmware page - don't allow skipping
if (currentPage.titleId == R.string.install_firmware && !NativeLibrary.isFirmwareAvailable()) {
SetupWarningDialogFragment.newInstance(
currentPage.warningTitleId,
currentPage.warningDescriptionId,
currentPage.warningHelpLinkId,
index,
allowSkip = false
).show(childFragmentManager, SetupWarningDialogFragment.TAG)
return@setOnClickListener
}
if (!hasBeenWarned[index]) {
SetupWarningDialogFragment.newInstance(
currentPage.warningTitleId,
@ -414,30 +346,6 @@ class SetupFragment : Fragment() {
}
}
private lateinit var titleKeyCallback: SetupCallback
val getTitleKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
mainActivity.processTitleKey(result)
titleKeyCallback.onStepCompleted()
}
}
private lateinit var firmwareCallback: SetupCallback
val getFirmware =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
mainActivity.getFirmware.launch(arrayOf("application/zip"))
binding.root.postDelayed({
if (NativeLibrary.isFirmwareAvailable()) {
firmwareCallback.onStepCompleted()
}
}, 1000)
}
}
private lateinit var gamesDirCallback: SetupCallback
val getGamesDirectory =

View File

@ -17,7 +17,6 @@ class SetupWarningDialogFragment : DialogFragment() {
private var descriptionId: Int = 0
private var helpLinkId: Int = 0
private var page: Int = 0
private var allowSkip: Boolean = true
private lateinit var setupFragment: SetupFragment
@ -27,24 +26,17 @@ class SetupWarningDialogFragment : DialogFragment() {
descriptionId = requireArguments().getInt(DESCRIPTION)
helpLinkId = requireArguments().getInt(HELP_LINK)
page = requireArguments().getInt(PAGE)
allowSkip = requireArguments().getBoolean(ALLOW_SKIP, true)
setupFragment = requireParentFragment() as SetupFragment
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = MaterialAlertDialogBuilder(requireContext())
if (allowSkip) {
builder.setPositiveButton(R.string.warning_skip) { _: DialogInterface?, _: Int ->
.setPositiveButton(R.string.warning_skip) { _: DialogInterface?, _: Int ->
setupFragment.pageForward()
setupFragment.setPageWarned(page)
}
builder.setNegativeButton(R.string.warning_cancel, null)
} else {
// For mandatory steps, only show an OK button that dismisses the dialog
builder.setPositiveButton(R.string.ok, null)
}
.setNegativeButton(R.string.warning_cancel, null)
if (titleId != 0) {
builder.setTitle(titleId)
@ -56,7 +48,7 @@ class SetupWarningDialogFragment : DialogFragment() {
}
if (helpLinkId != 0) {
builder.setNeutralButton(R.string.warning_help) { _: DialogInterface?, _: Int ->
val helpLink = resources.getString(helpLinkId)
val helpLink = resources.getString(R.string.install_prod_keys_warning_help)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(helpLink))
startActivity(intent)
}
@ -72,14 +64,12 @@ class SetupWarningDialogFragment : DialogFragment() {
private const val DESCRIPTION = "Description"
private const val HELP_LINK = "HelpLink"
private const val PAGE = "Page"
private const val ALLOW_SKIP = "AllowSkip"
fun newInstance(
titleId: Int,
descriptionId: Int,
helpLinkId: Int,
page: Int,
allowSkip: Boolean = true
page: Int
): SetupWarningDialogFragment {
val dialog = SetupWarningDialogFragment()
val bundle = Bundle()
@ -88,7 +78,6 @@ class SetupWarningDialogFragment : DialogFragment() {
putInt(DESCRIPTION, descriptionId)
putInt(HELP_LINK, helpLinkId)
putInt(PAGE, page)
putBoolean(ALLOW_SKIP, allowSkip)
}
dialog.arguments = bundle
return dialog

View File

@ -377,57 +377,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return false
}
val getTitleKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
processTitleKey(result)
}
}
fun processTitleKey(result: Uri): Boolean {
if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance(
this,
titleId = R.string.reading_keys_failure,
descriptionId = R.string.install_title_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return false
}
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
result,
dstPath,
"title.keys"
) != null
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
applicationContext,
R.string.install_keys_success,
Toast.LENGTH_SHORT
).show()
homeViewModel.setCheckKeys(true)
gamesViewModel.reloadGames(true)
return true
} else {
MessageDialogFragment.newInstance(
this,
titleId = R.string.invalid_keys_error,
descriptionId = R.string.install_keys_failure_description,
helpLinkId = R.string.dumping_keys_quickstart_link
).show(supportFragmentManager, MessageDialogFragment.TAG)
return false
}
}
return false
}
val getFirmware =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {

View File

@ -108,15 +108,11 @@
<string name="import_saves">Import</string>
<string name="export_saves">Export</string>
<string name="install_firmware">Install firmware</string>
<string name="install_firmware_description">Required for emulation of system features</string>
<string name="install_firmware_warning">Firmware installation is mandatory</string>
<string name="install_firmware_warning_description">Firmware is required for proper emulation. You must install firmware to continue.</string>
<string name="install_firmware_warning_help">https://citron-emu.org/help/quickstart/#dumping-system-firmware</string>
<string name="install_firmware_description">Firmware must be in a ZIP archive and is needed to boot some games</string>
<string name="firmware_installing">Installing firmware</string>
<string name="firmware_installed_success">Firmware successfully installed</string>
<string name="firmware_installed_failure">Failed to install firmware</string>
<string name="firmware_installed_failure_description">The selected file is not a valid firmware archive or is corrupt.</string>
<string name="select_firmware">Select Firmware</string>
<string name="firmware_installed_success">Firmware installed successfully</string>
<string name="firmware_installed_failure">Firmware installation failed</string>
<string name="firmware_installed_failure_description">Make sure the firmware nca files are at the root of the zip and try again.</string>
<string name="share_log">Share debug logs</string>
<string name="share_log_description">Share citron\'s log file to debug issues</string>
<string name="share_log_missing">No log file found</string>
@ -176,14 +172,6 @@
<string name="cabinet_restorer">Restorer</string>
<string name="cabinet_formatter">Formatter</string>
<!-- Title keys strings -->
<string name="install_title_keys">Install title.keys</string>
<string name="install_title_keys_description">Required for additional game compatibility</string>
<string name="install_title_keys_warning">Skip adding title keys?</string>
<string name="install_title_keys_warning_description">Title keys may be required for some games to function properly.</string>
<string name="install_title_keys_warning_help">https://citron-emu.org/</string>
<string name="install_title_keys_failure_extension_description">Verify your title keys file has a .keys extension and try again.</string>
<!-- About screen strings -->
<string name="gaia_is_not_real">Gaia isn\'t real</string>
<string name="copied_to_clipboard">Copied to clipboard</string>

View File

@ -4,9 +4,9 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
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
id("com.android.application") version "8.9.0" apply false
id("com.android.library") version "8.9.0" apply false
id("org.jetbrains.kotlin.android") version "2.1.20-RC2" apply false
}
tasks.register("clean").configure {
@ -18,6 +18,6 @@ buildscript {
google()
}
dependencies {
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.6.0")
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.8.8")
}
}

View File

@ -1,13 +1,10 @@
// 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"
@ -41,22 +38,7 @@ ConfigureCpu::ConfigureCpu(const Core::System& system_,
ConfigureCpu::~ConfigureCpu() = default;
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::SetConfiguration() {}
void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
auto* accuracy_layout = ui->widget_accuracy->layout();
auto* backend_layout = ui->widget_backend->layout();
@ -117,9 +99,6 @@ 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) {

View File

@ -126,67 +126,6 @@
</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">

View File

@ -423,12 +423,6 @@
</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>
@ -436,12 +430,6 @@
</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>

View File

@ -24,7 +24,7 @@
<property name="title">
<string>citron Web Service</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutCitronWebService">
<layout class="QVBoxLayout" name="verticalLayoutYuzuWebService">
<item>
<widget class="QLabel" name="web_credentials_disclaimer">
<property name="text">
@ -33,7 +33,7 @@
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayoutCitronUsername">
<layout class="QGridLayout" name="gridLayoutYuzuUsername">
<item row="2" column="3">
<widget class="QPushButton" name="button_reset_token">
<property name="sizePolicy">

View File

@ -1,5 +1,4 @@
// 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"
@ -147,11 +146,6 @@ 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"

View File

@ -759,7 +759,6 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) {
enable &= setting.UsingGlobal();
}
this->setEnabled(enable);
this->setToolTip(tooltip);

View File

@ -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("1361252452329848892", & handlers, 1, nullptr);
Discord_Initialize("1322413013248118888", & handlers, 1, nullptr);
}
DiscordImpl::~DiscordImpl() {

View File

@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
@ -90,7 +89,6 @@ 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) \

View File

@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@ -58,7 +57,6 @@ 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

View File

@ -198,7 +198,6 @@ 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,
@ -279,8 +278,6 @@ 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,
@ -395,11 +392,11 @@ struct Values {
Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID
true,
false, // Disabled due to crashes
#else
false,
false, // Disabled due to crashes
#endif
"async_presentation", Category::RendererAdvanced};
"async_presentation", Category::RendererAdvanced}; // Hide from UI
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
Category::RendererAdvanced};
SwitchableSetting<bool> use_reactive_flushing{linkage,
@ -612,17 +609,13 @@ struct Values {
Category::Network};
// WebService
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",
Setting<bool> enable_telemetry{linkage, true, "enable_telemetry", Category::WebService};
Setting<std::string> web_api_url{linkage, "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;
};

View File

@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@ -9,7 +8,6 @@
#include <ratio>
#include "common/common_types.h"
#include "core/hardware_properties.h"
namespace Common {
@ -17,10 +15,7 @@ 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
// 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
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
virtual ~WallClock() = default;
@ -81,28 +76,12 @@ protected:
using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
// Cycle Timing - using functions for dynamic values
// Cycle Timing
// 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();
};
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>;
};
std::unique_ptr<WallClock> CreateOptimalClock();

View File

@ -1,5 +1,4 @@
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-FileCopyrightText: 2025 citron Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(core STATIC
@ -777,12 +776,8 @@ 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
@ -1050,14 +1045,6 @@ 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
@ -1066,8 +1053,6 @@ 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

View File

@ -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 <algorithm>
@ -648,17 +649,13 @@ void KeyManager::ReloadKeys() {
if (Settings::values.use_dev_keys) {
dev_mode = true;
LoadFromFile(citron_keys_dir / "dev.keys_autogenerated", false);
LoadFromFile(citron_keys_dir / "dev.keys", false);
} else {
dev_mode = false;
LoadFromFile(citron_keys_dir / "prod.keys_autogenerated", false);
LoadFromFile(citron_keys_dir / "prod.keys", false);
}
LoadFromFile(citron_keys_dir / "title.keys_autogenerated", true);
LoadFromFile(citron_keys_dir / "title.keys", true);
LoadFromFile(citron_keys_dir / "console.keys_autogenerated", false);
LoadFromFile(citron_keys_dir / "console.keys", false);
}
@ -847,87 +844,15 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
template <size_t Size>
void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key) {
const auto citron_keys_dir = Common::FS::GetCitronPath(Common::FS::CitronPath::KeysDir);
std::string filename = "title.keys_autogenerated";
if (category == KeyCategory::Standard) {
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
} else if (category == KeyCategory::Console) {
filename = "console.keys_autogenerated";
}
const auto path = citron_keys_dir / filename;
const auto add_info_text = !Common::FS::Exists(path);
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Append,
Common::FS::FileType::TextFile};
if (!file.IsOpen()) {
return;
}
if (add_info_text) {
void(file.WriteString(
"# This file is autogenerated by Citron\n"
"# It serves to store keys that were automatically generated from the normal keys\n"
"# If you are experiencing issues involving keys, it may help to delete this file\n"));
}
void(file.WriteString(fmt::format("\n{} = {}", keyname, Common::HexToString(key))));
LoadFromFile(path, category == KeyCategory::Title);
// Function is now a no-op - keys are no longer written to autogenerated files
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) {
return;
}
if (id == S128KeyType::Titlekey) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
WriteKeyToFile(KeyCategory::Title, Common::HexToString(rights_id), key);
}
auto category = KeyCategory::Standard;
if (id == S128KeyType::Keyblob || id == S128KeyType::KeyblobMAC || id == S128KeyType::TSEC ||
id == S128KeyType::SecureBoot || id == S128KeyType::SDSeed || id == S128KeyType::BIS) {
category = KeyCategory::Console;
}
const auto iter2 = std::find_if(
s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter2 != s128_file_id.end()) {
WriteKeyToFile(category, iter2->first, key);
}
// Variable cases
if (id == S128KeyType::KeyArea) {
static constexpr std::array<const char*, 3> kak_names = {
"key_area_key_application_{:02X}",
"key_area_key_ocean_{:02X}",
"key_area_key_system_{:02X}",
};
WriteKeyToFile(category, fmt::format(fmt::runtime(kak_names.at(field2)), field1), key);
} else if (id == S128KeyType::Master) {
WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
} else if (id == S128KeyType::Package1) {
WriteKeyToFile(category, fmt::format("package1_key_{:02X}", field1), key);
} else if (id == S128KeyType::Package2) {
WriteKeyToFile(category, fmt::format("package2_key_{:02X}", field1), key);
} else if (id == S128KeyType::Titlekek) {
WriteKeyToFile(category, fmt::format("titlekek_{:02X}", field1), key);
} else if (id == S128KeyType::Keyblob) {
WriteKeyToFile(category, fmt::format("keyblob_key_{:02X}", field1), key);
} else if (id == S128KeyType::KeyblobMAC) {
WriteKeyToFile(category, fmt::format("keyblob_mac_key_{:02X}", field1), key);
} else if (id == S128KeyType::Source && field1 == static_cast<u64>(SourceKeyType::Keyblob)) {
WriteKeyToFile(category, fmt::format("keyblob_key_source_{:02X}", field2), key);
}
// Store the key in memory but don't write to file
s128_keys[{id, field1, field2}] = key;
}
@ -935,14 +860,8 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) {
return;
}
const auto iter = std::find_if(
s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter != s256_file_id.end()) {
WriteKeyToFile(KeyCategory::Standard, iter->first, key);
}
// Store the key in memory but don't write to file
s256_keys[{id, field1, field2}] = key;
}
@ -1052,8 +971,6 @@ void KeyManager::DeriveBase() {
// Decrypt keyblob
if (keyblobs[i] == std::array<u8, 0x90>{}) {
keyblobs[i] = DecryptKeyblob(encrypted_keyblobs[i], key);
WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
keyblobs[i]);
}
Key128 package1;
@ -1183,7 +1100,6 @@ void KeyManager::DeriveETicket(PartitionDataManager& data,
data.DecryptProdInfo(GetBISKey(0));
eticket_extended_kek = data.GetETicketExtendedKek();
WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek);
DeriveETicketRSAKey();
PopulateTickets();
}
@ -1261,8 +1177,6 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
continue;
}
encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
encrypted_keyblobs[i]);
}
SetKeyWrapped(S128KeyType::Source, data.GetPackage2KeySource(),

View File

@ -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
#pragma once

View File

@ -9,13 +9,12 @@
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/settings.h"
namespace Core {
namespace Hardware {
inline u64 BASE_CLOCK_RATE() { return Settings::values.cpu_clock_rate.GetValue(); } // Default CPU Frequency set in settings, defaults to 1020 MHz
constexpr u64 BASE_CLOCK_RATE = 1'785'000'000; // Default CPU Frequency = 1785 MHz
constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores

View File

@ -12,7 +12,7 @@ namespace HLE::ApiVersion {
// Horizon OS version constants.
constexpr u8 HOS_VERSION_MAJOR = 20;
constexpr u8 HOS_VERSION_MAJOR = 19;
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[] = "0b2540e5cd7498dd61f6caeca5136c73d9b1d21a";
constexpr char DISPLAY_VERSION[] = "20.0.1";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 20.0.1-1.0";
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";
// Atmosphere version constants.

View File

@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
@ -82,8 +81,8 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
void Controller::SetClockSpeed(u32 mhz) {
LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz);
// Update the clock rate setting with the provided MHz value (convert to Hz)
Settings::values.cpu_clock_rate = mhz * 1'000'000;
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
// TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
}
} // namespace Service::APM

View File

@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <queue>
@ -29,12 +28,12 @@ public:
{10102, nullptr, "UpdateFriendInfo"},
{10110, nullptr, "GetFriendProfileImage"},
{10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"},
{10121, &IFriendService::EnsureFriendListAvailable, "EnsureFriendListAvailable"},
{10121, nullptr, "EnsureFriendListAvailable"},
{10200, nullptr, "SendFriendRequestForApplication"},
{10211, nullptr, "AddFacedFriendRequestForApplication"},
{10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
{10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
{10421, &IFriendService::EnsureBlockedUserListAvailable, "EnsureBlockedUserListAvailable"},
{10421, nullptr, "EnsureBlockedUserListAvailable"},
{10500, nullptr, "GetProfileList"},
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
{10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
@ -167,27 +166,11 @@ 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.
@ -203,45 +186,11 @@ 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");
@ -299,6 +248,14 @@ 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;
@ -330,9 +287,6 @@ 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());
@ -409,11 +363,10 @@ 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) {

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