mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-12 01:38:29 +12:00
Merge remote-tracking branch 'base/main' into macos-arm64
temporarily reverted CI
This commit is contained in:
commit
7120ec92c8
92 changed files with 3025 additions and 759 deletions
|
@ -15,6 +15,7 @@ BinPackArguments: true
|
|||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterExternBlock: true
|
||||
|
|
9
.github/getversion.cpp
vendored
9
.github/getversion.cpp
vendored
|
@ -1,9 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include "./../src/Common/version.h"
|
||||
|
||||
// output current Cemu version for CI workflow. Do not modify
|
||||
int main()
|
||||
{
|
||||
printf("%d.%d", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR);
|
||||
return 0;
|
||||
}
|
205
.github/workflows/build.yml
vendored
205
.github/workflows/build.yml
vendored
|
@ -3,10 +3,10 @@ name: Build Cemu
|
|||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
deploymode:
|
||||
next_version_major:
|
||||
required: false
|
||||
type: string
|
||||
experimentalversion:
|
||||
next_version_minor:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
|
@ -24,30 +24,17 @@ jobs:
|
|||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Fetch full history for vcpkg submodule"
|
||||
run: |
|
||||
cd dependencies/vcpkg
|
||||
git fetch --unshallow
|
||||
|
||||
- name: Setup release mode parameters (for deploy)
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
- name: Setup release mode parameters
|
||||
run: |
|
||||
echo "BUILD_MODE=release" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is release"
|
||||
|
||||
- name: Setup debug mode parameters (for continous build)
|
||||
if: ${{ inputs.deploymode != 'release' }}
|
||||
- name: Setup build flags for version
|
||||
if: ${{ inputs.next_version_major != '' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=debug" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is debug"
|
||||
|
||||
- name: Setup version for experimental
|
||||
if: ${{ inputs.experimentalversion != '' }}
|
||||
run: |
|
||||
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV
|
||||
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Install system dependencies"
|
||||
run: |
|
||||
|
@ -86,12 +73,10 @@ jobs:
|
|||
cmake --build build
|
||||
|
||||
- name: Prepare artifact
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: mv bin/Cemu_release bin/Cemu
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
with:
|
||||
name: cemu-bin-linux-x64
|
||||
path: ./bin/Cemu
|
||||
|
@ -133,29 +118,17 @@ jobs:
|
|||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: "Fetch full history for vcpkg submodule"
|
||||
run: |
|
||||
cd dependencies/vcpkg
|
||||
git fetch --unshallow
|
||||
|
||||
- name: Setup release mode parameters (for deploy)
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
- name: Setup release mode parameters
|
||||
run: |
|
||||
echo "BUILD_MODE=release" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "Build mode is release"
|
||||
|
||||
- name: Setup debug mode parameters (for continous build)
|
||||
if: ${{ inputs.deploymode != 'release' }}
|
||||
|
||||
- name: Setup build flags for version
|
||||
if: ${{ inputs.next_version_major != '' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=debug" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "Build mode is debug"
|
||||
- name: Setup version for experimental
|
||||
if: ${{ inputs.experimentalversion != '' }}
|
||||
run: |
|
||||
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: "Setup cmake"
|
||||
uses: jwlawson/actions-setup-cmake@v2
|
||||
|
@ -194,61 +167,50 @@ jobs:
|
|||
cmake --build . --config ${{ env.BUILD_MODE }}
|
||||
|
||||
- name: Prepare artifact
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: Rename-Item bin/Cemu_release.exe Cemu.exe
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
with:
|
||||
name: cemu-bin-windows-x64
|
||||
path: ./bin/Cemu.exe
|
||||
|
||||
build-macos-intel:
|
||||
runs-on: macos-12
|
||||
build-macos:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: "Fetch full history for vcpkg submodule"
|
||||
run: |
|
||||
cd dependencies/vcpkg
|
||||
git fetch --unshallow
|
||||
|
||||
- name: Setup release mode parameters (for deploy)
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
- name: Setup release mode parameters
|
||||
run: |
|
||||
echo "BUILD_MODE=release" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is release"
|
||||
- name: Setup debug mode parameters (for continous build)
|
||||
if: ${{ inputs.deploymode != 'release' }}
|
||||
|
||||
- name: Setup build flags for version
|
||||
if: ${{ inputs.next_version_major != '' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=debug" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is debug"
|
||||
|
||||
- name: Setup version for experimental
|
||||
if: ${{ inputs.experimentalversion != '' }}
|
||||
run: |
|
||||
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV
|
||||
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Install system dependencies"
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@15 ninja nasm automake libtool cmake python3 ninja
|
||||
brew install ninja nasm automake libtool
|
||||
|
||||
- name: "Build and install molten-vk"
|
||||
- name: "Install molten-vk"
|
||||
run: |
|
||||
git clone https://github.com/KhronosGroup/MoltenVK.git
|
||||
cd MoltenVK
|
||||
git checkout bf097edc74ec3b6dfafdcd5a38d3ce14b11952d6
|
||||
./fetchDependencies --macos
|
||||
make macos
|
||||
make install
|
||||
curl -L -O https://github.com/KhronosGroup/MoltenVK/releases/download/v1.2.9/MoltenVK-macos.tar
|
||||
tar xf MoltenVK-macos.tar
|
||||
sudo mkdir -p /usr/local/lib
|
||||
sudo cp MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib /usr/local/lib
|
||||
|
||||
- name: "Setup cmake"
|
||||
uses: jwlawson/actions-setup-cmake@v2
|
||||
with:
|
||||
cmake-version: '3.29.0'
|
||||
|
||||
- name: "Bootstrap vcpkg"
|
||||
run: |
|
||||
|
@ -274,9 +236,8 @@ jobs:
|
|||
cd build
|
||||
cmake .. ${{ env.BUILD_FLAGS }} \
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DMACOS_BUNDLE=ON \
|
||||
-DCMAKE_C_COMPILER=/usr/local/opt/llvm@15/bin/clang \
|
||||
-DCMAKE_CXX_COMPILER=/usr/local/opt/llvm@15/bin/clang++ \
|
||||
-G Ninja
|
||||
|
||||
- name: "Build Cemu"
|
||||
|
@ -284,7 +245,6 @@ jobs:
|
|||
cmake --build build
|
||||
|
||||
- name: Prepare artifact
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: |
|
||||
mkdir bin/Cemu_app
|
||||
mv bin/Cemu_release.app bin/Cemu_app/Cemu.app
|
||||
|
@ -298,105 +258,6 @@ jobs:
|
|||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
with:
|
||||
name: cemu-bin-macos-x64
|
||||
path: ./bin/Cemu.dmg
|
||||
|
||||
build-macos-arm64:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: "Fetch full history for vcpkg submodule"
|
||||
run: |
|
||||
cd dependencies/vcpkg
|
||||
git fetch --unshallow
|
||||
|
||||
- name: Setup release mode parameters (for deploy)
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=release" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is release"
|
||||
- name: Setup debug mode parameters (for continous build)
|
||||
if: ${{ inputs.deploymode != 'release' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=debug" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is debug"
|
||||
|
||||
- name: Setup version for experimental
|
||||
if: ${{ inputs.experimentalversion != '' }}
|
||||
run: |
|
||||
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Install system dependencies"
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja automake libtool cmake ninja python3
|
||||
|
||||
- name: "Build and install molten-vk"
|
||||
run: |
|
||||
git clone https://github.com/KhronosGroup/MoltenVK.git
|
||||
cd MoltenVK
|
||||
git checkout bf097edc74ec3b6dfafdcd5a38d3ce14b11952d6
|
||||
./fetchDependencies --macos
|
||||
make macos
|
||||
sudo make install
|
||||
cp /usr/local/lib/libMoltenVK.dylib /opt/homebrew/lib
|
||||
|
||||
- name: "Bootstrap vcpkg"
|
||||
run: |
|
||||
bash ./dependencies/vcpkg/bootstrap-vcpkg.sh
|
||||
|
||||
- name: 'Setup NuGet Credentials for vcpkg'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \
|
||||
sources add \
|
||||
-source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \
|
||||
-storepasswordincleartext \
|
||||
-name "GitHub" \
|
||||
-username "${{ github.repository_owner }}" \
|
||||
-password "${{ secrets.GITHUB_TOKEN }}"
|
||||
mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \
|
||||
setapikey "${{ secrets.GITHUB_TOKEN }}" \
|
||||
-source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"
|
||||
|
||||
- name: "cmake"
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. ${{ env.BUILD_FLAGS }} \
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
|
||||
-DMACOS_BUNDLE=ON \
|
||||
-G Ninja
|
||||
|
||||
- name: "Build Cemu"
|
||||
run: |
|
||||
cmake --build build
|
||||
|
||||
- name: Prepare artifact
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: |
|
||||
mkdir bin/Cemu_app
|
||||
mv bin/Cemu_release.app bin/Cemu_app/Cemu.app
|
||||
mv bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu_release bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
|
||||
sed -i '' 's/Cemu_release/Cemu/g' bin/Cemu_app/Cemu.app/Contents/Info.plist
|
||||
chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/{Cemu,update.sh}
|
||||
ln -s /Applications bin/Cemu_app/Applications
|
||||
hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app"
|
||||
hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu.dmg
|
||||
rm bin/tmp.dmg
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
with:
|
||||
name: cemu-bin-macos-arm64
|
||||
path: ./bin/Cemu.dmg
|
||||
|
|
3
.github/workflows/build_check.yml
vendored
3
.github/workflows/build_check.yml
vendored
|
@ -16,6 +16,3 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
deploymode: release
|
||||
experimentalversion: 999999
|
||||
|
|
|
@ -1,20 +1,83 @@
|
|||
name: Deploy experimental release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
changelog0:
|
||||
description: 'Enter the changelog lines for this release. Each line is a feature / bullet point. Do not use dash.'
|
||||
required: true
|
||||
type: string
|
||||
changelog1:
|
||||
description: 'Feature 2'
|
||||
required: false
|
||||
type: string
|
||||
changelog2:
|
||||
description: 'Feature 3'
|
||||
required: false
|
||||
type: string
|
||||
changelog3:
|
||||
description: 'Feature 4'
|
||||
required: false
|
||||
type: string
|
||||
changelog4:
|
||||
description: 'Feature 5'
|
||||
required: false
|
||||
type: string
|
||||
changelog5:
|
||||
description: 'Feature 6'
|
||||
required: false
|
||||
type: string
|
||||
changelog6:
|
||||
description: 'Feature 7'
|
||||
required: false
|
||||
type: string
|
||||
changelog7:
|
||||
description: 'Feature 8'
|
||||
required: false
|
||||
type: string
|
||||
changelog8:
|
||||
description: 'Feature 9'
|
||||
required: false
|
||||
type: string
|
||||
changelog9:
|
||||
description: 'Feature 10'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
calculate-version:
|
||||
name: Calculate Version
|
||||
uses: ./.github/workflows/determine_release_version.yml
|
||||
call-release-build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
needs: calculate-version
|
||||
with:
|
||||
deploymode: release
|
||||
experimentalversion: ${{ github.run_number }}
|
||||
next_version_major: ${{ needs.calculate-version.outputs.next_version_major }}
|
||||
next_version_minor: ${{ needs.calculate-version.outputs.next_version_minor }}
|
||||
deploy:
|
||||
name: Deploy experimental release
|
||||
runs-on: ubuntu-22.04
|
||||
needs: call-release-build
|
||||
needs: [call-release-build, calculate-version]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Generate changelog
|
||||
id: generate_changelog
|
||||
run: |
|
||||
CHANGELOG=""
|
||||
if [ -n "${{ github.event.inputs.changelog0 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog0 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog1 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog1 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog2 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog2 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog3 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog3 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog4 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog4 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog5 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog5 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog6 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog6 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog7 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog7 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog8 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog8 }}\n"; fi
|
||||
if [ -n "${{ github.event.inputs.changelog9 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog9 }}\n"; fi
|
||||
echo -e "$CHANGELOG"
|
||||
echo "RELEASE_BODY=$CHANGELOG" >> $GITHUB_ENV
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cemu-bin-linux-x64
|
||||
|
@ -40,15 +103,13 @@ jobs:
|
|||
mkdir upload
|
||||
sudo apt install zip
|
||||
|
||||
- name: Get version
|
||||
- name: Set version dependent vars
|
||||
run: |
|
||||
echo "Experimental version: ${{ github.run_number }}"
|
||||
ls
|
||||
gcc -o getversion .github/getversion.cpp
|
||||
./getversion
|
||||
echo "Cemu CI version: $(./getversion)"
|
||||
echo "CEMU_FOLDER_NAME=Cemu_$(./getversion)-${{ github.run_number }}" >> $GITHUB_ENV
|
||||
echo "CEMU_VERSION=$(./getversion)-${{ github.run_number }}" >> $GITHUB_ENV
|
||||
echo "Version: ${{ needs.calculate-version.outputs.next_version }}"
|
||||
echo "CEMU_FOLDER_NAME=Cemu_${{ needs.calculate-version.outputs.next_version }}"
|
||||
echo "CEMU_VERSION=${{ needs.calculate-version.outputs.next_version }}"
|
||||
echo "CEMU_FOLDER_NAME=Cemu_${{ needs.calculate-version.outputs.next_version }}" >> $GITHUB_ENV
|
||||
echo "CEMU_VERSION=${{ needs.calculate-version.outputs.next_version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Create release from windows-bin
|
||||
run: |
|
||||
|
@ -83,4 +144,8 @@ jobs:
|
|||
wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.15.0/ghr_v0.15.0_linux_amd64.tar.gz
|
||||
tar xvzf ghr.tar.gz; rm ghr.tar.gz
|
||||
echo "[INFO] Release tag: v${{ env.CEMU_VERSION }}"
|
||||
ghr_v0.15.0_linux_amd64/ghr -prerelease -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }} (Experimental)" -b "Cemu experimental release" "v${{ env.CEMU_VERSION }}" ./upload
|
||||
CHANGELOG_UNESCAPED=$(printf "%s\n" "${{ env.RELEASE_BODY }}" | sed 's/\\n/\n/g')
|
||||
RELEASE_BODY=$(printf "%s\n%s" \
|
||||
"**Changelog:**" \
|
||||
"$CHANGELOG_UNESCAPED")
|
||||
ghr_v0.15.0_linux_amd64/ghr -draft -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }}" -b "$RELEASE_BODY" "v${{ env.CEMU_VERSION }}" ./upload
|
||||
|
|
74
.github/workflows/determine_release_version.yml
vendored
Normal file
74
.github/workflows/determine_release_version.yml
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
name: Calculate Next Version from release history
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
outputs:
|
||||
next_version:
|
||||
description: "The next semantic version"
|
||||
value: ${{ jobs.calculate-version.outputs.next_version }}
|
||||
next_version_major:
|
||||
description: "The next semantic version (major)"
|
||||
value: ${{ jobs.calculate-version.outputs.next_version_major }}
|
||||
next_version_minor:
|
||||
description: "The next semantic version (minor)"
|
||||
value: ${{ jobs.calculate-version.outputs.next_version_minor }}
|
||||
|
||||
jobs:
|
||||
calculate-version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
next_version: ${{ steps.calculate_next_version.outputs.next_version }}
|
||||
next_version_major: ${{ steps.calculate_next_version.outputs.next_version_major }}
|
||||
next_version_minor: ${{ steps.calculate_next_version.outputs.next_version_minor }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get all releases
|
||||
id: get_all_releases
|
||||
run: |
|
||||
# Fetch all releases and check for API errors
|
||||
RESPONSE=$(curl -s -o response.json -w "%{http_code}" "https://api.github.com/repos/${{ github.repository }}/releases?per_page=100")
|
||||
if [ "$RESPONSE" -ne 200 ]; then
|
||||
echo "Failed to fetch releases. HTTP status: $RESPONSE"
|
||||
cat response.json
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract and sort tags
|
||||
ALL_TAGS=$(jq -r '.[].tag_name' response.json | grep -E '^v[0-9]+\.[0-9]+(-[0-9]+)?$' | sed 's/-.*//' | sort -V | tail -n 1)
|
||||
|
||||
# Exit if no tags were found
|
||||
if [ -z "$ALL_TAGS" ]; then
|
||||
echo "No valid tags found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "::set-output name=tag::$ALL_TAGS"
|
||||
# echo "tag=$ALL_TAGS" >> $GITHUB_STATE
|
||||
|
||||
- name: Calculate next semver minor
|
||||
id: calculate_next_version
|
||||
run: |
|
||||
LATEST_VERSION=${{ steps.get_all_releases.outputs.tag }}
|
||||
|
||||
# strip 'v' prefix and split into major.minor
|
||||
LATEST_VERSION=${LATEST_VERSION//v/}
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$LATEST_VERSION"
|
||||
|
||||
MAJOR=${VERSION_PARTS[0]}
|
||||
MINOR=${VERSION_PARTS[1]}
|
||||
|
||||
# increment the minor version
|
||||
MINOR=$((MINOR + 1))
|
||||
|
||||
NEXT_VERSION="${MAJOR}.${MINOR}"
|
||||
|
||||
echo "Major: $MAJOR"
|
||||
echo "Minor: $MINOR"
|
||||
|
||||
echo "Next version: $NEXT_VERSION"
|
||||
echo "::set-output name=next_version::$NEXT_VERSION"
|
||||
echo "::set-output name=next_version_major::$MAJOR"
|
||||
echo "::set-output name=next_version_minor::$MINOR"
|
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -9,10 +9,12 @@
|
|||
[submodule "dependencies/vcpkg"]
|
||||
path = dependencies/vcpkg
|
||||
url = https://github.com/microsoft/vcpkg
|
||||
shallow = true
|
||||
shallow = false
|
||||
[submodule "dependencies/Vulkan-Headers"]
|
||||
path = dependencies/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers
|
||||
shallow = true
|
||||
[submodule "dependencies/imgui"]
|
||||
path = dependencies/imgui
|
||||
url = https://github.com/ocornut/imgui
|
||||
shallow = true
|
||||
|
|
50
BUILD.md
50
BUILD.md
|
@ -16,11 +16,11 @@
|
|||
- [Compiling Errors](#compiling-errors)
|
||||
- [Building Errors](#building-errors)
|
||||
- [macOS](#macos)
|
||||
- [On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used](#on-apple-silicon-macs-rosetta-2-and-the-x86_64-version-of-homebrew-must-be-used)
|
||||
- [Installing brew](#installing-brew)
|
||||
- [Installing Dependencies](#installing-dependencies)
|
||||
- [Build Cemu using CMake and Clang](#build-cemu-using-cmake-and-clang)
|
||||
- [Updating Cemu and source code](#updating-cemu-and-source-code)
|
||||
- [Installing Tool Dependencies](#installing-tool-dependencies)
|
||||
- [Installing Library Dependencies](#installing-library-dependencies)
|
||||
- [Build Cemu using CMake](#build-cemu-using-cmake)
|
||||
- [Updating Cemu and source code](#updating-cemu-and-source-code)
|
||||
|
||||
## Windows
|
||||
|
||||
|
@ -141,31 +141,41 @@ If you are getting a different error than any of the errors listed above, you ma
|
|||
|
||||
## macOS
|
||||
|
||||
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and
|
||||
below, built in LLVM, and Xcode LLVM don't support the C++20 feature set required. The OpenGL graphics
|
||||
API isn't support on macOS, Vulkan must be used. Additionally Vulkan must be used through the
|
||||
Molten-VK compatibility layer
|
||||
|
||||
### On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used
|
||||
|
||||
You can skip this section if you have an Intel Mac. Every time you compile, you need to perform steps 2.
|
||||
|
||||
1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once
|
||||
2. `arch -x86_64 zsh` # run an x64 shell
|
||||
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and below
|
||||
don't support the C++20 feature set required, so either install LLVM from Homebrew or make sure that
|
||||
you have a recent enough version of Xcode. Xcode 15 is known to work. The OpenGL graphics API isn't
|
||||
supported on macOS, so Vulkan must be used through the Molten-VK compatibility layer.
|
||||
|
||||
### Installing brew
|
||||
|
||||
1. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
|
||||
2. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"` # set x86_64 brew env
|
||||
2. Set up the Homebrew shell environment:
|
||||
1. **On an Intel Mac:** `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
|
||||
2. **On an Apple Silicon Mac:** eval `"$(/opt/homebrew/bin/brew shellenv)"`
|
||||
|
||||
### Installing Dependencies
|
||||
### Installing Tool Dependencies
|
||||
|
||||
`brew install boost git cmake llvm ninja nasm molten-vk automake libtool`
|
||||
The native versions of these can be used regardless of what type of Mac you have.
|
||||
|
||||
`brew install git cmake ninja nasm automake libtool`
|
||||
|
||||
### Installing Library Dependencies
|
||||
|
||||
**On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used to install these dependencies:**
|
||||
1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once
|
||||
2. `arch -x86_64 zsh` # run an x64 shell
|
||||
3. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
|
||||
4. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
|
||||
|
||||
Then install the dependencies:
|
||||
|
||||
`brew install boost molten-vk`
|
||||
|
||||
### Build Cemu using CMake
|
||||
|
||||
### Build Cemu using CMake and Clang
|
||||
1. `git clone --recursive https://github.com/cemu-project/Cemu`
|
||||
2. `cd Cemu`
|
||||
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/local/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/opt/llvm/bin/clang++ -G Ninja`
|
||||
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_OSX_ARCHITECTURES=x86_64 -G Ninja`
|
||||
4. `cmake --build build`
|
||||
5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`.
|
||||
|
||||
|
|
|
@ -2,20 +2,39 @@ cmake_minimum_required(VERSION 3.21.1)
|
|||
|
||||
option(ENABLE_VCPKG "Enable the vcpkg package manager" ON)
|
||||
option(MACOS_BUNDLE "The executable when built on macOS will be created as an application bundle" OFF)
|
||||
set(EXPERIMENTAL_VERSION "" CACHE STRING "") # used by CI script to set experimental version
|
||||
|
||||
if (EXPERIMENTAL_VERSION)
|
||||
add_definitions(-DEMULATOR_VERSION_MINOR=${EXPERIMENTAL_VERSION})
|
||||
execute_process(
|
||||
COMMAND git log --format=%h -1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
|
||||
endif()
|
||||
# used by CI script to set version:
|
||||
set(EMULATOR_VERSION_MAJOR "0" CACHE STRING "")
|
||||
set(EMULATOR_VERSION_MINOR "0" CACHE STRING "")
|
||||
set(EMULATOR_VERSION_PATCH "0" CACHE STRING "")
|
||||
|
||||
execute_process(
|
||||
COMMAND git log --format=%h -1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
|
||||
|
||||
if (ENABLE_VCPKG)
|
||||
# check if vcpkg is shallow and unshallow it if necessary
|
||||
execute_process(
|
||||
COMMAND git rev-parse --is-shallow-repository
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/dependencies/vcpkg
|
||||
OUTPUT_VARIABLE is_vcpkg_shallow
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(is_vcpkg_shallow STREQUAL "true")
|
||||
message(STATUS "vcpkg is shallow. Unshallowing it now...")
|
||||
execute_process(
|
||||
COMMAND git fetch --unshallow
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/dependencies/vcpkg"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux")
|
||||
elseif(APPLE)
|
||||
|
@ -44,6 +63,10 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|||
|
||||
add_compile_definitions($<$<CONFIG:Debug>:CEMU_DEBUG_ASSERT>) # if build type is debug, set CEMU_DEBUG_ASSERT
|
||||
|
||||
add_definitions(-DEMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR})
|
||||
add_definitions(-DEMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR})
|
||||
add_definitions(-DEMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH})
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# enable link time optimization for release builds
|
||||
|
@ -69,6 +92,7 @@ endif()
|
|||
|
||||
if (APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0")
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
|
@ -198,7 +222,7 @@ if (ENABLE_CUBEB)
|
|||
option(BUILD_TOOLS "" OFF)
|
||||
option(BUNDLE_SPEEX "" OFF)
|
||||
set(USE_WINMM OFF CACHE BOOL "")
|
||||
add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL)
|
||||
add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL SYSTEM)
|
||||
set_property(TARGET cubeb PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
add_library(cubeb::cubeb ALIAS cubeb)
|
||||
endif()
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
If you plan to transfer the shader cache to a different PC or Cemu installation you only need to copy the 'transferable' directory.
|
12
dependencies/ih264d/CMakeLists.txt
vendored
12
dependencies/ih264d/CMakeLists.txt
vendored
|
@ -117,7 +117,13 @@ add_library (ih264d
|
|||
"decoder/ivd.h"
|
||||
)
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
|
||||
if (CMAKE_OSX_ARCHITECTURES)
|
||||
set(IH264D_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
|
||||
else()
|
||||
set(IH264D_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
|
||||
if (IH264D_ARCHITECTURE STREQUAL "x86_64" OR IH264D_ARCHITECTURE STREQUAL "amd64" OR IH264D_ARCHITECTURE STREQUAL "AMD64")
|
||||
set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
|
||||
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
|
||||
target_sources(ih264d PRIVATE
|
||||
|
@ -140,7 +146,7 @@ target_sources(ih264d PRIVATE
|
|||
"decoder/x86/ih264d_function_selector_sse42.c"
|
||||
"decoder/x86/ih264d_function_selector_ssse3.c"
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
elseif(IH264D_ARCHITECTURE STREQUAL "aarch64" OR IH264D_ARCHITECTURE STREQUAL "arm64")
|
||||
enable_language( C CXX ASM )
|
||||
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
|
||||
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
|
||||
|
@ -178,7 +184,7 @@ target_sources(ih264d PRIVATE
|
|||
)
|
||||
target_compile_options(ih264d PRIVATE -DARMV8)
|
||||
else()
|
||||
message(FATAL_ERROR "ih264d unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
|
|
6
dist/linux/appimage.sh
vendored
6
dist/linux/appimage.sh
vendored
|
@ -10,6 +10,8 @@ curl -sSfL https://github.com"$(curl https://github.com/probonopd/go-appimage/re
|
|||
chmod a+x mkappimage.AppImage
|
||||
curl -sSfLO "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh"
|
||||
chmod a+x linuxdeploy-plugin-gtk.sh
|
||||
curl -sSfLO "https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh"
|
||||
chmod a+x linuxdeploy-plugin-checkrt.sh
|
||||
|
||||
if [[ ! -e /usr/lib/x86_64-linux-gnu ]]; then
|
||||
sed -i 's#lib\/x86_64-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh
|
||||
|
@ -39,7 +41,8 @@ export NO_STRIP=1
|
|||
-d "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.desktop \
|
||||
-i "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.png \
|
||||
-e "${GITHUB_WORKSPACE}"/AppDir/usr/bin/Cemu \
|
||||
--plugin gtk
|
||||
--plugin gtk \
|
||||
--plugin checkrt
|
||||
|
||||
if ! GITVERSION="$(git rev-parse --short HEAD 2>/dev/null)"; then
|
||||
GITVERSION=experimental
|
||||
|
@ -47,7 +50,6 @@ fi
|
|||
echo "Cemu Version Cemu-${GITVERSION}"
|
||||
|
||||
rm AppDir/usr/lib/libwayland-client.so.0
|
||||
cp /lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/lib/
|
||||
echo -e "export LC_ALL=C\nexport FONTCONFIG_PATH=/etc/fonts" >> AppDir/apprun-hooks/linuxdeploy-plugin-gtk.sh
|
||||
VERSION="${GITVERSION}" ./mkappimage.AppImage --appimage-extract-and-run "${GITHUB_WORKSPACE}"/AppDir
|
||||
|
||||
|
|
|
@ -101,12 +101,18 @@ if (MACOS_BUNDLE)
|
|||
COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory "${CMAKE_SOURCE_DIR}/bin/${folder}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/SharedSupport/${folder}")
|
||||
endforeach(folder)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/debug/lib/libusb-1.0.0.dylib")
|
||||
else()
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/lib/libusb-1.0.0.dylib")
|
||||
endif()
|
||||
|
||||
add_custom_command (TARGET CemuBin POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/lib/libusb-1.0.0.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${LIBUSB_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_SOURCE_DIR}/src/resource/update.sh" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/update.sh"
|
||||
COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}"
|
||||
COMMAND bash -c "install_name_tool -change /Users/runner/work/Cemu/Cemu/build/vcpkg_installed/x64-osx/lib/libusb-1.0.0.dylib @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
|
||||
COMMAND bash -c "install_name_tool -change ${LIBUSB_PATH} @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
|
||||
endif()
|
||||
|
||||
set_target_properties(CemuBin PROPERTIES
|
||||
|
|
|
@ -465,6 +465,8 @@ add_library(CemuCafe
|
|||
OS/libs/nsyshid/BackendLibusb.h
|
||||
OS/libs/nsyshid/BackendWindowsHID.cpp
|
||||
OS/libs/nsyshid/BackendWindowsHID.h
|
||||
OS/libs/nsyshid/Dimensions.cpp
|
||||
OS/libs/nsyshid/Dimensions.h
|
||||
OS/libs/nsyshid/Infinity.cpp
|
||||
OS/libs/nsyshid/Infinity.h
|
||||
OS/libs/nsyshid/Skylander.cpp
|
||||
|
|
|
@ -396,7 +396,7 @@ void cemu_initForGame()
|
|||
// replace any known function signatures with our HLE implementations and patch bugs in the games
|
||||
GamePatch_scan();
|
||||
}
|
||||
LatteGPUState.alwaysDisplayDRC = ActiveSettings::DisplayDRCEnabled();
|
||||
LatteGPUState.isDRCPrimary = ActiveSettings::DisplayDRCEnabled();
|
||||
InfoLog_PrintActiveSettings();
|
||||
Latte_Start();
|
||||
// check for debugger entrypoint bp
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "../fsc.h"
|
||||
|
||||
// path parser and utility class for Wii U paths
|
||||
// optimized to be allocation-free for common path lengths
|
||||
class FSCPath
|
||||
|
@ -119,9 +121,7 @@ public:
|
|||
template<typename F>
|
||||
class FSAFileTree
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
enum NODETYPE : uint8
|
||||
{
|
||||
|
@ -133,6 +133,7 @@ private:
|
|||
{
|
||||
std::string name;
|
||||
std::vector<node_t*> subnodes;
|
||||
size_t fileSize;
|
||||
F* custom;
|
||||
NODETYPE type;
|
||||
};
|
||||
|
@ -179,13 +180,54 @@ private:
|
|||
return newNode;
|
||||
}
|
||||
|
||||
class DirectoryIterator : public FSCVirtualFile
|
||||
{
|
||||
public:
|
||||
DirectoryIterator(node_t* node)
|
||||
: m_node(node), m_subnodeIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
sint32 fscGetType() override
|
||||
{
|
||||
return FSC_TYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
bool fscDirNext(FSCDirEntry* dirEntry) override
|
||||
{
|
||||
if (m_subnodeIndex >= m_node->subnodes.size())
|
||||
return false;
|
||||
|
||||
const node_t* subnode = m_node->subnodes[m_subnodeIndex];
|
||||
|
||||
strncpy(dirEntry->path, subnode->name.c_str(), sizeof(dirEntry->path) - 1);
|
||||
dirEntry->path[sizeof(dirEntry->path) - 1] = '\0';
|
||||
dirEntry->isDirectory = subnode->type == FSAFileTree::NODETYPE_DIRECTORY;
|
||||
dirEntry->isFile = subnode->type == FSAFileTree::NODETYPE_FILE;
|
||||
dirEntry->fileSize = subnode->type == FSAFileTree::NODETYPE_FILE ? subnode->fileSize : 0;
|
||||
|
||||
++m_subnodeIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fscRewindDir() override
|
||||
{
|
||||
m_subnodeIndex = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
node_t* m_node;
|
||||
size_t m_subnodeIndex;
|
||||
};
|
||||
|
||||
public:
|
||||
FSAFileTree()
|
||||
{
|
||||
rootNode.type = NODETYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
bool addFile(std::string_view path, F* custom)
|
||||
bool addFile(std::string_view path, size_t fileSize, F* custom)
|
||||
{
|
||||
FSCPath p(path);
|
||||
if (p.GetNodeCount() == 0)
|
||||
|
@ -196,6 +238,7 @@ public:
|
|||
return false; // node already exists
|
||||
// add file node
|
||||
node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.GetNodeName(p.GetNodeCount() - 1));
|
||||
fileNode->fileSize = fileSize;
|
||||
fileNode->custom = custom;
|
||||
return true;
|
||||
}
|
||||
|
@ -214,6 +257,20 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool getDirectory(std::string_view path, FSCVirtualFile*& dirIterator)
|
||||
{
|
||||
FSCPath p(path);
|
||||
if (p.GetNodeCount() == 0)
|
||||
return false;
|
||||
node_t* node = getByNodePath(p, p.GetNodeCount(), false);
|
||||
if (node == nullptr)
|
||||
return false;
|
||||
if (node->type != NODETYPE_DIRECTORY)
|
||||
return false;
|
||||
dirIterator = new DirectoryIterator(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeFile(std::string_view path)
|
||||
{
|
||||
FSCPath p(path);
|
||||
|
|
|
@ -212,4 +212,4 @@ bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTarg
|
|||
|
||||
// redirect device
|
||||
void fscDeviceRedirect_map();
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority);
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority);
|
||||
|
|
|
@ -11,7 +11,7 @@ struct RedirectEntry
|
|||
|
||||
FSAFileTree<RedirectEntry> redirectTree;
|
||||
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority)
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority)
|
||||
{
|
||||
// check if source already has a redirection
|
||||
RedirectEntry* existingEntry;
|
||||
|
@ -24,7 +24,7 @@ void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& t
|
|||
delete existingEntry;
|
||||
}
|
||||
RedirectEntry* entry = new RedirectEntry(targetFilePath, priority);
|
||||
redirectTree.addFile(virtualSourcePath, entry);
|
||||
redirectTree.addFile(virtualSourcePath, fileSize, entry);
|
||||
}
|
||||
|
||||
class fscDeviceTypeRedirect : public fscDeviceC
|
||||
|
@ -32,8 +32,15 @@ class fscDeviceTypeRedirect : public fscDeviceC
|
|||
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
||||
{
|
||||
RedirectEntry* redirectionEntry;
|
||||
if (redirectTree.getFile(path, redirectionEntry))
|
||||
|
||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) && redirectTree.getFile(path, redirectionEntry))
|
||||
return FSCVirtualFile_Host::OpenFile(redirectionEntry->dstPath, accessFlags, *fscStatus);
|
||||
|
||||
FSCVirtualFile* dirIterator;
|
||||
|
||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR) && redirectTree.getDirectory(path, dirIterator))
|
||||
return dirIterator;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -830,7 +830,7 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC
|
|||
{
|
||||
virtualMountPath = fs::path("vol/content/") / virtualMountPath;
|
||||
}
|
||||
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.path().generic_string(), m_fs_priority);
|
||||
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.file_size(), it.path().generic_string(), m_fs_priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ struct LatteGPUState_t
|
|||
uint32 gx2InitCalled; // incremented every time GX2Init() is called
|
||||
// OpenGL control
|
||||
uint32 glVendor; // GLVENDOR_*
|
||||
bool alwaysDisplayDRC = false;
|
||||
bool isDRCPrimary = false;
|
||||
// temporary (replace with proper solution later)
|
||||
bool tvBufferUsesSRGB;
|
||||
bool drcBufferUsesSRGB;
|
||||
|
|
|
@ -989,8 +989,6 @@ void LatteRenderTarget_copyToBackbuffer(LatteTextureView* textureView, bool isPa
|
|||
g_renderer->ImguiEnd();
|
||||
}
|
||||
|
||||
bool ctrlTabHotkeyPressed = false;
|
||||
|
||||
void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uint32 colorBufferWidth, uint32 colorBufferHeight, uint32 colorBufferSliceIndex, uint32 colorBufferFormat, uint32 colorBufferPitch, Latte::E_HWTILEMODE colorBufferTilemode, uint32 colorBufferSwizzle, uint32 renderTarget)
|
||||
{
|
||||
cemu_assert_debug(colorBufferSliceIndex == 0); // todo - support for non-zero slice
|
||||
|
@ -1000,38 +998,31 @@ void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uin
|
|||
return;
|
||||
}
|
||||
|
||||
auto getVPADScreenActive = [](size_t n) -> std::pair<bool, bool> {
|
||||
auto controller = InputManager::instance().get_vpad_controller(n);
|
||||
if (!controller)
|
||||
return {false,false};
|
||||
auto pressed = controller->is_screen_active();
|
||||
auto toggle = controller->is_screen_active_toggle();
|
||||
return {pressed && !toggle, pressed && toggle};
|
||||
};
|
||||
|
||||
const bool tabPressed = gui_isKeyDown(PlatformKeyCodes::TAB);
|
||||
const bool ctrlPressed = gui_isKeyDown(PlatformKeyCodes::LCONTROL);
|
||||
const auto [vpad0Active, vpad0Toggle] = getVPADScreenActive(0);
|
||||
const auto [vpad1Active, vpad1Toggle] = getVPADScreenActive(1);
|
||||
|
||||
bool showDRC = swkbd_hasKeyboardInputHook() == false && tabPressed;
|
||||
bool& alwaysDisplayDRC = LatteGPUState.alwaysDisplayDRC;
|
||||
const bool altScreenRequested = (!ctrlPressed && tabPressed) || vpad0Active || vpad1Active;
|
||||
const bool togglePressed = (ctrlPressed && tabPressed) || vpad0Toggle || vpad1Toggle;
|
||||
static bool togglePressedLast = false;
|
||||
|
||||
if (ctrlPressed && tabPressed)
|
||||
{
|
||||
if (ctrlTabHotkeyPressed == false)
|
||||
{
|
||||
alwaysDisplayDRC = !alwaysDisplayDRC;
|
||||
ctrlTabHotkeyPressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
ctrlTabHotkeyPressed = false;
|
||||
bool& isDRCPrimary = LatteGPUState.isDRCPrimary;
|
||||
|
||||
if (alwaysDisplayDRC)
|
||||
showDRC = !tabPressed;
|
||||
if(togglePressed && !togglePressedLast)
|
||||
isDRCPrimary = !isDRCPrimary;
|
||||
togglePressedLast = togglePressed;
|
||||
|
||||
if (!showDRC)
|
||||
{
|
||||
auto controller = InputManager::instance().get_vpad_controller(0);
|
||||
if (controller && controller->is_screen_active())
|
||||
showDRC = true;
|
||||
if (!showDRC)
|
||||
{
|
||||
controller = InputManager::instance().get_vpad_controller(1);
|
||||
if (controller && controller->is_screen_active())
|
||||
showDRC = true;
|
||||
}
|
||||
}
|
||||
bool showDRC = swkbd_hasKeyboardInputHook() == false && (isDRCPrimary ^ altScreenRequested);
|
||||
|
||||
if ((renderTarget & RENDER_TARGET_DRC) && g_renderer->IsPadWindowActive())
|
||||
LatteRenderTarget_copyToBackbuffer(texView, true);
|
||||
|
|
|
@ -524,7 +524,7 @@ void LatteSHRC_UpdateGSBaseHash(uint8* geometryShaderPtr, uint32 geometryShaderS
|
|||
// update hash from geometry shader data
|
||||
uint64 gsHash1 = 0;
|
||||
uint64 gsHash2 = 0;
|
||||
_calculateShaderProgramHash((uint32*)geometryShaderPtr, geometryShaderSize, &hashCacheVS, &gsHash1, &gsHash2);
|
||||
_calculateShaderProgramHash((uint32*)geometryShaderPtr, geometryShaderSize, &hashCacheGS, &gsHash1, &gsHash2);
|
||||
// get geometry shader
|
||||
uint64 gsHash = gsHash1 + gsHash2;
|
||||
gsHash += (uint64)_activeVertexShader->ringParameterCount;
|
||||
|
|
|
@ -12,9 +12,9 @@ uint32 RendererShader::GeneratePrecompiledCacheId()
|
|||
v += (uint32)(*s);
|
||||
s++;
|
||||
}
|
||||
v += (EMULATOR_VERSION_LEAD * 1000000u);
|
||||
v += (EMULATOR_VERSION_MAJOR * 10000u);
|
||||
v += (EMULATOR_VERSION_MINOR * 100u);
|
||||
v += (EMULATOR_VERSION_MAJOR * 1000000u);
|
||||
v += (EMULATOR_VERSION_MINOR * 10000u);
|
||||
v += (EMULATOR_VERSION_PATCH * 100u);
|
||||
|
||||
// settings that can influence shaders
|
||||
v += (uint32)g_current_game_profile->GetAccurateShaderMul() * 133;
|
||||
|
|
|
@ -146,8 +146,17 @@ void SwapchainInfoVk::Create()
|
|||
UnrecoverableError("Failed to create semaphore for swapchain acquire");
|
||||
}
|
||||
|
||||
VkFenceCreateInfo fenceInfo = {};
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
result = vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence);
|
||||
if (result != VK_SUCCESS)
|
||||
UnrecoverableError("Failed to create fence for swapchain");
|
||||
|
||||
m_acquireIndex = 0;
|
||||
hasDefinedSwapchainImage = false;
|
||||
|
||||
m_queueDepth = 0;
|
||||
}
|
||||
|
||||
void SwapchainInfoVk::Cleanup()
|
||||
|
@ -177,6 +186,12 @@ void SwapchainInfoVk::Cleanup()
|
|||
m_swapchainFramebuffers.clear();
|
||||
|
||||
|
||||
if (m_imageAvailableFence)
|
||||
{
|
||||
WaitAvailableFence();
|
||||
vkDestroyFence(m_logicalDevice, m_imageAvailableFence, nullptr);
|
||||
m_imageAvailableFence = nullptr;
|
||||
}
|
||||
if (m_swapchain)
|
||||
{
|
||||
vkDestroySwapchainKHR(m_logicalDevice, m_swapchain, nullptr);
|
||||
|
@ -189,6 +204,18 @@ bool SwapchainInfoVk::IsValid() const
|
|||
return m_swapchain && !m_acquireSemaphores.empty();
|
||||
}
|
||||
|
||||
void SwapchainInfoVk::WaitAvailableFence()
|
||||
{
|
||||
if(m_awaitableFence != VK_NULL_HANDLE)
|
||||
vkWaitForFences(m_logicalDevice, 1, &m_awaitableFence, VK_TRUE, UINT64_MAX);
|
||||
m_awaitableFence = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void SwapchainInfoVk::ResetAvailableFence() const
|
||||
{
|
||||
vkResetFences(m_logicalDevice, 1, &m_imageAvailableFence);
|
||||
}
|
||||
|
||||
VkSemaphore SwapchainInfoVk::ConsumeAcquireSemaphore()
|
||||
{
|
||||
VkSemaphore ret = m_currentSemaphore;
|
||||
|
@ -198,8 +225,10 @@ VkSemaphore SwapchainInfoVk::ConsumeAcquireSemaphore()
|
|||
|
||||
bool SwapchainInfoVk::AcquireImage()
|
||||
{
|
||||
ResetAvailableFence();
|
||||
|
||||
VkSemaphore acquireSemaphore = m_acquireSemaphores[m_acquireIndex];
|
||||
VkResult result = vkAcquireNextImageKHR(m_logicalDevice, m_swapchain, 1'000'000'000, acquireSemaphore, nullptr, &swapchainImageIndex);
|
||||
VkResult result = vkAcquireNextImageKHR(m_logicalDevice, m_swapchain, 1'000'000'000, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex);
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
|
||||
m_shouldRecreate = true;
|
||||
if (result == VK_TIMEOUT)
|
||||
|
@ -216,6 +245,7 @@ bool SwapchainInfoVk::AcquireImage()
|
|||
return false;
|
||||
}
|
||||
m_currentSemaphore = acquireSemaphore;
|
||||
m_awaitableFence = m_imageAvailableFence;
|
||||
m_acquireIndex = (m_acquireIndex + 1) % m_swapchainImages.size();
|
||||
|
||||
return true;
|
||||
|
@ -319,6 +349,7 @@ VkExtent2D SwapchainInfoVk::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& cap
|
|||
|
||||
VkPresentModeKHR SwapchainInfoVk::ChoosePresentMode(const std::vector<VkPresentModeKHR>& modes)
|
||||
{
|
||||
m_maxQueued = 0;
|
||||
const auto vsyncState = (VSync)GetConfig().vsync.GetValue();
|
||||
if (vsyncState == VSync::MAILBOX)
|
||||
{
|
||||
|
@ -345,6 +376,7 @@ VkPresentModeKHR SwapchainInfoVk::ChoosePresentMode(const std::vector<VkPresentM
|
|||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
||||
|
||||
m_maxQueued = 1;
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@ struct SwapchainInfoVk
|
|||
|
||||
bool IsValid() const;
|
||||
|
||||
void WaitAvailableFence();
|
||||
void ResetAvailableFence() const;
|
||||
|
||||
bool AcquireImage();
|
||||
// retrieve semaphore of last acquire for submitting a wait operation
|
||||
// only one wait operation must be submitted per acquire (which submits a single signal operation)
|
||||
|
@ -68,6 +71,9 @@ struct SwapchainInfoVk
|
|||
VkSwapchainKHR m_swapchain{};
|
||||
Vector2i m_desiredExtent{};
|
||||
uint32 swapchainImageIndex = (uint32)-1;
|
||||
uint64 m_presentId = 1;
|
||||
uint64 m_queueDepth = 0; // number of frames with pending presentation requests
|
||||
uint64 m_maxQueued = 0; // the maximum number of frames with presentation requests.
|
||||
|
||||
|
||||
// swapchain image ringbuffer (indexed by swapchainImageIndex)
|
||||
|
@ -81,6 +87,8 @@ struct SwapchainInfoVk
|
|||
private:
|
||||
uint32 m_acquireIndex = 0;
|
||||
std::vector<VkSemaphore> m_acquireSemaphores; // indexed by m_acquireIndex
|
||||
VkFence m_imageAvailableFence{};
|
||||
VkFence m_awaitableFence = VK_NULL_HANDLE;
|
||||
VkSemaphore m_currentSemaphore = VK_NULL_HANDLE;
|
||||
|
||||
std::array<uint32, 2> m_swapchainQueueFamilyIndices;
|
||||
|
|
|
@ -188,6 +188,9 @@ VKFUNC_DEVICE(vkCmdPipelineBarrier2KHR);
|
|||
VKFUNC_DEVICE(vkCmdBeginRenderingKHR);
|
||||
VKFUNC_DEVICE(vkCmdEndRenderingKHR);
|
||||
|
||||
// khr_present_wait
|
||||
VKFUNC_DEVICE(vkWaitForPresentKHR);
|
||||
|
||||
// transform feedback extension
|
||||
VKFUNC_DEVICE(vkCmdBindTransformFeedbackBuffersEXT);
|
||||
VKFUNC_DEVICE(vkCmdBeginTransformFeedbackEXT);
|
||||
|
|
|
@ -826,7 +826,7 @@ void PipelineCompiler::InitDepthStencilState()
|
|||
|
||||
depthStencilState.front.reference = stencilRefFront;
|
||||
depthStencilState.front.compareMask = stencilCompareMaskFront;
|
||||
depthStencilState.front.writeMask = stencilWriteMaskBack;
|
||||
depthStencilState.front.writeMask = stencilWriteMaskFront;
|
||||
depthStencilState.front.compareOp = vkDepthCompareTable[(size_t)frontStencilFunc];
|
||||
depthStencilState.front.depthFailOp = stencilOpTable[(size_t)frontStencilZFail];
|
||||
depthStencilState.front.failOp = stencilOpTable[(size_t)frontStencilFail];
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteOverlay.h"
|
||||
|
||||
#include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h"
|
||||
|
||||
|
@ -29,6 +30,7 @@
|
|||
#include <glslang/Public/ShaderLang.h>
|
||||
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/intl.h> // for localization
|
||||
|
||||
#ifndef VK_API_VERSION_MAJOR
|
||||
#define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU)
|
||||
|
@ -45,7 +47,9 @@ const std::vector<const char*> kOptionalDeviceExtensions =
|
|||
VK_EXT_FILTER_CUBIC_EXTENSION_NAME, // not supported by any device yet
|
||||
VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
|
||||
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
|
||||
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME
|
||||
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
|
||||
VK_KHR_PRESENT_WAIT_EXTENSION_NAME,
|
||||
VK_KHR_PRESENT_ID_EXTENSION_NAME
|
||||
};
|
||||
|
||||
const std::vector<const char*> kRequiredDeviceExtensions =
|
||||
|
@ -123,7 +127,7 @@ std::vector<VulkanRenderer::DeviceInfo> VulkanRenderer::GetDevices()
|
|||
VkApplicationInfo app_info{};
|
||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
app_info.pApplicationName = EMULATOR_NAME;
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR);
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH);
|
||||
app_info.pEngineName = EMULATOR_NAME;
|
||||
app_info.engineVersion = app_info.applicationVersion;
|
||||
app_info.apiVersion = apiVersion;
|
||||
|
@ -250,12 +254,24 @@ void VulkanRenderer::GetDeviceFeatures()
|
|||
pcc.pNext = prevStruct;
|
||||
prevStruct = &pcc;
|
||||
|
||||
VkPhysicalDevicePresentIdFeaturesKHR pidf{};
|
||||
pidf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR;
|
||||
pidf.pNext = prevStruct;
|
||||
prevStruct = &pidf;
|
||||
|
||||
VkPhysicalDevicePresentWaitFeaturesKHR pwf{};
|
||||
pwf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR;
|
||||
pwf.pNext = prevStruct;
|
||||
prevStruct = &pwf;
|
||||
|
||||
VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{};
|
||||
physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
physicalDeviceFeatures2.pNext = prevStruct;
|
||||
|
||||
vkGetPhysicalDeviceFeatures2(m_physicalDevice, &physicalDeviceFeatures2);
|
||||
|
||||
cemuLog_log(LogType::Force, "Vulkan: present_wait extension: {}", (pwf.presentWait && pidf.presentId) ? "supported" : "unsupported");
|
||||
|
||||
/* Get Vulkan device properties and limits */
|
||||
VkPhysicalDeviceFloatControlsPropertiesKHR pfcp{};
|
||||
prevStruct = nullptr;
|
||||
|
@ -285,7 +301,7 @@ void VulkanRenderer::GetDeviceFeatures()
|
|||
cemuLog_log(LogType::Force, "VK_EXT_pipeline_creation_cache_control not supported. Cannot use asynchronous shader and pipeline compilation");
|
||||
// if async shader compilation is enabled show warning message
|
||||
if (GetConfig().async_compile)
|
||||
wxMessageBox(_("The currently installed graphics driver does not support the Vulkan extension necessary for asynchronous shader compilation. Asynchronous compilation cannot be used.\n \nRequired extension: VK_EXT_pipeline_creation_cache_control\n\nInstalling the latest graphics driver may solve this error."), _("Information"), wxOK | wxCENTRE);
|
||||
LatteOverlay_pushNotification(_("Async shader compile is enabled but not supported by the graphics driver\nCemu will use synchronous compilation which can cause additional stutter").utf8_string(), 10000);
|
||||
}
|
||||
if (!m_featureControl.deviceExtensions.custom_border_color_without_format)
|
||||
{
|
||||
|
@ -337,7 +353,7 @@ VulkanRenderer::VulkanRenderer()
|
|||
VkApplicationInfo app_info{};
|
||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
app_info.pApplicationName = EMULATOR_NAME;
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR);
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH);
|
||||
app_info.pEngineName = EMULATOR_NAME;
|
||||
app_info.engineVersion = app_info.applicationVersion;
|
||||
app_info.apiVersion = apiVersion;
|
||||
|
@ -488,6 +504,24 @@ VulkanRenderer::VulkanRenderer()
|
|||
customBorderColorFeature.customBorderColors = VK_TRUE;
|
||||
customBorderColorFeature.customBorderColorWithoutFormat = VK_TRUE;
|
||||
}
|
||||
// enable VK_KHR_present_id
|
||||
VkPhysicalDevicePresentIdFeaturesKHR presentIdFeature{};
|
||||
if(m_featureControl.deviceExtensions.present_wait)
|
||||
{
|
||||
presentIdFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR;
|
||||
presentIdFeature.pNext = deviceExtensionFeatures;
|
||||
deviceExtensionFeatures = &presentIdFeature;
|
||||
presentIdFeature.presentId = VK_TRUE;
|
||||
}
|
||||
// enable VK_KHR_present_wait
|
||||
VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeature{};
|
||||
if(m_featureControl.deviceExtensions.present_wait)
|
||||
{
|
||||
presentWaitFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR;
|
||||
presentWaitFeature.pNext = deviceExtensionFeatures;
|
||||
deviceExtensionFeatures = &presentWaitFeature;
|
||||
presentWaitFeature.presentWait = VK_TRUE;
|
||||
}
|
||||
|
||||
std::vector<const char*> used_extensions;
|
||||
VkDeviceCreateInfo createInfo = CreateDeviceCreateInfo(queueCreateInfos, deviceFeatures, deviceExtensionFeatures, used_extensions);
|
||||
|
@ -1045,6 +1079,10 @@ VkDeviceCreateInfo VulkanRenderer::CreateDeviceCreateInfo(const std::vector<VkDe
|
|||
used_extensions.emplace_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
|
||||
if (m_featureControl.deviceExtensions.shader_float_controls)
|
||||
used_extensions.emplace_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
if (m_featureControl.deviceExtensions.present_wait)
|
||||
used_extensions.emplace_back(VK_KHR_PRESENT_ID_EXTENSION_NAME);
|
||||
if (m_featureControl.deviceExtensions.present_wait)
|
||||
used_extensions.emplace_back(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);
|
||||
|
||||
VkDeviceCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
|
@ -1142,6 +1180,7 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device,
|
|||
info.deviceExtensions.shader_float_controls = isExtensionAvailable(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
info.deviceExtensions.dynamic_rendering = false; // isExtensionAvailable(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
|
||||
// dynamic rendering doesn't provide any benefits for us right now. Driver implementations are very unoptimized as of Feb 2022
|
||||
info.deviceExtensions.present_wait = isExtensionAvailable(VK_KHR_PRESENT_WAIT_EXTENSION_NAME) && isExtensionAvailable(VK_KHR_PRESENT_ID_EXTENSION_NAME);
|
||||
|
||||
// check for framedebuggers
|
||||
info.debugMarkersSupported = false;
|
||||
|
@ -1853,6 +1892,7 @@ void VulkanRenderer::ProcessFinishedCommandBuffers()
|
|||
if (fenceStatus == VK_SUCCESS)
|
||||
{
|
||||
ProcessDestructionQueue();
|
||||
m_uniformVarBufferReadIndex = m_cmdBufferUniformRingbufIndices[m_commandBufferSyncIndex];
|
||||
m_commandBufferSyncIndex = (m_commandBufferSyncIndex + 1) % m_commandBuffers.size();
|
||||
memoryManager->cleanupBuffers(m_countCommandBufferFinished);
|
||||
m_countCommandBufferFinished++;
|
||||
|
@ -1946,6 +1986,7 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor
|
|||
cemuLog_logDebug(LogType::Force, "Vulkan: Waiting for available command buffer...");
|
||||
WaitForNextFinishedCommandBuffer();
|
||||
}
|
||||
m_cmdBufferUniformRingbufIndices[nextCmdBufferIndex] = m_cmdBufferUniformRingbufIndices[m_commandBufferIndex];
|
||||
m_commandBufferIndex = nextCmdBufferIndex;
|
||||
|
||||
|
||||
|
@ -2198,6 +2239,8 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD
|
|||
else
|
||||
{
|
||||
formatInfoOut->vkImageAspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
if(format == (Latte::E_GX2SURFFMT::R16_G16_B16_A16_FLOAT | Latte::E_GX2SURFFMT::FMT_BIT_SRGB)) // Seen in Sonic Transformed level Starry Speedway. SRGB should just be ignored for native float formats?
|
||||
format = Latte::E_GX2SURFFMT::R16_G16_B16_A16_FLOAT;
|
||||
switch (format)
|
||||
{
|
||||
// RGBA formats
|
||||
|
@ -2439,6 +2482,11 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD
|
|||
// used by Color Splash and Resident Evil
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UINT; // todo - should we use ABGR format?
|
||||
formatInfoOut->decoder = TextureDecoder_X24_G8_UINT::getInstance(); // todo - verify
|
||||
case Latte::E_GX2SURFFMT::R32_X8_FLOAT:
|
||||
// seen in Disney Infinity 3.0
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R32_SFLOAT;
|
||||
formatInfoOut->decoder = TextureDecoder_NullData64::getInstance();
|
||||
break;
|
||||
default:
|
||||
cemuLog_log(LogType::Force, "Unsupported color texture format {:04x}", (uint32)format);
|
||||
cemu_assert_debug(false);
|
||||
|
@ -2686,11 +2734,21 @@ void VulkanRenderer::SwapBuffer(bool mainWindow)
|
|||
ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
}
|
||||
|
||||
const size_t currentFrameCmdBufferID = GetCurrentCommandBufferId();
|
||||
|
||||
VkSemaphore presentSemaphore = chainInfo.m_presentSemaphores[chainInfo.swapchainImageIndex];
|
||||
SubmitCommandBuffer(presentSemaphore); // submit all command and signal semaphore
|
||||
|
||||
cemu_assert_debug(m_numSubmittedCmdBuffers > 0);
|
||||
|
||||
// wait for the previous frame to finish rendering
|
||||
WaitCommandBufferFinished(m_commandBufferIDOfPrevFrame);
|
||||
m_commandBufferIDOfPrevFrame = currentFrameCmdBufferID;
|
||||
|
||||
chainInfo.WaitAvailableFence();
|
||||
|
||||
VkPresentIdKHR presentId = {};
|
||||
|
||||
VkPresentInfoKHR presentInfo = {};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
presentInfo.swapchainCount = 1;
|
||||
|
@ -2700,6 +2758,24 @@ void VulkanRenderer::SwapBuffer(bool mainWindow)
|
|||
presentInfo.waitSemaphoreCount = 1;
|
||||
presentInfo.pWaitSemaphores = &presentSemaphore;
|
||||
|
||||
// if present_wait is available and enabled, add frame markers to present requests
|
||||
// and limit the number of queued present operations
|
||||
if (m_featureControl.deviceExtensions.present_wait && chainInfo.m_maxQueued > 0)
|
||||
{
|
||||
presentId.sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR;
|
||||
presentId.swapchainCount = 1;
|
||||
presentId.pPresentIds = &chainInfo.m_presentId;
|
||||
|
||||
presentInfo.pNext = &presentId;
|
||||
|
||||
if(chainInfo.m_queueDepth >= chainInfo.m_maxQueued)
|
||||
{
|
||||
uint64 waitFrameId = chainInfo.m_presentId - chainInfo.m_queueDepth;
|
||||
vkWaitForPresentKHR(m_logicalDevice, chainInfo.m_swapchain, waitFrameId, 40'000'000);
|
||||
chainInfo.m_queueDepth--;
|
||||
}
|
||||
}
|
||||
|
||||
VkResult result = vkQueuePresentKHR(m_presentQueue, &presentInfo);
|
||||
if (result < 0 && result != VK_ERROR_OUT_OF_DATE_KHR)
|
||||
{
|
||||
|
@ -2708,6 +2784,12 @@ void VulkanRenderer::SwapBuffer(bool mainWindow)
|
|||
if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
|
||||
chainInfo.m_shouldRecreate = true;
|
||||
|
||||
if(result >= 0)
|
||||
{
|
||||
chainInfo.m_queueDepth++;
|
||||
chainInfo.m_presentId++;
|
||||
}
|
||||
|
||||
chainInfo.hasDefinedSwapchainImage = false;
|
||||
|
||||
chainInfo.swapchainImageIndex = -1;
|
||||
|
@ -3482,13 +3564,13 @@ void VulkanRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType,
|
|||
switch (shaderType)
|
||||
{
|
||||
case LatteConst::ShaderType::Vertex:
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].unformBufferOffset[bufferIndex] = offset;
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].uniformBufferOffset[bufferIndex] = offset;
|
||||
break;
|
||||
case LatteConst::ShaderType::Geometry:
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].unformBufferOffset[bufferIndex] = offset;
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].uniformBufferOffset[bufferIndex] = offset;
|
||||
break;
|
||||
case LatteConst::ShaderType::Pixel:
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].unformBufferOffset[bufferIndex] = offset;
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].uniformBufferOffset[bufferIndex] = offset;
|
||||
break;
|
||||
default:
|
||||
cemu_assert_debug(false);
|
||||
|
|
|
@ -450,6 +450,7 @@ private:
|
|||
bool synchronization2 = false; // VK_KHR_synchronization2
|
||||
bool dynamic_rendering = false; // VK_KHR_dynamic_rendering
|
||||
bool shader_float_controls = false; // VK_KHR_shader_float_controls
|
||||
bool present_wait = false; // VK_KHR_present_wait
|
||||
}deviceExtensions;
|
||||
|
||||
struct
|
||||
|
@ -457,7 +458,7 @@ private:
|
|||
bool shaderRoundingModeRTEFloat32{ false };
|
||||
}shaderFloatControls; // from VK_KHR_shader_float_controls
|
||||
|
||||
struct
|
||||
struct
|
||||
{
|
||||
bool debug_utils = false; // VK_EXT_DEBUG_UTILS
|
||||
}instanceExtensions;
|
||||
|
@ -590,6 +591,7 @@ private:
|
|||
bool m_uniformVarBufferMemoryIsCoherent{false};
|
||||
uint8* m_uniformVarBufferPtr = nullptr;
|
||||
uint32 m_uniformVarBufferWriteIndex = 0;
|
||||
uint32 m_uniformVarBufferReadIndex = 0;
|
||||
|
||||
// transform feedback ringbuffer
|
||||
VkBuffer m_xfbRingBuffer = VK_NULL_HANDLE;
|
||||
|
@ -635,6 +637,8 @@ private:
|
|||
|
||||
size_t m_commandBufferIndex = 0; // current buffer being filled
|
||||
size_t m_commandBufferSyncIndex = 0; // latest buffer that finished execution (updated on submit)
|
||||
size_t m_commandBufferIDOfPrevFrame = 0;
|
||||
std::array<size_t, kCommandBufferPoolSize> m_cmdBufferUniformRingbufIndices {}; // index in the uniform ringbuffer
|
||||
std::array<VkFence, kCommandBufferPoolSize> m_cmd_buffer_fences;
|
||||
std::array<VkCommandBuffer, kCommandBufferPoolSize> m_commandBuffers;
|
||||
std::array<VkSemaphore, kCommandBufferPoolSize> m_commandBufferSemaphores;
|
||||
|
@ -657,7 +661,7 @@ private:
|
|||
uint32 uniformVarBufferOffset[VulkanRendererConst::SHADER_STAGE_INDEX_COUNT];
|
||||
struct
|
||||
{
|
||||
uint32 unformBufferOffset[LATTE_NUM_MAX_UNIFORM_BUFFERS];
|
||||
uint32 uniformBufferOffset[LATTE_NUM_MAX_UNIFORM_BUFFERS];
|
||||
}shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_COUNT];
|
||||
}dynamicOffsetInfo{};
|
||||
|
||||
|
|
|
@ -375,24 +375,20 @@ float s_vkUniformData[512 * 4];
|
|||
|
||||
void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader)
|
||||
{
|
||||
auto GET_UNIFORM_DATA_PTR = [&](size_t index) { return s_vkUniformData + (index / 4); };
|
||||
auto GET_UNIFORM_DATA_PTR = [](size_t index) { return s_vkUniformData + (index / 4); };
|
||||
|
||||
sint32 shaderAluConst;
|
||||
sint32 shaderUniformRegisterOffset;
|
||||
|
||||
switch (shader->shaderType)
|
||||
{
|
||||
case LatteConst::ShaderType::Vertex:
|
||||
shaderAluConst = 0x400;
|
||||
shaderUniformRegisterOffset = mmSQ_VTX_UNIFORM_BLOCK_START;
|
||||
break;
|
||||
case LatteConst::ShaderType::Pixel:
|
||||
shaderAluConst = 0;
|
||||
shaderUniformRegisterOffset = mmSQ_PS_UNIFORM_BLOCK_START;
|
||||
break;
|
||||
case LatteConst::ShaderType::Geometry:
|
||||
shaderAluConst = 0; // geometry shader has no ALU const
|
||||
shaderUniformRegisterOffset = mmSQ_GS_UNIFORM_BLOCK_START;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
|
@ -445,7 +441,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt
|
|||
}
|
||||
if (shader->uniform.loc_verticesPerInstance >= 0)
|
||||
{
|
||||
*(int*)(s_vkUniformData + ((size_t)shader->uniform.loc_verticesPerInstance / 4)) = m_streamoutState.verticesPerInstance;
|
||||
*(int*)GET_UNIFORM_DATA_PTR(shader->uniform.loc_verticesPerInstance) = m_streamoutState.verticesPerInstance;
|
||||
for (sint32 b = 0; b < LATTE_NUM_STREAMOUT_BUFFER; b++)
|
||||
{
|
||||
if (shader->uniform.loc_streamoutBufferBase[b] >= 0)
|
||||
|
@ -455,26 +451,63 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt
|
|||
}
|
||||
}
|
||||
// upload
|
||||
if ((m_uniformVarBufferWriteIndex + shader->uniform.uniformRangeSize + 1024) > UNIFORMVAR_RINGBUFFER_SIZE)
|
||||
const uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1;
|
||||
const uint32 uniformSize = (shader->uniform.uniformRangeSize + bufferAlignmentM1) & ~bufferAlignmentM1;
|
||||
|
||||
auto waitWhileCondition = [&](std::function<bool()> condition) {
|
||||
while (condition())
|
||||
{
|
||||
if (m_commandBufferSyncIndex == m_commandBufferIndex)
|
||||
{
|
||||
if (m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] != m_uniformVarBufferReadIndex)
|
||||
{
|
||||
draw_endRenderPass();
|
||||
SubmitCommandBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
// submitting work would not change readIndex, so there's no way for conditions based on it to change
|
||||
cemuLog_log(LogType::Force, "draw call overflowed and corrupted uniform ringbuffer. expect visual corruption");
|
||||
cemu_assert_suspicious();
|
||||
break;
|
||||
}
|
||||
}
|
||||
WaitForNextFinishedCommandBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
// wrap around if it doesnt fit consecutively
|
||||
if (m_uniformVarBufferWriteIndex + uniformSize > UNIFORMVAR_RINGBUFFER_SIZE)
|
||||
{
|
||||
waitWhileCondition([&]() {
|
||||
return m_uniformVarBufferReadIndex > m_uniformVarBufferWriteIndex || m_uniformVarBufferReadIndex == 0;
|
||||
});
|
||||
m_uniformVarBufferWriteIndex = 0;
|
||||
}
|
||||
uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1;
|
||||
|
||||
auto ringBufRemaining = [&]() {
|
||||
ssize_t ringBufferUsedBytes = (ssize_t)m_uniformVarBufferWriteIndex - m_uniformVarBufferReadIndex;
|
||||
if (ringBufferUsedBytes < 0)
|
||||
ringBufferUsedBytes += UNIFORMVAR_RINGBUFFER_SIZE;
|
||||
return UNIFORMVAR_RINGBUFFER_SIZE - 1 - ringBufferUsedBytes;
|
||||
};
|
||||
waitWhileCondition([&]() {
|
||||
return ringBufRemaining() < uniformSize;
|
||||
});
|
||||
|
||||
const uint32 uniformOffset = m_uniformVarBufferWriteIndex;
|
||||
memcpy(m_uniformVarBufferPtr + uniformOffset, s_vkUniformData, shader->uniform.uniformRangeSize);
|
||||
m_uniformVarBufferWriteIndex += shader->uniform.uniformRangeSize;
|
||||
m_uniformVarBufferWriteIndex = (m_uniformVarBufferWriteIndex + bufferAlignmentM1) & ~bufferAlignmentM1;
|
||||
m_uniformVarBufferWriteIndex += uniformSize;
|
||||
// update dynamic offset
|
||||
dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformOffset;
|
||||
// flush if not coherent
|
||||
if (!m_uniformVarBufferMemoryIsCoherent)
|
||||
{
|
||||
uint32 nonCoherentAtomSizeM1 = m_featureControl.limits.nonCoherentAtomSize - 1;
|
||||
VkMappedMemoryRange flushedRange{};
|
||||
flushedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
||||
flushedRange.memory = m_uniformVarBufferMemory;
|
||||
flushedRange.offset = uniformOffset;
|
||||
flushedRange.size = (shader->uniform.uniformRangeSize + nonCoherentAtomSizeM1) & ~nonCoherentAtomSizeM1;
|
||||
flushedRange.size = uniformSize;
|
||||
vkFlushMappedMemoryRanges(m_logicalDevice, 1, &flushedRange);
|
||||
}
|
||||
}
|
||||
|
@ -494,7 +527,7 @@ void VulkanRenderer::draw_prepareDynamicOffsetsForDescriptorSet(uint32 shaderSta
|
|||
{
|
||||
for (auto& itr : pipeline_info->dynamicOffsetInfo.list_uniformBuffers[shaderStageIndex])
|
||||
{
|
||||
dynamicOffsets[numDynOffsets] = dynamicOffsetInfo.shaderUB[shaderStageIndex].unformBufferOffset[itr];
|
||||
dynamicOffsets[numDynOffsets] = dynamicOffsetInfo.shaderUB[shaderStageIndex].uniformBufferOffset[itr];
|
||||
numDynOffsets++;
|
||||
}
|
||||
}
|
||||
|
@ -1357,6 +1390,24 @@ void VulkanRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
|
|||
return;
|
||||
}
|
||||
|
||||
// prepare streamout
|
||||
m_streamoutState.verticesPerInstance = count;
|
||||
LatteStreamout_PrepareDrawcall(count, instanceCount);
|
||||
|
||||
// update uniform vars
|
||||
LatteDecompilerShader* vertexShader = LatteSHRC_GetActiveVertexShader();
|
||||
LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader();
|
||||
LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader();
|
||||
|
||||
if (vertexShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader);
|
||||
if (pixelShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader);
|
||||
if (geometryShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader);
|
||||
// store where the read pointer should go after command buffer execution
|
||||
m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] = m_uniformVarBufferWriteIndex;
|
||||
|
||||
// process index data
|
||||
const LattePrimitiveMode primitiveMode = static_cast<LattePrimitiveMode>(LatteGPUState.contextRegister[mmVGT_PRIMITIVE_TYPE]);
|
||||
|
||||
|
@ -1410,22 +1461,6 @@ void VulkanRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
|
|||
LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount);
|
||||
}
|
||||
|
||||
// prepare streamout
|
||||
m_streamoutState.verticesPerInstance = count;
|
||||
LatteStreamout_PrepareDrawcall(count, instanceCount);
|
||||
|
||||
// update uniform vars
|
||||
LatteDecompilerShader* vertexShader = LatteSHRC_GetActiveVertexShader();
|
||||
LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader();
|
||||
LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader();
|
||||
|
||||
if (vertexShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader);
|
||||
if (pixelShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader);
|
||||
if (geometryShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader);
|
||||
|
||||
PipelineInfo* pipeline_info;
|
||||
|
||||
if (!isFirst)
|
||||
|
@ -1613,13 +1648,13 @@ void VulkanRenderer::draw_updateUniformBuffersDirectAccess(LatteDecompilerShader
|
|||
switch (shaderType)
|
||||
{
|
||||
case LatteConst::ShaderType::Vertex:
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].unformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress;
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].uniformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress;
|
||||
break;
|
||||
case LatteConst::ShaderType::Geometry:
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].unformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress;
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].uniformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress;
|
||||
break;
|
||||
case LatteConst::ShaderType::Pixel:
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].unformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress;
|
||||
dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].uniformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
|
|
|
@ -137,6 +137,10 @@ namespace iosu
|
|||
this->task_settings.taskType = settings->taskType;
|
||||
|
||||
curl = std::shared_ptr<CURL>(curl_easy_init(), curl_easy_cleanup);
|
||||
if(GetConfig().proxy_server.GetValue() != "")
|
||||
{
|
||||
curl_easy_setopt(curl.get(), CURLOPT_PROXY, GetConfig().proxy_server.GetValue().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -511,6 +511,8 @@ namespace iosu
|
|||
return CallHandler_GetBlackList(fpdClient, vecIn, numVecIn, vecOut, numVecOut);
|
||||
case FPD_REQUEST_ID::GetFriendListEx:
|
||||
return CallHandler_GetFriendListEx(fpdClient, vecIn, numVecIn, vecOut, numVecOut);
|
||||
case FPD_REQUEST_ID::UpdateCommentAsync:
|
||||
return CallHandler_UpdateCommentAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut);
|
||||
case FPD_REQUEST_ID::UpdatePreferenceAsync:
|
||||
return CallHandler_UpdatePreferenceAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut);
|
||||
case FPD_REQUEST_ID::AddFriendRequestByPlayRecordAsync:
|
||||
|
@ -719,18 +721,23 @@ namespace iosu
|
|||
|
||||
nnResult CallHandler_GetMyComment(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut)
|
||||
{
|
||||
static constexpr uint32 MY_COMMENT_LENGTH = 0x12; // are comments utf16? Buffer length is 0x24
|
||||
if(numVecIn != 0 || numVecOut != 1)
|
||||
return FPResult_InvalidIPCParam;
|
||||
if(vecOut->size != MY_COMMENT_LENGTH*sizeof(uint16be))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "GetMyComment: Unexpected output size");
|
||||
return FPResult_InvalidIPCParam;
|
||||
}
|
||||
std::basic_string<uint16be> myComment;
|
||||
myComment.resize(MY_COMMENT_LENGTH);
|
||||
memcpy(vecOut->basePhys.GetPtr(), myComment.data(), MY_COMMENT_LENGTH*sizeof(uint16be));
|
||||
return 0;
|
||||
if(g_fpd.nexFriendSession)
|
||||
{
|
||||
if(vecOut->size != MY_COMMENT_LENGTH * sizeof(uint16be))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "GetMyComment: Unexpected output size");
|
||||
return FPResult_InvalidIPCParam;
|
||||
}
|
||||
nexComment myNexComment;
|
||||
g_fpd.nexFriendSession->getMyComment(myNexComment);
|
||||
myComment = StringHelpers::FromUtf8(myNexComment.commentString);
|
||||
}
|
||||
myComment.insert(0, 1, '\0');
|
||||
memcpy(vecOut->basePhys.GetPtr(), myComment.c_str(), MY_COMMENT_LENGTH * sizeof(uint16be));
|
||||
return FPResult_Ok;
|
||||
}
|
||||
|
||||
nnResult CallHandler_GetMyPreference(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut)
|
||||
|
@ -1143,6 +1150,36 @@ namespace iosu
|
|||
return FPResult_Ok;
|
||||
}
|
||||
|
||||
nnResult CallHandler_UpdateCommentAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut)
|
||||
{
|
||||
std::unique_lock _l(g_fpd.mtxFriendSession);
|
||||
if (numVecIn != 1 || numVecOut != 0)
|
||||
return FPResult_InvalidIPCParam;
|
||||
if (!g_fpd.nexFriendSession)
|
||||
return FPResult_RequestFailed;
|
||||
uint32 messageLength = vecIn[0].size / sizeof(uint16be);
|
||||
DeclareInputPtr(newComment, uint16be, messageLength, 0);
|
||||
if (messageLength == 0 || newComment[messageLength-1] != 0)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "UpdateCommentAsync: Message must contain at least a null-termination character");
|
||||
return FPResult_InvalidIPCParam;
|
||||
}
|
||||
IPCCommandBody* cmd = ServiceCallDelayCurrentResponse();
|
||||
|
||||
auto utf8_comment = StringHelpers::ToUtf8(newComment, messageLength);
|
||||
nexComment temporaryComment;
|
||||
temporaryComment.ukn0 = 0;
|
||||
temporaryComment.commentString = utf8_comment;
|
||||
temporaryComment.ukn1 = 0;
|
||||
|
||||
g_fpd.nexFriendSession->updateCommentAsync(temporaryComment, [cmd](NexFriends::RpcErrorCode result) {
|
||||
if (result != NexFriends::ERR_NONE)
|
||||
return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed);
|
||||
ServiceCallAsyncRespond(cmd, FPResult_Ok);
|
||||
});
|
||||
return FPResult_Ok;
|
||||
}
|
||||
|
||||
nnResult CallHandler_UpdatePreferenceAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut)
|
||||
{
|
||||
std::unique_lock _l(g_fpd.mtxFriendSession);
|
||||
|
|
|
@ -212,6 +212,7 @@ namespace iosu
|
|||
static const int RELATIONSHIP_FRIEND = 3;
|
||||
|
||||
static const int GAMEMODE_MAX_MESSAGE_LENGTH = 0x80; // limit includes null-terminator character, so only 0x7F actual characters can be used
|
||||
static const int MY_COMMENT_LENGTH = 0x12;
|
||||
|
||||
enum class FPD_REQUEST_ID
|
||||
{
|
||||
|
@ -245,6 +246,7 @@ namespace iosu
|
|||
CheckSettingStatusAsync = 0x7596,
|
||||
GetFriendListEx = 0x75F9,
|
||||
GetFriendRequestListEx = 0x76C1,
|
||||
UpdateCommentAsync = 0x7726,
|
||||
UpdatePreferenceAsync = 0x7727,
|
||||
RemoveFriendAsync = 0x7789,
|
||||
DeleteFriendFlagsAsync = 0x778A,
|
||||
|
|
|
@ -23,3 +23,86 @@ void osLib_returnFromFunction64(PPCInterpreter_t* hCPU, uint64 returnValue64);
|
|||
|
||||
// utility functions
|
||||
#include "Cafe/OS/common/OSUtil.h"
|
||||
|
||||
// va_list
|
||||
struct ppc_va_list
|
||||
{
|
||||
uint8be gprIndex;
|
||||
uint8be fprIndex;
|
||||
uint8be _padding2[2];
|
||||
MEMPTR<uint8be> overflow_arg_area;
|
||||
MEMPTR<uint8be> reg_save_area;
|
||||
};
|
||||
static_assert(sizeof(ppc_va_list) == 0xC);
|
||||
|
||||
struct ppc_va_list_reg_storage
|
||||
{
|
||||
uint32be gpr_save_area[8]; // 32 bytes, r3 to r10
|
||||
float64be fpr_save_area[8]; // 64 bytes, f1 to f8
|
||||
ppc_va_list vargs;
|
||||
uint32be padding;
|
||||
};
|
||||
static_assert(sizeof(ppc_va_list_reg_storage) == 0x70);
|
||||
|
||||
// Equivalent of va_start for PPC HLE functions. Must be called before any StackAllocator<> definitions
|
||||
#define ppc_define_va_list(__gprIndex, __fprIndex) \
|
||||
MPTR vaOriginalR1 = PPCInterpreter_getCurrentInstance()->gpr[1]; \
|
||||
StackAllocator<ppc_va_list_reg_storage> va_list_storage; \
|
||||
for(int i=3; i<=10; i++) va_list_storage->gpr_save_area[i-3] = PPCInterpreter_getCurrentInstance()->gpr[i]; \
|
||||
for(int i=1; i<=8; i++) va_list_storage->fpr_save_area[i-1] = PPCInterpreter_getCurrentInstance()->fpr[i].fp0; \
|
||||
va_list_storage->vargs.gprIndex = __gprIndex; \
|
||||
va_list_storage->vargs.fprIndex = __fprIndex; \
|
||||
va_list_storage->vargs.reg_save_area = (uint8be*)&va_list_storage; \
|
||||
va_list_storage->vargs.overflow_arg_area = {vaOriginalR1 + 8}; \
|
||||
ppc_va_list& vargs = va_list_storage->vargs;
|
||||
|
||||
enum class ppc_va_type
|
||||
{
|
||||
INT32 = 1,
|
||||
INT64 = 2,
|
||||
FLOAT_OR_DOUBLE = 3,
|
||||
};
|
||||
|
||||
static void* _ppc_va_arg(ppc_va_list* vargs, ppc_va_type argType)
|
||||
{
|
||||
void* r;
|
||||
switch ( argType )
|
||||
{
|
||||
default:
|
||||
cemu_assert_suspicious();
|
||||
case ppc_va_type::INT32:
|
||||
if ( vargs[0].gprIndex < 8u )
|
||||
{
|
||||
r = &vargs->reg_save_area[4 * vargs->gprIndex];
|
||||
vargs->gprIndex++;
|
||||
return r;
|
||||
}
|
||||
r = vargs->overflow_arg_area;
|
||||
vargs->overflow_arg_area += 4;
|
||||
return r;
|
||||
case ppc_va_type::INT64:
|
||||
if ( (vargs->gprIndex & 1) != 0 )
|
||||
vargs->gprIndex++;
|
||||
if ( vargs->gprIndex < 8 )
|
||||
{
|
||||
r = &vargs->reg_save_area[4 * vargs->gprIndex];
|
||||
vargs->gprIndex += 2;
|
||||
return r;
|
||||
}
|
||||
vargs->overflow_arg_area = {(vargs->overflow_arg_area.GetMPTR()+7) & 0xFFFFFFF8};
|
||||
r = vargs->overflow_arg_area;
|
||||
vargs->overflow_arg_area += 8;
|
||||
return r;
|
||||
case ppc_va_type::FLOAT_OR_DOUBLE:
|
||||
if ( vargs->fprIndex < 8 )
|
||||
{
|
||||
r = &vargs->reg_save_area[0x20 + 8 * vargs->fprIndex];
|
||||
vargs->fprIndex++;
|
||||
return r;
|
||||
}
|
||||
vargs->overflow_arg_area = {(vargs->overflow_arg_area.GetMPTR()+7) & 0xFFFFFFF8};
|
||||
r = vargs->overflow_arg_area;
|
||||
vargs->overflow_arg_area += 8;
|
||||
return r;
|
||||
}
|
||||
}
|
|
@ -7,14 +7,9 @@
|
|||
|
||||
namespace coreinit
|
||||
{
|
||||
|
||||
/* coreinit logging and string format */
|
||||
|
||||
sint32 ppcSprintf(const char* formatStr, char* strOut, sint32 maxLength, PPCInterpreter_t* hCPU, sint32 initialParamIndex)
|
||||
sint32 ppc_vprintf(const char* formatStr, char* strOut, sint32 maxLength, ppc_va_list* vargs)
|
||||
{
|
||||
char tempStr[4096];
|
||||
sint32 integerParamIndex = initialParamIndex;
|
||||
sint32 floatParamIndex = 0;
|
||||
sint32 writeIndex = 0;
|
||||
while (*formatStr)
|
||||
{
|
||||
|
@ -101,8 +96,7 @@ namespace coreinit
|
|||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU32(hCPU, integerParamIndex));
|
||||
integerParamIndex++;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (uint32)*(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32));
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
|
@ -120,13 +114,12 @@ namespace coreinit
|
|||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
MPTR strOffset = PPCInterpreter_getCallParamU32(hCPU, integerParamIndex);
|
||||
MPTR strOffset = *(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32);
|
||||
sint32 tempLen = 0;
|
||||
if (strOffset == MPTR_NULL)
|
||||
tempLen = sprintf(tempStr, "NULL");
|
||||
else
|
||||
tempLen = sprintf(tempStr, tempFormat, memory_getPointerFromVirtualOffset(strOffset));
|
||||
integerParamIndex++;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
|
@ -136,25 +129,6 @@ namespace coreinit
|
|||
}
|
||||
strOut[std::min(maxLength - 1, writeIndex)] = '\0';
|
||||
}
|
||||
else if (*formatStr == 'f')
|
||||
{
|
||||
// float
|
||||
formatStr++;
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (float)hCPU->fpr[1 + floatParamIndex].fp0);
|
||||
floatParamIndex++;
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
else if (*formatStr == 'c')
|
||||
{
|
||||
// character
|
||||
|
@ -164,8 +138,24 @@ namespace coreinit
|
|||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU32(hCPU, integerParamIndex));
|
||||
integerParamIndex++;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (uint32)*(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32));
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
break;
|
||||
strOut[writeIndex] = tempStr[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
else if (*formatStr == 'f' || *formatStr == 'g' || *formatStr == 'G')
|
||||
{
|
||||
formatStr++;
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (double)*(betype<double>*)_ppc_va_arg(vargs, ppc_va_type::FLOAT_OR_DOUBLE));
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
|
@ -183,8 +173,7 @@ namespace coreinit
|
|||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (double)hCPU->fpr[1 + floatParamIndex].fp0);
|
||||
floatParamIndex++;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (double)*(betype<double>*)_ppc_va_arg(vargs, ppc_va_type::FLOAT_OR_DOUBLE));
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
|
@ -196,16 +185,13 @@ namespace coreinit
|
|||
else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && (formatStr[2] == 'x' || formatStr[2] == 'X')))
|
||||
{
|
||||
formatStr += 3;
|
||||
// double (64bit)
|
||||
// 64bit int
|
||||
strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart));
|
||||
if ((formatStr - formatStart) < sizeof(tempFormat))
|
||||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
if (integerParamIndex & 1)
|
||||
integerParamIndex++;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU64(hCPU, integerParamIndex));
|
||||
integerParamIndex += 2;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (uint64)*(uint64be*)_ppc_va_arg(vargs, ppc_va_type::INT64));
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
|
@ -223,10 +209,7 @@ namespace coreinit
|
|||
tempFormat[(formatStr - formatStart)] = '\0';
|
||||
else
|
||||
tempFormat[sizeof(tempFormat) - 1] = '\0';
|
||||
if (integerParamIndex & 1)
|
||||
integerParamIndex++;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU64(hCPU, integerParamIndex));
|
||||
integerParamIndex += 2;
|
||||
sint32 tempLen = sprintf(tempStr, tempFormat, (sint64)*(sint64be*)_ppc_va_arg(vargs, ppc_va_type::INT64));
|
||||
for (sint32 i = 0; i < tempLen; i++)
|
||||
{
|
||||
if (writeIndex >= maxLength)
|
||||
|
@ -255,9 +238,12 @@ namespace coreinit
|
|||
return std::min(writeIndex, maxLength - 1);
|
||||
}
|
||||
|
||||
/* coreinit logging and string format */
|
||||
|
||||
sint32 __os_snprintf(char* outputStr, sint32 maxLength, const char* formatStr)
|
||||
{
|
||||
sint32 r = ppcSprintf(formatStr, outputStr, maxLength, PPCInterpreter_getCurrentInstance(), 3);
|
||||
ppc_define_va_list(3, 0);
|
||||
sint32 r = ppc_vprintf(formatStr, outputStr, maxLength, &vargs);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -322,32 +308,40 @@ namespace coreinit
|
|||
}
|
||||
}
|
||||
|
||||
void OSReport(const char* format)
|
||||
void COSVReport(COSReportModule module, COSReportLevel level, const char* format, ppc_va_list* vargs)
|
||||
{
|
||||
char buffer[1024 * 2];
|
||||
sint32 len = ppcSprintf(format, buffer, sizeof(buffer), PPCInterpreter_getCurrentInstance(), 1);
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len);
|
||||
char tmpBuffer[1024];
|
||||
sint32 len = ppc_vprintf(format, tmpBuffer, sizeof(tmpBuffer), vargs);
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, tmpBuffer, len);
|
||||
}
|
||||
|
||||
void OSVReport(const char* format, MPTR vaArgs)
|
||||
void OSReport(const char* format)
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
ppc_define_va_list(1, 0);
|
||||
COSVReport(COSReportModule::coreinit, COSReportLevel::Info, format, &vargs);
|
||||
}
|
||||
|
||||
void OSVReport(const char* format, ppc_va_list* vargs)
|
||||
{
|
||||
COSVReport(COSReportModule::coreinit, COSReportLevel::Info, format, vargs);
|
||||
}
|
||||
|
||||
void COSWarn(int moduleId, const char* format)
|
||||
{
|
||||
char buffer[1024 * 2];
|
||||
int prefixLen = sprintf(buffer, "[COSWarn-%d] ", moduleId);
|
||||
sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, PPCInterpreter_getCurrentInstance(), 2);
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len + prefixLen);
|
||||
ppc_define_va_list(2, 0);
|
||||
char tmpBuffer[1024];
|
||||
int prefixLen = sprintf(tmpBuffer, "[COSWarn-%d] ", moduleId);
|
||||
sint32 len = ppc_vprintf(format, tmpBuffer + prefixLen, sizeof(tmpBuffer) - prefixLen, &vargs);
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, tmpBuffer, len + prefixLen);
|
||||
}
|
||||
|
||||
void OSLogPrintf(int ukn1, int ukn2, int ukn3, const char* format)
|
||||
{
|
||||
char buffer[1024 * 2];
|
||||
int prefixLen = sprintf(buffer, "[OSLogPrintf-%d-%d-%d] ", ukn1, ukn2, ukn3);
|
||||
sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, PPCInterpreter_getCurrentInstance(), 4);
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len + prefixLen);
|
||||
ppc_define_va_list(4, 0);
|
||||
char tmpBuffer[1024];
|
||||
int prefixLen = sprintf(tmpBuffer, "[OSLogPrintf-%d-%d-%d] ", ukn1, ukn2, ukn3);
|
||||
sint32 len = ppc_vprintf(format, tmpBuffer + prefixLen, sizeof(tmpBuffer) - prefixLen, &vargs);
|
||||
WriteCafeConsole(CafeLogType::OSCONSOLE, tmpBuffer, len + prefixLen);
|
||||
}
|
||||
|
||||
void OSConsoleWrite(const char* strPtr, sint32 length)
|
||||
|
@ -562,9 +556,11 @@ namespace coreinit
|
|||
s_transitionToForeground = false;
|
||||
|
||||
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", COSVReport, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSVReport, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder);
|
||||
|
||||
|
|
|
@ -26,5 +26,19 @@ namespace coreinit
|
|||
uint32 OSDriver_Register(uint32 moduleHandle, sint32 priority, OSDriverInterface* driverCallbacks, sint32 driverId, uint32be* outUkn1, uint32be* outUkn2, uint32be* outUkn3);
|
||||
uint32 OSDriver_Deregister(uint32 moduleHandle, sint32 driverId);
|
||||
|
||||
enum class COSReportModule
|
||||
{
|
||||
coreinit = 0,
|
||||
};
|
||||
|
||||
enum class COSReportLevel
|
||||
{
|
||||
Error = 0,
|
||||
Warn = 1,
|
||||
Info = 2
|
||||
};
|
||||
|
||||
sint32 ppc_vprintf(const char* formatStr, char* strOut, sint32 maxLength, ppc_va_list* vargs);
|
||||
|
||||
void miscInit();
|
||||
};
|
|
@ -140,7 +140,7 @@ namespace coreinit
|
|||
// we are in single-core mode and the lock will never be released unless we let other threads resume work
|
||||
// to avoid an infinite loop we have no choice but to yield the thread even it is in an uninterruptible state
|
||||
if( !OSIsInterruptEnabled() )
|
||||
cemuLog_log(LogType::APIErrors, "OSUninterruptibleSpinLock_Acquire(): Lock is occupied which requires a wait but current thread is already in an uninterruptible state (Avoid cascaded OSDisableInterrupts and/or OSUninterruptibleSpinLock)");
|
||||
cemuLog_logOnce(LogType::APIErrors, "OSUninterruptibleSpinLock_Acquire(): Lock is occupied which requires a wait but current thread is already in an uninterruptible state (Avoid cascaded OSDisableInterrupts and/or OSUninterruptibleSpinLock)");
|
||||
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
|
||||
{
|
||||
OSYieldThread();
|
||||
|
|
|
@ -464,6 +464,14 @@ namespace nn
|
|||
return ipcCtx->Submit(std::move(ipcCtx));
|
||||
}
|
||||
|
||||
nnResult GetMyPlayingGame(iosu::fpd::GameKey* myPlayingGame)
|
||||
{
|
||||
FP_API_BASE();
|
||||
auto ipcCtx = std::make_unique<FPIpcContext>(iosu::fpd::FPD_REQUEST_ID::GetMyPlayingGame);
|
||||
ipcCtx->AddOutput(myPlayingGame, sizeof(iosu::fpd::GameKey));
|
||||
return ipcCtx->Submit(std::move(ipcCtx));
|
||||
}
|
||||
|
||||
nnResult GetMyPreference(iosu::fpd::FPDPreference* myPreference)
|
||||
{
|
||||
FP_API_BASE();
|
||||
|
@ -472,6 +480,14 @@ namespace nn
|
|||
return ipcCtx->Submit(std::move(ipcCtx));
|
||||
}
|
||||
|
||||
nnResult GetMyComment(uint16be* myComment)
|
||||
{
|
||||
FP_API_BASE();
|
||||
auto ipcCtx = std::make_unique<FPIpcContext>(iosu::fpd::FPD_REQUEST_ID::GetMyComment);
|
||||
ipcCtx->AddOutput(myComment, iosu::fpd::MY_COMMENT_LENGTH * sizeof(uint16be));
|
||||
return ipcCtx->Submit(std::move(ipcCtx));
|
||||
}
|
||||
|
||||
nnResult GetMyMii(FFLData_t* fflData)
|
||||
{
|
||||
FP_API_BASE();
|
||||
|
@ -607,6 +623,20 @@ namespace nn
|
|||
return resultBuf != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
nnResult UpdateCommentAsync(uint16be* newComment, void* funcPtr, void* customParam)
|
||||
{
|
||||
FP_API_BASE();
|
||||
auto ipcCtx = std::make_unique<FPIpcContext>(iosu::fpd::FPD_REQUEST_ID::UpdateCommentAsync);
|
||||
uint32 commentLen = CafeStringHelpers::Length(newComment, iosu::fpd::MY_COMMENT_LENGTH-1);
|
||||
if (commentLen >= iosu::fpd::MY_COMMENT_LENGTH-1)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "UpdateCommentAsync: message too long");
|
||||
return FPResult_InvalidIPCParam;
|
||||
}
|
||||
ipcCtx->AddInput(newComment, sizeof(uint16be) * commentLen + 2);
|
||||
return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam);
|
||||
}
|
||||
|
||||
nnResult UpdatePreferenceAsync(iosu::fpd::FPDPreference* newPreference, void* funcPtr, void* customParam)
|
||||
{
|
||||
FP_API_BASE();
|
||||
|
@ -763,7 +793,9 @@ namespace nn
|
|||
cafeExportRegisterFunc(GetMyAccountId, "nn_fp", "GetMyAccountId__Q2_2nn2fpFPc", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(GetMyScreenName, "nn_fp", "GetMyScreenName__Q2_2nn2fpFPw", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(GetMyMii, "nn_fp", "GetMyMii__Q2_2nn2fpFP12FFLStoreData", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(GetMyPlayingGame, "nn_fp", "GetMyPlayingGame__Q2_2nn2fpFPQ3_2nn2fp7GameKey", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(GetMyPreference, "nn_fp", "GetMyPreference__Q2_2nn2fpFPQ3_2nn2fp10Preference", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(GetMyComment, "nn_fp", "GetMyComment__Q2_2nn2fpFPQ3_2nn2fp7Comment", LogType::NN_FP);
|
||||
|
||||
cafeExportRegisterFunc(GetFriendAccountId, "nn_fp", "GetFriendAccountId__Q2_2nn2fpFPA17_cPCUiUi", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(GetFriendScreenName, "nn_fp", "GetFriendScreenName__Q2_2nn2fpFPA11_wPCUiUibPUc", LogType::NN_FP);
|
||||
|
@ -774,6 +806,7 @@ namespace nn
|
|||
|
||||
cafeExportRegisterFunc(CheckSettingStatusAsync, "nn_fp", "CheckSettingStatusAsync__Q2_2nn2fpFPUcPFQ2_2nn6ResultPv_vPv", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(IsPreferenceValid, "nn_fp", "IsPreferenceValid__Q2_2nn2fpFv", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(UpdateCommentAsync, "nn_fp", "UpdateCommentAsync__Q2_2nn2fpFPCwPFQ2_2nn6ResultPv_vPv", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(UpdatePreferenceAsync, "nn_fp", "UpdatePreferenceAsync__Q2_2nn2fpFPCQ3_2nn2fp10PreferencePFQ2_2nn6ResultPv_vPv", LogType::NN_FP);
|
||||
cafeExportRegisterFunc(GetRequestBlockSettingAsync, "nn_fp", "GetRequestBlockSettingAsync__Q2_2nn2fpFPUcPCUiUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP);
|
||||
|
||||
|
|
|
@ -334,45 +334,63 @@ void nnNfpExport_MountRom(PPCInterpreter_t* hCPU)
|
|||
osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NFP, 0));
|
||||
}
|
||||
|
||||
typedef struct
|
||||
namespace nn::nfp
|
||||
{
|
||||
/* +0x00 */ uint8 characterId[3];
|
||||
/* +0x03 */ uint8 amiiboSeries;
|
||||
/* +0x04 */ uint16be number;
|
||||
/* +0x06 */ uint8 nfpType;
|
||||
/* +0x07 */ uint8 unused[0x2F];
|
||||
}nfpRomInfo_t;
|
||||
|
||||
static_assert(offsetof(nfpRomInfo_t, amiiboSeries) == 0x3, "nfpRomInfo.seriesId has invalid offset");
|
||||
static_assert(offsetof(nfpRomInfo_t, number) == 0x4, "nfpRomInfo.number has invalid offset");
|
||||
static_assert(offsetof(nfpRomInfo_t, nfpType) == 0x6, "nfpRomInfo.nfpType has invalid offset");
|
||||
static_assert(sizeof(nfpRomInfo_t) == 0x36, "nfpRomInfo_t has invalid size");
|
||||
|
||||
void nnNfpExport_GetNfpRomInfo(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
cemuLog_log(LogType::NN_NFP, "GetNfpRomInfo(0x{:08x})", hCPU->gpr[3]);
|
||||
ppcDefineParamStructPtr(romInfo, nfpRomInfo_t, 0);
|
||||
|
||||
nnNfpLock();
|
||||
if (nfp_data.hasActiveAmiibo == false)
|
||||
struct RomInfo
|
||||
{
|
||||
nnNfpUnlock();
|
||||
osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_NFP, 0)); // todo: Return correct error code
|
||||
return;
|
||||
/* +0x00 */ uint8 characterId[3];
|
||||
/* +0x03 */ uint8 amiiboSeries;
|
||||
/* +0x04 */ uint16be number;
|
||||
/* +0x06 */ uint8 nfpType;
|
||||
/* +0x07 */ uint8 unused[0x2F];
|
||||
};
|
||||
|
||||
static_assert(offsetof(RomInfo, amiiboSeries) == 0x3);
|
||||
static_assert(offsetof(RomInfo, number) == 0x4);
|
||||
static_assert(offsetof(RomInfo, nfpType) == 0x6);
|
||||
static_assert(sizeof(RomInfo) == 0x36);
|
||||
|
||||
using ReadOnlyInfo = RomInfo; // same layout
|
||||
|
||||
void GetRomInfo(RomInfo* romInfo)
|
||||
{
|
||||
cemu_assert_debug(nfp_data.hasActiveAmiibo);
|
||||
memset(romInfo, 0x00, sizeof(RomInfo));
|
||||
romInfo->characterId[0] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[0];
|
||||
romInfo->characterId[1] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[1];
|
||||
romInfo->characterId[2] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.characterVariation; // guessed
|
||||
romInfo->amiiboSeries = nfp_data.amiiboNFCData.amiiboIdentificationBlock.amiiboSeries; // guessed
|
||||
romInfo->number = *(uint16be*)nfp_data.amiiboNFCData.amiiboIdentificationBlock.amiiboModelNumber; // guessed
|
||||
romInfo->nfpType = nfp_data.amiiboNFCData.amiiboIdentificationBlock.amiiboFigureType; // guessed
|
||||
memset(romInfo->unused, 0x00, sizeof(romInfo->unused));
|
||||
}
|
||||
memset(romInfo, 0x00, sizeof(nfpRomInfo_t));
|
||||
|
||||
romInfo->characterId[0] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[0];
|
||||
romInfo->characterId[1] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[1];
|
||||
romInfo->characterId[2] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.characterVariation; // guessed
|
||||
nnResult GetNfpRomInfo(RomInfo* romInfo)
|
||||
{
|
||||
nnNfpLock();
|
||||
if (nfp_data.hasActiveAmiibo == false)
|
||||
{
|
||||
nnNfpUnlock();
|
||||
return BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_NFP, 0); // todo: Return correct error code
|
||||
}
|
||||
GetRomInfo(romInfo);
|
||||
nnNfpUnlock();
|
||||
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NFP, 0);
|
||||
}
|
||||
|
||||
romInfo->amiiboSeries = nfp_data.amiiboNFCData.amiiboIdentificationBlock.amiiboSeries; // guessed
|
||||
romInfo->number = *(uint16be*)nfp_data.amiiboNFCData.amiiboIdentificationBlock.amiiboModelNumber; // guessed
|
||||
romInfo->nfpType = nfp_data.amiiboNFCData.amiiboIdentificationBlock.amiiboFigureType; // guessed
|
||||
|
||||
nnNfpUnlock();
|
||||
osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NFP, 0));
|
||||
}
|
||||
nnResult GetNfpReadOnlyInfo(ReadOnlyInfo* readOnlyInfo)
|
||||
{
|
||||
nnNfpLock();
|
||||
if (nfp_data.hasActiveAmiibo == false)
|
||||
{
|
||||
nnNfpUnlock();
|
||||
return BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_NFP, 0); // todo: Return correct error code
|
||||
}
|
||||
GetRomInfo(readOnlyInfo);
|
||||
nnNfpUnlock();
|
||||
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NFP, 0);
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -880,13 +898,13 @@ void nnNfp_update()
|
|||
if (amiiboElapsedTouchTime >= 1500)
|
||||
{
|
||||
nnNfp_unloadAmiibo();
|
||||
if (nfp_data.deactivateEvent)
|
||||
{
|
||||
coreinit::OSEvent* osEvent = (coreinit::OSEvent*)memory_getPointerFromVirtualOffset(nfp_data.deactivateEvent);
|
||||
coreinit::OSSignalEvent(osEvent);
|
||||
}
|
||||
}
|
||||
nnNfpUnlock();
|
||||
if (nfp_data.deactivateEvent)
|
||||
{
|
||||
coreinit::OSEvent* osEvent = (coreinit::OSEvent*)memory_getPointerFromVirtualOffset(nfp_data.deactivateEvent);
|
||||
coreinit::OSSignalEvent(osEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void nnNfpExport_GetNfpState(PPCInterpreter_t* hCPU)
|
||||
|
@ -1001,8 +1019,6 @@ namespace nn::nfp
|
|||
osLib_addFunction("nn_nfp", "Mount__Q2_2nn3nfpFv", nnNfpExport_Mount);
|
||||
osLib_addFunction("nn_nfp", "MountRom__Q2_2nn3nfpFv", nnNfpExport_MountRom);
|
||||
osLib_addFunction("nn_nfp", "Unmount__Q2_2nn3nfpFv", nnNfpExport_Unmount);
|
||||
|
||||
osLib_addFunction("nn_nfp", "GetNfpRomInfo__Q2_2nn3nfpFPQ3_2nn3nfp7RomInfo", nnNfpExport_GetNfpRomInfo);
|
||||
osLib_addFunction("nn_nfp", "GetNfpCommonInfo__Q2_2nn3nfpFPQ3_2nn3nfp10CommonInfo", nnNfpExport_GetNfpCommonInfo);
|
||||
osLib_addFunction("nn_nfp", "GetNfpRegisterInfo__Q2_2nn3nfpFPQ3_2nn3nfp12RegisterInfo", nnNfpExport_GetNfpRegisterInfo);
|
||||
|
||||
|
@ -1028,7 +1044,9 @@ namespace nn::nfp
|
|||
{
|
||||
nnNfp_load(); // legacy interface, update these to use cafeExportRegister / cafeExportRegisterFunc
|
||||
|
||||
cafeExportRegisterFunc(nn::nfp::GetErrorCode, "nn_nfp", "GetErrorCode__Q2_2nn3nfpFRCQ2_2nn6Result", LogType::Placeholder);
|
||||
cafeExportRegisterFunc(nn::nfp::GetErrorCode, "nn_nfp", "GetErrorCode__Q2_2nn3nfpFRCQ2_2nn6Result", LogType::NN_NFP);
|
||||
cafeExportRegisterFunc(nn::nfp::GetNfpRomInfo, "nn_nfp", "GetNfpRomInfo__Q2_2nn3nfpFPQ3_2nn3nfp7RomInfo", LogType::NN_NFP);
|
||||
cafeExportRegisterFunc(nn::nfp::GetNfpReadOnlyInfo, "nn_nfp", "GetNfpReadOnlyInfo__Q2_2nn3nfpFPQ3_2nn3nfp12ReadOnlyInfo", LogType::NN_NFP);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ namespace nn
|
|||
|
||||
nnResult _Async_OfflineDB_DownloadPostDataListParam_DownloadPostDataList(coreinit::OSEvent* event, DownloadedTopicData* downloadedTopicData, DownloadedPostData* downloadedPostData, uint32be* postCountOut, uint32 maxCount, DownloadPostDataListParam* param)
|
||||
{
|
||||
scope_exit _se([&](){coreinit::OSSignalEvent(event);});
|
||||
stdx::scope_exit _se([&](){coreinit::OSSignalEvent(event);});
|
||||
|
||||
uint64 titleId = CafeSystem::GetForegroundTitleId();
|
||||
|
||||
|
@ -184,7 +184,7 @@ namespace nn
|
|||
|
||||
nnResult _Async_OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(coreinit::OSEvent* event, DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize)
|
||||
{
|
||||
scope_exit _se([&](){coreinit::OSSignalEvent(event);});
|
||||
stdx::scope_exit _se([&](){coreinit::OSSignalEvent(event);});
|
||||
|
||||
if (!_this->TestFlags(_this, DownloadedDataBase::FLAGS::HAS_EXTERNAL_IMAGE))
|
||||
return OLV_RESULT_MISSING_DATA;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include "BackendEmulated.h"
|
||||
|
||||
#include "Dimensions.h"
|
||||
#include "Infinity.h"
|
||||
#include "Skylander.h"
|
||||
#include "config/CemuConfig.h"
|
||||
|
@ -33,5 +35,12 @@ namespace nsyshid::backend::emulated
|
|||
auto device = std::make_shared<InfinityBaseDevice>();
|
||||
AttachDevice(device);
|
||||
}
|
||||
if (GetConfig().emulated_usb_devices.emulate_dimensions_toypad && !FindDeviceById(0x0E6F, 0x0241))
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "Attaching Emulated Toypad");
|
||||
// Add Dimensions Toypad
|
||||
auto device = std::make_shared<DimensionsToypadDevice>();
|
||||
AttachDevice(device);
|
||||
}
|
||||
}
|
||||
} // namespace nsyshid::backend::emulated
|
|
@ -15,7 +15,7 @@ namespace nsyshid::backend::libusb
|
|||
if (m_initReturnCode < 0)
|
||||
{
|
||||
m_ctx = nullptr;
|
||||
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb with return code %i",
|
||||
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb, return code: {}",
|
||||
m_initReturnCode);
|
||||
return;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ namespace nsyshid::backend::libusb
|
|||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force,
|
||||
"nsyshid::BackendLibusb: failed to register hotplug callback with return code %i",
|
||||
"nsyshid::BackendLibusb: failed to register hotplug callback with return code {}",
|
||||
ret);
|
||||
}
|
||||
else
|
||||
|
@ -415,7 +415,7 @@ namespace nsyshid::backend::libusb
|
|||
if (ret < 0)
|
||||
{
|
||||
cemuLog_log(LogType::Force,
|
||||
"nsyshid::DeviceLibusb::open(): failed to get device descriptor; return code: %i",
|
||||
"nsyshid::DeviceLibusb::open(): failed to get device descriptor, return code: {}",
|
||||
ret);
|
||||
libusb_free_device_list(devices, 1);
|
||||
return false;
|
||||
|
@ -439,8 +439,8 @@ namespace nsyshid::backend::libusb
|
|||
{
|
||||
this->m_libusbHandle = nullptr;
|
||||
cemuLog_log(LogType::Force,
|
||||
"nsyshid::DeviceLibusb::open(): failed to open device; return code: %i",
|
||||
ret);
|
||||
"nsyshid::DeviceLibusb::open(): failed to open device: {}",
|
||||
libusb_strerror(ret));
|
||||
libusb_free_device_list(devices, 1);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -67,13 +67,6 @@ namespace nsyshid::backend::windows
|
|||
device->m_productId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cemuLog_log(LogType::Force,
|
||||
"nsyshid::BackendWindowsHID: device not on whitelist: {:04x}:{:04x}",
|
||||
device->m_vendorId,
|
||||
device->m_productId);
|
||||
}
|
||||
}
|
||||
CloseHandle(hHIDDevice);
|
||||
}
|
||||
|
@ -125,14 +118,12 @@ namespace nsyshid::backend::windows
|
|||
}
|
||||
if (maxPacketInputLength <= 0 || maxPacketInputLength >= 0xF000)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "HID: Input packet length not available or out of range (length = {})",
|
||||
maxPacketInputLength);
|
||||
cemuLog_logDebug(LogType::Force, "HID: Input packet length not available or out of range (length = {})", maxPacketInputLength);
|
||||
maxPacketInputLength = 0x20;
|
||||
}
|
||||
if (maxPacketOutputLength <= 0 || maxPacketOutputLength >= 0xF000)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "HID: Output packet length not available or out of range (length = {})",
|
||||
maxPacketOutputLength);
|
||||
cemuLog_logDebug(LogType::Force, "HID: Output packet length not available or out of range (length = {})", maxPacketOutputLength);
|
||||
maxPacketOutputLength = 0x20;
|
||||
}
|
||||
|
||||
|
|
1162
src/Cafe/OS/libs/nsyshid/Dimensions.cpp
Normal file
1162
src/Cafe/OS/libs/nsyshid/Dimensions.cpp
Normal file
File diff suppressed because it is too large
Load diff
108
src/Cafe/OS/libs/nsyshid/Dimensions.h
Normal file
108
src/Cafe/OS/libs/nsyshid/Dimensions.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "nsyshid.h"
|
||||
#include "Backend.h"
|
||||
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
namespace nsyshid
|
||||
{
|
||||
class DimensionsToypadDevice final : public Device
|
||||
{
|
||||
public:
|
||||
DimensionsToypadDevice();
|
||||
~DimensionsToypadDevice() = default;
|
||||
|
||||
bool Open() override;
|
||||
|
||||
void Close() override;
|
||||
|
||||
bool IsOpened() override;
|
||||
|
||||
ReadResult Read(ReadMessage* message) override;
|
||||
|
||||
WriteResult Write(WriteMessage* message) override;
|
||||
|
||||
bool GetDescriptor(uint8 descType,
|
||||
uint8 descIndex,
|
||||
uint8 lang,
|
||||
uint8* output,
|
||||
uint32 outputMaxLength) override;
|
||||
|
||||
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||
|
||||
bool SetReport(ReportMessage* message) override;
|
||||
|
||||
private:
|
||||
bool m_IsOpened;
|
||||
};
|
||||
|
||||
class DimensionsUSB
|
||||
{
|
||||
public:
|
||||
struct DimensionsMini final
|
||||
{
|
||||
std::unique_ptr<FileStream> dimFile;
|
||||
std::array<uint8, 0x2D * 0x04> data{};
|
||||
uint8 index = 255;
|
||||
uint8 pad = 255;
|
||||
uint32 id = 0;
|
||||
void Save();
|
||||
};
|
||||
|
||||
void SendCommand(std::span<const uint8, 32> buf);
|
||||
std::array<uint8, 32> GetStatus();
|
||||
|
||||
void GenerateRandomNumber(std::span<const uint8, 8> buf, uint8 sequence,
|
||||
std::array<uint8, 32>& replyBuf);
|
||||
void InitializeRNG(uint32 seed);
|
||||
void GetChallengeResponse(std::span<const uint8, 8> buf, uint8 sequence,
|
||||
std::array<uint8, 32>& replyBuf);
|
||||
void QueryBlock(uint8 index, uint8 page, std::array<uint8, 32>& replyBuf,
|
||||
uint8 sequence);
|
||||
void WriteBlock(uint8 index, uint8 page, std::span<const uint8, 4> toWriteBuf, std::array<uint8, 32>& replyBuf,
|
||||
uint8 sequence);
|
||||
void GetModel(std::span<const uint8, 8> buf, uint8 sequence,
|
||||
std::array<uint8, 32>& replyBuf);
|
||||
|
||||
bool RemoveFigure(uint8 pad, uint8 index, bool fullRemove);
|
||||
bool TempRemove(uint8 index);
|
||||
bool CancelRemove(uint8 index);
|
||||
uint32 LoadFigure(const std::array<uint8, 0x2D * 0x04>& buf, std::unique_ptr<FileStream> file, uint8 pad, uint8 index);
|
||||
bool CreateFigure(fs::path pathName, uint32 id);
|
||||
bool MoveFigure(uint8 pad, uint8 index, uint8 oldPad, uint8 oldIndex);
|
||||
static std::map<const uint32, const char*> GetListMinifigs();
|
||||
static std::map<const uint32, const char*> GetListTokens();
|
||||
std::string FindFigure(uint32 figNum);
|
||||
|
||||
protected:
|
||||
std::mutex m_dimensionsMutex;
|
||||
std::array<DimensionsMini, 7> m_figures{};
|
||||
|
||||
private:
|
||||
void RandomUID(std::array<uint8, 0x2D * 0x04>& uidBuffer);
|
||||
uint8 GenerateChecksum(const std::array<uint8, 32>& data,
|
||||
int numOfBytes) const;
|
||||
std::array<uint8, 8> Decrypt(std::span<const uint8, 8> buf, std::optional<std::array<uint8, 16>> key);
|
||||
std::array<uint8, 8> Encrypt(std::span<const uint8, 8> buf, std::optional<std::array<uint8, 16>> key);
|
||||
std::array<uint8, 16> GenerateFigureKey(const std::array<uint8, 0x2D * 0x04>& uid);
|
||||
std::array<uint8, 4> PWDGenerate(const std::array<uint8, 0x2D * 0x04>& uid);
|
||||
std::array<uint8, 4> DimensionsRandomize(const std::vector<uint8> key, uint8 count);
|
||||
uint32 GetFigureId(const std::array<uint8, 0x2D * 0x04>& buf);
|
||||
uint32 Scramble(const std::array<uint8, 7>& uid, uint8 count);
|
||||
uint32 GetNext();
|
||||
DimensionsMini& GetFigureByIndex(uint8 index);
|
||||
|
||||
uint32 m_randomA;
|
||||
uint32 m_randomB;
|
||||
uint32 m_randomC;
|
||||
uint32 m_randomD;
|
||||
|
||||
bool m_isAwake = false;
|
||||
|
||||
std::queue<std::array<uint8, 32>> m_figureAddedRemovedResponses;
|
||||
std::queue<std::array<uint8, 32>> m_queries;
|
||||
};
|
||||
extern DimensionsUSB g_dimensionstoypad;
|
||||
|
||||
} // namespace nsyshid
|
|
@ -1017,11 +1017,7 @@ namespace nsyshid
|
|||
std::array<uint8, 16> InfinityUSB::GenerateInfinityFigureKey(const std::vector<uint8>& sha1Data)
|
||||
{
|
||||
std::array<uint8, 20> digest = {};
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, sha1Data.data(), sha1Data.size());
|
||||
SHA1_Final(digest.data(), &ctx);
|
||||
OPENSSL_cleanse(&ctx, sizeof(ctx));
|
||||
SHA1(sha1Data.data(), sha1Data.size(), digest.data());
|
||||
// Infinity AES keys are the first 16 bytes of the SHA1 Digest, every set of 4 bytes need to be
|
||||
// reversed due to endianness
|
||||
std::array<uint8, 16> key = {};
|
||||
|
|
|
@ -1210,6 +1210,14 @@ void nsysnetExport_select(PPCInterpreter_t* hCPU)
|
|||
|
||||
timeval tv = { 0 };
|
||||
|
||||
if (timeOut == NULL)
|
||||
{
|
||||
// return immediately
|
||||
cemuLog_log(LogType::Socket, "select returned immediately because of null timeout");
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 msTimeout = (_swapEndianU32(timeOut->tv_usec) / 1000) + (_swapEndianU32(timeOut->tv_sec) * 1000);
|
||||
uint32 startTime = GetTickCount();
|
||||
while (true)
|
||||
|
|
|
@ -509,7 +509,7 @@ namespace ntag
|
|||
noftHeader->writeCount = _swapEndianU16(_swapEndianU16(noftHeader->writeCount) + 1);
|
||||
}
|
||||
|
||||
memcpy(decryptedBuffer + 0x20, noftHeader, sizeof(noftHeader));
|
||||
memcpy(decryptedBuffer + 0x20, noftHeader, sizeof(NTAGNoftHeader));
|
||||
memcpy(decryptedBuffer + _swapEndianU16(rwHeader->offset), data, dataSize);
|
||||
|
||||
// Encrypt
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
enum class KPAD_ERROR : sint32
|
||||
{
|
||||
NONE = 0,
|
||||
NO_SAMPLE_DATA = -1,
|
||||
NO_CONTROLLER = -2,
|
||||
NOT_INITIALIZED = -5,
|
||||
};
|
||||
|
@ -106,6 +107,9 @@ void padscoreExport_WPADProbe(PPCInterpreter_t* hCPU)
|
|||
}
|
||||
else
|
||||
{
|
||||
if(type)
|
||||
*type = 253;
|
||||
|
||||
osLib_returnFromFunction(hCPU, WPAD_ERR_NO_CONTROLLER);
|
||||
}
|
||||
}
|
||||
|
@ -420,9 +424,12 @@ void padscoreExport_KPADSetConnectCallback(PPCInterpreter_t* hCPU)
|
|||
osLib_returnFromFunction(hCPU, old_callback.GetMPTR());
|
||||
}
|
||||
|
||||
uint64 g_kpadLastRead[InputManager::kMaxWPADControllers] = {0};
|
||||
bool g_kpadIsInited = true;
|
||||
|
||||
sint32 _KPADRead(uint32 channel, KPADStatus_t* samplingBufs, uint32 length, betype<KPAD_ERROR>* errResult)
|
||||
{
|
||||
|
||||
if (channel >= InputManager::kMaxWPADControllers)
|
||||
{
|
||||
debugBreakpoint();
|
||||
|
@ -446,6 +453,19 @@ sint32 _KPADRead(uint32 channel, KPADStatus_t* samplingBufs, uint32 length, bety
|
|||
return 0;
|
||||
}
|
||||
|
||||
// On console new input samples are only received every few ms and calling KPADRead(Ex) clears the internal queue regardless of length value
|
||||
// thus calling KPADRead(Ex) again too soon on the same channel will result in no data being returned
|
||||
// Games that depend on this: Affordable Space Adventures
|
||||
uint64 currentTime = coreinit::OSGetTime();
|
||||
uint64 timeDif = currentTime - g_kpadLastRead[channel];
|
||||
if(length == 0 || timeDif < coreinit::EspressoTime::ConvertNsToTimerTicks(1000000))
|
||||
{
|
||||
if (errResult)
|
||||
*errResult = KPAD_ERROR::NO_SAMPLE_DATA;
|
||||
return 0;
|
||||
}
|
||||
g_kpadLastRead[channel] = currentTime;
|
||||
|
||||
memset(samplingBufs, 0x00, sizeof(KPADStatus_t));
|
||||
samplingBufs->wpadErr = WPAD_ERR_NONE;
|
||||
samplingBufs->data_format = controller->get_data_format();
|
||||
|
@ -474,7 +494,6 @@ void padscoreExport_KPADReadEx(PPCInterpreter_t* hCPU)
|
|||
osLib_returnFromFunction(hCPU, samplesRead);
|
||||
}
|
||||
|
||||
bool debugUseDRC1 = true;
|
||||
void padscoreExport_KPADRead(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(channel, 0);
|
||||
|
@ -726,7 +745,8 @@ namespace padscore
|
|||
// call sampling callback
|
||||
for (auto i = 0; i < InputManager::kMaxWPADControllers; ++i)
|
||||
{
|
||||
if (g_padscore.controller_data[i].sampling_callback) {
|
||||
if (g_padscore.controller_data[i].sampling_callback)
|
||||
{
|
||||
if (const auto controller = instance.get_wpad_controller(i))
|
||||
{
|
||||
cemuLog_log(LogType::InputAPI, "Calling WPADsamplingCallback({})", i);
|
||||
|
@ -741,7 +761,7 @@ namespace padscore
|
|||
{
|
||||
OSCreateAlarm(&g_padscore.alarm);
|
||||
const uint64 start_tick = coreinit::coreinit_getOSTime();
|
||||
const uint64 period_tick = coreinit::EspressoTime::GetTimerClock(); // once a second
|
||||
const uint64 period_tick = coreinit::EspressoTime::GetTimerClock() / 200; // every 5ms
|
||||
MPTR handler = PPCInterpreter_makeCallableExportDepr(TickFunction);
|
||||
OSSetPeriodicAlarm(&g_padscore.alarm, start_tick, period_tick, handler);
|
||||
}
|
||||
|
|
|
@ -427,7 +427,7 @@ namespace proc_ui
|
|||
}
|
||||
if(callbackType != ProcUICallbackId::AcquireForeground)
|
||||
priority = -priority;
|
||||
AddCallbackInternal(funcPtr, userParam, priority, 0, s_CallbackTables[stdx::to_underlying(callbackType)][coreIndex]);
|
||||
AddCallbackInternal(funcPtr, userParam, 0, priority, s_CallbackTables[stdx::to_underlying(callbackType)][coreIndex]);
|
||||
}
|
||||
|
||||
void ProcUIRegisterCallback(ProcUICallbackId callbackType, void* funcPtr, void* userParam, sint32 priority)
|
||||
|
@ -437,7 +437,7 @@ namespace proc_ui
|
|||
|
||||
void ProcUIRegisterBackgroundCallback(void* funcPtr, void* userParam, uint64 tickDelay)
|
||||
{
|
||||
AddCallbackInternal(funcPtr, userParam, 0, tickDelay, s_backgroundCallbackList);
|
||||
AddCallbackInternal(funcPtr, userParam, tickDelay, 0, s_backgroundCallbackList);
|
||||
}
|
||||
|
||||
void FreeCallbackChain(ProcUICallbackList& callbackList)
|
||||
|
|
|
@ -522,10 +522,10 @@ namespace snd_core
|
|||
// called periodically to check for AX updates
|
||||
void AXOut_update()
|
||||
{
|
||||
constexpr auto kTimeout = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(((IAudioAPI::kBlockCount * 3) / 4) * (AX_FRAMES_PER_GROUP * 3)));
|
||||
constexpr auto kWaitDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(3));
|
||||
constexpr auto kWaitDurationFast = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::microseconds(2900));
|
||||
constexpr auto kWaitDurationMinimum = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::microseconds(1700));
|
||||
constexpr static auto kTimeout = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(((IAudioAPI::kBlockCount * 3) / 4) * (AX_FRAMES_PER_GROUP * 3)));
|
||||
constexpr static auto kWaitDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(3));
|
||||
constexpr static auto kWaitDurationFast = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::microseconds(2900));
|
||||
constexpr static auto kWaitDurationMinimum = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::microseconds(1700));
|
||||
|
||||
// if we haven't buffered any blocks, we will wait less time than usual
|
||||
bool additional_blocks_required = false;
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
|
||||
extern bool isLaunchTypeELF;
|
||||
|
||||
bool debugUseDRC = true;
|
||||
VPADDir g_vpadGyroDirOverwrite[VPAD_MAX_CONTROLLERS] =
|
||||
{
|
||||
{{1.0f,0.0f,0.0f}, {0.0f,1.0f,0.0f}, {0.0f, 0.0f, 0.1f}},
|
||||
|
@ -240,19 +239,20 @@ namespace vpad
|
|||
status->tpProcessed2.validity = VPAD_TP_VALIDITY_INVALID_XY;
|
||||
|
||||
const auto controller = InputManager::instance().get_vpad_controller(channel);
|
||||
if (!controller || debugUseDRC == false)
|
||||
if (!controller)
|
||||
{
|
||||
// no controller
|
||||
// most games expect the Wii U GamePad to be connected, so even if the user has not set it up we should still return empty samples for channel 0
|
||||
if(channel != 0)
|
||||
{
|
||||
if (error)
|
||||
*error = VPAD_READ_ERR_NO_CONTROLLER;
|
||||
if (length > 0)
|
||||
status->vpadErr = -1;
|
||||
return 0;
|
||||
}
|
||||
if (error)
|
||||
*error = VPAD_READ_ERR_NONE; // VPAD_READ_ERR_NO_DATA; // VPAD_READ_ERR_NO_CONTROLLER;
|
||||
|
||||
*error = VPAD_READ_ERR_NONE;
|
||||
return 1;
|
||||
//osLib_returnFromFunction(hCPU, 1); return;
|
||||
}
|
||||
|
||||
if (channel != 0)
|
||||
{
|
||||
debugBreakpoint();
|
||||
}
|
||||
|
||||
const bool vpadDelayEnabled = ActiveSettings::VPADDelayEnabled();
|
||||
|
@ -274,9 +274,7 @@ namespace vpad
|
|||
// not ready yet
|
||||
if (error)
|
||||
*error = VPAD_READ_ERR_NONE;
|
||||
|
||||
return 0;
|
||||
//osLib_returnFromFunction(hCPU, 0); return;
|
||||
}
|
||||
else if (dif <= ESPRESSO_TIMER_CLOCK)
|
||||
{
|
||||
|
|
|
@ -91,7 +91,11 @@ bool cemuLog_log(LogType type, std::basic_string<T> formatStr, TArgs&&... args)
|
|||
else
|
||||
{
|
||||
const auto format_view = fmt::basic_string_view<T>(formatStr);
|
||||
#if FMT_VERSION >= 110000
|
||||
const auto text = fmt::vformat(format_view, fmt::make_format_args<fmt::buffered_context<T>>(args...));
|
||||
#else
|
||||
const auto text = fmt::vformat(format_view, fmt::make_format_args<fmt::buffer_context<T>>(args...));
|
||||
#endif
|
||||
cemuLog_log(type, std::basic_string_view(text.data(), text.size()));
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -277,7 +277,8 @@ void NexFriends::handleResponse_getAllInformation(nexServiceResponse_t* response
|
|||
}
|
||||
NexFriends* session = (NexFriends*)nexFriends;
|
||||
session->myPreference = nexPrincipalPreference(&response->data);
|
||||
nexComment comment(&response->data);
|
||||
auto comment = nexComment(&response->data);
|
||||
session->myComment = comment;
|
||||
if (response->data.hasReadOutOfBounds())
|
||||
return;
|
||||
// acquire lock on lists
|
||||
|
@ -391,6 +392,28 @@ void NexFriends::getMyPreference(nexPrincipalPreference& preference)
|
|||
preference = myPreference;
|
||||
}
|
||||
|
||||
bool NexFriends::updateCommentAsync(nexComment newComment, std::function<void(RpcErrorCode)> cb)
|
||||
{
|
||||
uint8 tempNexBufferArray[1024];
|
||||
nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true);
|
||||
newComment.writeData(&packetBuffer);
|
||||
nexCon->callMethod(
|
||||
NEX_PROTOCOL_FRIENDS_WIIU, 15, &packetBuffer, [this, cb, newComment](nexServiceResponse_t* response) -> void {
|
||||
if (!response->isSuccessful)
|
||||
return cb(NexFriends::ERR_RPC_FAILED);
|
||||
this->myComment = newComment;
|
||||
return cb(NexFriends::ERR_NONE);
|
||||
},
|
||||
true);
|
||||
// TEST
|
||||
return true;
|
||||
}
|
||||
|
||||
void NexFriends::getMyComment(nexComment& comment)
|
||||
{
|
||||
comment = myComment;
|
||||
}
|
||||
|
||||
bool NexFriends::addProvisionalFriendByPidGuessed(uint32 principalId)
|
||||
{
|
||||
uint8 tempNexBufferArray[512];
|
||||
|
|
|
@ -297,7 +297,9 @@ public:
|
|||
|
||||
void writeData(nexPacketBuffer* pb) const override
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
pb->writeU8(ukn0);
|
||||
pb->writeString(commentString.c_str());
|
||||
pb->writeU64(ukn1);
|
||||
}
|
||||
|
||||
void readData(nexPacketBuffer* pb) override
|
||||
|
@ -554,6 +556,7 @@ public:
|
|||
bool getFriendRequestByMessageId(nexFriendRequest& friendRequestData, bool* isIncoming, uint64 messageId);
|
||||
bool isOnline();
|
||||
void getMyPreference(nexPrincipalPreference& preference);
|
||||
void getMyComment(nexComment& comment);
|
||||
|
||||
// asynchronous API (data has to be requested)
|
||||
bool addProvisionalFriend(char* name, std::function<void(RpcErrorCode)> cb);
|
||||
|
@ -565,6 +568,7 @@ public:
|
|||
void acceptFriendRequest(uint64 messageId, std::function<void(RpcErrorCode)> cb);
|
||||
void deleteFriendRequest(uint64 messageId, std::function<void(RpcErrorCode)> cb); // rejecting incoming friend request (differs from blocking friend requests)
|
||||
bool updatePreferencesAsync(const nexPrincipalPreference newPreferences, std::function<void(RpcErrorCode)> cb);
|
||||
bool updateCommentAsync(const nexComment newComment, std::function<void(RpcErrorCode)> cb);
|
||||
void updateMyPresence(nexPresenceV2& myPresence);
|
||||
|
||||
void setNotificationHandler(void(*notificationHandler)(NOTIFICATION_TYPE notificationType, uint32 pid));
|
||||
|
@ -619,6 +623,7 @@ private:
|
|||
// local friend state
|
||||
nexPresenceV2 myPresence;
|
||||
nexPrincipalPreference myPreference;
|
||||
nexComment myComment;
|
||||
|
||||
std::recursive_mutex mtx_lists;
|
||||
std::vector<nexFriend> list_friends;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
using MPTR = uint32; // generic address in PowerPC memory space
|
||||
|
||||
#define MPTR_NULL (0)
|
||||
#define MPTR_NULL (0)
|
||||
|
||||
using VAddr = uint32; // virtual address
|
||||
using PAddr = uint32; // physical address
|
||||
|
@ -14,144 +14,177 @@ extern uint8* PPCInterpreterGetStackPointer();
|
|||
extern uint8* PPCInterpreter_PushAndReturnStackPointer(sint32 offset);
|
||||
extern void PPCInterpreterModifyStackPointer(sint32 offset);
|
||||
|
||||
class MEMPTRBase {};
|
||||
class MEMPTRBase
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class MEMPTR : MEMPTRBase
|
||||
{
|
||||
public:
|
||||
constexpr MEMPTR()
|
||||
: m_value(0) { }
|
||||
public:
|
||||
constexpr MEMPTR() noexcept
|
||||
: m_value(0) {}
|
||||
|
||||
explicit constexpr MEMPTR(uint32 offset)
|
||||
: m_value(offset) { }
|
||||
explicit constexpr MEMPTR(uint32 offset) noexcept
|
||||
: m_value(offset) {}
|
||||
|
||||
explicit constexpr MEMPTR(const uint32be& offset)
|
||||
: m_value(offset) { }
|
||||
explicit constexpr MEMPTR(const uint32be& offset) noexcept
|
||||
: m_value(offset) {}
|
||||
|
||||
constexpr MEMPTR(std::nullptr_t)
|
||||
: m_value(0) { }
|
||||
constexpr MEMPTR(std::nullptr_t) noexcept
|
||||
: m_value(0) {}
|
||||
|
||||
MEMPTR(T* ptr)
|
||||
MEMPTR(T* ptr) noexcept
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
m_value = 0;
|
||||
else
|
||||
{
|
||||
cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000);
|
||||
m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base);
|
||||
}
|
||||
{
|
||||
cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000);
|
||||
m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr MEMPTR(const MEMPTR& memptr)
|
||||
: m_value(memptr.m_value) { }
|
||||
constexpr MEMPTR(const MEMPTR&) noexcept = default;
|
||||
|
||||
constexpr MEMPTR& operator=(const MEMPTR& memptr)
|
||||
{
|
||||
m_value = memptr.m_value;
|
||||
return *this;
|
||||
}
|
||||
constexpr MEMPTR& operator=(const MEMPTR&) noexcept = default;
|
||||
|
||||
constexpr MEMPTR& operator=(const uint32& offset)
|
||||
constexpr MEMPTR& operator=(const uint32& offset) noexcept
|
||||
{
|
||||
m_value = offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr MEMPTR& operator=(const std::nullptr_t rhs)
|
||||
constexpr MEMPTR& operator=(std::nullptr_t) noexcept
|
||||
{
|
||||
m_value = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MEMPTR& operator=(T* ptr)
|
||||
MEMPTR& operator=(T* ptr) noexcept
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
if (ptr == nullptr)
|
||||
m_value = 0;
|
||||
else
|
||||
{
|
||||
cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000);
|
||||
m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base);
|
||||
}
|
||||
{
|
||||
cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000);
|
||||
m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool atomic_compare_exchange(T* comparePtr, T* newPtr)
|
||||
bool atomic_compare_exchange(T* comparePtr, T* newPtr) noexcept
|
||||
{
|
||||
MEMPTR<T> mp_compare = comparePtr;
|
||||
MEMPTR<T> mp_new = newPtr;
|
||||
std::atomic<uint32be>* thisValueAtomic = (std::atomic<uint32be>*)&m_value;
|
||||
auto* thisValueAtomic = reinterpret_cast<std::atomic<uint32be>*>(&m_value);
|
||||
return thisValueAtomic->compare_exchange_strong(mp_compare.m_value, mp_new.m_value);
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const noexcept { return m_value != 0; }
|
||||
|
||||
constexpr operator T*() const noexcept { return GetPtr(); } // allow implicit cast to wrapped pointer type
|
||||
|
||||
|
||||
template <typename X>
|
||||
explicit operator MEMPTR<X>() const { return MEMPTR<X>(this->m_value); }
|
||||
|
||||
//bool operator==(const MEMPTR<T>& v) const { return m_value == v.m_value; }
|
||||
//bool operator==(const T* rhs) const { return (T*)(m_value == 0 ? nullptr : memory_base + (uint32)m_value) == rhs; } -> ambigious (implicit cast to T* allows for T* == T*)
|
||||
//bool operator==(std::nullptr_t rhs) const { return m_value == 0; }
|
||||
|
||||
//bool operator!=(const MEMPTR<T>& v) const { return !(*this == v); }
|
||||
//bool operator!=(const void* rhs) const { return !(*this == rhs); }
|
||||
//bool operator!=(int rhs) const { return !(*this == rhs); }
|
||||
|
||||
//bool operator==(const void* rhs) const { return (void*)(m_value == 0 ? nullptr : memory_base + (uint32)m_value) == rhs; }
|
||||
|
||||
//explicit bool operator==(int rhs) const { return *this == (const void*)(size_t)rhs; }
|
||||
|
||||
|
||||
MEMPTR operator+(const MEMPTR& ptr) { return MEMPTR(this->GetMPTR() + ptr.GetMPTR()); }
|
||||
MEMPTR operator-(const MEMPTR& ptr) { return MEMPTR(this->GetMPTR() - ptr.GetMPTR()); }
|
||||
|
||||
MEMPTR operator+(sint32 v)
|
||||
explicit constexpr operator bool() const noexcept
|
||||
{
|
||||
// pointer arithmetic
|
||||
return MEMPTR(this->GetMPTR() + v * 4);
|
||||
return m_value != 0;
|
||||
}
|
||||
|
||||
MEMPTR operator-(sint32 v)
|
||||
// allow implicit cast to wrapped pointer type
|
||||
constexpr operator T*() const noexcept
|
||||
{
|
||||
// pointer arithmetic
|
||||
return MEMPTR(this->GetMPTR() - v * 4);
|
||||
return GetPtr();
|
||||
}
|
||||
|
||||
template <class Q = T>
|
||||
typename std::enable_if<!std::is_same<Q, void>::value, Q>::type&
|
||||
operator*() const { return *GetPtr(); }
|
||||
template<typename X>
|
||||
explicit operator MEMPTR<X>() const noexcept
|
||||
{
|
||||
return MEMPTR<X>(this->m_value);
|
||||
}
|
||||
|
||||
T* operator->() const { return GetPtr(); }
|
||||
sint32 operator-(const MEMPTR& ptr) noexcept
|
||||
requires(!std::is_void_v<T>)
|
||||
{
|
||||
return static_cast<sint32>(this->GetMPTR() - ptr.GetMPTR());
|
||||
}
|
||||
|
||||
template <class Q = T>
|
||||
typename std::enable_if<!std::is_same<Q, void>::value, Q>::type&
|
||||
operator[](int index) { return GetPtr()[index]; }
|
||||
MEMPTR operator+(sint32 v) noexcept
|
||||
requires(!std::is_void_v<T>)
|
||||
{
|
||||
// pointer arithmetic
|
||||
return MEMPTR(this->GetMPTR() + v * sizeof(T));
|
||||
}
|
||||
|
||||
T* GetPtr() const { return (T*)(m_value == 0 ? nullptr : memory_base + (uint32)m_value); }
|
||||
MEMPTR operator-(sint32 v) noexcept
|
||||
requires(!std::is_void_v<T>)
|
||||
{
|
||||
// pointer arithmetic
|
||||
return MEMPTR(this->GetMPTR() - v * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
C* GetPtr() const { return (C*)(GetPtr()); }
|
||||
MEMPTR& operator+=(sint32 v) noexcept
|
||||
requires(!std::is_void_v<T>)
|
||||
{
|
||||
m_value += v * sizeof(T);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr uint32 GetMPTR() const { return m_value.value(); }
|
||||
constexpr const uint32be& GetBEValue() const { return m_value; }
|
||||
template<typename Q = T>
|
||||
requires(!std::is_void_v<Q>)
|
||||
Q& operator*() const noexcept
|
||||
{
|
||||
return *GetPtr();
|
||||
}
|
||||
|
||||
constexpr bool IsNull() const { return m_value == 0; }
|
||||
constexpr T* operator->() const noexcept
|
||||
{
|
||||
return GetPtr();
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Q = T>
|
||||
requires(!std::is_void_v<Q>)
|
||||
Q& operator[](int index) noexcept
|
||||
{
|
||||
return GetPtr()[index];
|
||||
}
|
||||
|
||||
T* GetPtr() const noexcept
|
||||
{
|
||||
return (T*)(m_value == 0 ? nullptr : memory_base + (uint32)m_value);
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
C* GetPtr() const noexcept
|
||||
{
|
||||
return static_cast<C*>(GetPtr());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint32 GetMPTR() const noexcept
|
||||
{
|
||||
return m_value.value();
|
||||
}
|
||||
[[nodiscard]] constexpr const uint32be& GetBEValue() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsNull() const noexcept
|
||||
{
|
||||
return m_value == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32be m_value;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MEMPTR<void*>) == sizeof(uint32be));
|
||||
static_assert(std::is_trivially_copyable_v<MEMPTR<void*>>);
|
||||
|
||||
#include "StackAllocator.h"
|
||||
#include "SysAllocator.h"
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct fmt::formatter<MEMPTR<T>> : formatter<string_view>
|
||||
{
|
||||
template <typename FormatContext>
|
||||
auto format(const MEMPTR<T>& v, FormatContext& ctx) const -> format_context::iterator { return fmt::format_to(ctx.out(), "{:#x}", v.GetMPTR()); }
|
||||
template<typename FormatContext>
|
||||
auto format(const MEMPTR<T>& v, FormatContext& ctx) const -> format_context::iterator
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "{:#x}", v.GetMPTR());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -121,6 +121,12 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
betype<T>& operator+=(const T& v) requires std::integral<T>
|
||||
{
|
||||
m_value = SwapEndian(T(value() + v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
betype<T>& operator-=(const betype<T>& v)
|
||||
{
|
||||
m_value = SwapEndian(T(value() - v.value()));
|
||||
|
@ -188,17 +194,36 @@ public:
|
|||
return from_bevalue(T(~m_value));
|
||||
}
|
||||
|
||||
// pre-increment
|
||||
betype<T>& operator++() requires std::integral<T>
|
||||
{
|
||||
m_value = SwapEndian(T(value() + 1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// post-increment
|
||||
betype<T> operator++(int) requires std::integral<T>
|
||||
{
|
||||
betype<T> tmp(*this);
|
||||
m_value = SwapEndian(T(value() + 1));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// pre-decrement
|
||||
betype<T>& operator--() requires std::integral<T>
|
||||
{
|
||||
m_value = SwapEndian(T(value() - 1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// post-decrement
|
||||
betype<T> operator--(int) requires std::integral<T>
|
||||
{
|
||||
betype<T> tmp(*this);
|
||||
m_value = SwapEndian(T(value() - 1));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
//T m_value{}; // before 1.26.2
|
||||
T m_value;
|
||||
|
|
|
@ -395,16 +395,10 @@ void vectorRemoveByIndex(std::vector<T>& vec, const size_t index)
|
|||
vec.erase(vec.begin() + index);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
int match_any_of(T1 value, T2 compareTo)
|
||||
template<typename T1, typename... Types>
|
||||
bool match_any_of(T1&& value, Types&&... others)
|
||||
{
|
||||
return value == compareTo;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename... Types>
|
||||
bool match_any_of(T1 value, T2 compareTo, Types&&... others)
|
||||
{
|
||||
return value == compareTo || match_any_of(value, others...);
|
||||
return ((value == others) || ...);
|
||||
}
|
||||
|
||||
// we cache the frequency in a static variable
|
||||
|
@ -502,13 +496,6 @@ bool future_is_ready(std::future<T>& f)
|
|||
#endif
|
||||
}
|
||||
|
||||
// replace with std::scope_exit once available
|
||||
struct scope_exit
|
||||
{
|
||||
std::function<void()> f_;
|
||||
explicit scope_exit(std::function<void()> f) noexcept : f_(std::move(f)) {}
|
||||
~scope_exit() { if (f_) f_(); }
|
||||
};
|
||||
|
||||
// helper function to cast raw pointers to std::atomic
|
||||
// this is technically not legal but works on most platforms as long as alignment restrictions are met and the implementation of atomic doesnt come with additional members
|
||||
|
@ -516,6 +503,8 @@ struct scope_exit
|
|||
template<typename T>
|
||||
std::atomic<T>* _rawPtrToAtomic(T* ptr)
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(std::atomic<T>));
|
||||
cemu_assert_debug((reinterpret_cast<std::uintptr_t>(ptr) % alignof(std::atomic<T>)) == 0);
|
||||
return reinterpret_cast<std::atomic<T>*>(ptr);
|
||||
}
|
||||
|
||||
|
@ -579,13 +568,34 @@ struct fmt::formatter<betype<T>> : fmt::formatter<T>
|
|||
}
|
||||
};
|
||||
|
||||
// useful C++23 stuff that isn't yet widely supported
|
||||
|
||||
// std::to_underlying
|
||||
// useful future C++ stuff
|
||||
namespace stdx
|
||||
{
|
||||
// std::to_underlying
|
||||
template <typename EnumT, typename = std::enable_if_t < std::is_enum<EnumT>{} >>
|
||||
constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) noexcept {
|
||||
return static_cast<std::underlying_type_t<EnumT>>(e);
|
||||
};
|
||||
|
||||
// std::scope_exit
|
||||
template <typename Fn>
|
||||
class scope_exit
|
||||
{
|
||||
Fn m_func;
|
||||
bool m_released = false;
|
||||
public:
|
||||
explicit scope_exit(Fn&& f) noexcept
|
||||
: m_func(std::forward<Fn>(f))
|
||||
{}
|
||||
~scope_exit()
|
||||
{
|
||||
if (!m_released) m_func();
|
||||
}
|
||||
scope_exit(scope_exit&& other) noexcept
|
||||
: m_func(std::move(other.m_func)), m_released(std::exchange(other.m_released, true))
|
||||
{}
|
||||
scope_exit(const scope_exit&) = delete;
|
||||
scope_exit& operator=(scope_exit) = delete;
|
||||
void release() { m_released = true;}
|
||||
};
|
||||
}
|
|
@ -1,36 +1,19 @@
|
|||
#ifndef EMULATOR_NAME
|
||||
|
||||
#define EMULATOR_NAME "Cemu"
|
||||
#define EMULATOR_VERSION_LEAD 2
|
||||
#define EMULATOR_VERSION_MAJOR 0
|
||||
|
||||
// the minor version is used for experimental builds to indicate the build index. Set by command line option from CI build script
|
||||
// if zero, the version text will be constructed as LEAD.MAJOR, otherwise as LEAD.MAJOR-MINOR
|
||||
|
||||
#if defined(EMULATOR_VERSION_MINOR) && EMULATOR_VERSION_MINOR == 0
|
||||
#define EMULATOR_VERSION_SUFFIX ""
|
||||
#else
|
||||
#define EMULATOR_VERSION_SUFFIX " (experimental)"
|
||||
#endif
|
||||
|
||||
#ifndef EMULATOR_VERSION_MINOR
|
||||
#define EMULATOR_VERSION_MINOR 0
|
||||
#endif
|
||||
|
||||
#define _XSTRINGFY(s) _STRINGFY(s)
|
||||
#define _STRINGFY(s) #s
|
||||
|
||||
#if EMULATOR_VERSION_MINOR != 0
|
||||
#if defined(EMULATOR_HASH) && EMULATOR_VERSION_MINOR == 999999
|
||||
#define BUILD_VERSION_WITH_NAME_STRING (EMULATOR_NAME " " _XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) "-" _XSTRINGFY(EMULATOR_HASH) EMULATOR_VERSION_SUFFIX)
|
||||
#define BUILD_VERSION_STRING (_XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) "-" _XSTRINGFY(EMULATOR_HASH) EMULATOR_VERSION_SUFFIX)
|
||||
#if EMULATOR_VERSION_MAJOR != 0
|
||||
#define BUILD_VERSION_WITH_NAME_STRING (EMULATOR_NAME " " _XSTRINGFY(EMULATOR_VERSION_MAJOR) "." _XSTRINGFY(EMULATOR_VERSION_MINOR) EMULATOR_VERSION_SUFFIX)
|
||||
#define BUILD_VERSION_STRING (_XSTRINGFY(EMULATOR_VERSION_MAJOR) "." _XSTRINGFY(EMULATOR_VERSION_MINOR) EMULATOR_VERSION_SUFFIX)
|
||||
#else
|
||||
#define BUILD_VERSION_WITH_NAME_STRING (EMULATOR_NAME " " _XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) "-" _XSTRINGFY(EMULATOR_VERSION_MINOR) EMULATOR_VERSION_SUFFIX)
|
||||
#define BUILD_VERSION_STRING (_XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) "-" _XSTRINGFY(EMULATOR_VERSION_MINOR) EMULATOR_VERSION_SUFFIX)
|
||||
#endif
|
||||
#else
|
||||
#define BUILD_VERSION_STRING (_XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) EMULATOR_VERSION_SUFFIX)
|
||||
#define BUILD_VERSION_WITH_NAME_STRING (EMULATOR_NAME " " _XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) EMULATOR_VERSION_SUFFIX)
|
||||
// no version provided. Only show commit hash
|
||||
#define BUILD_VERSION_STRING (_XSTRINGFY(EMULATOR_HASH) EMULATOR_VERSION_SUFFIX)
|
||||
#define BUILD_VERSION_WITH_NAME_STRING (EMULATOR_NAME " " _XSTRINGFY(EMULATOR_HASH) EMULATOR_VERSION_SUFFIX)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
project(CemuAsm C)
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||
if (CMAKE_OSX_ARCHITECTURES)
|
||||
set(CEMU_ASM_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
|
||||
else()
|
||||
set(CEMU_ASM_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
|
||||
if (CEMU_ASM_ARCHITECTURE MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||
|
||||
if (WIN32)
|
||||
|
||||
|
@ -40,8 +46,8 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
|||
|
||||
endif()
|
||||
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(aarch64)|(AARCH64)")
|
||||
elseif(CEMU_ASM_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
|
||||
add_library(CemuAsm stub.cpp)
|
||||
else()
|
||||
message(STATUS "CemuAsm - Unsupported arch: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
message(STATUS "CemuAsm - Unsupported arch: ${CEMU_ASM_ARCHITECTURE}")
|
||||
endif()
|
||||
|
|
|
@ -38,6 +38,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
|||
fullscreen_menubar = parser.get("fullscreen_menubar", false);
|
||||
feral_gamemode = parser.get("feral_gamemode", false);
|
||||
check_update = parser.get("check_update", check_update);
|
||||
receive_untested_updates = parser.get("receive_untested_updates", check_update);
|
||||
save_screenshot = parser.get("save_screenshot", save_screenshot);
|
||||
did_show_vulkan_warning = parser.get("vk_warning", did_show_vulkan_warning);
|
||||
did_show_graphic_pack_download = parser.get("gp_download", did_show_graphic_pack_download);
|
||||
|
@ -345,6 +346,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
|||
auto usbdevices = parser.get("EmulatedUsbDevices");
|
||||
emulated_usb_devices.emulate_skylander_portal = usbdevices.get("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal);
|
||||
emulated_usb_devices.emulate_infinity_base = usbdevices.get("EmulateInfinityBase", emulated_usb_devices.emulate_infinity_base);
|
||||
emulated_usb_devices.emulate_dimensions_toypad = usbdevices.get("EmulateDimensionsToypad", emulated_usb_devices.emulate_dimensions_toypad);
|
||||
}
|
||||
|
||||
void CemuConfig::Save(XMLConfigParser& parser)
|
||||
|
@ -360,6 +362,7 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
|||
config.set<bool>("fullscreen_menubar", fullscreen_menubar);
|
||||
config.set<bool>("feral_gamemode", feral_gamemode);
|
||||
config.set<bool>("check_update", check_update);
|
||||
config.set<bool>("receive_untested_updates", receive_untested_updates);
|
||||
config.set<bool>("save_screenshot", save_screenshot);
|
||||
config.set<bool>("vk_warning", did_show_vulkan_warning);
|
||||
config.set<bool>("gp_download", did_show_graphic_pack_download);
|
||||
|
@ -543,6 +546,7 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
|||
auto usbdevices = config.set("EmulatedUsbDevices");
|
||||
usbdevices.set("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal.GetValue());
|
||||
usbdevices.set("EmulateInfinityBase", emulated_usb_devices.emulate_infinity_base.GetValue());
|
||||
usbdevices.set("EmulateDimensionsToypad", emulated_usb_devices.emulate_dimensions_toypad.GetValue());
|
||||
}
|
||||
|
||||
GameEntry* CemuConfig::GetGameEntryByTitleId(uint64 titleId)
|
||||
|
|
|
@ -194,7 +194,7 @@ ENABLE_ENUM_ITERATORS(CrashDump, CrashDump::Disabled, CrashDump::Enabled);
|
|||
template <>
|
||||
struct fmt::formatter<PrecompiledShaderOption> : formatter<string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const PrecompiledShaderOption c, FormatContext &ctx) {
|
||||
auto format(const PrecompiledShaderOption c, FormatContext &ctx) const {
|
||||
string_view name;
|
||||
switch (c)
|
||||
{
|
||||
|
@ -209,7 +209,7 @@ struct fmt::formatter<PrecompiledShaderOption> : formatter<string_view> {
|
|||
template <>
|
||||
struct fmt::formatter<AccurateShaderMulOption> : formatter<string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const AccurateShaderMulOption c, FormatContext &ctx) {
|
||||
auto format(const AccurateShaderMulOption c, FormatContext &ctx) const {
|
||||
string_view name;
|
||||
switch (c)
|
||||
{
|
||||
|
@ -223,7 +223,7 @@ struct fmt::formatter<AccurateShaderMulOption> : formatter<string_view> {
|
|||
template <>
|
||||
struct fmt::formatter<CPUMode> : formatter<string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const CPUMode c, FormatContext &ctx) {
|
||||
auto format(const CPUMode c, FormatContext &ctx) const {
|
||||
string_view name;
|
||||
switch (c)
|
||||
{
|
||||
|
@ -240,7 +240,7 @@ struct fmt::formatter<CPUMode> : formatter<string_view> {
|
|||
template <>
|
||||
struct fmt::formatter<CPUModeLegacy> : formatter<string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const CPUModeLegacy c, FormatContext &ctx) {
|
||||
auto format(const CPUModeLegacy c, FormatContext &ctx) const {
|
||||
string_view name;
|
||||
switch (c)
|
||||
{
|
||||
|
@ -257,7 +257,7 @@ struct fmt::formatter<CPUModeLegacy> : formatter<string_view> {
|
|||
template <>
|
||||
struct fmt::formatter<CafeConsoleRegion> : formatter<string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const CafeConsoleRegion v, FormatContext &ctx) {
|
||||
auto format(const CafeConsoleRegion v, FormatContext &ctx) const {
|
||||
string_view name;
|
||||
switch (v)
|
||||
{
|
||||
|
@ -413,7 +413,8 @@ struct CemuConfig
|
|||
Vector2i pad_size{ -1,-1 };
|
||||
ConfigValue<bool> pad_maximized;
|
||||
|
||||
ConfigValue<bool> check_update{false};
|
||||
ConfigValue<bool> check_update{true};
|
||||
ConfigValue<bool> receive_untested_updates{false};
|
||||
ConfigValue<bool> save_screenshot{true};
|
||||
|
||||
ConfigValue<bool> did_show_vulkan_warning{false};
|
||||
|
@ -441,7 +442,7 @@ struct CemuConfig
|
|||
ConfigValue<int> vsync{ 0 }; // 0 = off, 1+ = on depending on render backend
|
||||
ConfigValue<bool> gx2drawdone_sync {true};
|
||||
ConfigValue<bool> render_upside_down{ false };
|
||||
ConfigValue<bool> async_compile{ false };
|
||||
ConfigValue<bool> async_compile{ true };
|
||||
|
||||
ConfigValue<bool> vk_accurate_barriers{ true };
|
||||
|
||||
|
@ -520,6 +521,7 @@ struct CemuConfig
|
|||
{
|
||||
ConfigValue<bool> emulate_skylander_portal{false};
|
||||
ConfigValue<bool> emulate_infinity_base{false};
|
||||
ConfigValue<bool> emulate_dimensions_toypad{false};
|
||||
}emulated_usb_devices{};
|
||||
|
||||
private:
|
||||
|
|
|
@ -112,10 +112,10 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
|
|||
{
|
||||
requireConsole();
|
||||
std::string versionStr;
|
||||
#if EMULATOR_VERSION_MINOR == 0
|
||||
versionStr = fmt::format("{}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX);
|
||||
#if EMULATOR_VERSION_PATCH == 0
|
||||
versionStr = fmt::format("{}.{}{}", EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_SUFFIX);
|
||||
#else
|
||||
versionStr = fmt::format("{}.{}-{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_SUFFIX);
|
||||
versionStr = fmt::format("{}.{}-{}{}", EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, EMULATOR_VERSION_SUFFIX);
|
||||
#endif
|
||||
std::cout << versionStr << std::endl;
|
||||
return false; // exit in main
|
||||
|
@ -199,7 +199,11 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
|
|||
std::string errorMsg;
|
||||
errorMsg.append("Error while trying to parse command line parameter:\n");
|
||||
errorMsg.append(ex.what());
|
||||
#if BOOST_OS_WINDOWS
|
||||
wxMessageBox(errorMsg, "Parameter error", wxICON_ERROR);
|
||||
#else
|
||||
std::cout << errorMsg << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
#if BOOST_OS_LINUX && HAS_WAYLAND
|
||||
#include "gui/helpers/wxWayland.h"
|
||||
#endif
|
||||
#if __WXGTK__
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
#include <wx/image.h>
|
||||
#include <wx/filename.h>
|
||||
|
|
|
@ -116,9 +116,11 @@ bool CemuUpdateWindow::QueryUpdateInfo(std::string& downloadUrlOut, std::string&
|
|||
#elif BOOST_OS_MACOS
|
||||
urlStr.append("&platform=macos_bundle_x86");
|
||||
#elif
|
||||
|
||||
#error Name for current platform is missing
|
||||
#endif
|
||||
const auto& config = GetConfig();
|
||||
if(config.receive_untested_updates)
|
||||
urlStr.append("&allowNewUpdates=1");
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, urlStr.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
|
|
|
@ -115,7 +115,7 @@ void DownloadGraphicPacksWindow::UpdateThread()
|
|||
curlDownloadFileState_t tempDownloadState;
|
||||
std::string queryUrl("https://cemu.info/api2/query_graphicpack_url.php?");
|
||||
char temp[64];
|
||||
sprintf(temp, "version=%d.%d.%d", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR);
|
||||
sprintf(temp, "version=%d.%d.%d", EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH);
|
||||
queryUrl.append(temp);
|
||||
queryUrl.append("&");
|
||||
sprintf(temp, "t=%u", (uint32)std::chrono::seconds(std::time(NULL)).count()); // add a dynamic part to the url to bypass overly aggressive caching (like some proxies do)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h"
|
||||
#include "EmulatedUSBDeviceFrame.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -8,14 +8,17 @@
|
|||
#include "util/helpers/helpers.h"
|
||||
|
||||
#include "Cafe/OS/libs/nsyshid/nsyshid.h"
|
||||
#include "Cafe/OS/libs/nsyshid/Dimensions.h"
|
||||
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/panel.h>
|
||||
|
@ -29,7 +32,6 @@
|
|||
#include <wx/wfstream.h>
|
||||
|
||||
#include "resource/embedded/resources.h"
|
||||
#include "EmulatedUSBDeviceFrame.h"
|
||||
|
||||
EmulatedUSBDeviceFrame::EmulatedUSBDeviceFrame(wxWindow* parent)
|
||||
: wxFrame(parent, wxID_ANY, _("Emulated USB Devices"), wxDefaultPosition,
|
||||
|
@ -44,6 +46,7 @@ EmulatedUSBDeviceFrame::EmulatedUSBDeviceFrame(wxWindow* parent)
|
|||
|
||||
notebook->AddPage(AddSkylanderPage(notebook), _("Skylanders Portal"));
|
||||
notebook->AddPage(AddInfinityPage(notebook), _("Infinity Base"));
|
||||
notebook->AddPage(AddDimensionsPage(notebook), _("Dimensions Toypad"));
|
||||
|
||||
sizer->Add(notebook, 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
|
@ -120,8 +123,52 @@ wxPanel* EmulatedUSBDeviceFrame::AddInfinityPage(wxNotebook* notebook)
|
|||
return panel;
|
||||
}
|
||||
|
||||
wxBoxSizer* EmulatedUSBDeviceFrame::AddSkylanderRow(uint8 rowNumber,
|
||||
wxStaticBox* box)
|
||||
wxPanel* EmulatedUSBDeviceFrame::AddDimensionsPage(wxNotebook* notebook)
|
||||
{
|
||||
auto* panel = new wxPanel(notebook);
|
||||
auto* panel_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto* box = new wxStaticBox(panel, wxID_ANY, _("Dimensions Manager"));
|
||||
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
|
||||
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_emulateToypad =
|
||||
new wxCheckBox(box, wxID_ANY, _("Emulate Dimensions Toypad"));
|
||||
m_emulateToypad->SetValue(
|
||||
GetConfig().emulated_usb_devices.emulate_dimensions_toypad);
|
||||
m_emulateToypad->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
|
||||
GetConfig().emulated_usb_devices.emulate_dimensions_toypad =
|
||||
m_emulateToypad->IsChecked();
|
||||
g_config.Save();
|
||||
});
|
||||
row->Add(m_emulateToypad, 1, wxEXPAND | wxALL, 2);
|
||||
box_sizer->Add(row, 1, wxEXPAND | wxALL, 2);
|
||||
auto* top_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* bottom_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
auto* dummy = new wxStaticText(box, wxID_ANY, "");
|
||||
|
||||
top_row->Add(AddDimensionPanel(2, 0, box), 1, wxEXPAND | wxALL, 2);
|
||||
top_row->Add(dummy, 1, wxEXPAND | wxLEFT | wxRIGHT, 2);
|
||||
top_row->Add(AddDimensionPanel(1, 1, box), 1, wxEXPAND | wxALL, 2);
|
||||
top_row->Add(dummy, 1, wxEXPAND | wxLEFT | wxRIGHT, 2);
|
||||
top_row->Add(AddDimensionPanel(3, 2, box), 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
bottom_row->Add(AddDimensionPanel(2, 3, box), 1, wxEXPAND | wxALL, 2);
|
||||
bottom_row->Add(AddDimensionPanel(2, 4, box), 1, wxEXPAND | wxALL, 2);
|
||||
bottom_row->Add(dummy, 1, wxEXPAND | wxLEFT | wxRIGHT, 0);
|
||||
bottom_row->Add(AddDimensionPanel(3, 5, box), 1, wxEXPAND | wxALL, 2);
|
||||
bottom_row->Add(AddDimensionPanel(3, 6, box), 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
box_sizer->Add(top_row, 1, wxEXPAND | wxALL, 2);
|
||||
box_sizer->Add(bottom_row, 1, wxEXPAND | wxALL, 2);
|
||||
panel_sizer->Add(box_sizer, 1, wxEXPAND | wxALL, 2);
|
||||
panel->SetSizerAndFit(panel_sizer);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
wxBoxSizer* EmulatedUSBDeviceFrame::AddSkylanderRow(uint8 rowNumber, wxStaticBox* box)
|
||||
{
|
||||
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
|
@ -184,6 +231,44 @@ wxBoxSizer* EmulatedUSBDeviceFrame::AddInfinityRow(wxString name, uint8 rowNumbe
|
|||
return row;
|
||||
}
|
||||
|
||||
wxBoxSizer* EmulatedUSBDeviceFrame::AddDimensionPanel(uint8 pad, uint8 index, wxStaticBox* box)
|
||||
{
|
||||
auto* panel = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* combo_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_dimensionSlots[index] = new wxTextCtrl(box, wxID_ANY, _("None"), wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_READONLY);
|
||||
combo_row->Add(m_dimensionSlots[index], 1, wxEXPAND | wxALL, 2);
|
||||
auto* move_button = new wxButton(box, wxID_ANY, _("Move"));
|
||||
move_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
|
||||
MoveMinifig(pad, index);
|
||||
});
|
||||
|
||||
combo_row->Add(move_button, 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
auto* button_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* load_button = new wxButton(box, wxID_ANY, _("Load"));
|
||||
load_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
|
||||
LoadMinifig(pad, index);
|
||||
});
|
||||
auto* clear_button = new wxButton(box, wxID_ANY, _("Clear"));
|
||||
clear_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
|
||||
ClearMinifig(pad, index);
|
||||
});
|
||||
auto* create_button = new wxButton(box, wxID_ANY, _("Create"));
|
||||
create_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
|
||||
CreateMinifig(pad, index);
|
||||
});
|
||||
button_row->Add(clear_button, 1, wxEXPAND | wxALL, 2);
|
||||
button_row->Add(create_button, 1, wxEXPAND | wxALL, 2);
|
||||
button_row->Add(load_button, 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
panel->Add(combo_row, 1, wxEXPAND | wxALL, 2);
|
||||
panel->Add(button_row, 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::LoadSkylander(uint8 slot)
|
||||
{
|
||||
wxFileDialog openFileDialog(this, _("Open Skylander dump"), "", "",
|
||||
|
@ -307,8 +392,8 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot)
|
|||
return;
|
||||
|
||||
m_filePath = saveFileDialog.GetPath();
|
||||
|
||||
if(!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar))
|
||||
|
||||
if (!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar))
|
||||
{
|
||||
wxMessageDialog errorMessage(this, "Failed to create file");
|
||||
errorMessage.ShowModal();
|
||||
|
@ -351,6 +436,80 @@ wxString CreateSkylanderDialog::GetFilePath() const
|
|||
return m_filePath;
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::UpdateSkylanderEdits()
|
||||
{
|
||||
for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
|
||||
{
|
||||
std::string displayString;
|
||||
if (auto sd = m_skySlots[i])
|
||||
{
|
||||
auto [portalSlot, skyId, skyVar] = sd.value();
|
||||
displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayString = "None";
|
||||
}
|
||||
|
||||
m_skylanderSlots[i]->ChangeValue(displayString);
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::LoadFigure(uint8 slot)
|
||||
{
|
||||
wxFileDialog openFileDialog(this, _("Open Infinity Figure dump"), "", "",
|
||||
"BIN files (*.bin)|*.bin",
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty())
|
||||
{
|
||||
wxMessageDialog errorMessage(this, "File Okay Error");
|
||||
errorMessage.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
LoadFigurePath(slot, openFileDialog.GetPath());
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::LoadFigurePath(uint8 slot, wxString path)
|
||||
{
|
||||
std::unique_ptr<FileStream> infFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
|
||||
if (!infFile)
|
||||
{
|
||||
wxMessageDialog errorMessage(this, "File Open Error");
|
||||
errorMessage.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8, nsyshid::INF_FIGURE_SIZE> fileData;
|
||||
if (infFile->readData(fileData.data(), fileData.size()) != fileData.size())
|
||||
{
|
||||
wxMessageDialog open_error(this, "Failed to read file! File was too small");
|
||||
open_error.ShowModal();
|
||||
return;
|
||||
}
|
||||
ClearFigure(slot);
|
||||
|
||||
uint32 number = nsyshid::g_infinitybase.LoadFigure(fileData, std::move(infFile), slot);
|
||||
m_infinitySlots[slot]->ChangeValue(nsyshid::g_infinitybase.FindFigure(number).second);
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::CreateFigure(uint8 slot)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Create Figure: {}", slot);
|
||||
CreateInfinityFigureDialog create_dlg(this, slot);
|
||||
create_dlg.ShowModal();
|
||||
if (create_dlg.GetReturnCode() == 1)
|
||||
{
|
||||
LoadFigurePath(slot, create_dlg.GetFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::ClearFigure(uint8 slot)
|
||||
{
|
||||
m_infinitySlots[slot]->ChangeValue("None");
|
||||
nsyshid::g_infinitybase.RemoveFigure(slot);
|
||||
}
|
||||
|
||||
CreateInfinityFigureDialog::CreateInfinityFigureDialog(wxWindow* parent, uint8 slot)
|
||||
: wxDialog(parent, wxID_ANY, _("Infinity Figure Creator"), wxDefaultPosition, wxSize(500, 150))
|
||||
{
|
||||
|
@ -447,76 +606,231 @@ wxString CreateInfinityFigureDialog::GetFilePath() const
|
|||
return m_filePath;
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::LoadFigure(uint8 slot)
|
||||
void EmulatedUSBDeviceFrame::LoadMinifig(uint8 pad, uint8 index)
|
||||
{
|
||||
wxFileDialog openFileDialog(this, _("Open Infinity Figure dump"), "", "",
|
||||
"BIN files (*.bin)|*.bin",
|
||||
wxFileDialog openFileDialog(this, _("Load Dimensions Figure"), "", "",
|
||||
"Dimensions files (*.bin)|*.bin",
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty())
|
||||
return;
|
||||
|
||||
LoadMinifigPath(openFileDialog.GetPath(), pad, index);
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::LoadMinifigPath(wxString path_name, uint8 pad, uint8 index)
|
||||
{
|
||||
std::unique_ptr<FileStream> dim_file(FileStream::openFile2(_utf8ToPath(path_name.utf8_string()), true));
|
||||
if (!dim_file)
|
||||
{
|
||||
wxMessageDialog errorMessage(this, "File Okay Error");
|
||||
wxMessageDialog errorMessage(this, "Failed to open minifig file");
|
||||
errorMessage.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
LoadFigurePath(slot, openFileDialog.GetPath());
|
||||
}
|
||||
std::array<uint8, 0x2D * 0x04> file_data;
|
||||
|
||||
void EmulatedUSBDeviceFrame::LoadFigurePath(uint8 slot, wxString path)
|
||||
{
|
||||
std::unique_ptr<FileStream> infFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
|
||||
if (!infFile)
|
||||
if (dim_file->readData(file_data.data(), file_data.size()) != file_data.size())
|
||||
{
|
||||
wxMessageDialog errorMessage(this, "File Open Error");
|
||||
wxMessageDialog errorMessage(this, "Failed to read minifig file data");
|
||||
errorMessage.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8, nsyshid::INF_FIGURE_SIZE> fileData;
|
||||
if (infFile->readData(fileData.data(), fileData.size()) != fileData.size())
|
||||
{
|
||||
wxMessageDialog open_error(this, "Failed to read file! File was too small");
|
||||
open_error.ShowModal();
|
||||
return;
|
||||
}
|
||||
ClearFigure(slot);
|
||||
ClearMinifig(pad, index);
|
||||
|
||||
uint32 number = nsyshid::g_infinitybase.LoadFigure(fileData, std::move(infFile), slot);
|
||||
m_infinitySlots[slot]->ChangeValue(nsyshid::g_infinitybase.FindFigure(number).second);
|
||||
uint32 id = nsyshid::g_dimensionstoypad.LoadFigure(file_data, std::move(dim_file), pad, index);
|
||||
m_dimensionSlots[index]->ChangeValue(nsyshid::g_dimensionstoypad.FindFigure(id));
|
||||
m_dimSlots[index] = id;
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::CreateFigure(uint8 slot)
|
||||
void EmulatedUSBDeviceFrame::ClearMinifig(uint8 pad, uint8 index)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Create Figure: {}", slot);
|
||||
CreateInfinityFigureDialog create_dlg(this, slot);
|
||||
nsyshid::g_dimensionstoypad.RemoveFigure(pad, index, true);
|
||||
m_dimensionSlots[index]->ChangeValue("None");
|
||||
m_dimSlots[index] = std::nullopt;
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::CreateMinifig(uint8 pad, uint8 index)
|
||||
{
|
||||
CreateDimensionFigureDialog create_dlg(this);
|
||||
create_dlg.ShowModal();
|
||||
if (create_dlg.GetReturnCode() == 1)
|
||||
{
|
||||
LoadFigurePath(slot, create_dlg.GetFilePath());
|
||||
LoadMinifigPath(create_dlg.GetFilePath(), pad, index);
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::ClearFigure(uint8 slot)
|
||||
void EmulatedUSBDeviceFrame::MoveMinifig(uint8 pad, uint8 index)
|
||||
{
|
||||
m_infinitySlots[slot]->ChangeValue("None");
|
||||
nsyshid::g_infinitybase.RemoveFigure(slot);
|
||||
}
|
||||
if (!m_dimSlots[index])
|
||||
return;
|
||||
|
||||
void EmulatedUSBDeviceFrame::UpdateSkylanderEdits()
|
||||
{
|
||||
for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
|
||||
MoveDimensionFigureDialog move_dlg(this, index);
|
||||
nsyshid::g_dimensionstoypad.TempRemove(index);
|
||||
move_dlg.ShowModal();
|
||||
if (move_dlg.GetReturnCode() == 1)
|
||||
{
|
||||
std::string displayString;
|
||||
if (auto sd = m_skySlots[i])
|
||||
nsyshid::g_dimensionstoypad.MoveFigure(move_dlg.GetNewPad(), move_dlg.GetNewIndex(), pad, index);
|
||||
if (index != move_dlg.GetNewIndex())
|
||||
{
|
||||
auto [portalSlot, skyId, skyVar] = sd.value();
|
||||
displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar);
|
||||
m_dimSlots[move_dlg.GetNewIndex()] = m_dimSlots[index];
|
||||
m_dimensionSlots[move_dlg.GetNewIndex()]->ChangeValue(m_dimensionSlots[index]->GetValue());
|
||||
m_dimSlots[index] = std::nullopt;
|
||||
m_dimensionSlots[index]->ChangeValue("None");
|
||||
}
|
||||
else
|
||||
{
|
||||
displayString = "None";
|
||||
}
|
||||
|
||||
m_skylanderSlots[i]->ChangeValue(displayString);
|
||||
}
|
||||
else
|
||||
{
|
||||
nsyshid::g_dimensionstoypad.CancelRemove(index);
|
||||
}
|
||||
}
|
||||
|
||||
CreateDimensionFigureDialog::CreateDimensionFigureDialog(wxWindow* parent)
|
||||
: wxDialog(parent, wxID_ANY, _("Dimensions Figure Creator"), wxDefaultPosition, wxSize(500, 200))
|
||||
{
|
||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* comboRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
auto* comboBox = new wxComboBox(this, wxID_ANY);
|
||||
comboBox->Append("---Select---", reinterpret_cast<void*>(0xFFFFFFFF));
|
||||
wxArrayString filterlist;
|
||||
for (const auto& it : nsyshid::g_dimensionstoypad.GetListMinifigs())
|
||||
{
|
||||
const uint32 figure = it.first;
|
||||
comboBox->Append(it.second, reinterpret_cast<void*>(figure));
|
||||
filterlist.Add(it.second);
|
||||
}
|
||||
comboBox->SetSelection(0);
|
||||
bool enabled = comboBox->AutoComplete(filterlist);
|
||||
comboRow->Add(comboBox, 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
auto* figNumRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxIntegerValidator<uint32> validator;
|
||||
|
||||
auto* labelFigNum = new wxStaticText(this, wxID_ANY, "Figure Number:");
|
||||
auto* editFigNum = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator);
|
||||
|
||||
figNumRow->Add(labelFigNum, 1, wxALL, 5);
|
||||
figNumRow->Add(editFigNum, 1, wxALL, 5);
|
||||
|
||||
auto* buttonRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
auto* createButton = new wxButton(this, wxID_ANY, _("Create"));
|
||||
createButton->Bind(wxEVT_BUTTON, [editFigNum, this](wxCommandEvent&) {
|
||||
long longFigNum;
|
||||
if (!editFigNum->GetValue().ToLong(&longFigNum) || longFigNum > 0xFFFF)
|
||||
{
|
||||
wxMessageDialog idError(this, "Error Converting Figure Number!", "Number Entered is Invalid");
|
||||
idError.ShowModal();
|
||||
this->EndModal(0);
|
||||
}
|
||||
uint16 figNum = longFigNum & 0xFFFF;
|
||||
auto figure = nsyshid::g_dimensionstoypad.FindFigure(figNum);
|
||||
wxString predefName = figure + ".bin";
|
||||
wxFileDialog
|
||||
saveFileDialog(this, _("Create Dimensions Figure file"), "", predefName,
|
||||
"BIN files (*.bin)|*.bin", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
|
||||
if (saveFileDialog.ShowModal() == wxID_CANCEL)
|
||||
this->EndModal(0);
|
||||
|
||||
m_filePath = saveFileDialog.GetPath();
|
||||
|
||||
nsyshid::g_dimensionstoypad.CreateFigure(_utf8ToPath(m_filePath.utf8_string()), figNum);
|
||||
|
||||
this->EndModal(1);
|
||||
});
|
||||
auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel"));
|
||||
cancelButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
this->EndModal(0);
|
||||
});
|
||||
|
||||
comboBox->Bind(wxEVT_COMBOBOX, [comboBox, editFigNum, this](wxCommandEvent&) {
|
||||
const uint64 fig_info = reinterpret_cast<uint64>(comboBox->GetClientData(comboBox->GetSelection()));
|
||||
if (fig_info != 0xFFFF)
|
||||
{
|
||||
const uint16 figNum = fig_info & 0xFFFF;
|
||||
|
||||
editFigNum->SetValue(wxString::Format(wxT("%i"), figNum));
|
||||
}
|
||||
});
|
||||
|
||||
buttonRow->Add(createButton, 1, wxALL, 5);
|
||||
buttonRow->Add(cancelButton, 1, wxALL, 5);
|
||||
|
||||
sizer->Add(comboRow, 1, wxEXPAND | wxALL, 2);
|
||||
sizer->Add(figNumRow, 1, wxEXPAND | wxALL, 2);
|
||||
sizer->Add(buttonRow, 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
this->SetSizer(sizer);
|
||||
this->Centre(wxBOTH);
|
||||
}
|
||||
|
||||
wxString CreateDimensionFigureDialog::GetFilePath() const
|
||||
{
|
||||
return m_filePath;
|
||||
}
|
||||
|
||||
MoveDimensionFigureDialog::MoveDimensionFigureDialog(EmulatedUSBDeviceFrame* parent, uint8 currentIndex)
|
||||
: wxDialog(parent, wxID_ANY, _("Dimensions Figure Mover"), wxDefaultPosition, wxSize(700, 300))
|
||||
{
|
||||
auto* sizer = new wxGridSizer(2, 5, 10, 10);
|
||||
|
||||
std::array<std::optional<uint32>, 7> ids = parent->GetCurrentMinifigs();
|
||||
|
||||
sizer->Add(AddMinifigSlot(2, 0, currentIndex, ids[0]), 1, wxALL, 5);
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5);
|
||||
sizer->Add(AddMinifigSlot(1, 1, currentIndex, ids[1]), 1, wxALL, 5);
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5);
|
||||
sizer->Add(AddMinifigSlot(3, 2, currentIndex, ids[2]), 1, wxALL, 5);
|
||||
|
||||
sizer->Add(AddMinifigSlot(2, 3, currentIndex, ids[3]), 1, wxALL, 5);
|
||||
sizer->Add(AddMinifigSlot(2, 4, currentIndex, ids[4]), 1, wxALL, 5);
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5);
|
||||
sizer->Add(AddMinifigSlot(3, 5, currentIndex, ids[5]), 1, wxALL, 5);
|
||||
sizer->Add(AddMinifigSlot(3, 6, currentIndex, ids[6]), 1, wxALL, 5);
|
||||
|
||||
this->SetSizer(sizer);
|
||||
this->Centre(wxBOTH);
|
||||
}
|
||||
|
||||
wxBoxSizer* MoveDimensionFigureDialog::AddMinifigSlot(uint8 pad, uint8 index, uint8 currentIndex, std::optional<uint32> currentId)
|
||||
{
|
||||
auto* panel = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* label = new wxStaticText(this, wxID_ANY, "None");
|
||||
if (currentId)
|
||||
label->SetLabel(nsyshid::g_dimensionstoypad.FindFigure(currentId.value()));
|
||||
|
||||
auto* moveButton = new wxButton(this, wxID_ANY, _("Move Here"));
|
||||
if (index == currentIndex)
|
||||
moveButton->SetLabelText("Pick up and Place");
|
||||
|
||||
moveButton->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
|
||||
m_newPad = pad;
|
||||
m_newIndex = index;
|
||||
this->EndModal(1);
|
||||
});
|
||||
|
||||
panel->Add(label, 1, wxALL, 5);
|
||||
panel->Add(moveButton, 1, wxALL, 5);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
uint8 MoveDimensionFigureDialog::GetNewPad() const
|
||||
{
|
||||
return m_newPad;
|
||||
}
|
||||
|
||||
uint8 MoveDimensionFigureDialog::GetNewIndex() const
|
||||
{
|
||||
return m_newIndex;
|
||||
}
|
||||
|
||||
std::array<std::optional<uint32>, 7> EmulatedUSBDeviceFrame::GetCurrentMinifigs()
|
||||
{
|
||||
return m_dimSlots;
|
||||
}
|
|
@ -17,33 +17,47 @@ class wxStaticBox;
|
|||
class wxString;
|
||||
class wxTextCtrl;
|
||||
|
||||
class EmulatedUSBDeviceFrame : public wxFrame {
|
||||
class EmulatedUSBDeviceFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
EmulatedUSBDeviceFrame(wxWindow* parent);
|
||||
~EmulatedUSBDeviceFrame();
|
||||
std::array<std::optional<uint32>, 7> GetCurrentMinifigs();
|
||||
|
||||
private:
|
||||
wxCheckBox* m_emulatePortal;
|
||||
wxCheckBox* m_emulateBase;
|
||||
wxCheckBox* m_emulateToypad;
|
||||
std::array<wxTextCtrl*, nsyshid::MAX_SKYLANDERS> m_skylanderSlots;
|
||||
std::array<wxTextCtrl*, nsyshid::MAX_FIGURES> m_infinitySlots;
|
||||
std::array<wxTextCtrl*, 7> m_dimensionSlots;
|
||||
std::array<std::optional<std::tuple<uint8, uint16, uint16>>, nsyshid::MAX_SKYLANDERS> m_skySlots;
|
||||
std::array<std::optional<uint32>, 7> m_dimSlots;
|
||||
|
||||
wxPanel* AddSkylanderPage(wxNotebook* notebook);
|
||||
wxPanel* AddInfinityPage(wxNotebook* notebook);
|
||||
wxPanel* AddDimensionsPage(wxNotebook* notebook);
|
||||
wxBoxSizer* AddSkylanderRow(uint8 row_number, wxStaticBox* box);
|
||||
wxBoxSizer* AddInfinityRow(wxString name, uint8 row_number, wxStaticBox* box);
|
||||
wxBoxSizer* AddDimensionPanel(uint8 pad, uint8 index, wxStaticBox* box);
|
||||
void LoadSkylander(uint8 slot);
|
||||
void LoadSkylanderPath(uint8 slot, wxString path);
|
||||
void CreateSkylander(uint8 slot);
|
||||
void ClearSkylander(uint8 slot);
|
||||
void UpdateSkylanderEdits();
|
||||
void LoadFigure(uint8 slot);
|
||||
void LoadFigurePath(uint8 slot, wxString path);
|
||||
void CreateFigure(uint8 slot);
|
||||
void ClearFigure(uint8 slot);
|
||||
void UpdateSkylanderEdits();
|
||||
void LoadMinifig(uint8 pad, uint8 index);
|
||||
void LoadMinifigPath(wxString path_name, uint8 pad, uint8 index);
|
||||
void CreateMinifig(uint8 pad, uint8 index);
|
||||
void ClearMinifig(uint8 pad, uint8 index);
|
||||
void MoveMinifig(uint8 pad, uint8 index);
|
||||
};
|
||||
class CreateSkylanderDialog : public wxDialog {
|
||||
|
||||
class CreateSkylanderDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
explicit CreateSkylanderDialog(wxWindow* parent, uint8 slot);
|
||||
wxString GetFilePath() const;
|
||||
|
@ -52,11 +66,37 @@ class CreateSkylanderDialog : public wxDialog {
|
|||
wxString m_filePath;
|
||||
};
|
||||
|
||||
class CreateInfinityFigureDialog : public wxDialog {
|
||||
class CreateInfinityFigureDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
explicit CreateInfinityFigureDialog(wxWindow* parent, uint8 slot);
|
||||
wxString GetFilePath() const;
|
||||
|
||||
protected:
|
||||
wxString m_filePath;
|
||||
};
|
||||
|
||||
class CreateDimensionFigureDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
explicit CreateDimensionFigureDialog(wxWindow* parent);
|
||||
wxString GetFilePath() const;
|
||||
|
||||
protected:
|
||||
wxString m_filePath;
|
||||
};
|
||||
|
||||
class MoveDimensionFigureDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
explicit MoveDimensionFigureDialog(EmulatedUSBDeviceFrame* parent, uint8 currentIndex);
|
||||
uint8 GetNewPad() const;
|
||||
uint8 GetNewIndex() const;
|
||||
|
||||
protected:
|
||||
uint8 m_newIndex = 0;
|
||||
uint8 m_newPad = 0;
|
||||
|
||||
private:
|
||||
wxBoxSizer* AddMinifigSlot(uint8 pad, uint8 index, uint8 oldIndex, std::optional<uint32> currentId);
|
||||
};
|
|
@ -141,49 +141,66 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook)
|
|||
second_row->SetFlexibleDirection(wxBOTH);
|
||||
second_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
||||
|
||||
sint32 checkboxCount = 0;
|
||||
auto CountRowElement = [&]()
|
||||
{
|
||||
checkboxCount++;
|
||||
if(checkboxCount != 2)
|
||||
return;
|
||||
second_row->AddSpacer(10);
|
||||
checkboxCount = 0;
|
||||
};
|
||||
|
||||
auto InsertEmptyRow = [&]()
|
||||
{
|
||||
while(checkboxCount != 0)
|
||||
CountRowElement();
|
||||
second_row->AddSpacer(10);
|
||||
second_row->AddSpacer(10);
|
||||
second_row->AddSpacer(10);
|
||||
};
|
||||
|
||||
const int topflag = wxALIGN_CENTER_VERTICAL | wxALL;
|
||||
m_save_window_position_size = new wxCheckBox(box, wxID_ANY, _("Remember main window position"));
|
||||
m_save_window_position_size->SetToolTip(_("Restores the last known window position and size when starting Cemu"));
|
||||
second_row->Add(m_save_window_position_size, 0, topflag, 5);
|
||||
second_row->AddSpacer(10);
|
||||
CountRowElement();
|
||||
//second_row->AddSpacer(10);
|
||||
m_save_padwindow_position_size = new wxCheckBox(box, wxID_ANY, _("Remember pad window position"));
|
||||
m_save_padwindow_position_size->SetToolTip(_("Restores the last known pad window position and size when opening it"));
|
||||
second_row->Add(m_save_padwindow_position_size, 0, topflag, 5);
|
||||
CountRowElement();
|
||||
|
||||
const int botflag = wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxBOTTOM;
|
||||
m_discord_presence = new wxCheckBox(box, wxID_ANY, _("Discord Presence"));
|
||||
m_discord_presence->SetToolTip(_("Enables the Discord Rich Presence feature\nYou will also need to enable it in the Discord settings itself!"));
|
||||
second_row->Add(m_discord_presence, 0, botflag, 5);
|
||||
CountRowElement();
|
||||
#ifndef ENABLE_DISCORD_RPC
|
||||
m_discord_presence->Disable();
|
||||
#endif
|
||||
second_row->AddSpacer(10);
|
||||
//second_row->AddSpacer(10);
|
||||
m_fullscreen_menubar = new wxCheckBox(box, wxID_ANY, _("Fullscreen menu bar"));
|
||||
m_fullscreen_menubar->SetToolTip(_("Displays the menu bar when Cemu is running in fullscreen mode and the mouse cursor is moved to the top"));
|
||||
second_row->Add(m_fullscreen_menubar, 0, botflag, 5);
|
||||
CountRowElement();
|
||||
|
||||
m_auto_update = new wxCheckBox(box, wxID_ANY, _("Automatically check for updates"));
|
||||
m_auto_update->SetToolTip(_("Automatically checks for new cemu versions on startup"));
|
||||
second_row->Add(m_auto_update, 0, botflag, 5);
|
||||
#if BOOST_OS_LINUX
|
||||
if (!std::getenv("APPIMAGE")) {
|
||||
m_auto_update->Disable();
|
||||
}
|
||||
#endif
|
||||
second_row->AddSpacer(10);
|
||||
m_save_screenshot = new wxCheckBox(box, wxID_ANY, _("Save screenshot"));
|
||||
m_save_screenshot->SetToolTip(_("Pressing the screenshot key (F12) will save a screenshot directly to the screenshots folder"));
|
||||
second_row->Add(m_save_screenshot, 0, botflag, 5);
|
||||
CountRowElement();
|
||||
|
||||
m_disable_screensaver = new wxCheckBox(box, wxID_ANY, _("Disable screen saver"));
|
||||
m_disable_screensaver->SetToolTip(_("Prevents the system from activating the screen saver or going to sleep while running a game."));
|
||||
second_row->Add(m_disable_screensaver, 0, botflag, 5);
|
||||
CountRowElement();
|
||||
|
||||
// Enable/disable feral interactive gamemode
|
||||
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
|
||||
m_feral_gamemode = new wxCheckBox(box, wxID_ANY, _("Enable Feral GameMode"));
|
||||
m_feral_gamemode->SetToolTip(_("Use FeralInteractive GameMode if installed."));
|
||||
second_row->Add(m_feral_gamemode, 0, botflag, 5);
|
||||
CountRowElement();
|
||||
#endif
|
||||
|
||||
// temporary workaround because feature crashes on macOS
|
||||
|
@ -191,6 +208,22 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook)
|
|||
m_disable_screensaver->Enable(false);
|
||||
#endif
|
||||
|
||||
// InsertEmptyRow();
|
||||
|
||||
m_auto_update = new wxCheckBox(box, wxID_ANY, _("Automatically check for updates"));
|
||||
m_auto_update->SetToolTip(_("Automatically checks for new cemu versions on startup"));
|
||||
second_row->Add(m_auto_update, 0, botflag, 5);
|
||||
CountRowElement();
|
||||
|
||||
m_receive_untested_releases = new wxCheckBox(box, wxID_ANY, _("Receive untested updates"));
|
||||
m_receive_untested_releases->SetToolTip(_("When checking for updates, include brand new and untested releases. These may contain bugs!"));
|
||||
second_row->Add(m_receive_untested_releases, 0, botflag, 5);
|
||||
#if BOOST_OS_LINUX
|
||||
if (!std::getenv("APPIMAGE")) {
|
||||
m_auto_update->Disable();
|
||||
}
|
||||
#endif
|
||||
|
||||
box_sizer->Add(second_row, 0, wxEXPAND, 5);
|
||||
}
|
||||
|
||||
|
@ -899,6 +932,7 @@ void GeneralSettings2::StoreConfig()
|
|||
config.fullscreen_menubar = m_fullscreen_menubar->IsChecked();
|
||||
config.check_update = m_auto_update->IsChecked();
|
||||
config.save_screenshot = m_save_screenshot->IsChecked();
|
||||
config.receive_untested_updates = m_receive_untested_releases->IsChecked();
|
||||
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
|
||||
config.feral_gamemode = m_feral_gamemode->IsChecked();
|
||||
#endif
|
||||
|
@ -1536,6 +1570,7 @@ void GeneralSettings2::ApplyConfig()
|
|||
m_fullscreen_menubar->SetValue(config.fullscreen_menubar);
|
||||
|
||||
m_auto_update->SetValue(config.check_update);
|
||||
m_receive_untested_releases->SetValue(config.receive_untested_updates);
|
||||
m_save_screenshot->SetValue(config.save_screenshot);
|
||||
|
||||
m_disable_screensaver->SetValue(config.disable_screensaver);
|
||||
|
|
|
@ -41,7 +41,7 @@ private:
|
|||
wxCheckBox* m_save_window_position_size;
|
||||
wxCheckBox* m_save_padwindow_position_size;
|
||||
wxCheckBox* m_discord_presence, *m_fullscreen_menubar;
|
||||
wxCheckBox* m_auto_update, *m_save_screenshot;
|
||||
wxCheckBox* m_auto_update, *m_receive_untested_releases, *m_save_screenshot;
|
||||
wxCheckBox* m_disable_screensaver;
|
||||
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
|
||||
wxCheckBox* m_feral_gamemode;
|
||||
|
|
|
@ -458,10 +458,10 @@ void GraphicPacksWindow2::OnTreeSelectionChanged(wxTreeEvent& event)
|
|||
|
||||
m_shown_graphic_pack = gp;
|
||||
|
||||
m_graphic_pack_name->Wrap(m_graphic_pack_name->GetParent()->GetClientSize().GetWidth() - 10);
|
||||
m_graphic_pack_name->Wrap(m_graphic_pack_name->GetParent()->GetClientSize().GetWidth() - 20);
|
||||
m_graphic_pack_name->GetGrandParent()->Layout();
|
||||
|
||||
m_graphic_pack_description->Wrap(m_graphic_pack_description->GetParent()->GetClientSize().GetWidth() - 10);
|
||||
m_graphic_pack_description->Wrap(m_graphic_pack_description->GetParent()->GetClientSize().GetWidth() - 20);
|
||||
m_graphic_pack_description->GetGrandParent()->Layout();
|
||||
|
||||
m_right_panel->FitInside();
|
||||
|
|
|
@ -1392,7 +1392,6 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
|||
const auto outputPath = shortcutDialog.GetPath();
|
||||
|
||||
std::optional<fs::path> icon_path = std::nullopt;
|
||||
[&]()
|
||||
{
|
||||
int iconIdx;
|
||||
int smallIconIdx;
|
||||
|
@ -1402,15 +1401,13 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
|||
return;
|
||||
}
|
||||
const auto icon = m_image_list->GetIcon(iconIdx);
|
||||
PWSTR localAppData;
|
||||
const auto hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData);
|
||||
wxBitmap bitmap{};
|
||||
auto folder = fs::path(localAppData) / "Cemu" / "icons";
|
||||
if (!SUCCEEDED(hres) || (!fs::exists(folder) && !fs::create_directories(folder)))
|
||||
const auto folder = ActiveSettings::GetUserDataPath("icons");
|
||||
if (!fs::exists(folder) && !fs::create_directories(folder))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to create icon directory");
|
||||
return;
|
||||
}
|
||||
wxBitmap bitmap{};
|
||||
if (!bitmap.CopyFromIcon(icon))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to copy icon");
|
||||
|
@ -1426,7 +1423,7 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
|||
icon_path = std::nullopt;
|
||||
cemuLog_log(LogType::Force, "Icon failed to save");
|
||||
}
|
||||
}();
|
||||
}
|
||||
|
||||
IShellLinkW* shellLink;
|
||||
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<LPVOID*>(&shellLink));
|
||||
|
|
|
@ -8,7 +8,7 @@ template <>
|
|||
struct fmt::formatter<wxString> : formatter<string_view>
|
||||
{
|
||||
template <typename FormatContext>
|
||||
auto format(const wxString& str, FormatContext& ctx)
|
||||
auto format(const wxString& str, FormatContext& ctx) const
|
||||
{
|
||||
return formatter<string_view>::format(str.c_str().AsChar(), ctx);
|
||||
}
|
||||
|
|
|
@ -114,6 +114,11 @@ InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position,
|
|||
this->Bind(wxControllersRefreshed, &InputAPIAddWindow::on_controllers_refreshed, this);
|
||||
}
|
||||
|
||||
InputAPIAddWindow::~InputAPIAddWindow()
|
||||
{
|
||||
discard_thread_result();
|
||||
}
|
||||
|
||||
void InputAPIAddWindow::on_add_button(wxCommandEvent& event)
|
||||
{
|
||||
const auto selection = m_input_api->GetSelection();
|
||||
|
@ -159,6 +164,8 @@ std::unique_ptr<ControllerProviderSettings> InputAPIAddWindow::get_settings() co
|
|||
|
||||
void InputAPIAddWindow::on_api_selected(wxCommandEvent& event)
|
||||
{
|
||||
discard_thread_result();
|
||||
|
||||
if (m_input_api->GetSelection() == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
@ -239,19 +246,25 @@ void InputAPIAddWindow::on_controller_dropdown(wxCommandEvent& event)
|
|||
m_controller_list->Append(_("Searching for controllers..."), (wxClientData*)nullptr);
|
||||
m_controller_list->SetSelection(wxNOT_FOUND);
|
||||
|
||||
std::thread([this, provider, selected_uuid]()
|
||||
m_search_thread_data = std::make_unique<AsyncThreadData>();
|
||||
std::thread([this, provider, selected_uuid](std::shared_ptr<AsyncThreadData> data)
|
||||
{
|
||||
auto available_controllers = provider->get_controllers();
|
||||
|
||||
wxCommandEvent event(wxControllersRefreshed);
|
||||
event.SetEventObject(m_controller_list);
|
||||
event.SetClientObject(new wxCustomData(std::move(available_controllers)));
|
||||
event.SetInt(provider->api());
|
||||
event.SetString(selected_uuid);
|
||||
wxPostEvent(this, event);
|
||||
|
||||
m_search_running = false;
|
||||
}).detach();
|
||||
{
|
||||
std::lock_guard lock{data->mutex};
|
||||
if(!data->discardResult)
|
||||
{
|
||||
wxCommandEvent event(wxControllersRefreshed);
|
||||
event.SetEventObject(m_controller_list);
|
||||
event.SetClientObject(new wxCustomData(std::move(available_controllers)));
|
||||
event.SetInt(provider->api());
|
||||
event.SetString(selected_uuid);
|
||||
wxPostEvent(this, event);
|
||||
m_search_running = false;
|
||||
}
|
||||
}
|
||||
}, m_search_thread_data).detach();
|
||||
}
|
||||
|
||||
void InputAPIAddWindow::on_controller_selected(wxCommandEvent& event)
|
||||
|
@ -301,3 +314,13 @@ void InputAPIAddWindow::on_controllers_refreshed(wxCommandEvent& event)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputAPIAddWindow::discard_thread_result()
|
||||
{
|
||||
m_search_running = false;
|
||||
if(m_search_thread_data)
|
||||
{
|
||||
std::lock_guard lock{m_search_thread_data->mutex};
|
||||
m_search_thread_data->discardResult = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class InputAPIAddWindow : public wxDialog
|
|||
{
|
||||
public:
|
||||
InputAPIAddWindow(wxWindow* parent, const wxPoint& position, const std::vector<ControllerPtr>& controllers);
|
||||
~InputAPIAddWindow();
|
||||
|
||||
bool is_valid() const { return m_type.has_value() && m_controller != nullptr; }
|
||||
InputAPI::Type get_type() const { return m_type.value(); }
|
||||
|
@ -38,6 +39,8 @@ private:
|
|||
void on_controller_selected(wxCommandEvent& event);
|
||||
void on_controllers_refreshed(wxCommandEvent& event);
|
||||
|
||||
void discard_thread_result();
|
||||
|
||||
wxChoice* m_input_api;
|
||||
wxComboBox* m_controller_list;
|
||||
wxButton* m_ok_button;
|
||||
|
@ -50,4 +53,10 @@ private:
|
|||
|
||||
std::vector<ControllerPtr> m_controllers;
|
||||
std::atomic_bool m_search_running = false;
|
||||
struct AsyncThreadData
|
||||
{
|
||||
std::atomic_bool discardResult = false;
|
||||
std::mutex mutex;
|
||||
};
|
||||
std::shared_ptr<AsyncThreadData> m_search_thread_data;
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <wx/statline.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/slider.h>
|
||||
#include <wx/checkbox.h>
|
||||
|
||||
|
||||
#include "gui/helpers/wxControlObject.h"
|
||||
|
@ -131,11 +132,23 @@ VPADInputPanel::VPADInputPanel(wxWindow* parent)
|
|||
}
|
||||
|
||||
// Blow Mic
|
||||
row = 9;
|
||||
row = 8;
|
||||
add_button_row(main_sizer, row, column, VPADController::kButtonId_Mic, _("blow mic"));
|
||||
row++;
|
||||
|
||||
add_button_row(main_sizer, row, column, VPADController::kButtonId_Screen, _("show screen"));
|
||||
row++;
|
||||
|
||||
auto toggleScreenText = new wxStaticText(this, wxID_ANY, _("toggle screen"));
|
||||
main_sizer->Add(toggleScreenText,
|
||||
wxGBPosition(row, column),
|
||||
wxDefaultSpan,
|
||||
wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
m_togglePadViewCheckBox = new wxCheckBox(this, wxID_ANY, {}, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
wxString toggleScreenTT = _("Makes the \"show screen\" button toggle between the TV and gamepad screens");
|
||||
m_togglePadViewCheckBox->SetToolTip(toggleScreenTT);
|
||||
toggleScreenText->SetToolTip(toggleScreenTT);
|
||||
main_sizer->Add(m_togglePadViewCheckBox, wxGBPosition(row,column+1), wxDefaultSpan, wxALL | wxEXPAND, 5);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -168,6 +181,8 @@ void VPADInputPanel::on_timer(const EmulatedControllerPtr& emulated_controller,
|
|||
{
|
||||
InputPanel::on_timer(emulated_controller, controller_base);
|
||||
|
||||
static_cast<VPADController*>(emulated_controller.get())->set_screen_toggle(m_togglePadViewCheckBox->GetValue());
|
||||
|
||||
if(emulated_controller)
|
||||
{
|
||||
const auto axis = emulated_controller->get_axis();
|
||||
|
@ -182,3 +197,10 @@ void VPADInputPanel::OnVolumeChange(wxCommandEvent& event)
|
|||
{
|
||||
|
||||
}
|
||||
void VPADInputPanel::load_controller(const EmulatedControllerPtr& controller)
|
||||
{
|
||||
InputPanel::load_controller(controller);
|
||||
|
||||
const bool isToggle = static_cast<VPADController*>(controller.get())->is_screen_active_toggle();
|
||||
m_togglePadViewCheckBox->SetValue(isToggle);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "gui/input/panels/InputPanel.h"
|
||||
|
||||
class wxInputDraw;
|
||||
class wxCheckBox;
|
||||
|
||||
class VPADInputPanel : public InputPanel
|
||||
{
|
||||
|
@ -11,11 +12,13 @@ public:
|
|||
VPADInputPanel(wxWindow* parent);
|
||||
|
||||
void on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) override;
|
||||
virtual void load_controller(const EmulatedControllerPtr& controller) override;
|
||||
|
||||
private:
|
||||
void OnVolumeChange(wxCommandEvent& event);
|
||||
|
||||
wxInputDraw* m_left_draw, * m_right_draw;
|
||||
wxCheckBox* m_togglePadViewCheckBox;
|
||||
|
||||
void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id);
|
||||
void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id, const wxString &label);
|
||||
|
|
|
@ -127,7 +127,7 @@ using EmulatedControllerPtr = std::shared_ptr<EmulatedController>;
|
|||
template <>
|
||||
struct fmt::formatter<EmulatedController::Type> : formatter<string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(EmulatedController::Type v, FormatContext& ctx) {
|
||||
auto format(EmulatedController::Type v, FormatContext& ctx) const {
|
||||
switch (v)
|
||||
{
|
||||
case EmulatedController::Type::VPAD: return formatter<string_view>::format("Wii U Gamepad", ctx);
|
||||
|
|
|
@ -686,3 +686,14 @@ bool VPADController::set_default_mapping(const std::shared_ptr<ControllerBase>&
|
|||
|
||||
return mapping_updated;
|
||||
}
|
||||
|
||||
void VPADController::load(const pugi::xml_node& node)
|
||||
{
|
||||
if (const auto value = node.child("toggle_display"))
|
||||
m_screen_active_toggle = ConvertString<bool>(value.child_value());
|
||||
}
|
||||
|
||||
void VPADController::save(pugi::xml_node& node)
|
||||
{
|
||||
node.append_child("toggle_display").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (int)m_screen_active_toggle).c_str());
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ public:
|
|||
|
||||
bool is_mic_active() { return m_mic_active; }
|
||||
bool is_screen_active() { return m_screen_active; }
|
||||
bool is_screen_active_toggle() { return m_screen_active_toggle; }
|
||||
void set_screen_toggle(bool toggle) {m_screen_active_toggle = toggle;}
|
||||
|
||||
static std::string_view get_button_name(ButtonId id);
|
||||
|
||||
|
@ -86,9 +88,13 @@ public:
|
|||
|
||||
bool set_default_mapping(const std::shared_ptr<ControllerBase>& controller) override;
|
||||
|
||||
void load(const pugi::xml_node& node) override;
|
||||
void save(pugi::xml_node& node) override;
|
||||
|
||||
private:
|
||||
bool m_mic_active = false;
|
||||
bool m_screen_active = false;
|
||||
bool m_screen_active_toggle = false;
|
||||
uint32be m_last_holdvalue = 0;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point m_last_hold_change{}, m_last_pulse{};
|
||||
|
|
|
@ -73,8 +73,8 @@ END
|
|||
#define str(s) #s
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, 0
|
||||
PRODUCTVERSION EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, 0
|
||||
FILEVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0
|
||||
PRODUCTVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -94,7 +94,7 @@ BEGIN
|
|||
VALUE "LegalCopyright", "Team Cemu"
|
||||
VALUE "OriginalFilename", "Cemu.exe"
|
||||
VALUE "ProductName", "Cemu"
|
||||
VALUE "ProductVersion", xstr(EMULATOR_VERSION_LEAD) "." xstr(EMULATOR_VERSION_MAJOR) "." xstr(EMULATOR_VERSION_MINOR) EMULATOR_VERSION_SUFFIX "\0"
|
||||
VALUE "ProductVersion", xstr(EMULATOR_VERSION_MAJOR) "." xstr(EMULATOR_VERSION_MINOR) "." xstr(EMULATOR_VERSION_PATCH) EMULATOR_VERSION_SUFFIX "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
@ -50,7 +50,6 @@ add_library(CemuUtil
|
|||
MemMapper/MemMapper.h
|
||||
SystemInfo/SystemInfo.cpp
|
||||
SystemInfo/SystemInfo.h
|
||||
ThreadPool/ThreadPool.cpp
|
||||
ThreadPool/ThreadPool.h
|
||||
tinyxml2/tinyxml2.cpp
|
||||
tinyxml2/tinyxml2.h
|
||||
|
|
|
@ -194,7 +194,7 @@ namespace robin_hood {
|
|||
|
||||
// workaround missing "is_trivially_copyable" in g++ < 5.0
|
||||
// See https://stackoverflow.com/a/31798726/48181
|
||||
#if defined(__GNUC__) && __GNUC__ < 5
|
||||
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
|
||||
# define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
|
||||
#else
|
||||
# define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
|
||||
|
|
|
@ -1,29 +1,6 @@
|
|||
#include "crc32.h"
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#ifdef __MINGW32__
|
||||
#define PREFETCH(location) __builtin_prefetch(location)
|
||||
#else
|
||||
#define PREFETCH(location) _mm_prefetch(location, _MM_HINT_T0)
|
||||
#endif
|
||||
#else
|
||||
// defines __BYTE_ORDER as __LITTLE_ENDIAN or __BIG_ENDIAN
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PREFETCH(location) __builtin_prefetch(location)
|
||||
#else
|
||||
// no prefetching
|
||||
#define PREFETCH(location) ;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
unsigned int Crc32Lookup[8][256] =
|
||||
constexpr uint32 Crc32Lookup[8][256] =
|
||||
{
|
||||
{
|
||||
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,
|
||||
|
@ -301,20 +278,7 @@ unsigned int Crc32Lookup[8][256] =
|
|||
}
|
||||
};
|
||||
|
||||
/// swap endianess
|
||||
static inline uint32_t swap(uint32_t x)
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap32(x);
|
||||
#else
|
||||
return (x >> 24) |
|
||||
((x >> 8) & 0x0000FF00) |
|
||||
((x << 8) & 0x00FF0000) |
|
||||
(x << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data, int length)
|
||||
uint32 crc32_calc_slice_by_8(uint32 previousCrc32, const void* data, size_t length)
|
||||
{
|
||||
uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF
|
||||
const uint32_t* current = (const uint32_t*)data;
|
||||
|
@ -323,7 +287,7 @@ unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data,
|
|||
while (length >= 8)
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::big){
|
||||
uint32_t one = *current++ ^ swap(crc);
|
||||
uint32_t one = *current++ ^ _swapEndianU32(crc);
|
||||
uint32_t two = *current++;
|
||||
crc = Crc32Lookup[0][two & 0xFF] ^
|
||||
Crc32Lookup[1][(two >> 8) & 0xFF] ^
|
||||
|
@ -348,13 +312,14 @@ unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data,
|
|||
Crc32Lookup[7][one & 0xFF];
|
||||
}
|
||||
else {
|
||||
cemu_assert(false);
|
||||
static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little,
|
||||
"Platform byte-order is unsupported");
|
||||
}
|
||||
|
||||
length -= 8;
|
||||
}
|
||||
|
||||
const uint8_t* currentChar = (const uint8_t*)current;
|
||||
const uint8* currentChar = (const uint8*)current;
|
||||
// remaining 1 to 7 bytes (standard algorithm)
|
||||
while (length-- != 0)
|
||||
crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++];
|
||||
|
@ -362,20 +327,20 @@ unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data,
|
|||
return ~crc; // same as crc ^ 0xFFFFFFFF
|
||||
}
|
||||
|
||||
unsigned int crc32_calc(unsigned int c, const void* data, int length)
|
||||
uint32 crc32_calc(uint32 c, const void* data, size_t length)
|
||||
{
|
||||
if (length >= 16)
|
||||
{
|
||||
return crc32_calc_slice_by_8(c, data, length);
|
||||
}
|
||||
unsigned char* p = (unsigned char*)data;
|
||||
const uint8* p = (const uint8*)data;
|
||||
if (length == 0)
|
||||
return c;
|
||||
c ^= 0xFFFFFFFF;
|
||||
while (length)
|
||||
{
|
||||
unsigned char temp = *p;
|
||||
temp ^= (unsigned char)c;
|
||||
uint8 temp = *p;
|
||||
temp ^= (uint8)c;
|
||||
c = (c >> 8) ^ Crc32Lookup[0][temp];
|
||||
// next
|
||||
length--;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
unsigned int crc32_calc(unsigned int c, const void* data, int length);
|
||||
uint32 crc32_calc(uint32 c, const void* data, size_t length);
|
||||
|
||||
inline unsigned int crc32_calc(const void* data, int length)
|
||||
inline uint32 crc32_calc(const void* data, size_t length)
|
||||
{
|
||||
return crc32_calc(0, data, length);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue