Compare commits

..

No commits in common. "main" and "v2.0-52" have entirely different histories.

543 changed files with 29599 additions and 48124 deletions

View file

@ -15,7 +15,6 @@ BinPackArguments: true
BinPackParameters: true BinPackParameters: true
BraceWrapping: BraceWrapping:
AfterCaseLabel: true AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always AfterControlStatement: Always
AfterEnum: true AfterEnum: true
AfterExternBlock: true AfterExternBlock: true

View file

@ -0,0 +1,34 @@
---
name: Bug Report / Feature Request
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Cemu or you are requesting a feature you believe would make Cemu better.
title: ''
labels: ''
assignees: ''
---
<!---
Please keep in mind Cemu is EXPERIMENTAL SOFTWARE.
Please read the FAQ:
https://cemu.info/faq.html
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
https://discord.com/invite/5psYsup
If the FAQ does not answer your question, please go to:
https://discord.com/invite/5psYsup
When submitting an issue, please check the following:
- You have read the above.
- You have provided the version (commit hash) of Cemu you are using.
- You have provided sufficient detail for the issue to be reproduced.
- You have provided system specs (if relevant).
- Please also provide:
- For any issues, a log file
- For crashes, a backtrace.
- For graphical issues, comparison screenshots with real hardware.
- For emulation inaccuracies, a test-case (if able).
-->

View file

@ -2,4 +2,4 @@ blank_issues_enabled: false
contact_links: contact_links:
- name: Cemu Discord - name: Cemu Discord
url: https://discord.com/invite/5psYsup url: https://discord.com/invite/5psYsup
about: If you need technical support with Cemu or have other questions the best place to ask is on the official Cemu Discord linked here about: If you are experiencing an issue with Cemu, and you need tech support, or if you have a general question, try asking in the official Cemu Discord linked here. Piracy is not allowed.

View file

@ -1,69 +0,0 @@
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Bug Report
description: Report an issue with Cemu emulator
title: "Enter a title for the bug report here"
labels: bug
body:
- type: markdown
id: md_readme
attributes:
value: |
## Important: Read First
If you discovered a bug you can report it here. Please make sure of the following first:
- That you are using the latest version of Cemu
- Only report something if you are sure it's a bug and not any technical issue on your end. For troubleshooting help see the [links page](https://github.com/cemu-project/Cemu#links)
- Problems specific to a single game should be reported on the [compatibility wiki](https://wiki.cemu.info/wiki/Main_Page) instead
- Verify that your problem isn't already mentioned on the [issue tracker](https://github.com/cemu-project/Cemu/issues)
Additionally, be aware that graphic packs can also causes issues. There is a separate issue tracker for graphic pack bugs over at the [graphic pack repository](https://github.com/cemu-project/cemu_graphic_packs)
- type: textarea
id: current_behavior
attributes:
label: Current Behavior
description: "What the bug is, in a brief description"
validations:
required: true
- type: textarea
id: expected_behavior
attributes:
label: Expected Behavior
description: "What did you expect to happen?"
validations:
required: true
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to Reproduce
description: "How to reproduce the issue"
validations:
required: true
- type: textarea
id: sys_info
attributes:
label: System Info (Optional)
description: "Your PC specifications. Usually only the operating system and graphics card is important. But feel free to add more info."
placeholder: |
Info
OS: Windows 10
GPU: NVIDIA GeForce RTX 4090
value: |
OS:
GPU:
- type: textarea
id: emulation_settings
attributes:
label: Emulation Settings (Optional)
description: |
Any non-default settings. You can leave this empty if you didn't change anything other than input settings.
validations:
required: false
- type: textarea
id: logs_files
attributes:
label: "Logs (Optional)"
description: |
"Attach `log.txt` from your Cemu folder (*File > Open Cemu folder*)".
validations:
required: false

View file

@ -1,28 +0,0 @@
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Feature suggestion
description: Suggest a new feature
title: "Enter a title for the suggestion here"
labels: feature request
body:
- type: markdown
id: md_readme
attributes:
value: |
## Important: Read First
While we appreciate suggestions, it is important to note that we are a very small team and there are already many more ideas than we could ever implement in the near future. Therefore, please only suggest something if you believe it is a great addition and the idea is reasonably unique.
*Avoid* to create suggestions for:
- Overly obvious features ("Game xyz does not work and should be fixed", "Wiimote support should be improved", "You should add an Android port", "Copy feature xyz from another emulator", "A button to pause/stop emulation")
- Niche features which are only interesting to a tiny percentage of users
- Large scale features ("Add a Metal backend for MacOS", "Add ARM support", "Add savestates")
Note that this doesn't mean we aren't interested in these ideas, but rather we likely have them planned anyway and it's mostly up to finding the time to implement them.
If you believe your idea is worthwhile even if it doesn't meet all the criteria above, you can still try suggesting it but we might close it.
- type: textarea
id: idea_suggestion
attributes:
label: Your suggestion
description: "Describe what your suggestion is in as much detail as possible"
validations:
required: true

9
.github/getversion.cpp vendored Normal file
View file

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

View file

@ -3,10 +3,10 @@ name: Build Cemu
on: on:
workflow_call: workflow_call:
inputs: inputs:
next_version_major: deploymode:
required: false required: false
type: string type: string
next_version_minor: experimentalversion:
required: false required: false
type: string type: string
@ -16,35 +16,44 @@ env:
jobs: jobs:
build-ubuntu: build-ubuntu:
runs-on: ubuntu-22.04 runs-on: ubuntu-20.04
steps: steps:
- name: "Checkout repo" - name: "Checkout repo"
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
submodules: "recursive" submodules: "recursive"
fetch-depth: 0 fetch-depth: 0
- name: Setup release mode parameters - name: "Fetch full history for vcpkg submodule"
run: |
cd dependencies/vcpkg
git fetch --unshallow
git pull --all
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
run: | run: |
echo "BUILD_MODE=release" >> $GITHUB_ENV echo "BUILD_MODE=release" >> $GITHUB_ENV
echo "BUILD_FLAGS=" >> $GITHUB_ENV echo "BUILD_FLAGS=" >> $GITHUB_ENV
echo "Build mode is release" echo "Build mode is release"
- name: Setup build flags for version - name: Setup debug mode parameters (for continous build)
if: ${{ inputs.next_version_major != '' }} if: ${{ inputs.deploymode != 'release' }}
run: | run: |
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}" echo "BUILD_MODE=debug" >> $GITHUB_ENV
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $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" - name: "Install system dependencies"
run: | run: |
sudo apt update -qq sudo apt update -qq
sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build libbluetooth-dev sudo apt install -y clang-12 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build
- name: "Setup cmake"
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.29.0'
- name: "Bootstrap vcpkg" - name: "Bootstrap vcpkg"
run: | run: |
@ -66,29 +75,31 @@ jobs:
- name: "cmake" - name: "cmake"
run: | run: |
cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DPORTABLE=OFF -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja
- name: "Build Cemu" - name: "Build Cemu"
run: | run: |
cmake --build build cmake --build build
- name: Prepare artifact - name: Prepare artifact
if: ${{ inputs.deploymode == 'release' }}
run: mv bin/Cemu_release bin/Cemu run: mv bin/Cemu_release bin/Cemu
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: ${{ inputs.deploymode == 'release' }}
with: with:
name: cemu-bin-linux-x64 name: cemu-bin-linux-x64
path: ./bin/Cemu path: ./bin/Cemu
build-appimage: build-appimage:
runs-on: ubuntu-22.04 runs-on: ubuntu-20.04
needs: build-ubuntu needs: build-ubuntu
steps: steps:
- name: Checkout Upstream Repo - name: Checkout Upstream Repo
uses: actions/checkout@v4 uses: actions/checkout@v3
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
name: cemu-bin-linux-x64 name: cemu-bin-linux-x64
path: bin path: bin
@ -96,7 +107,7 @@ jobs:
- name: "Install system dependencies" - name: "Install system dependencies"
run: | run: |
sudo apt update -qq sudo apt update -qq
sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build appstream libbluetooth-dev sudo apt install -y clang-12 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build appstream
- name: "Build AppImage" - name: "Build AppImage"
run: | run: |
@ -105,7 +116,7 @@ jobs:
dist/linux/appimage.sh dist/linux/appimage.sh
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: cemu-appimage-x64 name: cemu-appimage-x64
path: artifacts path: artifacts
@ -114,26 +125,34 @@ jobs:
runs-on: windows-2022 runs-on: windows-2022
steps: steps:
- name: "Checkout repo" - name: "Checkout repo"
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
submodules: "recursive" submodules: "recursive"
- name: Setup release mode parameters - name: "Fetch full history for vcpkg submodule"
run: |
cd dependencies/vcpkg
git fetch --unshallow
git pull --all
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
run: | run: |
echo "BUILD_MODE=release" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append 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_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "Build mode is release" echo "Build mode is release"
- name: Setup build flags for version - name: Setup debug mode parameters (for continous build)
if: ${{ inputs.next_version_major != '' }} if: ${{ inputs.deploymode != 'release' }}
run: | run: |
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}" echo "BUILD_MODE=debug" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
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 echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "Build mode is debug"
- name: "Setup cmake" - name: Setup version for experimental
uses: jwlawson/actions-setup-cmake@v2 if: ${{ inputs.experimentalversion != '' }}
with: run: |
cmake-version: '3.29.0' 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
- name: "Bootstrap vcpkg" - name: "Bootstrap vcpkg"
run: | run: |
@ -164,56 +183,56 @@ jobs:
- name: "Build Cemu" - name: "Build Cemu"
run: | run: |
cd build cd build
cmake --build . --config ${{ env.BUILD_MODE }} cmake --build . --config ${{ env.BUILD_MODE }} -j 2
- name: Prepare artifact - name: Prepare artifact
if: ${{ inputs.deploymode == 'release' }}
run: Rename-Item bin/Cemu_release.exe Cemu.exe run: Rename-Item bin/Cemu_release.exe Cemu.exe
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: ${{ inputs.deploymode == 'release' }}
with: with:
name: cemu-bin-windows-x64 name: cemu-bin-windows-x64
path: ./bin/Cemu.exe path: ./bin/Cemu.exe
build-macos: build-macos:
runs-on: macos-14 runs-on: macos-12
strategy:
matrix:
arch: [x86_64, arm64]
steps: steps:
- name: "Checkout repo" - name: "Checkout repo"
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
submodules: "recursive" submodules: "recursive"
- name: Setup release mode parameters - name: "Fetch full history for vcpkg submodule"
run: |
cd dependencies/vcpkg
git fetch --unshallow
git pull --all
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
run: | run: |
echo "BUILD_MODE=release" >> $GITHUB_ENV echo "BUILD_MODE=release" >> $GITHUB_ENV
echo "BUILD_FLAGS=" >> $GITHUB_ENV echo "BUILD_FLAGS=" >> $GITHUB_ENV
echo "Build mode is release" echo "Build mode is release"
- name: Setup debug mode parameters (for continous build)
- name: Setup build flags for version if: ${{ inputs.deploymode != 'release' }}
if: ${{ inputs.next_version_major != '' }}
run: | run: |
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}" echo "BUILD_MODE=debug" >> $GITHUB_ENV
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $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" - name: "Install system dependencies"
run: | run: |
brew update brew update
brew install ninja nasm automake libtool brew install llvm@15 ninja nasm molten-vk automake libtool
- name: "Install molten-vk"
run: |
curl -L -O https://github.com/KhronosGroup/MoltenVK/releases/download/v1.3.0/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" - name: "Bootstrap vcpkg"
run: | run: |
@ -239,8 +258,10 @@ jobs:
cd build cd build
cmake .. ${{ env.BUILD_FLAGS }} \ cmake .. ${{ env.BUILD_FLAGS }} \
-DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \ -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ -DPORTABLE=OFF \
-DMACOS_BUNDLE=ON \ -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 -G Ninja
- name: "Build Cemu" - name: "Build Cemu"
@ -248,19 +269,21 @@ jobs:
cmake --build build cmake --build build
- name: Prepare artifact - name: Prepare artifact
if: ${{ inputs.deploymode == 'release' }}
run: | run: |
mkdir bin/Cemu_app mkdir bin/Cemu_app
mv bin/Cemu_release.app bin/Cemu_app/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 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 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} chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
ln -s /Applications bin/Cemu_app/Applications ln -s /Applications bin/Cemu_app/Applications
hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app" 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 hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu.dmg
rm bin/tmp.dmg rm bin/tmp.dmg
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: ${{ inputs.deploymode == 'release' }}
with: with:
name: cemu-bin-macos-${{ matrix.arch }} name: cemu-bin-macos-x64
path: ./bin/Cemu.dmg path: ./bin/Cemu.dmg

View file

@ -16,3 +16,6 @@ on:
jobs: jobs:
build: build:
uses: ./.github/workflows/build.yml uses: ./.github/workflows/build.yml
with:
deploymode: release
experimentalversion: 999999

View file

@ -0,0 +1,86 @@
name: Deploy experimental release
on:
workflow_dispatch:
jobs:
call-release-build:
uses: ./.github/workflows/build.yml
with:
deploymode: release
experimentalversion: ${{ github.run_number }}
deploy:
name: Deploy experimental release
runs-on: ubuntu-20.04
needs: call-release-build
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: cemu-bin-linux-x64
path: cemu-bin-linux-x64
- uses: actions/download-artifact@v3
with:
name: cemu-appimage-x64
path: cemu-appimage-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-windows-x64
path: cemu-bin-windows-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-macos-x64
path: cemu-bin-macos-x64
- name: Initialize
run: |
mkdir upload
sudo apt install zip
- name: Get version
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
- name: Create release from windows-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create appimage
run: |
VERSION=${{ env.CEMU_VERSION }}
echo "Cemu Version is $VERSION"
ls cemu-appimage-x64
mv cemu-appimage-x64/Cemu-*-x86_64.AppImage upload/Cemu-$VERSION-x86_64.AppImage
- name: Create release from linux-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-20.04-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create release from macos-bin
run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg
- name: Create release
run: |
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

View file

@ -1,151 +0,0 @@
name: Deploy 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:
next_version_major: ${{ needs.calculate-version.outputs.next_version_major }}
next_version_minor: ${{ needs.calculate-version.outputs.next_version_minor }}
deploy:
name: Deploy release
runs-on: ubuntu-22.04
needs: [call-release-build, calculate-version]
steps:
- 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
path: cemu-bin-linux-x64
- uses: actions/download-artifact@v4
with:
name: cemu-appimage-x64
path: cemu-appimage-x64
- uses: actions/download-artifact@v4
with:
name: cemu-bin-windows-x64
path: cemu-bin-windows-x64
- uses: actions/download-artifact@v4
with:
name: cemu-bin-macos-x64
path: cemu-bin-macos-x64
- name: Initialize
run: |
mkdir upload
sudo apt install zip
- name: Set version dependent vars
run: |
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: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create appimage
run: |
VERSION=${{ env.CEMU_VERSION }}
echo "Cemu Version is $VERSION"
ls cemu-appimage-x64
mv cemu-appimage-x64/Cemu-*-x86_64.AppImage upload/Cemu-$VERSION-x86_64.AppImage
- name: Create release from linux-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-22.04-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create release from macos-bin
run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg
- name: Create release
run: |
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 }}"
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

View file

@ -0,0 +1,85 @@
name: Create new release
on:
workflow_dispatch:
inputs:
PlaceholderInput:
description: PlaceholderInput
required: false
jobs:
call-release-build:
uses: ./.github/workflows/build.yml
with:
deploymode: release
deploy:
name: Deploy release
runs-on: ubuntu-20.04
needs: call-release-build
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: cemu-bin-linux-x64
path: cemu-bin-linux-x64
- uses: actions/download-artifact@v3
with:
name: cemu-appimage-x64
path: cemu-appimage-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-windows-x64
path: cemu-bin-windows-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-macos-x64
path: cemu-bin-macos-x64
- name: Initialize
run: |
mkdir upload
sudo apt update -qq
sudo apt install -y zip
- name: Get Cemu release version
run: |
gcc -o getversion .github/getversion.cpp
echo "Cemu CI version: $(./getversion)"
echo "CEMU_FOLDER_NAME=Cemu_$(./getversion)" >> $GITHUB_ENV
echo "CEMU_VERSION=$(./getversion)" >> $GITHUB_ENV
- name: Create release from windows-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create appimage
run: |
VERSION=${{ env.CEMU_VERSION }}
echo "Cemu Version is $VERSION"
ls cemu-appimage-x64
mv cemu-appimage-x64/Cemu-*-x86_64.AppImage upload/Cemu-$VERSION-x86_64.AppImage
- name: Create release from ubuntu-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-20.04-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create release from macos-bin
run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg
- name: Create release
run: |
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
ghr_v0.15.0_linux_amd64/ghr -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }}" -b "Changelog:" v${{ env.CEMU_VERSION }} ./upload

View file

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

View file

@ -35,7 +35,7 @@ jobs:
-o cemu.pot -o cemu.pot
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: POT file name: POT file
path: ./cemu.pot path: ./cemu.pot

4
.gitignore vendored
View file

@ -17,7 +17,7 @@
.idea/ .idea/
build/ build/
cmake-build-*/ cmake-build-*-*/
out/ out/
.cache/ .cache/
bin/Cemu_* bin/Cemu_*
@ -39,8 +39,6 @@ bin/sdcard/*
bin/screenshots/* bin/screenshots/*
bin/dump/* bin/dump/*
bin/cafeLibs/* bin/cafeLibs/*
bin/portable/*
bin/keys.txt
!bin/shaderCache/info.txt !bin/shaderCache/info.txt
bin/shaderCache/* bin/shaderCache/*

7
.gitmodules vendored
View file

@ -9,15 +9,10 @@
[submodule "dependencies/vcpkg"] [submodule "dependencies/vcpkg"]
path = dependencies/vcpkg path = dependencies/vcpkg
url = https://github.com/microsoft/vcpkg url = https://github.com/microsoft/vcpkg
shallow = false shallow = true
[submodule "dependencies/Vulkan-Headers"] [submodule "dependencies/Vulkan-Headers"]
path = dependencies/Vulkan-Headers path = dependencies/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers url = https://github.com/KhronosGroup/Vulkan-Headers
shallow = true
[submodule "dependencies/imgui"] [submodule "dependencies/imgui"]
path = dependencies/imgui path = dependencies/imgui
url = https://github.com/ocornut/imgui url = https://github.com/ocornut/imgui
shallow = true
[submodule "dependencies/xbyak_aarch64"]
path = dependencies/xbyak_aarch64
url = https://github.com/fujitsu/xbyak_aarch64

232
BUILD.md
View file

@ -1,26 +1,4 @@
# Build Instructions # Build instructions
## Table of Contents
- [Windows](#windows)
- [Linux](#linux)
- [Dependencies](#dependencies)
- [For Arch and derivatives:](#for-arch-and-derivatives)
- [For Debian, Ubuntu and derivatives](#for-debian-ubuntu-and-derivatives)
- [For Fedora and derivatives:](#for-fedora-and-derivatives)
- [Build Cemu](#build-cemu)
- [CMake and Clang](#cmake-and-clang)
- [GCC](#gcc)
- [Debug Build](#debug-build)
- [Troubleshooting Steps](#troubleshooting-steps)
- [Compiling Errors](#compiling-errors)
- [Building Errors](#building-errors)
- [macOS](#macos)
- [Installing brew](#installing-brew)
- [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 ## Windows
@ -41,144 +19,79 @@ Any other IDE should also work as long as it has CMake and MSVC support. CLion a
## Linux ## Linux
To compile Cemu, a recent enough compiler and STL with C++20 support is required! Clang-15 or higher is what we recommend. To compile Cemu, a recent enough compiler and STL with C++20 support is required! clang-12 or higher is what we recommend.
### Dependencies ### Installing dependencies
#### For Ubuntu and derivatives:
`sudo apt install -y cmake curl freeglut3-dev git libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build`
*Additionally, for Ubuntu 22.04 only:*
- `sudo apt install -y clang-12`
- At step 3 while building, use
`cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja`
#### For Arch and derivatives: #### For Arch and derivatives:
`sudo pacman -S --needed base-devel bluez-libs clang cmake freeglut git glm gtk3 libgcrypt libpulse libsecret linux-headers llvm nasm ninja systemd unzip zip` `sudo pacman -S --needed base-devel clang cmake freeglut git glm gtk3 libgcrypt libpulse libsecret linux-headers llvm nasm ninja systemd unzip zip`
#### For Debian, Ubuntu and derivatives:
`sudo apt install -y cmake curl clang-15 freeglut3-dev git libbluetooth-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libtool nasm ninja-build`
You may also need to install `libusb-1.0-0-dev` as a workaround for an issue with the vcpkg hidapi package.
At Step 3 in [Build Cemu using cmake and clang](#build-cemu-using-cmake-and-clang), use the following command instead:
`cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja`
#### For Fedora and derivatives: #### For Fedora and derivatives:
`sudo dnf install bluez-libs-devel clang cmake cubeb-devel freeglut-devel git glm-devel gtk3-devel kernel-headers libgcrypt-devel libsecret-devel libtool libusb1-devel llvm nasm ninja-build perl-core systemd-devel wayland-protocols-devel zlib-devel zlib-static` `sudo dnf install clang cmake cubeb-devel freeglut-devel git glm-devel gtk3-devel kernel-headers libgcrypt-devel libsecret-devel libtool libusb1-devel nasm ninja-build perl-core systemd-devel zlib-devel`
### Build Cemu ### 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/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -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`.
#### CMake and Clang #### Using GCC
While we build and test Cemu using clang, using GCC might work better with your distro (they should be fairly similar performance/issues wise and should only be considered if compilation is the issue).
``` You can use GCC by doing the following:
git clone --recursive https://github.com/cemu-project/Cemu - make sure you have g++ installed in your system
cd Cemu - installation for Ubuntu and derivatives: `sudo apt install g++`
cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja - installation for Fedora and derivatives: `sudo dnf install gcc-c++`
cmake --build build - replace the step 3 with the following:
``` `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ -G Ninja`
#### GCC #### Troubleshooting steps
- If step 3 gives you an error about not being able to find ninja, try appending `-DCMAKE_MAKE_PROGRAM=/usr/bin/ninja` to the command and running it again.
If you are building using GCC, make sure you have g++ installed: - If step 3 fails while compiling the boost-build dependency, it means you don't have a working/good standard library installation. Check the integrity of your system headers and making sure that C++ related packages are installed and intact.
- Installation for Arch and derivatives: `sudo pacman -S gcc` - If step 3 gives a random error, read the `[package-name-and-platform]-out.log` and `[package-name-and-platform]-err.log` for the actual reason to see if you might be lacking the headers from a dependency.
- Installation for Debian, Ubuntu and derivatives: `sudo apt install g++` - If step 3 is still failing or if you're not able to find the cause, please make an issue on our Github about it!
- Installation for Fedora and derivatives: `sudo dnf install gcc-c++` - If step 3 fails during rebuild after `git pull` with an error that mentions RPATH, add this to the end of step 3: `-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON`
- If step 4 gives you an error that contains something like `main.cpp.o: in function 'std::__cxx11::basic_string...`, you likely are experiencing a clang-14 issue. This can only be fixed by either lowering the clang version or using GCC, see below.
``` - If step 4 gives you a different error, you could report it to this repo or try using GCC. Just make sure your standard library and compilers are updated since Cemu uses a lot of modern features!
git clone --recursive https://github.com/cemu-project/Cemu - If step 4 gives you undefined libdecor_xx, you are likely experiencing an issue with sdl2 package that comes with vcpkg. Delete sdl2 from vcpkg.json in source file and recompile.
cd Cemu - If step 4 gives you `fatal error: 'span' file not found`, then you're either missing `libstdc++` or are using a version that's too old. Install at least v10 with your package manager, eg `sudo apt install libstdc++-10-dev`. See #644.
cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ -G Ninja
cmake --build build
```
#### Debug Build
```
git clone --recursive https://github.com/cemu-project/Cemu
cd Cemu
cmake -S . -B build -DCMAKE_BUILD_TYPE=debug -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja
cmake --build build
```
If you are using GCC, replace `cmake -S . -B build -DCMAKE_BUILD_TYPE=debug -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja` with `cmake -S . -B build -DCMAKE_BUILD_TYPE=debug -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ -G Ninja`
#### Troubleshooting Steps
##### Compiling Errors
This section refers to running `cmake -S...` (truncated).
* `vcpkg install failed`
* Run the following in the root directory and try running the command again (don't forget to change directories afterwards):
* `cd dependencies/vcpkg && git fetch --unshallow`
* `Please ensure you're using the latest port files with git pull and vcpkg update.`
* Either:
* Update vcpkg by running by the following command:
* `git submodule update --remote dependencies/vcpkg`
* If you are sure vcpkg is up to date, check the following logs:
* `Cemu/dependencies/vcpkg/buildtrees/wxwidgets/config-x64-linux-out.log`
* `Cemu/dependencies/vcpkg/buildtrees/libsystemd/config-x64-linux-dbg-meson-log.txt.log`
* `Cemu/dependencies/vcpkg/buildtrees/libsystemd/config-x64-linux-dbg-out.log`
* Not able to find Ninja.
* Add the following and try running the command again:
* `-DCMAKE_MAKE_PROGRAM=/usr/bin/ninja`
* Compiling failed during the boost-build dependency.
* It means you don't have a working/good standard library installation. Check the integrity of your system headers and making sure that C++ related packages are installed and intact.
* Compiling failed during rebuild after `git pull` with an error that mentions RPATH
* Add the following and try running the command again:
* `-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON`
* Environment variable `VCPKG_FORCE_SYSTEM_BINARIES` must be set.
* Execute the folowing and then try running the command again:
* `export VCPKG_FORCE_SYSTEM_BINARIES=1`
* If you are getting a random error, read the [package-name-and-platform]-out.log and [package-name-and-platform]-err.log for the actual reason to see if you might be lacking the headers from a dependency.
If you are getting a different error than any of the errors listed above, you may either open an issue in this repo or try using [GCC](#gcc). Make sure your standard library and compilers are updated since Cemu uses a lot of modern features!
##### Building Errors
This section refers to running `cmake --build build`.
* `main.cpp.o: in function 'std::__cxx11::basic_string...`
* You likely are experiencing a clang-14 issue. This can only be fixed by either lowering the clang version or using GCC, see [GCC](#gcc).
* `fatal error: 'span' file not found`
* You're either missing `libstdc++` or are using a version that's too old. Install at least v10 with your package manager, eg `sudo apt install libstdc++-10-dev`. See [#644](https://github.com/cemu-project/Cemu/issues/644).
* `undefined libdecor_xx`
* You are likely experiencing an issue with sdl2 package that comes with vcpkg. Delete sdl2 from vcpkg.json in source file and recompile.
If you are getting a different error than any of the errors listed above, you may either open an issue in this repo or try using [GCC](#gcc). Make sure your standard library and compilers are updated since Cemu uses a lot of modern features!
## macOS ## macOS
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and below To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and
don't support the C++20 feature set required, so either install LLVM from Homebrew or make sure that below, built in LLVM, and Xcode LLVM don't support the C++20 feature set required. The OpenGL graphics
you have a recent enough version of Xcode. Xcode 15 is known to work. The OpenGL graphics API isn't API isn't support on macOS, Vulkan must be used. Additionally Vulkan must be used through the
supported on macOS, so Vulkan must be used through the Molten-VK compatibility layer. 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
### Installing brew ### Installing brew
1. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` 1. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
2. Set up the Homebrew shell environment: 2. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"` # set x86_64 brew env
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 Tool Dependencies ### Installing dependencies
The native versions of these can be used regardless of what type of Mac you have. `brew install boost git cmake llvm ninja nasm molten-vk automake libtool`
`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` 1. `git clone --recursive https://github.com/cemu-project/Cemu`
2. `cd Cemu` 2. `cd Cemu`
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_OSX_ARCHITECTURES=x86_64 -G Ninja` 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`
4. `cmake --build build` 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`. 5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`.
@ -191,42 +104,3 @@ Then install the dependencies:
2. Then, you can rebuild Cemu using the steps listed above, according to whether you use Linux or Windows. 2. Then, you can rebuild Cemu using the steps listed above, according to whether you use Linux or Windows.
If CMake complains about Cemu already being compiled or another similar error, try deleting the `CMakeCache.txt` file inside the `build` folder and retry building. If CMake complains about Cemu already being compiled or another similar error, try deleting the `CMakeCache.txt` file inside the `build` folder and retry building.
## CMake configure flags
Some flags can be passed during CMake configure to customise which features are enabled on build.
Example usage: `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DENABLE_SDL=ON -DENABLE_VULKAN=OFF`
### All platforms
| Flag | | Description | Default | Note |
|--------------------|:--|-----------------------------------------------------------------------------|---------|--------------------|
| ALLOW_PORTABLE | | Allow Cemu to use the `portable` directory to store configs and data | ON | |
| CEMU_CXX_FLAGS | | Flags passed straight to the compiler, e.g. `-march=native`, `-Wall`, `/W3` | "" | |
| ENABLE_CUBEB | | Enable cubeb audio backend | ON | |
| ENABLE_DISCORD_RPC | | Enable Discord Rich presence support | ON | |
| ENABLE_OPENGL | | Enable OpenGL graphics backend | ON | Currently required |
| ENABLE_HIDAPI | | Enable HIDAPI (used for Wiimote controller API) | ON | |
| ENABLE_SDL | | Enable SDLController controller API | ON | Currently required |
| ENABLE_VCPKG | | Use VCPKG package manager to obtain dependencies | ON | |
| ENABLE_VULKAN | | Enable the Vulkan graphics backend | ON | |
| ENABLE_WXWIDGETS | | Enable wxWidgets UI | ON | Currently required |
### Windows
| Flag | Description | Default | Note |
|--------------------|-----------------------------------|---------|--------------------|
| ENABLE_DIRECTAUDIO | Enable DirectAudio audio backend | ON | Currently required |
| ENABLE_DIRECTINPUT | Enable DirectInput controller API | ON | Currently required |
| ENABLE_XAUDIO | Enable XAudio audio backend | ON | |
| ENABLE_XINPUT | Enable XInput controller API | ON | |
### Linux
| Flag | Description | Default |
|-----------------------|----------------------------------------------------|---------|
| ENABLE_BLUEZ | Build with Bluez (used for Wiimote controller API) | ON |
| ENABLE_FERAL_GAMEMODE | Enable Feral Interactive GameMode support | ON |
| ENABLE_WAYLAND | Enable Wayland support | ON |
### macOS
| Flag | Description | Default |
|--------------|------------------------------------------------|---------|
| MACOS_BUNDLE | MacOS executable will be an application bundle | OFF |

View file

@ -1,45 +1,24 @@
cmake_minimum_required(VERSION 3.21.1) cmake_minimum_required(VERSION 3.21.1)
option(ENABLE_VCPKG "Enable the vcpkg package manager" ON) option(ENABLE_VCPKG "Enable the vcpkg package manager" ON)
option(PORTABLE "All data created and maintained by Cemu will be in the directory where the executable file is located" ON)
option(MACOS_BUNDLE "The executable when built on macOS will be created as an application bundle" OFF) option(MACOS_BUNDLE "The executable when built on macOS will be created as an application bundle" OFF)
option(ALLOW_PORTABLE "Allow Cemu to be run in portable mode" ON) set(EXPERIMENTAL_VERSION "" CACHE STRING "") # used by CI script to set experimental version
# used by CI script to set version: if (EXPERIMENTAL_VERSION)
set(EMULATOR_VERSION_MAJOR "0" CACHE STRING "") add_definitions(-DEMULATOR_VERSION_MINOR=${EXPERIMENTAL_VERSION})
set(EMULATOR_VERSION_MINOR "0" CACHE STRING "") execute_process(
set(EMULATOR_VERSION_PATCH "0" CACHE STRING "") COMMAND git log --format=%h -1
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
execute_process( OUTPUT_VARIABLE GIT_HASH
COMMAND git log --format=%h -1 OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} )
OUTPUT_VARIABLE GIT_HASH add_definitions(-DEMULATOR_HASH=${GIT_HASH})
OUTPUT_STRIP_TRAILING_WHITESPACE endif()
)
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
if (ENABLE_VCPKG) 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) if(UNIX AND NOT APPLE)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux") set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux")
elseif(APPLE)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_mac")
else() else()
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports") set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
endif() endif()
@ -64,9 +43,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_definitions($<$<CONFIG:Debug>:CEMU_DEBUG_ASSERT>) # if build type is debug, set CEMU_DEBUG_ASSERT 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}) if(PORTABLE)
add_definitions(-DEMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR}) add_compile_definitions(PORTABLE)
add_definitions(-DEMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}) endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -93,13 +72,11 @@ endif()
if (APPLE) if (APPLE)
enable_language(OBJC OBJCXX) enable_language(OBJC OBJCXX)
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0")
endif() endif()
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
option(ENABLE_WAYLAND "Build with Wayland support" ON) option(ENABLE_WAYLAND "Build with Wayland support" ON)
option(ENABLE_FERAL_GAMEMODE "Enables Feral Interactive GameMode Support" ON) option(ENABLE_FERAL_GAMEMODE "Enables Feral Interactive GameMode Support" ON)
option(ENABLE_BLUEZ "Build with Bluez support" ON)
endif() endif()
option(ENABLE_OPENGL "Enables the OpenGL backend" ON) option(ENABLE_OPENGL "Enables the OpenGL backend" ON)
@ -112,9 +89,10 @@ if (WIN32)
option(ENABLE_XINPUT "Enables the usage of XInput" ON) option(ENABLE_XINPUT "Enables the usage of XInput" ON)
option(ENABLE_DIRECTINPUT "Enables the usage of DirectInput" ON) option(ENABLE_DIRECTINPUT "Enables the usage of DirectInput" ON)
add_compile_definitions(HAS_DIRECTINPUT) add_compile_definitions(HAS_DIRECTINPUT)
set(ENABLE_WIIMOTE ON)
elseif (UNIX)
option(ENABLE_HIDAPI "Build with HIDAPI" ON)
endif() endif()
option(ENABLE_HIDAPI "Build with HIDAPI" ON)
option(ENABLE_SDL "Enables the SDLController backend" ON) option(ENABLE_SDL "Enables the SDLController backend" ON)
# audio backends # audio backends
@ -124,6 +102,23 @@ if (WIN32)
endif() endif()
option(ENABLE_CUBEB "Enabled cubeb backend" ON) option(ENABLE_CUBEB "Enabled cubeb backend" ON)
# usb hid backends
if (WIN32)
option(ENABLE_NSYSHID_WINDOWS_HID "Enables the native Windows HID backend for nsyshid" ON)
endif ()
# libusb and windows hid backends shouldn't be active at the same time; otherwise we'd see all devices twice!
if (NOT ENABLE_NSYSHID_WINDOWS_HID)
option(ENABLE_NSYSHID_LIBUSB "Enables the libusb backend for nsyshid" ON)
else ()
set(ENABLE_NSYSHID_LIBUSB OFF CACHE BOOL "" FORCE)
endif ()
if (ENABLE_NSYSHID_WINDOWS_HID)
add_compile_definitions(NSYSHID_ENABLE_BACKEND_WINDOWS_HID)
endif ()
if (ENABLE_NSYSHID_LIBUSB)
add_compile_definitions(NSYSHID_ENABLE_BACKEND_LIBUSB)
endif ()
option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON) option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON)
set(THREADS_PREFER_PTHREAD_FLAG true) set(THREADS_PREFER_PTHREAD_FLAG true)
@ -139,7 +134,7 @@ find_package(ZLIB REQUIRED)
find_package(zstd MODULE REQUIRED) # MODULE so that zstd::zstd is available find_package(zstd MODULE REQUIRED) # MODULE so that zstd::zstd is available
find_package(OpenSSL COMPONENTS Crypto SSL REQUIRED) find_package(OpenSSL COMPONENTS Crypto SSL REQUIRED)
find_package(glm REQUIRED) find_package(glm REQUIRED)
find_package(fmt 9 REQUIRED) find_package(fmt 9.1.0...<10 REQUIRED)
find_package(PNG REQUIRED) find_package(PNG REQUIRED)
# glslang versions older than 11.11.0 define targets without a namespace # glslang versions older than 11.11.0 define targets without a namespace
@ -164,12 +159,6 @@ if (UNIX AND NOT APPLE)
endif() endif()
find_package(GTK3 REQUIRED) find_package(GTK3 REQUIRED)
if(ENABLE_BLUEZ)
find_package(bluez REQUIRED)
set(SUPPORTS_WIIMOTE ON)
add_compile_definitions(HAS_BLUEZ)
endif()
endif() endif()
if (ENABLE_VULKAN) if (ENABLE_VULKAN)
@ -188,7 +177,7 @@ endif()
if (ENABLE_HIDAPI) if (ENABLE_HIDAPI)
find_package(hidapi REQUIRED) find_package(hidapi REQUIRED)
set(SUPPORTS_WIIMOTE ON) set(ENABLE_WIIMOTE ON)
add_compile_definitions(HAS_HIDAPI) add_compile_definitions(HAS_HIDAPI)
endif () endif ()
@ -213,7 +202,7 @@ if (ENABLE_CUBEB)
option(BUILD_TOOLS "" OFF) option(BUILD_TOOLS "" OFF)
option(BUNDLE_SPEEX "" OFF) option(BUNDLE_SPEEX "" OFF)
set(USE_WINMM OFF CACHE BOOL "") set(USE_WINMM OFF CACHE BOOL "")
add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL SYSTEM) add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL)
set_property(TARGET cubeb PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET cubeb PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_library(cubeb::cubeb ALIAS cubeb) add_library(cubeb::cubeb ALIAS cubeb)
endif() endif()
@ -222,15 +211,6 @@ endif()
add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL) add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL)
if (CMAKE_OSX_ARCHITECTURES)
set(CEMU_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
else()
set(CEMU_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
add_subdirectory("dependencies/xbyak_aarch64" EXCLUDE_FROM_ALL)
endif()
find_package(ZArchive) find_package(ZArchive)
if (NOT ZArchive_FOUND) if (NOT ZArchive_FOUND)
add_subdirectory("dependencies/ZArchive" EXCLUDE_FROM_ALL) add_subdirectory("dependencies/ZArchive" EXCLUDE_FROM_ALL)

View file

@ -14,7 +14,7 @@
"generator": "Ninja", "generator": "Ninja",
"inheritEnvironments": [ "msvc_x64_x64" ], "inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}", "buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}" "installRoot": "${projectDir}\\out\\install\\${name}",
}, },
{ {
"name": "Debug", "name": "Debug",

View file

@ -55,7 +55,6 @@ std::string _pathToUtf8(const fs::path& path);
fs::path _utf8ToPath(std::string_view input); fs::path _utf8ToPath(std::string_view input);
// wxString <-> std::string // wxString <-> std::string
wxString wxString::FromUTF8(const std::string& s)
wxString to_wxString(std::string_view str); // in gui/helpers.h wxString to_wxString(std::string_view str); // in gui/helpers.h
std::string wxString::utf8_string(); std::string wxString::utf8_string();

View file

@ -48,7 +48,7 @@ Before submitting a pull request, please read and follow our code style guidelin
If coding isn't your thing, testing games and making detailed bug reports or updating the (usually outdated) compatibility wiki is also appreciated! If coding isn't your thing, testing games and making detailed bug reports or updating the (usually outdated) compatibility wiki is also appreciated!
Questions about Cemu's software architecture can also be answered on Discord (or through the Matrix bridge). Questions about Cemu's software architecture can also be answered on Discord (through the Matrix bridge).
## License ## License
Cemu is licensed under [Mozilla Public License 2.0](/LICENSE.txt). Exempt from this are all files in the dependencies directory for which the licenses of the original code apply as well as some individual files in the src folder, as specified in those file headers respectively. Cemu is licensed under [Mozilla Public License 2.0](/LICENSE.txt). Exempt from this are all files in the dependencies directory for which the licenses of the original code apply as well as some individual files in the src folder, as specified in those file headers respectively.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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
bin/shaderCache/info.txt Normal file
View file

@ -0,0 +1 @@
If you plan to transfer the shader cache to a different PC or Cemu installation you only need to copy the 'transferable' directory.

View file

@ -1,26 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="boost::container::small_vector&lt;*&gt;">
<Expand>
<Item Name="[size]">m_holder.m_size</Item>
<ArrayItems>
<Size>m_holder.m_size</Size>
<ValuePointer>m_holder.m_start</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="boost::container::static_vector&lt;*&gt;">
<DisplayString>{{ size={m_holder.m_size} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">m_holder.m_size</Item>
<Item Name="[capacity]" ExcludeView="simple">static_capacity</Item>
<ArrayItems>
<Size>m_holder.m_size</Size>
<ValuePointer>($T1*)m_holder.storage.data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
</AutoVisualizer>

View file

@ -1,20 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
# SPDX-License-Identifier: ISC
find_package(bluez CONFIG)
if (NOT bluez_FOUND)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(bluez IMPORTED_TARGET GLOBAL bluez-1.0 bluez)
if (bluez_FOUND)
add_library(bluez::bluez ALIAS PkgConfig::bluez)
endif ()
endif ()
endif ()
find_package_handle_standard_args(bluez
REQUIRED_VARS
bluez_LINK_LIBRARIES
bluez_FOUND
VERSION_VAR bluez_VERSION
)

View file

@ -117,13 +117,7 @@ add_library (ih264d
"decoder/ivd.h" "decoder/ivd.h"
) )
if (CMAKE_OSX_ARCHITECTURES) if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
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") set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES}) include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
target_sources(ih264d PRIVATE target_sources(ih264d PRIVATE
@ -146,7 +140,7 @@ target_sources(ih264d PRIVATE
"decoder/x86/ih264d_function_selector_sse42.c" "decoder/x86/ih264d_function_selector_sse42.c"
"decoder/x86/ih264d_function_selector_ssse3.c" "decoder/x86/ih264d_function_selector_ssse3.c"
) )
elseif(IH264D_ARCHITECTURE STREQUAL "aarch64" OR IH264D_ARCHITECTURE STREQUAL "arm64") elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
enable_language( C CXX ASM ) enable_language( C CXX ASM )
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm") set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES}) include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
@ -183,11 +177,8 @@ target_sources(ih264d PRIVATE
"decoder/arm/ih264d_function_selector.c" "decoder/arm/ih264d_function_selector.c"
) )
target_compile_options(ih264d PRIVATE -DARMV8) target_compile_options(ih264d PRIVATE -DARMV8)
if(APPLE)
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
endif()
else() else()
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}") message(FATAL_ERROR "ih264d unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif() endif()
if(MSVC) if(MSVC)

View file

@ -429,13 +429,8 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
rev64 v7.4h, v2.4h rev64 v7.4h, v2.4h
ld1 {v3.2s}, [x10] ld1 {v3.2s}, [x10]
sub x5, x3, #8 sub x5, x3, #8
#ifdef __APPLE__
adrp x12, _ih264_gai1_intrapred_chroma_plane_coeffs1@GOTPAGE
ldr x12, [x12, _ih264_gai1_intrapred_chroma_plane_coeffs1@GOTPAGEOFF]
#else
adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs1 adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs1
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs1] ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs1]
#endif
usubl v10.8h, v5.8b, v1.8b usubl v10.8h, v5.8b, v1.8b
ld1 {v8.8b, v9.8b}, [x12] // Load multiplication factors 1 to 8 into D3 ld1 {v8.8b, v9.8b}, [x12] // Load multiplication factors 1 to 8 into D3
mov v8.d[1], v9.d[0] mov v8.d[1], v9.d[0]
@ -489,13 +484,10 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
zip1 v1.8h, v0.8h, v2.8h zip1 v1.8h, v0.8h, v2.8h
zip2 v2.8h, v0.8h, v2.8h zip2 v2.8h, v0.8h, v2.8h
mov v0.16b, v1.16b mov v0.16b, v1.16b
#ifdef __APPLE__
adrp x12, _ih264_gai1_intrapred_chroma_plane_coeffs2@GOTPAGE
ldr x12, [x12, _ih264_gai1_intrapred_chroma_plane_coeffs2@GOTPAGEOFF]
#else
adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs2 adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs2
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs2] ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs2]
#endif
ld1 {v8.2s, v9.2s}, [x12] ld1 {v8.2s, v9.2s}, [x12]
mov v8.d[1], v9.d[0] mov v8.d[1], v9.d[0]
mov v10.16b, v8.16b mov v10.16b, v8.16b

View file

@ -431,13 +431,10 @@ ih264_intra_pred_luma_16x16_mode_plane_av8:
mov x10, x1 //top_left mov x10, x1 //top_left
mov x4, #-1 mov x4, #-1
ld1 {v2.2s}, [x1], x8 ld1 {v2.2s}, [x1], x8
#ifdef __APPLE__
adrp x7, _ih264_gai1_intrapred_luma_plane_coeffs@GOTPAGE
ldr x7, [x7, _ih264_gai1_intrapred_luma_plane_coeffs@GOTPAGEOFF]
#else
adrp x7, :got:ih264_gai1_intrapred_luma_plane_coeffs adrp x7, :got:ih264_gai1_intrapred_luma_plane_coeffs
ldr x7, [x7, #:got_lo12:ih264_gai1_intrapred_luma_plane_coeffs] ldr x7, [x7, #:got_lo12:ih264_gai1_intrapred_luma_plane_coeffs]
#endif
ld1 {v0.2s}, [x1] ld1 {v0.2s}, [x1]
rev64 v2.8b, v2.8b rev64 v2.8b, v2.8b
ld1 {v6.2s, v7.2s}, [x7] ld1 {v6.2s, v7.2s}, [x7]

View file

@ -1029,13 +1029,9 @@ ih264_intra_pred_luma_8x8_mode_horz_u_av8:
mov v3.d[0], v2.d[1] mov v3.d[0], v2.d[1]
ext v4.16b, v2.16b , v2.16b , #1 ext v4.16b, v2.16b , v2.16b , #1
mov v5.d[0], v4.d[1] mov v5.d[0], v4.d[1]
#ifdef __APPLE__
adrp x12, _ih264_gai1_intrapred_luma_8x8_horz_u@GOTPAGE
ldr x12, [x12, _ih264_gai1_intrapred_luma_8x8_horz_u@GOTPAGEOFF]
#else
adrp x12, :got:ih264_gai1_intrapred_luma_8x8_horz_u adrp x12, :got:ih264_gai1_intrapred_luma_8x8_horz_u
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_luma_8x8_horz_u] ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_luma_8x8_horz_u]
#endif
uaddl v20.8h, v0.8b, v2.8b uaddl v20.8h, v0.8b, v2.8b
uaddl v22.8h, v1.8b, v3.8b uaddl v22.8h, v1.8b, v3.8b
uaddl v24.8h, v2.8b, v4.8b uaddl v24.8h, v2.8b, v4.8b

View file

@ -142,22 +142,14 @@ ih264_weighted_bi_pred_luma_av8:
sxtw x4, w4 sxtw x4, w4
sxtw x5, w5 sxtw x5, w5
stp x19, x20, [sp, #-16]! stp x19, x20, [sp, #-16]!
#ifndef __APPLE__
ldr w8, [sp, #80] //Load wt2 in w8 ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #88] //Load ofst1 in w9 ldr w9, [sp, #88] //Load ofst1 in w9
ldr w10, [sp, #96] //Load ofst2 in w10
ldr w11, [sp, #104] //Load ht in w11
ldr w12, [sp, #112] //Load wd in w12
#else
ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #84] //Load ofst1 in w9
ldr w10, [sp, #88] //Load ofst2 in w10
ldr w11, [sp, #92] //Load ht in w11
ldr w12, [sp, #96] //Load wd in w12
#endif
add w6, w6, #1 //w6 = log_WD + 1 add w6, w6, #1 //w6 = log_WD + 1
neg w10, w6 //w10 = -(log_WD + 1) neg w10, w6 //w10 = -(log_WD + 1)
dup v0.8h, w10 //Q0 = -(log_WD + 1) (32-bit) dup v0.8h, w10 //Q0 = -(log_WD + 1) (32-bit)
ldr w10, [sp, #96] //Load ofst2 in w10
ldr w11, [sp, #104] //Load ht in w11
ldr w12, [sp, #112] //Load wd in w12
add w9, w9, #1 //w9 = ofst1 + 1 add w9, w9, #1 //w9 = ofst1 + 1
add w9, w9, w10 //w9 = ofst1 + ofst2 + 1 add w9, w9, w10 //w9 = ofst1 + ofst2 + 1
mov v2.s[0], w7 mov v2.s[0], w7
@ -432,24 +424,17 @@ ih264_weighted_bi_pred_chroma_av8:
sxtw x5, w5 sxtw x5, w5
stp x19, x20, [sp, #-16]! stp x19, x20, [sp, #-16]!
#ifndef __APPLE__
ldr w8, [sp, #80] //Load wt2 in w8 ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #88] //Load ofst1 in w9
ldr w10, [sp, #96] //Load ofst2 in w10
ldr w11, [sp, #104] //Load ht in w11
ldr w12, [sp, #112] //Load wd in w12
#else
ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #84] //Load ofst1 in w9
ldr w10, [sp, #88] //Load ofst2 in w10
ldr w11, [sp, #92] //Load ht in w11
ldr w12, [sp, #96] //Load wd in w12
#endif
dup v4.4s, w8 //Q2 = (wt2_u, wt2_v) (32-bit) dup v4.4s, w8 //Q2 = (wt2_u, wt2_v) (32-bit)
dup v2.4s, w7 //Q1 = (wt1_u, wt1_v) (32-bit) dup v2.4s, w7 //Q1 = (wt1_u, wt1_v) (32-bit)
add w6, w6, #1 //w6 = log_WD + 1 add w6, w6, #1 //w6 = log_WD + 1
ldr w9, [sp, #88] //Load ofst1 in w9
ldr w10, [sp, #96] //Load ofst2 in w10
neg w20, w6 //w20 = -(log_WD + 1) neg w20, w6 //w20 = -(log_WD + 1)
dup v0.8h, w20 //Q0 = -(log_WD + 1) (16-bit) dup v0.8h, w20 //Q0 = -(log_WD + 1) (16-bit)
ldr w11, [sp, #104] //Load ht in x11
ldr w12, [sp, #112] //Load wd in x12
dup v20.8h, w9 //0ffset1 dup v20.8h, w9 //0ffset1
dup v21.8h, w10 //0ffset2 dup v21.8h, w10 //0ffset2
srhadd v6.8b, v20.8b, v21.8b srhadd v6.8b, v20.8b, v21.8b

View file

@ -1,185 +0,0 @@
// macOS clang compilers append preceding underscores to function names, this is to prevent
// mismatches with the assembly function names and the C functions as defined in the header.
.global _ih264_deblk_chroma_horz_bs4_av8
_ih264_deblk_chroma_horz_bs4_av8 = ih264_deblk_chroma_horz_bs4_av8
.global _ih264_deblk_chroma_horz_bslt4_av8
_ih264_deblk_chroma_horz_bslt4_av8 = ih264_deblk_chroma_horz_bslt4_av8
.global _ih264_deblk_chroma_vert_bs4_av8
_ih264_deblk_chroma_vert_bs4_av8 = ih264_deblk_chroma_vert_bs4_av8
.global _ih264_deblk_chroma_vert_bslt4_av8
_ih264_deblk_chroma_vert_bslt4_av8 = ih264_deblk_chroma_vert_bslt4_av8
.global _ih264_deblk_luma_horz_bs4_av8
_ih264_deblk_luma_horz_bs4_av8 = ih264_deblk_luma_horz_bs4_av8
.global _ih264_deblk_luma_horz_bslt4_av8
_ih264_deblk_luma_horz_bslt4_av8 = ih264_deblk_luma_horz_bslt4_av8
.global _ih264_deblk_luma_vert_bs4_av8
_ih264_deblk_luma_vert_bs4_av8 = ih264_deblk_luma_vert_bs4_av8
.global _ih264_deblk_luma_vert_bslt4_av8
_ih264_deblk_luma_vert_bslt4_av8 = ih264_deblk_luma_vert_bslt4_av8
.global _ih264_default_weighted_pred_chroma_av8
_ih264_default_weighted_pred_chroma_av8 = ih264_default_weighted_pred_chroma_av8
.global _ih264_default_weighted_pred_luma_av8
_ih264_default_weighted_pred_luma_av8 = ih264_default_weighted_pred_luma_av8
.global _ih264_ihadamard_scaling_4x4_av8
_ih264_ihadamard_scaling_4x4_av8 = ih264_ihadamard_scaling_4x4_av8
.global _ih264_inter_pred_chroma_av8
_ih264_inter_pred_chroma_av8 = ih264_inter_pred_chroma_av8
.global _ih264_inter_pred_luma_copy_av8
_ih264_inter_pred_luma_copy_av8 = ih264_inter_pred_luma_copy_av8
.global _ih264_inter_pred_luma_horz_av8
_ih264_inter_pred_luma_horz_av8 = ih264_inter_pred_luma_horz_av8
.global _ih264_inter_pred_luma_horz_hpel_vert_hpel_av8
_ih264_inter_pred_luma_horz_hpel_vert_hpel_av8 = ih264_inter_pred_luma_horz_hpel_vert_hpel_av8
.global _ih264_inter_pred_luma_horz_hpel_vert_qpel_av8
_ih264_inter_pred_luma_horz_hpel_vert_qpel_av8 = ih264_inter_pred_luma_horz_hpel_vert_qpel_av8
.global _ih264_inter_pred_luma_horz_qpel_av8
_ih264_inter_pred_luma_horz_qpel_av8 = ih264_inter_pred_luma_horz_qpel_av8
.global _ih264_inter_pred_luma_horz_qpel_vert_hpel_av8
_ih264_inter_pred_luma_horz_qpel_vert_hpel_av8 = ih264_inter_pred_luma_horz_qpel_vert_hpel_av8
.global _ih264_inter_pred_luma_horz_qpel_vert_qpel_av8
_ih264_inter_pred_luma_horz_qpel_vert_qpel_av8 = ih264_inter_pred_luma_horz_qpel_vert_qpel_av8
.global _ih264_inter_pred_luma_vert_av8
_ih264_inter_pred_luma_vert_av8 = ih264_inter_pred_luma_vert_av8
.global _ih264_inter_pred_luma_vert_qpel_av8
_ih264_inter_pred_luma_vert_qpel_av8 = ih264_inter_pred_luma_vert_qpel_av8
.global _ih264_intra_pred_chroma_8x8_mode_horz_av8
_ih264_intra_pred_chroma_8x8_mode_horz_av8 = ih264_intra_pred_chroma_8x8_mode_horz_av8
.global _ih264_intra_pred_chroma_8x8_mode_plane_av8
_ih264_intra_pred_chroma_8x8_mode_plane_av8 = ih264_intra_pred_chroma_8x8_mode_plane_av8
.global _ih264_intra_pred_chroma_8x8_mode_vert_av8
_ih264_intra_pred_chroma_8x8_mode_vert_av8 = ih264_intra_pred_chroma_8x8_mode_vert_av8
.global _ih264_intra_pred_luma_16x16_mode_dc_av8
_ih264_intra_pred_luma_16x16_mode_dc_av8 = ih264_intra_pred_luma_16x16_mode_dc_av8
.global _ih264_intra_pred_luma_16x16_mode_horz_av8
_ih264_intra_pred_luma_16x16_mode_horz_av8 = ih264_intra_pred_luma_16x16_mode_horz_av8
.global _ih264_intra_pred_luma_16x16_mode_plane_av8
_ih264_intra_pred_luma_16x16_mode_plane_av8 = ih264_intra_pred_luma_16x16_mode_plane_av8
.global _ih264_intra_pred_luma_16x16_mode_vert_av8
_ih264_intra_pred_luma_16x16_mode_vert_av8 = ih264_intra_pred_luma_16x16_mode_vert_av8
.global _ih264_intra_pred_luma_4x4_mode_dc_av8
_ih264_intra_pred_luma_4x4_mode_dc_av8 = ih264_intra_pred_luma_4x4_mode_dc_av8
.global _ih264_intra_pred_luma_4x4_mode_diag_dl_av8
_ih264_intra_pred_luma_4x4_mode_diag_dl_av8 = ih264_intra_pred_luma_4x4_mode_diag_dl_av8
.global _ih264_intra_pred_luma_4x4_mode_diag_dr_av8
_ih264_intra_pred_luma_4x4_mode_diag_dr_av8 = ih264_intra_pred_luma_4x4_mode_diag_dr_av8
.global _ih264_intra_pred_luma_4x4_mode_horz_av8
_ih264_intra_pred_luma_4x4_mode_horz_av8 = ih264_intra_pred_luma_4x4_mode_horz_av8
.global _ih264_intra_pred_luma_4x4_mode_horz_d_av8
_ih264_intra_pred_luma_4x4_mode_horz_d_av8 = ih264_intra_pred_luma_4x4_mode_horz_d_av8
.global _ih264_intra_pred_luma_4x4_mode_horz_u_av8
_ih264_intra_pred_luma_4x4_mode_horz_u_av8 = ih264_intra_pred_luma_4x4_mode_horz_u_av8
.global _ih264_intra_pred_luma_4x4_mode_vert_av8
_ih264_intra_pred_luma_4x4_mode_vert_av8 = ih264_intra_pred_luma_4x4_mode_vert_av8
.global _ih264_intra_pred_luma_4x4_mode_vert_l_av8
_ih264_intra_pred_luma_4x4_mode_vert_l_av8 = ih264_intra_pred_luma_4x4_mode_vert_l_av8
.global _ih264_intra_pred_luma_4x4_mode_vert_r_av8
_ih264_intra_pred_luma_4x4_mode_vert_r_av8 = ih264_intra_pred_luma_4x4_mode_vert_r_av8
.global _ih264_intra_pred_luma_8x8_mode_dc_av8
_ih264_intra_pred_luma_8x8_mode_dc_av8 = ih264_intra_pred_luma_8x8_mode_dc_av8
.global _ih264_intra_pred_luma_8x8_mode_diag_dl_av8
_ih264_intra_pred_luma_8x8_mode_diag_dl_av8 = ih264_intra_pred_luma_8x8_mode_diag_dl_av8
.global _ih264_intra_pred_luma_8x8_mode_diag_dr_av8
_ih264_intra_pred_luma_8x8_mode_diag_dr_av8 = ih264_intra_pred_luma_8x8_mode_diag_dr_av8
.global _ih264_intra_pred_luma_8x8_mode_horz_av8
_ih264_intra_pred_luma_8x8_mode_horz_av8 = ih264_intra_pred_luma_8x8_mode_horz_av8
.global _ih264_intra_pred_luma_8x8_mode_horz_d_av8
_ih264_intra_pred_luma_8x8_mode_horz_d_av8 = ih264_intra_pred_luma_8x8_mode_horz_d_av8
.global _ih264_intra_pred_luma_8x8_mode_horz_u_av8
_ih264_intra_pred_luma_8x8_mode_horz_u_av8 = ih264_intra_pred_luma_8x8_mode_horz_u_av8
.global _ih264_intra_pred_luma_8x8_mode_vert_av8
_ih264_intra_pred_luma_8x8_mode_vert_av8 = ih264_intra_pred_luma_8x8_mode_vert_av8
.global _ih264_intra_pred_luma_8x8_mode_vert_l_av8
_ih264_intra_pred_luma_8x8_mode_vert_l_av8 = ih264_intra_pred_luma_8x8_mode_vert_l_av8
.global _ih264_intra_pred_luma_8x8_mode_vert_r_av8
_ih264_intra_pred_luma_8x8_mode_vert_r_av8 = ih264_intra_pred_luma_8x8_mode_vert_r_av8
.global _ih264_iquant_itrans_recon_4x4_av8
_ih264_iquant_itrans_recon_4x4_av8 = ih264_iquant_itrans_recon_4x4_av8
.global _ih264_iquant_itrans_recon_4x4_dc_av8
_ih264_iquant_itrans_recon_4x4_dc_av8 = ih264_iquant_itrans_recon_4x4_dc_av8
.global _ih264_iquant_itrans_recon_8x8_av8
_ih264_iquant_itrans_recon_8x8_av8 = ih264_iquant_itrans_recon_8x8_av8
.global _ih264_iquant_itrans_recon_8x8_dc_av8
_ih264_iquant_itrans_recon_8x8_dc_av8 = ih264_iquant_itrans_recon_8x8_dc_av8
.global _ih264_iquant_itrans_recon_chroma_4x4_av8
_ih264_iquant_itrans_recon_chroma_4x4_av8 = ih264_iquant_itrans_recon_chroma_4x4_av8
.global _ih264_iquant_itrans_recon_chroma_4x4_dc_av8
_ih264_iquant_itrans_recon_chroma_4x4_dc_av8 = ih264_iquant_itrans_recon_chroma_4x4_dc_av8
.global _ih264_pad_left_chroma_av8
_ih264_pad_left_chroma_av8 = ih264_pad_left_chroma_av8
.global _ih264_pad_left_luma_av8
_ih264_pad_left_luma_av8 = ih264_pad_left_luma_av8
.global _ih264_pad_right_chroma_av8
_ih264_pad_right_chroma_av8 = ih264_pad_right_chroma_av8
.global _ih264_pad_right_luma_av8
_ih264_pad_right_luma_av8 = ih264_pad_right_luma_av8
.global _ih264_pad_top_av8
_ih264_pad_top_av8 = ih264_pad_top_av8
.global _ih264_weighted_bi_pred_chroma_av8
_ih264_weighted_bi_pred_chroma_av8 = ih264_weighted_bi_pred_chroma_av8
.global _ih264_weighted_bi_pred_luma_av8
_ih264_weighted_bi_pred_luma_av8 = ih264_weighted_bi_pred_luma_av8
.global _ih264_weighted_pred_chroma_av8
_ih264_weighted_pred_chroma_av8 = ih264_weighted_pred_chroma_av8
.global _ih264_weighted_pred_luma_av8
_ih264_weighted_pred_luma_av8 = ih264_weighted_pred_luma_av8

2
dependencies/vcpkg vendored

@ -1 +1 @@
Subproject commit 533a5fda5c0646d1771345fb572e759283444d5f Subproject commit b81bc3a83fdbdffe80325eeabb2ec735a1f3c29d

View file

@ -0,0 +1,13 @@
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index 65a98efbe..2f99f28f1 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -352,7 +352,7 @@ endmacro()
# - HAVE_SDL_LOADSO opt
macro(CheckLibSampleRate)
if(SDL_LIBSAMPLERATE)
- find_package(SampleRate QUIET)
+ find_package(SampleRate CONFIG REQUIRED)
if(SampleRate_FOUND AND TARGET SampleRate::samplerate)
set(HAVE_LIBSAMPLERATE TRUE)
set(HAVE_LIBSAMPLERATE_H TRUE)

View file

@ -0,0 +1,130 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO libsdl-org/SDL
REF "release-${VERSION}"
SHA512 90858ae8c5fdddd5e13724e05ad0970e11bbab1df8a0201c3f4ce354dc6018e5d4ab7279402a263c716aacdaa52745f78531dc225d48d790ee9307e2f6198695
HEAD_REF main
PATCHES
deps.patch
)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" SDL_STATIC)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SDL_SHARED)
string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" FORCE_STATIC_VCRT)
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
vulkan SDL_VULKAN
x11 SDL_X11
wayland SDL_WAYLAND
samplerate SDL_LIBSAMPLERATE
ibus SDL_IBUS
)
if ("x11" IN_LIST FEATURES)
message(WARNING "You will need to install Xorg dependencies to use feature x11:\nsudo apt install libx11-dev libxft-dev libxext-dev\n")
endif()
if ("wayland" IN_LIST FEATURES)
message(WARNING "You will need to install Wayland dependencies to use feature wayland:\nsudo apt install libwayland-dev libxkbcommon-dev libegl1-mesa-dev\n")
endif()
if ("ibus" IN_LIST FEATURES)
message(WARNING "You will need to install ibus dependencies to use feature ibus:\nsudo apt install libibus-1.0-dev\n")
endif()
if(VCPKG_TARGET_IS_UWP)
set(configure_opts WINDOWS_USE_MSBUILD)
endif()
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
${configure_opts}
OPTIONS ${FEATURE_OPTIONS}
-DSDL_STATIC=${SDL_STATIC}
-DSDL_SHARED=${SDL_SHARED}
-DSDL_FORCE_STATIC_VCRT=${FORCE_STATIC_VCRT}
-DSDL_LIBC=ON
-DSDL_TEST=OFF
-DSDL_INSTALL_CMAKEDIR="cmake"
-DCMAKE_DISABLE_FIND_PACKAGE_Git=ON
-DSDL_LIBSAMPLERATE_SHARED=OFF
MAYBE_UNUSED_VARIABLES
SDL_FORCE_STATIC_VCRT
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH cmake)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/debug/include"
"${CURRENT_PACKAGES_DIR}/debug/share"
"${CURRENT_PACKAGES_DIR}/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/debug/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/debug/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/share/licenses"
"${CURRENT_PACKAGES_DIR}/share/aclocal"
)
file(GLOB BINS "${CURRENT_PACKAGES_DIR}/debug/bin/*" "${CURRENT_PACKAGES_DIR}/bin/*")
if(NOT BINS)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/bin"
"${CURRENT_PACKAGES_DIR}/debug/bin"
)
endif()
if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_UWP AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/lib/SDL2main.lib" "${CURRENT_PACKAGES_DIR}/lib/manual-link/SDL2main.lib")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/debug/lib/SDL2maind.lib" "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link/SDL2maind.lib")
endif()
file(GLOB SHARE_FILES "${CURRENT_PACKAGES_DIR}/share/sdl2/*.cmake")
foreach(SHARE_FILE ${SHARE_FILES})
vcpkg_replace_string("${SHARE_FILE}" "lib/SDL2main" "lib/manual-link/SDL2main")
endforeach()
endif()
vcpkg_copy_pdbs()
set(DYLIB_COMPATIBILITY_VERSION_REGEX "set\\(DYLIB_COMPATIBILITY_VERSION (.+)\\)")
set(DYLIB_CURRENT_VERSION_REGEX "set\\(DYLIB_CURRENT_VERSION (.+)\\)")
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_COMPATIBILITY_VERSION REGEX ${DYLIB_COMPATIBILITY_VERSION_REGEX})
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_CURRENT_VERSION REGEX ${DYLIB_CURRENT_VERSION_REGEX})
string(REGEX REPLACE ${DYLIB_COMPATIBILITY_VERSION_REGEX} "\\1" DYLIB_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}")
string(REGEX REPLACE ${DYLIB_CURRENT_VERSION_REGEX} "\\1" DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION}")
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2main" "-lSDL2maind")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2 " "-lSDL2d ")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-static " "-lSDL2-staticd ")
endif()
if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic" AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-lSDL2-static " " ")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-staticd " " ")
endif()
endif()
if(VCPKG_TARGET_IS_UWP)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "d")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
endif()
vcpkg_fixup_pkgconfig()
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt")

View file

@ -0,0 +1,8 @@
sdl2 provides CMake targets:
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(main
PRIVATE
$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>
$<IF:$<TARGET_EXISTS:SDL2::SDL2>,SDL2::SDL2,SDL2::SDL2-static>
)

View file

@ -0,0 +1,58 @@
{
"name": "sdl2",
"version": "2.26.5",
"description": "Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.",
"homepage": "https://www.libsdl.org/download-2.0.php",
"license": "Zlib",
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
],
"default-features": [
"base"
],
"features": {
"base": {
"description": "Base functionality for SDL",
"dependencies": [
{
"name": "sdl2",
"default-features": false,
"features": [
"ibus",
"wayland",
"x11"
],
"platform": "linux"
}
]
},
"ibus": {
"description": "Build with ibus IME support",
"supports": "linux"
},
"samplerate": {
"description": "Use libsamplerate for audio rate conversion",
"dependencies": [
"libsamplerate"
]
},
"vulkan": {
"description": "Vulkan functionality for SDL"
},
"wayland": {
"description": "Build with Wayland support",
"supports": "linux"
},
"x11": {
"description": "Build with X11 support",
"supports": "!windows"
}
}
}

View file

@ -0,0 +1,13 @@
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index 65a98efbe..2f99f28f1 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -352,7 +352,7 @@ endmacro()
# - HAVE_SDL_LOADSO opt
macro(CheckLibSampleRate)
if(SDL_LIBSAMPLERATE)
- find_package(SampleRate QUIET)
+ find_package(SampleRate CONFIG REQUIRED)
if(SampleRate_FOUND AND TARGET SampleRate::samplerate)
set(HAVE_LIBSAMPLERATE TRUE)
set(HAVE_LIBSAMPLERATE_H TRUE)

View file

@ -0,0 +1,130 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO libsdl-org/SDL
REF "release-${VERSION}"
SHA512 90858ae8c5fdddd5e13724e05ad0970e11bbab1df8a0201c3f4ce354dc6018e5d4ab7279402a263c716aacdaa52745f78531dc225d48d790ee9307e2f6198695
HEAD_REF main
PATCHES
deps.patch
)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" SDL_STATIC)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SDL_SHARED)
string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" FORCE_STATIC_VCRT)
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
vulkan SDL_VULKAN
x11 SDL_X11
wayland SDL_WAYLAND
samplerate SDL_LIBSAMPLERATE
ibus SDL_IBUS
)
if ("x11" IN_LIST FEATURES)
message(WARNING "You will need to install Xorg dependencies to use feature x11:\nsudo apt install libx11-dev libxft-dev libxext-dev\n")
endif()
if ("wayland" IN_LIST FEATURES)
message(WARNING "You will need to install Wayland dependencies to use feature wayland:\nsudo apt install libwayland-dev libxkbcommon-dev libegl1-mesa-dev\n")
endif()
if ("ibus" IN_LIST FEATURES)
message(WARNING "You will need to install ibus dependencies to use feature ibus:\nsudo apt install libibus-1.0-dev\n")
endif()
if(VCPKG_TARGET_IS_UWP)
set(configure_opts WINDOWS_USE_MSBUILD)
endif()
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
${configure_opts}
OPTIONS ${FEATURE_OPTIONS}
-DSDL_STATIC=${SDL_STATIC}
-DSDL_SHARED=${SDL_SHARED}
-DSDL_FORCE_STATIC_VCRT=${FORCE_STATIC_VCRT}
-DSDL_LIBC=ON
-DSDL_TEST=OFF
-DSDL_INSTALL_CMAKEDIR="cmake"
-DCMAKE_DISABLE_FIND_PACKAGE_Git=ON
-DSDL_LIBSAMPLERATE_SHARED=OFF
MAYBE_UNUSED_VARIABLES
SDL_FORCE_STATIC_VCRT
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH cmake)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/debug/include"
"${CURRENT_PACKAGES_DIR}/debug/share"
"${CURRENT_PACKAGES_DIR}/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/debug/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/debug/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/share/licenses"
"${CURRENT_PACKAGES_DIR}/share/aclocal"
)
file(GLOB BINS "${CURRENT_PACKAGES_DIR}/debug/bin/*" "${CURRENT_PACKAGES_DIR}/bin/*")
if(NOT BINS)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/bin"
"${CURRENT_PACKAGES_DIR}/debug/bin"
)
endif()
if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_UWP AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/lib/SDL2main.lib" "${CURRENT_PACKAGES_DIR}/lib/manual-link/SDL2main.lib")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/debug/lib/SDL2maind.lib" "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link/SDL2maind.lib")
endif()
file(GLOB SHARE_FILES "${CURRENT_PACKAGES_DIR}/share/sdl2/*.cmake")
foreach(SHARE_FILE ${SHARE_FILES})
vcpkg_replace_string("${SHARE_FILE}" "lib/SDL2main" "lib/manual-link/SDL2main")
endforeach()
endif()
vcpkg_copy_pdbs()
set(DYLIB_COMPATIBILITY_VERSION_REGEX "set\\(DYLIB_COMPATIBILITY_VERSION (.+)\\)")
set(DYLIB_CURRENT_VERSION_REGEX "set\\(DYLIB_CURRENT_VERSION (.+)\\)")
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_COMPATIBILITY_VERSION REGEX ${DYLIB_COMPATIBILITY_VERSION_REGEX})
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_CURRENT_VERSION REGEX ${DYLIB_CURRENT_VERSION_REGEX})
string(REGEX REPLACE ${DYLIB_COMPATIBILITY_VERSION_REGEX} "\\1" DYLIB_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}")
string(REGEX REPLACE ${DYLIB_CURRENT_VERSION_REGEX} "\\1" DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION}")
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2main" "-lSDL2maind")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2 " "-lSDL2d ")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-static " "-lSDL2-staticd ")
endif()
if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic" AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-lSDL2-static " " ")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-staticd " " ")
endif()
endif()
if(VCPKG_TARGET_IS_UWP)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "d")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
endif()
vcpkg_fixup_pkgconfig()
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt")

View file

@ -0,0 +1,8 @@
sdl2 provides CMake targets:
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(main
PRIVATE
$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>
$<IF:$<TARGET_EXISTS:SDL2::SDL2>,SDL2::SDL2,SDL2::SDL2-static>
)

View file

@ -0,0 +1,58 @@
{
"name": "sdl2",
"version": "2.26.5",
"description": "Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.",
"homepage": "https://www.libsdl.org/download-2.0.php",
"license": "Zlib",
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
],
"default-features": [
"base"
],
"features": {
"base": {
"description": "Base functionality for SDL",
"dependencies": [
{
"name": "sdl2",
"default-features": false,
"features": [
"ibus",
"wayland",
"x11"
],
"platform": "linux"
}
]
},
"ibus": {
"description": "Build with ibus IME support",
"supports": "linux"
},
"samplerate": {
"description": "Use libsamplerate for audio rate conversion",
"dependencies": [
"libsamplerate"
]
},
"vulkan": {
"description": "Vulkan functionality for SDL"
},
"wayland": {
"description": "Build with Wayland support",
"supports": "linux"
},
"x11": {
"description": "Build with X11 support",
"supports": "!windows"
}
}
}

View file

@ -1,71 +0,0 @@
set(VCPKG_LIBRARY_LINKAGE dynamic)
if(VCPKG_TARGET_IS_LINUX)
message("${PORT} currently requires the following tools and libraries from the system package manager:\n autoreconf\n libudev\n\nThese can be installed on Ubuntu systems via apt-get install autoconf libudev-dev")
endif()
set(VERSION 1.0.26)
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO libusb/libusb
REF fcf0c710ef5911ae37fbbf1b39d48a89f6f14e8a # v1.0.26.11791 2023-03-12
SHA512 0aa6439f7988487adf2a3bff473fec80b5c722a47f117a60696d2aa25c87cc3f20fb6aaca7c66e49be25db6a35eb0bb5f71ed7b211d1b8ee064c5d7f1b985c73
HEAD_REF master
)
if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic")
set(LIBUSB_PROJECT_TYPE dll)
else()
set(LIBUSB_PROJECT_TYPE static)
endif()
# The README.md file in the archive is a symlink to README
# which causes issues with the windows MSBUILD process
file(REMOVE "${SOURCE_PATH}/README.md")
vcpkg_msbuild_install(
SOURCE_PATH "${SOURCE_PATH}"
PROJECT_SUBPATH msvc/libusb_${LIBUSB_PROJECT_TYPE}.vcxproj
)
file(INSTALL "${SOURCE_PATH}/libusb/libusb.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include/libusb-1.0")
set(prefix "")
set(exec_prefix [[${prefix}]])
set(libdir [[${prefix}/lib]])
set(includedir [[${prefix}/include]])
configure_file("${SOURCE_PATH}/libusb-1.0.pc.in" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/libusb-1.0.pc" @ONLY)
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/libusb-1.0.pc" " -lusb-1.0" " -llibusb-1.0")
if(NOT VCPKG_BUILD_TYPE)
set(includedir [[${prefix}/../include]])
configure_file("${SOURCE_PATH}/libusb-1.0.pc.in" "${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/libusb-1.0.pc" @ONLY)
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/libusb-1.0.pc" " -lusb-1.0" " -llibusb-1.0")
endif()
else()
vcpkg_list(SET MAKE_OPTIONS)
vcpkg_list(SET LIBUSB_LINK_LIBRARIES)
if(VCPKG_TARGET_IS_EMSCRIPTEN)
vcpkg_list(APPEND MAKE_OPTIONS BUILD_TRIPLET --host=wasm32)
endif()
if("udev" IN_LIST FEATURES)
vcpkg_list(APPEND MAKE_OPTIONS "--enable-udev")
vcpkg_list(APPEND LIBUSB_LINK_LIBRARIES udev)
else()
vcpkg_list(APPEND MAKE_OPTIONS "--disable-udev")
endif()
vcpkg_configure_make(
SOURCE_PATH "${SOURCE_PATH}"
AUTOCONFIG
OPTIONS
${MAKE_OPTIONS}
"--enable-examples-build=no"
"--enable-tests-build=no"
)
vcpkg_install_make()
endif()
vcpkg_fixup_pkgconfig()
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING")

View file

@ -1,5 +0,0 @@
libusb can be imported via CMake FindPkgConfig module:
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
target_link_libraries(main PRIVATE PkgConfig::libusb)

View file

@ -1,8 +0,0 @@
{
"name": "libusb",
"version": "1.0.26.11791",
"port-version": 7,
"description": "a cross-platform library to access USB devices",
"homepage": "https://github.com/libusb/libusb",
"license": "LGPL-2.1-or-later"
}

@ -1 +0,0 @@
Subproject commit 904b8923457f3ec0d6f82ea2d6832a792851194d

View file

@ -10,8 +10,6 @@ curl -sSfL https://github.com"$(curl https://github.com/probonopd/go-appimage/re
chmod a+x mkappimage.AppImage chmod a+x mkappimage.AppImage
curl -sSfLO "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh" curl -sSfLO "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh"
chmod a+x 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 if [[ ! -e /usr/lib/x86_64-linux-gnu ]]; then
sed -i 's#lib\/x86_64-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh sed -i 's#lib\/x86_64-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh
@ -35,14 +33,12 @@ chmod +x AppDir/usr/bin/Cemu
cp /usr/lib/x86_64-linux-gnu/{libsepol.so.1,libffi.so.7,libpcre.so.3,libGLU.so.1,libthai.so.0} AppDir/usr/lib cp /usr/lib/x86_64-linux-gnu/{libsepol.so.1,libffi.so.7,libpcre.so.3,libGLU.so.1,libthai.so.0} AppDir/usr/lib
export UPD_INFO="gh-releases-zsync|cemu-project|Cemu|ci|Cemu.AppImage.zsync" export UPD_INFO="gh-releases-zsync|cemu-project|Cemu|ci|Cemu.AppImage.zsync"
export NO_STRIP=1
./linuxdeploy-x86_64.AppImage --appimage-extract-and-run \ ./linuxdeploy-x86_64.AppImage --appimage-extract-and-run \
--appdir="${GITHUB_WORKSPACE}"/AppDir/ \ --appdir="${GITHUB_WORKSPACE}"/AppDir/ \
-d "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.desktop \ -d "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.desktop \
-i "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.png \ -i "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.png \
-e "${GITHUB_WORKSPACE}"/AppDir/usr/bin/Cemu \ -e "${GITHUB_WORKSPACE}"/AppDir/usr/bin/Cemu \
--plugin gtk \ --plugin gtk
--plugin checkrt
if ! GITVERSION="$(git rev-parse --short HEAD 2>/dev/null)"; then if ! GITVERSION="$(git rev-parse --short HEAD 2>/dev/null)"; then
GITVERSION=experimental GITVERSION=experimental

View file

@ -24,4 +24,3 @@ Comment[it]=Software per emulare giochi e applicazioni per Wii U su PC
Categories=Game;Emulator; Categories=Game;Emulator;
Keywords=Nintendo; Keywords=Nintendo;
MimeType=application/x-wii-u-rom; MimeType=application/x-wii-u-rom;
StartupWMClass=Cemu

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<content>
<networkname>CustomExample</networkname>
<disablesslverification>0</disablesslverification>
<urls>
<act>https://account.nintendo.net</act>
<ecs>https://ecs.wup.shop.nintendo.net/ecs/services/ECommerceSOAP</ecs>
<nus>https://nus.wup.shop.nintendo.net/nus/services/NetUpdateSOAP</nus>
<ias>https://ias.wup.shop.nintendo.net/ias/services/IdentityAuthenticationSOAP</ias>
<ccsu>https://ccs.wup.shop.nintendo.net/ccs/download</ccsu>
<ccs>http://ccs.cdn.wup.shop.nintendo.net/ccs/download</ccs>
<idbe>https://idbe-wup.cdn.nintendo.net/icondata</idbe>
<boss>https://npts.app.nintendo.net/p01/tasksheet</boss>
<tagaya>https://tagaya.wup.shop.nintendo.net/tagaya/versionlist</tagaya>
<olv>https://discovery.olv.nintendo.net/v1/endpoint</olv>
</urls>
</content>

2
generate_vs_solution.bat Normal file
View file

@ -0,0 +1,2 @@
"C:\PROGRAM FILES\MICROSOFT VISUAL STUDIO\2022\COMMUNITY\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe" -B build/
pause

View file

@ -49,18 +49,13 @@ add_subdirectory(audio)
add_subdirectory(util) add_subdirectory(util)
add_subdirectory(imgui) add_subdirectory(imgui)
add_subdirectory(resource) add_subdirectory(resource)
add_subdirectory(asm)
add_executable(CemuBin add_executable(CemuBin
main.cpp main.cpp
mainLLE.cpp mainLLE.cpp
) )
if(MSVC AND MSVC_VERSION EQUAL 1940)
# workaround for an msvc issue on VS 17.10 where generated ILK files are too large
# see https://developercommunity.visualstudio.com/t/After-updating-to-VS-1710-the-size-of-/10665511
set_target_properties(CemuBin PROPERTIES LINK_FLAGS "/INCREMENTAL:NO")
endif()
if(WIN32) if(WIN32)
target_sources(CemuBin PRIVATE target_sources(CemuBin PRIVATE
resource/cemu.rc resource/cemu.rc
@ -81,13 +76,11 @@ if (MACOS_BUNDLE)
set(MACOSX_BUNDLE_ICON_FILE "cemu.icns") set(MACOSX_BUNDLE_ICON_FILE "cemu.icns")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "info.cemu.Cemu") set(MACOSX_BUNDLE_GUI_IDENTIFIER "info.cemu.Cemu")
set(MACOSX_BUNDLE_BUNDLE_NAME "Cemu") set(MACOSX_BUNDLE_BUNDLE_NAME "Cemu")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${CMAKE_PROJECT_VERSION})
set(MACOSX_BUNDLE_BUNDLE_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}") set(MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION})
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2024 Cemu Project") set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2023 Cemu Project")
set(MACOSX_BUNDLE_CATEGORY "public.app-category.games") set(MACOSX_BUNDLE_CATEGORY "public.app-category.games")
set(MACOSX_MINIMUM_SYSTEM_VERSION "12.0")
set(MACOSX_BUNDLE_TYPE_EXTENSION "wua")
set_target_properties(CemuBin PROPERTIES set_target_properties(CemuBin PROPERTIES
MACOSX_BUNDLE true MACOSX_BUNDLE true
@ -100,26 +93,11 @@ 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}") COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory "${CMAKE_SOURCE_DIR}/bin/${folder}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/SharedSupport/${folder}")
endforeach(folder) endforeach(folder)
if(CMAKE_BUILD_TYPE STREQUAL "Debug") add_custom_command (TARGET CemuBin POST_BUILD
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/lib/libusb-1.0.0.dylib") COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib")
else()
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/lib/libusb-1.0.0.dylib")
endif()
if (EXISTS "/usr/local/lib/libMoltenVK.dylib")
set(MOLTENVK_PATH "/usr/local/lib/libMoltenVK.dylib")
elseif (EXISTS "/opt/homebrew/lib/libMoltenVK.dylib")
set(MOLTENVK_PATH "/opt/homebrew/lib/libMoltenVK.dylib")
else()
message(FATAL_ERROR "failed to find libMoltenVK.dylib")
endif ()
add_custom_command (TARGET CemuBin POST_BUILD add_custom_command (TARGET CemuBin POST_BUILD
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${MOLTENVK_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib" COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
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 ${LIBUSB_PATH} @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
endif() endif()
set_target_properties(CemuBin PROPERTIES set_target_properties(CemuBin PROPERTIES

View file

@ -10,7 +10,6 @@ add_library(CemuCafe
Filesystem/fscDeviceRedirect.cpp Filesystem/fscDeviceRedirect.cpp
Filesystem/fscDeviceWua.cpp Filesystem/fscDeviceWua.cpp
Filesystem/fscDeviceWud.cpp Filesystem/fscDeviceWud.cpp
Filesystem/fscDeviceWuhb.cpp
Filesystem/fsc.h Filesystem/fsc.h
Filesystem/FST/FST.cpp Filesystem/FST/FST.cpp
Filesystem/FST/FST.h Filesystem/FST/FST.h
@ -19,9 +18,6 @@ add_library(CemuCafe
Filesystem/FST/KeyCache.h Filesystem/FST/KeyCache.h
Filesystem/WUD/wud.cpp Filesystem/WUD/wud.cpp
Filesystem/WUD/wud.h Filesystem/WUD/wud.h
Filesystem/WUHB/RomFSStructs.h
Filesystem/WUHB/WUHBReader.cpp
Filesystem/WUHB/WUHBReader.h
GamePatch.cpp GamePatch.cpp
GamePatch.h GamePatch.h
GameProfile/GameProfile.cpp GameProfile/GameProfile.cpp
@ -44,7 +40,6 @@ add_library(CemuCafe
HW/Espresso/Debugger/DebugSymbolStorage.h HW/Espresso/Debugger/DebugSymbolStorage.h
HW/Espresso/Debugger/GDBStub.h HW/Espresso/Debugger/GDBStub.h
HW/Espresso/Debugger/GDBStub.cpp HW/Espresso/Debugger/GDBStub.cpp
HW/Espresso/Debugger/GDBBreakpoints.cpp
HW/Espresso/Debugger/GDBBreakpoints.h HW/Espresso/Debugger/GDBBreakpoints.h
HW/Espresso/EspressoISA.h HW/Espresso/EspressoISA.h
HW/Espresso/Interpreter/PPCInterpreterALU.hpp HW/Espresso/Interpreter/PPCInterpreterALU.hpp
@ -67,31 +62,24 @@ add_library(CemuCafe
HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h
HW/Espresso/Recompiler/PPCRecompiler.cpp HW/Espresso/Recompiler/PPCRecompiler.cpp
HW/Espresso/Recompiler/PPCRecompiler.h HW/Espresso/Recompiler/PPCRecompiler.h
HW/Espresso/Recompiler/IML/IML.h HW/Espresso/Recompiler/PPCRecompilerImlAnalyzer.cpp
HW/Espresso/Recompiler/IML/IMLSegment.cpp
HW/Espresso/Recompiler/IML/IMLSegment.h
HW/Espresso/Recompiler/IML/IMLInstruction.cpp
HW/Espresso/Recompiler/IML/IMLInstruction.h
HW/Espresso/Recompiler/IML/IMLDebug.cpp
HW/Espresso/Recompiler/IML/IMLAnalyzer.cpp
HW/Espresso/Recompiler/IML/IMLOptimizer.cpp
HW/Espresso/Recompiler/IML/IMLRegisterAllocator.cpp
HW/Espresso/Recompiler/IML/IMLRegisterAllocator.h
HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.cpp
HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.h
HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp
HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp
HW/Espresso/Recompiler/PPCRecompilerIml.h HW/Espresso/Recompiler/PPCRecompilerIml.h
HW/Espresso/Recompiler/PPCRecompilerImlOptimizer.cpp
HW/Espresso/Recompiler/PPCRecompilerImlRanges.cpp
HW/Espresso/Recompiler/PPCRecompilerImlRanges.h
HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator2.cpp
HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator.cpp
HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp HW/Espresso/Recompiler/PPCRecompilerX64AVX.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp HW/Espresso/Recompiler/PPCRecompilerX64BMI.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64.cpp HW/Espresso/Recompiler/PPCRecompilerX64.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp HW/Espresso/Recompiler/PPCRecompilerX64FPU.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp HW/Espresso/Recompiler/PPCRecompilerX64Gen.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp HW/Espresso/Recompiler/PPCRecompilerX64GenFPU.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64.h HW/Espresso/Recompiler/PPCRecompilerX64.h
HW/Espresso/Recompiler/BackendX64/X64Emit.hpp HW/Espresso/Recompiler/x64Emit.hpp
HW/Espresso/Recompiler/BackendX64/x86Emitter.h
HW/Latte/Common/RegisterSerializer.cpp HW/Latte/Common/RegisterSerializer.cpp
HW/Latte/Common/RegisterSerializer.h HW/Latte/Common/RegisterSerializer.h
HW/Latte/Common/ShaderSerializer.cpp HW/Latte/Common/ShaderSerializer.cpp
@ -225,8 +213,6 @@ add_library(CemuCafe
HW/SI/SI.cpp HW/SI/SI.cpp
HW/SI/si.h HW/SI/si.h
HW/VI/VI.cpp HW/VI/VI.cpp
IOSU/ccr_nfc/iosu_ccr_nfc.cpp
IOSU/ccr_nfc/iosu_ccr_nfc.h
IOSU/fsa/fsa_types.h IOSU/fsa/fsa_types.h
IOSU/fsa/iosu_fsa.cpp IOSU/fsa/iosu_fsa.cpp
IOSU/fsa/iosu_fsa.h IOSU/fsa/iosu_fsa.h
@ -381,24 +367,12 @@ add_library(CemuCafe
OS/libs/gx2/GX2_Texture.h OS/libs/gx2/GX2_Texture.h
OS/libs/gx2/GX2_TilingAperture.cpp OS/libs/gx2/GX2_TilingAperture.cpp
OS/libs/h264_avc/H264Dec.cpp OS/libs/h264_avc/H264Dec.cpp
OS/libs/h264_avc/H264DecBackendAVC.cpp
OS/libs/h264_avc/h264dec.h OS/libs/h264_avc/h264dec.h
OS/libs/h264_avc/H264DecInternal.h
OS/libs/h264_avc/parser OS/libs/h264_avc/parser
OS/libs/h264_avc/parser/H264Parser.cpp OS/libs/h264_avc/parser/H264Parser.cpp
OS/libs/h264_avc/parser/H264Parser.h OS/libs/h264_avc/parser/H264Parser.h
OS/libs/mic/mic.cpp OS/libs/mic/mic.cpp
OS/libs/mic/mic.h OS/libs/mic/mic.h
OS/libs/nfc/ndef.cpp
OS/libs/nfc/ndef.h
OS/libs/nfc/nfc.cpp
OS/libs/nfc/nfc.h
OS/libs/nfc/stream.cpp
OS/libs/nfc/stream.h
OS/libs/nfc/TagV0.cpp
OS/libs/nfc/TagV0.h
OS/libs/nfc/TLV.cpp
OS/libs/nfc/TLV.h
OS/libs/nlibcurl/nlibcurl.cpp OS/libs/nlibcurl/nlibcurl.cpp
OS/libs/nlibcurl/nlibcurlDebug.hpp OS/libs/nlibcurl/nlibcurlDebug.hpp
OS/libs/nlibcurl/nlibcurl.h OS/libs/nlibcurl/nlibcurl.h
@ -429,8 +403,6 @@ add_library(CemuCafe
OS/libs/nn_ndm/nn_ndm.h OS/libs/nn_ndm/nn_ndm.h
OS/libs/nn_spm/nn_spm.cpp OS/libs/nn_spm/nn_spm.cpp
OS/libs/nn_spm/nn_spm.h OS/libs/nn_spm/nn_spm.h
OS/libs/nn_sl/nn_sl.cpp
OS/libs/nn_sl/nn_sl.h
OS/libs/nn_nfp/AmiiboCrypto.h OS/libs/nn_nfp/AmiiboCrypto.h
OS/libs/nn_nfp/nn_nfp.cpp OS/libs/nn_nfp/nn_nfp.cpp
OS/libs/nn_nfp/nn_nfp.h OS/libs/nn_nfp/nn_nfp.h
@ -466,26 +438,14 @@ add_library(CemuCafe
OS/libs/nsyshid/AttachDefaultBackends.cpp OS/libs/nsyshid/AttachDefaultBackends.cpp
OS/libs/nsyshid/Whitelist.cpp OS/libs/nsyshid/Whitelist.cpp
OS/libs/nsyshid/Whitelist.h OS/libs/nsyshid/Whitelist.h
OS/libs/nsyshid/BackendEmulated.cpp
OS/libs/nsyshid/BackendEmulated.h
OS/libs/nsyshid/BackendLibusb.cpp OS/libs/nsyshid/BackendLibusb.cpp
OS/libs/nsyshid/BackendLibusb.h OS/libs/nsyshid/BackendLibusb.h
OS/libs/nsyshid/Dimensions.cpp OS/libs/nsyshid/BackendWindowsHID.cpp
OS/libs/nsyshid/Dimensions.h OS/libs/nsyshid/BackendWindowsHID.h
OS/libs/nsyshid/Infinity.cpp
OS/libs/nsyshid/Infinity.h
OS/libs/nsyshid/Skylander.cpp
OS/libs/nsyshid/Skylander.h
OS/libs/nsyshid/SkylanderXbox360.cpp
OS/libs/nsyshid/SkylanderXbox360.h
OS/libs/nsyshid/g721/g721.cpp
OS/libs/nsyshid/g721/g721.h
OS/libs/nsyskbd/nsyskbd.cpp OS/libs/nsyskbd/nsyskbd.cpp
OS/libs/nsyskbd/nsyskbd.h OS/libs/nsyskbd/nsyskbd.h
OS/libs/nsysnet/nsysnet.cpp OS/libs/nsysnet/nsysnet.cpp
OS/libs/nsysnet/nsysnet.h OS/libs/nsysnet/nsysnet.h
OS/libs/ntag/ntag.cpp
OS/libs/ntag/ntag.h
OS/libs/padscore/padscore.cpp OS/libs/padscore/padscore.cpp
OS/libs/padscore/padscore.h OS/libs/padscore/padscore.h
OS/libs/proc_ui/proc_ui.cpp OS/libs/proc_ui/proc_ui.cpp
@ -537,25 +497,12 @@ if(APPLE)
target_sources(CemuCafe PRIVATE "HW/Latte/Renderer/Vulkan/CocoaSurface.mm") target_sources(CemuCafe PRIVATE "HW/Latte/Renderer/Vulkan/CocoaSurface.mm")
endif() endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
target_sources(CemuCafe PRIVATE
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.h
)
target_link_libraries(CemuCafe PRIVATE xbyak_aarch64)
endif()
set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_include_directories(CemuCafe PUBLIC "../") target_include_directories(CemuCafe PUBLIC "../")
if (glslang_VERSION VERSION_LESS "15.0.0")
set(glslang_target "glslang::SPIRV")
else()
set(glslang_target "glslang::glslang")
endif()
target_link_libraries(CemuCafe PRIVATE target_link_libraries(CemuCafe PRIVATE
CemuAsm
CemuAudio CemuAudio
CemuCommon CemuCommon
CemuComponents CemuComponents
@ -569,7 +516,7 @@ target_link_libraries(CemuCafe PRIVATE
Boost::nowide Boost::nowide
CURL::libcurl CURL::libcurl
fmt::fmt fmt::fmt
${glslang_target} glslang::SPIRV
ih264d ih264d
OpenSSL::Crypto OpenSSL::Crypto
OpenSSL::SSL OpenSSL::SSL
@ -585,16 +532,15 @@ if (ENABLE_WAYLAND)
target_link_libraries(CemuCafe PUBLIC Wayland::Client) target_link_libraries(CemuCafe PUBLIC Wayland::Client)
endif() endif()
if (ENABLE_VCPKG) if (ENABLE_NSYSHID_LIBUSB)
if(WIN32) if (ENABLE_VCPKG)
set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/x64-windows/tools/pkgconf/pkgconf.exe") find_package(libusb CONFIG REQUIRED)
endif() target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS})
find_package(PkgConfig REQUIRED) target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES})
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0) else ()
target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb) find_package(libusb MODULE REQUIRED)
else () target_link_libraries(CemuCafe PRIVATE libusb::libusb)
find_package(libusb MODULE REQUIRED) endif ()
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
endif () endif ()
if (ENABLE_WXWIDGETS) if (ENABLE_WXWIDGETS)

View file

@ -9,7 +9,6 @@
#include "audio/IAudioAPI.h" #include "audio/IAudioAPI.h"
#include "audio/IAudioInputAPI.h" #include "audio/IAudioInputAPI.h"
#include "config/ActiveSettings.h" #include "config/ActiveSettings.h"
#include "config/LaunchSettings.h"
#include "Cafe/TitleList/GameInfo.h" #include "Cafe/TitleList/GameInfo.h"
#include "Cafe/GraphicPack/GraphicPack2.h" #include "Cafe/GraphicPack/GraphicPack2.h"
#include "util/helpers/SystemException.h" #include "util/helpers/SystemException.h"
@ -36,7 +35,6 @@
#include "Cafe/IOSU/legacy/iosu_boss.h" #include "Cafe/IOSU/legacy/iosu_boss.h"
#include "Cafe/IOSU/legacy/iosu_nim.h" #include "Cafe/IOSU/legacy/iosu_nim.h"
#include "Cafe/IOSU/PDM/iosu_pdm.h" #include "Cafe/IOSU/PDM/iosu_pdm.h"
#include "Cafe/IOSU/ccr_nfc/iosu_ccr_nfc.h"
// IOSU initializer functions // IOSU initializer functions
#include "Cafe/IOSU/kernel/iosu_kernel.h" #include "Cafe/IOSU/kernel/iosu_kernel.h"
@ -53,8 +51,6 @@
#include "Cafe/OS/libs/gx2/GX2.h" #include "Cafe/OS/libs/gx2/GX2.h"
#include "Cafe/OS/libs/gx2/GX2_Misc.h" #include "Cafe/OS/libs/gx2/GX2_Misc.h"
#include "Cafe/OS/libs/mic/mic.h" #include "Cafe/OS/libs/mic/mic.h"
#include "Cafe/OS/libs/nfc/nfc.h"
#include "Cafe/OS/libs/ntag/ntag.h"
#include "Cafe/OS/libs/nn_aoc/nn_aoc.h" #include "Cafe/OS/libs/nn_aoc/nn_aoc.h"
#include "Cafe/OS/libs/nn_pdm/nn_pdm.h" #include "Cafe/OS/libs/nn_pdm/nn_pdm.h"
#include "Cafe/OS/libs/nn_cmpt/nn_cmpt.h" #include "Cafe/OS/libs/nn_cmpt/nn_cmpt.h"
@ -255,7 +251,14 @@ void InfoLog_PrintActiveSettings()
if(!GetConfig().vk_accurate_barriers.GetValue()) if(!GetConfig().vk_accurate_barriers.GetValue())
cemuLog_log(LogType::Force, "Accurate barriers are disabled!"); cemuLog_log(LogType::Force, "Accurate barriers are disabled!");
} }
cemuLog_log(LogType::Force, "Console language: {}", stdx::to_underlying(config.console_language.GetValue())); cemuLog_log(LogType::Force, "Console language: {}", config.console_language);
}
void PPCCore_setupSPR(PPCInterpreter_t* hCPU, uint32 coreIndex)
{
hCPU->sprExtended.PVR = 0x70010001;
hCPU->spr.UPIR = coreIndex;
hCPU->sprExtended.msr |= MSR_FP; // enable floating point
} }
struct SharedDataEntry struct SharedDataEntry
@ -397,7 +400,7 @@ void cemu_initForGame()
// replace any known function signatures with our HLE implementations and patch bugs in the games // replace any known function signatures with our HLE implementations and patch bugs in the games
GamePatch_scan(); GamePatch_scan();
} }
LatteGPUState.isDRCPrimary = ActiveSettings::DisplayDRCEnabled(); LatteGPUState.alwaysDisplayDRC = ActiveSettings::DisplayDRCEnabled();
InfoLog_PrintActiveSettings(); InfoLog_PrintActiveSettings();
Latte_Start(); Latte_Start();
// check for debugger entrypoint bp // check for debugger entrypoint bp
@ -530,16 +533,6 @@ namespace CafeSystem
cemuLog_log(LogType::Force, "Platform: {}", platform); cemuLog_log(LogType::Force, "Platform: {}", platform);
} }
static std::vector<IOSUModule*> s_iosuModules =
{
// entries in this list are ordered by initialization order. Shutdown in reverse order
iosu::kernel::GetModule(),
iosu::acp::GetModule(),
iosu::fpd::GetModule(),
iosu::pdm::GetModule(),
iosu::ccr_nfc::GetModule(),
};
// initialize all subsystems which are persistent and don't depend on a game running // initialize all subsystems which are persistent and don't depend on a game running
void Initialize() void Initialize()
{ {
@ -564,20 +557,20 @@ namespace CafeSystem
// allocate memory for all SysAllocators // allocate memory for all SysAllocators
// must happen before COS module init, but also before iosu::kernel::Initialize() // must happen before COS module init, but also before iosu::kernel::Initialize()
SysAllocatorContainer::GetInstance().Initialize(); SysAllocatorContainer::GetInstance().Initialize();
// init IOSU modules // init IOSU
for(auto& module : s_iosuModules)
module->SystemLaunch();
// init IOSU (deprecated manual init)
iosuCrypto_init(); iosuCrypto_init();
iosu::kernel::Initialize();
iosu::fsa::Initialize(); iosu::fsa::Initialize();
iosuIoctl_init(); iosuIoctl_init();
iosuAct_init_depr(); iosuAct_init_depr();
iosu::act::Initialize(); iosu::act::Initialize();
iosu::fpd::Initialize();
iosu::iosuMcp_init(); iosu::iosuMcp_init();
iosu::mcp::Init(); iosu::mcp::Init();
iosu::iosuAcp_init(); iosu::iosuAcp_init();
iosu::boss_init(); iosu::boss_init();
iosu::nim::Initialize(); iosu::nim::Initialize();
iosu::pdm::Initialize();
iosu::odm::Initialize(); iosu::odm::Initialize();
// init Cafe OS // init Cafe OS
avm::Initialize(); avm::Initialize();
@ -592,8 +585,6 @@ namespace CafeSystem
H264::Initialize(); H264::Initialize();
snd_core::Initialize(); snd_core::Initialize();
mic::Initialize(); mic::Initialize();
nfc::Initialize();
ntag::Initialize();
// init hardware register interfaces // init hardware register interfaces
HW_SI::Initialize(); HW_SI::Initialize();
} }
@ -609,14 +600,11 @@ namespace CafeSystem
// if a title is running, shut it down // if a title is running, shut it down
if (sSystemRunning) if (sSystemRunning)
ShutdownTitle(); ShutdownTitle();
// shutdown persistent subsystems (deprecated manual shutdown) // shutdown persistent subsystems
iosu::odm::Shutdown(); iosu::odm::Shutdown();
iosu::act::Stop(); iosu::act::Stop();
iosu::mcp::Shutdown(); iosu::mcp::Shutdown();
iosu::fsa::Shutdown(); iosu::fsa::Shutdown();
// shutdown IOSU modules
for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it)
(*it)->SystemExit();
s_initialized = false; s_initialized = false;
} }
@ -638,40 +626,40 @@ namespace CafeSystem
fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE); fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE);
} }
PREPARE_STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId) STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
{ {
cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId); cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId);
sGameInfo_ForegroundTitle = CafeTitleList::GetGameInfo(titleId); sGameInfo_ForegroundTitle = CafeTitleList::GetGameInfo(titleId);
if (!sGameInfo_ForegroundTitle.IsValid()) if (!sGameInfo_ForegroundTitle.IsValid())
{ {
cemuLog_log(LogType::Force, "Mounting failed: Game meta information is either missing, inaccessible or not valid (missing or invalid .xml files in code and meta folder)"); cemuLog_log(LogType::Force, "Mounting failed: Game meta information is either missing, inaccessible or not valid (missing or invalid .xml files in code and meta folder)");
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
} }
// check base // check base
TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase(); TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase();
if (!titleBase.IsValid()) if (!titleBase.IsValid())
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
if(!titleBase.ParseXmlInfo()) if(!titleBase.ParseXmlInfo())
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
cemuLog_log(LogType::Force, "Base: {}", titleBase.GetPrintPath()); cemuLog_log(LogType::Force, "Base: {}", titleBase.GetPrintPath());
// mount base // mount base
if (!titleBase.Mount("/vol/content", "content", FSC_PRIORITY_BASE) || !titleBase.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_BASE)) if (!titleBase.Mount("/vol/content", "content", FSC_PRIORITY_BASE) || !titleBase.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_BASE))
{ {
cemuLog_log(LogType::Force, "Mounting failed"); cemuLog_log(LogType::Force, "Mounting failed");
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
} }
// check update // check update
TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate(); TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate();
if (titleUpdate.IsValid()) if (titleUpdate.IsValid())
{ {
if (!titleUpdate.ParseXmlInfo()) if (!titleUpdate.ParseXmlInfo())
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
cemuLog_log(LogType::Force, "Update: {}", titleUpdate.GetPrintPath()); cemuLog_log(LogType::Force, "Update: {}", titleUpdate.GetPrintPath());
// mount update // mount update
if (!titleUpdate.Mount("/vol/content", "content", FSC_PRIORITY_PATCH) || !titleUpdate.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_PATCH)) if (!titleUpdate.Mount("/vol/content", "content", FSC_PRIORITY_PATCH) || !titleUpdate.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_PATCH))
{ {
cemuLog_log(LogType::Force, "Mounting failed"); cemuLog_log(LogType::Force, "Mounting failed");
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
} }
} }
else else
@ -683,20 +671,20 @@ namespace CafeSystem
// todo - support for multi-title AOC // todo - support for multi-title AOC
TitleInfo& titleAOC = aocList[0]; TitleInfo& titleAOC = aocList[0];
if (!titleAOC.ParseXmlInfo()) if (!titleAOC.ParseXmlInfo())
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
cemu_assert_debug(titleAOC.IsValid()); cemu_assert_debug(titleAOC.IsValid());
cemuLog_log(LogType::Force, "DLC: {}", titleAOC.GetPrintPath()); cemuLog_log(LogType::Force, "DLC: {}", titleAOC.GetPrintPath());
// mount AOC // mount AOC
if (!titleAOC.Mount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()), "content", FSC_PRIORITY_PATCH)) if (!titleAOC.Mount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()), "content", FSC_PRIORITY_PATCH))
{ {
cemuLog_log(LogType::Force, "Mounting failed"); cemuLog_log(LogType::Force, "Mounting failed");
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
} }
} }
else else
cemuLog_log(LogType::Force, "DLC: Not present"); cemuLog_log(LogType::Force, "DLC: Not present");
sForegroundTitleId = titleId; sForegroundTitleId = titleId;
return PREPARE_STATUS_CODE::SUCCESS; return STATUS_CODE::SUCCESS;
} }
void UnmountForegroundTitle() void UnmountForegroundTitle()
@ -724,7 +712,7 @@ namespace CafeSystem
} }
} }
PREPARE_STATUS_CODE SetupExecutable() STATUS_CODE SetupExecutable()
{ {
// set rpx path from cos.xml if available // set rpx path from cos.xml if available
_pathToBaseExecutable = _pathToExecutable; _pathToBaseExecutable = _pathToExecutable;
@ -756,7 +744,8 @@ namespace CafeSystem
} }
} }
LoadMainExecutable(); LoadMainExecutable();
return PREPARE_STATUS_CODE::SUCCESS; gameProfile_load();
return STATUS_CODE::SUCCESS;
} }
void SetupMemorySpace() void SetupMemorySpace()
@ -770,7 +759,7 @@ namespace CafeSystem
memory_unmapForCurrentTitle(); memory_unmapForCurrentTitle();
} }
PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId) STATUS_CODE PrepareForegroundTitle(TitleId titleId)
{ {
CafeTitleList::WaitForMandatoryScan(); CafeTitleList::WaitForMandatoryScan();
sLaunchModeIsStandalone = false; sLaunchModeIsStandalone = false;
@ -781,21 +770,20 @@ namespace CafeSystem
// mount mlc storage // mount mlc storage
MountBaseDirectories(); MountBaseDirectories();
// mount title folders // mount title folders
PREPARE_STATUS_CODE r = LoadAndMountForegroundTitle(titleId); STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
if (r != PREPARE_STATUS_CODE::SUCCESS) if (r != STATUS_CODE::SUCCESS)
return r; return r;
gameProfile_load();
// setup memory space and PPC recompiler // setup memory space and PPC recompiler
SetupMemorySpace(); SetupMemorySpace();
PPCRecompiler_init(); PPCRecompiler_init();
r = SetupExecutable(); // load RPX r = SetupExecutable(); // load RPX
if (r != PREPARE_STATUS_CODE::SUCCESS) if (r != STATUS_CODE::SUCCESS)
return r; return r;
InitVirtualMlcStorage(); InitVirtualMlcStorage();
return PREPARE_STATUS_CODE::SUCCESS; return STATUS_CODE::SUCCESS;
} }
PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path) STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path)
{ {
sLaunchModeIsStandalone = true; sLaunchModeIsStandalone = true;
cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files"); cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files");
@ -813,7 +801,7 @@ namespace CafeSystem
if (!r) if (!r)
{ {
cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath)); cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath));
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT; return STATUS_CODE::UNABLE_TO_MOUNT;
} }
} }
} }
@ -825,7 +813,7 @@ namespace CafeSystem
// since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash // since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash
auto execData = fsc_extractFile(_pathToExecutable.c_str()); auto execData = fsc_extractFile(_pathToExecutable.c_str());
if (!execData) if (!execData)
return PREPARE_STATUS_CODE::INVALID_RPX; return STATUS_CODE::INVALID_RPX;
uint32 h = generateHashFromRawRPXData(execData->data(), execData->size()); uint32 h = generateHashFromRawRPXData(execData->data(), execData->size());
sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h; sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h;
cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId); cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId);
@ -835,19 +823,19 @@ namespace CafeSystem
// load executable // load executable
SetupExecutable(); SetupExecutable();
InitVirtualMlcStorage(); InitVirtualMlcStorage();
return PREPARE_STATUS_CODE::SUCCESS; return STATUS_CODE::SUCCESS;
} }
void _LaunchTitleThread() void _LaunchTitleThread()
{ {
for(auto& module : s_iosuModules) // init
module->TitleStart();
cemu_initForGame(); cemu_initForGame();
// enter scheduler // enter scheduler
if ((ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler || LaunchSettings::ForceMultiCoreInterpreter()) && !LaunchSettings::ForceInterpreter()) if (ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler)
coreinit::OSSchedulerBegin(3); coreinit::OSSchedulerBegin(3);
else else
coreinit::OSSchedulerBegin(1); coreinit::OSSchedulerBegin(1);
iosu::pdm::StartTrackingTime(GetForegroundTitleId());
} }
void LaunchForegroundTitle() void LaunchForegroundTitle()
@ -922,27 +910,6 @@ namespace CafeSystem
return sGameInfo_ForegroundTitle.GetBase().GetArgStr(); return sGameInfo_ForegroundTitle.GetBase().GetArgStr();
} }
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group)
{
if (sLaunchModeIsStandalone)
return CosCapabilityBits::All;
auto& update = sGameInfo_ForegroundTitle.GetUpdate();
if (update.IsValid())
{
ParsedCosXml* cosXml = update.GetCosInfo();
if (cosXml)
return cosXml->GetCapabilityBits(group);
}
auto& base = sGameInfo_ForegroundTitle.GetBase();
if(base.IsValid())
{
ParsedCosXml* cosXml = base.GetCosInfo();
if (cosXml)
return cosXml->GetCapabilityBits(group);
}
return CosCapabilityBits::All;
}
// when switching titles custom parameters can be passed, returns true if override args are used // when switching titles custom parameters can be passed, returns true if override args are used
bool GetOverrideArgStr(std::vector<std::string>& args) bool GetOverrideArgStr(std::vector<std::string>& args)
{ {
@ -996,8 +963,8 @@ namespace CafeSystem
nn::save::ResetToDefaultState(); nn::save::ResetToDefaultState();
coreinit::__OSDeleteAllActivePPCThreads(); coreinit::__OSDeleteAllActivePPCThreads();
RPLLoader_ResetState(); RPLLoader_ResetState();
for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) // stop time tracking
(*it)->TitleStop(); iosu::pdm::Stop();
// reset Cemu subsystems // reset Cemu subsystems
PPCRecompiler_Shutdown(); PPCRecompiler_Shutdown();
GraphicPack2::Reset(); GraphicPack2::Reset();

View file

@ -4,9 +4,6 @@
#include "Cafe/TitleList/TitleId.h" #include "Cafe/TitleList/TitleId.h"
#include "config/CemuConfig.h" #include "config/CemuConfig.h"
enum class CosCapabilityBits : uint64;
enum class CosCapabilityGroup : uint32;
namespace CafeSystem namespace CafeSystem
{ {
class SystemImplementation class SystemImplementation
@ -15,19 +12,20 @@ namespace CafeSystem
virtual void CafeRecreateCanvas() = 0; virtual void CafeRecreateCanvas() = 0;
}; };
enum class PREPARE_STATUS_CODE enum class STATUS_CODE
{ {
SUCCESS, SUCCESS,
INVALID_RPX, INVALID_RPX,
UNABLE_TO_MOUNT, // failed to mount through TitleInfo (most likely caused by an invalid or outdated path) UNABLE_TO_MOUNT, // failed to mount through TitleInfo (most likely caused by an invalid or outdated path)
//BAD_META_DATA, - the title list only stores titles with valid meta, so this error code is impossible
}; };
void Initialize(); void Initialize();
void SetImplementation(SystemImplementation* impl); void SetImplementation(SystemImplementation* impl);
void Shutdown(); void Shutdown();
PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId); STATUS_CODE PrepareForegroundTitle(TitleId titleId);
PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path); STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
void LaunchForegroundTitle(); void LaunchForegroundTitle();
bool IsTitleRunning(); bool IsTitleRunning();
@ -43,7 +41,6 @@ namespace CafeSystem
std::string GetForegroundTitleName(); std::string GetForegroundTitleName();
std::string GetForegroundTitleArgStr(); std::string GetForegroundTitleArgStr();
uint32 GetForegroundTitleOlvAccesskey(); uint32 GetForegroundTitleOlvAccesskey();
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group);
void ShutdownTitle(); void ShutdownTitle();

View file

@ -3,7 +3,8 @@
#include "Cemu/ncrypto/ncrypto.h" #include "Cemu/ncrypto/ncrypto.h"
#include "Cafe/Filesystem/WUD/wud.h" #include "Cafe/Filesystem/WUD/wud.h"
#include "util/crypto/aes128.h" #include "util/crypto/aes128.h"
#include "openssl/sha.h" /* SHA1 / SHA256 */ #include "openssl/evp.h" /* EVP_Digest */
#include "openssl/sha.h" /* SHA1 / SHA256_DIGEST_LENGTH */
#include "fstUtil.h" #include "fstUtil.h"
#include "FST.h" #include "FST.h"
@ -13,8 +14,6 @@
#define SET_FST_ERROR(__code) if (errorCodeOut) *errorCodeOut = ErrorCode::__code #define SET_FST_ERROR(__code) if (errorCodeOut) *errorCodeOut = ErrorCode::__code
static_assert(sizeof(NCrypto::AesIv) == 16); // make sure IV is actually 16 bytes
class FSTDataSource class FSTDataSource
{ {
public: public:
@ -142,7 +141,7 @@ struct DiscPartitionTableHeader
static constexpr uint32 MAGIC_VALUE = 0xCCA6E67B; static constexpr uint32 MAGIC_VALUE = 0xCCA6E67B;
/* +0x00 */ uint32be magic; /* +0x00 */ uint32be magic;
/* +0x04 */ uint32be blockSize; // must be 0x8000? /* +0x04 */ uint32be sectorSize; // must be 0x8000?
/* +0x08 */ uint8 partitionTableHash[20]; // hash of the data range at +0x800 to end of sector (0x8000) /* +0x08 */ uint8 partitionTableHash[20]; // hash of the data range at +0x800 to end of sector (0x8000)
/* +0x1C */ uint32be numPartitions; /* +0x1C */ uint32be numPartitions;
}; };
@ -165,10 +164,10 @@ struct DiscPartitionHeader
static constexpr uint32 MAGIC_VALUE = 0xCC93A4F5; static constexpr uint32 MAGIC_VALUE = 0xCC93A4F5;
/* +0x00 */ uint32be magic; /* +0x00 */ uint32be magic;
/* +0x04 */ uint32be sectorSize; // must match DISC_SECTOR_SIZE for hashed blocks /* +0x04 */ uint32be sectorSize; // must match DISC_SECTOR_SIZE
/* +0x08 */ uint32be ukn008; /* +0x08 */ uint32be ukn008;
/* +0x0C */ uint32be ukn00C; // h3 array size? /* +0x0C */ uint32be ukn00C;
/* +0x10 */ uint32be h3HashNum; /* +0x10 */ uint32be h3HashNum;
/* +0x14 */ uint32be fstSize; // in bytes /* +0x14 */ uint32be fstSize; // in bytes
/* +0x18 */ uint32be fstSector; // relative to partition start /* +0x18 */ uint32be fstSector; // relative to partition start
@ -179,15 +178,13 @@ struct DiscPartitionHeader
/* +0x24 */ uint8 fstHashType; /* +0x24 */ uint8 fstHashType;
/* +0x25 */ uint8 fstEncryptionType; // purpose of this isn't really understood. Maybe it controls which key is being used? (1 -> disc key, 2 -> partition key) /* +0x25 */ uint8 fstEncryptionType; // purpose of this isn't really understood. Maybe it controls which key is being used? (1 -> disc key, 2 -> partition key)
/* +0x26 */ uint8be versionA; /* +0x26 */ uint8 versionA;
/* +0x27 */ uint8be ukn027; // also a version field? /* +0x27 */ uint8 ukn027; // also a version field?
// there is an array at +0x40 ? Related to H3 list. Also related to value at +0x0C and h3HashNum // there is an array at +0x40 ? Related to H3 list. Also related to value at +0x0C and h3HashNum
/* +0x28 */ uint8be _uknOrPadding028[0x18];
/* +0x40 */ uint8be h3HashArray[32]; // dynamic size. Only present if fstHashType != 0
}; };
static_assert(sizeof(DiscPartitionHeader) == 0x40+0x20); static_assert(sizeof(DiscPartitionHeader) == 0x28);
bool FSTVolume::FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey) bool FSTVolume::FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey)
{ {
@ -272,7 +269,7 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
cemuLog_log(LogType::Force, "Disc image rejected because decryption failed"); cemuLog_log(LogType::Force, "Disc image rejected because decryption failed");
return nullptr; return nullptr;
} }
if (partitionHeader->blockSize != DISC_SECTOR_SIZE) if (partitionHeader->sectorSize != DISC_SECTOR_SIZE)
{ {
cemuLog_log(LogType::Force, "Disc image rejected because partition sector size is invalid"); cemuLog_log(LogType::Force, "Disc image rejected because partition sector size is invalid");
return nullptr; return nullptr;
@ -339,9 +336,6 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
cemu_assert_debug(partitionHeaderSI.fstEncryptionType == 1); cemu_assert_debug(partitionHeaderSI.fstEncryptionType == 1);
// todo - check other fields? // todo - check other fields?
if(partitionHeaderSI.fstHashType == 0 && partitionHeaderSI.h3HashNum != 0)
cemuLog_log(LogType::Force, "FST: Partition uses unhashed blocks but stores a non-zero amount of H3 hashes");
// GM partition // GM partition
DiscPartitionHeader partitionHeaderGM{}; DiscPartitionHeader partitionHeaderGM{};
if (!readPartitionHeader(partitionHeaderGM, gmPartitionIndex)) if (!readPartitionHeader(partitionHeaderGM, gmPartitionIndex))
@ -355,10 +349,9 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
// if decryption is necessary // if decryption is necessary
// load SI FST // load SI FST
dataSource->SetBaseOffset((uint64)partitionArray[siPartitionIndex].partitionAddress * DISC_SECTOR_SIZE); dataSource->SetBaseOffset((uint64)partitionArray[siPartitionIndex].partitionAddress * DISC_SECTOR_SIZE);
auto siFST = OpenFST(dataSource.get(), (uint64)partitionHeaderSI.fstSector * DISC_SECTOR_SIZE, partitionHeaderSI.fstSize, &discTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderSI.fstHashType), nullptr); auto siFST = OpenFST(dataSource.get(), (uint64)partitionHeaderSI.fstSector * DISC_SECTOR_SIZE, partitionHeaderSI.fstSize, &discTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderSI.fstHashType));
if (!siFST) if (!siFST)
return nullptr; return nullptr;
cemu_assert_debug(!(siFST->HashIsDisabled() && partitionHeaderSI.h3HashNum != 0)); // if hash is disabled, no H3 data may be present
// load ticket file for partition that we want to decrypt // load ticket file for partition that we want to decrypt
NCrypto::ETicketParser ticketParser; NCrypto::ETicketParser ticketParser;
std::vector<uint8> ticketData = siFST->ExtractFile(fmt::format("{:02x}/title.tik", gmPartitionIndex)); std::vector<uint8> ticketData = siFST->ExtractFile(fmt::format("{:02x}/title.tik", gmPartitionIndex));
@ -367,32 +360,16 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
cemuLog_log(LogType::Force, "Disc image ticket file is invalid"); cemuLog_log(LogType::Force, "Disc image ticket file is invalid");
return nullptr; return nullptr;
} }
#if 0
// each SI partition seems to contain a title.tmd that we could parse and which should have information about the associated GM partition
// but the console seems to ignore this file for disc images, at least when mounting, so we shouldn't rely on it either
std::vector<uint8> tmdData = siFST->ExtractFile(fmt::format("{:02x}/title.tmd", gmPartitionIndex));
if (tmdData.empty())
{
cemuLog_log(LogType::Force, "Disc image TMD file is missing");
return nullptr;
}
// parse TMD
NCrypto::TMDParser tmdParser;
if (!tmdParser.parse(tmdData.data(), tmdData.size()))
{
cemuLog_log(LogType::Force, "Disc image TMD file is invalid");
return nullptr;
}
#endif
delete siFST; delete siFST;
NCrypto::AesKey gmTitleKey; NCrypto::AesKey gmTitleKey;
ticketParser.GetTitleKey(gmTitleKey); ticketParser.GetTitleKey(gmTitleKey);
// load GM partition // load GM partition
dataSource->SetBaseOffset((uint64)partitionArray[gmPartitionIndex].partitionAddress * DISC_SECTOR_SIZE); dataSource->SetBaseOffset((uint64)partitionArray[gmPartitionIndex].partitionAddress * DISC_SECTOR_SIZE);
FSTVolume* r = OpenFST(std::move(dataSource), (uint64)partitionHeaderGM.fstSector * DISC_SECTOR_SIZE, partitionHeaderGM.fstSize, &gmTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderGM.fstHashType), nullptr); FSTVolume* r = OpenFST(std::move(dataSource), (uint64)partitionHeaderGM.fstSector * DISC_SECTOR_SIZE, partitionHeaderGM.fstSize, &gmTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderGM.fstHashType));
if (r) if (r)
SET_FST_ERROR(OK); SET_FST_ERROR(OK);
cemu_assert_debug(!(r->HashIsDisabled() && partitionHeaderGM.h3HashNum != 0)); // if hash is disabled, no H3 data may be present
return r; return r;
} }
@ -449,15 +426,15 @@ FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath, ErrorCode* erro
} }
// load FST // load FST
// fstSize = size of first cluster? // fstSize = size of first cluster?
FSTVolume* fstVolume = FSTVolume::OpenFST(std::move(dataSource), 0, fstSize, &titleKey, fstHashMode, &tmdParser); FSTVolume* fstVolume = FSTVolume::OpenFST(std::move(dataSource), 0, fstSize, &titleKey, fstHashMode);
if (fstVolume) if (fstVolume)
SET_FST_ERROR(OK); SET_FST_ERROR(OK);
return fstVolume; return fstVolume;
} }
FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD) FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode)
{ {
cemu_assert_debug(fstHashMode != ClusterHashMode::RAW || fstHashMode != ClusterHashMode::RAW_STREAM); cemu_assert_debug(fstHashMode != ClusterHashMode::RAW || fstHashMode != ClusterHashMode::RAW2);
if (fstSize < sizeof(FSTHeader)) if (fstSize < sizeof(FSTHeader))
return nullptr; return nullptr;
constexpr uint64 FST_CLUSTER_OFFSET = 0; constexpr uint64 FST_CLUSTER_OFFSET = 0;
@ -488,34 +465,6 @@ FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint3
clusterTable[i].offset = clusterDataTable[i].offset; clusterTable[i].offset = clusterDataTable[i].offset;
clusterTable[i].size = clusterDataTable[i].size; clusterTable[i].size = clusterDataTable[i].size;
clusterTable[i].hashMode = static_cast<FSTVolume::ClusterHashMode>((uint8)clusterDataTable[i].hashMode); clusterTable[i].hashMode = static_cast<FSTVolume::ClusterHashMode>((uint8)clusterDataTable[i].hashMode);
clusterTable[i].hasContentHash = false; // from the TMD file (H4?)
}
// if the TMD is available (when opening .app files) we can use the extra info from it to validate unhashed clusters
// each content entry in the TMD corresponds to one cluster used by the FST
if(optionalTMD)
{
if(numCluster != optionalTMD->GetContentList().size())
{
cemuLog_log(LogType::Force, "FST: Number of clusters does not match TMD content list");
return nullptr;
}
auto& contentList = optionalTMD->GetContentList();
for(size_t i=0; i<contentList.size(); i++)
{
auto& cluster = clusterTable[i];
auto& content = contentList[i];
cluster.hasContentHash = true;
cluster.contentHashIsSHA1 = HAS_FLAG(contentList[i].contentFlags, NCrypto::TMDParser::TMDContentFlags::FLAG_SHA1);
cluster.contentSize = content.size;
static_assert(sizeof(content.hash32) == sizeof(cluster.contentHash32));
memcpy(cluster.contentHash32, content.hash32, sizeof(cluster.contentHash32));
// if unhashed mode, then initialize the hash context
if(cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW_STREAM)
{
cluster.singleHashCtx.reset(EVP_MD_CTX_new());
EVP_DigestInit_ex(cluster.singleHashCtx.get(), cluster.contentHashIsSHA1 ? EVP_sha1() : EVP_sha256(), nullptr);
}
}
} }
// preprocess FST table // preprocess FST table
FSTHeader_FileEntry* fileTable = (FSTHeader_FileEntry*)(clusterDataTable + numCluster); FSTHeader_FileEntry* fileTable = (FSTHeader_FileEntry*)(clusterDataTable + numCluster);
@ -542,17 +491,16 @@ FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint3
fstVolume->m_offsetFactor = fstHeader->offsetFactor; fstVolume->m_offsetFactor = fstHeader->offsetFactor;
fstVolume->m_sectorSize = DISC_SECTOR_SIZE; fstVolume->m_sectorSize = DISC_SECTOR_SIZE;
fstVolume->m_partitionTitlekey = *partitionTitleKey; fstVolume->m_partitionTitlekey = *partitionTitleKey;
fstVolume->m_hashIsDisabled = fstHeader->hashIsDisabled != 0; std::swap(fstVolume->m_cluster, clusterTable);
fstVolume->m_cluster = std::move(clusterTable); std::swap(fstVolume->m_entries, fstEntries);
fstVolume->m_entries = std::move(fstEntries); std::swap(fstVolume->m_nameStringTable, nameStringTable);
fstVolume->m_nameStringTable = std::move(nameStringTable);
return fstVolume; return fstVolume;
} }
FSTVolume* FSTVolume::OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD) FSTVolume* FSTVolume::OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode)
{ {
FSTDataSource* ds = dataSource.release(); FSTDataSource* ds = dataSource.release();
FSTVolume* fstVolume = OpenFST(ds, fstOffset, fstSize, partitionTitleKey, fstHashMode, optionalTMD); FSTVolume* fstVolume = OpenFST(ds, fstOffset, fstSize, partitionTitleKey, fstHashMode);
if (!fstVolume) if (!fstVolume)
{ {
delete ds; delete ds;
@ -738,25 +686,25 @@ bool FSTVolume::OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bo
return true; return true;
} }
bool FSTVolume::IsDirectory(const FSTFileHandle& fileHandle) const bool FSTVolume::IsDirectory(FSTFileHandle& fileHandle) const
{ {
cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size()); cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size());
return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::DIRECTORY; return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::DIRECTORY;
}; };
bool FSTVolume::IsFile(const FSTFileHandle& fileHandle) const bool FSTVolume::IsFile(FSTFileHandle& fileHandle) const
{ {
cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size()); cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size());
return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::FILE; return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::FILE;
}; };
bool FSTVolume::HasLinkFlag(const FSTFileHandle& fileHandle) const bool FSTVolume::HasLinkFlag(FSTFileHandle& fileHandle) const
{ {
cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size()); cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size());
return HAS_FLAG(m_entries[fileHandle.m_fstIndex].GetFlags(), FSTEntry::FLAGS::FLAG_LINK); return HAS_FLAG(m_entries[fileHandle.m_fstIndex].GetFlags(), FSTEntry::FLAGS::FLAG_LINK);
}; };
std::string_view FSTVolume::GetName(const FSTFileHandle& fileHandle) const std::string_view FSTVolume::GetName(FSTFileHandle& fileHandle) const
{ {
if (fileHandle.m_fstIndex > m_entries.size()) if (fileHandle.m_fstIndex > m_entries.size())
return ""; return "";
@ -764,7 +712,7 @@ std::string_view FSTVolume::GetName(const FSTFileHandle& fileHandle) const
return entryName; return entryName;
} }
std::string FSTVolume::GetPath(const FSTFileHandle& fileHandle) const std::string FSTVolume::GetPath(FSTFileHandle& fileHandle) const
{ {
std::string path; std::string path;
auto& entry = m_entries[fileHandle.m_fstIndex]; auto& entry = m_entries[fileHandle.m_fstIndex];
@ -795,7 +743,7 @@ std::string FSTVolume::GetPath(const FSTFileHandle& fileHandle) const
return path; return path;
} }
uint32 FSTVolume::GetFileSize(const FSTFileHandle& fileHandle) const uint32 FSTVolume::GetFileSize(FSTFileHandle& fileHandle) const
{ {
if (m_entries[fileHandle.m_fstIndex].GetType() != FSTEntry::TYPE::FILE) if (m_entries[fileHandle.m_fstIndex].GetType() != FSTEntry::TYPE::FILE)
return 0; return 0;
@ -809,7 +757,7 @@ uint32 FSTVolume::ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size
return 0; return 0;
cemu_assert_debug(!HAS_FLAG(entry.GetFlags(), FSTEntry::FLAGS::FLAG_LINK)); cemu_assert_debug(!HAS_FLAG(entry.GetFlags(), FSTEntry::FLAGS::FLAG_LINK));
FSTCluster& cluster = m_cluster[entry.fileInfo.clusterIndex]; FSTCluster& cluster = m_cluster[entry.fileInfo.clusterIndex];
if (cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW_STREAM) if (cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW2)
return ReadFile_HashModeRaw(entry.fileInfo.clusterIndex, entry, offset, size, dataOut); return ReadFile_HashModeRaw(entry.fileInfo.clusterIndex, entry, offset, size, dataOut);
else if (cluster.hashMode == ClusterHashMode::HASH_INTERLEAVED) else if (cluster.hashMode == ClusterHashMode::HASH_INTERLEAVED)
return ReadFile_HashModeHashed(entry.fileInfo.clusterIndex, entry, offset, size, dataOut); return ReadFile_HashModeHashed(entry.fileInfo.clusterIndex, entry, offset, size, dataOut);
@ -817,15 +765,87 @@ uint32 FSTVolume::ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size
return 0; return 0;
} }
uint32 FSTVolume::ReadFile_HashModeRaw(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut)
{
const uint32 readSizeInput = readSize;
uint8* dataOutU8 = (uint8*)dataOut;
if (readOffset >= entry.fileInfo.fileSize)
return 0;
else if ((readOffset + readSize) >= entry.fileInfo.fileSize)
readSize = (entry.fileInfo.fileSize - readOffset);
const FSTCluster& cluster = m_cluster[clusterIndex];
uint64 clusterOffset = (uint64)cluster.offset * m_sectorSize;
uint64 absFileOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset;
// make sure the raw range we read is aligned to AES block size (16)
uint64 readAddrStart = absFileOffset & ~0xF;
uint64 readAddrEnd = (absFileOffset + readSize + 0xF) & ~0xF;
bool usesInitialIV = readOffset < 16;
if (!usesInitialIV)
readAddrStart -= 16; // read previous AES block since we require it for the IV
uint32 prePadding = (uint32)(absFileOffset - readAddrStart); // number of extra bytes we read before readOffset (for AES alignment and IV calculation)
uint32 postPadding = (uint32)(readAddrEnd - (absFileOffset + readSize));
uint8 readBuffer[64 * 1024];
// read first chunk
// if file read offset (readOffset) is within the first AES-block then use initial IV calculated from cluster index
// otherwise read previous AES-block is the IV (AES-CBC)
uint64 readAddrCurrent = readAddrStart;
uint32 rawBytesToRead = (uint32)std::min((readAddrEnd - readAddrStart), (uint64)sizeof(readBuffer));
if (m_dataSource->readData(clusterIndex, clusterOffset, readAddrCurrent, readBuffer, rawBytesToRead) != rawBytesToRead)
{
cemuLog_log(LogType::Force, "FST read error in raw content");
return 0;
}
readAddrCurrent += rawBytesToRead;
uint8 iv[16]{};
if (usesInitialIV)
{
// for the first AES block, the IV is initialized from cluster index
iv[0] = (uint8)(clusterIndex >> 8);
iv[1] = (uint8)(clusterIndex >> 0);
AES128_CBC_decrypt_updateIV(readBuffer, readBuffer, rawBytesToRead, m_partitionTitlekey.b, iv);
std::memcpy(dataOutU8, readBuffer + prePadding, rawBytesToRead - prePadding - postPadding);
dataOutU8 += (rawBytesToRead - prePadding - postPadding);
readSize -= (rawBytesToRead - prePadding - postPadding);
}
else
{
// IV is initialized from previous AES block (AES-CBC)
std::memcpy(iv, readBuffer, 16);
AES128_CBC_decrypt_updateIV(readBuffer + 16, readBuffer + 16, rawBytesToRead - 16, m_partitionTitlekey.b, iv);
std::memcpy(dataOutU8, readBuffer + prePadding, rawBytesToRead - prePadding - postPadding);
dataOutU8 += (rawBytesToRead - prePadding - postPadding);
readSize -= (rawBytesToRead - prePadding - postPadding);
}
// read remaining chunks
while (readSize > 0)
{
uint32 bytesToRead = (uint32)std::min((uint32)sizeof(readBuffer), readSize);
uint32 alignedBytesToRead = (bytesToRead + 15) & ~0xF;
if (m_dataSource->readData(clusterIndex, clusterOffset, readAddrCurrent, readBuffer, alignedBytesToRead) != alignedBytesToRead)
{
cemuLog_log(LogType::Force, "FST read error in raw content");
return 0;
}
AES128_CBC_decrypt_updateIV(readBuffer, readBuffer, alignedBytesToRead, m_partitionTitlekey.b, iv);
std::memcpy(dataOutU8, readBuffer, bytesToRead);
dataOutU8 += bytesToRead;
readSize -= bytesToRead;
readAddrCurrent += alignedBytesToRead;
}
return readSizeInput - readSize;
}
constexpr size_t BLOCK_SIZE = 0x10000; constexpr size_t BLOCK_SIZE = 0x10000;
constexpr size_t BLOCK_HASH_SIZE = 0x0400; constexpr size_t BLOCK_HASH_SIZE = 0x0400;
constexpr size_t BLOCK_FILE_SIZE = 0xFC00; constexpr size_t BLOCK_FILE_SIZE = 0xFC00;
struct FSTRawBlock
{
std::vector<uint8> rawData; // unhashed block size depends on sector size field in partition header
};
struct FSTHashedBlock struct FSTHashedBlock
{ {
uint8 rawData[BLOCK_SIZE]; uint8 rawData[BLOCK_SIZE];
@ -867,160 +887,12 @@ struct FSTHashedBlock
static_assert(sizeof(FSTHashedBlock) == BLOCK_SIZE); static_assert(sizeof(FSTHashedBlock) == BLOCK_SIZE);
struct FSTCachedRawBlock
{
FSTRawBlock blockData;
NCrypto::AesIv ivForNextBlock;
uint64 lastAccess;
};
struct FSTCachedHashedBlock struct FSTCachedHashedBlock
{ {
FSTHashedBlock blockData; FSTHashedBlock blockData;
uint64 lastAccess; uint64 lastAccess;
}; };
// Checks cache fill state and if necessary drops least recently accessed block from the cache. Optionally allows to recycle the released cache entry to cut down cost of memory allocation and clearing
void FSTVolume::TrimCacheIfRequired(FSTCachedRawBlock** droppedRawBlock, FSTCachedHashedBlock** droppedHashedBlock)
{
// calculate size used by cache
size_t cacheSize = 0;
for (auto& itr : m_cacheDecryptedRawBlocks)
cacheSize += itr.second->blockData.rawData.size();
for (auto& itr : m_cacheDecryptedHashedBlocks)
cacheSize += sizeof(FSTCachedHashedBlock) + sizeof(FSTHashedBlock);
// only trim if cache is full (larger than 2MB)
if (cacheSize < 2*1024*1024) // 2MB
return;
// scan both cache lists to find least recently accessed block to drop
auto dropRawItr = std::min_element(m_cacheDecryptedRawBlocks.begin(), m_cacheDecryptedRawBlocks.end(), [](const auto& a, const auto& b) -> bool
{ return a.second->lastAccess < b.second->lastAccess; });
auto dropHashedItr = std::min_element(m_cacheDecryptedHashedBlocks.begin(), m_cacheDecryptedHashedBlocks.end(), [](const auto& a, const auto& b) -> bool
{ return a.second->lastAccess < b.second->lastAccess; });
uint64 lastAccess = std::numeric_limits<uint64>::max();
if(dropRawItr != m_cacheDecryptedRawBlocks.end())
lastAccess = dropRawItr->second->lastAccess;
if(dropHashedItr != m_cacheDecryptedHashedBlocks.end())
lastAccess = std::min<uint64>(lastAccess, dropHashedItr->second->lastAccess);
if(dropRawItr != m_cacheDecryptedRawBlocks.end() && dropRawItr->second->lastAccess == lastAccess)
{
if (droppedRawBlock)
*droppedRawBlock = dropRawItr->second;
else
delete dropRawItr->second;
m_cacheDecryptedRawBlocks.erase(dropRawItr);
return;
}
else if(dropHashedItr != m_cacheDecryptedHashedBlocks.end() && dropHashedItr->second->lastAccess == lastAccess)
{
if (droppedHashedBlock)
*droppedHashedBlock = dropHashedItr->second;
else
delete dropHashedItr->second;
m_cacheDecryptedHashedBlocks.erase(dropHashedItr);
}
}
void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, NCrypto::AesIv& ivOut)
{
ivOut = {};
if(blockIndex == 0)
{
ivOut.iv[0] = (uint8)(clusterIndex >> 8);
ivOut.iv[1] = (uint8)(clusterIndex >> 0);
}
else
{
// the last 16 encrypted bytes of the previous block are the IV (AES CBC)
// if the previous block is cached we can grab the IV from there. Otherwise we have to read the 16 bytes from the data source
uint32 prevBlockIndex = blockIndex - 1;
uint64 cacheBlockId = ((uint64)clusterIndex << (64 - 16)) | (uint64)prevBlockIndex;
auto itr = m_cacheDecryptedRawBlocks.find(cacheBlockId);
if (itr != m_cacheDecryptedRawBlocks.end())
{
ivOut = itr->second->ivForNextBlock;
}
else
{
cemu_assert(m_sectorSize >= NCrypto::AesIv::SIZE);
uint64 clusterOffset = (uint64)m_cluster[clusterIndex].offset * m_sectorSize;
NCrypto::AesIv prevIV{};
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize - NCrypto::AesIv::SIZE, prevIV.iv, NCrypto::AesIv::SIZE) != NCrypto::AesIv::SIZE)
{
cemuLog_log(LogType::Force, "Failed to read IV for raw FST block");
m_detectedCorruption = true;
return;
}
ivOut = prevIV;
}
}
}
FSTCachedRawBlock* FSTVolume::GetDecryptedRawBlock(uint32 clusterIndex, uint32 blockIndex)
{
FSTCluster& cluster = m_cluster[clusterIndex];
uint64 clusterOffset = (uint64)cluster.offset * m_sectorSize;
// generate id for cache
uint64 cacheBlockId = ((uint64)clusterIndex << (64 - 16)) | (uint64)blockIndex;
// lookup block in cache
FSTCachedRawBlock* block = nullptr;
auto itr = m_cacheDecryptedRawBlocks.find(cacheBlockId);
if (itr != m_cacheDecryptedRawBlocks.end())
{
block = itr->second;
block->lastAccess = ++m_cacheAccessCounter;
return block;
}
// if cache already full, drop least recently accessed block and recycle FSTCachedRawBlock object if possible
TrimCacheIfRequired(&block, nullptr);
if (!block)
block = new FSTCachedRawBlock();
block->blockData.rawData.resize(m_sectorSize);
// block not cached, read new
block->lastAccess = ++m_cacheAccessCounter;
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize, block->blockData.rawData.data(), m_sectorSize) != m_sectorSize)
{
cemuLog_log(LogType::Force, "Failed to read raw FST block");
delete block;
m_detectedCorruption = true;
return nullptr;
}
// decrypt hash data
NCrypto::AesIv iv{};
DetermineUnhashedBlockIV(clusterIndex, blockIndex, iv);
std::copy(block->blockData.rawData.data() + m_sectorSize - NCrypto::AesIv::SIZE, block->blockData.rawData.data() + m_sectorSize, block->ivForNextBlock.iv);
AES128_CBC_decrypt(block->blockData.rawData.data(), block->blockData.rawData.data(), m_sectorSize, m_partitionTitlekey.b, iv.iv);
// if this is the next block, then hash it
if(cluster.hasContentHash)
{
if(cluster.singleHashNumBlocksHashed == blockIndex)
{
cemu_assert_debug(!(cluster.contentSize % m_sectorSize)); // size should be multiple of sector size? Regardless, the hashing code below can handle non-aligned sizes
bool isLastBlock = blockIndex == (std::max<uint32>(cluster.contentSize / m_sectorSize, 1) - 1);
uint32 hashSize = m_sectorSize;
if(isLastBlock)
hashSize = cluster.contentSize - (uint64)blockIndex*m_sectorSize;
EVP_DigestUpdate(cluster.singleHashCtx.get(), block->blockData.rawData.data(), hashSize);
cluster.singleHashNumBlocksHashed++;
if(isLastBlock)
{
uint8 hash[32];
EVP_DigestFinal_ex(cluster.singleHashCtx.get(), hash, nullptr);
if(memcmp(hash, cluster.contentHash32, cluster.contentHashIsSHA1 ? 20 : 32) != 0)
{
cemuLog_log(LogType::Force, "FST: Raw section hash mismatch");
delete block;
m_detectedCorruption = true;
return nullptr;
}
}
}
}
// register in cache
m_cacheDecryptedRawBlocks.emplace(cacheBlockId, block);
return block;
}
FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex) FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex)
{ {
const FSTCluster& cluster = m_cluster[clusterIndex]; const FSTCluster& cluster = m_cluster[clusterIndex];
@ -1036,17 +908,22 @@ FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, ui
block->lastAccess = ++m_cacheAccessCounter; block->lastAccess = ++m_cacheAccessCounter;
return block; return block;
} }
// if cache already full, drop least recently accessed block and recycle FSTCachedHashedBlock object if possible // if cache already full, drop least recently accessed block (but recycle the FSTHashedBlock* object)
TrimCacheIfRequired(nullptr, &block); if (m_cacheDecryptedHashedBlocks.size() >= 16)
if (!block) {
auto dropItr = std::min_element(m_cacheDecryptedHashedBlocks.begin(), m_cacheDecryptedHashedBlocks.end(), [](const auto& a, const auto& b) -> bool
{ return a.second->lastAccess < b.second->lastAccess; });
block = dropItr->second;
m_cacheDecryptedHashedBlocks.erase(dropItr);
}
else
block = new FSTCachedHashedBlock(); block = new FSTCachedHashedBlock();
// block not cached, read new // block not cached, read new
block->lastAccess = ++m_cacheAccessCounter; block->lastAccess = ++m_cacheAccessCounter;
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * BLOCK_SIZE, block->blockData.rawData, BLOCK_SIZE) != BLOCK_SIZE) if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * BLOCK_SIZE, block->blockData.rawData, BLOCK_SIZE) != BLOCK_SIZE)
{ {
cemuLog_log(LogType::Force, "Failed to read hashed FST block"); cemuLog_log(LogType::Force, "Failed to read FST block");
delete block; delete block;
m_detectedCorruption = true;
return nullptr; return nullptr;
} }
// decrypt hash data // decrypt hash data
@ -1054,46 +931,11 @@ FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, ui
AES128_CBC_decrypt(block->blockData.getHashData(), block->blockData.getHashData(), BLOCK_HASH_SIZE, m_partitionTitlekey.b, iv); AES128_CBC_decrypt(block->blockData.getHashData(), block->blockData.getHashData(), BLOCK_HASH_SIZE, m_partitionTitlekey.b, iv);
// decrypt file data // decrypt file data
AES128_CBC_decrypt(block->blockData.getFileData(), block->blockData.getFileData(), BLOCK_FILE_SIZE, m_partitionTitlekey.b, block->blockData.getH0Hash(blockIndex%16)); AES128_CBC_decrypt(block->blockData.getFileData(), block->blockData.getFileData(), BLOCK_FILE_SIZE, m_partitionTitlekey.b, block->blockData.getH0Hash(blockIndex%16));
// compare with H0 to verify data integrity
NCrypto::CHash160 h0;
SHA1(block->blockData.getFileData(), BLOCK_FILE_SIZE, h0.b);
uint32 h0Index = (blockIndex % 4096);
if (memcmp(h0.b, block->blockData.getH0Hash(h0Index & 0xF), sizeof(h0.b)) != 0)
{
cemuLog_log(LogType::Force, "FST: Hash H0 mismatch in hashed block (section {} index {})", clusterIndex, blockIndex);
delete block;
m_detectedCorruption = true;
return nullptr;
}
// register in cache // register in cache
m_cacheDecryptedHashedBlocks.emplace(cacheBlockId, block); m_cacheDecryptedHashedBlocks.emplace(cacheBlockId, block);
return block; return block;
} }
uint32 FSTVolume::ReadFile_HashModeRaw(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut)
{
uint8* dataOutU8 = (uint8*)dataOut;
if (readOffset >= entry.fileInfo.fileSize)
return 0;
else if ((readOffset + readSize) >= entry.fileInfo.fileSize)
readSize = (entry.fileInfo.fileSize - readOffset);
uint64 absFileOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset;
uint32 remainingReadSize = readSize;
while (remainingReadSize > 0)
{
const FSTCachedRawBlock* rawBlock = this->GetDecryptedRawBlock(clusterIndex, absFileOffset/m_sectorSize);
if (!rawBlock)
break;
uint32 blockOffset = (uint32)(absFileOffset % m_sectorSize);
uint32 bytesToRead = std::min<uint32>(remainingReadSize, m_sectorSize - blockOffset);
std::memcpy(dataOutU8, rawBlock->blockData.rawData.data() + blockOffset, bytesToRead);
dataOutU8 += bytesToRead;
remainingReadSize -= bytesToRead;
absFileOffset += bytesToRead;
}
return readSize - remainingReadSize;
}
uint32 FSTVolume::ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut) uint32 FSTVolume::ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut)
{ {
/* /*
@ -1124,6 +966,7 @@ uint32 FSTVolume::ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry,
*/ */
const FSTCluster& cluster = m_cluster[clusterIndex]; const FSTCluster& cluster = m_cluster[clusterIndex];
uint64 clusterBaseOffset = (uint64)cluster.offset * m_sectorSize;
uint64 fileReadOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset; uint64 fileReadOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset;
uint32 blockIndex = (uint32)(fileReadOffset / BLOCK_FILE_SIZE); uint32 blockIndex = (uint32)(fileReadOffset / BLOCK_FILE_SIZE);
uint32 bytesRemaining = readSize; uint32 bytesRemaining = readSize;
@ -1151,7 +994,6 @@ bool FSTVolume::OpenDirectoryIterator(std::string_view path, FSTDirectoryIterato
if (!IsDirectory(fileHandle)) if (!IsDirectory(fileHandle))
return false; return false;
auto const& fstEntry = m_entries[fileHandle.m_fstIndex]; auto const& fstEntry = m_entries[fileHandle.m_fstIndex];
directoryIteratorOut.dirHandle = fileHandle;
directoryIteratorOut.startIndex = fileHandle.m_fstIndex + 1; directoryIteratorOut.startIndex = fileHandle.m_fstIndex + 1;
directoryIteratorOut.endIndex = fstEntry.dirInfo.endIndex; directoryIteratorOut.endIndex = fstEntry.dirInfo.endIndex;
directoryIteratorOut.currentIndex = directoryIteratorOut.startIndex; directoryIteratorOut.currentIndex = directoryIteratorOut.startIndex;
@ -1176,8 +1018,6 @@ bool FSTVolume::Next(FSTDirectoryIterator& directoryIterator, FSTFileHandle& fil
FSTVolume::~FSTVolume() FSTVolume::~FSTVolume()
{ {
for (auto& itr : m_cacheDecryptedRawBlocks)
delete itr.second;
for (auto& itr : m_cacheDecryptedHashedBlocks) for (auto& itr : m_cacheDecryptedHashedBlocks)
delete itr.second; delete itr.second;
if (m_sourceIsOwned) if (m_sourceIsOwned)

View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "Cemu/ncrypto/ncrypto.h" #include "Cemu/ncrypto/ncrypto.h"
#include "openssl/evp.h"
struct FSTFileHandle struct FSTFileHandle
{ {
@ -12,13 +11,7 @@ private:
struct FSTDirectoryIterator struct FSTDirectoryIterator
{ {
friend class FSTVolume; friend class FSTVolume;
const FSTFileHandle& GetDirHandle() const
{
return dirHandle;
}
private: private:
FSTFileHandle dirHandle;
uint32 startIndex; uint32 startIndex;
uint32 endIndex; uint32 endIndex;
uint32 currentIndex; uint32 currentIndex;
@ -46,20 +39,19 @@ public:
~FSTVolume(); ~FSTVolume();
uint32 GetFileCount() const; uint32 GetFileCount() const;
bool HasCorruption() const { return m_detectedCorruption; }
bool OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bool openOnlyFiles = false); bool OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bool openOnlyFiles = false);
// file and directory functions // file and directory functions
bool IsDirectory(const FSTFileHandle& fileHandle) const; bool IsDirectory(FSTFileHandle& fileHandle) const;
bool IsFile(const FSTFileHandle& fileHandle) const; bool IsFile(FSTFileHandle& fileHandle) const;
bool HasLinkFlag(const FSTFileHandle& fileHandle) const; bool HasLinkFlag(FSTFileHandle& fileHandle) const;
std::string_view GetName(const FSTFileHandle& fileHandle) const; std::string_view GetName(FSTFileHandle& fileHandle) const;
std::string GetPath(const FSTFileHandle& fileHandle) const; std::string GetPath(FSTFileHandle& fileHandle) const;
// file functions // file functions
uint32 GetFileSize(const FSTFileHandle& fileHandle) const; uint32 GetFileSize(FSTFileHandle& fileHandle) const;
uint32 ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size, void* dataOut); uint32 ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size, void* dataOut);
// directory iterator // directory iterator
@ -83,29 +75,20 @@ public:
} }
private: private:
/* FST data (in memory) */ /* FST data (in memory) */
enum class ClusterHashMode : uint8 enum class ClusterHashMode : uint8
{ {
RAW = 0, // raw data + encryption, no hashing? RAW = 0, // raw data + encryption, no hashing?
RAW_STREAM = 1, // raw data + encryption, with hash stored in tmd? RAW2 = 1, // raw data + encryption, with hash stored in tmd?
HASH_INTERLEAVED = 2, // hashes + raw interleaved in 0x10000 blocks (0x400 bytes of hashes at the beginning, followed by 0xFC00 bytes of data) HASH_INTERLEAVED = 2, // hashes + raw interleaved in 0x10000 blocks (0x400 bytes of hashes at the beginning, followed by 0xFC00 bytes of data)
}; };
struct FSTCluster struct FSTCluster
{ {
FSTCluster() : singleHashCtx(nullptr, &EVP_MD_CTX_free) {}
uint32 offset; uint32 offset;
uint32 size; uint32 size;
ClusterHashMode hashMode; ClusterHashMode hashMode;
// extra data if TMD is available
bool hasContentHash;
uint8 contentHash32[32];
bool contentHashIsSHA1; // if true then it's SHA1 (with extra bytes zeroed out), otherwise it's SHA256
uint64 contentSize; // size of the content (in blocks)
// hash context for single hash mode (content hash must be available)
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> singleHashCtx; // unique_ptr to make this move-only
uint32 singleHashNumBlocksHashed{0};
}; };
struct FSTEntry struct FSTEntry
@ -175,30 +158,17 @@ private:
bool m_sourceIsOwned{}; bool m_sourceIsOwned{};
uint32 m_sectorSize{}; // for cluster offsets uint32 m_sectorSize{}; // for cluster offsets
uint32 m_offsetFactor{}; // for file offsets uint32 m_offsetFactor{}; // for file offsets
bool m_hashIsDisabled{}; // disables hash verification (for all clusters of this volume?)
std::vector<FSTCluster> m_cluster; std::vector<FSTCluster> m_cluster;
std::vector<FSTEntry> m_entries; std::vector<FSTEntry> m_entries;
std::vector<char> m_nameStringTable; std::vector<char> m_nameStringTable;
NCrypto::AesKey m_partitionTitlekey; NCrypto::AesKey m_partitionTitlekey;
bool m_detectedCorruption{false};
bool HashIsDisabled() const /* Cache for decrypted hashed blocks */
{
return m_hashIsDisabled;
}
/* Cache for decrypted raw and hashed blocks */
std::unordered_map<uint64, struct FSTCachedRawBlock*> m_cacheDecryptedRawBlocks;
std::unordered_map<uint64, struct FSTCachedHashedBlock*> m_cacheDecryptedHashedBlocks; std::unordered_map<uint64, struct FSTCachedHashedBlock*> m_cacheDecryptedHashedBlocks;
uint64 m_cacheAccessCounter{}; uint64 m_cacheAccessCounter{};
void DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, NCrypto::AesIv& ivOut);
struct FSTCachedRawBlock* GetDecryptedRawBlock(uint32 clusterIndex, uint32 blockIndex);
struct FSTCachedHashedBlock* GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex); struct FSTCachedHashedBlock* GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex);
void TrimCacheIfRequired(struct FSTCachedRawBlock** droppedRawBlock, struct FSTCachedHashedBlock** droppedHashedBlock);
/* File reading */ /* File reading */
uint32 ReadFile_HashModeRaw(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut); uint32 ReadFile_HashModeRaw(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut);
uint32 ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut); uint32 ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut);
@ -209,10 +179,7 @@ private:
/* +0x00 */ uint32be magic; /* +0x00 */ uint32be magic;
/* +0x04 */ uint32be offsetFactor; /* +0x04 */ uint32be offsetFactor;
/* +0x08 */ uint32be numCluster; /* +0x08 */ uint32be numCluster;
/* +0x0C */ uint8be hashIsDisabled; /* +0x0C */ uint32be ukn0C;
/* +0x0D */ uint8be ukn0D;
/* +0x0E */ uint8be ukn0E;
/* +0x0F */ uint8be ukn0F;
/* +0x10 */ uint32be ukn10; /* +0x10 */ uint32be ukn10;
/* +0x14 */ uint32be ukn14; /* +0x14 */ uint32be ukn14;
/* +0x18 */ uint32be ukn18; /* +0x18 */ uint32be ukn18;
@ -289,8 +256,8 @@ private:
static_assert(sizeof(FSTHeader_FileEntry) == 0x10); static_assert(sizeof(FSTHeader_FileEntry) == 0x10);
static FSTVolume* OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD); static FSTVolume* OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode);
static FSTVolume* OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD); static FSTVolume* OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode);
static bool ProcessFST(FSTHeader_FileEntry* fileTable, uint32 numFileEntries, uint32 numCluster, std::vector<char>& nameStringTable, std::vector<FSTEntry>& fstEntries); static bool ProcessFST(FSTHeader_FileEntry* fileTable, uint32 numFileEntries, uint32 numCluster, std::vector<char>& nameStringTable, std::vector<FSTEntry>& fstEntries);
bool MatchFSTEntryName(FSTEntry& entry, std::string_view comparedName) bool MatchFSTEntryName(FSTEntry& entry, std::string_view comparedName)

View file

@ -3,8 +3,6 @@
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include "../fsc.h"
// path parser and utility class for Wii U paths // path parser and utility class for Wii U paths
// optimized to be allocation-free for common path lengths // optimized to be allocation-free for common path lengths
class FSCPath class FSCPath
@ -121,7 +119,9 @@ public:
template<typename F> template<typename F>
class FSAFileTree class FSAFileTree
{ {
private: public:
private:
enum NODETYPE : uint8 enum NODETYPE : uint8
{ {
@ -133,7 +133,6 @@ class FSAFileTree
{ {
std::string name; std::string name;
std::vector<node_t*> subnodes; std::vector<node_t*> subnodes;
size_t fileSize;
F* custom; F* custom;
NODETYPE type; NODETYPE type;
}; };
@ -180,54 +179,13 @@ class FSAFileTree
return newNode; 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: public:
FSAFileTree() FSAFileTree()
{ {
rootNode.type = NODETYPE_DIRECTORY; rootNode.type = NODETYPE_DIRECTORY;
} }
bool addFile(std::string_view path, size_t fileSize, F* custom) bool addFile(std::string_view path, F* custom)
{ {
FSCPath p(path); FSCPath p(path);
if (p.GetNodeCount() == 0) if (p.GetNodeCount() == 0)
@ -238,7 +196,6 @@ public:
return false; // node already exists return false; // node already exists
// add file node // add file node
node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.GetNodeName(p.GetNodeCount() - 1)); node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.GetNodeName(p.GetNodeCount() - 1));
fileNode->fileSize = fileSize;
fileNode->custom = custom; fileNode->custom = custom;
return true; return true;
} }
@ -257,20 +214,6 @@ public:
return true; 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) bool removeFile(std::string_view path)
{ {
FSCPath p(path); FSCPath p(path);

View file

@ -1,40 +0,0 @@
#pragma once
struct romfs_header_t
{
uint32 header_magic;
uint32be header_size;
uint64be dir_hash_table_ofs;
uint64be dir_hash_table_size;
uint64be dir_table_ofs;
uint64be dir_table_size;
uint64be file_hash_table_ofs;
uint64be file_hash_table_size;
uint64be file_table_ofs;
uint64be file_table_size;
uint64be file_partition_ofs;
};
struct romfs_direntry_t
{
uint32be parent;
uint32be listNext; // offset to next directory entry in linked list of parent directory (aka "sibling")
uint32be dirListHead; // offset to first entry in linked list of directory entries (aka "child")
uint32be fileListHead; // offset to first entry in linked list of file entries (aka "file")
uint32be hash;
uint32be name_size;
std::string name;
};
struct romfs_fentry_t
{
uint32be parent;
uint32be listNext; // offset to next file entry in linked list of parent directory (aka "sibling")
uint64be offset;
uint64be size;
uint32be hash;
uint32be name_size;
std::string name;
};
#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF

View file

@ -1,224 +0,0 @@
#include "WUHBReader.h"
WUHBReader* WUHBReader::FromPath(const fs::path& path)
{
FileStream* fileIn{FileStream::openFile2(path)};
if (!fileIn)
return nullptr;
WUHBReader* ret = new WUHBReader(fileIn);
if (!ret->CheckMagicValue())
{
delete ret;
return nullptr;
}
if (!ret->ReadHeader())
{
delete ret;
return nullptr;
}
return ret;
}
static const romfs_direntry_t fallbackDirEntry{
.parent = ROMFS_ENTRY_EMPTY,
.listNext = ROMFS_ENTRY_EMPTY,
.dirListHead = ROMFS_ENTRY_EMPTY,
.fileListHead = ROMFS_ENTRY_EMPTY,
.hash = ROMFS_ENTRY_EMPTY,
.name_size = 0,
.name = ""
};
static const romfs_fentry_t fallbackFileEntry{
.parent = ROMFS_ENTRY_EMPTY,
.listNext = ROMFS_ENTRY_EMPTY,
.offset = 0,
.size = 0,
.hash = ROMFS_ENTRY_EMPTY,
.name_size = 0,
.name = ""
};
template<bool File>
const WUHBReader::EntryType<File>& WUHBReader::GetFallback()
{
if constexpr (File)
return fallbackFileEntry;
else
return fallbackDirEntry;
}
template<bool File>
WUHBReader::EntryType<File> WUHBReader::GetEntry(uint32 offset) const
{
auto fallback = GetFallback<File>();
if(offset == ROMFS_ENTRY_EMPTY)
return fallback;
const char* typeName = File ? "fentry" : "direntry";
EntryType<File> ret;
if (offset >= (File ? m_header.file_table_size : m_header.dir_table_size))
{
cemuLog_log(LogType::Force, "WUHB {} offset exceeds table size declared in header", typeName);
return fallback;
}
// read the entry
m_fileIn->SetPosition((File ? m_header.file_table_ofs : m_header.dir_table_ofs) + offset);
auto read = m_fileIn->readData(&ret, offsetof(EntryType<File>, name));
if (read != offsetof(EntryType<File>, name))
{
cemuLog_log(LogType::Force, "failed to read WUHB {} at offset: {}", typeName, offset);
return fallback;
}
// read the name
ret.name.resize(ret.name_size);
read = m_fileIn->readData(ret.name.data(), ret.name_size);
if (read != ret.name_size)
{
cemuLog_log(LogType::Force, "failed to read WUHB {} name", typeName);
return fallback;
}
return ret;
}
romfs_direntry_t WUHBReader::GetDirEntry(uint32 offset) const
{
return GetEntry<false>(offset);
}
romfs_fentry_t WUHBReader::GetFileEntry(uint32 offset) const
{
return GetEntry<true>(offset);
}
uint64 WUHBReader::GetFileSize(uint32 entryOffset) const
{
return GetFileEntry(entryOffset).size;
}
uint64 WUHBReader::ReadFromFile(uint32 entryOffset, uint64 fileOffset, uint64 length, void* buffer) const
{
const auto fileEntry = GetFileEntry(entryOffset);
if (fileOffset >= fileEntry.size)
return 0;
const uint64 readAmount = std::min(length, fileEntry.size - fileOffset);
const uint64 wuhbOffset = m_header.file_partition_ofs + fileEntry.offset + fileOffset;
m_fileIn->SetPosition(wuhbOffset);
return m_fileIn->readData(buffer, readAmount);
}
uint32 WUHBReader::GetHashTableEntryOffset(uint32 hash, bool isFile) const
{
const uint64 hash_table_size = (isFile ? m_header.file_hash_table_size : m_header.dir_hash_table_size);
const uint64 hash_table_ofs = (isFile ? m_header.file_hash_table_ofs : m_header.dir_hash_table_ofs);
const uint64 hash_table_entry_count = hash_table_size / sizeof(uint32);
const uint64 hash_table_entry_offset = hash_table_ofs + (hash % hash_table_entry_count) * sizeof(uint32);
m_fileIn->SetPosition(hash_table_entry_offset);
uint32 tableOffset;
if (!m_fileIn->readU32(tableOffset))
{
cemuLog_log(LogType::Force, "failed to read WUHB hash table entry at file offset: {}", hash_table_entry_offset);
return ROMFS_ENTRY_EMPTY;
}
return uint32be::from_bevalue(tableOffset);
}
template<bool T>
bool WUHBReader::SearchHashList(uint32& entryOffset, const fs::path& targetName) const
{
for (;;)
{
if (entryOffset == ROMFS_ENTRY_EMPTY)
return false;
auto entry = GetEntry<T>(entryOffset);
if (entry.name == targetName)
return true;
entryOffset = entry.hash;
}
return false;
}
uint32 WUHBReader::Lookup(const std::filesystem::path& path, bool isFile) const
{
uint32 currentEntryOffset = 0;
auto look = [&](const fs::path& part, bool lookInFileHT) {
const auto partString = part.string();
currentEntryOffset = GetHashTableEntryOffset(CalcPathHash(currentEntryOffset, partString.c_str(), 0, partString.size()), lookInFileHT);
if (lookInFileHT)
return SearchHashList<true>(currentEntryOffset, part);
else
return SearchHashList<false>(currentEntryOffset, part);
};
// look for the root entry
if (!look("", false))
return ROMFS_ENTRY_EMPTY;
auto it = path.begin();
while (it != path.end())
{
fs::path part = *it;
++it;
// no need to recurse after trailing forward slash (e.g. directory/)
if (part.empty() && !isFile)
break;
// skip leading forward slash
if (part == "/")
continue;
// if the lookup target is a file and this is the last iteration, look in the file hash table instead.
if (!look(part, it == path.end() && isFile))
return ROMFS_ENTRY_EMPTY;
}
return currentEntryOffset;
}
bool WUHBReader::CheckMagicValue() const
{
uint8 magic[4];
m_fileIn->SetPosition(0);
int read = m_fileIn->readData(magic, 4);
if (read != 4)
{
cemuLog_log(LogType::Force, "Failed to read WUHB magic numbers");
return false;
}
static_assert(sizeof(magic) == s_headerMagicValue.size());
return std::memcmp(&magic, s_headerMagicValue.data(), sizeof(magic)) == 0;
}
bool WUHBReader::ReadHeader()
{
m_fileIn->SetPosition(0);
auto read = m_fileIn->readData(&m_header, sizeof(m_header));
auto readSuccess = read == sizeof(m_header);
if (!readSuccess)
cemuLog_log(LogType::Force, "Failed to read WUHB header");
return readSuccess;
}
unsigned char WUHBReader::NormalizeChar(unsigned char c)
{
if (c >= 'a' && c <= 'z')
{
return c + 'A' - 'a';
}
else
{
return c;
}
}
uint32 WUHBReader::CalcPathHash(uint32 parent, const char* path, uint32 start, size_t path_len)
{
cemu_assert(path != nullptr || path_len == 0);
uint32 hash = parent ^ 123456789;
for (uint32 i = 0; i < path_len; i++)
{
hash = (hash >> 5) | (hash << 27);
hash ^= NormalizeChar(path[start + i]);
}
return hash;
}

View file

@ -1,45 +0,0 @@
#pragma once
#include <Common/FileStream.h>
#include "RomFSStructs.h"
class WUHBReader
{
public:
static WUHBReader* FromPath(const fs::path& path);
romfs_direntry_t GetDirEntry(uint32 offset) const;
romfs_fentry_t GetFileEntry(uint32 offset) const;
uint64 GetFileSize(uint32 entryOffset) const;
uint64 ReadFromFile(uint32 entryOffset, uint64 fileOffset, uint64 length, void* buffer) const;
uint32 Lookup(const std::filesystem::path& path, bool isFile) const;
private:
WUHBReader(FileStream* file)
: m_fileIn(file)
{
cemu_assert_debug(file != nullptr);
};
WUHBReader() = delete;
romfs_header_t m_header;
std::unique_ptr<FileStream> m_fileIn;
constexpr static std::string_view s_headerMagicValue = "WUHB";
bool ReadHeader();
bool CheckMagicValue() const;
static inline unsigned char NormalizeChar(unsigned char c);
static uint32 CalcPathHash(uint32 parent, const char* path, uint32 start, size_t path_len);
template<bool File>
using EntryType = std::conditional_t<File, romfs_fentry_t, romfs_direntry_t>;
template<bool File>
static const EntryType<File>& GetFallback();
template<bool File>
EntryType<File> GetEntry(uint32 offset) const;
template<bool T>
bool SearchHashList(uint32& entryOffset, const fs::path& targetName) const;
uint32 GetHashTableEntryOffset(uint32 hash, bool isFile) const;
};

View file

@ -204,12 +204,9 @@ bool FSCDeviceWUD_Mount(std::string_view mountPath, std::string_view destination
// wua device // wua device
bool FSCDeviceWUA_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class ZArchiveReader* archive, sint32 priority); bool FSCDeviceWUA_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class ZArchiveReader* archive, sint32 priority);
// wuhb device
bool FSCDeviceWUHB_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class WUHBReader* wuhbReader, sint32 priority);
// hostFS device // hostFS device
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority); bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
// redirect device // redirect device
void fscDeviceRedirect_map(); void fscDeviceRedirect_map();
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority); void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority);

View file

@ -11,7 +11,7 @@ struct RedirectEntry
FSAFileTree<RedirectEntry> redirectTree; FSAFileTree<RedirectEntry> redirectTree;
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority) void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority)
{ {
// check if source already has a redirection // check if source already has a redirection
RedirectEntry* existingEntry; RedirectEntry* existingEntry;
@ -24,7 +24,7 @@ void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize,
delete existingEntry; delete existingEntry;
} }
RedirectEntry* entry = new RedirectEntry(targetFilePath, priority); RedirectEntry* entry = new RedirectEntry(targetFilePath, priority);
redirectTree.addFile(virtualSourcePath, fileSize, entry); redirectTree.addFile(virtualSourcePath, entry);
} }
class fscDeviceTypeRedirect : public fscDeviceC class fscDeviceTypeRedirect : public fscDeviceC
@ -32,15 +32,8 @@ class fscDeviceTypeRedirect : public fscDeviceC
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
{ {
RedirectEntry* redirectionEntry; 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); 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; return nullptr;
} }

View file

@ -128,7 +128,7 @@ class fscDeviceWUDC : public fscDeviceC
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE)) if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
{ {
FSTFileHandle fstFileHandle; FSTFileHandle fstFileHandle;
if (mountedVolume->OpenFile(path, fstFileHandle, true) && !mountedVolume->HasLinkFlag(fstFileHandle)) if (mountedVolume->OpenFile(path, fstFileHandle, true))
{ {
*fscStatus = FSC_STATUS_OK; *fscStatus = FSC_STATUS_OK;
return new FSCDeviceWudFileCtx(mountedVolume, fstFileHandle); return new FSCDeviceWudFileCtx(mountedVolume, fstFileHandle);
@ -137,7 +137,7 @@ class fscDeviceWUDC : public fscDeviceC
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR)) if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
{ {
FSTDirectoryIterator dirIterator; FSTDirectoryIterator dirIterator;
if (mountedVolume->OpenDirectoryIterator(path, dirIterator) && !mountedVolume->HasLinkFlag(dirIterator.GetDirHandle())) if (mountedVolume->OpenDirectoryIterator(path, dirIterator))
{ {
*fscStatus = FSC_STATUS_OK; *fscStatus = FSC_STATUS_OK;
return new FSCDeviceWudFileCtx(mountedVolume, dirIterator); return new FSCDeviceWudFileCtx(mountedVolume, dirIterator);

View file

@ -1,151 +0,0 @@
#include "Filesystem/WUHB/WUHBReader.h"
#include "Cafe/Filesystem/fsc.h"
#include "Cafe/Filesystem/FST/FST.h"
class FSCDeviceWuhbFileCtx : public FSCVirtualFile
{
public:
FSCDeviceWuhbFileCtx(WUHBReader* reader, uint32 entryOffset, uint32 fscType)
: m_wuhbReader(reader), m_entryOffset(entryOffset), m_fscType(fscType)
{
cemu_assert(entryOffset != ROMFS_ENTRY_EMPTY);
if (fscType == FSC_TYPE_DIRECTORY)
{
romfs_direntry_t entry = reader->GetDirEntry(entryOffset);
m_dirIterOffset = entry.dirListHead;
m_fileIterOffset = entry.fileListHead;
}
}
sint32 fscGetType() override
{
return m_fscType;
}
uint64 fscQueryValueU64(uint32 id) override
{
if (m_fscType == FSC_TYPE_FILE)
{
if (id == FSC_QUERY_SIZE)
return m_wuhbReader->GetFileSize(m_entryOffset);
else if (id == FSC_QUERY_WRITEABLE)
return 0; // WUHB images are read-only
else
cemu_assert_error();
}
else
{
cemu_assert_unimplemented();
}
return 0;
}
uint32 fscWriteData(void* buffer, uint32 size) override
{
cemu_assert_error();
return 0;
}
uint32 fscReadData(void* buffer, uint32 size) override
{
if (m_fscType != FSC_TYPE_FILE)
return 0;
auto read = m_wuhbReader->ReadFromFile(m_entryOffset, m_seek, size, buffer);
m_seek += read;
return read;
}
void fscSetSeek(uint64 seek) override
{
m_seek = seek;
}
uint64 fscGetSeek() override
{
if (m_fscType != FSC_TYPE_FILE)
return 0;
return m_seek;
}
void fscSetFileLength(uint64 endOffset) override
{
cemu_assert_error();
}
bool fscDirNext(FSCDirEntry* dirEntry) override
{
if (m_dirIterOffset != ROMFS_ENTRY_EMPTY)
{
romfs_direntry_t entry = m_wuhbReader->GetDirEntry(m_dirIterOffset);
m_dirIterOffset = entry.listNext;
if(entry.name_size > 0)
{
dirEntry->isDirectory = true;
dirEntry->isFile = false;
dirEntry->fileSize = 0;
std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH);
return true;
}
}
if (m_fileIterOffset != ROMFS_ENTRY_EMPTY)
{
romfs_fentry_t entry = m_wuhbReader->GetFileEntry(m_fileIterOffset);
m_fileIterOffset = entry.listNext;
if(entry.name_size > 0)
{
dirEntry->isDirectory = false;
dirEntry->isFile = true;
dirEntry->fileSize = entry.size;
std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH);
return true;
}
}
return false;
}
private:
WUHBReader* m_wuhbReader{};
uint32 m_fscType;
uint32 m_entryOffset = ROMFS_ENTRY_EMPTY;
uint32 m_dirIterOffset = ROMFS_ENTRY_EMPTY;
uint32 m_fileIterOffset = ROMFS_ENTRY_EMPTY;
uint64 m_seek = 0;
};
class fscDeviceWUHB : public fscDeviceC
{
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
{
WUHBReader* reader = (WUHBReader*)ctx;
cemu_assert_debug(!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION)); // writing to WUHB is not supported
bool isFile;
uint32 table_offset = ROMFS_ENTRY_EMPTY;
if (table_offset == ROMFS_ENTRY_EMPTY && HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
{
table_offset = reader->Lookup(path, false);
isFile = false;
}
if (table_offset == ROMFS_ENTRY_EMPTY && HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
{
table_offset = reader->Lookup(path, true);
isFile = true;
}
if (table_offset == ROMFS_ENTRY_EMPTY)
{
*fscStatus = FSC_STATUS_FILE_NOT_FOUND;
return nullptr;
}
*fscStatus = FSC_STATUS_OK;
return new FSCDeviceWuhbFileCtx(reader, table_offset, isFile ? FSC_TYPE_FILE : FSC_TYPE_DIRECTORY);
}
// singleton
public:
static fscDeviceWUHB& instance()
{
static fscDeviceWUHB _instance;
return _instance;
}
};
bool FSCDeviceWUHB_Mount(std::string_view mountPath, std::string_view destinationBaseDir, WUHBReader* wuhbReader, sint32 priority)
{
return fsc_mount(mountPath, destinationBaseDir, &fscDeviceWUHB::instance(), wuhbReader, priority) == FSC_STATUS_OK;
}

View file

@ -52,7 +52,7 @@ typedef struct
void hleExport_xcx_enterCriticalSection(PPCInterpreter_t* hCPU) void hleExport_xcx_enterCriticalSection(PPCInterpreter_t* hCPU)
{ {
ppcDefineParamStructPtr(xcxCS, xcxCS_t, 0); ppcDefineParamStructPtr(xcxCS, xcxCS_t, 0);
uint32 threadId = MEMPTR<OSThread_t>(coreinit::OSGetCurrentThread()).GetMPTR(); uint32 threadId = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
cemu_assert_debug(xcxCS->ukn08 != 0); cemu_assert_debug(xcxCS->ukn08 != 0);
cemu_assert_debug(threadId); cemu_assert_debug(threadId);
if (xcxCS->ownerThreadId == (uint32be)threadId) if (xcxCS->ownerThreadId == (uint32be)threadId)

View file

@ -140,7 +140,7 @@ bool gameProfile_loadEnumOption(IniParser& iniParser, const char* optionName, T&
for(const T& v : T()) for(const T& v : T())
{ {
// test integer option // test integer option
if (boost::iequals(fmt::format("{}", fmt::underlying(v)), *option_value)) if (boost::iequals(fmt::format("{}", static_cast<typename std::underlying_type<T>::type>(v)), *option_value))
{ {
option = v; option = v;
return true; return true;
@ -209,7 +209,7 @@ bool GameProfile::Load(uint64_t title_id)
m_gameName = std::string(game_name.begin(), game_name.end()); m_gameName = std::string(game_name.begin(), game_name.end());
trim(m_gameName.value()); trim(m_gameName.value());
} }
IniParser iniParser(*profileContents, _pathToUtf8(gameProfilePath)); IniParser iniParser(*profileContents, gameProfilePath.string());
// parse ini // parse ini
while (iniParser.NextSection()) while (iniParser.NextSection())
{ {

View file

@ -28,7 +28,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
return; return;
std::vector<uint8> rulesData; std::vector<uint8> rulesData;
fs_rules->extract(rulesData); fs_rules->extract(rulesData);
IniParser iniParser(rulesData, _pathToUtf8(rulesPath)); IniParser iniParser(rulesData, rulesPath.string());
if (!iniParser.NextSection()) if (!iniParser.NextSection())
{ {
@ -51,9 +51,10 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
cemuLog_log(LogType::Force, "{}: Unable to parse version", _pathToUtf8(rulesPath)); cemuLog_log(LogType::Force, "{}: Unable to parse version", _pathToUtf8(rulesPath));
return; return;
} }
if (versionNum > GP_LEGACY_VERSION) if (versionNum > GP_LEGACY_VERSION)
{ {
GraphicPack2::LoadGraphicPack(rulesPath, iniParser); GraphicPack2::LoadGraphicPack(_pathToUtf8(rulesPath), iniParser);
return; return;
} }
} }
@ -78,22 +79,22 @@ void GraphicPack2::LoadAll()
} }
} }
bool GraphicPack2::LoadGraphicPack(const fs::path& rulesPath, IniParser& rules) bool GraphicPack2::LoadGraphicPack(const std::string& filename, IniParser& rules)
{ {
try try
{ {
auto gp = std::make_shared<GraphicPack2>(rulesPath, rules); auto gp = std::make_shared<GraphicPack2>(filename, rules);
// check if enabled and preset set // check if enabled and preset set
const auto& config_entries = g_config.data().graphic_pack_entries; const auto& config_entries = g_config.data().graphic_pack_entries;
// legacy absolute path checking for not breaking compatibility // legacy absolute path checking for not breaking compatibility
auto file = gp->GetRulesPath(); auto file = gp->GetFilename2();
auto it = config_entries.find(file.lexically_normal()); auto it = config_entries.find(file.lexically_normal());
if (it == config_entries.cend()) if (it == config_entries.cend())
{ {
// check for relative path // check for relative path
it = config_entries.find(_utf8ToPath(gp->GetNormalizedPathString())); it = config_entries.find(MakeRelativePath(ActiveSettings::GetUserDataPath(), gp->GetFilename2()).lexically_normal());
} }
if (it != config_entries.cend()) if (it != config_entries.cend())
@ -144,7 +145,7 @@ bool GraphicPack2::DeactivateGraphicPack(const std::shared_ptr<GraphicPack2>& gr
const auto it = std::find_if(s_active_graphic_packs.begin(), s_active_graphic_packs.end(), const auto it = std::find_if(s_active_graphic_packs.begin(), s_active_graphic_packs.end(),
[graphic_pack](const GraphicPackPtr& gp) [graphic_pack](const GraphicPackPtr& gp)
{ {
return gp->GetNormalizedPathString() == graphic_pack->GetNormalizedPathString(); return gp->GetFilename() == graphic_pack->GetFilename();
} }
); );
@ -172,12 +173,12 @@ void GraphicPack2::ActivateForCurrentTitle()
{ {
if (gp->GetPresets().empty()) if (gp->GetPresets().empty())
{ {
cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetVirtualPath()); cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetPath());
} }
else else
{ {
std::string logLine; std::string logLine;
logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetVirtualPath())); logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetPath()));
bool isFirst = true; bool isFirst = true;
for (auto& itr : gp->GetPresets()) for (auto& itr : gp->GetPresets())
{ {
@ -248,8 +249,8 @@ std::unordered_map<std::string, GraphicPack2::PresetVar> GraphicPack2::ParsePres
return vars; return vars;
} }
GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
: m_rulesPath(std::move(rulesPath)) : m_filename(std::move(filename))
{ {
// we're already in [Definition] // we're already in [Definition]
auto option_version = rules.FindOption("version"); auto option_version = rules.FindOption("version");
@ -258,7 +259,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
m_version = StringHelpers::ToInt(*option_version, -1); m_version = StringHelpers::ToInt(*option_version, -1);
if (m_version < 0) if (m_version < 0)
{ {
cemuLog_log(LogType::Force, "{}: Invalid version", _pathToUtf8(m_rulesPath)); cemuLog_log(LogType::Force, "{}: Invalid version", m_filename);
throw std::exception(); throw std::exception();
} }
@ -280,10 +281,6 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
m_enabled = m_default_enabled; m_enabled = m_default_enabled;
} }
auto option_allowRendertargetSizeOptimization = rules.FindOption("colorbufferOptimizationAware");
if (option_allowRendertargetSizeOptimization)
m_allowRendertargetSizeOptimization = boost::iequals(*option_allowRendertargetSizeOptimization, "true") || boost::iequals(*option_allowRendertargetSizeOptimization, "1");
auto option_vendorFilter = rules.FindOption("vendorFilter"); auto option_vendorFilter = rules.FindOption("vendorFilter");
if (option_vendorFilter) if (option_vendorFilter)
{ {
@ -308,7 +305,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
cemuLog_log(LogType::Force, "[Definition] section from '{}' graphic pack must contain option: path", gp_name_log.has_value() ? *gp_name_log : "Unknown"); cemuLog_log(LogType::Force, "[Definition] section from '{}' graphic pack must contain option: path", gp_name_log.has_value() ? *gp_name_log : "Unknown");
throw std::exception(); throw std::exception();
} }
m_virtualPath = *option_path; m_path = *option_path;
auto option_gp_name = rules.FindOption("name"); auto option_gp_name = rules.FindOption("name");
if (option_gp_name) if (option_gp_name)
@ -345,7 +342,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
const auto preset_name = rules.FindOption("name"); const auto preset_name = rules.FindOption("name");
if (!preset_name) if (!preset_name)
{ {
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", GetNormalizedPathString(), rules.GetCurrentSectionLineNumber()); cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", m_name, rules.GetCurrentSectionLineNumber());
continue; continue;
} }
@ -369,7 +366,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
} }
catch (const std::exception & ex) catch (const std::exception & ex)
{ {
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Can't parse preset \"{}\": {}", GetNormalizedPathString(), *preset_name, ex.what()); cemuLog_log(LogType::Force, "Graphic pack \"{}\": Can't parse preset \"{}\": {}", m_name, *preset_name, ex.what());
} }
} }
else if (boost::iequals(currentSectionName, "RAM")) else if (boost::iequals(currentSectionName, "RAM"))
@ -383,7 +380,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
{ {
if (m_version <= 5) if (m_version <= 5)
{ {
cemuLog_log(LogType::Force, "Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", GetNormalizedPathString(), optionNameBuf); cemuLog_log(LogType::Force, "Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", m_name, optionNameBuf);
throw std::exception(); throw std::exception();
} }
@ -393,12 +390,12 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
{ {
if (addrEnd <= addrStart) if (addrEnd <= addrStart)
{ {
cemuLog_log(LogType::Force, "Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", GetNormalizedPathString(), addrStart, addrEnd, optionNameBuf); cemuLog_log(LogType::Force, "Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", m_name, addrStart, addrEnd, optionNameBuf);
throw std::exception(); throw std::exception();
} }
else if ((addrStart & 0xFFF) != 0 || (addrEnd & 0xFFF) != 0) else if ((addrStart & 0xFFF) != 0 || (addrEnd & 0xFFF) != 0)
{ {
cemuLog_log(LogType::Force, "Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", GetNormalizedPathString(), optionNameBuf); cemuLog_log(LogType::Force, "Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", m_name, optionNameBuf);
throw std::exception(); throw std::exception();
} }
else else
@ -408,7 +405,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
} }
else else
{ {
cemuLog_log(LogType::Force, "Graphic pack \"{}\": has invalid syntax for option {}", GetNormalizedPathString(), optionNameBuf); cemuLog_log(LogType::Force, "Graphic pack \"{}\": has invalid syntax for option {}", m_name, optionNameBuf);
throw std::exception(); throw std::exception();
} }
} }
@ -422,30 +419,22 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
std::unordered_map<std::string, std::vector<PresetPtr>> tmp_map; std::unordered_map<std::string, std::vector<PresetPtr>> tmp_map;
// all vars must be defined in the default preset vars before // all vars must be defined in the default preset vars before
std::vector<std::pair<std::string, std::string>> mismatchingPresetVars; for (const auto& entry : m_presets)
for (const auto& presetEntry : m_presets)
{ {
tmp_map[presetEntry->category].emplace_back(presetEntry); tmp_map[entry->category].emplace_back(entry);
for (auto& presetVar : presetEntry->variables) for (auto& kv : entry->variables)
{ {
const auto it = m_preset_vars.find(presetVar.first); const auto it = m_preset_vars.find(kv.first);
if (it == m_preset_vars.cend()) if (it == m_preset_vars.cend())
{ {
mismatchingPresetVars.emplace_back(presetEntry->name, presetVar.first); cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains preset variables which are not defined in the default section", m_name);
continue; throw std::exception();
} }
// overwrite var type with default var type
presetVar.second.first = it->second.first;
}
}
if(!mismatchingPresetVars.empty()) // overwrite var type with default var type
{ kv.second.first = it->second.first;
cemuLog_log(LogType::Force, "Graphic pack \"{}\" contains preset variables which are not defined in the [Default] section:", GetNormalizedPathString()); }
for (const auto& [presetName, varName] : mismatchingPresetVars)
cemuLog_log(LogType::Force, "Preset: {} Variable: {}", presetName, varName);
throw std::exception();
} }
// have first entry be default active for every category if no default= is set // have first entry be default active for every category if no default= is set
@ -477,7 +466,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
auto& p2 = kv.second[i + 1]; auto& p2 = kv.second[i + 1];
if (p1->variables.size() != p2->variables.size()) if (p1->variables.size() != p2->variables.size())
{ {
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString()); cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", m_name);
throw std::exception(); throw std::exception();
} }
@ -485,14 +474,14 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
std::set<std::string> keys2(get_keys(p2->variables).begin(), get_keys(p2->variables).end()); std::set<std::string> keys2(get_keys(p2->variables).begin(), get_keys(p2->variables).end());
if (keys1 != keys2) if (keys1 != keys2)
{ {
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString()); cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", m_name);
throw std::exception(); throw std::exception();
} }
if(p1->is_default) if(p1->is_default)
{ {
if(has_default) if(has_default)
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", GetNormalizedPathString(), p1->name); cemuLog_log(LogType::Force, "Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", m_name, p1->name);
p1->active = true; p1->active = true;
has_default = true; has_default = true;
} }
@ -519,11 +508,6 @@ bool GraphicPack2::Reload()
return Activate(); return Activate();
} }
std::string GraphicPack2::GetNormalizedPathString() const
{
return _pathToUtf8(MakeRelativePath(ActiveSettings::GetUserDataPath(), GetRulesPath()).lexically_normal());
}
bool GraphicPack2::ContainsTitleId(uint64_t title_id) const bool GraphicPack2::ContainsTitleId(uint64_t title_id) const
{ {
const auto it = std::find_if(m_title_ids.begin(), m_title_ids.end(), [title_id](uint64 id) { return id == title_id; }); const auto it = std::find_if(m_title_ids.begin(), m_title_ids.end(), [title_id](uint64 id) { return id == title_id; });
@ -666,7 +650,7 @@ bool GraphicPack2::SetActivePreset(std::string_view category, std::string_view n
void GraphicPack2::LoadShaders() void GraphicPack2::LoadShaders()
{ {
fs::path path = GetRulesPath(); fs::path path(m_filename);
for (auto& it : fs::directory_iterator(path.remove_filename())) for (auto& it : fs::directory_iterator(path.remove_filename()))
{ {
if (!is_regular_file(it)) if (!is_regular_file(it))
@ -692,7 +676,7 @@ void GraphicPack2::LoadShaders()
{ {
std::ifstream file(p); std::ifstream file(p);
if (!file.is_open()) if (!file.is_open())
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename()))); throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
file.seekg(0, std::ios::end); file.seekg(0, std::ios::end);
m_output_shader_source.reserve(file.tellg()); m_output_shader_source.reserve(file.tellg());
@ -705,7 +689,7 @@ void GraphicPack2::LoadShaders()
{ {
std::ifstream file(p); std::ifstream file(p);
if (!file.is_open()) if (!file.is_open())
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename()))); throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
file.seekg(0, std::ios::end); file.seekg(0, std::ios::end);
m_upscaling_shader_source.reserve(file.tellg()); m_upscaling_shader_source.reserve(file.tellg());
@ -718,7 +702,7 @@ void GraphicPack2::LoadShaders()
{ {
std::ifstream file(p); std::ifstream file(p);
if (!file.is_open()) if (!file.is_open())
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename()))); throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
file.seekg(0, std::ios::end); file.seekg(0, std::ios::end);
m_downscaling_shader_source.reserve(file.tellg()); m_downscaling_shader_source.reserve(file.tellg());
@ -821,7 +805,7 @@ void GraphicPack2::AddConstantsForCurrentPreset(ExpressionParser& ep)
} }
} }
void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC, const char* virtualMountBase) void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC)
{ {
uint64 currentTitleId = CafeSystem::GetForegroundTitleId(); uint64 currentTitleId = CafeSystem::GetForegroundTitleId();
uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull; uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull;
@ -836,9 +820,9 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC
} }
else else
{ {
virtualMountPath = fs::path(virtualMountBase) / virtualMountPath; virtualMountPath = fs::path("vol/content/") / virtualMountPath;
} }
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.file_size(), it.path().generic_string(), m_fs_priority); fscDeviceRedirect_add(virtualMountPath.generic_string(), it.path().generic_string(), m_fs_priority);
} }
} }
} }
@ -849,7 +833,7 @@ void GraphicPack2::LoadReplacedFiles()
return; return;
m_patchedFilesLoaded = true; m_patchedFilesLoaded = true;
fs::path gfxPackPath = GetRulesPath(); fs::path gfxPackPath = _utf8ToPath(m_filename);
gfxPackPath = gfxPackPath.remove_filename(); gfxPackPath = gfxPackPath.remove_filename();
// /content/ // /content/
@ -859,9 +843,10 @@ void GraphicPack2::LoadReplacedFiles()
std::error_code ec; std::error_code ec;
if (fs::exists(contentPath, ec)) if (fs::exists(contentPath, ec))
{ {
std::wstring internalPath(L"/vol/content/");
// setup redirections // setup redirections
fscDeviceRedirect_map(); fscDeviceRedirect_map();
_iterateReplacedFiles(contentPath, false, "vol/content/"); _iterateReplacedFiles(contentPath, internalPath, false);
} }
// /aoc/ // /aoc/
fs::path aocPath(gfxPackPath); fs::path aocPath(gfxPackPath);
@ -872,20 +857,13 @@ void GraphicPack2::LoadReplacedFiles()
uint64 aocTitleId = CafeSystem::GetForegroundTitleId(); uint64 aocTitleId = CafeSystem::GetForegroundTitleId();
aocTitleId = aocTitleId & 0xFFFFFFFFULL; aocTitleId = aocTitleId & 0xFFFFFFFFULL;
aocTitleId |= 0x0005000c00000000ULL; aocTitleId |= 0x0005000c00000000ULL;
wchar_t internalAocPath[128];
swprintf(internalAocPath, sizeof(internalAocPath)/sizeof(wchar_t), L"/aoc/%016llx/", aocTitleId);
std::wstring internalPath(internalAocPath);
// setup redirections // setup redirections
fscDeviceRedirect_map(); fscDeviceRedirect_map();
_iterateReplacedFiles(aocPath, true, nullptr); _iterateReplacedFiles(aocPath, internalPath, true);
}
// /code/
fs::path codePath(gfxPackPath);
codePath.append("code");
if (fs::exists(codePath, ec))
{
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(codePath, false, CafeSystem::GetInternalVirtualCodeFolder().c_str());
} }
} }
@ -901,18 +879,21 @@ bool GraphicPack2::Activate()
if (m_gfx_vendor.has_value()) if (m_gfx_vendor.has_value())
{ {
auto vendor = g_renderer->GetVendor(); auto vendor = g_renderer->GetVendor();
if (vendor == GfxVendor::IntelLegacy || vendor == GfxVendor::IntelNoLegacy)
vendor = GfxVendor::Intel;
if (m_gfx_vendor.value() != vendor) if (m_gfx_vendor.value() != vendor)
return false; return false;
} }
FileStream* fs_rules = FileStream::openFile2(m_rulesPath); FileStream* fs_rules = FileStream::openFile2(_utf8ToPath(m_filename));
if (!fs_rules) if (!fs_rules)
return false; return false;
std::vector<uint8> rulesData; std::vector<uint8> rulesData;
fs_rules->extract(rulesData); fs_rules->extract(rulesData);
delete fs_rules; delete fs_rules;
IniParser rules({ (char*)rulesData.data(), rulesData.size()}, GetNormalizedPathString()); IniParser rules({ (char*)rulesData.data(), rulesData.size()}, m_filename);
// load rules // load rules
try try
@ -966,7 +947,7 @@ bool GraphicPack2::Activate()
else if (anisotropyValue == 16) else if (anisotropyValue == 16)
rule.overwrite_settings.anistropic_value = 4; rule.overwrite_settings.anistropic_value = 4;
else else
cemuLog_log(LogType::Force, "Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, GetNormalizedPathString()); cemuLog_log(LogType::Force, "Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, m_filename);
} }
m_texture_rules.emplace_back(rule); m_texture_rules.emplace_back(rule);
} }
@ -979,7 +960,7 @@ bool GraphicPack2::Activate()
auto option_upscale = rules.FindOption("upscaleMagFilter"); auto option_upscale = rules.FindOption("upscaleMagFilter");
if(option_upscale && boost::iequals(*option_upscale, "NearestNeighbor")) if(option_upscale && boost::iequals(*option_upscale, "NearestNeighbor"))
m_output_settings.upscale_filter = LatteTextureView::MagFilter::kNearestNeighbor; m_output_settings.upscale_filter = LatteTextureView::MagFilter::kNearestNeighbor;
auto option_downscale = rules.FindOption("downscaleMinFilter"); auto option_downscale = rules.FindOption("NearestNeighbor");
if (option_downscale && boost::iequals(*option_downscale, "NearestNeighbor")) if (option_downscale && boost::iequals(*option_downscale, "NearestNeighbor"))
m_output_settings.downscale_filter = LatteTextureView::MagFilter::kNearestNeighbor; m_output_settings.downscale_filter = LatteTextureView::MagFilter::kNearestNeighbor;
} }
@ -1011,11 +992,11 @@ bool GraphicPack2::Activate()
if (LatteTiming_getCustomVsyncFrequency(globalCustomVsyncFreq)) if (LatteTiming_getCustomVsyncFrequency(globalCustomVsyncFreq))
{ {
if (customVsyncFreq != globalCustomVsyncFreq) if (customVsyncFreq != globalCustomVsyncFreq)
cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetVirtualPath()); cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetPath());
} }
else else
{ {
cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetVirtualPath()); cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetPath());
LatteTiming_setCustomVsyncFrequency(customVsyncFreq); LatteTiming_setCustomVsyncFrequency(customVsyncFreq);
} }
} }

View file

@ -97,23 +97,22 @@ public:
}; };
using PresetPtr = std::shared_ptr<Preset>; using PresetPtr = std::shared_ptr<Preset>;
GraphicPack2(fs::path rulesPath, IniParser& rules); GraphicPack2(std::string filename, IniParser& rules);
bool IsEnabled() const { return m_enabled; } bool IsEnabled() const { return m_enabled; }
bool IsActivated() const { return m_activated; } bool IsActivated() const { return m_activated; }
sint32 GetVersion() const { return m_version; } sint32 GetVersion() const { return m_version; }
const fs::path GetRulesPath() const { return m_rulesPath; } const std::string& GetFilename() const { return m_filename; }
std::string GetNormalizedPathString() const; const fs::path GetFilename2() const { return fs::path(m_filename); }
bool RequiresRestart(bool changeEnableState, bool changePreset); bool RequiresRestart(bool changeEnableState, bool changePreset);
bool Reload(); bool Reload();
bool HasName() const { return !m_name.empty(); } bool HasName() const { return !m_name.empty(); }
const std::string& GetName() const { return m_name.empty() ? m_virtualPath : m_name; } const std::string& GetName() const { return m_name.empty() ? m_path : m_name; }
const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy const std::string& GetPath() const { return m_path; }
const std::string& GetDescription() const { return m_description; } const std::string& GetDescription() const { return m_description; }
bool IsDefaultEnabled() const { return m_default_enabled; } bool IsDefaultEnabled() const { return m_default_enabled; }
bool AllowRendertargetSizeOptimization() const { return m_allowRendertargetSizeOptimization; }
void SetEnabled(bool state) { m_enabled = state; } void SetEnabled(bool state) { m_enabled = state; }
@ -165,7 +164,7 @@ public:
static const std::vector<std::shared_ptr<GraphicPack2>>& GetGraphicPacks() { return s_graphic_packs; } static const std::vector<std::shared_ptr<GraphicPack2>>& GetGraphicPacks() { return s_graphic_packs; }
static const std::vector<std::shared_ptr<GraphicPack2>>& GetActiveGraphicPacks() { return s_active_graphic_packs; } static const std::vector<std::shared_ptr<GraphicPack2>>& GetActiveGraphicPacks() { return s_active_graphic_packs; }
static void LoadGraphicPack(fs::path graphicPackPath); static void LoadGraphicPack(fs::path graphicPackPath);
static bool LoadGraphicPack(const fs::path& rulesPath, class IniParser& rules); static bool LoadGraphicPack(const std::string& filename, class IniParser& rules);
static bool ActivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack); static bool ActivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack);
static bool DeactivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack); static bool DeactivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack);
static void ClearGraphicPacks(); static void ClearGraphicPacks();
@ -209,17 +208,15 @@ private:
parser.TryAddConstant(var.first, (TType)var.second.second); parser.TryAddConstant(var.first, (TType)var.second.second);
} }
fs::path m_rulesPath; std::string m_filename;
sint32 m_version; sint32 m_version;
std::string m_name; std::string m_name;
std::string m_virtualPath; std::string m_path;
std::string m_description; std::string m_description;
bool m_default_enabled = false; bool m_default_enabled = false;
bool m_allowRendertargetSizeOptimization = false; // gfx pack supports framebuffers with non-padded sizes, which is an optional optimization introduced with Cemu 2.0-74
// filter // filter
std::optional<RendererAPI> m_renderer_api; std::optional<RendererAPI> m_renderer_api;
std::optional<GfxVendor> m_gfx_vendor; std::optional<GfxVendor> m_gfx_vendor;
@ -260,7 +257,7 @@ private:
CustomShader LoadShader(const fs::path& path, uint64 shader_base_hash, uint64 shader_aux_hash, GP_SHADER_TYPE shader_type) const; CustomShader LoadShader(const fs::path& path, uint64 shader_base_hash, uint64 shader_aux_hash, GP_SHADER_TYPE shader_type) const;
void ApplyShaderPresets(std::string& shader_source) const; void ApplyShaderPresets(std::string& shader_source) const;
void LoadReplacedFiles(); void LoadReplacedFiles();
void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC, const char* virtualMountBase); void _iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC);
// ram mappings // ram mappings
std::vector<std::pair<MPTR, MPTR>> m_ramMappings; std::vector<std::pair<MPTR, MPTR>> m_ramMappings;

View file

@ -71,8 +71,19 @@ void PatchErrorHandler::showStageErrorMessageBox()
// returns true if at least one file was found even if it could not be successfully parsed // returns true if at least one file was found even if it could not be successfully parsed
bool GraphicPack2::LoadCemuPatches() bool GraphicPack2::LoadCemuPatches()
{ {
// todo - once we have updated to C++20 we can replace these with the new std::string functions
auto startsWith = [](const std::wstring& str, const std::wstring& prefix)
{
return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix);
};
auto endsWith = [](const std::wstring& str, const std::wstring& suffix)
{
return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
};
bool foundPatches = false; bool foundPatches = false;
fs::path path(m_rulesPath); fs::path path(_utf8ToPath(m_filename));
path.remove_filename(); path.remove_filename();
for (auto& p : fs::directory_iterator(path)) for (auto& p : fs::directory_iterator(path))
{ {
@ -118,7 +129,7 @@ void GraphicPack2::LoadPatchFiles()
if (LoadCemuPatches()) if (LoadCemuPatches())
return; // exit if at least one Cemu style patch file was found return; // exit if at least one Cemu style patch file was found
// fall back to Cemuhook patches.txt to guarantee backward compatibility // fall back to Cemuhook patches.txt to guarantee backward compatibility
fs::path path(m_rulesPath); fs::path path(_utf8ToPath(m_filename));
path.remove_filename(); path.remove_filename();
path.append("patches.txt"); path.append("patches.txt");
FileStream* patchFile = FileStream::openFile2(path); FileStream* patchFile = FileStream::openFile2(path);

View file

@ -25,7 +25,7 @@ sint32 GraphicPack2::GetLengthWithoutComment(const char* str, size_t length)
void GraphicPack2::LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg) void GraphicPack2::LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg)
{ {
cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", _pathToUtf8(this->GetRulesPath())); cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", this->GetFilename());
if(lineNumber >= 0) if(lineNumber >= 0)
cemuLog_log(LogType::Force, fmt::format("Line {0}: {1}", lineNumber, errorMsg)); cemuLog_log(LogType::Force, fmt::format("Line {0}: {1}", lineNumber, errorMsg));
else else

View file

@ -45,17 +45,12 @@ public:
static void ClearRange(MPTR address, uint32 length) static void ClearRange(MPTR address, uint32 length)
{ {
if (length == 0)
return;
s_lock.lock(); s_lock.lock();
for (;;) while (length > 0)
{ {
auto itr = s_typeStorage.find(address); auto itr = s_typeStorage.find(address);
if (itr != s_typeStorage.end()) if (itr != s_typeStorage.end())
s_typeStorage.erase(itr); s_typeStorage.erase(itr);
if (length <= 4)
break;
address += 4; address += 4;
length -= 4; length -= 4;
} }

View file

@ -8,7 +8,6 @@
#include "gui/debugger/DebuggerWindow2.h" #include "gui/debugger/DebuggerWindow2.h"
#include "Cafe/OS/libs/coreinit/coreinit.h" #include "Cafe/OS/libs/coreinit/coreinit.h"
#include "util/helpers/helpers.h"
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
#include <Windows.h> #include <Windows.h>
@ -137,6 +136,11 @@ void debugger_createCodeBreakpoint(uint32 address, uint8 bpType)
debugger_updateExecutionBreakpoint(address); debugger_updateExecutionBreakpoint(address);
} }
void debugger_createExecuteBreakpoint(uint32 address)
{
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
}
namespace coreinit namespace coreinit
{ {
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads(); std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
@ -206,8 +210,7 @@ void debugger_handleSingleStepException(uint64 dr6)
} }
if (catchBP) if (catchBP)
{ {
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); debugger_createCodeBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT);
debugger_createCodeBreakpoint(hCPU->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT);
} }
} }
@ -290,23 +293,8 @@ void debugger_toggleExecuteBreakpoint(uint32 address)
} }
else else
{ {
// create new execution breakpoint // create new breakpoint
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); debugger_createExecuteBreakpoint(address);
}
}
void debugger_toggleLoggingBreakpoint(uint32 address)
{
auto existingBP = debugger_getFirstBP(address, DEBUGGER_BP_T_LOGGING);
if (existingBP)
{
// delete existing breakpoint
debugger_deleteBreakpoint(existingBP);
}
else
{
// create new logging breakpoint
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_LOGGING);
} }
} }
@ -458,34 +446,6 @@ bool debugger_hasPatch(uint32 address)
return false; return false;
} }
void debugger_removePatch(uint32 address)
{
for (sint32 i = 0; i < debuggerState.patches.size(); i++)
{
auto& patch = debuggerState.patches[i];
if (address < patch->address || address >= (patch->address + patch->length))
continue;
MPTR startAddress = patch->address;
MPTR endAddress = patch->address + patch->length;
// remove any breakpoints overlapping with the patch
for (auto& bp : debuggerState.breakpoints)
{
if (bp->address + 4 > startAddress && bp->address < endAddress)
{
bp->enabled = false;
debugger_updateExecutionBreakpoint(bp->address);
}
}
// restore original data
memcpy(MEMPTR<void>(startAddress).GetPtr(), patch->origData.data(), patch->length);
PPCRecompiler_invalidateRange(startAddress, endAddress);
// remove patch
delete patch;
debuggerState.patches.erase(debuggerState.patches.begin() + i);
return;
}
}
void debugger_stepInto(PPCInterpreter_t* hCPU, bool updateDebuggerWindow = true) void debugger_stepInto(PPCInterpreter_t* hCPU, bool updateDebuggerWindow = true)
{ {
bool isRecEnabled = ppcRecompilerEnabled; bool isRecEnabled = ppcRecompilerEnabled;
@ -540,6 +500,8 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU)
debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i]; debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i];
} }
void DebugLogStackTrace(OSThread_t* thread, MPTR sp);
void debugger_enterTW(PPCInterpreter_t* hCPU) void debugger_enterTW(PPCInterpreter_t* hCPU)
{ {
// handle logging points // handle logging points
@ -549,52 +511,11 @@ void debugger_enterTW(PPCInterpreter_t* hCPU)
{ {
if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled) if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled)
{ {
std::string comment = !bp->comment.empty() ? boost::nowide::narrow(bp->comment) : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address); std::string logName = !bp->comment.empty() ? "Breakpoint '"+boost::nowide::narrow(bp->comment)+"'" : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address);
std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", coreinitThread_getCurrentThreadMPTRDepr(hCPU), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : "");
auto replacePlaceholders = [&](const std::string& prefix, const auto& formatFunc)
{
size_t pos = 0;
while ((pos = comment.find(prefix, pos)) != std::string::npos)
{
size_t endPos = comment.find('}', pos);
if (endPos == std::string::npos)
break;
try
{
if (int regNum = ConvertString<int>(comment.substr(pos + prefix.length(), endPos - pos - prefix.length())); regNum >= 0 && regNum < 32)
{
std::string replacement = formatFunc(regNum);
comment.replace(pos, endPos - pos + 1, replacement);
pos += replacement.length();
}
else
{
pos = endPos + 1;
}
}
catch (...)
{
pos = endPos + 1;
}
}
};
// Replace integer register placeholders {rX}
replacePlaceholders("{r", [&](int regNum) {
return fmt::format("0x{:08X}", hCPU->gpr[regNum]);
});
// Replace floating point register placeholders {fX}
replacePlaceholders("{f", [&](int regNum) {
return fmt::format("{}", hCPU->fpr[regNum].fpr);
});
std::string logName = "Breakpoint '" + comment + "'";
std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", MEMPTR<OSThread_t>(coreinit::OSGetCurrentThread()).GetMPTR(), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : "");
cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext); cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext);
if (cemuLog_advancedPPCLoggingEnabled()) if (cemuLog_advancedPPCLoggingEnabled())
DebugLogStackTrace(coreinit::OSGetCurrentThread(), hCPU->gpr[1]); DebugLogStackTrace(coreinitThread_getCurrentThreadDepr(hCPU), hCPU->gpr[1]);
break; break;
} }
bp = bp->next; bp = bp->next;
@ -613,7 +534,7 @@ void debugger_enterTW(PPCInterpreter_t* hCPU)
// handle breakpoints // handle breakpoints
debuggerState.debugSession.isTrapped = true; debuggerState.debugSession.isTrapped = true;
debuggerState.debugSession.debuggedThreadMPTR = MEMPTR<OSThread_t>(coreinit::OSGetCurrentThread()).GetMPTR(); debuggerState.debugSession.debuggedThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; debuggerState.debugSession.instructionPointer = hCPU->instructionPointer;
debuggerState.debugSession.hCPU = hCPU; debuggerState.debugSession.hCPU = hCPU;
debugger_createPPCStateSnapshot(hCPU); debugger_createPPCStateSnapshot(hCPU);
@ -627,7 +548,7 @@ void debugger_enterTW(PPCInterpreter_t* hCPU)
debuggerState.debugSession.stepInto = false; debuggerState.debugSession.stepInto = false;
debuggerState.debugSession.stepOver = false; debuggerState.debugSession.stepOver = false;
debuggerState.debugSession.run = false; debuggerState.debugSession.run = false;
while (debuggerState.debugSession.isTrapped) while (true)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
// check for step commands // check for step commands

View file

@ -100,8 +100,8 @@ extern debuggerState_t debuggerState;
// new API // new API
DebuggerBreakpoint* debugger_getFirstBP(uint32 address); DebuggerBreakpoint* debugger_getFirstBP(uint32 address);
void debugger_createCodeBreakpoint(uint32 address, uint8 bpType); void debugger_createCodeBreakpoint(uint32 address, uint8 bpType);
void debugger_createExecuteBreakpoint(uint32 address);
void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint
void debugger_toggleLoggingBreakpoint(uint32 address); // create/remove logging breakpoint
void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp); void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp);
void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite); void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite);
@ -114,7 +114,6 @@ void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore = fals
void debugger_createPatch(uint32 address, std::span<uint8> patchData); void debugger_createPatch(uint32 address, std::span<uint8> patchData);
bool debugger_hasPatch(uint32 address); bool debugger_hasPatch(uint32 address);
void debugger_removePatch(uint32 address);
void debugger_forceBreak(); // force breakpoint at the next possible instruction void debugger_forceBreak(); // force breakpoint at the next possible instruction
bool debugger_isTrapped(); bool debugger_isTrapped();

View file

@ -1,304 +0,0 @@
#include "GDBBreakpoints.h"
#include "Debugger.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#if defined(ARCH_X86_64) && BOOST_OS_LINUX
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
DRType _GetDR(pid_t tid, int drIndex)
{
size_t drOffset = offsetof(struct user, u_debugreg) + drIndex * sizeof(user::u_debugreg[0]);
long v;
v = ptrace(PTRACE_PEEKUSER, tid, drOffset, nullptr);
if (v == -1)
perror("ptrace(PTRACE_PEEKUSER)");
return (DRType)v;
}
void _SetDR(pid_t tid, int drIndex, DRType newValue)
{
size_t drOffset = offsetof(struct user, u_debugreg) + drIndex * sizeof(user::u_debugreg[0]);
long rc = ptrace(PTRACE_POKEUSER, tid, drOffset, newValue);
if (rc == -1)
perror("ptrace(PTRACE_POKEUSER)");
}
DRType _ReadDR6()
{
pid_t tid = gettid();
// linux doesn't let us attach to the current thread / threads in the current thread group
// we have to create a child process which then modifies the debug registers and quits
pid_t child = fork();
if (child == -1)
{
perror("fork");
return 0;
}
if (child == 0)
{
if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr))
{
perror("attach");
_exit(0);
}
waitpid(tid, NULL, 0);
uint64_t dr6 = _GetDR(tid, 6);
if (ptrace(PTRACE_DETACH, tid, nullptr, nullptr))
perror("detach");
// since the status code only uses the lower 8 bits, we have to discard the rest of DR6
// this should be fine though, since the lower 4 bits of DR6 contain all the bp conditions
_exit(dr6 & 0xff);
}
// wait for child process
int wstatus;
waitpid(child, &wstatus, 0);
return (DRType)WEXITSTATUS(wstatus);
}
#endif
GDBServer::ExecutionBreakpoint::ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason)
: m_address(address), m_removedAfterInterrupt(false), m_reason(std::move(reason))
{
if (type == BreakpointType::BP_SINGLE)
{
this->m_pauseThreads = true;
this->m_restoreAfterInterrupt = false;
this->m_deleteAfterAnyInterrupt = false;
this->m_pauseOnNextInterrupt = false;
this->m_visible = visible;
}
else if (type == BreakpointType::BP_PERSISTENT)
{
this->m_pauseThreads = true;
this->m_restoreAfterInterrupt = true;
this->m_deleteAfterAnyInterrupt = false;
this->m_pauseOnNextInterrupt = false;
this->m_visible = visible;
}
else if (type == BreakpointType::BP_RESTORE_POINT)
{
this->m_pauseThreads = false;
this->m_restoreAfterInterrupt = false;
this->m_deleteAfterAnyInterrupt = false;
this->m_pauseOnNextInterrupt = false;
this->m_visible = false;
}
else if (type == BreakpointType::BP_STEP_POINT)
{
this->m_pauseThreads = false;
this->m_restoreAfterInterrupt = false;
this->m_deleteAfterAnyInterrupt = true;
this->m_pauseOnNextInterrupt = true;
this->m_visible = false;
}
this->m_origOpCode = memory_readU32(address);
memory_writeU32(address, DEBUGGER_BP_T_GDBSTUB_TW);
PPCRecompiler_invalidateRange(address, address + 4);
}
GDBServer::ExecutionBreakpoint::~ExecutionBreakpoint()
{
memory_writeU32(this->m_address, this->m_origOpCode);
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
}
uint32 GDBServer::ExecutionBreakpoint::GetVisibleOpCode() const
{
if (this->m_visible)
return memory_readU32(this->m_address);
else
return this->m_origOpCode;
}
void GDBServer::ExecutionBreakpoint::RemoveTemporarily()
{
memory_writeU32(this->m_address, this->m_origOpCode);
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
this->m_restoreAfterInterrupt = true;
}
void GDBServer::ExecutionBreakpoint::Restore()
{
memory_writeU32(this->m_address, DEBUGGER_BP_T_GDBSTUB_TW);
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
this->m_restoreAfterInterrupt = false;
}
namespace coreinit
{
#if BOOST_OS_LINUX
std::vector<pid_t>& OSGetSchedulerThreadIds();
#endif
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
}
GDBServer::AccessBreakpoint::AccessBreakpoint(MPTR address, AccessPointType type)
: m_address(address), m_type(type)
{
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
{
HANDLE hThread = (HANDLE)hThreadNH;
CONTEXT ctx{};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
ctx.Dr2 = (DWORD64)memory_getPointerFromVirtualOffset(address);
ctx.Dr3 = (DWORD64)memory_getPointerFromVirtualOffset(address);
// breakpoint 2
SetBits(ctx.Dr7, 4, 1, 1); // breakpoint #3 enabled: true
SetBits(ctx.Dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
SetBits(ctx.Dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
// breakpoint 3
SetBits(ctx.Dr7, 6, 1, 1); // breakpoint #4 enabled: true
SetBits(ctx.Dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
SetBits(ctx.Dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX
// linux doesn't let us attach to threads which are in the same thread group as our current thread
// we have to create a child process which then modifies the debug registers and quits
pid_t child = fork();
if (child == -1)
{
perror("fork");
return;
}
if (child == 0)
{
for (pid_t tid : coreinit::OSGetSchedulerThreadIds())
{
long rc = ptrace(PTRACE_ATTACH, tid, nullptr, nullptr);
if (rc == -1)
perror("ptrace(PTRACE_ATTACH)");
waitpid(tid, nullptr, 0);
DRType dr7 = _GetDR(tid, 7);
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
DRType dr2 = (uint64)memory_getPointerFromVirtualOffset(address);
DRType dr3 = (uint64)memory_getPointerFromVirtualOffset(address);
// breakpoint 2
SetBits(dr7, 4, 1, 1); // breakpoint #3 enabled: true
SetBits(dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
SetBits(dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
// breakpoint 3
SetBits(dr7, 6, 1, 1); // breakpoint #4 enabled: true
SetBits(dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
SetBits(dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
_SetDR(tid, 2, dr2);
_SetDR(tid, 3, dr3);
_SetDR(tid, 7, dr7);
rc = ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
if (rc == -1)
perror("ptrace(PTRACE_DETACH)");
}
// exit child process
_exit(0);
}
// wait for child process
waitpid(child, nullptr, 0);
#else
cemuLog_log(LogType::Force, "Debugger read/write breakpoints are not supported on non-x86 CPUs yet.");
#endif
}
GDBServer::AccessBreakpoint::~AccessBreakpoint()
{
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
{
HANDLE hThread = (HANDLE)hThreadNH;
CONTEXT ctx{};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
// reset BP 2/3 to zero
ctx.Dr2 = (DWORD64)0;
ctx.Dr3 = (DWORD64)0;
// breakpoint 2
SetBits(ctx.Dr7, 4, 1, 0);
SetBits(ctx.Dr7, 24, 2, 0);
SetBits(ctx.Dr7, 26, 2, 0);
// breakpoint 3
SetBits(ctx.Dr7, 6, 1, 0);
SetBits(ctx.Dr7, 28, 2, 0);
SetBits(ctx.Dr7, 30, 2, 0);
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX
// linux doesn't let us attach to threads which are in the same thread group as our current thread
// we have to create a child process which then modifies the debug registers and quits
pid_t child = fork();
if (child == -1)
{
perror("fork");
return;
}
if (child == 0)
{
for (pid_t tid : coreinit::OSGetSchedulerThreadIds())
{
long rc = ptrace(PTRACE_ATTACH, tid, nullptr, nullptr);
if (rc == -1)
perror("ptrace(PTRACE_ATTACH)");
waitpid(tid, nullptr, 0);
DRType dr7 = _GetDR(tid, 7);
// reset BP 2/3 to zero
DRType dr2 = 0;
DRType dr3 = 0;
// breakpoint 2
SetBits(dr7, 4, 1, 0);
SetBits(dr7, 24, 2, 0);
SetBits(dr7, 26, 2, 0);
// breakpoint 3
SetBits(dr7, 6, 1, 0);
SetBits(dr7, 28, 2, 0);
SetBits(dr7, 30, 2, 0);
_SetDR(tid, 2, dr2);
_SetDR(tid, 3, dr3);
_SetDR(tid, 7, dr7);
rc = ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
if (rc == -1)
perror("ptrace(PTRACE_DETACH)");
}
// exit child process
_exit(0);
}
// wait for child process
waitpid(child, nullptr, 0);
#endif
}

View file

@ -1,18 +1,33 @@
#pragma once
#include "GDBStub.h"
#include <utility> #include <utility>
#if defined(ARCH_X86_64) && BOOST_OS_LINUX #if defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE
#include <sys/types.h> #include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
// helpers for accessing debug register // helpers for accessing debug register
typedef unsigned long DRType; typedef unsigned long DRType;
DRType _GetDR(pid_t tid, int drIndex); DRType _GetDR(pid_t tid, int drIndex)
void _SetDR(pid_t tid, int drIndex, DRType newValue); {
DRType _ReadDR6(); unsigned long v;
v = ptrace (PTRACE_PEEKUSER, tid, offsetof (struct user, u_debugreg[drIndex]), 0);
return (DRType)v;
}
void _SetDR(pid_t tid, int drIndex, DRType newValue)
{
unsigned long v = newValue;
ptrace (PTRACE_POKEUSER, tid, offsetof (struct user, u_debugreg[drIndex]), v);
}
#endif #endif
namespace coreinit
{
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
}
enum class BreakpointType enum class BreakpointType
{ {
BP_SINGLE, BP_SINGLE,
@ -23,10 +38,59 @@ enum class BreakpointType
class GDBServer::ExecutionBreakpoint { class GDBServer::ExecutionBreakpoint {
public: public:
ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason); ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason)
~ExecutionBreakpoint(); : m_address(address), m_removedAfterInterrupt(false), m_reason(std::move(reason))
{
if (type == BreakpointType::BP_SINGLE)
{
this->m_pauseThreads = true;
this->m_restoreAfterInterrupt = false;
this->m_deleteAfterAnyInterrupt = false;
this->m_pauseOnNextInterrupt = false;
this->m_visible = visible;
}
else if (type == BreakpointType::BP_PERSISTENT)
{
this->m_pauseThreads = true;
this->m_restoreAfterInterrupt = true;
this->m_deleteAfterAnyInterrupt = false;
this->m_pauseOnNextInterrupt = false;
this->m_visible = visible;
}
else if (type == BreakpointType::BP_RESTORE_POINT)
{
this->m_pauseThreads = false;
this->m_restoreAfterInterrupt = false;
this->m_deleteAfterAnyInterrupt = false;
this->m_pauseOnNextInterrupt = false;
this->m_visible = false;
}
else if (type == BreakpointType::BP_STEP_POINT)
{
this->m_pauseThreads = false;
this->m_restoreAfterInterrupt = false;
this->m_deleteAfterAnyInterrupt = true;
this->m_pauseOnNextInterrupt = true;
this->m_visible = false;
}
[[nodiscard]] uint32 GetVisibleOpCode() const; this->m_origOpCode = memory_readU32(address);
memory_writeU32(address, DEBUGGER_BP_T_GDBSTUB_TW);
PPCRecompiler_invalidateRange(address, address + 4);
};
~ExecutionBreakpoint()
{
memory_writeU32(this->m_address, this->m_origOpCode);
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
};
[[nodiscard]] uint32 GetVisibleOpCode() const
{
if (this->m_visible)
return memory_readU32(this->m_address);
else
return this->m_origOpCode;
};
[[nodiscard]] bool ShouldBreakThreads() const [[nodiscard]] bool ShouldBreakThreads() const
{ {
return this->m_pauseThreads; return this->m_pauseThreads;
@ -54,8 +118,18 @@ public:
return m_reason; return m_reason;
}; };
void RemoveTemporarily(); void RemoveTemporarily()
void Restore(); {
memory_writeU32(this->m_address, this->m_origOpCode);
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
this->m_restoreAfterInterrupt = true;
};
void Restore()
{
memory_writeU32(this->m_address, DEBUGGER_BP_T_GDBSTUB_TW);
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
this->m_restoreAfterInterrupt = false;
};
void PauseOnNextInterrupt() void PauseOnNextInterrupt()
{ {
this->m_pauseOnNextInterrupt = true; this->m_pauseOnNextInterrupt = true;
@ -88,8 +162,115 @@ enum class AccessPointType
class GDBServer::AccessBreakpoint { class GDBServer::AccessBreakpoint {
public: public:
AccessBreakpoint(MPTR address, AccessPointType type); AccessBreakpoint(MPTR address, AccessPointType type)
~AccessBreakpoint(); : m_address(address), m_type(type)
{
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
{
HANDLE hThread = (HANDLE)hThreadNH;
CONTEXT ctx{};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
ctx.Dr2 = (DWORD64)memory_getPointerFromVirtualOffset(address);
ctx.Dr3 = (DWORD64)memory_getPointerFromVirtualOffset(address);
// breakpoint 2
SetBits(ctx.Dr7, 4, 1, 1); // breakpoint #3 enabled: true
SetBits(ctx.Dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
SetBits(ctx.Dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
// breakpoint 3
SetBits(ctx.Dr7, 6, 1, 1); // breakpoint #4 enabled: true
SetBits(ctx.Dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
SetBits(ctx.Dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
// todo: port the following code to all unix platforms, they seem to differ quite a bit
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
{
pid_t pid = (pid_t)(uintptr_t)hThreadNH;
ptrace(PTRACE_ATTACH, pid, nullptr, nullptr);
waitpid(pid, nullptr, 0);
DRType dr7 = _GetDR(pid, 7);
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
DRType dr2 = (uint64)memory_getPointerFromVirtualOffset(address);
DRType dr3 = (uint64)memory_getPointerFromVirtualOffset(address);
// breakpoint 2
SetBits(dr7, 4, 1, 1); // breakpoint #3 enabled: true
SetBits(dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
SetBits(dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
// breakpoint 3
SetBits(dr7, 6, 1, 1); // breakpoint #4 enabled: true
SetBits(dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
SetBits(dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
_SetDR(pid, 2, dr2);
_SetDR(pid, 3, dr3);
_SetDR(pid, 7, dr7);
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
}
#else
cemuLog_log(LogType::Force, "Debugger read/write breakpoints are not supported on non-x86 CPUs yet.");
#endif
};
~AccessBreakpoint()
{
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
{
HANDLE hThread = (HANDLE)hThreadNH;
CONTEXT ctx{};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
// reset BP 2/3 to zero
ctx.Dr2 = (DWORD64)0;
ctx.Dr3 = (DWORD64)0;
// breakpoint 2
SetBits(ctx.Dr7, 4, 1, 0);
SetBits(ctx.Dr7, 24, 2, 0);
SetBits(ctx.Dr7, 26, 2, 0);
// breakpoint 3
SetBits(ctx.Dr7, 6, 1, 0);
SetBits(ctx.Dr7, 28, 2, 0);
SetBits(ctx.Dr7, 30, 2, 0);
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
{
pid_t pid = (pid_t)(uintptr_t)hThreadNH;
ptrace(PTRACE_ATTACH, pid, nullptr, nullptr);
waitpid(pid, nullptr, 0);
DRType dr7 = _GetDR(pid, 7);
// reset BP 2/3 to zero
DRType dr2 = 0;
DRType dr3 = 0;
// breakpoint 2
SetBits(dr7, 4, 1, 0);
SetBits(dr7, 24, 2, 0);
SetBits(dr7, 26, 2, 0);
// breakpoint 3
SetBits(dr7, 6, 1, 0);
SetBits(dr7, 28, 2, 0);
SetBits(dr7, 30, 2, 0);
_SetDR(pid, 2, dr2);
_SetDR(pid, 3, dr3);
_SetDR(pid, 7, dr7);
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
}
#endif
};
MPTR GetAddress() const MPTR GetAddress() const
{ {

View file

@ -263,14 +263,6 @@ bool GDBServer::Initialize()
return false; return false;
} }
int nodelayEnabled = TRUE;
if (setsockopt(m_server_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelayEnabled, sizeof(nodelayEnabled)) == SOCKET_ERROR)
{
closesocket(m_server_socket);
m_server_socket = INVALID_SOCKET;
return false;
}
memset(&m_server_addr, 0, sizeof(m_server_addr)); memset(&m_server_addr, 0, sizeof(m_server_addr));
m_server_addr.sin_family = AF_INET; m_server_addr.sin_family = AF_INET;
m_server_addr.sin_addr.s_addr = htonl(INADDR_ANY); m_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
@ -297,7 +289,7 @@ bool GDBServer::Initialize()
void GDBServer::ThreadFunc() void GDBServer::ThreadFunc()
{ {
SetThreadName("GDBServer"); SetThreadName("GDBServer::ThreadFunc");
while (!m_stopRequested) while (!m_stopRequested)
{ {
@ -364,7 +356,7 @@ void GDBServer::ThreadFunc()
} }
char checkSumStr[2]; char checkSumStr[2];
receiveMessage(checkSumStr, 2); receiveMessage(checkSumStr, 2);
uint32_t checkSum = std::stoi(std::string(checkSumStr, sizeof(checkSumStr)), nullptr, 16); uint32_t checkSum = std::stoi(checkSumStr, nullptr, 16);
assert((checkedSum & 0xFF) == checkSum); assert((checkedSum & 0xFF) == checkSum);
HandleCommand(message); HandleCommand(message);
@ -908,7 +900,7 @@ void GDBServer::HandleTrapInstruction(PPCInterpreter_t* hCPU)
return cemu_assert_suspicious(); return cemu_assert_suspicious();
// Secondly, delete one-shot breakpoints but also temporarily delete patched instruction to run original instruction // Secondly, delete one-shot breakpoints but also temporarily delete patched instruction to run original instruction
OSThread_t* currThread = coreinit::OSGetCurrentThread(); OSThread_t* currThread = coreinitThread_getCurrentThreadDepr(hCPU);
std::string pauseReason = fmt::format("T05thread:{:08X};core:{:02X};{}", GET_THREAD_ID(currThread), PPCInterpreter_getCoreIndex(hCPU), patchedBP->second.GetReason()); std::string pauseReason = fmt::format("T05thread:{:08X};core:{:02X};{}", GET_THREAD_ID(currThread), PPCInterpreter_getCoreIndex(hCPU), patchedBP->second.GetReason());
bool pauseThreads = patchedBP->second.ShouldBreakThreads() || patchedBP->second.ShouldBreakThreadsOnNextInterrupt(); bool pauseThreads = patchedBP->second.ShouldBreakThreads() || patchedBP->second.ShouldBreakThreadsOnNextInterrupt();
if (patchedBP->second.IsPersistent()) if (patchedBP->second.IsPersistent())
@ -947,7 +939,7 @@ void GDBServer::HandleTrapInstruction(PPCInterpreter_t* hCPU)
ThreadPool::FireAndForget(&waitForBrokenThreads, std::move(m_resumed_context), pauseReason); ThreadPool::FireAndForget(&waitForBrokenThreads, std::move(m_resumed_context), pauseReason);
} }
breakThreads(GET_THREAD_ID(coreinit::OSGetCurrentThread())); breakThreads(GET_THREAD_ID(coreinitThread_getCurrentThreadDepr(hCPU)));
cemuLog_logDebug(LogType::Force, "[GDBStub] Resumed from a breakpoint!"); cemuLog_logDebug(LogType::Force, "[GDBStub] Resumed from a breakpoint!");
} }
} }
@ -967,9 +959,8 @@ void GDBServer::HandleAccessException(uint64 dr6)
if (!response.empty()) if (!response.empty())
{ {
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
cemuLog_logDebug(LogType::Force, "Received matching breakpoint exception: {}", response); cemuLog_logDebug(LogType::Force, "Received matching breakpoint exception: {}", response);
auto nextInstructions = findNextInstruction(hCPU->instructionPointer, hCPU->spr.LR, hCPU->spr.CTR); auto nextInstructions = findNextInstruction(ppcInterpreterCurrentInstance->instructionPointer, ppcInterpreterCurrentInstance->spr.LR, ppcInterpreterCurrentInstance->spr.CTR);
for (MPTR nextInstr : nextInstructions) for (MPTR nextInstr : nextInstructions)
{ {
auto bpIt = m_patchedInstructions.find(nextInstr); auto bpIt = m_patchedInstructions.find(nextInstr);

View file

@ -10,18 +10,6 @@ namespace Espresso
CR_BIT_INDEX_SO = 3, CR_BIT_INDEX_SO = 3,
}; };
enum class PSQ_LOAD_TYPE
{
TYPE_F32 = 0,
TYPE_UNUSED1 = 1,
TYPE_UNUSED2 = 2,
TYPE_UNUSED3 = 3,
TYPE_U8 = 4,
TYPE_U16 = 5,
TYPE_S8 = 6,
TYPE_S16 = 7,
};
enum class PrimaryOpcode enum class PrimaryOpcode
{ {
// underscore at the end of the name means that this instruction always updates CR0 (as if RC bit is set) // underscore at the end of the name means that this instruction always updates CR0 (as if RC bit is set)
@ -103,15 +91,13 @@ namespace Espresso
BCCTR = 528 BCCTR = 528
}; };
enum class Opcode31 enum class OPCODE_31
{ {
TW = 4,
MFTB = 371,
}; };
inline PrimaryOpcode GetPrimaryOpcode(uint32 opcode) { return (PrimaryOpcode)(opcode >> 26); }; inline PrimaryOpcode GetPrimaryOpcode(uint32 opcode) { return (PrimaryOpcode)(opcode >> 26); };
inline Opcode19 GetGroup19Opcode(uint32 opcode) { return (Opcode19)((opcode >> 1) & 0x3FF); }; inline Opcode19 GetGroup19Opcode(uint32 opcode) { return (Opcode19)((opcode >> 1) & 0x3FF); };
inline Opcode31 GetGroup31Opcode(uint32 opcode) { return (Opcode31)((opcode >> 1) & 0x3FF); };
struct BOField struct BOField
{ {
@ -146,12 +132,6 @@ namespace Espresso
uint8 bo; uint8 bo;
}; };
// returns true if LK bit is set, only valid for branch instructions
inline bool DecodeLK(uint32 opcode)
{
return (opcode & 1) != 0;
}
inline void _decodeForm_I(uint32 opcode, uint32& LI, bool& AA, bool& LK) inline void _decodeForm_I(uint32 opcode, uint32& LI, bool& AA, bool& LK)
{ {
LI = opcode & 0x3fffffc; LI = opcode & 0x3fffffc;
@ -203,7 +183,13 @@ namespace Espresso
_decodeForm_D_branch(opcode, BD, BO, BI, AA, LK); _decodeForm_D_branch(opcode, BD, BO, BI, AA, LK);
} }
inline void decodeOp_BCSPR(uint32 opcode, BOField& BO, uint32& BI, bool& LK) // BCLR and BCSPR inline void decodeOp_BCLR(uint32 opcode, BOField& BO, uint32& BI, bool& LK)
{
// form XL (with BD field expected to be zero)
_decodeForm_XL(opcode, BO, BI, LK);
}
inline void decodeOp_BCCTR(uint32 opcode, BOField& BO, uint32& BI, bool& LK)
{ {
// form XL (with BD field expected to be zero) // form XL (with BD field expected to be zero)
_decodeForm_XL(opcode, BO, BI, LK); _decodeForm_XL(opcode, BO, BI, LK);

View file

@ -3,12 +3,12 @@ static void PPCInterpreter_setXerOV(PPCInterpreter_t* hCPU, bool hasOverflow)
{ {
if (hasOverflow) if (hasOverflow)
{ {
hCPU->xer_so = 1; hCPU->spr.XER |= XER_SO;
hCPU->xer_ov = 1; hCPU->spr.XER |= XER_OV;
} }
else else
{ {
hCPU->xer_ov = 0; hCPU->spr.XER &= ~XER_OV;
} }
} }
@ -41,7 +41,7 @@ static void PPCInterpreter_ADD(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_ADDO(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_ADDO(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
// Don't Starve Giant Edition uses this instruction + BSO // untested (Don't Starve Giant Edition uses this instruction + BSO)
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
uint32 result = hCPU->gpr[rA] + hCPU->gpr[rB]; uint32 result = hCPU->gpr[rA] + hCPU->gpr[rB];
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(hCPU->gpr[rA], hCPU->gpr[rB], result)); PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(hCPU->gpr[rA], hCPU->gpr[rB], result));
@ -113,6 +113,7 @@ static void PPCInterpreter_ADDEO(PPCInterpreter_t* hCPU, uint32 opcode)
else else
hCPU->xer_ca = 0; hCPU->xer_ca = 0;
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(a, b, hCPU->gpr[rD])); PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(a, b, hCPU->gpr[rD]));
// update CR
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -129,7 +130,7 @@ static void PPCInterpreter_ADDI(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_ADDIC(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_ADDIC(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
sint32 rD, rA; int rD, rA;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
uint32 a = hCPU->gpr[rA]; uint32 a = hCPU->gpr[rA];
@ -144,7 +145,7 @@ static void PPCInterpreter_ADDIC(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_ADDIC_(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_ADDIC_(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
sint32 rD, rA; int rD, rA;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
uint32 a = hCPU->gpr[rA]; uint32 a = hCPU->gpr[rA];
@ -154,13 +155,14 @@ static void PPCInterpreter_ADDIC_(PPCInterpreter_t* hCPU, uint32 opcode)
hCPU->xer_ca = 1; hCPU->xer_ca = 1;
else else
hCPU->xer_ca = 0; hCPU->xer_ca = 0;
// update cr0 flags
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
static void PPCInterpreter_ADDIS(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_ADDIS(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
sint32 rD, rA; int rD, rA;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm); PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm);
hCPU->gpr[rD] = (rA ? hCPU->gpr[rA] : 0) + imm; hCPU->gpr[rD] = (rA ? hCPU->gpr[rA] : 0) + imm;
@ -183,23 +185,6 @@ static void PPCInterpreter_ADDZE(PPCInterpreter_t* hCPU, uint32 opcode)
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
static void PPCInterpreter_ADDZEO(PPCInterpreter_t* hCPU, uint32 opcode)
{
PPC_OPC_TEMPL3_XO();
PPC_ASSERT(rB == 0);
uint32 a = hCPU->gpr[rA];
uint32 ca = hCPU->xer_ca;
hCPU->gpr[rD] = a + ca;
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(a, 0, hCPU->gpr[rD]));
if ((a == 0xffffffff) && ca)
hCPU->xer_ca = 1;
else
hCPU->xer_ca = 0;
if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU);
}
static void PPCInterpreter_ADDME(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_ADDME(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
@ -216,23 +201,6 @@ static void PPCInterpreter_ADDME(PPCInterpreter_t* hCPU, uint32 opcode)
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
static void PPCInterpreter_ADDMEO(PPCInterpreter_t* hCPU, uint32 opcode)
{
PPC_OPC_TEMPL3_XO();
PPC_ASSERT(rB == 0);
uint32 a = hCPU->gpr[rA];
uint32 ca = hCPU->xer_ca;
hCPU->gpr[rD] = a + ca + 0xffffffff;
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(a, 0xffffffff, hCPU->gpr[rD]));
if (a || ca)
hCPU->xer_ca = 1;
else
hCPU->xer_ca = 0;
if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU);
}
static void PPCInterpreter_SUBF(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_SUBF(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
@ -244,12 +212,11 @@ static void PPCInterpreter_SUBF(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_SUBFO(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_SUBFO(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
// Seen in Don't Starve Giant Edition and Teslagrad // untested (Don't Starve Giant Edition uses this)
// also used by DS Virtual Console (Super Mario 64 DS) // also used by DS Virtual Console (Super Mario 64 DS)
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
uint32 result = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1; hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1;
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~hCPU->gpr[rA], hCPU->gpr[rB], result)); PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~hCPU->gpr[rA], hCPU->gpr[rB], hCPU->gpr[rD]));
hCPU->gpr[rD] = result;
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -278,7 +245,7 @@ static void PPCInterpreter_SUBFCO(PPCInterpreter_t* hCPU, uint32 opcode)
uint32 a = hCPU->gpr[rA]; uint32 a = hCPU->gpr[rA];
uint32 b = hCPU->gpr[rB]; uint32 b = hCPU->gpr[rB];
hCPU->gpr[rD] = ~a + b + 1; hCPU->gpr[rD] = ~a + b + 1;
// update carry // update xer
if (ppc_carry_3(~a, b, 1)) if (ppc_carry_3(~a, b, 1))
hCPU->xer_ca = 1; hCPU->xer_ca = 1;
else else
@ -292,7 +259,7 @@ static void PPCInterpreter_SUBFCO(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_SUBFIC(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_SUBFIC(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
sint32 rD, rA; int rD, rA;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
uint32 a = hCPU->gpr[rA]; uint32 a = hCPU->gpr[rA];
@ -316,6 +283,7 @@ static void PPCInterpreter_SUBFE(PPCInterpreter_t* hCPU, uint32 opcode)
hCPU->xer_ca = 1; hCPU->xer_ca = 1;
else else
hCPU->xer_ca = 0; hCPU->xer_ca = 0;
// update cr0
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -335,6 +303,7 @@ static void PPCInterpreter_SUBFEO(PPCInterpreter_t* hCPU, uint32 opcode)
else else
hCPU->xer_ca = 0; hCPU->xer_ca = 0;
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~a, b, result)); PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~a, b, result));
// update cr0
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -356,25 +325,9 @@ static void PPCInterpreter_SUBFZE(PPCInterpreter_t* hCPU, uint32 opcode)
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
static void PPCInterpreter_SUBFZEO(PPCInterpreter_t* hCPU, uint32 opcode)
{
PPC_OPC_TEMPL3_XO();
PPC_ASSERT(rB == 0);
uint32 a = hCPU->gpr[rA];
uint32 ca = hCPU->xer_ca;
hCPU->gpr[rD] = ~a + ca;
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~a, 0, hCPU->gpr[rD]));
if (a == 0 && ca)
hCPU->xer_ca = 1;
else
hCPU->xer_ca = 0;
if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU);
}
static void PPCInterpreter_SUBFME(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_SUBFME(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
// untested
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
PPC_ASSERT(rB == 0); PPC_ASSERT(rB == 0);
uint32 a = hCPU->gpr[rA]; uint32 a = hCPU->gpr[rA];
@ -385,24 +338,7 @@ static void PPCInterpreter_SUBFME(PPCInterpreter_t* hCPU, uint32 opcode)
hCPU->xer_ca = 1; hCPU->xer_ca = 1;
else else
hCPU->xer_ca = 0; hCPU->xer_ca = 0;
if (opcode & PPC_OPC_RC) // update cr0
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU);
}
static void PPCInterpreter_SUBFMEO(PPCInterpreter_t* hCPU, uint32 opcode)
{
PPC_OPC_TEMPL3_XO();
PPC_ASSERT(rB == 0);
uint32 a = hCPU->gpr[rA];
uint32 ca = hCPU->xer_ca;
hCPU->gpr[rD] = ~a + 0xFFFFFFFF + ca;
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~a, 0xFFFFFFFF, hCPU->gpr[rD]));
// update xer carry
if (ppc_carry_3(~a, 0xFFFFFFFF, ca))
hCPU->xer_ca = 1;
else
hCPU->xer_ca = 0;
if (opcode & PPC_OPC_RC) if (opcode & PPC_OPC_RC)
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -415,8 +351,13 @@ static void PPCInterpreter_MULHW_(PPCInterpreter_t* hCPU, uint32 opcode)
sint64 b = (sint32)hCPU->gpr[rB]; sint64 b = (sint32)hCPU->gpr[rB];
sint64 c = a * b; sint64 c = a * b;
hCPU->gpr[rD] = ((uint64)c) >> 32; hCPU->gpr[rD] = ((uint64)c) >> 32;
if (opHasRC()) if (opcode & PPC_OPC_RC) {
// update cr0 flags
#ifdef CEMU_DEBUG_ASSERT
assert_dbg();
#endif
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
}
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
@ -467,14 +408,14 @@ static void PPCInterpreter_MULLI(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_DIVW(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_DIVW(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
sint32 a = (sint32)hCPU->gpr[rA]; sint32 a = hCPU->gpr[rA];
sint32 b = (sint32)hCPU->gpr[rB]; sint32 b = hCPU->gpr[rB];
if (b == 0) if (b == 0)
hCPU->gpr[rD] = a < 0 ? 0xFFFFFFFF : 0; {
else if (a == 0x80000000 && b == 0xFFFFFFFF) cemuLog_logDebug(LogType::Force, "Error: Division by zero! [{:08x}]", (uint32)hCPU->instructionPointer);
hCPU->gpr[rD] = 0xFFFFFFFF; b++;
else }
hCPU->gpr[rD] = a / b; hCPU->gpr[rD] = a / b;
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -483,23 +424,16 @@ static void PPCInterpreter_DIVW(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_DIVWO(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_DIVWO(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
sint32 a = (sint32)hCPU->gpr[rA]; sint32 a = hCPU->gpr[rA];
sint32 b = (sint32)hCPU->gpr[rB]; sint32 b = hCPU->gpr[rB];
if (b == 0) if (b == 0)
{ {
PPCInterpreter_setXerOV(hCPU, true); PPCInterpreter_setXerOV(hCPU, true);
hCPU->gpr[rD] = a < 0 ? 0xFFFFFFFF : 0; PPCInterpreter_nextInstruction(hCPU);
} return;
else if(a == 0x80000000 && b == 0xFFFFFFFF)
{
PPCInterpreter_setXerOV(hCPU, true);
hCPU->gpr[rD] = 0xFFFFFFFF;
}
else
{
hCPU->gpr[rD] = a / b;
PPCInterpreter_setXerOV(hCPU, false);
} }
hCPU->gpr[rD] = a / b;
PPCInterpreter_setXerOV(hCPU, false);
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -508,14 +442,12 @@ static void PPCInterpreter_DIVWO(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_DIVWU(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_DIVWU(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
uint32 a = hCPU->gpr[rA]; if (hCPU->gpr[rB] == 0)
uint32 b = hCPU->gpr[rB]; {
if (b == 0) PPCInterpreter_nextInstruction(hCPU);
hCPU->gpr[rD] = 0; return;
else if (a == 0x80000000 && b == 0xFFFFFFFF) }
hCPU->gpr[rD] = 0; hCPU->gpr[rD] = hCPU->gpr[rA] / hCPU->gpr[rB];
else
hCPU->gpr[rD] = a / b;
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -524,23 +456,14 @@ static void PPCInterpreter_DIVWU(PPCInterpreter_t* hCPU, uint32 opcode)
static void PPCInterpreter_DIVWUO(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_DIVWUO(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
PPC_OPC_TEMPL3_XO(); PPC_OPC_TEMPL3_XO();
uint32 a = hCPU->gpr[rA]; if (hCPU->gpr[rB] == 0)
uint32 b = hCPU->gpr[rB];
if (b == 0)
{ {
PPCInterpreter_setXerOV(hCPU, true); PPCInterpreter_setXerOV(hCPU, true);
hCPU->gpr[rD] = 0; PPCInterpreter_nextInstruction(hCPU);
} return;
else if(a == 0x80000000 && b == 0xFFFFFFFF)
{
PPCInterpreter_setXerOV(hCPU, false);
hCPU->gpr[rD] = 0;
}
else
{
hCPU->gpr[rD] = a / b;
PPCInterpreter_setXerOV(hCPU, false);
} }
hCPU->gpr[rD] = hCPU->gpr[rA] / hCPU->gpr[rB];
PPCInterpreter_setXerOV(hCPU, false);
if (opHasRC()) if (opHasRC())
ppc_update_cr0(hCPU, hCPU->gpr[rD]); ppc_update_cr0(hCPU, hCPU->gpr[rD]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -567,13 +490,6 @@ static void PPCInterpreter_CRANDC(PPCInterpreter_t* hCPU, uint32 opcode)
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
static void PPCInterpreter_CRNAND(PPCInterpreter_t* hCPU, uint32 opcode)
{
PPC_OPC_TEMPL_X_CR();
ppc_setCRBit(hCPU, crD, (ppc_getCRBit(hCPU, crA)&ppc_getCRBit(hCPU, crB)) ^ 1);
PPCInterpreter_nextInstruction(hCPU);
}
static void PPCInterpreter_CROR(PPCInterpreter_t* hCPU, uint32 opcode) static void PPCInterpreter_CROR(PPCInterpreter_t* hCPU, uint32 opcode)
{ {
PPC_OPC_TEMPL_X_CR(); PPC_OPC_TEMPL_X_CR();
@ -931,7 +847,8 @@ static void PPCInterpreter_CMP(PPCInterpreter_t* hCPU, uint32 opcode)
hCPU->cr[cr * 4 + CR_BIT_GT] = 1; hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
else else
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; if ((hCPU->spr.XER & XER_SO) != 0)
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
@ -953,7 +870,8 @@ static void PPCInterpreter_CMPL(PPCInterpreter_t* hCPU, uint32 opcode)
hCPU->cr[cr * 4 + CR_BIT_GT] = 1; hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
else else
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; if ((hCPU->spr.XER & XER_SO) != 0)
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
@ -976,7 +894,8 @@ static void PPCInterpreter_CMPI(PPCInterpreter_t* hCPU, uint32 opcode)
hCPU->cr[cr * 4 + CR_BIT_GT] = 1; hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
else else
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; if (hCPU->spr.XER & XER_SO)
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
@ -999,7 +918,8 @@ static void PPCInterpreter_CMPLI(PPCInterpreter_t* hCPU, uint32 opcode)
hCPU->cr[cr * 4 + CR_BIT_GT] = 1; hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
else else
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; if (hCPU->spr.XER & XER_SO)
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }

View file

@ -32,7 +32,7 @@ espresso_frsqrte_entry_t frsqrteLookupTable[32] =
{0x20c1000, 0x35e},{0x1f12000, 0x332},{0x1d79000, 0x30a},{0x1bf4000, 0x2e6}, {0x20c1000, 0x35e},{0x1f12000, 0x332},{0x1d79000, 0x30a},{0x1bf4000, 0x2e6},
}; };
ATTR_MS_ABI double frsqrte_espresso(double input) double frsqrte_espresso(double input)
{ {
unsigned long long x = *(unsigned long long*)&input; unsigned long long x = *(unsigned long long*)&input;
@ -111,7 +111,7 @@ espresso_fres_entry_t fresLookupTable[32] =
{0x88400, 0x11a}, {0x65000, 0x11a}, {0x41c00, 0x108}, {0x20c00, 0x106} {0x88400, 0x11a}, {0x65000, 0x11a}, {0x41c00, 0x108}, {0x20c00, 0x106}
}; };
ATTR_MS_ABI double fres_espresso(double input) double fres_espresso(double input)
{ {
// based on testing we know that fres uses only the first 15 bits of the mantissa // based on testing we know that fres uses only the first 15 bits of the mantissa
// seee eeee eeee mmmm mmmm mmmm mmmx xxxx .... (s = sign, e = exponent, m = mantissa, x = not used) // seee eeee eeee mmmm mmmm mmmm mmmx xxxx .... (s = sign, e = exponent, m = mantissa, x = not used)

View file

@ -2,70 +2,62 @@
#include "PPCInterpreterInternal.h" #include "PPCInterpreterInternal.h"
#include "PPCInterpreterHelper.h" #include "PPCInterpreterHelper.h"
std::unordered_set<std::string> s_unsupportedHLECalls; std::unordered_set<std::string> sUnsupportedHLECalls;
void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU) void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU)
{ {
const char* libFuncName = (char*)memory_getPointerFromVirtualOffset(hCPU->instructionPointer + 8); const char* libFuncName = (char*)memory_getPointerFromVirtualOffset(hCPU->instructionPointer + 8);
std::string tempString = fmt::format("Unsupported lib call: {}", libFuncName); std::string tempString = fmt::format("Unsupported lib call: {}", libFuncName);
if (s_unsupportedHLECalls.find(tempString) == s_unsupportedHLECalls.end()) if (sUnsupportedHLECalls.find(tempString) == sUnsupportedHLECalls.end())
{ {
cemuLog_log(LogType::UnsupportedAPI, "{}", tempString); cemuLog_log(LogType::UnsupportedAPI, "{}", tempString);
s_unsupportedHLECalls.emplace(tempString); sUnsupportedHLECalls.emplace(tempString);
} }
hCPU->gpr[3] = 0; hCPU->gpr[3] = 0;
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
static constexpr size_t HLE_TABLE_CAPACITY = 0x4000; std::vector<void(*)(PPCInterpreter_t* hCPU)>* sPPCHLETable{};
HLECALL s_ppcHleTable[HLE_TABLE_CAPACITY]{};
sint32 s_ppcHleTableWriteIndex = 0;
std::mutex s_ppcHleTableMutex;
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName) HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall)
{ {
std::unique_lock _l(s_ppcHleTableMutex); if (!sPPCHLETable)
if (s_ppcHleTableWriteIndex >= HLE_TABLE_CAPACITY) sPPCHLETable = new std::vector<void(*)(PPCInterpreter_t* hCPU)>();
for (sint32 i = 0; i < sPPCHLETable->size(); i++)
{ {
cemuLog_log(LogType::Force, "HLE table is full"); if ((*sPPCHLETable)[i] == hleCall)
cemu_assert(false);
}
for (sint32 i = 0; i < s_ppcHleTableWriteIndex; i++)
{
if (s_ppcHleTable[i] == hleCall)
{
return i; return i;
}
} }
cemu_assert(s_ppcHleTableWriteIndex < HLE_TABLE_CAPACITY); HLEIDX newFuncIndex = (sint32)sPPCHLETable->size();
s_ppcHleTable[s_ppcHleTableWriteIndex] = hleCall; sPPCHLETable->resize(sPPCHLETable->size() + 1);
HLEIDX funcIndex = s_ppcHleTableWriteIndex; (*sPPCHLETable)[newFuncIndex] = hleCall;
s_ppcHleTableWriteIndex++; return newFuncIndex;
return funcIndex;
} }
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex) HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex)
{ {
if (funcIndex < 0 || funcIndex >= HLE_TABLE_CAPACITY) if (funcIndex < 0 || funcIndex >= sPPCHLETable->size())
return nullptr; return nullptr;
return s_ppcHleTable[funcIndex]; return sPPCHLETable->data()[funcIndex];
} }
std::mutex s_hleLogMutex; std::mutex g_hleLogMutex;
void PPCInterpreter_virtualHLE(PPCInterpreter_t* hCPU, unsigned int opcode) void PPCInterpreter_virtualHLE(PPCInterpreter_t* hCPU, unsigned int opcode)
{ {
uint32 hleFuncId = opcode & 0xFFFF; uint32 hleFuncId = opcode & 0xFFFF;
if (hleFuncId == 0xFFD0) [[unlikely]] if (hleFuncId == 0xFFD0)
{ {
s_hleLogMutex.lock(); g_hleLogMutex.lock();
PPCInterpreter_handleUnsupportedHLECall(hCPU); PPCInterpreter_handleUnsupportedHLECall(hCPU);
s_hleLogMutex.unlock(); g_hleLogMutex.unlock();
return;
} }
else else
{ {
// os lib function // os lib function
auto hleCall = PPCInterpreter_getHLECall(hleFuncId); cemu_assert(hleFuncId < sPPCHLETable->size());
auto hleCall = (*sPPCHLETable)[hleFuncId];
cemu_assert(hleCall); cemu_assert(hleCall);
hleCall(hCPU); hleCall(hCPU);
} }

View file

@ -428,6 +428,9 @@ public:
} }
}; };
uint32 testIP[100];
uint32 testIPC = 0;
template <typename ppcItpCtrl> template <typename ppcItpCtrl>
class PPCInterpreterContainer class PPCInterpreterContainer
{ {
@ -463,10 +466,6 @@ public:
case 1: // virtual HLE case 1: // virtual HLE
PPCInterpreter_virtualHLE(hCPU, opcode); PPCInterpreter_virtualHLE(hCPU, opcode);
break; break;
case 3:
cemuLog_logDebug(LogType::Force, "Unsupported TWI instruction executed at {:08x}", hCPU->instructionPointer);
PPCInterpreter_nextInstruction(hCPU);
break;
case 4: case 4:
switch (PPC_getBits(opcode, 30, 5)) switch (PPC_getBits(opcode, 30, 5))
{ {
@ -483,9 +482,8 @@ public:
PPCInterpreter_PS_CMPU1(hCPU, opcode); PPCInterpreter_PS_CMPU1(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4->0] at {:08x}", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [4->0] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
cemu_assert_unimplemented(); cemu_assert_unimplemented();
hCPU->instructionPointer += 4;
break; break;
} }
break; break;
@ -511,9 +509,8 @@ public:
PPCInterpreter_PS_ABS(hCPU, opcode); PPCInterpreter_PS_ABS(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4->8] at {:08x}", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [4->8] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
cemu_assert_unimplemented(); cemu_assert_unimplemented();
hCPU->instructionPointer += 4;
break; break;
} }
break; break;
@ -551,9 +548,8 @@ public:
PPCInterpreter_PS_MERGE11(hCPU, opcode); PPCInterpreter_PS_MERGE11(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4->16] at {:08x}", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [4->16] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
cemu_assert_unimplemented(); debugBreakpoint();
hCPU->instructionPointer += 4;
break; break;
} }
break; break;
@ -594,9 +590,8 @@ public:
PPCInterpreter_PS_NMADD(hCPU, opcode); PPCInterpreter_PS_NMADD(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4] at {:08x}", PPC_getBits(opcode, 30, 5), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [4] at %08X\n", PPC_getBits(opcode, 30, 5), hCPU->instructionPointer);
cemu_assert_unimplemented(); cemu_assert_unimplemented();
hCPU->instructionPointer += 4;
break; break;
} }
break; break;
@ -628,15 +623,12 @@ public:
PPCInterpreter_BCX(hCPU, opcode); PPCInterpreter_BCX(hCPU, opcode);
break; break;
case 17: case 17:
if (PPC_getBits(opcode, 30, 1) == 1) if (PPC_getBits(opcode, 30, 1) == 1) {
{
PPCInterpreter_SC(hCPU, opcode); PPCInterpreter_SC(hCPU, opcode);
} }
else else {
{ debug_printf("Unsupported Opcode [0x17 --> 0x0]\n");
cemuLog_logDebug(LogType::Force, "Unsupported Opcode [0x17 --> 0x0]");
cemu_assert_unimplemented(); cemu_assert_unimplemented();
hCPU->instructionPointer += 4;
} }
break; break;
case 18: case 18:
@ -666,9 +658,6 @@ public:
case 193: case 193:
PPCInterpreter_CRXOR(hCPU, opcode); PPCInterpreter_CRXOR(hCPU, opcode);
break; break;
case 225:
PPCInterpreter_CRNAND(hCPU, opcode);
break;
case 257: case 257:
PPCInterpreter_CRAND(hCPU, opcode); PPCInterpreter_CRAND(hCPU, opcode);
break; break;
@ -685,9 +674,8 @@ public:
PPCInterpreter_BCCTR(hCPU, opcode); PPCInterpreter_BCCTR(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [19] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [19] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
cemu_assert_unimplemented(); cemu_assert_unimplemented();
hCPU->instructionPointer += 4;
break; break;
} }
break; break;
@ -725,6 +713,9 @@ public:
PPCInterpreter_CMP(hCPU, opcode); PPCInterpreter_CMP(hCPU, opcode);
break; break;
case 4: case 4:
#ifdef CEMU_DEBUG_ASSERT
debug_printf("TW instruction executed at %08x\n", hCPU->instructionPointer);
#endif
PPCInterpreter_TW(hCPU, opcode); PPCInterpreter_TW(hCPU, opcode);
break; break;
case 8: case 8:
@ -904,12 +895,6 @@ public:
case 522: case 522:
PPCInterpreter_ADDCO(hCPU, opcode); PPCInterpreter_ADDCO(hCPU, opcode);
break; break;
case 523: // 11 | OE
PPCInterpreter_MULHWU_(hCPU, opcode); // OE is ignored
break;
case 533:
PPCInterpreter_LSWX(hCPU, opcode);
break;
case 534: case 534:
PPCInterpreter_LWBRX(hCPU, opcode); PPCInterpreter_LWBRX(hCPU, opcode);
break; break;
@ -928,9 +913,6 @@ public:
case 567: case 567:
PPCInterpreter_LFSUX(hCPU, opcode); PPCInterpreter_LFSUX(hCPU, opcode);
break; break;
case 587: // 75 | OE
PPCInterpreter_MULHW_(hCPU, opcode); // OE is ignored for MULHW
break;
case 595: case 595:
PPCInterpreter_MFSR(hCPU, opcode); PPCInterpreter_MFSR(hCPU, opcode);
break; break;
@ -961,30 +943,15 @@ public:
case 663: case 663:
PPCInterpreter_STFSX(hCPU, opcode); PPCInterpreter_STFSX(hCPU, opcode);
break; break;
case 661:
PPCInterpreter_STSWX(hCPU, opcode);
break;
case 695: case 695:
PPCInterpreter_STFSUX(hCPU, opcode); PPCInterpreter_STFSUX(hCPU, opcode);
break; break;
case 712: // 200 | OE
PPCInterpreter_SUBFZEO(hCPU, opcode);
break;
case 714: // 202 | OE
PPCInterpreter_ADDZEO(hCPU, opcode);
break;
case 725: case 725:
PPCInterpreter_STSWI(hCPU, opcode); PPCInterpreter_STSWI(hCPU, opcode);
break; break;
case 727: case 727:
PPCInterpreter_STFDX(hCPU, opcode); PPCInterpreter_STFDX(hCPU, opcode);
break; break;
case 744: // 232 | OE
PPCInterpreter_SUBFMEO(hCPU, opcode);
break;
case 746: // 234 | OE
PPCInterpreter_ADDMEO(hCPU, opcode);
break;
case 747: case 747:
PPCInterpreter_MULLWO(hCPU, opcode); PPCInterpreter_MULLWO(hCPU, opcode);
break; break;
@ -1031,8 +998,10 @@ public:
PPCInterpreter_DCBZ(hCPU, opcode); PPCInterpreter_DCBZ(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [31] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [31] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
cemu_assert_unimplemented(); #ifdef CEMU_DEBUG_ASSERT
assert_dbg();
#endif
hCPU->instructionPointer += 4; hCPU->instructionPointer += 4;
break; break;
} }
@ -1115,7 +1084,7 @@ public:
case 57: case 57:
PPCInterpreter_PSQ_LU(hCPU, opcode); PPCInterpreter_PSQ_LU(hCPU, opcode);
break; break;
case 59: // opcode category case 59: //Opcode category
switch (PPC_getBits(opcode, 30, 5)) switch (PPC_getBits(opcode, 30, 5))
{ {
case 18: case 18:
@ -1146,9 +1115,8 @@ public:
PPCInterpreter_FNMADDS(hCPU, opcode); PPCInterpreter_FNMADDS(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [59] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [59] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
cemu_assert_unimplemented(); cemu_assert_unimplemented();
hCPU->instructionPointer += 4;
break; break;
} }
break; break;
@ -1227,19 +1195,18 @@ public:
case 583: case 583:
PPCInterpreter_MFFS(hCPU, opcode); PPCInterpreter_MFFS(hCPU, opcode);
break; break;
case 711: case 711: // IBM documentation has this wrong as 771?
PPCInterpreter_MTFSF(hCPU, opcode); PPCInterpreter_MTFSF(hCPU, opcode);
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [63] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer); debug_printf("Unknown execute %04X as [63] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
cemu_assert_unimplemented(); cemu_assert_unimplemented();
PPCInterpreter_nextInstruction(hCPU);
break; break;
} }
} }
break; break;
default: default:
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} at {:08x}\n", PPC_getBits(opcode, 5, 6), (unsigned int)hCPU->instructionPointer); debug_printf("Unknown execute %04X at %08X\n", PPC_getBits(opcode, 5, 6), (unsigned int)hCPU->instructionPointer);
cemu_assert_unimplemented(); cemu_assert_unimplemented();
} }
} }

View file

@ -50,9 +50,9 @@
#define CR_BIT_EQ 2 #define CR_BIT_EQ 2
#define CR_BIT_SO 3 #define CR_BIT_SO 3
#define XER_SO (1<<31) // summary overflow bit
#define XER_OV (1<<30) // overflow bit
#define XER_BIT_CA (29) // carry bit index. To accelerate frequent access, this bit is stored as a separate uint8 #define XER_BIT_CA (29) // carry bit index. To accelerate frequent access, this bit is stored as a separate uint8
#define XER_BIT_SO (31) // summary overflow, counterpart to CR SO
#define XER_BIT_OV (30)
// FPSCR // FPSCR
#define FPSCR_VXSNAN (1<<24) #define FPSCR_VXSNAN (1<<24)
@ -118,8 +118,7 @@
static inline void ppc_update_cr0(PPCInterpreter_t* hCPU, uint32 r) static inline void ppc_update_cr0(PPCInterpreter_t* hCPU, uint32 r)
{ {
cemu_assert_debug(hCPU->xer_so <= 1); hCPU->cr[CR_BIT_SO] = (hCPU->spr.XER&XER_SO) ? 1 : 0;
hCPU->cr[CR_BIT_SO] = hCPU->xer_so;
hCPU->cr[CR_BIT_LT] = ((r != 0) ? 1 : 0) & ((r & 0x80000000) ? 1 : 0); hCPU->cr[CR_BIT_LT] = ((r != 0) ? 1 : 0) & ((r & 0x80000000) ? 1 : 0);
hCPU->cr[CR_BIT_EQ] = (r == 0); hCPU->cr[CR_BIT_EQ] = (r == 0);
hCPU->cr[CR_BIT_GT] = hCPU->cr[CR_BIT_EQ] ^ hCPU->cr[CR_BIT_LT] ^ 1; // this works because EQ and LT can never be set at the same time. So the only case where GT becomes 1 is when LT=0 and EQ=0 hCPU->cr[CR_BIT_GT] = hCPU->cr[CR_BIT_EQ] ^ hCPU->cr[CR_BIT_LT] ^ 1; // this works because EQ and LT can never be set at the same time. So the only case where GT becomes 1 is when LT=0 and EQ=0
@ -191,8 +190,8 @@ inline double roundTo25BitAccuracy(double d)
return *(double*)&v; return *(double*)&v;
} }
ATTR_MS_ABI double fres_espresso(double input); double fres_espresso(double input);
ATTR_MS_ABI double frsqrte_espresso(double input); double frsqrte_espresso(double input);
void fcmpu_espresso(PPCInterpreter_t* hCPU, int crfD, double a, double b); void fcmpu_espresso(PPCInterpreter_t* hCPU, int crfD, double a, double b);

View file

@ -31,7 +31,7 @@ static void PPCInterpreter_STW(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STWU(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STWU(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS; int rA, rS;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm); PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
ppcItpCtrl::ppcMem_writeDataU32(hCPU, hCPU->gpr[rA] + imm, hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU32(hCPU, hCPU->gpr[rA] + imm, hCPU->gpr[rS]);
@ -42,7 +42,7 @@ static void PPCInterpreter_STWU(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STWX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STWX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], hCPU->gpr[rS]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -85,8 +85,7 @@ static void PPCInterpreter_STWCX(PPCInterpreter_t* hCPU, uint32 Opcode)
ppc_setCRBit(hCPU, CR_BIT_GT, 0); ppc_setCRBit(hCPU, CR_BIT_GT, 0);
ppc_setCRBit(hCPU, CR_BIT_EQ, 1); ppc_setCRBit(hCPU, CR_BIT_EQ, 1);
} }
cemu_assert_debug(hCPU->xer_so <= 1); ppc_setCRBit(hCPU, CR_BIT_SO, (hCPU->spr.XER&XER_SO) != 0 ? 1 : 0);
ppc_setCRBit(hCPU, CR_BIT_SO, hCPU->xer_so);
// remove reservation // remove reservation
hCPU->reservedMemAddr = 0; hCPU->reservedMemAddr = 0;
hCPU->reservedMemValue = 0; hCPU->reservedMemValue = 0;
@ -103,7 +102,7 @@ static void PPCInterpreter_STWCX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STWUX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STWUX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], hCPU->gpr[rS]);
if (rA) if (rA)
@ -113,7 +112,7 @@ static void PPCInterpreter_STWUX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STWBRX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STWBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], _swapEndianU32(hCPU->gpr[rS])); ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], _swapEndianU32(hCPU->gpr[rS]));
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -121,7 +120,7 @@ static void PPCInterpreter_STWBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STMW(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STMW(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rS, rA; int rS, rA;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm); PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
uint32 ea = (rA ? hCPU->gpr[rA] : 0) + imm; uint32 ea = (rA ? hCPU->gpr[rA] : 0) + imm;
@ -136,7 +135,7 @@ static void PPCInterpreter_STMW(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STH(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STH(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS; int rA, rS;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm); PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint16)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint16)hCPU->gpr[rS]);
@ -145,7 +144,7 @@ static void PPCInterpreter_STH(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STHU(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STHU(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS; int rA, rS;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm); PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint16)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint16)hCPU->gpr[rS]);
@ -156,7 +155,7 @@ static void PPCInterpreter_STHU(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STHX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STHX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint16)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint16)hCPU->gpr[rS]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -164,7 +163,7 @@ static void PPCInterpreter_STHX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STHUX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STHUX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint16)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint16)hCPU->gpr[rS]);
if (rA) if (rA)
@ -174,7 +173,7 @@ static void PPCInterpreter_STHUX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STHBRX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STHBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], _swapEndianU16((uint16)hCPU->gpr[rS])); ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], _swapEndianU16((uint16)hCPU->gpr[rS]));
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -182,7 +181,7 @@ static void PPCInterpreter_STHBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STB(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STB(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS; int rA, rS;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm); PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
ppcItpCtrl::ppcMem_writeDataU8(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint8)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU8(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint8)hCPU->gpr[rS]);
@ -191,7 +190,7 @@ static void PPCInterpreter_STB(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STBU(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STBU(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS; int rA, rS;
uint32 imm; uint32 imm;
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm); PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
ppcItpCtrl::ppcMem_writeDataU8(hCPU, hCPU->gpr[rA] + imm, (uint8)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU8(hCPU, hCPU->gpr[rA] + imm, (uint8)hCPU->gpr[rS]);
@ -201,7 +200,7 @@ static void PPCInterpreter_STBU(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STBX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STBX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU8(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint8)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU8(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint8)hCPU->gpr[rS]);
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
@ -209,7 +208,7 @@ static void PPCInterpreter_STBX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STBUX(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STBUX(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, rB; int rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB); PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
ppcItpCtrl::ppcMem_writeDataU8(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint8)hCPU->gpr[rS]); ppcItpCtrl::ppcMem_writeDataU8(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], (uint8)hCPU->gpr[rS]);
if (rA) if (rA)
@ -219,7 +218,7 @@ static void PPCInterpreter_STBUX(PPCInterpreter_t* hCPU, uint32 Opcode)
static void PPCInterpreter_STSWI(PPCInterpreter_t* hCPU, uint32 Opcode) static void PPCInterpreter_STSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
sint32 rA, rS, nb; int rA, rS, nb;
PPC_OPC_TEMPL_X(Opcode, rS, rA, nb); PPC_OPC_TEMPL_X(Opcode, rS, rA, nb);
if (nb == 0) nb = 32; if (nb == 0) nb = 32;
uint32 ea = rA ? hCPU->gpr[rA] : 0; uint32 ea = rA ? hCPU->gpr[rA] : 0;
@ -229,39 +228,7 @@ static void PPCInterpreter_STSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
if (i == 0) if (i == 0)
{ {
r = rS < 32 ? hCPU->gpr[rS] : 0; // what happens if rS is out of bounds? r = hCPU->gpr[rS];
rS++;
rS %= 32;
i = 4;
}
ppcItpCtrl::ppcMem_writeDataU8(hCPU, ea, (r >> 24));
r <<= 8;
ea++;
i--;
nb--;
}
PPCInterpreter_nextInstruction(hCPU);
}
static void PPCInterpreter_STSWX(PPCInterpreter_t* hCPU, uint32 Opcode)
{
sint32 rA, rS, rB;
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
sint32 nb = hCPU->spr.XER&0x7F;
if (nb == 0)
{
PPCInterpreter_nextInstruction(hCPU);
return;
}
uint32 ea = rA ? hCPU->gpr[rA] : 0;
ea += hCPU->gpr[rB];
uint32 r = 0;
int i = 0;
while (nb > 0)
{
if (i == 0)
{
r = rS < 32 ? hCPU->gpr[rS] : 0; // what happens if rS is out of bounds?
rS++; rS++;
rS %= 32; rS %= 32;
i = 4; i = 4;
@ -492,51 +459,8 @@ static void PPCInterpreter_LSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
PPC_OPC_TEMPL_X(Opcode, rD, rA, nb); PPC_OPC_TEMPL_X(Opcode, rD, rA, nb);
if (nb == 0) if (nb == 0)
nb = 32; nb = 32;
uint32 ea = rA ? hCPU->gpr[rA] : 0;
uint32 r = 0;
int i = 4;
uint8 v;
while (nb>0)
{
if (i == 0)
{
i = 4;
if(rD < 32)
hCPU->gpr[rD] = r;
rD++;
rD %= 32;
r = 0;
}
v = ppcItpCtrl::ppcMem_readDataU8(hCPU, ea);
r <<= 8;
r |= v;
ea++;
i--;
nb--;
}
while (i)
{
r <<= 8;
i--;
}
if(rD < 32)
hCPU->gpr[rD] = r;
PPCInterpreter_nextInstruction(hCPU);
}
static void PPCInterpreter_LSWX(PPCInterpreter_t* hCPU, uint32 Opcode)
{
sint32 rA, rD, rB;
PPC_OPC_TEMPL_X(Opcode, rD, rA, rB);
// byte count comes from XER
uint32 nb = (hCPU->spr.XER>>0)&0x7F;
if (nb == 0)
{
PPCInterpreter_nextInstruction(hCPU);
return; // no-op
}
uint32 ea = rA ? hCPU->gpr[rA] : 0; uint32 ea = rA ? hCPU->gpr[rA] : 0;
ea += hCPU->gpr[rB];
uint32 r = 0; uint32 r = 0;
int i = 4; int i = 4;
uint8 v; uint8 v;
@ -545,8 +469,7 @@ static void PPCInterpreter_LSWX(PPCInterpreter_t* hCPU, uint32 Opcode)
if (i == 0) if (i == 0)
{ {
i = 4; i = 4;
if(rD < 32) hCPU->gpr[rD] = r;
hCPU->gpr[rD] = r;
rD++; rD++;
rD %= 32; rD %= 32;
r = 0; r = 0;
@ -563,8 +486,7 @@ static void PPCInterpreter_LSWX(PPCInterpreter_t* hCPU, uint32 Opcode)
r <<= 8; r <<= 8;
i--; i--;
} }
if(rD < 32) hCPU->gpr[rD] = r;
hCPU->gpr[rD] = r;
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }

View file

@ -6,6 +6,7 @@
thread_local PPCInterpreter_t* ppcInterpreterCurrentInstance; thread_local PPCInterpreter_t* ppcInterpreterCurrentInstance;
// main thread instruction counter and timing // main thread instruction counter and timing
volatile uint64 ppcMainThreadCycleCounter = 0;
uint64 ppcMainThreadDECCycleValue = 0; // value that was set to dec register uint64 ppcMainThreadDECCycleValue = 0; // value that was set to dec register
uint64 ppcMainThreadDECCycleStart = 0; // at which cycle the dec register was set, if == 0 -> dec is 0 uint64 ppcMainThreadDECCycleStart = 0; // at which cycle the dec register was set, if == 0 -> dec is 0
uint64 ppcCyclesSince2000 = 0; uint64 ppcCyclesSince2000 = 0;
@ -28,16 +29,11 @@ PPCInterpreter_t* PPCInterpreter_createInstance(unsigned int Entrypoint)
return pData; return pData;
} }
TLS_WORKAROUND_NOINLINE PPCInterpreter_t* PPCInterpreter_getCurrentInstance() PPCInterpreter_t* PPCInterpreter_getCurrentInstance()
{ {
return ppcInterpreterCurrentInstance; return ppcInterpreterCurrentInstance;
} }
TLS_WORKAROUND_NOINLINE void PPCInterpreter_setCurrentInstance(PPCInterpreter_t* hCPU)
{
ppcInterpreterCurrentInstance = hCPU;
}
uint64 PPCInterpreter_getMainCoreCycleCounter() uint64 PPCInterpreter_getMainCoreCycleCounter()
{ {
return PPCTimer_getFromRDTSC(); return PPCTimer_getFromRDTSC();
@ -63,25 +59,16 @@ void PPCInterpreter_setDEC(PPCInterpreter_t* hCPU, uint32 newValue)
uint32 PPCInterpreter_getXER(PPCInterpreter_t* hCPU) uint32 PPCInterpreter_getXER(PPCInterpreter_t* hCPU)
{ {
uint32 xerValue = hCPU->spr.XER; uint32 xerValue = hCPU->spr.XER;
xerValue &= ~(1 << XER_BIT_CA); xerValue &= ~(1<<XER_BIT_CA);
xerValue &= ~(1 << XER_BIT_SO); if( hCPU->xer_ca )
xerValue &= ~(1 << XER_BIT_OV); xerValue |= (1<<XER_BIT_CA);
if (hCPU->xer_ca)
xerValue |= (1 << XER_BIT_CA);
if (hCPU->xer_so)
xerValue |= (1 << XER_BIT_SO);
if (hCPU->xer_ov)
xerValue |= (1 << XER_BIT_OV);
return xerValue; return xerValue;
} }
void PPCInterpreter_setXER(PPCInterpreter_t* hCPU, uint32 v) void PPCInterpreter_setXER(PPCInterpreter_t* hCPU, uint32 v)
{ {
const uint32 XER_MASK = 0xE0FFFFFF; // some bits are masked out. Figure out which ones exactly hCPU->spr.XER = v;
hCPU->spr.XER = v & XER_MASK; hCPU->xer_ca = (v>>XER_BIT_CA)&1;
hCPU->xer_ca = (v >> XER_BIT_CA) & 1;
hCPU->xer_so = (v >> XER_BIT_SO) & 1;
hCPU->xer_ov = (v >> XER_BIT_OV) & 1;
} }
uint32 PPCInterpreter_getCoreIndex(PPCInterpreter_t* hCPU) uint32 PPCInterpreter_getCoreIndex(PPCInterpreter_t* hCPU)
@ -91,25 +78,24 @@ uint32 PPCInterpreter_getCoreIndex(PPCInterpreter_t* hCPU)
uint32 PPCInterpreter_getCurrentCoreIndex() uint32 PPCInterpreter_getCurrentCoreIndex()
{ {
return PPCInterpreter_getCurrentInstance()->spr.UPIR; return ppcInterpreterCurrentInstance->spr.UPIR;
}; };
uint8* PPCInterpreterGetStackPointer() uint8* PPCInterpreterGetStackPointer()
{ {
return memory_getPointerFromVirtualOffset(PPCInterpreter_getCurrentInstance()->gpr[1]); return memory_getPointerFromVirtualOffset(ppcInterpreterCurrentInstance->gpr[1]);
} }
uint8* PPCInterpreter_PushAndReturnStackPointer(sint32 offset) uint8* PPCInterpreterGetAndModifyStackPointer(sint32 offset)
{ {
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); uint8* result = memory_getPointerFromVirtualOffset(ppcInterpreterCurrentInstance->gpr[1] - offset);
uint8* result = memory_getPointerFromVirtualOffset(hCPU->gpr[1] - offset); ppcInterpreterCurrentInstance->gpr[1] -= offset;
hCPU->gpr[1] -= offset;
return result; return result;
} }
void PPCInterpreterModifyStackPointer(sint32 offset) void PPCInterpreterModifyStackPointer(sint32 offset)
{ {
PPCInterpreter_getCurrentInstance()->gpr[1] -= offset; ppcInterpreterCurrentInstance->gpr[1] -= offset;
} }
uint32 RPLLoader_MakePPCCallable(void(*ppcCallableExport)(PPCInterpreter_t* hCPU)); uint32 RPLLoader_MakePPCCallable(void(*ppcCallableExport)(PPCInterpreter_t* hCPU));

View file

@ -5,6 +5,7 @@
#include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h" #include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h"
#include "../Recompiler/PPCRecompiler.h" #include "../Recompiler/PPCRecompiler.h"
#include "../Recompiler/PPCRecompilerX64.h"
#include <float.h> #include <float.h>
#include "Cafe/HW/Latte/Core/LatteBufferCache.h" #include "Cafe/HW/Latte/Core/LatteBufferCache.h"
@ -93,6 +94,7 @@ void PPCInterpreter_MTCRF(PPCInterpreter_t* hCPU, uint32 Opcode)
{ {
// frequently used by GCC compiled code (e.g. SM64 port) // frequently used by GCC compiled code (e.g. SM64 port)
// tested // tested
uint32 rS; uint32 rS;
uint32 crfMask; uint32 crfMask;
PPC_OPC_TEMPL_XFX(Opcode, rS, crfMask); PPC_OPC_TEMPL_XFX(Opcode, rS, crfMask);

View file

@ -68,8 +68,6 @@ static void PPCInterpreter_TW(PPCInterpreter_t* hCPU, uint32 opcode)
PPC_OPC_TEMPL_X(opcode, to, rA, rB); PPC_OPC_TEMPL_X(opcode, to, rA, rB);
cemu_assert_debug(to == 0); cemu_assert_debug(to == 0);
if(to != 0)
PPCInterpreter_nextInstruction(hCPU);
if (rA == DEBUGGER_BP_T_DEBUGGER) if (rA == DEBUGGER_BP_T_DEBUGGER)
debugger_enterTW(hCPU); debugger_enterTW(hCPU);

View file

@ -5,28 +5,8 @@ struct PPCCoreCallbackData_t
{ {
sint32 gprCount = 0; sint32 gprCount = 0;
sint32 floatCount = 0; sint32 floatCount = 0;
sint32 stackCount = 0;
}; };
inline void _PPCCoreCallback_writeGPRArg(PPCCoreCallbackData_t& data, PPCInterpreter_t* hCPU, uint32 value)
{
if (data.gprCount < 8)
{
hCPU->gpr[3 + data.gprCount] = value;
data.gprCount++;
}
else
{
uint32 stackOffset = 8 + data.stackCount * 4;
// PPCCore_executeCallbackInternal does -16*4 to save the current stack area
stackOffset -= 16 * 4;
memory_writeU32(hCPU->gpr[1] + stackOffset, value);
data.stackCount++;
}
}
// callback functions // callback functions
inline uint32 PPCCoreCallback(MPTR function, const PPCCoreCallbackData_t& data) inline uint32 PPCCoreCallback(MPTR function, const PPCCoreCallbackData_t& data)
{ {
@ -36,21 +16,22 @@ inline uint32 PPCCoreCallback(MPTR function, const PPCCoreCallbackData_t& data)
template <typename T, typename... TArgs> template <typename T, typename... TArgs>
uint32 PPCCoreCallback(MPTR function, PPCCoreCallbackData_t& data, T currentArg, TArgs... args) uint32 PPCCoreCallback(MPTR function, PPCCoreCallbackData_t& data, T currentArg, TArgs... args)
{ {
// TODO float arguments on stack cemu_assert_debug(data.gprCount <= 8);
cemu_assert_debug(data.floatCount < 8); cemu_assert_debug(data.floatCount <= 8);
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
if constexpr (std::is_pointer_v<T>) if constexpr (std::is_pointer_v<T>)
{ {
_PPCCoreCallback_writeGPRArg(data, hCPU, MEMPTR(currentArg).GetMPTR()); ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = MEMPTR(currentArg).GetMPTR();
data.gprCount++;
} }
else if constexpr (std::is_base_of_v<MEMPTRBase, std::remove_reference_t<T>>) else if constexpr (std::is_base_of_v<MEMPTRBase, std::remove_reference_t<T>>)
{ {
_PPCCoreCallback_writeGPRArg(data, hCPU, currentArg.GetMPTR()); ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = currentArg.GetMPTR();
data.gprCount++;
} }
else if constexpr (std::is_reference_v<T>) else if constexpr (std::is_reference_v<T>)
{ {
_PPCCoreCallback_writeGPRArg(data, hCPU, MEMPTR(&currentArg).GetMPTR()); ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = MEMPTR(&currentArg).GetMPTR();
data.gprCount++;
} }
else if constexpr(std::is_enum_v<T>) else if constexpr(std::is_enum_v<T>)
{ {
@ -59,19 +40,20 @@ uint32 PPCCoreCallback(MPTR function, PPCCoreCallbackData_t& data, T currentArg,
} }
else if constexpr (std::is_floating_point_v<T>) else if constexpr (std::is_floating_point_v<T>)
{ {
hCPU->fpr[1 + data.floatCount].fpr = (double)currentArg; ppcInterpreterCurrentInstance->fpr[1 + data.floatCount].fpr = (double)currentArg;
data.floatCount++; data.floatCount++;
} }
else if constexpr (std::is_integral_v<T> && sizeof(T) == sizeof(uint64)) else if constexpr (std::is_integral_v<T> && sizeof(T) == sizeof(uint64))
{ {
hCPU->gpr[3 + data.gprCount] = (uint32)(currentArg >> 32); // high ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = (uint32)(currentArg >> 32); // high
hCPU->gpr[3 + data.gprCount + 1] = (uint32)currentArg; // low ppcInterpreterCurrentInstance->gpr[3 + data.gprCount + 1] = (uint32)currentArg; // low
data.gprCount += 2; data.gprCount += 2;
} }
else else
{ {
_PPCCoreCallback_writeGPRArg(data, hCPU, (uint32)currentArg); ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = (uint32)currentArg;
data.gprCount++;
} }
return PPCCoreCallback(function, data, args...); return PPCCoreCallback(function, data, args...);

View file

@ -11,24 +11,21 @@ uint32 ppcThreadQuantum = 45000; // execute 45000 instructions before thread res
void PPCInterpreter_relinquishTimeslice() void PPCInterpreter_relinquishTimeslice()
{ {
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); if( ppcInterpreterCurrentInstance->remainingCycles >= 0 )
if( hCPU->remainingCycles >= 0 )
{ {
hCPU->skippedCycles = hCPU->remainingCycles + 1; ppcInterpreterCurrentInstance->skippedCycles = ppcInterpreterCurrentInstance->remainingCycles + 1;
hCPU->remainingCycles = -1; ppcInterpreterCurrentInstance->remainingCycles = -1;
} }
} }
void PPCCore_boostQuantum(sint32 numCycles) void PPCCore_boostQuantum(sint32 numCycles)
{ {
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); ppcInterpreterCurrentInstance->remainingCycles += numCycles;
hCPU->remainingCycles += numCycles;
} }
void PPCCore_deboostQuantum(sint32 numCycles) void PPCCore_deboostQuantum(sint32 numCycles)
{ {
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); ppcInterpreterCurrentInstance->remainingCycles -= numCycles;
hCPU->remainingCycles -= numCycles;
} }
namespace coreinit namespace coreinit
@ -39,7 +36,7 @@ namespace coreinit
void PPCCore_switchToScheduler() void PPCCore_switchToScheduler()
{ {
cemu_assert_debug(__OSHasSchedulerLock() == false); // scheduler lock must not be hold past thread time slice cemu_assert_debug(__OSHasSchedulerLock() == false); // scheduler lock must not be hold past thread time slice
cemu_assert_debug(PPCInterpreter_getCurrentInstance()->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600); cemu_assert_debug(ppcInterpreterCurrentInstance->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600);
__OSLockScheduler(); __OSLockScheduler();
coreinit::__OSThreadSwitchToNext(); coreinit::__OSThreadSwitchToNext();
__OSUnlockScheduler(); __OSUnlockScheduler();
@ -48,7 +45,7 @@ void PPCCore_switchToScheduler()
void PPCCore_switchToSchedulerWithLock() void PPCCore_switchToSchedulerWithLock()
{ {
cemu_assert_debug(__OSHasSchedulerLock() == true); // scheduler lock must be hold cemu_assert_debug(__OSHasSchedulerLock() == true); // scheduler lock must be hold
cemu_assert_debug(PPCInterpreter_getCurrentInstance()->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600); cemu_assert_debug(ppcInterpreterCurrentInstance->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600);
coreinit::__OSThreadSwitchToNext(); coreinit::__OSThreadSwitchToNext();
} }
@ -61,7 +58,7 @@ void _PPCCore_callbackExit(PPCInterpreter_t* hCPU)
PPCInterpreter_t* PPCCore_executeCallbackInternal(uint32 functionMPTR) PPCInterpreter_t* PPCCore_executeCallbackInternal(uint32 functionMPTR)
{ {
cemu_assert_debug(functionMPTR != 0); cemu_assert_debug(functionMPTR != 0);
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); PPCInterpreter_t* hCPU = ppcInterpreterCurrentInstance;
// remember LR and instruction pointer // remember LR and instruction pointer
uint32 lr = hCPU->spr.LR; uint32 lr = hCPU->spr.LR;
uint32 ip = hCPU->instructionPointer; uint32 ip = hCPU->instructionPointer;

View file

@ -220,7 +220,7 @@ void PPCCoreLLE_startSingleCoreScheduler(uint32 entrypoint)
for (uint32 coreIndex = 0; coreIndex < 3; coreIndex++) for (uint32 coreIndex = 0; coreIndex < 3; coreIndex++)
{ {
PPCInterpreter_t* hCPU = cpuContext->cores+coreIndex; PPCInterpreter_t* hCPU = cpuContext->cores+coreIndex;
PPCInterpreter_setCurrentInstance(hCPU); ppcInterpreterCurrentInstance = hCPU;
if (coreIndex == 1) if (coreIndex == 1)
{ {
// check SCR core 1 enable bit // check SCR core 1 enable bit

View file

@ -49,8 +49,8 @@ struct PPCInterpreter_t
uint32 fpscr; uint32 fpscr;
uint8 cr[32]; // 0 -> bit not set, 1 -> bit set (upper 7 bits of each byte must always be zero) (cr0 starts at index 0, cr1 at index 4 ..) uint8 cr[32]; // 0 -> bit not set, 1 -> bit set (upper 7 bits of each byte must always be zero) (cr0 starts at index 0, cr1 at index 4 ..)
uint8 xer_ca; // carry from xer uint8 xer_ca; // carry from xer
uint8 xer_so; uint8 LSQE;
uint8 xer_ov; uint8 PSE;
// thread remaining cycles // thread remaining cycles
sint32 remainingCycles; // if this value goes below zero, the next thread is scheduled sint32 remainingCycles; // if this value goes below zero, the next thread is scheduled
sint32 skippedCycles; // number of skipped cycles sint32 skippedCycles; // number of skipped cycles
@ -67,8 +67,7 @@ struct PPCInterpreter_t
uint32 reservedMemValue; uint32 reservedMemValue;
// temporary storage for recompiler // temporary storage for recompiler
FPR_t temporaryFPR[8]; FPR_t temporaryFPR[8];
uint32 temporaryGPR[4]; // deprecated, refactor backend dependency on this away uint32 temporaryGPR[4];
uint32 temporaryGPR_reg[4];
// values below this are not used by Cafe OS usermode // values below this are not used by Cafe OS usermode
struct struct
{ {
@ -93,8 +92,6 @@ struct PPCInterpreter_t
uint32 sr[16]; uint32 sr[16];
uint32 sdr1; uint32 sdr1;
}sprExtended; }sprExtended;
uint8 LSQE;
uint8 PSE;
// global CPU values // global CPU values
PPCInterpreterGlobal_t* global; PPCInterpreterGlobal_t* global;
// interpreter control // interpreter control
@ -152,7 +149,6 @@ static uint64 PPCInterpreter_getCallParamU64(PPCInterpreter_t* hCPU, uint32 inde
PPCInterpreter_t* PPCInterpreter_createInstance(unsigned int Entrypoint); PPCInterpreter_t* PPCInterpreter_createInstance(unsigned int Entrypoint);
PPCInterpreter_t* PPCInterpreter_getCurrentInstance(); PPCInterpreter_t* PPCInterpreter_getCurrentInstance();
void PPCInterpreter_setCurrentInstance(PPCInterpreter_t* hCPU);
uint64 PPCInterpreter_getMainCoreCycleCounter(); uint64 PPCInterpreter_getMainCoreCycleCounter();
@ -196,6 +192,7 @@ uint32 PPCInterpreter_getCurrentCoreIndex();
void PPCInterpreter_setDEC(PPCInterpreter_t* hCPU, uint32 newValue); void PPCInterpreter_setDEC(PPCInterpreter_t* hCPU, uint32 newValue);
// timing for main processor // timing for main processor
extern volatile uint64 ppcMainThreadCycleCounter;
extern uint64 ppcCyclesSince2000; // on init this is set to the cycles that passed since 1.1.2000 extern uint64 ppcCyclesSince2000; // on init this is set to the cycles that passed since 1.1.2000
extern uint64 ppcCyclesSince2000TimerClock; // on init this is set to the cycles that passed since 1.1.2000 / 20 extern uint64 ppcCyclesSince2000TimerClock; // on init this is set to the cycles that passed since 1.1.2000 / 20
extern uint64 ppcCyclesSince2000_UTC; extern uint64 ppcCyclesSince2000_UTC;
@ -216,7 +213,8 @@ void PPCTimer_start();
// core info and control // core info and control
extern uint32 ppcThreadQuantum; extern uint32 ppcThreadQuantum;
uint8* PPCInterpreter_PushAndReturnStackPointer(sint32 offset); extern thread_local PPCInterpreter_t *ppcInterpreterCurrentInstance;
uint8* PPCInterpreterGetAndModifyStackPointer(sint32 offset);
uint8* PPCInterpreterGetStackPointer(); uint8* PPCInterpreterGetStackPointer();
void PPCInterpreterModifyStackPointer(sint32 offset); void PPCInterpreterModifyStackPointer(sint32 offset);
@ -230,10 +228,10 @@ static inline float flushDenormalToZero(float f)
// HLE interface // HLE interface
using HLECALL = void(*)(PPCInterpreter_t*); typedef void(*HLECALL)(PPCInterpreter_t* hCPU);
using HLEIDX = sint32;
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName); typedef sint32 HLEIDX;
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall);
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex); HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);
// HLE scheduler // HLE scheduler

View file

@ -1,4 +1,5 @@
#include "Cafe/HW/Espresso/Const.h" #include "Cafe/HW/Espresso/Const.h"
#include "asm/x64util.h"
#include "config/ActiveSettings.h" #include "config/ActiveSettings.h"
#include "util/helpers/fspinlock.h" #include "util/helpers/fspinlock.h"
#include "util/highresolutiontimer/HighResolutionTimer.h" #include "util/highresolutiontimer/HighResolutionTimer.h"

View file

@ -1,18 +0,0 @@
#pragma once
#include "HW/Espresso/Recompiler/IML/IMLInstruction.h"
#include "../PPCRecompiler.h"
bool PPCRecompiler_generateAArch64Code(struct PPCRecFunction_t* PPCRecFunction, struct ppcImlGenContext_t* ppcImlGenContext);
void PPCRecompiler_cleanupAArch64Code(void* code, size_t size);
void PPCRecompilerAArch64Gen_generateRecompilerInterfaceFunctions();
// architecture specific constants
namespace IMLArchAArch64
{
static constexpr int PHYSREG_GPR_BASE = 0;
static constexpr int PHYSREG_GPR_COUNT = 25;
static constexpr int PHYSREG_FPR_BASE = PHYSREG_GPR_COUNT;
static constexpr int PHYSREG_FPR_COUNT = 31;
}; // namespace IMLArchAArch64

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