mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-03 13:31:18 +12:00
Compare commits
320 commits
Author | SHA1 | Date | |
---|---|---|---|
|
e68c31e5fb | ||
|
35ecfa3f54 | ||
|
6c392d5a22 | ||
|
9fb3c76b76 | ||
|
13ccf9a160 | ||
|
7db2b77983 | ||
|
0a121c97c7 | ||
|
e91740cf29 | ||
|
4f4c9594ac | ||
|
5a4731f919 | ||
|
522b5ef260 | ||
|
057ef4598e | ||
|
4f4412b334 | ||
|
00ff5549d9 | ||
|
c8ffff8f41 | ||
|
2f02fda9ea | ||
|
da98aa4176 | ||
|
95dc590d2c | ||
|
f3fe6f3455 | ||
|
2eec6b44c3 | ||
|
3eff2d4a60 | ||
|
d427b59019 | ||
|
a184a04e56 | ||
|
162fdabb9d | ||
|
c8045f7f04 | ||
|
6df3e1742e | ||
|
152b790242 | ||
|
02616bf6be | ||
|
7168d20cde | ||
|
783d88a892 | ||
|
28ea70b6d8 | ||
|
96765e4ac6 | ||
|
111637a9fd | ||
|
bed5fdb195 | ||
|
996539fce8 | ||
|
05617a332b | ||
|
caef34f2ff | ||
|
f801fc1fe8 | ||
|
61484598fc | ||
|
081ebead5f | ||
|
d13dab0fd8 | ||
|
ba09daf328 | ||
|
557aff4024 | ||
|
de542410c2 | ||
|
33d5c6d490 | ||
|
352a918494 | ||
|
d083fc0470 | ||
|
fa7ae84314 | ||
|
00099c5ecc | ||
|
e6a64aadda | ||
|
a5f3558b79 | ||
|
b089ae5b32 | ||
|
06233e3462 | ||
|
4972381edc | ||
|
cd6eb1097b | ||
|
c4eab08f30 | ||
|
57ff99ce53 | ||
|
8b5cafa98e | ||
|
186e92221a | ||
|
31d2db6f78 | ||
|
ebb5ab53e2 | ||
|
a6fb0a48eb | ||
|
ec2d7c086a | ||
|
c714e8cb6b | ||
|
e834515f43 | ||
|
4f9eea07e0 | ||
|
372c314f06 | ||
|
5bd253a1f8 | ||
|
4ac65159ef | ||
|
eab1b24320 | ||
|
07cd402531 | ||
|
0a59085021 | ||
|
8dd809d725 | ||
|
1923b7a7c4 | ||
|
f61539a262 | ||
|
92021db230 | ||
|
4b792aa4d2 | ||
|
1e30d72658 | ||
|
2b0cbf7f6b | ||
|
3738ccd2e6 | ||
|
b53b223ba9 | ||
|
6aaad1eb83 | ||
|
adab729f43 | ||
|
dd0af0a56f | ||
|
934cb54605 | ||
|
356cf0e5e0 | ||
|
e2d0871ca3 | ||
|
40d9664d1c | ||
|
eca7374567 | ||
|
80a6057512 | ||
|
0735237686 | ||
|
90eb2e01f4 | ||
|
409f12b13a | ||
|
7b513f1744 | ||
|
c3e29fb619 | ||
|
2065ac5f63 | ||
|
269d5b9aab | ||
|
6f9f3d52ea | ||
|
719c631f13 | ||
|
66658351c1 | ||
|
a5717e1b11 | ||
|
ca2e0a7c31 | ||
|
2e829479d9 | ||
|
4ac1ab162a | ||
|
813f9148b1 | ||
|
9941e00b54 | ||
|
1c49a8a1ba | ||
|
47001ad233 | ||
|
459fd5d9bb | ||
|
63e1289bb5 | ||
|
f9a4b2dbb1 | ||
|
d6575455ee | ||
|
3acd0c4f2c | ||
|
6dc73f5d79 | ||
|
8508c62540 | ||
|
adffd53dbd | ||
|
a05bdb172d | ||
|
1a4d9660e7 | ||
|
ba54d1540c | ||
|
0d8fd7c0dc | ||
|
b06990607d | ||
|
9a53b19403 | ||
|
03484d2146 | ||
|
1234e2c118 | ||
|
d7f39aab05 | ||
|
dc9d99b03b | ||
|
573c98b2f8 | ||
|
9e53c1ce27 | ||
|
958137a301 | ||
|
294a6de779 | ||
|
2843da4479 | ||
|
b0bab273e2 | ||
|
c49296acdc | ||
|
a6d8c0fb9f | ||
|
e551f8f524 | ||
|
f52970c822 | ||
|
e02cc42d67 | ||
|
9812a47cb1 | ||
|
7fd532436d | ||
|
598298cb3d | ||
|
54e695a6e8 | ||
|
bf2208145b | ||
|
b52b676413 | ||
|
2129644781 | ||
|
d81eb952a4 | ||
|
1575866eca | ||
|
517e68fe57 | ||
|
593da5ed79 | ||
|
c73fa3761c | ||
|
5328e9eb10 | ||
|
47f1dcf996 | ||
|
252429933f | ||
|
026d547dcc | ||
|
f1685eab66 | ||
|
4b9c7c0d30 | ||
|
e65abf4898 | ||
|
a1c1a608d7 | ||
|
64232ffdbd | ||
|
7522c8470e | ||
|
9d366937cd | ||
|
5209677f2f | ||
|
64b0b85ed5 | ||
|
aefbb918be | ||
|
93b58ae6f7 | ||
|
f3d20832c1 | ||
|
d4c2c3d209 | ||
|
1672f969bb | ||
|
6772b1993f | ||
|
16070458ed | ||
|
5f825a1fa8 | ||
|
d33337d539 | ||
|
f576269ed0 | ||
|
da8fd5b7c7 | ||
|
1ee9d5c78c | ||
|
aadd2f4a1a | ||
|
149fe10a4e | ||
|
917ea2ef23 | ||
|
b048a1fd9e | ||
|
a059338890 | ||
|
523a1652df | ||
|
c913a59c7a | ||
|
964d2acb44 | ||
|
a115921b43 | ||
|
eb1983daa6 | ||
|
8fe69cd0fb | ||
|
41fe598e33 | ||
|
8e8431113a | ||
|
1c6b209692 | ||
|
84e78088fb | ||
|
13b90874f9 | ||
|
cf41c3b136 | ||
|
97d8cf4ba3 | ||
|
b2a6cccc89 | ||
|
10d553e1c9 | ||
|
3f8722f0a6 | ||
|
065fb7eb58 | ||
|
7d6d417354 | ||
|
bd13d4bdc3 | ||
|
bf37a8281e | ||
|
dd3ed56509 | ||
|
70afe3a033 | ||
|
dc480ac00b | ||
|
f28043e0e9 | ||
|
a744670486 | ||
|
48d2a8371b | ||
|
91a010fbdd | ||
|
a16c37f0c5 | ||
|
041f29a914 | ||
|
1b5c885621 | ||
|
c11d83e9d8 | ||
|
379950d185 | ||
|
e7c6862e19 | ||
|
1c73dc9e1b | ||
|
c038e758ae | ||
|
b2be3c13df | ||
|
fdf239929f | ||
|
5be98da0ac | ||
|
efbbb817fe | ||
|
989e2b8c8c | ||
|
e2f9725719 | ||
|
ee36992bd6 | ||
|
10c78eccce | ||
|
6ea42d958c | ||
|
9c28a728e4 | ||
|
d5a8530246 | ||
|
84cad8b280 | ||
|
391533dbe5 | ||
|
bac1ac3b49 | ||
|
d45c2fa6d1 | ||
|
12eda10387 | ||
|
33a74c2035 | ||
|
7b635e7eb8 | ||
|
9b30be0258 | ||
|
efbf712305 | ||
|
74e8d205b0 | ||
|
fde7230191 | ||
|
075eac626b | ||
|
85141f17f9 | ||
|
5c0d5a54ac | ||
|
51072b510c | ||
|
3e467e220e | ||
|
fa8bab2f39 | ||
|
60adc38205 | ||
|
b0b2c25762 | ||
|
5230fcab37 | ||
|
4f3d4624f5 | ||
|
111e383d1b | ||
|
fa4ad9b8c1 | ||
|
4b7d2f88ae | ||
|
4d148b3696 | ||
|
241915e1a6 | ||
|
17060752b6 | ||
|
4d609f06b8 | ||
|
42d14eec96 | ||
|
eaa82817dd | ||
|
731713de3a | ||
|
193767e6cc | ||
|
bc04662525 | ||
|
8bc444bb97 | ||
|
6fa77feba3 | ||
|
224866c3d2 | ||
|
a50e25300d | ||
|
1f9b89116f | ||
|
40d1eaeb72 | ||
|
dd7cb74cd2 | ||
|
0993658c82 | ||
|
3d0d987d89 | ||
|
bb88b5c36d | ||
|
ccabd93159 | ||
|
788da3cdf7 | ||
|
e1435066ee | ||
|
a2d74972d4 | ||
|
f69fddc6e5 | ||
|
d9e8ca2c83 | ||
|
b390023bc5 | ||
|
ea68f787eb | ||
|
9f9bc9865f | ||
|
b8d81283e8 | ||
|
8f1cd4f925 | ||
|
49c55a3f56 | ||
|
8b37e316d0 | ||
|
a63678c1f4 | ||
|
72ce4838ea | ||
|
96bbd3bd25 | ||
|
3a02490a1f | ||
|
8d7fc98275 | ||
|
ed01eaf5f9 | ||
|
9bbb7c8b97 | ||
|
6a08d04af9 | ||
|
81acd80a97 | ||
|
ca01e923bf | ||
|
4e4ac0de51 | ||
|
18679af4ec | ||
|
72aacbdcec | ||
|
e53c63b828 | ||
|
f899ab7c34 | ||
|
7e778042ee | ||
|
f58b260cbd | ||
|
f39a5e757b | ||
|
9b0a1d53dc | ||
|
223833cac4 | ||
|
4405116324 | ||
|
bab1616565 | ||
|
d2ba4e65c5 | ||
|
2167143c17 | ||
|
df282ab230 | ||
|
646835346c | ||
|
dee764473d | ||
|
e7fa8ec0c6 | ||
|
bffeb818d1 | ||
|
67f7ce815c | ||
|
9398c0ca6b | ||
|
f6bb666abf | ||
|
b6aaf66330 | ||
|
1849083073 | ||
|
09409a5108 | ||
|
5047c4d083 | ||
|
f3c95f72e7 | ||
|
b0a7fd4e07 | ||
|
9bb409314d |
515 changed files with 45033 additions and 27359 deletions
|
@ -15,6 +15,7 @@ BinPackArguments: true
|
|||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterExternBlock: true
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
---
|
||||
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).
|
||||
|
||||
-->
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -2,4 +2,4 @@ blank_issues_enabled: false
|
|||
contact_links:
|
||||
- name: Cemu Discord
|
||||
url: https://discord.com/invite/5psYsup
|
||||
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.
|
||||
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
|
||||
|
|
69
.github/ISSUE_TEMPLATE/emulation_bug_report.yaml
vendored
Normal file
69
.github/ISSUE_TEMPLATE/emulation_bug_report.yaml
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
# 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
|
28
.github/ISSUE_TEMPLATE/feature_report.yaml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/feature_report.yaml
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
# 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
9
.github/getversion.cpp
vendored
|
@ -1,9 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include "./../src/Common/version.h"
|
||||
|
||||
// output current Cemu version for CI workflow. Do not modify
|
||||
int main()
|
||||
{
|
||||
printf("%d.%d", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR);
|
||||
return 0;
|
||||
}
|
147
.github/workflows/build.yml
vendored
147
.github/workflows/build.yml
vendored
|
@ -3,10 +3,10 @@ name: Build Cemu
|
|||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
deploymode:
|
||||
next_version_major:
|
||||
required: false
|
||||
type: string
|
||||
experimentalversion:
|
||||
next_version_minor:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
|
@ -19,41 +19,32 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- 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' }}
|
||||
- name: Setup release mode parameters
|
||||
run: |
|
||||
echo "BUILD_MODE=release" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is release"
|
||||
|
||||
- name: Setup debug mode parameters (for continous build)
|
||||
if: ${{ inputs.deploymode != 'release' }}
|
||||
- name: Setup build flags for version
|
||||
if: ${{ inputs.next_version_major != '' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=debug" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is debug"
|
||||
|
||||
- name: Setup version for experimental
|
||||
if: ${{ inputs.experimentalversion != '' }}
|
||||
run: |
|
||||
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV
|
||||
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Install system dependencies"
|
||||
run: |
|
||||
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
|
||||
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
|
||||
|
||||
- name: "Setup cmake"
|
||||
uses: jwlawson/actions-setup-cmake@v2
|
||||
with:
|
||||
cmake-version: '3.29.0'
|
||||
|
||||
- name: "Bootstrap vcpkg"
|
||||
run: |
|
||||
|
@ -75,19 +66,17 @@ jobs:
|
|||
|
||||
- name: "cmake"
|
||||
run: |
|
||||
cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DPORTABLE=OFF -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 }} -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja
|
||||
|
||||
- name: "Build Cemu"
|
||||
run: |
|
||||
cmake --build build
|
||||
|
||||
- name: Prepare artifact
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: mv bin/Cemu_release bin/Cemu
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cemu-bin-linux-x64
|
||||
path: ./bin/Cemu
|
||||
|
@ -97,9 +86,9 @@ jobs:
|
|||
needs: build-ubuntu
|
||||
steps:
|
||||
- name: Checkout Upstream Repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cemu-bin-linux-x64
|
||||
path: bin
|
||||
|
@ -107,7 +96,7 @@ jobs:
|
|||
- name: "Install system dependencies"
|
||||
run: |
|
||||
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
|
||||
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
|
||||
|
||||
- name: "Build AppImage"
|
||||
run: |
|
||||
|
@ -116,7 +105,7 @@ jobs:
|
|||
dist/linux/appimage.sh
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cemu-appimage-x64
|
||||
path: artifacts
|
||||
|
@ -125,34 +114,26 @@ jobs:
|
|||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- 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' }}
|
||||
- name: Setup release mode parameters
|
||||
run: |
|
||||
echo "BUILD_MODE=release" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "Build mode is release"
|
||||
|
||||
- name: Setup debug mode parameters (for continous build)
|
||||
if: ${{ inputs.deploymode != 'release' }}
|
||||
|
||||
- name: Setup build flags for version
|
||||
if: ${{ inputs.next_version_major != '' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=debug" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "Build mode is debug"
|
||||
- name: Setup version for experimental
|
||||
if: ${{ inputs.experimentalversion != '' }}
|
||||
run: |
|
||||
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: "Setup cmake"
|
||||
uses: jwlawson/actions-setup-cmake@v2
|
||||
with:
|
||||
cmake-version: '3.29.0'
|
||||
|
||||
- name: "Bootstrap vcpkg"
|
||||
run: |
|
||||
|
@ -183,56 +164,56 @@ jobs:
|
|||
- name: "Build Cemu"
|
||||
run: |
|
||||
cd build
|
||||
cmake --build . --config ${{ env.BUILD_MODE }} -j 2
|
||||
cmake --build . --config ${{ env.BUILD_MODE }}
|
||||
|
||||
- name: Prepare artifact
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: Rename-Item bin/Cemu_release.exe Cemu.exe
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cemu-bin-windows-x64
|
||||
path: ./bin/Cemu.exe
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-12
|
||||
runs-on: macos-14
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- 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' }}
|
||||
- name: Setup release mode parameters
|
||||
run: |
|
||||
echo "BUILD_MODE=release" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is release"
|
||||
- name: Setup debug mode parameters (for continous build)
|
||||
if: ${{ inputs.deploymode != 'release' }}
|
||||
|
||||
- name: Setup build flags for version
|
||||
if: ${{ inputs.next_version_major != '' }}
|
||||
run: |
|
||||
echo "BUILD_MODE=debug" >> $GITHUB_ENV
|
||||
echo "BUILD_FLAGS=" >> $GITHUB_ENV
|
||||
echo "Build mode is debug"
|
||||
|
||||
- name: Setup version for experimental
|
||||
if: ${{ inputs.experimentalversion != '' }}
|
||||
run: |
|
||||
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV
|
||||
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
|
||||
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Install system dependencies"
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@15 ninja nasm molten-vk automake libtool
|
||||
brew install ninja nasm 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"
|
||||
run: |
|
||||
|
@ -258,10 +239,8 @@ jobs:
|
|||
cd build
|
||||
cmake .. ${{ env.BUILD_FLAGS }} \
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
|
||||
-DPORTABLE=OFF \
|
||||
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \
|
||||
-DMACOS_BUNDLE=ON \
|
||||
-DCMAKE_C_COMPILER=/usr/local/opt/llvm@15/bin/clang \
|
||||
-DCMAKE_CXX_COMPILER=/usr/local/opt/llvm@15/bin/clang++ \
|
||||
-G Ninja
|
||||
|
||||
- name: "Build Cemu"
|
||||
|
@ -269,21 +248,19 @@ jobs:
|
|||
cmake --build build
|
||||
|
||||
- name: Prepare artifact
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
run: |
|
||||
mkdir bin/Cemu_app
|
||||
mv bin/Cemu_release.app bin/Cemu_app/Cemu.app
|
||||
mv bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu_release bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
|
||||
sed -i '' 's/Cemu_release/Cemu/g' bin/Cemu_app/Cemu.app/Contents/Info.plist
|
||||
chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
|
||||
chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/{Cemu,update.sh}
|
||||
ln -s /Applications bin/Cemu_app/Applications
|
||||
hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app"
|
||||
hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu.dmg
|
||||
rm bin/tmp.dmg
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cemu-bin-macos-x64
|
||||
name: cemu-bin-macos-${{ matrix.arch }}
|
||||
path: ./bin/Cemu.dmg
|
||||
|
|
3
.github/workflows/build_check.yml
vendored
3
.github/workflows/build_check.yml
vendored
|
@ -16,6 +16,3 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
deploymode: release
|
||||
experimentalversion: 999999
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
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-22.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-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 }}"
|
||||
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
|
151
.github/workflows/deploy_release.yml
vendored
Normal file
151
.github/workflows/deploy_release.yml
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
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
|
85
.github/workflows/deploy_stable_release.yml
vendored
85
.github/workflows/deploy_stable_release.yml
vendored
|
@ -1,85 +0,0 @@
|
|||
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
|
74
.github/workflows/determine_release_version.yml
vendored
Normal file
74
.github/workflows/determine_release_version.yml
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
name: Calculate Next Version from release history
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
outputs:
|
||||
next_version:
|
||||
description: "The next semantic version"
|
||||
value: ${{ jobs.calculate-version.outputs.next_version }}
|
||||
next_version_major:
|
||||
description: "The next semantic version (major)"
|
||||
value: ${{ jobs.calculate-version.outputs.next_version_major }}
|
||||
next_version_minor:
|
||||
description: "The next semantic version (minor)"
|
||||
value: ${{ jobs.calculate-version.outputs.next_version_minor }}
|
||||
|
||||
jobs:
|
||||
calculate-version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
next_version: ${{ steps.calculate_next_version.outputs.next_version }}
|
||||
next_version_major: ${{ steps.calculate_next_version.outputs.next_version_major }}
|
||||
next_version_minor: ${{ steps.calculate_next_version.outputs.next_version_minor }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get all releases
|
||||
id: get_all_releases
|
||||
run: |
|
||||
# Fetch all releases and check for API errors
|
||||
RESPONSE=$(curl -s -o response.json -w "%{http_code}" "https://api.github.com/repos/${{ github.repository }}/releases?per_page=100")
|
||||
if [ "$RESPONSE" -ne 200 ]; then
|
||||
echo "Failed to fetch releases. HTTP status: $RESPONSE"
|
||||
cat response.json
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract and sort tags
|
||||
ALL_TAGS=$(jq -r '.[].tag_name' response.json | grep -E '^v[0-9]+\.[0-9]+(-[0-9]+)?$' | sed 's/-.*//' | sort -V | tail -n 1)
|
||||
|
||||
# Exit if no tags were found
|
||||
if [ -z "$ALL_TAGS" ]; then
|
||||
echo "No valid tags found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "::set-output name=tag::$ALL_TAGS"
|
||||
# echo "tag=$ALL_TAGS" >> $GITHUB_STATE
|
||||
|
||||
- name: Calculate next semver minor
|
||||
id: calculate_next_version
|
||||
run: |
|
||||
LATEST_VERSION=${{ steps.get_all_releases.outputs.tag }}
|
||||
|
||||
# strip 'v' prefix and split into major.minor
|
||||
LATEST_VERSION=${LATEST_VERSION//v/}
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$LATEST_VERSION"
|
||||
|
||||
MAJOR=${VERSION_PARTS[0]}
|
||||
MINOR=${VERSION_PARTS[1]}
|
||||
|
||||
# increment the minor version
|
||||
MINOR=$((MINOR + 1))
|
||||
|
||||
NEXT_VERSION="${MAJOR}.${MINOR}"
|
||||
|
||||
echo "Major: $MAJOR"
|
||||
echo "Minor: $MINOR"
|
||||
|
||||
echo "Next version: $NEXT_VERSION"
|
||||
echo "::set-output name=next_version::$NEXT_VERSION"
|
||||
echo "::set-output name=next_version_major::$MAJOR"
|
||||
echo "::set-output name=next_version_minor::$MINOR"
|
2
.github/workflows/generate_pot.yml
vendored
2
.github/workflows/generate_pot.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
|||
-o cemu.pot
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: POT file
|
||||
path: ./cemu.pot
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -39,6 +39,8 @@ bin/sdcard/*
|
|||
bin/screenshots/*
|
||||
bin/dump/*
|
||||
bin/cafeLibs/*
|
||||
bin/portable/*
|
||||
bin/keys.txt
|
||||
|
||||
!bin/shaderCache/info.txt
|
||||
bin/shaderCache/*
|
||||
|
|
7
.gitmodules
vendored
7
.gitmodules
vendored
|
@ -9,10 +9,15 @@
|
|||
[submodule "dependencies/vcpkg"]
|
||||
path = dependencies/vcpkg
|
||||
url = https://github.com/microsoft/vcpkg
|
||||
shallow = true
|
||||
shallow = false
|
||||
[submodule "dependencies/Vulkan-Headers"]
|
||||
path = dependencies/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers
|
||||
shallow = true
|
||||
[submodule "dependencies/imgui"]
|
||||
path = dependencies/imgui
|
||||
url = https://github.com/ocornut/imgui
|
||||
shallow = true
|
||||
[submodule "dependencies/xbyak_aarch64"]
|
||||
path = dependencies/xbyak_aarch64
|
||||
url = https://github.com/fujitsu/xbyak_aarch64
|
||||
|
|
226
BUILD.md
226
BUILD.md
|
@ -1,4 +1,26 @@
|
|||
# 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
|
||||
|
||||
|
@ -19,79 +41,144 @@ Any other IDE should also work as long as it has CMake and MSVC support. CLion a
|
|||
|
||||
## 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-15 or higher is what we recommend.
|
||||
|
||||
### Installing dependencies
|
||||
### Dependencies
|
||||
|
||||
#### For Ubuntu and derivatives:
|
||||
`sudo apt install -y cmake curl clang-15 freeglut3-dev git libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build`
|
||||
#### 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`
|
||||
|
||||
#### 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 while building, use:
|
||||
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 Arch and derivatives:
|
||||
`sudo pacman -S --needed base-devel clang cmake freeglut git glm gtk3 libgcrypt libpulse libsecret linux-headers llvm nasm ninja systemd unzip zip`
|
||||
|
||||
#### For Fedora and derivatives:
|
||||
`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`
|
||||
`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`
|
||||
|
||||
### 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`.
|
||||
### Build Cemu
|
||||
|
||||
#### 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).
|
||||
#### CMake and Clang
|
||||
|
||||
You can use GCC by doing the following:
|
||||
- make sure you have g++ installed in your system
|
||||
- installation for Ubuntu and derivatives: `sudo apt install g++`
|
||||
- installation for Fedora and derivatives: `sudo dnf install gcc-c++`
|
||||
- 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`
|
||||
```
|
||||
git clone --recursive https://github.com/cemu-project/Cemu
|
||||
cd Cemu
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
#### 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 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.
|
||||
- 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.
|
||||
- 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!
|
||||
- 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!
|
||||
- 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.
|
||||
- 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.
|
||||
#### GCC
|
||||
|
||||
If you are building using GCC, make sure you have g++ installed:
|
||||
- Installation for Arch and derivatives: `sudo pacman -S gcc`
|
||||
- Installation for Debian, Ubuntu and derivatives: `sudo apt install g++`
|
||||
- Installation for Fedora and derivatives: `sudo dnf install gcc-c++`
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/cemu-project/Cemu
|
||||
cd Cemu
|
||||
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
|
||||
|
||||
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and
|
||||
below, built in LLVM, and Xcode LLVM don't support the C++20 feature set required. The OpenGL graphics
|
||||
API isn't support on macOS, Vulkan must be used. Additionally Vulkan must be used through the
|
||||
Molten-VK compatibility layer
|
||||
|
||||
### On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used
|
||||
|
||||
You can skip this section if you have an Intel Mac. Every time you compile, you need to perform steps 2.
|
||||
|
||||
1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once
|
||||
2. `arch -x86_64 zsh` # run an x64 shell
|
||||
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and below
|
||||
don't support the C++20 feature set required, so either install LLVM from Homebrew or make sure that
|
||||
you have a recent enough version of Xcode. Xcode 15 is known to work. The OpenGL graphics API isn't
|
||||
supported on macOS, so Vulkan must be used through the Molten-VK compatibility layer.
|
||||
|
||||
### Installing brew
|
||||
|
||||
1. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
|
||||
2. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"` # set x86_64 brew env
|
||||
2. Set up the Homebrew shell environment:
|
||||
1. **On an Intel Mac:** `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
|
||||
2. **On an Apple Silicon Mac:** eval `"$(/opt/homebrew/bin/brew shellenv)"`
|
||||
|
||||
### Installing dependencies
|
||||
### Installing Tool Dependencies
|
||||
|
||||
`brew install boost git cmake llvm ninja nasm molten-vk automake libtool`
|
||||
The native versions of these can be used regardless of what type of Mac you have.
|
||||
|
||||
`brew install git cmake ninja nasm automake libtool`
|
||||
|
||||
### Installing Library Dependencies
|
||||
|
||||
**On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used to install these dependencies:**
|
||||
1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once
|
||||
2. `arch -x86_64 zsh` # run an x64 shell
|
||||
3. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
|
||||
4. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
|
||||
|
||||
Then install the dependencies:
|
||||
|
||||
`brew install boost molten-vk`
|
||||
|
||||
### Build Cemu using CMake
|
||||
|
||||
### Build Cemu using cmake and clang
|
||||
1. `git clone --recursive https://github.com/cemu-project/Cemu`
|
||||
2. `cd Cemu`
|
||||
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/local/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/opt/llvm/bin/clang++ -G Ninja`
|
||||
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_OSX_ARCHITECTURES=x86_64 -G Ninja`
|
||||
4. `cmake --build build`
|
||||
5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`.
|
||||
|
||||
|
@ -104,3 +191,42 @@ You can skip this section if you have an Intel Mac. Every time you compile, you
|
|||
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.
|
||||
|
||||
## 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 |
|
||||
|
|
|
@ -1,24 +1,45 @@
|
|||
cmake_minimum_required(VERSION 3.21.1)
|
||||
|
||||
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)
|
||||
set(EXPERIMENTAL_VERSION "" CACHE STRING "") # used by CI script to set experimental version
|
||||
option(ALLOW_PORTABLE "Allow Cemu to be run in portable mode" ON)
|
||||
|
||||
if (EXPERIMENTAL_VERSION)
|
||||
add_definitions(-DEMULATOR_VERSION_MINOR=${EXPERIMENTAL_VERSION})
|
||||
execute_process(
|
||||
COMMAND git log --format=%h -1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
|
||||
endif()
|
||||
# used by CI script to set version:
|
||||
set(EMULATOR_VERSION_MAJOR "0" CACHE STRING "")
|
||||
set(EMULATOR_VERSION_MINOR "0" CACHE STRING "")
|
||||
set(EMULATOR_VERSION_PATCH "0" CACHE STRING "")
|
||||
|
||||
execute_process(
|
||||
COMMAND git log --format=%h -1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
|
||||
|
||||
if (ENABLE_VCPKG)
|
||||
# check if vcpkg is shallow and unshallow it if necessary
|
||||
execute_process(
|
||||
COMMAND git rev-parse --is-shallow-repository
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/dependencies/vcpkg
|
||||
OUTPUT_VARIABLE is_vcpkg_shallow
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(is_vcpkg_shallow STREQUAL "true")
|
||||
message(STATUS "vcpkg is shallow. Unshallowing it now...")
|
||||
execute_process(
|
||||
COMMAND git fetch --unshallow
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/dependencies/vcpkg"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux")
|
||||
elseif(APPLE)
|
||||
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_mac")
|
||||
else()
|
||||
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
|
||||
endif()
|
||||
|
@ -43,9 +64,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|||
|
||||
add_compile_definitions($<$<CONFIG:Debug>:CEMU_DEBUG_ASSERT>) # if build type is debug, set CEMU_DEBUG_ASSERT
|
||||
|
||||
if(PORTABLE)
|
||||
add_compile_definitions(PORTABLE)
|
||||
endif()
|
||||
add_definitions(-DEMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR})
|
||||
add_definitions(-DEMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR})
|
||||
add_definitions(-DEMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH})
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
|
@ -72,11 +93,13 @@ endif()
|
|||
|
||||
if (APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0")
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
option(ENABLE_WAYLAND "Build with Wayland support" ON)
|
||||
option(ENABLE_FERAL_GAMEMODE "Enables Feral Interactive GameMode Support" ON)
|
||||
option(ENABLE_BLUEZ "Build with Bluez support" ON)
|
||||
endif()
|
||||
|
||||
option(ENABLE_OPENGL "Enables the OpenGL backend" ON)
|
||||
|
@ -89,10 +112,9 @@ if (WIN32)
|
|||
option(ENABLE_XINPUT "Enables the usage of XInput" ON)
|
||||
option(ENABLE_DIRECTINPUT "Enables the usage of DirectInput" ON)
|
||||
add_compile_definitions(HAS_DIRECTINPUT)
|
||||
set(ENABLE_WIIMOTE ON)
|
||||
elseif (UNIX)
|
||||
option(ENABLE_HIDAPI "Build with HIDAPI" ON)
|
||||
endif()
|
||||
|
||||
option(ENABLE_HIDAPI "Build with HIDAPI" ON)
|
||||
option(ENABLE_SDL "Enables the SDLController backend" ON)
|
||||
|
||||
# audio backends
|
||||
|
@ -102,23 +124,6 @@ if (WIN32)
|
|||
endif()
|
||||
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)
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG true)
|
||||
|
@ -159,6 +164,12 @@ if (UNIX AND NOT APPLE)
|
|||
endif()
|
||||
find_package(GTK3 REQUIRED)
|
||||
|
||||
if(ENABLE_BLUEZ)
|
||||
find_package(bluez REQUIRED)
|
||||
set(SUPPORTS_WIIMOTE ON)
|
||||
add_compile_definitions(HAS_BLUEZ)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
|
@ -177,7 +188,7 @@ endif()
|
|||
|
||||
if (ENABLE_HIDAPI)
|
||||
find_package(hidapi REQUIRED)
|
||||
set(ENABLE_WIIMOTE ON)
|
||||
set(SUPPORTS_WIIMOTE ON)
|
||||
add_compile_definitions(HAS_HIDAPI)
|
||||
endif ()
|
||||
|
||||
|
@ -202,7 +213,7 @@ if (ENABLE_CUBEB)
|
|||
option(BUILD_TOOLS "" OFF)
|
||||
option(BUNDLE_SPEEX "" OFF)
|
||||
set(USE_WINMM OFF CACHE BOOL "")
|
||||
add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL)
|
||||
add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL SYSTEM)
|
||||
set_property(TARGET cubeb PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
add_library(cubeb::cubeb ALIAS cubeb)
|
||||
endif()
|
||||
|
@ -211,9 +222,18 @@ endif()
|
|||
|
||||
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)
|
||||
if (NOT ZArchive_FOUND)
|
||||
add_subdirectory("dependencies/ZArchive" EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(src)
|
|
@ -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!
|
||||
|
||||
Questions about Cemu's software architecture can also be answered on Discord (through the Matrix bridge).
|
||||
Questions about Cemu's software architecture can also be answered on Discord (or through the Matrix bridge).
|
||||
|
||||
## 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.
|
||||
|
|
BIN
bin/resources/ar/cemu.mo
Normal file
BIN
bin/resources/ar/cemu.mo
Normal file
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 +0,0 @@
|
|||
If you plan to transfer the shader cache to a different PC or Cemu installation you only need to copy the 'transferable' directory.
|
26
boost.natvis
Normal file
26
boost.natvis
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
|
||||
<Type Name="boost::container::small_vector<*>">
|
||||
<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<*>">
|
||||
<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>
|
20
cmake/Findbluez.cmake
Normal file
20
cmake/Findbluez.cmake
Normal file
|
@ -0,0 +1,20 @@
|
|||
# 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
|
||||
)
|
15
dependencies/ih264d/CMakeLists.txt
vendored
15
dependencies/ih264d/CMakeLists.txt
vendored
|
@ -117,7 +117,13 @@ add_library (ih264d
|
|||
"decoder/ivd.h"
|
||||
)
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
|
||||
if (CMAKE_OSX_ARCHITECTURES)
|
||||
set(IH264D_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
|
||||
else()
|
||||
set(IH264D_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
|
||||
if (IH264D_ARCHITECTURE STREQUAL "x86_64" OR IH264D_ARCHITECTURE STREQUAL "amd64" OR IH264D_ARCHITECTURE STREQUAL "AMD64")
|
||||
set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
|
||||
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
|
||||
target_sources(ih264d PRIVATE
|
||||
|
@ -140,7 +146,7 @@ target_sources(ih264d PRIVATE
|
|||
"decoder/x86/ih264d_function_selector_sse42.c"
|
||||
"decoder/x86/ih264d_function_selector_ssse3.c"
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
elseif(IH264D_ARCHITECTURE STREQUAL "aarch64" OR IH264D_ARCHITECTURE STREQUAL "arm64")
|
||||
enable_language( C CXX ASM )
|
||||
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
|
||||
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
|
||||
|
@ -177,8 +183,11 @@ target_sources(ih264d PRIVATE
|
|||
"decoder/arm/ih264d_function_selector.c"
|
||||
)
|
||||
target_compile_options(ih264d PRIVATE -DARMV8)
|
||||
if(APPLE)
|
||||
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "ih264d unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
|
|
|
@ -429,8 +429,13 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
|
|||
rev64 v7.4h, v2.4h
|
||||
ld1 {v3.2s}, [x10]
|
||||
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
|
||||
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs1]
|
||||
#endif
|
||||
usubl v10.8h, v5.8b, v1.8b
|
||||
ld1 {v8.8b, v9.8b}, [x12] // Load multiplication factors 1 to 8 into D3
|
||||
mov v8.d[1], v9.d[0]
|
||||
|
@ -484,10 +489,13 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
|
|||
zip1 v1.8h, v0.8h, v2.8h
|
||||
zip2 v2.8h, v0.8h, v2.8h
|
||||
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
|
||||
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs2]
|
||||
|
||||
#endif
|
||||
ld1 {v8.2s, v9.2s}, [x12]
|
||||
mov v8.d[1], v9.d[0]
|
||||
mov v10.16b, v8.16b
|
||||
|
|
|
@ -431,10 +431,13 @@ ih264_intra_pred_luma_16x16_mode_plane_av8:
|
|||
mov x10, x1 //top_left
|
||||
mov x4, #-1
|
||||
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
|
||||
ldr x7, [x7, #:got_lo12:ih264_gai1_intrapred_luma_plane_coeffs]
|
||||
|
||||
#endif
|
||||
ld1 {v0.2s}, [x1]
|
||||
rev64 v2.8b, v2.8b
|
||||
ld1 {v6.2s, v7.2s}, [x7]
|
||||
|
|
|
@ -1029,9 +1029,13 @@ ih264_intra_pred_luma_8x8_mode_horz_u_av8:
|
|||
mov v3.d[0], v2.d[1]
|
||||
ext v4.16b, v2.16b , v2.16b , #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
|
||||
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_luma_8x8_horz_u]
|
||||
#endif
|
||||
uaddl v20.8h, v0.8b, v2.8b
|
||||
uaddl v22.8h, v1.8b, v3.8b
|
||||
uaddl v24.8h, v2.8b, v4.8b
|
||||
|
|
|
@ -142,14 +142,22 @@ ih264_weighted_bi_pred_luma_av8:
|
|||
sxtw x4, w4
|
||||
sxtw x5, w5
|
||||
stp x19, x20, [sp, #-16]!
|
||||
#ifndef __APPLE__
|
||||
ldr w8, [sp, #80] //Load wt2 in w8
|
||||
ldr w9, [sp, #88] //Load ofst1 in w9
|
||||
add w6, w6, #1 //w6 = log_WD + 1
|
||||
neg w10, w6 //w10 = -(log_WD + 1)
|
||||
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
|
||||
#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
|
||||
neg w10, w6 //w10 = -(log_WD + 1)
|
||||
dup v0.8h, w10 //Q0 = -(log_WD + 1) (32-bit)
|
||||
add w9, w9, #1 //w9 = ofst1 + 1
|
||||
add w9, w9, w10 //w9 = ofst1 + ofst2 + 1
|
||||
mov v2.s[0], w7
|
||||
|
@ -424,17 +432,24 @@ ih264_weighted_bi_pred_chroma_av8:
|
|||
sxtw x5, w5
|
||||
stp x19, x20, [sp, #-16]!
|
||||
|
||||
|
||||
#ifndef __APPLE__
|
||||
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 v2.4s, w7 //Q1 = (wt1_u, wt1_v) (32-bit)
|
||||
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)
|
||||
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 v21.8h, w10 //0ffset2
|
||||
srhadd v6.8b, v20.8b, v21.8b
|
||||
|
|
185
dependencies/ih264d/common/armv8/macos_arm_symbol_aliases.s
vendored
Normal file
185
dependencies/ih264d/common/armv8/macos_arm_symbol_aliases.s
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
// 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
2
dependencies/vcpkg
vendored
|
@ -1 +1 @@
|
|||
Subproject commit b81bc3a83fdbdffe80325eeabb2ec735a1f3c29d
|
||||
Subproject commit 533a5fda5c0646d1771345fb572e759283444d5f
|
13
dependencies/vcpkg_overlay_ports/sdl2/deps.patch
vendored
13
dependencies/vcpkg_overlay_ports/sdl2/deps.patch
vendored
|
@ -1,13 +0,0 @@
|
|||
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)
|
130
dependencies/vcpkg_overlay_ports/sdl2/portfile.cmake
vendored
130
dependencies/vcpkg_overlay_ports/sdl2/portfile.cmake
vendored
|
@ -1,130 +0,0 @@
|
|||
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")
|
8
dependencies/vcpkg_overlay_ports/sdl2/usage
vendored
8
dependencies/vcpkg_overlay_ports/sdl2/usage
vendored
|
@ -1,8 +0,0 @@
|
|||
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>
|
||||
)
|
58
dependencies/vcpkg_overlay_ports/sdl2/vcpkg.json
vendored
58
dependencies/vcpkg_overlay_ports/sdl2/vcpkg.json
vendored
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
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)
|
|
@ -1,130 +0,0 @@
|
|||
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")
|
|
@ -1,8 +0,0 @@
|
|||
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>
|
||||
)
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
71
dependencies/vcpkg_overlay_ports_mac/libusb/portfile.cmake
vendored
Normal file
71
dependencies/vcpkg_overlay_ports_mac/libusb/portfile.cmake
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
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")
|
5
dependencies/vcpkg_overlay_ports_mac/libusb/usage
vendored
Normal file
5
dependencies/vcpkg_overlay_ports_mac/libusb/usage
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
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)
|
8
dependencies/vcpkg_overlay_ports_mac/libusb/vcpkg.json
vendored
Normal file
8
dependencies/vcpkg_overlay_ports_mac/libusb/vcpkg.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"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
dependencies/xbyak_aarch64
vendored
Submodule
1
dependencies/xbyak_aarch64
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 904b8923457f3ec0d6f82ea2d6832a792851194d
|
6
dist/linux/appimage.sh
vendored
6
dist/linux/appimage.sh
vendored
|
@ -10,6 +10,8 @@ curl -sSfL https://github.com"$(curl https://github.com/probonopd/go-appimage/re
|
|||
chmod a+x mkappimage.AppImage
|
||||
curl -sSfLO "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh"
|
||||
chmod a+x linuxdeploy-plugin-gtk.sh
|
||||
curl -sSfLO "https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh"
|
||||
chmod a+x linuxdeploy-plugin-checkrt.sh
|
||||
|
||||
if [[ ! -e /usr/lib/x86_64-linux-gnu ]]; then
|
||||
sed -i 's#lib\/x86_64-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh
|
||||
|
@ -33,12 +35,14 @@ 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
|
||||
|
||||
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 \
|
||||
--appdir="${GITHUB_WORKSPACE}"/AppDir/ \
|
||||
-d "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.desktop \
|
||||
-i "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.png \
|
||||
-e "${GITHUB_WORKSPACE}"/AppDir/usr/bin/Cemu \
|
||||
--plugin gtk
|
||||
--plugin gtk \
|
||||
--plugin checkrt
|
||||
|
||||
if ! GITVERSION="$(git rev-parse --short HEAD 2>/dev/null)"; then
|
||||
GITVERSION=experimental
|
||||
|
|
1
dist/linux/info.cemu.Cemu.desktop
vendored
1
dist/linux/info.cemu.Cemu.desktop
vendored
|
@ -24,3 +24,4 @@ Comment[it]=Software per emulare giochi e applicazioni per Wii U su PC
|
|||
Categories=Game;Emulator;
|
||||
Keywords=Nintendo;
|
||||
MimeType=application/x-wii-u-rom;
|
||||
StartupWMClass=Cemu
|
||||
|
|
17
dist/network_services.xml
vendored
Normal file
17
dist/network_services.xml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?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>
|
|
@ -49,13 +49,18 @@ add_subdirectory(audio)
|
|||
add_subdirectory(util)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(resource)
|
||||
add_subdirectory(asm)
|
||||
|
||||
add_executable(CemuBin
|
||||
main.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)
|
||||
target_sources(CemuBin PRIVATE
|
||||
resource/cemu.rc
|
||||
|
@ -76,11 +81,13 @@ if (MACOS_BUNDLE)
|
|||
set(MACOSX_BUNDLE_ICON_FILE "cemu.icns")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "info.cemu.Cemu")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "Cemu")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${CMAKE_PROJECT_VERSION})
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION})
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2023 Cemu Project")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}")
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2024 Cemu Project")
|
||||
|
||||
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
|
||||
MACOSX_BUNDLE true
|
||||
|
@ -93,11 +100,26 @@ if (MACOS_BUNDLE)
|
|||
COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory "${CMAKE_SOURCE_DIR}/bin/${folder}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/SharedSupport/${folder}")
|
||||
endforeach(folder)
|
||||
|
||||
add_custom_command (TARGET CemuBin POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/lib/libusb-1.0.0.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
|
||||
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 "${MOLTENVK_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${LIBUSB_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_SOURCE_DIR}/src/resource/update.sh" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/update.sh"
|
||||
COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}"
|
||||
COMMAND bash -c "install_name_tool -change ${LIBUSB_PATH} @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
|
||||
endif()
|
||||
|
||||
set_target_properties(CemuBin PROPERTIES
|
||||
|
|
|
@ -10,6 +10,7 @@ add_library(CemuCafe
|
|||
Filesystem/fscDeviceRedirect.cpp
|
||||
Filesystem/fscDeviceWua.cpp
|
||||
Filesystem/fscDeviceWud.cpp
|
||||
Filesystem/fscDeviceWuhb.cpp
|
||||
Filesystem/fsc.h
|
||||
Filesystem/FST/FST.cpp
|
||||
Filesystem/FST/FST.h
|
||||
|
@ -18,6 +19,9 @@ add_library(CemuCafe
|
|||
Filesystem/FST/KeyCache.h
|
||||
Filesystem/WUD/wud.cpp
|
||||
Filesystem/WUD/wud.h
|
||||
Filesystem/WUHB/RomFSStructs.h
|
||||
Filesystem/WUHB/WUHBReader.cpp
|
||||
Filesystem/WUHB/WUHBReader.h
|
||||
GamePatch.cpp
|
||||
GamePatch.h
|
||||
GameProfile/GameProfile.cpp
|
||||
|
@ -40,6 +44,7 @@ add_library(CemuCafe
|
|||
HW/Espresso/Debugger/DebugSymbolStorage.h
|
||||
HW/Espresso/Debugger/GDBStub.h
|
||||
HW/Espresso/Debugger/GDBStub.cpp
|
||||
HW/Espresso/Debugger/GDBBreakpoints.cpp
|
||||
HW/Espresso/Debugger/GDBBreakpoints.h
|
||||
HW/Espresso/EspressoISA.h
|
||||
HW/Espresso/Interpreter/PPCInterpreterALU.hpp
|
||||
|
@ -62,24 +67,31 @@ add_library(CemuCafe
|
|||
HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h
|
||||
HW/Espresso/Recompiler/PPCRecompiler.cpp
|
||||
HW/Espresso/Recompiler/PPCRecompiler.h
|
||||
HW/Espresso/Recompiler/PPCRecompilerImlAnalyzer.cpp
|
||||
HW/Espresso/Recompiler/IML/IML.h
|
||||
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/PPCRecompilerImlGenFPU.cpp
|
||||
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/PPCRecompilerX64AVX.cpp
|
||||
HW/Espresso/Recompiler/PPCRecompilerX64BMI.cpp
|
||||
HW/Espresso/Recompiler/PPCRecompilerX64.cpp
|
||||
HW/Espresso/Recompiler/PPCRecompilerX64FPU.cpp
|
||||
HW/Espresso/Recompiler/PPCRecompilerX64Gen.cpp
|
||||
HW/Espresso/Recompiler/PPCRecompilerX64GenFPU.cpp
|
||||
HW/Espresso/Recompiler/PPCRecompilerX64.h
|
||||
HW/Espresso/Recompiler/x64Emit.hpp
|
||||
HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp
|
||||
HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp
|
||||
HW/Espresso/Recompiler/BackendX64/BackendX64.cpp
|
||||
HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp
|
||||
HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp
|
||||
HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp
|
||||
HW/Espresso/Recompiler/BackendX64/BackendX64.h
|
||||
HW/Espresso/Recompiler/BackendX64/X64Emit.hpp
|
||||
HW/Espresso/Recompiler/BackendX64/x86Emitter.h
|
||||
HW/Latte/Common/RegisterSerializer.cpp
|
||||
HW/Latte/Common/RegisterSerializer.h
|
||||
HW/Latte/Common/ShaderSerializer.cpp
|
||||
|
@ -213,6 +225,8 @@ add_library(CemuCafe
|
|||
HW/SI/SI.cpp
|
||||
HW/SI/si.h
|
||||
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/iosu_fsa.cpp
|
||||
IOSU/fsa/iosu_fsa.h
|
||||
|
@ -367,12 +381,24 @@ add_library(CemuCafe
|
|||
OS/libs/gx2/GX2_Texture.h
|
||||
OS/libs/gx2/GX2_TilingAperture.cpp
|
||||
OS/libs/h264_avc/H264Dec.cpp
|
||||
OS/libs/h264_avc/H264DecBackendAVC.cpp
|
||||
OS/libs/h264_avc/h264dec.h
|
||||
OS/libs/h264_avc/H264DecInternal.h
|
||||
OS/libs/h264_avc/parser
|
||||
OS/libs/h264_avc/parser/H264Parser.cpp
|
||||
OS/libs/h264_avc/parser/H264Parser.h
|
||||
OS/libs/mic/mic.cpp
|
||||
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/nlibcurlDebug.hpp
|
||||
OS/libs/nlibcurl/nlibcurl.h
|
||||
|
@ -403,6 +429,8 @@ add_library(CemuCafe
|
|||
OS/libs/nn_ndm/nn_ndm.h
|
||||
OS/libs/nn_spm/nn_spm.cpp
|
||||
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/nn_nfp.cpp
|
||||
OS/libs/nn_nfp/nn_nfp.h
|
||||
|
@ -438,14 +466,26 @@ add_library(CemuCafe
|
|||
OS/libs/nsyshid/AttachDefaultBackends.cpp
|
||||
OS/libs/nsyshid/Whitelist.cpp
|
||||
OS/libs/nsyshid/Whitelist.h
|
||||
OS/libs/nsyshid/BackendEmulated.cpp
|
||||
OS/libs/nsyshid/BackendEmulated.h
|
||||
OS/libs/nsyshid/BackendLibusb.cpp
|
||||
OS/libs/nsyshid/BackendLibusb.h
|
||||
OS/libs/nsyshid/BackendWindowsHID.cpp
|
||||
OS/libs/nsyshid/BackendWindowsHID.h
|
||||
OS/libs/nsyshid/Dimensions.cpp
|
||||
OS/libs/nsyshid/Dimensions.h
|
||||
OS/libs/nsyshid/Infinity.cpp
|
||||
OS/libs/nsyshid/Infinity.h
|
||||
OS/libs/nsyshid/Skylander.cpp
|
||||
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.h
|
||||
OS/libs/nsysnet/nsysnet.cpp
|
||||
OS/libs/nsysnet/nsysnet.h
|
||||
OS/libs/ntag/ntag.cpp
|
||||
OS/libs/ntag/ntag.h
|
||||
OS/libs/padscore/padscore.cpp
|
||||
OS/libs/padscore/padscore.h
|
||||
OS/libs/proc_ui/proc_ui.cpp
|
||||
|
@ -497,12 +537,25 @@ if(APPLE)
|
|||
target_sources(CemuCafe PRIVATE "HW/Latte/Renderer/Vulkan/CocoaSurface.mm")
|
||||
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>")
|
||||
|
||||
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
|
||||
CemuAsm
|
||||
CemuAudio
|
||||
CemuCommon
|
||||
CemuComponents
|
||||
|
@ -516,7 +569,7 @@ target_link_libraries(CemuCafe PRIVATE
|
|||
Boost::nowide
|
||||
CURL::libcurl
|
||||
fmt::fmt
|
||||
glslang::SPIRV
|
||||
${glslang_target}
|
||||
ih264d
|
||||
OpenSSL::Crypto
|
||||
OpenSSL::SSL
|
||||
|
@ -532,15 +585,16 @@ if (ENABLE_WAYLAND)
|
|||
target_link_libraries(CemuCafe PUBLIC Wayland::Client)
|
||||
endif()
|
||||
|
||||
if (ENABLE_NSYSHID_LIBUSB)
|
||||
if (ENABLE_VCPKG)
|
||||
find_package(libusb CONFIG REQUIRED)
|
||||
target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS})
|
||||
target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES})
|
||||
else ()
|
||||
find_package(libusb MODULE REQUIRED)
|
||||
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
|
||||
endif ()
|
||||
if (ENABLE_VCPKG)
|
||||
if(WIN32)
|
||||
set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/x64-windows/tools/pkgconf/pkgconf.exe")
|
||||
endif()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
|
||||
target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb)
|
||||
else ()
|
||||
find_package(libusb MODULE REQUIRED)
|
||||
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_WXWIDGETS)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "audio/IAudioAPI.h"
|
||||
#include "audio/IAudioInputAPI.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "config/LaunchSettings.h"
|
||||
#include "Cafe/TitleList/GameInfo.h"
|
||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||
#include "util/helpers/SystemException.h"
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include "Cafe/IOSU/legacy/iosu_boss.h"
|
||||
#include "Cafe/IOSU/legacy/iosu_nim.h"
|
||||
#include "Cafe/IOSU/PDM/iosu_pdm.h"
|
||||
#include "Cafe/IOSU/ccr_nfc/iosu_ccr_nfc.h"
|
||||
|
||||
// IOSU initializer functions
|
||||
#include "Cafe/IOSU/kernel/iosu_kernel.h"
|
||||
|
@ -51,6 +53,8 @@
|
|||
#include "Cafe/OS/libs/gx2/GX2.h"
|
||||
#include "Cafe/OS/libs/gx2/GX2_Misc.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_pdm/nn_pdm.h"
|
||||
#include "Cafe/OS/libs/nn_cmpt/nn_cmpt.h"
|
||||
|
@ -393,7 +397,7 @@ void cemu_initForGame()
|
|||
// replace any known function signatures with our HLE implementations and patch bugs in the games
|
||||
GamePatch_scan();
|
||||
}
|
||||
LatteGPUState.alwaysDisplayDRC = ActiveSettings::DisplayDRCEnabled();
|
||||
LatteGPUState.isDRCPrimary = ActiveSettings::DisplayDRCEnabled();
|
||||
InfoLog_PrintActiveSettings();
|
||||
Latte_Start();
|
||||
// check for debugger entrypoint bp
|
||||
|
@ -530,7 +534,10 @@ namespace CafeSystem
|
|||
{
|
||||
// entries in this list are ordered by initialization order. Shutdown in reverse order
|
||||
iosu::kernel::GetModule(),
|
||||
iosu::fpd::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
|
||||
|
@ -571,7 +578,6 @@ namespace CafeSystem
|
|||
iosu::iosuAcp_init();
|
||||
iosu::boss_init();
|
||||
iosu::nim::Initialize();
|
||||
iosu::pdm::Initialize();
|
||||
iosu::odm::Initialize();
|
||||
// init Cafe OS
|
||||
avm::Initialize();
|
||||
|
@ -586,6 +592,8 @@ namespace CafeSystem
|
|||
H264::Initialize();
|
||||
snd_core::Initialize();
|
||||
mic::Initialize();
|
||||
nfc::Initialize();
|
||||
ntag::Initialize();
|
||||
// init hardware register interfaces
|
||||
HW_SI::Initialize();
|
||||
}
|
||||
|
@ -630,40 +638,40 @@ namespace CafeSystem
|
|||
fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE);
|
||||
}
|
||||
|
||||
STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
|
||||
PREPARE_STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId);
|
||||
sGameInfo_ForegroundTitle = CafeTitleList::GetGameInfo(titleId);
|
||||
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)");
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
}
|
||||
// check base
|
||||
TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase();
|
||||
if (!titleBase.IsValid())
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
if(!titleBase.ParseXmlInfo())
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
cemuLog_log(LogType::Force, "Base: {}", titleBase.GetPrintPath());
|
||||
// mount base
|
||||
if (!titleBase.Mount("/vol/content", "content", FSC_PRIORITY_BASE) || !titleBase.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_BASE))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Mounting failed");
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
}
|
||||
// check update
|
||||
TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate();
|
||||
if (titleUpdate.IsValid())
|
||||
{
|
||||
if (!titleUpdate.ParseXmlInfo())
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
cemuLog_log(LogType::Force, "Update: {}", titleUpdate.GetPrintPath());
|
||||
// mount update
|
||||
if (!titleUpdate.Mount("/vol/content", "content", FSC_PRIORITY_PATCH) || !titleUpdate.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_PATCH))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Mounting failed");
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -675,20 +683,20 @@ namespace CafeSystem
|
|||
// todo - support for multi-title AOC
|
||||
TitleInfo& titleAOC = aocList[0];
|
||||
if (!titleAOC.ParseXmlInfo())
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
cemu_assert_debug(titleAOC.IsValid());
|
||||
cemuLog_log(LogType::Force, "DLC: {}", titleAOC.GetPrintPath());
|
||||
// mount AOC
|
||||
if (!titleAOC.Mount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()), "content", FSC_PRIORITY_PATCH))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Mounting failed");
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
}
|
||||
}
|
||||
else
|
||||
cemuLog_log(LogType::Force, "DLC: Not present");
|
||||
sForegroundTitleId = titleId;
|
||||
return STATUS_CODE::SUCCESS;
|
||||
return PREPARE_STATUS_CODE::SUCCESS;
|
||||
}
|
||||
|
||||
void UnmountForegroundTitle()
|
||||
|
@ -716,7 +724,7 @@ namespace CafeSystem
|
|||
}
|
||||
}
|
||||
|
||||
STATUS_CODE SetupExecutable()
|
||||
PREPARE_STATUS_CODE SetupExecutable()
|
||||
{
|
||||
// set rpx path from cos.xml if available
|
||||
_pathToBaseExecutable = _pathToExecutable;
|
||||
|
@ -748,8 +756,7 @@ namespace CafeSystem
|
|||
}
|
||||
}
|
||||
LoadMainExecutable();
|
||||
gameProfile_load();
|
||||
return STATUS_CODE::SUCCESS;
|
||||
return PREPARE_STATUS_CODE::SUCCESS;
|
||||
}
|
||||
|
||||
void SetupMemorySpace()
|
||||
|
@ -763,7 +770,7 @@ namespace CafeSystem
|
|||
memory_unmapForCurrentTitle();
|
||||
}
|
||||
|
||||
STATUS_CODE PrepareForegroundTitle(TitleId titleId)
|
||||
PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId)
|
||||
{
|
||||
CafeTitleList::WaitForMandatoryScan();
|
||||
sLaunchModeIsStandalone = false;
|
||||
|
@ -774,20 +781,21 @@ namespace CafeSystem
|
|||
// mount mlc storage
|
||||
MountBaseDirectories();
|
||||
// mount title folders
|
||||
STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
|
||||
if (r != STATUS_CODE::SUCCESS)
|
||||
PREPARE_STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
|
||||
if (r != PREPARE_STATUS_CODE::SUCCESS)
|
||||
return r;
|
||||
gameProfile_load();
|
||||
// setup memory space and PPC recompiler
|
||||
SetupMemorySpace();
|
||||
PPCRecompiler_init();
|
||||
r = SetupExecutable(); // load RPX
|
||||
if (r != STATUS_CODE::SUCCESS)
|
||||
if (r != PREPARE_STATUS_CODE::SUCCESS)
|
||||
return r;
|
||||
InitVirtualMlcStorage();
|
||||
return STATUS_CODE::SUCCESS;
|
||||
return PREPARE_STATUS_CODE::SUCCESS;
|
||||
}
|
||||
|
||||
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path)
|
||||
PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path)
|
||||
{
|
||||
sLaunchModeIsStandalone = true;
|
||||
cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files");
|
||||
|
@ -805,7 +813,7 @@ namespace CafeSystem
|
|||
if (!r)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath));
|
||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -817,7 +825,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
|
||||
auto execData = fsc_extractFile(_pathToExecutable.c_str());
|
||||
if (!execData)
|
||||
return STATUS_CODE::INVALID_RPX;
|
||||
return PREPARE_STATUS_CODE::INVALID_RPX;
|
||||
uint32 h = generateHashFromRawRPXData(execData->data(), execData->size());
|
||||
sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h;
|
||||
cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId);
|
||||
|
@ -827,7 +835,7 @@ namespace CafeSystem
|
|||
// load executable
|
||||
SetupExecutable();
|
||||
InitVirtualMlcStorage();
|
||||
return STATUS_CODE::SUCCESS;
|
||||
return PREPARE_STATUS_CODE::SUCCESS;
|
||||
}
|
||||
|
||||
void _LaunchTitleThread()
|
||||
|
@ -836,11 +844,10 @@ namespace CafeSystem
|
|||
module->TitleStart();
|
||||
cemu_initForGame();
|
||||
// enter scheduler
|
||||
if (ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler)
|
||||
if ((ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler || LaunchSettings::ForceMultiCoreInterpreter()) && !LaunchSettings::ForceInterpreter())
|
||||
coreinit::OSSchedulerBegin(3);
|
||||
else
|
||||
coreinit::OSSchedulerBegin(1);
|
||||
iosu::pdm::StartTrackingTime(GetForegroundTitleId());
|
||||
}
|
||||
|
||||
void LaunchForegroundTitle()
|
||||
|
@ -915,6 +922,27 @@ namespace CafeSystem
|
|||
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
|
||||
bool GetOverrideArgStr(std::vector<std::string>& args)
|
||||
{
|
||||
|
@ -970,8 +998,6 @@ namespace CafeSystem
|
|||
RPLLoader_ResetState();
|
||||
for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it)
|
||||
(*it)->TitleStop();
|
||||
// stop time tracking
|
||||
iosu::pdm::Stop();
|
||||
// reset Cemu subsystems
|
||||
PPCRecompiler_Shutdown();
|
||||
GraphicPack2::Reset();
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include "Cafe/TitleList/TitleId.h"
|
||||
#include "config/CemuConfig.h"
|
||||
|
||||
enum class CosCapabilityBits : uint64;
|
||||
enum class CosCapabilityGroup : uint32;
|
||||
|
||||
namespace CafeSystem
|
||||
{
|
||||
class SystemImplementation
|
||||
|
@ -12,20 +15,19 @@ namespace CafeSystem
|
|||
virtual void CafeRecreateCanvas() = 0;
|
||||
};
|
||||
|
||||
enum class STATUS_CODE
|
||||
enum class PREPARE_STATUS_CODE
|
||||
{
|
||||
SUCCESS,
|
||||
INVALID_RPX,
|
||||
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 SetImplementation(SystemImplementation* impl);
|
||||
void Shutdown();
|
||||
|
||||
STATUS_CODE PrepareForegroundTitle(TitleId titleId);
|
||||
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
|
||||
PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId);
|
||||
PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
|
||||
void LaunchForegroundTitle();
|
||||
bool IsTitleRunning();
|
||||
|
||||
|
@ -41,6 +43,7 @@ namespace CafeSystem
|
|||
std::string GetForegroundTitleName();
|
||||
std::string GetForegroundTitleArgStr();
|
||||
uint32 GetForegroundTitleOlvAccesskey();
|
||||
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group);
|
||||
|
||||
void ShutdownTitle();
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
#include "Cafe/Filesystem/WUD/wud.h"
|
||||
#include "util/crypto/aes128.h"
|
||||
#include "openssl/evp.h" /* EVP_Digest */
|
||||
#include "openssl/sha.h" /* SHA1 / SHA256_DIGEST_LENGTH */
|
||||
#include "openssl/sha.h" /* SHA1 / SHA256 */
|
||||
#include "fstUtil.h"
|
||||
|
||||
#include "FST.h"
|
||||
|
@ -14,6 +13,8 @@
|
|||
|
||||
#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
|
||||
{
|
||||
public:
|
||||
|
@ -141,7 +142,7 @@ struct DiscPartitionTableHeader
|
|||
static constexpr uint32 MAGIC_VALUE = 0xCCA6E67B;
|
||||
|
||||
/* +0x00 */ uint32be magic;
|
||||
/* +0x04 */ uint32be sectorSize; // must be 0x8000?
|
||||
/* +0x04 */ uint32be blockSize; // must be 0x8000?
|
||||
/* +0x08 */ uint8 partitionTableHash[20]; // hash of the data range at +0x800 to end of sector (0x8000)
|
||||
/* +0x1C */ uint32be numPartitions;
|
||||
};
|
||||
|
@ -164,10 +165,10 @@ struct DiscPartitionHeader
|
|||
static constexpr uint32 MAGIC_VALUE = 0xCC93A4F5;
|
||||
|
||||
/* +0x00 */ uint32be magic;
|
||||
/* +0x04 */ uint32be sectorSize; // must match DISC_SECTOR_SIZE
|
||||
/* +0x04 */ uint32be sectorSize; // must match DISC_SECTOR_SIZE for hashed blocks
|
||||
|
||||
/* +0x08 */ uint32be ukn008;
|
||||
/* +0x0C */ uint32be ukn00C;
|
||||
/* +0x0C */ uint32be ukn00C; // h3 array size?
|
||||
/* +0x10 */ uint32be h3HashNum;
|
||||
/* +0x14 */ uint32be fstSize; // in bytes
|
||||
/* +0x18 */ uint32be fstSector; // relative to partition start
|
||||
|
@ -178,13 +179,15 @@ struct DiscPartitionHeader
|
|||
/* +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)
|
||||
|
||||
/* +0x26 */ uint8 versionA;
|
||||
/* +0x27 */ uint8 ukn027; // also a version field?
|
||||
/* +0x26 */ uint8be versionA;
|
||||
/* +0x27 */ uint8be ukn027; // also a version field?
|
||||
|
||||
// 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) == 0x28);
|
||||
static_assert(sizeof(DiscPartitionHeader) == 0x40+0x20);
|
||||
|
||||
bool FSTVolume::FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey)
|
||||
{
|
||||
|
@ -269,7 +272,7 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
|
|||
cemuLog_log(LogType::Force, "Disc image rejected because decryption failed");
|
||||
return nullptr;
|
||||
}
|
||||
if (partitionHeader->sectorSize != DISC_SECTOR_SIZE)
|
||||
if (partitionHeader->blockSize != DISC_SECTOR_SIZE)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Disc image rejected because partition sector size is invalid");
|
||||
return nullptr;
|
||||
|
@ -336,6 +339,9 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
|
|||
cemu_assert_debug(partitionHeaderSI.fstEncryptionType == 1);
|
||||
// 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
|
||||
DiscPartitionHeader partitionHeaderGM{};
|
||||
if (!readPartitionHeader(partitionHeaderGM, gmPartitionIndex))
|
||||
|
@ -349,9 +355,10 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
|
|||
// if decryption is necessary
|
||||
// load SI FST
|
||||
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));
|
||||
auto siFST = OpenFST(dataSource.get(), (uint64)partitionHeaderSI.fstSector * DISC_SECTOR_SIZE, partitionHeaderSI.fstSize, &discTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderSI.fstHashType), nullptr);
|
||||
if (!siFST)
|
||||
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
|
||||
NCrypto::ETicketParser ticketParser;
|
||||
std::vector<uint8> ticketData = siFST->ExtractFile(fmt::format("{:02x}/title.tik", gmPartitionIndex));
|
||||
|
@ -360,16 +367,32 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
|
|||
cemuLog_log(LogType::Force, "Disc image ticket file is invalid");
|
||||
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;
|
||||
|
||||
NCrypto::AesKey gmTitleKey;
|
||||
ticketParser.GetTitleKey(gmTitleKey);
|
||||
|
||||
// load GM partition
|
||||
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));
|
||||
FSTVolume* r = OpenFST(std::move(dataSource), (uint64)partitionHeaderGM.fstSector * DISC_SECTOR_SIZE, partitionHeaderGM.fstSize, &gmTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderGM.fstHashType), nullptr);
|
||||
if (r)
|
||||
SET_FST_ERROR(OK);
|
||||
cemu_assert_debug(!(r->HashIsDisabled() && partitionHeaderGM.h3HashNum != 0)); // if hash is disabled, no H3 data may be present
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -426,15 +449,15 @@ FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath, ErrorCode* erro
|
|||
}
|
||||
// load FST
|
||||
// fstSize = size of first cluster?
|
||||
FSTVolume* fstVolume = FSTVolume::OpenFST(std::move(dataSource), 0, fstSize, &titleKey, fstHashMode);
|
||||
FSTVolume* fstVolume = FSTVolume::OpenFST(std::move(dataSource), 0, fstSize, &titleKey, fstHashMode, &tmdParser);
|
||||
if (fstVolume)
|
||||
SET_FST_ERROR(OK);
|
||||
return fstVolume;
|
||||
}
|
||||
|
||||
FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode)
|
||||
FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD)
|
||||
{
|
||||
cemu_assert_debug(fstHashMode != ClusterHashMode::RAW || fstHashMode != ClusterHashMode::RAW2);
|
||||
cemu_assert_debug(fstHashMode != ClusterHashMode::RAW || fstHashMode != ClusterHashMode::RAW_STREAM);
|
||||
if (fstSize < sizeof(FSTHeader))
|
||||
return nullptr;
|
||||
constexpr uint64 FST_CLUSTER_OFFSET = 0;
|
||||
|
@ -465,6 +488,34 @@ FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint3
|
|||
clusterTable[i].offset = clusterDataTable[i].offset;
|
||||
clusterTable[i].size = clusterDataTable[i].size;
|
||||
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
|
||||
FSTHeader_FileEntry* fileTable = (FSTHeader_FileEntry*)(clusterDataTable + numCluster);
|
||||
|
@ -491,16 +542,17 @@ FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint3
|
|||
fstVolume->m_offsetFactor = fstHeader->offsetFactor;
|
||||
fstVolume->m_sectorSize = DISC_SECTOR_SIZE;
|
||||
fstVolume->m_partitionTitlekey = *partitionTitleKey;
|
||||
std::swap(fstVolume->m_cluster, clusterTable);
|
||||
std::swap(fstVolume->m_entries, fstEntries);
|
||||
std::swap(fstVolume->m_nameStringTable, nameStringTable);
|
||||
fstVolume->m_hashIsDisabled = fstHeader->hashIsDisabled != 0;
|
||||
fstVolume->m_cluster = std::move(clusterTable);
|
||||
fstVolume->m_entries = std::move(fstEntries);
|
||||
fstVolume->m_nameStringTable = std::move(nameStringTable);
|
||||
return fstVolume;
|
||||
}
|
||||
|
||||
FSTVolume* FSTVolume::OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode)
|
||||
FSTVolume* FSTVolume::OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD)
|
||||
{
|
||||
FSTDataSource* ds = dataSource.release();
|
||||
FSTVolume* fstVolume = OpenFST(ds, fstOffset, fstSize, partitionTitleKey, fstHashMode);
|
||||
FSTVolume* fstVolume = OpenFST(ds, fstOffset, fstSize, partitionTitleKey, fstHashMode, optionalTMD);
|
||||
if (!fstVolume)
|
||||
{
|
||||
delete ds;
|
||||
|
@ -757,7 +809,7 @@ uint32 FSTVolume::ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size
|
|||
return 0;
|
||||
cemu_assert_debug(!HAS_FLAG(entry.GetFlags(), FSTEntry::FLAGS::FLAG_LINK));
|
||||
FSTCluster& cluster = m_cluster[entry.fileInfo.clusterIndex];
|
||||
if (cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW2)
|
||||
if (cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW_STREAM)
|
||||
return ReadFile_HashModeRaw(entry.fileInfo.clusterIndex, entry, offset, size, dataOut);
|
||||
else if (cluster.hashMode == ClusterHashMode::HASH_INTERLEAVED)
|
||||
return ReadFile_HashModeHashed(entry.fileInfo.clusterIndex, entry, offset, size, dataOut);
|
||||
|
@ -765,87 +817,15 @@ uint32 FSTVolume::ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size
|
|||
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_HASH_SIZE = 0x0400;
|
||||
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
|
||||
{
|
||||
uint8 rawData[BLOCK_SIZE];
|
||||
|
@ -887,12 +867,160 @@ struct FSTHashedBlock
|
|||
|
||||
static_assert(sizeof(FSTHashedBlock) == BLOCK_SIZE);
|
||||
|
||||
struct FSTCachedRawBlock
|
||||
{
|
||||
FSTRawBlock blockData;
|
||||
NCrypto::AesIv ivForNextBlock;
|
||||
uint64 lastAccess;
|
||||
};
|
||||
|
||||
struct FSTCachedHashedBlock
|
||||
{
|
||||
FSTHashedBlock blockData;
|
||||
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)
|
||||
{
|
||||
const FSTCluster& cluster = m_cluster[clusterIndex];
|
||||
|
@ -908,22 +1036,17 @@ FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, ui
|
|||
block->lastAccess = ++m_cacheAccessCounter;
|
||||
return block;
|
||||
}
|
||||
// if cache already full, drop least recently accessed block (but recycle the FSTHashedBlock* object)
|
||||
if (m_cacheDecryptedHashedBlocks.size() >= 16)
|
||||
{
|
||||
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
|
||||
// if cache already full, drop least recently accessed block and recycle FSTCachedHashedBlock object if possible
|
||||
TrimCacheIfRequired(nullptr, &block);
|
||||
if (!block)
|
||||
block = new FSTCachedHashedBlock();
|
||||
// block not cached, read new
|
||||
block->lastAccess = ++m_cacheAccessCounter;
|
||||
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * BLOCK_SIZE, block->blockData.rawData, BLOCK_SIZE) != BLOCK_SIZE)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to read FST block");
|
||||
cemuLog_log(LogType::Force, "Failed to read hashed FST block");
|
||||
delete block;
|
||||
m_detectedCorruption = true;
|
||||
return nullptr;
|
||||
}
|
||||
// decrypt hash data
|
||||
|
@ -931,11 +1054,46 @@ FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, ui
|
|||
AES128_CBC_decrypt(block->blockData.getHashData(), block->blockData.getHashData(), BLOCK_HASH_SIZE, m_partitionTitlekey.b, iv);
|
||||
// decrypt file data
|
||||
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
|
||||
m_cacheDecryptedHashedBlocks.emplace(cacheBlockId, 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)
|
||||
{
|
||||
/*
|
||||
|
@ -966,7 +1124,6 @@ uint32 FSTVolume::ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry,
|
|||
*/
|
||||
|
||||
const FSTCluster& cluster = m_cluster[clusterIndex];
|
||||
uint64 clusterBaseOffset = (uint64)cluster.offset * m_sectorSize;
|
||||
uint64 fileReadOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset;
|
||||
uint32 blockIndex = (uint32)(fileReadOffset / BLOCK_FILE_SIZE);
|
||||
uint32 bytesRemaining = readSize;
|
||||
|
@ -1019,6 +1176,8 @@ bool FSTVolume::Next(FSTDirectoryIterator& directoryIterator, FSTFileHandle& fil
|
|||
|
||||
FSTVolume::~FSTVolume()
|
||||
{
|
||||
for (auto& itr : m_cacheDecryptedRawBlocks)
|
||||
delete itr.second;
|
||||
for (auto& itr : m_cacheDecryptedHashedBlocks)
|
||||
delete itr.second;
|
||||
if (m_sourceIsOwned)
|
||||
|
@ -1115,4 +1274,4 @@ bool FSTVerifier::VerifyHashedContentFile(FileStream* fileContent, const NCrypto
|
|||
void FSTVolumeTest()
|
||||
{
|
||||
FSTPathUnitTest();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
#include "openssl/evp.h"
|
||||
|
||||
struct FSTFileHandle
|
||||
{
|
||||
|
@ -45,6 +46,7 @@ public:
|
|||
~FSTVolume();
|
||||
|
||||
uint32 GetFileCount() const;
|
||||
bool HasCorruption() const { return m_detectedCorruption; }
|
||||
|
||||
bool OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bool openOnlyFiles = false);
|
||||
|
||||
|
@ -81,20 +83,29 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
/* FST data (in memory) */
|
||||
enum class ClusterHashMode : uint8
|
||||
{
|
||||
RAW = 0, // raw data + encryption, no hashing?
|
||||
RAW2 = 1, // raw data + encryption, with hash stored in tmd?
|
||||
RAW_STREAM = 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)
|
||||
};
|
||||
|
||||
struct FSTCluster
|
||||
{
|
||||
FSTCluster() : singleHashCtx(nullptr, &EVP_MD_CTX_free) {}
|
||||
|
||||
uint32 offset;
|
||||
uint32 size;
|
||||
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
|
||||
|
@ -164,17 +175,30 @@ private:
|
|||
bool m_sourceIsOwned{};
|
||||
uint32 m_sectorSize{}; // for cluster 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<FSTEntry> m_entries;
|
||||
std::vector<char> m_nameStringTable;
|
||||
NCrypto::AesKey m_partitionTitlekey;
|
||||
bool m_detectedCorruption{false};
|
||||
|
||||
/* Cache for decrypted hashed blocks */
|
||||
bool HashIsDisabled() const
|
||||
{
|
||||
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;
|
||||
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);
|
||||
|
||||
void TrimCacheIfRequired(struct FSTCachedRawBlock** droppedRawBlock, struct FSTCachedHashedBlock** droppedHashedBlock);
|
||||
|
||||
/* File reading */
|
||||
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);
|
||||
|
@ -185,7 +209,10 @@ private:
|
|||
/* +0x00 */ uint32be magic;
|
||||
/* +0x04 */ uint32be offsetFactor;
|
||||
/* +0x08 */ uint32be numCluster;
|
||||
/* +0x0C */ uint32be ukn0C;
|
||||
/* +0x0C */ uint8be hashIsDisabled;
|
||||
/* +0x0D */ uint8be ukn0D;
|
||||
/* +0x0E */ uint8be ukn0E;
|
||||
/* +0x0F */ uint8be ukn0F;
|
||||
/* +0x10 */ uint32be ukn10;
|
||||
/* +0x14 */ uint32be ukn14;
|
||||
/* +0x18 */ uint32be ukn18;
|
||||
|
@ -262,8 +289,8 @@ private:
|
|||
|
||||
static_assert(sizeof(FSTHeader_FileEntry) == 0x10);
|
||||
|
||||
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);
|
||||
static FSTVolume* OpenFST(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, NCrypto::TMDParser* optionalTMD);
|
||||
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)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "../fsc.h"
|
||||
|
||||
// path parser and utility class for Wii U paths
|
||||
// optimized to be allocation-free for common path lengths
|
||||
class FSCPath
|
||||
|
@ -119,9 +121,7 @@ public:
|
|||
template<typename F>
|
||||
class FSAFileTree
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
enum NODETYPE : uint8
|
||||
{
|
||||
|
@ -133,6 +133,7 @@ private:
|
|||
{
|
||||
std::string name;
|
||||
std::vector<node_t*> subnodes;
|
||||
size_t fileSize;
|
||||
F* custom;
|
||||
NODETYPE type;
|
||||
};
|
||||
|
@ -179,13 +180,54 @@ private:
|
|||
return newNode;
|
||||
}
|
||||
|
||||
class DirectoryIterator : public FSCVirtualFile
|
||||
{
|
||||
public:
|
||||
DirectoryIterator(node_t* node)
|
||||
: m_node(node), m_subnodeIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
sint32 fscGetType() override
|
||||
{
|
||||
return FSC_TYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
bool fscDirNext(FSCDirEntry* dirEntry) override
|
||||
{
|
||||
if (m_subnodeIndex >= m_node->subnodes.size())
|
||||
return false;
|
||||
|
||||
const node_t* subnode = m_node->subnodes[m_subnodeIndex];
|
||||
|
||||
strncpy(dirEntry->path, subnode->name.c_str(), sizeof(dirEntry->path) - 1);
|
||||
dirEntry->path[sizeof(dirEntry->path) - 1] = '\0';
|
||||
dirEntry->isDirectory = subnode->type == FSAFileTree::NODETYPE_DIRECTORY;
|
||||
dirEntry->isFile = subnode->type == FSAFileTree::NODETYPE_FILE;
|
||||
dirEntry->fileSize = subnode->type == FSAFileTree::NODETYPE_FILE ? subnode->fileSize : 0;
|
||||
|
||||
++m_subnodeIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fscRewindDir() override
|
||||
{
|
||||
m_subnodeIndex = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
node_t* m_node;
|
||||
size_t m_subnodeIndex;
|
||||
};
|
||||
|
||||
public:
|
||||
FSAFileTree()
|
||||
{
|
||||
rootNode.type = NODETYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
bool addFile(std::string_view path, F* custom)
|
||||
bool addFile(std::string_view path, size_t fileSize, F* custom)
|
||||
{
|
||||
FSCPath p(path);
|
||||
if (p.GetNodeCount() == 0)
|
||||
|
@ -196,6 +238,7 @@ public:
|
|||
return false; // node already exists
|
||||
// add file node
|
||||
node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.GetNodeName(p.GetNodeCount() - 1));
|
||||
fileNode->fileSize = fileSize;
|
||||
fileNode->custom = custom;
|
||||
return true;
|
||||
}
|
||||
|
@ -214,6 +257,20 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool getDirectory(std::string_view path, FSCVirtualFile*& dirIterator)
|
||||
{
|
||||
FSCPath p(path);
|
||||
if (p.GetNodeCount() == 0)
|
||||
return false;
|
||||
node_t* node = getByNodePath(p, p.GetNodeCount(), false);
|
||||
if (node == nullptr)
|
||||
return false;
|
||||
if (node->type != NODETYPE_DIRECTORY)
|
||||
return false;
|
||||
dirIterator = new DirectoryIterator(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeFile(std::string_view path)
|
||||
{
|
||||
FSCPath p(path);
|
||||
|
|
40
src/Cafe/Filesystem/WUHB/RomFSStructs.h
Normal file
40
src/Cafe/Filesystem/WUHB/RomFSStructs.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#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
|
224
src/Cafe/Filesystem/WUHB/WUHBReader.cpp
Normal file
224
src/Cafe/Filesystem/WUHB/WUHBReader.cpp
Normal file
|
@ -0,0 +1,224 @@
|
|||
#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;
|
||||
}
|
45
src/Cafe/Filesystem/WUHB/WUHBReader.h
Normal file
45
src/Cafe/Filesystem/WUHB/WUHBReader.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#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;
|
||||
};
|
|
@ -204,9 +204,12 @@ bool FSCDeviceWUD_Mount(std::string_view mountPath, std::string_view destination
|
|||
// wua device
|
||||
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
|
||||
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
|
||||
|
||||
// redirect device
|
||||
void fscDeviceRedirect_map();
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority);
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority);
|
||||
|
|
|
@ -11,7 +11,7 @@ struct RedirectEntry
|
|||
|
||||
FSAFileTree<RedirectEntry> redirectTree;
|
||||
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority)
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority)
|
||||
{
|
||||
// check if source already has a redirection
|
||||
RedirectEntry* existingEntry;
|
||||
|
@ -24,7 +24,7 @@ void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& t
|
|||
delete existingEntry;
|
||||
}
|
||||
RedirectEntry* entry = new RedirectEntry(targetFilePath, priority);
|
||||
redirectTree.addFile(virtualSourcePath, entry);
|
||||
redirectTree.addFile(virtualSourcePath, fileSize, entry);
|
||||
}
|
||||
|
||||
class fscDeviceTypeRedirect : public fscDeviceC
|
||||
|
@ -32,8 +32,15 @@ class fscDeviceTypeRedirect : public fscDeviceC
|
|||
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
||||
{
|
||||
RedirectEntry* redirectionEntry;
|
||||
if (redirectTree.getFile(path, redirectionEntry))
|
||||
|
||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) && redirectTree.getFile(path, redirectionEntry))
|
||||
return FSCVirtualFile_Host::OpenFile(redirectionEntry->dstPath, accessFlags, *fscStatus);
|
||||
|
||||
FSCVirtualFile* dirIterator;
|
||||
|
||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR) && redirectTree.getDirectory(path, dirIterator))
|
||||
return dirIterator;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
151
src/Cafe/Filesystem/fscDeviceWuhb.cpp
Normal file
151
src/Cafe/Filesystem/fscDeviceWuhb.cpp
Normal file
|
@ -0,0 +1,151 @@
|
|||
#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;
|
||||
}
|
|
@ -140,7 +140,7 @@ bool gameProfile_loadEnumOption(IniParser& iniParser, const char* optionName, T&
|
|||
for(const T& v : T())
|
||||
{
|
||||
// test integer option
|
||||
if (boost::iequals(fmt::format("{}", static_cast<typename std::underlying_type<T>::type>(v)), *option_value))
|
||||
if (boost::iequals(fmt::format("{}", fmt::underlying(v)), *option_value))
|
||||
{
|
||||
option = v;
|
||||
return true;
|
||||
|
@ -209,7 +209,7 @@ bool GameProfile::Load(uint64_t title_id)
|
|||
m_gameName = std::string(game_name.begin(), game_name.end());
|
||||
trim(m_gameName.value());
|
||||
}
|
||||
IniParser iniParser(*profileContents, gameProfilePath.string());
|
||||
IniParser iniParser(*profileContents, _pathToUtf8(gameProfilePath));
|
||||
// parse ini
|
||||
while (iniParser.NextSection())
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
|
|||
return;
|
||||
std::vector<uint8> rulesData;
|
||||
fs_rules->extract(rulesData);
|
||||
IniParser iniParser(rulesData, rulesPath.string());
|
||||
IniParser iniParser(rulesData, _pathToUtf8(rulesPath));
|
||||
|
||||
if (!iniParser.NextSection())
|
||||
{
|
||||
|
@ -51,10 +51,9 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
|
|||
cemuLog_log(LogType::Force, "{}: Unable to parse version", _pathToUtf8(rulesPath));
|
||||
return;
|
||||
}
|
||||
|
||||
if (versionNum > GP_LEGACY_VERSION)
|
||||
{
|
||||
GraphicPack2::LoadGraphicPack(_pathToUtf8(rulesPath), iniParser);
|
||||
GraphicPack2::LoadGraphicPack(rulesPath, iniParser);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -79,22 +78,22 @@ void GraphicPack2::LoadAll()
|
|||
}
|
||||
}
|
||||
|
||||
bool GraphicPack2::LoadGraphicPack(const std::string& filename, IniParser& rules)
|
||||
bool GraphicPack2::LoadGraphicPack(const fs::path& rulesPath, IniParser& rules)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto gp = std::make_shared<GraphicPack2>(filename, rules);
|
||||
auto gp = std::make_shared<GraphicPack2>(rulesPath, rules);
|
||||
|
||||
// check if enabled and preset set
|
||||
const auto& config_entries = g_config.data().graphic_pack_entries;
|
||||
|
||||
// legacy absolute path checking for not breaking compatibility
|
||||
auto file = gp->GetFilename2();
|
||||
auto file = gp->GetRulesPath();
|
||||
auto it = config_entries.find(file.lexically_normal());
|
||||
if (it == config_entries.cend())
|
||||
{
|
||||
// check for relative path
|
||||
it = config_entries.find(MakeRelativePath(ActiveSettings::GetUserDataPath(), gp->GetFilename2()).lexically_normal());
|
||||
it = config_entries.find(_utf8ToPath(gp->GetNormalizedPathString()));
|
||||
}
|
||||
|
||||
if (it != config_entries.cend())
|
||||
|
@ -145,7 +144,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(),
|
||||
[graphic_pack](const GraphicPackPtr& gp)
|
||||
{
|
||||
return gp->GetFilename() == graphic_pack->GetFilename();
|
||||
return gp->GetNormalizedPathString() == graphic_pack->GetNormalizedPathString();
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -173,12 +172,12 @@ void GraphicPack2::ActivateForCurrentTitle()
|
|||
{
|
||||
if (gp->GetPresets().empty())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetPath());
|
||||
cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetVirtualPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string logLine;
|
||||
logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetPath()));
|
||||
logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetVirtualPath()));
|
||||
bool isFirst = true;
|
||||
for (auto& itr : gp->GetPresets())
|
||||
{
|
||||
|
@ -249,8 +248,8 @@ std::unordered_map<std::string, GraphicPack2::PresetVar> GraphicPack2::ParsePres
|
|||
return vars;
|
||||
}
|
||||
|
||||
GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
||||
: m_filename(std::move(filename))
|
||||
GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
|
||||
: m_rulesPath(std::move(rulesPath))
|
||||
{
|
||||
// we're already in [Definition]
|
||||
auto option_version = rules.FindOption("version");
|
||||
|
@ -259,7 +258,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
m_version = StringHelpers::ToInt(*option_version, -1);
|
||||
if (m_version < 0)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "{}: Invalid version", m_filename);
|
||||
cemuLog_log(LogType::Force, "{}: Invalid version", _pathToUtf8(m_rulesPath));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
|
@ -281,6 +280,10 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
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");
|
||||
if (option_vendorFilter)
|
||||
{
|
||||
|
@ -305,7 +308,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
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();
|
||||
}
|
||||
m_path = *option_path;
|
||||
m_virtualPath = *option_path;
|
||||
|
||||
auto option_gp_name = rules.FindOption("name");
|
||||
if (option_gp_name)
|
||||
|
@ -342,7 +345,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
const auto preset_name = rules.FindOption("name");
|
||||
if (!preset_name)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", m_name, rules.GetCurrentSectionLineNumber());
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", GetNormalizedPathString(), rules.GetCurrentSectionLineNumber());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -366,7 +369,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
}
|
||||
catch (const std::exception & ex)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Can't parse preset \"{}\": {}", m_name, *preset_name, ex.what());
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Can't parse preset \"{}\": {}", GetNormalizedPathString(), *preset_name, ex.what());
|
||||
}
|
||||
}
|
||||
else if (boost::iequals(currentSectionName, "RAM"))
|
||||
|
@ -380,7 +383,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
{
|
||||
if (m_version <= 5)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", m_name, optionNameBuf);
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", GetNormalizedPathString(), optionNameBuf);
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
|
@ -390,12 +393,12 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
{
|
||||
if (addrEnd <= addrStart)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", m_name, addrStart, addrEnd, optionNameBuf);
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", GetNormalizedPathString(), addrStart, addrEnd, optionNameBuf);
|
||||
throw std::exception();
|
||||
}
|
||||
else if ((addrStart & 0xFFF) != 0 || (addrEnd & 0xFFF) != 0)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", m_name, optionNameBuf);
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", GetNormalizedPathString(), optionNameBuf);
|
||||
throw std::exception();
|
||||
}
|
||||
else
|
||||
|
@ -405,7 +408,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
}
|
||||
else
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": has invalid syntax for option {}", m_name, optionNameBuf);
|
||||
cemuLog_log(LogType::Force, "Graphic pack \"{}\": has invalid syntax for option {}", GetNormalizedPathString(), optionNameBuf);
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
@ -419,24 +422,32 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
std::unordered_map<std::string, std::vector<PresetPtr>> tmp_map;
|
||||
|
||||
// all vars must be defined in the default preset vars before
|
||||
for (const auto& entry : m_presets)
|
||||
std::vector<std::pair<std::string, std::string>> mismatchingPresetVars;
|
||||
for (const auto& presetEntry : m_presets)
|
||||
{
|
||||
tmp_map[entry->category].emplace_back(entry);
|
||||
tmp_map[presetEntry->category].emplace_back(presetEntry);
|
||||
|
||||
for (auto& kv : entry->variables)
|
||||
for (auto& presetVar : presetEntry->variables)
|
||||
{
|
||||
const auto it = m_preset_vars.find(kv.first);
|
||||
const auto it = m_preset_vars.find(presetVar.first);
|
||||
if (it == m_preset_vars.cend())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains preset variables which are not defined in the default section", m_name);
|
||||
throw std::exception();
|
||||
mismatchingPresetVars.emplace_back(presetEntry->name, presetVar.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
// overwrite var type with default var type
|
||||
kv.second.first = it->second.first;
|
||||
presetVar.second.first = it->second.first;
|
||||
}
|
||||
}
|
||||
|
||||
if(!mismatchingPresetVars.empty())
|
||||
{
|
||||
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
|
||||
for(auto entry : get_values(tmp_map))
|
||||
{
|
||||
|
@ -466,7 +477,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
auto& p2 = kv.second[i + 1];
|
||||
if (p1->variables.size() != p2->variables.size())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", m_name);
|
||||
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString());
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
|
@ -474,14 +485,14 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules)
|
|||
std::set<std::string> keys2(get_keys(p2->variables).begin(), get_keys(p2->variables).end());
|
||||
if (keys1 != keys2)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", m_name);
|
||||
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString());
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
if(p1->is_default)
|
||||
{
|
||||
if(has_default)
|
||||
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", m_name, p1->name);
|
||||
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", GetNormalizedPathString(), p1->name);
|
||||
p1->active = true;
|
||||
has_default = true;
|
||||
}
|
||||
|
@ -508,6 +519,11 @@ bool GraphicPack2::Reload()
|
|||
return Activate();
|
||||
}
|
||||
|
||||
std::string GraphicPack2::GetNormalizedPathString() const
|
||||
{
|
||||
return _pathToUtf8(MakeRelativePath(ActiveSettings::GetUserDataPath(), GetRulesPath()).lexically_normal());
|
||||
}
|
||||
|
||||
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; });
|
||||
|
@ -650,7 +666,7 @@ bool GraphicPack2::SetActivePreset(std::string_view category, std::string_view n
|
|||
|
||||
void GraphicPack2::LoadShaders()
|
||||
{
|
||||
fs::path path(m_filename);
|
||||
fs::path path = GetRulesPath();
|
||||
for (auto& it : fs::directory_iterator(path.remove_filename()))
|
||||
{
|
||||
if (!is_regular_file(it))
|
||||
|
@ -676,7 +692,7 @@ void GraphicPack2::LoadShaders()
|
|||
{
|
||||
std::ifstream file(p);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
|
||||
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename())));
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
m_output_shader_source.reserve(file.tellg());
|
||||
|
@ -689,7 +705,7 @@ void GraphicPack2::LoadShaders()
|
|||
{
|
||||
std::ifstream file(p);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
|
||||
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename())));
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
m_upscaling_shader_source.reserve(file.tellg());
|
||||
|
@ -702,7 +718,7 @@ void GraphicPack2::LoadShaders()
|
|||
{
|
||||
std::ifstream file(p);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
|
||||
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename())));
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
m_downscaling_shader_source.reserve(file.tellg());
|
||||
|
@ -805,7 +821,7 @@ void GraphicPack2::AddConstantsForCurrentPreset(ExpressionParser& ep)
|
|||
}
|
||||
}
|
||||
|
||||
void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC)
|
||||
void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC, const char* virtualMountBase)
|
||||
{
|
||||
uint64 currentTitleId = CafeSystem::GetForegroundTitleId();
|
||||
uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull;
|
||||
|
@ -820,9 +836,9 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, std::wstri
|
|||
}
|
||||
else
|
||||
{
|
||||
virtualMountPath = fs::path("vol/content/") / virtualMountPath;
|
||||
virtualMountPath = fs::path(virtualMountBase) / virtualMountPath;
|
||||
}
|
||||
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.path().generic_string(), m_fs_priority);
|
||||
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.file_size(), it.path().generic_string(), m_fs_priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -833,7 +849,7 @@ void GraphicPack2::LoadReplacedFiles()
|
|||
return;
|
||||
m_patchedFilesLoaded = true;
|
||||
|
||||
fs::path gfxPackPath = _utf8ToPath(m_filename);
|
||||
fs::path gfxPackPath = GetRulesPath();
|
||||
gfxPackPath = gfxPackPath.remove_filename();
|
||||
|
||||
// /content/
|
||||
|
@ -843,10 +859,9 @@ void GraphicPack2::LoadReplacedFiles()
|
|||
std::error_code ec;
|
||||
if (fs::exists(contentPath, ec))
|
||||
{
|
||||
std::wstring internalPath(L"/vol/content/");
|
||||
// setup redirections
|
||||
fscDeviceRedirect_map();
|
||||
_iterateReplacedFiles(contentPath, internalPath, false);
|
||||
_iterateReplacedFiles(contentPath, false, "vol/content/");
|
||||
}
|
||||
// /aoc/
|
||||
fs::path aocPath(gfxPackPath);
|
||||
|
@ -857,13 +872,20 @@ void GraphicPack2::LoadReplacedFiles()
|
|||
uint64 aocTitleId = CafeSystem::GetForegroundTitleId();
|
||||
aocTitleId = aocTitleId & 0xFFFFFFFFULL;
|
||||
aocTitleId |= 0x0005000c00000000ULL;
|
||||
wchar_t internalAocPath[128];
|
||||
swprintf(internalAocPath, sizeof(internalAocPath)/sizeof(wchar_t), L"/aoc/%016llx/", aocTitleId);
|
||||
|
||||
std::wstring internalPath(internalAocPath);
|
||||
// setup redirections
|
||||
fscDeviceRedirect_map();
|
||||
_iterateReplacedFiles(aocPath, internalPath, true);
|
||||
_iterateReplacedFiles(aocPath, true, nullptr);
|
||||
}
|
||||
|
||||
// /code/
|
||||
fs::path codePath(gfxPackPath);
|
||||
codePath.append("code");
|
||||
|
||||
if (fs::exists(codePath, ec))
|
||||
{
|
||||
// setup redirections
|
||||
fscDeviceRedirect_map();
|
||||
_iterateReplacedFiles(codePath, false, CafeSystem::GetInternalVirtualCodeFolder().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -879,21 +901,18 @@ bool GraphicPack2::Activate()
|
|||
if (m_gfx_vendor.has_value())
|
||||
{
|
||||
auto vendor = g_renderer->GetVendor();
|
||||
if (vendor == GfxVendor::IntelLegacy || vendor == GfxVendor::IntelNoLegacy)
|
||||
vendor = GfxVendor::Intel;
|
||||
|
||||
if (m_gfx_vendor.value() != vendor)
|
||||
return false;
|
||||
}
|
||||
|
||||
FileStream* fs_rules = FileStream::openFile2(_utf8ToPath(m_filename));
|
||||
FileStream* fs_rules = FileStream::openFile2(m_rulesPath);
|
||||
if (!fs_rules)
|
||||
return false;
|
||||
std::vector<uint8> rulesData;
|
||||
fs_rules->extract(rulesData);
|
||||
delete fs_rules;
|
||||
|
||||
IniParser rules({ (char*)rulesData.data(), rulesData.size()}, m_filename);
|
||||
IniParser rules({ (char*)rulesData.data(), rulesData.size()}, GetNormalizedPathString());
|
||||
|
||||
// load rules
|
||||
try
|
||||
|
@ -947,7 +966,7 @@ bool GraphicPack2::Activate()
|
|||
else if (anisotropyValue == 16)
|
||||
rule.overwrite_settings.anistropic_value = 4;
|
||||
else
|
||||
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);
|
||||
cemuLog_log(LogType::Force, "Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, GetNormalizedPathString());
|
||||
}
|
||||
m_texture_rules.emplace_back(rule);
|
||||
}
|
||||
|
@ -960,7 +979,7 @@ bool GraphicPack2::Activate()
|
|||
auto option_upscale = rules.FindOption("upscaleMagFilter");
|
||||
if(option_upscale && boost::iequals(*option_upscale, "NearestNeighbor"))
|
||||
m_output_settings.upscale_filter = LatteTextureView::MagFilter::kNearestNeighbor;
|
||||
auto option_downscale = rules.FindOption("NearestNeighbor");
|
||||
auto option_downscale = rules.FindOption("downscaleMinFilter");
|
||||
if (option_downscale && boost::iequals(*option_downscale, "NearestNeighbor"))
|
||||
m_output_settings.downscale_filter = LatteTextureView::MagFilter::kNearestNeighbor;
|
||||
}
|
||||
|
@ -992,11 +1011,11 @@ bool GraphicPack2::Activate()
|
|||
if (LatteTiming_getCustomVsyncFrequency(globalCustomVsyncFreq))
|
||||
{
|
||||
if (customVsyncFreq != globalCustomVsyncFreq)
|
||||
cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetPath());
|
||||
cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetVirtualPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetPath());
|
||||
cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetVirtualPath());
|
||||
LatteTiming_setCustomVsyncFrequency(customVsyncFreq);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,22 +97,23 @@ public:
|
|||
};
|
||||
using PresetPtr = std::shared_ptr<Preset>;
|
||||
|
||||
GraphicPack2(std::string filename, IniParser& rules);
|
||||
GraphicPack2(fs::path rulesPath, IniParser& rules);
|
||||
|
||||
bool IsEnabled() const { return m_enabled; }
|
||||
bool IsActivated() const { return m_activated; }
|
||||
sint32 GetVersion() const { return m_version; }
|
||||
const std::string& GetFilename() const { return m_filename; }
|
||||
const fs::path GetFilename2() const { return fs::path(m_filename); }
|
||||
const fs::path GetRulesPath() const { return m_rulesPath; }
|
||||
std::string GetNormalizedPathString() const;
|
||||
bool RequiresRestart(bool changeEnableState, bool changePreset);
|
||||
bool Reload();
|
||||
|
||||
bool HasName() const { return !m_name.empty(); }
|
||||
|
||||
const std::string& GetName() const { return m_name.empty() ? m_path : m_name; }
|
||||
const std::string& GetPath() const { return m_path; }
|
||||
const std::string& GetName() const { return m_name.empty() ? m_virtualPath : m_name; }
|
||||
const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy
|
||||
const std::string& GetDescription() const { return m_description; }
|
||||
bool IsDefaultEnabled() const { return m_default_enabled; }
|
||||
bool AllowRendertargetSizeOptimization() const { return m_allowRendertargetSizeOptimization; }
|
||||
|
||||
void SetEnabled(bool state) { m_enabled = state; }
|
||||
|
||||
|
@ -164,7 +165,7 @@ public:
|
|||
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 void LoadGraphicPack(fs::path graphicPackPath);
|
||||
static bool LoadGraphicPack(const std::string& filename, class IniParser& rules);
|
||||
static bool LoadGraphicPack(const fs::path& rulesPath, class IniParser& rules);
|
||||
static bool ActivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack);
|
||||
static bool DeactivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack);
|
||||
static void ClearGraphicPacks();
|
||||
|
@ -208,15 +209,17 @@ private:
|
|||
parser.TryAddConstant(var.first, (TType)var.second.second);
|
||||
}
|
||||
|
||||
std::string m_filename;
|
||||
fs::path m_rulesPath;
|
||||
|
||||
sint32 m_version;
|
||||
std::string m_name;
|
||||
std::string m_path;
|
||||
std::string m_virtualPath;
|
||||
std::string m_description;
|
||||
|
||||
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
|
||||
std::optional<RendererAPI> m_renderer_api;
|
||||
std::optional<GfxVendor> m_gfx_vendor;
|
||||
|
@ -257,7 +260,7 @@ private:
|
|||
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 LoadReplacedFiles();
|
||||
void _iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC);
|
||||
void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC, const char* virtualMountBase);
|
||||
|
||||
// ram mappings
|
||||
std::vector<std::pair<MPTR, MPTR>> m_ramMappings;
|
||||
|
|
|
@ -71,19 +71,8 @@ void PatchErrorHandler::showStageErrorMessageBox()
|
|||
// returns true if at least one file was found even if it could not be successfully parsed
|
||||
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;
|
||||
fs::path path(_utf8ToPath(m_filename));
|
||||
fs::path path(m_rulesPath);
|
||||
path.remove_filename();
|
||||
for (auto& p : fs::directory_iterator(path))
|
||||
{
|
||||
|
@ -129,7 +118,7 @@ void GraphicPack2::LoadPatchFiles()
|
|||
if (LoadCemuPatches())
|
||||
return; // exit if at least one Cemu style patch file was found
|
||||
// fall back to Cemuhook patches.txt to guarantee backward compatibility
|
||||
fs::path path(_utf8ToPath(m_filename));
|
||||
fs::path path(m_rulesPath);
|
||||
path.remove_filename();
|
||||
path.append("patches.txt");
|
||||
FileStream* patchFile = FileStream::openFile2(path);
|
||||
|
|
|
@ -25,7 +25,7 @@ sint32 GraphicPack2::GetLengthWithoutComment(const char* str, size_t length)
|
|||
|
||||
void GraphicPack2::LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", this->GetFilename());
|
||||
cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", _pathToUtf8(this->GetRulesPath()));
|
||||
if(lineNumber >= 0)
|
||||
cemuLog_log(LogType::Force, fmt::format("Line {0}: {1}", lineNumber, errorMsg));
|
||||
else
|
||||
|
|
|
@ -45,12 +45,17 @@ public:
|
|||
|
||||
static void ClearRange(MPTR address, uint32 length)
|
||||
{
|
||||
if (length == 0)
|
||||
return;
|
||||
s_lock.lock();
|
||||
while (length > 0)
|
||||
for (;;)
|
||||
{
|
||||
auto itr = s_typeStorage.find(address);
|
||||
if (itr != s_typeStorage.end())
|
||||
s_typeStorage.erase(itr);
|
||||
|
||||
if (length <= 4)
|
||||
break;
|
||||
address += 4;
|
||||
length -= 4;
|
||||
}
|
||||
|
@ -60,4 +65,4 @@ public:
|
|||
private:
|
||||
static FSpinlock s_lock;
|
||||
static std::unordered_map<MPTR, DEBUG_SYMBOL_TYPE> s_typeStorage;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "gui/debugger/DebuggerWindow2.h"
|
||||
|
||||
#include "Cafe/OS/libs/coreinit/coreinit.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
|
@ -136,11 +137,6 @@ void debugger_createCodeBreakpoint(uint32 address, uint8 bpType)
|
|||
debugger_updateExecutionBreakpoint(address);
|
||||
}
|
||||
|
||||
void debugger_createExecuteBreakpoint(uint32 address)
|
||||
{
|
||||
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
|
||||
}
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
|
||||
|
@ -294,8 +290,23 @@ void debugger_toggleExecuteBreakpoint(uint32 address)
|
|||
}
|
||||
else
|
||||
{
|
||||
// create new breakpoint
|
||||
debugger_createExecuteBreakpoint(address);
|
||||
// create new execution breakpoint
|
||||
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,6 +458,34 @@ bool debugger_hasPatch(uint32 address)
|
|||
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)
|
||||
{
|
||||
bool isRecEnabled = ppcRecompilerEnabled;
|
||||
|
@ -501,8 +540,6 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU)
|
|||
debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i];
|
||||
}
|
||||
|
||||
void DebugLogStackTrace(OSThread_t* thread, MPTR sp);
|
||||
|
||||
void debugger_enterTW(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// handle logging points
|
||||
|
@ -512,7 +549,48 @@ void debugger_enterTW(PPCInterpreter_t* hCPU)
|
|||
{
|
||||
if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled)
|
||||
{
|
||||
std::string logName = !bp->comment.empty() ? "Breakpoint '"+boost::nowide::narrow(bp->comment)+"'" : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address);
|
||||
std::string comment = !bp->comment.empty() ? boost::nowide::narrow(bp->comment) : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address);
|
||||
|
||||
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);
|
||||
if (cemuLog_advancedPPCLoggingEnabled())
|
||||
|
@ -549,7 +627,7 @@ void debugger_enterTW(PPCInterpreter_t* hCPU)
|
|||
debuggerState.debugSession.stepInto = false;
|
||||
debuggerState.debugSession.stepOver = false;
|
||||
debuggerState.debugSession.run = false;
|
||||
while (true)
|
||||
while (debuggerState.debugSession.isTrapped)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
// check for step commands
|
||||
|
|
|
@ -100,8 +100,8 @@ extern debuggerState_t debuggerState;
|
|||
// new API
|
||||
DebuggerBreakpoint* debugger_getFirstBP(uint32 address);
|
||||
void debugger_createCodeBreakpoint(uint32 address, uint8 bpType);
|
||||
void debugger_createExecuteBreakpoint(uint32 address);
|
||||
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_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite);
|
||||
|
@ -114,6 +114,7 @@ void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore = fals
|
|||
|
||||
void debugger_createPatch(uint32 address, std::span<uint8> patchData);
|
||||
bool debugger_hasPatch(uint32 address);
|
||||
void debugger_removePatch(uint32 address);
|
||||
|
||||
void debugger_forceBreak(); // force breakpoint at the next possible instruction
|
||||
bool debugger_isTrapped();
|
||||
|
|
304
src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp
Normal file
304
src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp
Normal file
|
@ -0,0 +1,304 @@
|
|||
#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
|
||||
}
|
|
@ -1,33 +1,18 @@
|
|||
#pragma once
|
||||
#include "GDBStub.h"
|
||||
#include <utility>
|
||||
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/user.h>
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_LINUX
|
||||
#include <sys/types.h>
|
||||
|
||||
// helpers for accessing debug register
|
||||
typedef unsigned long DRType;
|
||||
|
||||
DRType _GetDR(pid_t tid, int drIndex)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
DRType _GetDR(pid_t tid, int drIndex);
|
||||
void _SetDR(pid_t tid, int drIndex, DRType newValue);
|
||||
DRType _ReadDR6();
|
||||
#endif
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
|
||||
}
|
||||
|
||||
enum class BreakpointType
|
||||
{
|
||||
BP_SINGLE,
|
||||
|
@ -38,59 +23,10 @@ enum class BreakpointType
|
|||
|
||||
class GDBServer::ExecutionBreakpoint {
|
||||
public:
|
||||
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;
|
||||
}
|
||||
ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason);
|
||||
~ExecutionBreakpoint();
|
||||
|
||||
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]] uint32 GetVisibleOpCode() const;
|
||||
[[nodiscard]] bool ShouldBreakThreads() const
|
||||
{
|
||||
return this->m_pauseThreads;
|
||||
|
@ -118,18 +54,8 @@ public:
|
|||
return m_reason;
|
||||
};
|
||||
|
||||
void RemoveTemporarily()
|
||||
{
|
||||
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 RemoveTemporarily();
|
||||
void Restore();
|
||||
void PauseOnNextInterrupt()
|
||||
{
|
||||
this->m_pauseOnNextInterrupt = true;
|
||||
|
@ -162,115 +88,8 @@ enum class AccessPointType
|
|||
|
||||
class GDBServer::AccessBreakpoint {
|
||||
public:
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
};
|
||||
AccessBreakpoint(MPTR address, AccessPointType type);
|
||||
~AccessBreakpoint();
|
||||
|
||||
MPTR GetAddress() const
|
||||
{
|
||||
|
@ -284,4 +103,4 @@ public:
|
|||
private:
|
||||
const MPTR m_address;
|
||||
const AccessPointType m_type;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -263,6 +263,14 @@ bool GDBServer::Initialize()
|
|||
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));
|
||||
m_server_addr.sin_family = AF_INET;
|
||||
m_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
@ -289,7 +297,7 @@ bool GDBServer::Initialize()
|
|||
|
||||
void GDBServer::ThreadFunc()
|
||||
{
|
||||
SetThreadName("GDBServer::ThreadFunc");
|
||||
SetThreadName("GDBServer");
|
||||
|
||||
while (!m_stopRequested)
|
||||
{
|
||||
|
@ -356,7 +364,7 @@ void GDBServer::ThreadFunc()
|
|||
}
|
||||
char checkSumStr[2];
|
||||
receiveMessage(checkSumStr, 2);
|
||||
uint32_t checkSum = std::stoi(checkSumStr, nullptr, 16);
|
||||
uint32_t checkSum = std::stoi(std::string(checkSumStr, sizeof(checkSumStr)), nullptr, 16);
|
||||
assert((checkedSum & 0xFF) == checkSum);
|
||||
|
||||
HandleCommand(message);
|
||||
|
|
|
@ -10,6 +10,18 @@ namespace Espresso
|
|||
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
|
||||
{
|
||||
// underscore at the end of the name means that this instruction always updates CR0 (as if RC bit is set)
|
||||
|
@ -91,13 +103,15 @@ namespace Espresso
|
|||
BCCTR = 528
|
||||
};
|
||||
|
||||
enum class OPCODE_31
|
||||
enum class Opcode31
|
||||
{
|
||||
|
||||
TW = 4,
|
||||
MFTB = 371,
|
||||
};
|
||||
|
||||
inline PrimaryOpcode GetPrimaryOpcode(uint32 opcode) { return (PrimaryOpcode)(opcode >> 26); };
|
||||
inline Opcode19 GetGroup19Opcode(uint32 opcode) { return (Opcode19)((opcode >> 1) & 0x3FF); };
|
||||
inline Opcode31 GetGroup31Opcode(uint32 opcode) { return (Opcode31)((opcode >> 1) & 0x3FF); };
|
||||
|
||||
struct BOField
|
||||
{
|
||||
|
@ -132,6 +146,12 @@ namespace Espresso
|
|||
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)
|
||||
{
|
||||
LI = opcode & 0x3fffffc;
|
||||
|
@ -183,13 +203,7 @@ namespace Espresso
|
|||
_decodeForm_D_branch(opcode, BD, BO, BI, AA, LK);
|
||||
}
|
||||
|
||||
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)
|
||||
inline void decodeOp_BCSPR(uint32 opcode, BOField& BO, uint32& BI, bool& LK) // BCLR and BCSPR
|
||||
{
|
||||
// form XL (with BD field expected to be zero)
|
||||
_decodeForm_XL(opcode, BO, BI, LK);
|
||||
|
|
|
@ -3,12 +3,12 @@ static void PPCInterpreter_setXerOV(PPCInterpreter_t* hCPU, bool hasOverflow)
|
|||
{
|
||||
if (hasOverflow)
|
||||
{
|
||||
hCPU->spr.XER |= XER_SO;
|
||||
hCPU->spr.XER |= XER_OV;
|
||||
hCPU->xer_so = 1;
|
||||
hCPU->xer_ov = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hCPU->spr.XER &= ~XER_OV;
|
||||
hCPU->xer_ov = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ static void PPCInterpreter_ADD(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
|
||||
static void PPCInterpreter_ADDO(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
// untested (Don't Starve Giant Edition uses this instruction + BSO)
|
||||
// Don't Starve Giant Edition uses this instruction + BSO
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
uint32 result = hCPU->gpr[rA] + hCPU->gpr[rB];
|
||||
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(hCPU->gpr[rA], hCPU->gpr[rB], result));
|
||||
|
@ -113,7 +113,6 @@ static void PPCInterpreter_ADDEO(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
else
|
||||
hCPU->xer_ca = 0;
|
||||
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(a, b, hCPU->gpr[rD]));
|
||||
// update CR
|
||||
if (opHasRC())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -130,7 +129,7 @@ static void PPCInterpreter_ADDI(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
|
||||
static void PPCInterpreter_ADDIC(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
int rD, rA;
|
||||
sint32 rD, rA;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
||||
uint32 a = hCPU->gpr[rA];
|
||||
|
@ -145,7 +144,7 @@ static void PPCInterpreter_ADDIC(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
|
||||
static void PPCInterpreter_ADDIC_(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
int rD, rA;
|
||||
sint32 rD, rA;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
||||
uint32 a = hCPU->gpr[rA];
|
||||
|
@ -155,14 +154,13 @@ static void PPCInterpreter_ADDIC_(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
hCPU->xer_ca = 1;
|
||||
else
|
||||
hCPU->xer_ca = 0;
|
||||
// update cr0 flags
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
static void PPCInterpreter_ADDIS(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
int rD, rA;
|
||||
sint32 rD, rA;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm);
|
||||
hCPU->gpr[rD] = (rA ? hCPU->gpr[rA] : 0) + imm;
|
||||
|
@ -185,6 +183,23 @@ static void PPCInterpreter_ADDZE(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
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)
|
||||
{
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
|
@ -201,6 +216,23 @@ static void PPCInterpreter_ADDME(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
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)
|
||||
{
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
|
@ -212,11 +244,12 @@ static void PPCInterpreter_SUBF(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
|
||||
static void PPCInterpreter_SUBFO(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
// untested (Don't Starve Giant Edition uses this)
|
||||
// Seen in Don't Starve Giant Edition and Teslagrad
|
||||
// also used by DS Virtual Console (Super Mario 64 DS)
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1;
|
||||
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~hCPU->gpr[rA], hCPU->gpr[rB], hCPU->gpr[rD]));
|
||||
uint32 result = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1;
|
||||
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~hCPU->gpr[rA], hCPU->gpr[rB], result));
|
||||
hCPU->gpr[rD] = result;
|
||||
if (opHasRC())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -245,7 +278,7 @@ static void PPCInterpreter_SUBFCO(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
uint32 a = hCPU->gpr[rA];
|
||||
uint32 b = hCPU->gpr[rB];
|
||||
hCPU->gpr[rD] = ~a + b + 1;
|
||||
// update xer
|
||||
// update carry
|
||||
if (ppc_carry_3(~a, b, 1))
|
||||
hCPU->xer_ca = 1;
|
||||
else
|
||||
|
@ -259,7 +292,7 @@ static void PPCInterpreter_SUBFCO(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
|
||||
static void PPCInterpreter_SUBFIC(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
int rD, rA;
|
||||
sint32 rD, rA;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
||||
uint32 a = hCPU->gpr[rA];
|
||||
|
@ -283,7 +316,6 @@ static void PPCInterpreter_SUBFE(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
hCPU->xer_ca = 1;
|
||||
else
|
||||
hCPU->xer_ca = 0;
|
||||
// update cr0
|
||||
if (opHasRC())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -303,7 +335,6 @@ static void PPCInterpreter_SUBFEO(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
else
|
||||
hCPU->xer_ca = 0;
|
||||
PPCInterpreter_setXerOV(hCPU, checkAdditionOverflow(~a, b, result));
|
||||
// update cr0
|
||||
if (opHasRC())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -325,9 +356,25 @@ static void PPCInterpreter_SUBFZE(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
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)
|
||||
{
|
||||
// untested
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
PPC_ASSERT(rB == 0);
|
||||
uint32 a = hCPU->gpr[rA];
|
||||
|
@ -338,7 +385,24 @@ static void PPCInterpreter_SUBFME(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
hCPU->xer_ca = 1;
|
||||
else
|
||||
hCPU->xer_ca = 0;
|
||||
// update cr0
|
||||
if (opcode & PPC_OPC_RC)
|
||||
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)
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -351,13 +415,8 @@ static void PPCInterpreter_MULHW_(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
sint64 b = (sint32)hCPU->gpr[rB];
|
||||
sint64 c = a * b;
|
||||
hCPU->gpr[rD] = ((uint64)c) >> 32;
|
||||
if (opcode & PPC_OPC_RC) {
|
||||
// update cr0 flags
|
||||
#ifdef CEMU_DEBUG_ASSERT
|
||||
assert_dbg();
|
||||
#endif
|
||||
if (opHasRC())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
}
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
|
@ -408,14 +467,14 @@ static void PPCInterpreter_MULLI(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
static void PPCInterpreter_DIVW(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
sint32 a = hCPU->gpr[rA];
|
||||
sint32 b = hCPU->gpr[rB];
|
||||
sint32 a = (sint32)hCPU->gpr[rA];
|
||||
sint32 b = (sint32)hCPU->gpr[rB];
|
||||
if (b == 0)
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "Error: Division by zero! [{:08x}]", (uint32)hCPU->instructionPointer);
|
||||
b++;
|
||||
}
|
||||
hCPU->gpr[rD] = a / b;
|
||||
hCPU->gpr[rD] = a < 0 ? 0xFFFFFFFF : 0;
|
||||
else if (a == 0x80000000 && b == 0xFFFFFFFF)
|
||||
hCPU->gpr[rD] = 0xFFFFFFFF;
|
||||
else
|
||||
hCPU->gpr[rD] = a / b;
|
||||
if (opHasRC())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -424,16 +483,23 @@ static void PPCInterpreter_DIVW(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
static void PPCInterpreter_DIVWO(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
sint32 a = hCPU->gpr[rA];
|
||||
sint32 b = hCPU->gpr[rB];
|
||||
sint32 a = (sint32)hCPU->gpr[rA];
|
||||
sint32 b = (sint32)hCPU->gpr[rB];
|
||||
if (b == 0)
|
||||
{
|
||||
PPCInterpreter_setXerOV(hCPU, true);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
return;
|
||||
hCPU->gpr[rD] = a < 0 ? 0xFFFFFFFF : 0;
|
||||
}
|
||||
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())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -442,12 +508,14 @@ static void PPCInterpreter_DIVWO(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
static void PPCInterpreter_DIVWU(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
if (hCPU->gpr[rB] == 0)
|
||||
{
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
return;
|
||||
}
|
||||
hCPU->gpr[rD] = hCPU->gpr[rA] / hCPU->gpr[rB];
|
||||
uint32 a = hCPU->gpr[rA];
|
||||
uint32 b = hCPU->gpr[rB];
|
||||
if (b == 0)
|
||||
hCPU->gpr[rD] = 0;
|
||||
else if (a == 0x80000000 && b == 0xFFFFFFFF)
|
||||
hCPU->gpr[rD] = 0;
|
||||
else
|
||||
hCPU->gpr[rD] = a / b;
|
||||
if (opHasRC())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -456,14 +524,23 @@ static void PPCInterpreter_DIVWU(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
static void PPCInterpreter_DIVWUO(PPCInterpreter_t* hCPU, uint32 opcode)
|
||||
{
|
||||
PPC_OPC_TEMPL3_XO();
|
||||
if (hCPU->gpr[rB] == 0)
|
||||
uint32 a = hCPU->gpr[rA];
|
||||
uint32 b = hCPU->gpr[rB];
|
||||
if (b == 0)
|
||||
{
|
||||
PPCInterpreter_setXerOV(hCPU, true);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
return;
|
||||
hCPU->gpr[rD] = 0;
|
||||
}
|
||||
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())
|
||||
ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -490,6 +567,13 @@ static void PPCInterpreter_CRANDC(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
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)
|
||||
{
|
||||
PPC_OPC_TEMPL_X_CR();
|
||||
|
@ -847,8 +931,7 @@ static void PPCInterpreter_CMP(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
|
||||
else
|
||||
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
|
||||
if ((hCPU->spr.XER & XER_SO) != 0)
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so;
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
|
@ -870,8 +953,7 @@ static void PPCInterpreter_CMPL(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
|
||||
else
|
||||
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
|
||||
if ((hCPU->spr.XER & XER_SO) != 0)
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so;
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
|
@ -894,8 +976,7 @@ static void PPCInterpreter_CMPI(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
|
||||
else
|
||||
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
|
||||
if (hCPU->spr.XER & XER_SO)
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so;
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
|
@ -918,8 +999,7 @@ static void PPCInterpreter_CMPLI(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
hCPU->cr[cr * 4 + CR_BIT_GT] = 1;
|
||||
else
|
||||
hCPU->cr[cr * 4 + CR_BIT_EQ] = 1;
|
||||
if (hCPU->spr.XER & XER_SO)
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = 1;
|
||||
hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so;
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ espresso_frsqrte_entry_t frsqrteLookupTable[32] =
|
|||
{0x20c1000, 0x35e},{0x1f12000, 0x332},{0x1d79000, 0x30a},{0x1bf4000, 0x2e6},
|
||||
};
|
||||
|
||||
double frsqrte_espresso(double input)
|
||||
ATTR_MS_ABI double frsqrte_espresso(double 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}
|
||||
};
|
||||
|
||||
double fres_espresso(double input)
|
||||
ATTR_MS_ABI double fres_espresso(double input)
|
||||
{
|
||||
// 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)
|
||||
|
|
|
@ -2,62 +2,70 @@
|
|||
#include "PPCInterpreterInternal.h"
|
||||
#include "PPCInterpreterHelper.h"
|
||||
|
||||
std::unordered_set<std::string> sUnsupportedHLECalls;
|
||||
std::unordered_set<std::string> s_unsupportedHLECalls;
|
||||
|
||||
void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
const char* libFuncName = (char*)memory_getPointerFromVirtualOffset(hCPU->instructionPointer + 8);
|
||||
std::string tempString = fmt::format("Unsupported lib call: {}", libFuncName);
|
||||
if (sUnsupportedHLECalls.find(tempString) == sUnsupportedHLECalls.end())
|
||||
if (s_unsupportedHLECalls.find(tempString) == s_unsupportedHLECalls.end())
|
||||
{
|
||||
cemuLog_log(LogType::UnsupportedAPI, "{}", tempString);
|
||||
sUnsupportedHLECalls.emplace(tempString);
|
||||
s_unsupportedHLECalls.emplace(tempString);
|
||||
}
|
||||
hCPU->gpr[3] = 0;
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
std::vector<void(*)(PPCInterpreter_t* hCPU)>* sPPCHLETable{};
|
||||
static constexpr size_t HLE_TABLE_CAPACITY = 0x4000;
|
||||
HLECALL s_ppcHleTable[HLE_TABLE_CAPACITY]{};
|
||||
sint32 s_ppcHleTableWriteIndex = 0;
|
||||
std::mutex s_ppcHleTableMutex;
|
||||
|
||||
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName)
|
||||
{
|
||||
if (!sPPCHLETable)
|
||||
sPPCHLETable = new std::vector<void(*)(PPCInterpreter_t* hCPU)>();
|
||||
for (sint32 i = 0; i < sPPCHLETable->size(); i++)
|
||||
std::unique_lock _l(s_ppcHleTableMutex);
|
||||
if (s_ppcHleTableWriteIndex >= HLE_TABLE_CAPACITY)
|
||||
{
|
||||
if ((*sPPCHLETable)[i] == hleCall)
|
||||
return i;
|
||||
cemuLog_log(LogType::Force, "HLE table is full");
|
||||
cemu_assert(false);
|
||||
}
|
||||
HLEIDX newFuncIndex = (sint32)sPPCHLETable->size();
|
||||
sPPCHLETable->resize(sPPCHLETable->size() + 1);
|
||||
(*sPPCHLETable)[newFuncIndex] = hleCall;
|
||||
return newFuncIndex;
|
||||
for (sint32 i = 0; i < s_ppcHleTableWriteIndex; i++)
|
||||
{
|
||||
if (s_ppcHleTable[i] == hleCall)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
cemu_assert(s_ppcHleTableWriteIndex < HLE_TABLE_CAPACITY);
|
||||
s_ppcHleTable[s_ppcHleTableWriteIndex] = hleCall;
|
||||
HLEIDX funcIndex = s_ppcHleTableWriteIndex;
|
||||
s_ppcHleTableWriteIndex++;
|
||||
return funcIndex;
|
||||
}
|
||||
|
||||
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex)
|
||||
{
|
||||
if (funcIndex < 0 || funcIndex >= sPPCHLETable->size())
|
||||
if (funcIndex < 0 || funcIndex >= HLE_TABLE_CAPACITY)
|
||||
return nullptr;
|
||||
return sPPCHLETable->data()[funcIndex];
|
||||
return s_ppcHleTable[funcIndex];
|
||||
}
|
||||
|
||||
std::mutex g_hleLogMutex;
|
||||
std::mutex s_hleLogMutex;
|
||||
|
||||
void PPCInterpreter_virtualHLE(PPCInterpreter_t* hCPU, unsigned int opcode)
|
||||
{
|
||||
uint32 hleFuncId = opcode & 0xFFFF;
|
||||
if (hleFuncId == 0xFFD0)
|
||||
if (hleFuncId == 0xFFD0) [[unlikely]]
|
||||
{
|
||||
g_hleLogMutex.lock();
|
||||
s_hleLogMutex.lock();
|
||||
PPCInterpreter_handleUnsupportedHLECall(hCPU);
|
||||
g_hleLogMutex.unlock();
|
||||
return;
|
||||
s_hleLogMutex.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
// os lib function
|
||||
cemu_assert(hleFuncId < sPPCHLETable->size());
|
||||
auto hleCall = (*sPPCHLETable)[hleFuncId];
|
||||
auto hleCall = PPCInterpreter_getHLECall(hleFuncId);
|
||||
cemu_assert(hleCall);
|
||||
hleCall(hCPU);
|
||||
}
|
||||
|
|
|
@ -428,9 +428,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
uint32 testIP[100];
|
||||
uint32 testIPC = 0;
|
||||
|
||||
template <typename ppcItpCtrl>
|
||||
class PPCInterpreterContainer
|
||||
{
|
||||
|
@ -466,6 +463,10 @@ public:
|
|||
case 1: // virtual HLE
|
||||
PPCInterpreter_virtualHLE(hCPU, opcode);
|
||||
break;
|
||||
case 3:
|
||||
cemuLog_logDebug(LogType::Force, "Unsupported TWI instruction executed at {:08x}", hCPU->instructionPointer);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
break;
|
||||
case 4:
|
||||
switch (PPC_getBits(opcode, 30, 5))
|
||||
{
|
||||
|
@ -482,8 +483,9 @@ public:
|
|||
PPCInterpreter_PS_CMPU1(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [4->0] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4->0] at {:08x}", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -509,8 +511,9 @@ public:
|
|||
PPCInterpreter_PS_ABS(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [4->8] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4->8] at {:08x}", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -548,8 +551,9 @@ public:
|
|||
PPCInterpreter_PS_MERGE11(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [4->16] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
||||
debugBreakpoint();
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4->16] at {:08x}", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -590,8 +594,9 @@ public:
|
|||
PPCInterpreter_PS_NMADD(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [4] at %08X\n", PPC_getBits(opcode, 30, 5), hCPU->instructionPointer);
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [4] at {:08x}", PPC_getBits(opcode, 30, 5), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -623,12 +628,15 @@ public:
|
|||
PPCInterpreter_BCX(hCPU, opcode);
|
||||
break;
|
||||
case 17:
|
||||
if (PPC_getBits(opcode, 30, 1) == 1) {
|
||||
if (PPC_getBits(opcode, 30, 1) == 1)
|
||||
{
|
||||
PPCInterpreter_SC(hCPU, opcode);
|
||||
}
|
||||
else {
|
||||
debug_printf("Unsupported Opcode [0x17 --> 0x0]\n");
|
||||
else
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "Unsupported Opcode [0x17 --> 0x0]");
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
}
|
||||
break;
|
||||
case 18:
|
||||
|
@ -658,6 +666,9 @@ public:
|
|||
case 193:
|
||||
PPCInterpreter_CRXOR(hCPU, opcode);
|
||||
break;
|
||||
case 225:
|
||||
PPCInterpreter_CRNAND(hCPU, opcode);
|
||||
break;
|
||||
case 257:
|
||||
PPCInterpreter_CRAND(hCPU, opcode);
|
||||
break;
|
||||
|
@ -674,8 +685,9 @@ public:
|
|||
PPCInterpreter_BCCTR(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [19] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [19] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -713,9 +725,6 @@ public:
|
|||
PPCInterpreter_CMP(hCPU, opcode);
|
||||
break;
|
||||
case 4:
|
||||
#ifdef CEMU_DEBUG_ASSERT
|
||||
debug_printf("TW instruction executed at %08x\n", hCPU->instructionPointer);
|
||||
#endif
|
||||
PPCInterpreter_TW(hCPU, opcode);
|
||||
break;
|
||||
case 8:
|
||||
|
@ -895,6 +904,12 @@ public:
|
|||
case 522:
|
||||
PPCInterpreter_ADDCO(hCPU, opcode);
|
||||
break;
|
||||
case 523: // 11 | OE
|
||||
PPCInterpreter_MULHWU_(hCPU, opcode); // OE is ignored
|
||||
break;
|
||||
case 533:
|
||||
PPCInterpreter_LSWX(hCPU, opcode);
|
||||
break;
|
||||
case 534:
|
||||
PPCInterpreter_LWBRX(hCPU, opcode);
|
||||
break;
|
||||
|
@ -913,6 +928,9 @@ public:
|
|||
case 567:
|
||||
PPCInterpreter_LFSUX(hCPU, opcode);
|
||||
break;
|
||||
case 587: // 75 | OE
|
||||
PPCInterpreter_MULHW_(hCPU, opcode); // OE is ignored for MULHW
|
||||
break;
|
||||
case 595:
|
||||
PPCInterpreter_MFSR(hCPU, opcode);
|
||||
break;
|
||||
|
@ -943,15 +961,30 @@ public:
|
|||
case 663:
|
||||
PPCInterpreter_STFSX(hCPU, opcode);
|
||||
break;
|
||||
case 661:
|
||||
PPCInterpreter_STSWX(hCPU, opcode);
|
||||
break;
|
||||
case 695:
|
||||
PPCInterpreter_STFSUX(hCPU, opcode);
|
||||
break;
|
||||
case 712: // 200 | OE
|
||||
PPCInterpreter_SUBFZEO(hCPU, opcode);
|
||||
break;
|
||||
case 714: // 202 | OE
|
||||
PPCInterpreter_ADDZEO(hCPU, opcode);
|
||||
break;
|
||||
case 725:
|
||||
PPCInterpreter_STSWI(hCPU, opcode);
|
||||
break;
|
||||
case 727:
|
||||
PPCInterpreter_STFDX(hCPU, opcode);
|
||||
break;
|
||||
case 744: // 232 | OE
|
||||
PPCInterpreter_SUBFMEO(hCPU, opcode);
|
||||
break;
|
||||
case 746: // 234 | OE
|
||||
PPCInterpreter_ADDMEO(hCPU, opcode);
|
||||
break;
|
||||
case 747:
|
||||
PPCInterpreter_MULLWO(hCPU, opcode);
|
||||
break;
|
||||
|
@ -998,10 +1031,8 @@ public:
|
|||
PPCInterpreter_DCBZ(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [31] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
#ifdef CEMU_DEBUG_ASSERT
|
||||
assert_dbg();
|
||||
#endif
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [31] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
break;
|
||||
}
|
||||
|
@ -1084,7 +1115,7 @@ public:
|
|||
case 57:
|
||||
PPCInterpreter_PSQ_LU(hCPU, opcode);
|
||||
break;
|
||||
case 59: //Opcode category
|
||||
case 59: // opcode category
|
||||
switch (PPC_getBits(opcode, 30, 5))
|
||||
{
|
||||
case 18:
|
||||
|
@ -1115,8 +1146,9 @@ public:
|
|||
PPCInterpreter_FNMADDS(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [59] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [59] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
hCPU->instructionPointer += 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -1195,18 +1227,19 @@ public:
|
|||
case 583:
|
||||
PPCInterpreter_MFFS(hCPU, opcode);
|
||||
break;
|
||||
case 711: // IBM documentation has this wrong as 771?
|
||||
case 711:
|
||||
PPCInterpreter_MTFSF(hCPU, opcode);
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X as [63] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} as [63] at {:08x}\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug_printf("Unknown execute %04X at %08X\n", PPC_getBits(opcode, 5, 6), (unsigned int)hCPU->instructionPointer);
|
||||
cemuLog_logDebug(LogType::Force, "Unknown execute {:04x} at {:08x}\n", PPC_getBits(opcode, 5, 6), (unsigned int)hCPU->instructionPointer);
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,9 +50,9 @@
|
|||
#define CR_BIT_EQ 2
|
||||
#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_SO (31) // summary overflow, counterpart to CR SO
|
||||
#define XER_BIT_OV (30)
|
||||
|
||||
// FPSCR
|
||||
#define FPSCR_VXSNAN (1<<24)
|
||||
|
@ -118,7 +118,8 @@
|
|||
|
||||
static inline void ppc_update_cr0(PPCInterpreter_t* hCPU, uint32 r)
|
||||
{
|
||||
hCPU->cr[CR_BIT_SO] = (hCPU->spr.XER&XER_SO) ? 1 : 0;
|
||||
cemu_assert_debug(hCPU->xer_so <= 1);
|
||||
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_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
|
||||
|
@ -190,8 +191,8 @@ inline double roundTo25BitAccuracy(double d)
|
|||
return *(double*)&v;
|
||||
}
|
||||
|
||||
double fres_espresso(double input);
|
||||
double frsqrte_espresso(double input);
|
||||
ATTR_MS_ABI double fres_espresso(double input);
|
||||
ATTR_MS_ABI double frsqrte_espresso(double input);
|
||||
|
||||
void fcmpu_espresso(PPCInterpreter_t* hCPU, int crfD, double a, double b);
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ static void PPCInterpreter_STW(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STWU(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS;
|
||||
sint32 rA, rS;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
|
||||
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)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, rB;
|
||||
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
|
||||
ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], hCPU->gpr[rS]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -85,7 +85,8 @@ static void PPCInterpreter_STWCX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
ppc_setCRBit(hCPU, CR_BIT_GT, 0);
|
||||
ppc_setCRBit(hCPU, CR_BIT_EQ, 1);
|
||||
}
|
||||
ppc_setCRBit(hCPU, CR_BIT_SO, (hCPU->spr.XER&XER_SO) != 0 ? 1 : 0);
|
||||
cemu_assert_debug(hCPU->xer_so <= 1);
|
||||
ppc_setCRBit(hCPU, CR_BIT_SO, hCPU->xer_so);
|
||||
// remove reservation
|
||||
hCPU->reservedMemAddr = 0;
|
||||
hCPU->reservedMemValue = 0;
|
||||
|
@ -102,7 +103,7 @@ static void PPCInterpreter_STWCX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STWUX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, rB;
|
||||
PPC_OPC_TEMPL_X(Opcode, rS, rA, rB);
|
||||
ppcItpCtrl::ppcMem_writeDataU32(hCPU, (rA ? hCPU->gpr[rA] : 0) + hCPU->gpr[rB], hCPU->gpr[rS]);
|
||||
if (rA)
|
||||
|
@ -112,7 +113,7 @@ static void PPCInterpreter_STWUX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STWBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, 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]));
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -120,7 +121,7 @@ static void PPCInterpreter_STWBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STMW(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rS, rA;
|
||||
sint32 rS, rA;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
|
||||
uint32 ea = (rA ? hCPU->gpr[rA] : 0) + imm;
|
||||
|
@ -135,7 +136,7 @@ static void PPCInterpreter_STMW(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STH(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS;
|
||||
sint32 rA, rS;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
|
||||
ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint16)hCPU->gpr[rS]);
|
||||
|
@ -144,7 +145,7 @@ static void PPCInterpreter_STH(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STHU(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS;
|
||||
sint32 rA, rS;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
|
||||
ppcItpCtrl::ppcMem_writeDataU16(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint16)hCPU->gpr[rS]);
|
||||
|
@ -155,7 +156,7 @@ static void PPCInterpreter_STHU(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STHX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, 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]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -163,7 +164,7 @@ static void PPCInterpreter_STHX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STHUX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, 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]);
|
||||
if (rA)
|
||||
|
@ -173,7 +174,7 @@ static void PPCInterpreter_STHUX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STHBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, 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]));
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -181,7 +182,7 @@ static void PPCInterpreter_STHBRX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STB(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS;
|
||||
sint32 rA, rS;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
|
||||
ppcItpCtrl::ppcMem_writeDataU8(hCPU, (rA ? hCPU->gpr[rA] : 0) + imm, (uint8)hCPU->gpr[rS]);
|
||||
|
@ -190,7 +191,7 @@ static void PPCInterpreter_STB(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STBU(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS;
|
||||
sint32 rA, rS;
|
||||
uint32 imm;
|
||||
PPC_OPC_TEMPL_D_SImm(Opcode, rS, rA, imm);
|
||||
ppcItpCtrl::ppcMem_writeDataU8(hCPU, hCPU->gpr[rA] + imm, (uint8)hCPU->gpr[rS]);
|
||||
|
@ -200,7 +201,7 @@ static void PPCInterpreter_STBU(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STBX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, 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]);
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
@ -208,7 +209,7 @@ static void PPCInterpreter_STBX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STBUX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, rB;
|
||||
sint32 rA, rS, 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]);
|
||||
if (rA)
|
||||
|
@ -218,7 +219,7 @@ static void PPCInterpreter_STBUX(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
|
||||
static void PPCInterpreter_STSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
|
||||
{
|
||||
int rA, rS, nb;
|
||||
sint32 rA, rS, nb;
|
||||
PPC_OPC_TEMPL_X(Opcode, rS, rA, nb);
|
||||
if (nb == 0) nb = 32;
|
||||
uint32 ea = rA ? hCPU->gpr[rA] : 0;
|
||||
|
@ -228,7 +229,39 @@ static void PPCInterpreter_STSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
{
|
||||
if (i == 0)
|
||||
{
|
||||
r = hCPU->gpr[rS];
|
||||
r = rS < 32 ? hCPU->gpr[rS] : 0; // what happens if rS is out of bounds?
|
||||
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 %= 32;
|
||||
i = 4;
|
||||
|
@ -459,7 +492,6 @@ static void PPCInterpreter_LSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
PPC_OPC_TEMPL_X(Opcode, rD, rA, nb);
|
||||
if (nb == 0)
|
||||
nb = 32;
|
||||
|
||||
uint32 ea = rA ? hCPU->gpr[rA] : 0;
|
||||
uint32 r = 0;
|
||||
int i = 4;
|
||||
|
@ -469,7 +501,8 @@ static void PPCInterpreter_LSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
if (i == 0)
|
||||
{
|
||||
i = 4;
|
||||
hCPU->gpr[rD] = r;
|
||||
if(rD < 32)
|
||||
hCPU->gpr[rD] = r;
|
||||
rD++;
|
||||
rD %= 32;
|
||||
r = 0;
|
||||
|
@ -486,7 +519,52 @@ static void PPCInterpreter_LSWI(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
r <<= 8;
|
||||
i--;
|
||||
}
|
||||
hCPU->gpr[rD] = r;
|
||||
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;
|
||||
ea += hCPU->gpr[rB];
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,16 +63,25 @@ void PPCInterpreter_setDEC(PPCInterpreter_t* hCPU, uint32 newValue)
|
|||
uint32 PPCInterpreter_getXER(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
uint32 xerValue = hCPU->spr.XER;
|
||||
xerValue &= ~(1<<XER_BIT_CA);
|
||||
if( hCPU->xer_ca )
|
||||
xerValue |= (1<<XER_BIT_CA);
|
||||
xerValue &= ~(1 << XER_BIT_CA);
|
||||
xerValue &= ~(1 << XER_BIT_SO);
|
||||
xerValue &= ~(1 << XER_BIT_OV);
|
||||
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;
|
||||
}
|
||||
|
||||
void PPCInterpreter_setXER(PPCInterpreter_t* hCPU, uint32 v)
|
||||
{
|
||||
hCPU->spr.XER = v;
|
||||
hCPU->xer_ca = (v>>XER_BIT_CA)&1;
|
||||
const uint32 XER_MASK = 0xE0FFFFFF; // some bits are masked out. Figure out which ones exactly
|
||||
hCPU->spr.XER = v & XER_MASK;
|
||||
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)
|
||||
|
@ -90,7 +99,7 @@ uint8* PPCInterpreterGetStackPointer()
|
|||
return memory_getPointerFromVirtualOffset(PPCInterpreter_getCurrentInstance()->gpr[1]);
|
||||
}
|
||||
|
||||
uint8* PPCInterpreterGetAndModifyStackPointer(sint32 offset)
|
||||
uint8* PPCInterpreter_PushAndReturnStackPointer(sint32 offset)
|
||||
{
|
||||
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
|
||||
uint8* result = memory_getPointerFromVirtualOffset(hCPU->gpr[1] - offset);
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h"
|
||||
|
||||
#include "../Recompiler/PPCRecompiler.h"
|
||||
#include "../Recompiler/PPCRecompilerX64.h"
|
||||
|
||||
#include <float.h>
|
||||
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
|
||||
|
@ -94,7 +93,6 @@ void PPCInterpreter_MTCRF(PPCInterpreter_t* hCPU, uint32 Opcode)
|
|||
{
|
||||
// frequently used by GCC compiled code (e.g. SM64 port)
|
||||
// tested
|
||||
|
||||
uint32 rS;
|
||||
uint32 crfMask;
|
||||
PPC_OPC_TEMPL_XFX(Opcode, rS, crfMask);
|
||||
|
|
|
@ -68,6 +68,8 @@ static void PPCInterpreter_TW(PPCInterpreter_t* hCPU, uint32 opcode)
|
|||
PPC_OPC_TEMPL_X(opcode, to, rA, rB);
|
||||
|
||||
cemu_assert_debug(to == 0);
|
||||
if(to != 0)
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
|
||||
if (rA == DEBUGGER_BP_T_DEBUGGER)
|
||||
debugger_enterTW(hCPU);
|
||||
|
|
|
@ -5,8 +5,28 @@ struct PPCCoreCallbackData_t
|
|||
{
|
||||
sint32 gprCount = 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
|
||||
inline uint32 PPCCoreCallback(MPTR function, const PPCCoreCallbackData_t& data)
|
||||
{
|
||||
|
@ -16,23 +36,21 @@ inline uint32 PPCCoreCallback(MPTR function, const PPCCoreCallbackData_t& data)
|
|||
template <typename T, typename... TArgs>
|
||||
uint32 PPCCoreCallback(MPTR function, PPCCoreCallbackData_t& data, T currentArg, TArgs... args)
|
||||
{
|
||||
cemu_assert_debug(data.gprCount <= 8);
|
||||
cemu_assert_debug(data.floatCount <= 8);
|
||||
// TODO float arguments on stack
|
||||
cemu_assert_debug(data.floatCount < 8);
|
||||
|
||||
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
{
|
||||
hCPU->gpr[3 + data.gprCount] = MEMPTR(currentArg).GetMPTR();
|
||||
data.gprCount++;
|
||||
_PPCCoreCallback_writeGPRArg(data, hCPU, MEMPTR(currentArg).GetMPTR());
|
||||
}
|
||||
else if constexpr (std::is_base_of_v<MEMPTRBase, std::remove_reference_t<T>>)
|
||||
{
|
||||
hCPU->gpr[3 + data.gprCount] = currentArg.GetMPTR();
|
||||
data.gprCount++;
|
||||
_PPCCoreCallback_writeGPRArg(data, hCPU, currentArg.GetMPTR());
|
||||
}
|
||||
else if constexpr (std::is_reference_v<T>)
|
||||
{
|
||||
hCPU->gpr[3 + data.gprCount] = MEMPTR(¤tArg).GetMPTR();
|
||||
data.gprCount++;
|
||||
_PPCCoreCallback_writeGPRArg(data, hCPU, MEMPTR(¤tArg).GetMPTR());
|
||||
}
|
||||
else if constexpr(std::is_enum_v<T>)
|
||||
{
|
||||
|
@ -53,8 +71,7 @@ uint32 PPCCoreCallback(MPTR function, PPCCoreCallbackData_t& data, T currentArg,
|
|||
}
|
||||
else
|
||||
{
|
||||
hCPU->gpr[3 + data.gprCount] = (uint32)currentArg;
|
||||
data.gprCount++;
|
||||
_PPCCoreCallback_writeGPRArg(data, hCPU, (uint32)currentArg);
|
||||
}
|
||||
|
||||
return PPCCoreCallback(function, data, args...);
|
||||
|
|
|
@ -49,12 +49,12 @@ struct PPCInterpreter_t
|
|||
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 xer_ca; // carry from xer
|
||||
uint8 LSQE;
|
||||
uint8 PSE;
|
||||
uint8 xer_so;
|
||||
uint8 xer_ov;
|
||||
// thread remaining cycles
|
||||
sint32 remainingCycles; // if this value goes below zero, the next thread is scheduled
|
||||
sint32 skippedCycles; // number of skipped cycles
|
||||
struct
|
||||
struct
|
||||
{
|
||||
uint32 LR;
|
||||
uint32 CTR;
|
||||
|
@ -67,9 +67,10 @@ struct PPCInterpreter_t
|
|||
uint32 reservedMemValue;
|
||||
// temporary storage for recompiler
|
||||
FPR_t temporaryFPR[8];
|
||||
uint32 temporaryGPR[4];
|
||||
uint32 temporaryGPR[4]; // deprecated, refactor backend dependency on this away
|
||||
uint32 temporaryGPR_reg[4];
|
||||
// values below this are not used by Cafe OS usermode
|
||||
struct
|
||||
struct
|
||||
{
|
||||
uint32 fpecr; // is this the same register as fpscr ?
|
||||
uint32 DEC;
|
||||
|
@ -84,7 +85,7 @@ struct PPCInterpreter_t
|
|||
// DMA
|
||||
uint32 dmaU;
|
||||
uint32 dmaL;
|
||||
// MMU
|
||||
// MMU
|
||||
uint32 dbatU[8];
|
||||
uint32 dbatL[8];
|
||||
uint32 ibatU[8];
|
||||
|
@ -92,6 +93,8 @@ struct PPCInterpreter_t
|
|||
uint32 sr[16];
|
||||
uint32 sdr1;
|
||||
}sprExtended;
|
||||
uint8 LSQE;
|
||||
uint8 PSE;
|
||||
// global CPU values
|
||||
PPCInterpreterGlobal_t* global;
|
||||
// interpreter control
|
||||
|
@ -213,7 +216,7 @@ void PPCTimer_start();
|
|||
// core info and control
|
||||
extern uint32 ppcThreadQuantum;
|
||||
|
||||
uint8* PPCInterpreterGetAndModifyStackPointer(sint32 offset);
|
||||
uint8* PPCInterpreter_PushAndReturnStackPointer(sint32 offset);
|
||||
uint8* PPCInterpreterGetStackPointer();
|
||||
void PPCInterpreterModifyStackPointer(sint32 offset);
|
||||
|
||||
|
@ -227,9 +230,9 @@ static inline float flushDenormalToZero(float f)
|
|||
|
||||
// HLE interface
|
||||
|
||||
typedef void(*HLECALL)(PPCInterpreter_t* hCPU);
|
||||
using HLECALL = void(*)(PPCInterpreter_t*);
|
||||
using HLEIDX = sint32;
|
||||
|
||||
typedef sint32 HLEIDX;
|
||||
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName);
|
||||
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "Cafe/HW/Espresso/Const.h"
|
||||
#include "asm/x64util.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "util/helpers/fspinlock.h"
|
||||
#include "util/highresolutiontimer/HighResolutionTimer.h"
|
||||
|
|
1695
src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
Normal file
1695
src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,18 @@
|
|||
#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
|
1672
src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.cpp
Normal file
1672
src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,104 +1,56 @@
|
|||
|
||||
typedef struct
|
||||
#include "../PPCRecompiler.h" // todo - get rid of dependency
|
||||
|
||||
#include "x86Emitter.h"
|
||||
|
||||
struct x64RelocEntry_t
|
||||
{
|
||||
x64RelocEntry_t(uint32 offset, void* extraInfo) : offset(offset), extraInfo(extraInfo) {};
|
||||
|
||||
uint32 offset;
|
||||
uint8 type;
|
||||
void* extraInfo;
|
||||
}x64RelocEntry_t;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
struct x64GenContext_t
|
||||
{
|
||||
uint8* codeBuffer;
|
||||
sint32 codeBufferIndex;
|
||||
sint32 codeBufferSize;
|
||||
// cr state
|
||||
sint32 activeCRRegister; // current x86 condition flags reflect this cr* register
|
||||
sint32 activeCRState; // describes the way in which x86 flags map to the cr register (signed / unsigned)
|
||||
IMLSegment* currentSegment{};
|
||||
x86Assembler64* emitter;
|
||||
sint32 m_currentInstructionEmitIndex;
|
||||
|
||||
x64GenContext_t()
|
||||
{
|
||||
emitter = new x86Assembler64();
|
||||
}
|
||||
|
||||
~x64GenContext_t()
|
||||
{
|
||||
delete emitter;
|
||||
}
|
||||
|
||||
IMLInstruction* GetNextInstruction(sint32 relativeIndex = 1)
|
||||
{
|
||||
sint32 index = m_currentInstructionEmitIndex + relativeIndex;
|
||||
if(index < 0 || index >= (sint32)currentSegment->imlList.size())
|
||||
return nullptr;
|
||||
return currentSegment->imlList.data() + index;
|
||||
}
|
||||
|
||||
// relocate offsets
|
||||
x64RelocEntry_t* relocateOffsetTable;
|
||||
sint32 relocateOffsetTableSize;
|
||||
sint32 relocateOffsetTableCount;
|
||||
}x64GenContext_t;
|
||||
|
||||
// Some of these are defined by winnt.h and gnu headers
|
||||
#undef REG_EAX
|
||||
#undef REG_ECX
|
||||
#undef REG_EDX
|
||||
#undef REG_EBX
|
||||
#undef REG_ESP
|
||||
#undef REG_EBP
|
||||
#undef REG_ESI
|
||||
#undef REG_EDI
|
||||
#undef REG_NONE
|
||||
#undef REG_RAX
|
||||
#undef REG_RCX
|
||||
#undef REG_RDX
|
||||
#undef REG_RBX
|
||||
#undef REG_RSP
|
||||
#undef REG_RBP
|
||||
#undef REG_RSI
|
||||
#undef REG_RDI
|
||||
#undef REG_R8
|
||||
#undef REG_R9
|
||||
#undef REG_R10
|
||||
#undef REG_R11
|
||||
#undef REG_R12
|
||||
#undef REG_R13
|
||||
#undef REG_R14
|
||||
#undef REG_R15
|
||||
|
||||
#define REG_EAX 0
|
||||
#define REG_ECX 1
|
||||
#define REG_EDX 2
|
||||
#define REG_EBX 3
|
||||
#define REG_ESP 4 // reserved for low half of hCPU pointer
|
||||
#define REG_EBP 5
|
||||
#define REG_ESI 6
|
||||
#define REG_EDI 7
|
||||
#define REG_NONE -1
|
||||
|
||||
#define REG_RAX 0
|
||||
#define REG_RCX 1
|
||||
#define REG_RDX 2
|
||||
#define REG_RBX 3
|
||||
#define REG_RSP 4 // reserved for hCPU pointer
|
||||
#define REG_RBP 5
|
||||
#define REG_RSI 6
|
||||
#define REG_RDI 7
|
||||
#define REG_R8 8
|
||||
#define REG_R9 9
|
||||
#define REG_R10 10
|
||||
#define REG_R11 11
|
||||
#define REG_R12 12
|
||||
#define REG_R13 13 // reserved to hold pointer to memory base? (Not decided yet)
|
||||
#define REG_R14 14 // reserved as temporary register
|
||||
#define REG_R15 15 // reserved for pointer to ppcRecompilerInstanceData
|
||||
|
||||
#define REG_AL 0
|
||||
#define REG_CL 1
|
||||
#define REG_DL 2
|
||||
#define REG_BL 3
|
||||
#define REG_AH 4
|
||||
#define REG_CH 5
|
||||
#define REG_DH 6
|
||||
#define REG_BH 7
|
||||
std::vector<x64RelocEntry_t> relocateOffsetTable2;
|
||||
};
|
||||
|
||||
// reserved registers
|
||||
#define REG_RESV_TEMP (REG_R14)
|
||||
#define REG_RESV_HCPU (REG_RSP)
|
||||
#define REG_RESV_MEMBASE (REG_R13)
|
||||
#define REG_RESV_RECDATA (REG_R15)
|
||||
#define REG_RESV_TEMP (X86_REG_R14)
|
||||
#define REG_RESV_HCPU (X86_REG_RSP)
|
||||
#define REG_RESV_MEMBASE (X86_REG_R13)
|
||||
#define REG_RESV_RECDATA (X86_REG_R15)
|
||||
|
||||
// reserved floating-point registers
|
||||
#define REG_RESV_FPR_TEMP (15)
|
||||
|
||||
#define reg32ToReg16(__x) (__x) // deprecated
|
||||
|
||||
extern sint32 x64Gen_registerMap[12];
|
||||
|
||||
#define tempToRealRegister(__x) (x64Gen_registerMap[__x])
|
||||
#define tempToRealFPRRegister(__x) (__x)
|
||||
#define reg32ToReg16(__x) (__x)
|
||||
|
||||
// deprecated condition flags
|
||||
enum
|
||||
{
|
||||
X86_CONDITION_EQUAL, // or zero
|
||||
|
@ -119,36 +71,23 @@ enum
|
|||
X86_CONDITION_NONE, // no condition, jump always
|
||||
};
|
||||
|
||||
#define PPCREC_CR_TEMPORARY (8) // never stored
|
||||
#define PPCREC_CR_STATE_TYPE_UNSIGNED_ARITHMETIC (0) // for signed arithmetic operations (ADD, CMPI)
|
||||
#define PPCREC_CR_STATE_TYPE_SIGNED_ARITHMETIC (1) // for unsigned arithmetic operations (ADD, CMPI)
|
||||
#define PPCREC_CR_STATE_TYPE_LOGICAL (2) // for unsigned operations (CMPLI)
|
||||
|
||||
#define X86_RELOC_MAKE_RELATIVE (0) // make code imm relative to instruction
|
||||
#define X64_RELOC_LINK_TO_PPC (1) // translate from ppc address to x86 offset
|
||||
#define X64_RELOC_LINK_TO_SEGMENT (2) // link to beginning of segment
|
||||
|
||||
#define PPC_X64_GPR_USABLE_REGISTERS (16-4)
|
||||
#define PPC_X64_FPR_USABLE_REGISTERS (16-1) // Use XMM0 - XMM14, XMM15 is the temp register
|
||||
|
||||
|
||||
bool PPCRecompiler_generateX64Code(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext);
|
||||
|
||||
void PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext);
|
||||
bool PPCRecompiler_generateX64Code(struct PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext);
|
||||
|
||||
void PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext_t* x64GenContext, sint32 jumpInstructionOffset, sint32 destinationOffset);
|
||||
|
||||
void PPCRecompilerX64Gen_generateRecompilerInterfaceFunctions();
|
||||
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction);
|
||||
bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed);
|
||||
bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction);
|
||||
bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed);
|
||||
bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed);
|
||||
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction);
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction);
|
||||
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_compare(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction);
|
||||
|
||||
// ASM gen
|
||||
void x64Gen_writeU8(x64GenContext_t* x64GenContext, uint8 v);
|
||||
|
@ -196,9 +135,6 @@ void x64Gen_or_reg64Low8_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstReg
|
|||
void x64Gen_and_reg64Low8_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 memRegister64, sint32 memImmS32);
|
||||
void x64Gen_mov_mem8Reg64_reg64Low8(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 memRegister64, sint32 memImmS32);
|
||||
|
||||
void x64Gen_lock_cmpxchg_mem32Reg64PlusReg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32, sint32 srcRegister);
|
||||
void x64Gen_lock_cmpxchg_mem32Reg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegister64, sint32 memImmS32, sint32 srcRegister);
|
||||
|
||||
void x64Gen_add_reg64_reg64(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister);
|
||||
void x64Gen_add_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister);
|
||||
void x64Gen_add_reg64_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32);
|
||||
|
@ -207,9 +143,6 @@ void x64Gen_sub_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 des
|
|||
void x64Gen_sub_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32);
|
||||
void x64Gen_sub_reg64_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32);
|
||||
void x64Gen_sub_mem32reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegister, sint32 memImmS32, uint64 immU32);
|
||||
void x64Gen_sbb_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister);
|
||||
void x64Gen_adc_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister);
|
||||
void x64Gen_adc_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32);
|
||||
void x64Gen_dec_mem32(x64GenContext_t* x64GenContext, sint32 memoryRegister, uint32 memoryImmU32);
|
||||
void x64Gen_imul_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 operandRegister);
|
||||
void x64Gen_idiv_reg64Low32(x64GenContext_t* x64GenContext, sint32 operandRegister);
|
||||
|
@ -241,9 +174,7 @@ void x64Gen_not_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister);
|
|||
void x64Gen_neg_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister);
|
||||
void x64Gen_cdq(x64GenContext_t* x64GenContext);
|
||||
|
||||
void x64Gen_bswap_reg64(x64GenContext_t* x64GenContext, sint32 destRegister);
|
||||
void x64Gen_bswap_reg64Lower32bit(x64GenContext_t* x64GenContext, sint32 destRegister);
|
||||
void x64Gen_bswap_reg64Lower16bit(x64GenContext_t* x64GenContext, sint32 destRegister);
|
||||
|
||||
void x64Gen_lzcnt_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister);
|
||||
void x64Gen_bsr_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister);
|
||||
|
@ -274,6 +205,7 @@ void x64Gen_movddup_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegi
|
|||
void x64Gen_movhlps_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
void x64Gen_movsd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
void x64Gen_movsd_memReg64_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32);
|
||||
void x64Gen_movsd_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32);
|
||||
void x64Gen_movlpd_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32);
|
||||
void x64Gen_unpcklpd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
void x64Gen_unpckhpd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
|
@ -299,6 +231,7 @@ void x64Gen_andps_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegist
|
|||
void x64Gen_pcmpeqd_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, uint32 memReg, uint32 memImmS32);
|
||||
void x64Gen_cvttpd2dq_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
void x64Gen_cvttsd2si_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 registerDest, sint32 xmmRegisterSrc);
|
||||
void x64Gen_cvtsi2sd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 registerSrc);
|
||||
void x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
void x64Gen_cvtpd2ps_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
void x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc);
|
||||
|
@ -329,4 +262,8 @@ void x64Gen_movBEZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext_t* x64G
|
|||
void x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32, sint32 srcRegister);
|
||||
|
||||
void x64Gen_shrx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB);
|
||||
void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB);
|
||||
void x64Gen_shrx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB);
|
||||
void x64Gen_sarx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB);
|
||||
void x64Gen_sarx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB);
|
||||
void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB);
|
||||
void x64Gen_shlx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB);
|
|
@ -1,5 +1,4 @@
|
|||
#include "PPCRecompiler.h"
|
||||
#include "PPCRecompilerX64.h"
|
||||
#include "BackendX64.h"
|
||||
|
||||
void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataRegister, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32);
|
||||
|
||||
|
@ -21,11 +20,10 @@ void _x64Gen_vex128_nds(x64GenContext_t* x64GenContext, uint8 opcodeMap, uint8 a
|
|||
x64Gen_writeU8(x64GenContext, opcode);
|
||||
}
|
||||
|
||||
#define VEX_PP_0F 0 // guessed
|
||||
#define VEX_PP_0F 0
|
||||
#define VEX_PP_66_0F 1
|
||||
#define VEX_PP_F3_0F 2 // guessed
|
||||
#define VEX_PP_F2_0F 3 // guessed
|
||||
|
||||
#define VEX_PP_F3_0F 2
|
||||
#define VEX_PP_F2_0F 3
|
||||
|
||||
void x64Gen_avx_VPUNPCKHQDQ_xmm_xmm_xmm(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 srcRegisterA, sint32 srcRegisterB)
|
||||
{
|
|
@ -1,5 +1,4 @@
|
|||
#include "PPCRecompiler.h"
|
||||
#include "PPCRecompilerX64.h"
|
||||
#include "BackendX64.h"
|
||||
|
||||
void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataRegister, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32);
|
||||
|
||||
|
@ -69,6 +68,34 @@ void x64Gen_shrx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 regist
|
|||
x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7));
|
||||
}
|
||||
|
||||
void x64Gen_shrx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB)
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0xC4);
|
||||
x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0));
|
||||
x64Gen_writeU8(x64GenContext, 0x7B - registerB * 8);
|
||||
x64Gen_writeU8(x64GenContext, 0xF7);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7));
|
||||
}
|
||||
|
||||
void x64Gen_sarx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB)
|
||||
{
|
||||
// SARX reg64, reg64, reg64
|
||||
x64Gen_writeU8(x64GenContext, 0xC4);
|
||||
x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0));
|
||||
x64Gen_writeU8(x64GenContext, 0xFA - registerB * 8);
|
||||
x64Gen_writeU8(x64GenContext, 0xF7);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7));
|
||||
}
|
||||
|
||||
void x64Gen_sarx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB)
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0xC4);
|
||||
x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0));
|
||||
x64Gen_writeU8(x64GenContext, 0x7A - registerB * 8);
|
||||
x64Gen_writeU8(x64GenContext, 0xF7);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7));
|
||||
}
|
||||
|
||||
void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB)
|
||||
{
|
||||
// SHLX reg64, reg64, reg64
|
||||
|
@ -77,4 +104,13 @@ void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 regist
|
|||
x64Gen_writeU8(x64GenContext, 0xF9 - registerB * 8);
|
||||
x64Gen_writeU8(x64GenContext, 0xF7);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7));
|
||||
}
|
||||
|
||||
void x64Gen_shlx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB)
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0xC4);
|
||||
x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0));
|
||||
x64Gen_writeU8(x64GenContext, 0x79 - registerB * 8);
|
||||
x64Gen_writeU8(x64GenContext, 0xF7);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7));
|
||||
}
|
469
src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp
Normal file
469
src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp
Normal file
|
@ -0,0 +1,469 @@
|
|||
#include "../PPCRecompiler.h"
|
||||
#include "../IML/IML.h"
|
||||
#include "BackendX64.h"
|
||||
#include "Common/cpu_features.h"
|
||||
|
||||
uint32 _regF64(IMLReg physReg);
|
||||
|
||||
uint32 _regI32(IMLReg r)
|
||||
{
|
||||
cemu_assert_debug(r.GetRegFormat() == IMLRegFormat::I32);
|
||||
return (uint32)r.GetRegID();
|
||||
}
|
||||
|
||||
static x86Assembler64::GPR32 _reg32(sint8 physRegId)
|
||||
{
|
||||
return (x86Assembler64::GPR32)physRegId;
|
||||
}
|
||||
|
||||
static x86Assembler64::GPR8_REX _reg8(IMLReg r)
|
||||
{
|
||||
cemu_assert_debug(r.GetRegFormat() == IMLRegFormat::I32); // currently bool regs are implemented as 32bit registers
|
||||
return (x86Assembler64::GPR8_REX)r.GetRegID();
|
||||
}
|
||||
|
||||
static x86Assembler64::GPR32 _reg32_from_reg8(x86Assembler64::GPR8_REX regId)
|
||||
{
|
||||
return (x86Assembler64::GPR32)regId;
|
||||
}
|
||||
|
||||
static x86Assembler64::GPR8_REX _reg8_from_reg32(x86Assembler64::GPR32 regId)
|
||||
{
|
||||
return (x86Assembler64::GPR8_REX)regId;
|
||||
}
|
||||
|
||||
// load from memory
|
||||
bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed)
|
||||
{
|
||||
sint32 realRegisterXMM = _regF64(imlInstruction->op_storeLoad.registerData);
|
||||
sint32 realRegisterMem = _regI32(imlInstruction->op_storeLoad.registerMem);
|
||||
sint32 realRegisterMem2 = PPC_REC_INVALID_REGISTER;
|
||||
if( indexed )
|
||||
realRegisterMem2 = _regI32(imlInstruction->op_storeLoad.registerMem2);
|
||||
uint8 mode = imlInstruction->op_storeLoad.mode;
|
||||
|
||||
if( mode == PPCREC_FPR_LD_MODE_SINGLE )
|
||||
{
|
||||
// load byte swapped single into temporary FPR
|
||||
if( indexed )
|
||||
{
|
||||
x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2);
|
||||
x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem);
|
||||
if(g_CPUFeatures.x86.movbe)
|
||||
x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32);
|
||||
else
|
||||
x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_CPUFeatures.x86.movbe)
|
||||
x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32);
|
||||
else
|
||||
x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32);
|
||||
}
|
||||
if(g_CPUFeatures.x86.movbe == false )
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
x64Gen_movd_xmmReg_reg64Low32(x64GenContext, realRegisterXMM, REG_RESV_TEMP);
|
||||
|
||||
if (imlInstruction->op_storeLoad.flags2.notExpanded)
|
||||
{
|
||||
// leave value as single
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, realRegisterXMM, realRegisterXMM);
|
||||
}
|
||||
}
|
||||
else if( mode == PPCREC_FPR_LD_MODE_DOUBLE )
|
||||
{
|
||||
if( g_CPUFeatures.x86.avx )
|
||||
{
|
||||
if( indexed )
|
||||
{
|
||||
// calculate offset
|
||||
x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem);
|
||||
x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2);
|
||||
// load value
|
||||
x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+0);
|
||||
x64GenContext->emitter->BSWAP_q(REG_RESV_TEMP);
|
||||
x64Gen_movq_xmmReg_reg64(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_TEMP);
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, realRegisterXMM, REG_RESV_FPR_TEMP);
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32+0);
|
||||
x64GenContext->emitter->BSWAP_q(REG_RESV_TEMP);
|
||||
x64Gen_movq_xmmReg_reg64(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_TEMP);
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, realRegisterXMM, REG_RESV_FPR_TEMP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( indexed )
|
||||
{
|
||||
// calculate offset
|
||||
x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem);
|
||||
x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2);
|
||||
// load double low part to temporaryFPR
|
||||
x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+0);
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
x64Emit_mov_mem32_reg64(x64GenContext, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR)+4, REG_RESV_TEMP);
|
||||
// calculate offset again
|
||||
x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem);
|
||||
x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2);
|
||||
// load double high part to temporaryFPR
|
||||
x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+4);
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
x64Emit_mov_mem32_reg64(x64GenContext, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR)+0, REG_RESV_TEMP);
|
||||
// load double from temporaryFPR
|
||||
x64Gen_movlpd_xmmReg_memReg64(x64GenContext, realRegisterXMM, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR));
|
||||
}
|
||||
else
|
||||
{
|
||||
// load double low part to temporaryFPR
|
||||
x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32+0);
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
x64Emit_mov_mem32_reg64(x64GenContext, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR)+4, REG_RESV_TEMP);
|
||||
// load double high part to temporaryFPR
|
||||
x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32+4);
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
x64Emit_mov_mem32_reg64(x64GenContext, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR)+0, REG_RESV_TEMP);
|
||||
// load double from temporaryFPR
|
||||
x64Gen_movlpd_xmmReg_memReg64(x64GenContext, realRegisterXMM, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// store to memory
|
||||
bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed)
|
||||
{
|
||||
sint32 realRegisterXMM = _regF64(imlInstruction->op_storeLoad.registerData);
|
||||
sint32 realRegisterMem = _regI32(imlInstruction->op_storeLoad.registerMem);
|
||||
sint32 realRegisterMem2 = PPC_REC_INVALID_REGISTER;
|
||||
if( indexed )
|
||||
realRegisterMem2 = _regI32(imlInstruction->op_storeLoad.registerMem2);
|
||||
uint8 mode = imlInstruction->op_storeLoad.mode;
|
||||
if( mode == PPCREC_FPR_ST_MODE_SINGLE )
|
||||
{
|
||||
if (imlInstruction->op_storeLoad.flags2.notExpanded)
|
||||
{
|
||||
// value is already in single format
|
||||
x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, realRegisterXMM);
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, realRegisterXMM);
|
||||
x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, REG_RESV_FPR_TEMP);
|
||||
}
|
||||
if(g_CPUFeatures.x86.movbe == false )
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
if( indexed )
|
||||
{
|
||||
if( realRegisterMem == realRegisterMem2 )
|
||||
assert_dbg();
|
||||
x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2);
|
||||
}
|
||||
if(g_CPUFeatures.x86.movbe)
|
||||
x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP);
|
||||
else
|
||||
x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP);
|
||||
if( indexed )
|
||||
{
|
||||
x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2);
|
||||
}
|
||||
}
|
||||
else if( mode == PPCREC_FPR_ST_MODE_DOUBLE )
|
||||
{
|
||||
if( indexed )
|
||||
{
|
||||
if( realRegisterMem == realRegisterMem2 )
|
||||
assert_dbg();
|
||||
x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2);
|
||||
}
|
||||
x64Gen_movsd_memReg64_xmmReg(x64GenContext, realRegisterXMM, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR));
|
||||
// store double low part
|
||||
x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR)+0);
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32+4, REG_RESV_TEMP);
|
||||
// store double high part
|
||||
x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryFPR)+4);
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32+0, REG_RESV_TEMP);
|
||||
if( indexed )
|
||||
{
|
||||
x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2);
|
||||
}
|
||||
}
|
||||
else if( mode == PPCREC_FPR_ST_MODE_UI32_FROM_PS0 )
|
||||
{
|
||||
x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, realRegisterXMM);
|
||||
x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP);
|
||||
if( indexed )
|
||||
{
|
||||
cemu_assert_debug(realRegisterMem == realRegisterMem2);
|
||||
x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2);
|
||||
x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP);
|
||||
x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2);
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("PPCRecompilerX64Gen_imlInstruction_fpr_store(): Unsupported mode %d\n", mode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// FPR op FPR
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction)
|
||||
{
|
||||
if( imlInstruction->operation == PPCREC_IML_OP_FPR_FLOAT_TO_INT )
|
||||
{
|
||||
uint32 regGpr = _regI32(imlInstruction->op_fpr_r_r.regR);
|
||||
uint32 regFpr = _regF64(imlInstruction->op_fpr_r_r.regA);
|
||||
x64Gen_cvttsd2si_reg64Low_xmmReg(x64GenContext, regGpr, regFpr);
|
||||
return;
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_INT_TO_FLOAT )
|
||||
{
|
||||
uint32 regFpr = _regF64(imlInstruction->op_fpr_r_r.regR);
|
||||
uint32 regGpr = _regI32(imlInstruction->op_fpr_r_r.regA);
|
||||
x64Gen_cvtsi2sd_xmmReg_xmmReg(x64GenContext, regFpr, regGpr);
|
||||
return;
|
||||
}
|
||||
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_BITCAST_INT_TO_FLOAT)
|
||||
{
|
||||
cemu_assert_debug(imlInstruction->op_fpr_r_r.regR.GetRegFormat() == IMLRegFormat::F64); // assuming target is always F64 for now
|
||||
cemu_assert_debug(imlInstruction->op_fpr_r_r.regA.GetRegFormat() == IMLRegFormat::I32); // supporting only 32bit floats as input for now
|
||||
// exact operation depends on size of types. Floats are automatically promoted to double if the target is F64
|
||||
uint32 regFpr = _regF64(imlInstruction->op_fpr_r_r.regR);
|
||||
if (imlInstruction->op_fpr_r_r.regA.GetRegFormat() == IMLRegFormat::I32)
|
||||
{
|
||||
uint32 regGpr = _regI32(imlInstruction->op_fpr_r_r.regA);
|
||||
x64Gen_movq_xmmReg_reg64(x64GenContext, regFpr, regGpr); // using reg32 as reg64 param here is ok. We'll refactor later
|
||||
// float to double
|
||||
x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, regFpr, regFpr);
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 regR = _regF64(imlInstruction->op_fpr_r_r.regR);
|
||||
uint32 regA = _regF64(imlInstruction->op_fpr_r_r.regA);
|
||||
if( imlInstruction->operation == PPCREC_IML_OP_FPR_ASSIGN )
|
||||
{
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY )
|
||||
{
|
||||
x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_DIVIDE )
|
||||
{
|
||||
x64Gen_divsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ADD )
|
||||
{
|
||||
x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SUB )
|
||||
{
|
||||
x64Gen_subsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_FCTIWZ )
|
||||
{
|
||||
x64Gen_cvttsd2si_xmmReg_xmmReg(x64GenContext, REG_RESV_TEMP, regA);
|
||||
x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP);
|
||||
// move to FPR register
|
||||
x64Gen_movq_xmmReg_reg64(x64GenContext, regR, REG_RESV_TEMP);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FPR = op (fprA, fprB)
|
||||
*/
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction)
|
||||
{
|
||||
uint32 regR = _regF64(imlInstruction->op_fpr_r_r_r.regR);
|
||||
uint32 regA = _regF64(imlInstruction->op_fpr_r_r_r.regA);
|
||||
uint32 regB = _regF64(imlInstruction->op_fpr_r_r_r.regB);
|
||||
|
||||
if (imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY)
|
||||
{
|
||||
if (regR == regA)
|
||||
{
|
||||
x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regB);
|
||||
}
|
||||
else if (regR == regB)
|
||||
{
|
||||
x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regB);
|
||||
}
|
||||
}
|
||||
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_ADD)
|
||||
{
|
||||
// todo: Use AVX 3-operand VADDSD if available
|
||||
if (regR == regA)
|
||||
{
|
||||
x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regB);
|
||||
}
|
||||
else if (regR == regB)
|
||||
{
|
||||
x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regB);
|
||||
}
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SUB )
|
||||
{
|
||||
if( regR == regA )
|
||||
{
|
||||
x64Gen_subsd_xmmReg_xmmReg(x64GenContext, regR, regB);
|
||||
}
|
||||
else if( regR == regB )
|
||||
{
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA);
|
||||
x64Gen_subsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regB);
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP);
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regA);
|
||||
x64Gen_subsd_xmmReg_xmmReg(x64GenContext, regR, regB);
|
||||
}
|
||||
}
|
||||
else
|
||||
assert_dbg();
|
||||
}
|
||||
|
||||
/*
|
||||
* FPR = op (fprA, fprB, fprC)
|
||||
*/
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction)
|
||||
{
|
||||
uint32 regR = _regF64(imlInstruction->op_fpr_r_r_r_r.regR);
|
||||
uint32 regA = _regF64(imlInstruction->op_fpr_r_r_r_r.regA);
|
||||
uint32 regB = _regF64(imlInstruction->op_fpr_r_r_r_r.regB);
|
||||
uint32 regC = _regF64(imlInstruction->op_fpr_r_r_r_r.regC);
|
||||
|
||||
if( imlInstruction->operation == PPCREC_IML_OP_FPR_SELECT )
|
||||
{
|
||||
x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext, regA, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_constDouble0_0));
|
||||
sint32 jumpInstructionOffset1 = x64GenContext->emitter->GetWriteIndex();
|
||||
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_UNSIGNED_BELOW, 0);
|
||||
// select C
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regC);
|
||||
sint32 jumpInstructionOffset2 = x64GenContext->emitter->GetWriteIndex();
|
||||
x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0);
|
||||
// select B
|
||||
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->emitter->GetWriteIndex());
|
||||
x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regB);
|
||||
// end
|
||||
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->emitter->GetWriteIndex());
|
||||
}
|
||||
else
|
||||
assert_dbg();
|
||||
}
|
||||
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction)
|
||||
{
|
||||
uint32 regR = _regF64(imlInstruction->op_fpr_r.regR);
|
||||
|
||||
if( imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATE )
|
||||
{
|
||||
x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom));
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_LOAD_ONE )
|
||||
{
|
||||
x64Gen_movsd_xmmReg_memReg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_constDouble1_1));
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ABS )
|
||||
{
|
||||
x64Gen_andps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_andAbsMaskBottom));
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATIVE_ABS )
|
||||
{
|
||||
x64Gen_orps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom));
|
||||
}
|
||||
else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM )
|
||||
{
|
||||
// convert to 32bit single
|
||||
x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext, regR, regR);
|
||||
// convert back to 64bit double
|
||||
x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, regR, regR);
|
||||
}
|
||||
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_EXPAND_F32_TO_F64)
|
||||
{
|
||||
// convert bottom to 64bit double
|
||||
x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, regR, regR);
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
}
|
||||
|
||||
void PPCRecompilerX64Gen_imlInstruction_fpr_compare(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction)
|
||||
{
|
||||
auto regR = _reg8(imlInstruction->op_fpr_compare.regR);
|
||||
auto regA = _regF64(imlInstruction->op_fpr_compare.regA);
|
||||
auto regB = _regF64(imlInstruction->op_fpr_compare.regB);
|
||||
|
||||
x64GenContext->emitter->XOR_dd(_reg32_from_reg8(regR), _reg32_from_reg8(regR));
|
||||
x64Gen_ucomisd_xmmReg_xmmReg(x64GenContext, regA, regB);
|
||||
|
||||
if (imlInstruction->op_fpr_compare.cond == IMLCondition::UNORDERED_GT)
|
||||
{
|
||||
// GT case can be covered with a single SETnbe which checks CF==0 && ZF==0 (unordered sets both)
|
||||
x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_NBE, regR);
|
||||
return;
|
||||
}
|
||||
else if (imlInstruction->op_fpr_compare.cond == IMLCondition::UNORDERED_U)
|
||||
{
|
||||
// unordered case can be checked via PF
|
||||
x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_PE, regR);
|
||||
return;
|
||||
}
|
||||
|
||||
// remember unordered state
|
||||
auto regTmp = _reg32_from_reg8(_reg32(REG_RESV_TEMP));
|
||||
x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_PO, regTmp); // by reversing the parity we can avoid having to XOR the value for masking the LT/EQ conditions
|
||||
|
||||
X86Cond x86Cond;
|
||||
switch (imlInstruction->op_fpr_compare.cond)
|
||||
{
|
||||
case IMLCondition::UNORDERED_LT:
|
||||
x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_B, regR);
|
||||
break;
|
||||
case IMLCondition::UNORDERED_EQ:
|
||||
x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_Z, regR);
|
||||
break;
|
||||
default:
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
x64GenContext->emitter->AND_bb(_reg8_from_reg32(regR), _reg8_from_reg32(regTmp)); // if unordered (PF=1) then force LT/GT/EQ to zero
|
||||
}
|
|
@ -1,62 +1,31 @@
|
|||
#include "PPCRecompiler.h"
|
||||
#include "PPCRecompilerIml.h"
|
||||
#include "PPCRecompilerX64.h"
|
||||
#include "BackendX64.h"
|
||||
|
||||
// x86/x64 extension opcodes that could be useful:
|
||||
// ANDN
|
||||
// mulx, rorx, sarx, shlx, shrx
|
||||
// PDEP, PEXT
|
||||
|
||||
void x64Gen_checkBuffer(x64GenContext_t* x64GenContext)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
void x64Gen_writeU8(x64GenContext_t* x64GenContext, uint8 v)
|
||||
{
|
||||
if( x64GenContext->codeBufferIndex+1 > x64GenContext->codeBufferSize )
|
||||
{
|
||||
x64GenContext->codeBufferSize *= 2;
|
||||
x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize);
|
||||
}
|
||||
*(uint8*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v;
|
||||
x64GenContext->codeBufferIndex++;
|
||||
x64GenContext->emitter->_emitU8(v);
|
||||
}
|
||||
|
||||
void x64Gen_writeU16(x64GenContext_t* x64GenContext, uint32 v)
|
||||
{
|
||||
if( x64GenContext->codeBufferIndex+2 > x64GenContext->codeBufferSize )
|
||||
{
|
||||
x64GenContext->codeBufferSize *= 2;
|
||||
x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize);
|
||||
}
|
||||
*(uint16*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v;
|
||||
x64GenContext->codeBufferIndex += 2;
|
||||
x64GenContext->emitter->_emitU16(v);
|
||||
}
|
||||
|
||||
void x64Gen_writeU32(x64GenContext_t* x64GenContext, uint32 v)
|
||||
{
|
||||
if( x64GenContext->codeBufferIndex+4 > x64GenContext->codeBufferSize )
|
||||
{
|
||||
x64GenContext->codeBufferSize *= 2;
|
||||
x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize);
|
||||
}
|
||||
*(uint32*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v;
|
||||
x64GenContext->codeBufferIndex += 4;
|
||||
x64GenContext->emitter->_emitU32(v);
|
||||
}
|
||||
|
||||
void x64Gen_writeU64(x64GenContext_t* x64GenContext, uint64 v)
|
||||
{
|
||||
if( x64GenContext->codeBufferIndex+8 > x64GenContext->codeBufferSize )
|
||||
{
|
||||
x64GenContext->codeBufferSize *= 2;
|
||||
x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize);
|
||||
}
|
||||
*(uint64*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v;
|
||||
x64GenContext->codeBufferIndex += 8;
|
||||
x64GenContext->emitter->_emitU64(v);
|
||||
}
|
||||
|
||||
#include "x64Emit.hpp"
|
||||
#include "X64Emit.hpp"
|
||||
|
||||
void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataRegister, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32)
|
||||
{
|
||||
|
@ -67,7 +36,7 @@ void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataReg
|
|||
forceUseOffset = true;
|
||||
}
|
||||
|
||||
if (memRegisterB64 == REG_NONE)
|
||||
if (memRegisterB64 == X86_REG_NONE)
|
||||
{
|
||||
// memRegisterA64 + memImmS32
|
||||
uint8 modRM = (dataRegister & 7) * 8 + (memRegisterA64 & 7);
|
||||
|
@ -352,7 +321,7 @@ void x64Gen_mov_mem32Reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis
|
|||
void x64Gen_mov_mem64Reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegister, uint32 memImmU32, uint32 dataImmU32)
|
||||
{
|
||||
// MOV QWORD [<memReg>+<memImmU32>], dataImmU32
|
||||
if( memRegister == REG_R14 )
|
||||
if( memRegister == X86_REG_R14 )
|
||||
{
|
||||
sint32 memImmS32 = (sint32)memImmU32;
|
||||
if( memImmS32 == 0 )
|
||||
|
@ -384,7 +353,7 @@ void x64Gen_mov_mem64Reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis
|
|||
void x64Gen_mov_mem8Reg64_imm8(x64GenContext_t* x64GenContext, sint32 memRegister, uint32 memImmU32, uint8 dataImmU8)
|
||||
{
|
||||
// MOV BYTE [<memReg64>+<memImmU32>], dataImmU8
|
||||
if( memRegister == REG_RSP )
|
||||
if( memRegister == X86_REG_RSP )
|
||||
{
|
||||
sint32 memImmS32 = (sint32)memImmU32;
|
||||
if( memImmS32 >= -128 && memImmS32 <= 127 )
|
||||
|
@ -625,7 +594,7 @@ void _x64_op_reg64Low_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstRegist
|
|||
if (memRegister64 >= 8)
|
||||
x64Gen_writeU8(x64GenContext, 0x41);
|
||||
x64Gen_writeU8(x64GenContext, opByte);
|
||||
_x64Gen_writeMODRMDeprecated(x64GenContext, dstRegister, memRegister64, REG_NONE, memImmS32);
|
||||
_x64Gen_writeMODRMDeprecated(x64GenContext, dstRegister, memRegister64, X86_REG_NONE, memImmS32);
|
||||
}
|
||||
|
||||
void x64Gen_or_reg64Low8_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 memRegister64, sint32 memImmS32)
|
||||
|
@ -643,40 +612,6 @@ void x64Gen_mov_mem8Reg64_reg64Low8(x64GenContext_t* x64GenContext, sint32 dstRe
|
|||
_x64_op_reg64Low_mem8Reg64(x64GenContext, dstRegister, memRegister64, memImmS32, 0x88);
|
||||
}
|
||||
|
||||
void x64Gen_lock_cmpxchg_mem32Reg64PlusReg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32, sint32 srcRegister)
|
||||
{
|
||||
// LOCK CMPXCHG DWORD [<reg64> + <reg64> + <imm64>], <srcReg64> (low dword)
|
||||
x64Gen_writeU8(x64GenContext, 0xF0); // LOCK prefix
|
||||
|
||||
if( srcRegister >= 8 || memRegisterA64 >= 8|| memRegisterB64 >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x40+((srcRegister>=8)?4:0)+((memRegisterA64>=8)?1:0)+((memRegisterB64>=8)?2:0));
|
||||
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
x64Gen_writeU8(x64GenContext, 0xB1);
|
||||
|
||||
_x64Gen_writeMODRMDeprecated(x64GenContext, srcRegister, memRegisterA64, memRegisterB64, memImmS32);
|
||||
}
|
||||
|
||||
void x64Gen_lock_cmpxchg_mem32Reg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegister64, sint32 memImmS32, sint32 srcRegister)
|
||||
{
|
||||
// LOCK CMPXCHG DWORD [<reg64> + <imm64>], <srcReg64> (low dword)
|
||||
x64Gen_writeU8(x64GenContext, 0xF0); // LOCK prefix
|
||||
|
||||
if( srcRegister >= 8 || memRegister64 >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x40+((srcRegister>=8)?4:0)+((memRegister64>=8)?1:0));
|
||||
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
x64Gen_writeU8(x64GenContext, 0xB1);
|
||||
|
||||
if( memImmS32 == 0 )
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x45+(srcRegister&7)*8);
|
||||
x64Gen_writeU8(x64GenContext, 0x00);
|
||||
}
|
||||
else
|
||||
assert_dbg();
|
||||
}
|
||||
|
||||
void x64Gen_add_reg64_reg64(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister)
|
||||
{
|
||||
// ADD <destReg>, <srcReg>
|
||||
|
@ -732,7 +667,7 @@ void x64Gen_add_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis
|
|||
}
|
||||
else
|
||||
{
|
||||
if( srcRegister == REG_RAX )
|
||||
if( srcRegister == X86_REG_RAX )
|
||||
{
|
||||
// special EAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0x05);
|
||||
|
@ -772,7 +707,7 @@ void x64Gen_sub_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis
|
|||
}
|
||||
else
|
||||
{
|
||||
if( srcRegister == REG_RAX )
|
||||
if( srcRegister == X86_REG_RAX )
|
||||
{
|
||||
// special EAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0x2D);
|
||||
|
@ -811,7 +746,7 @@ void x64Gen_sub_mem32reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis
|
|||
{
|
||||
// SUB <mem32_memReg64>, <imm32>
|
||||
sint32 immS32 = (sint32)immU32;
|
||||
if( memRegister == REG_RSP )
|
||||
if( memRegister == X86_REG_RSP )
|
||||
{
|
||||
if( memImmS32 >= 128 )
|
||||
{
|
||||
|
@ -843,64 +778,11 @@ void x64Gen_sub_mem32reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis
|
|||
}
|
||||
}
|
||||
|
||||
void x64Gen_sbb_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister)
|
||||
{
|
||||
// SBB <destReg64_low32>, <srcReg64_low32>
|
||||
if( destRegister >= 8 && srcRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x45);
|
||||
else if( srcRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x44);
|
||||
else if( destRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x41);
|
||||
x64Gen_writeU8(x64GenContext, 0x19);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0+(srcRegister&7)*8+(destRegister&7));
|
||||
}
|
||||
|
||||
void x64Gen_adc_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister)
|
||||
{
|
||||
// ADC <destReg64_low32>, <srcReg64_low32>
|
||||
if( destRegister >= 8 && srcRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x45);
|
||||
else if( srcRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x44);
|
||||
else if( destRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x41);
|
||||
x64Gen_writeU8(x64GenContext, 0x11);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0+(srcRegister&7)*8+(destRegister&7));
|
||||
}
|
||||
|
||||
void x64Gen_adc_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32)
|
||||
{
|
||||
sint32 immS32 = (sint32)immU32;
|
||||
if( srcRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x41);
|
||||
if( immS32 >= -128 && immS32 <= 127 )
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x83);
|
||||
x64Gen_writeU8(x64GenContext, 0xD0+(srcRegister&7));
|
||||
x64Gen_writeU8(x64GenContext, (uint8)immS32);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( srcRegister == REG_RAX )
|
||||
{
|
||||
// special EAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0x15);
|
||||
}
|
||||
else
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x81);
|
||||
x64Gen_writeU8(x64GenContext, 0xD0+(srcRegister&7));
|
||||
}
|
||||
x64Gen_writeU32(x64GenContext, immU32);
|
||||
}
|
||||
}
|
||||
|
||||
void x64Gen_dec_mem32(x64GenContext_t* x64GenContext, sint32 memoryRegister, uint32 memoryImmU32)
|
||||
{
|
||||
// DEC dword [<reg64>+imm]
|
||||
sint32 memoryImmS32 = (sint32)memoryImmU32;
|
||||
if (memoryRegister != REG_RSP)
|
||||
if (memoryRegister != X86_REG_RSP)
|
||||
assert_dbg(); // not supported yet
|
||||
if (memoryImmS32 >= -128 && memoryImmS32 <= 127)
|
||||
{
|
||||
|
@ -981,7 +863,7 @@ void x64Gen_and_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis
|
|||
}
|
||||
else
|
||||
{
|
||||
if( srcRegister == REG_RAX )
|
||||
if( srcRegister == X86_REG_RAX )
|
||||
{
|
||||
// special EAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0x25);
|
||||
|
@ -1026,7 +908,7 @@ void x64Gen_test_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegi
|
|||
sint32 immS32 = (sint32)immU32;
|
||||
if( srcRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x41);
|
||||
if( srcRegister == REG_RAX )
|
||||
if( srcRegister == X86_REG_RAX )
|
||||
{
|
||||
// special EAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0xA9);
|
||||
|
@ -1052,7 +934,7 @@ void x64Gen_cmp_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis
|
|||
}
|
||||
else
|
||||
{
|
||||
if( srcRegister == REG_RAX )
|
||||
if( srcRegister == X86_REG_RAX )
|
||||
{
|
||||
// special RAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0x3D);
|
||||
|
@ -1082,7 +964,7 @@ void x64Gen_cmp_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 des
|
|||
void x64Gen_cmp_reg64Low32_mem32reg64(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 memRegister, sint32 memImmS32)
|
||||
{
|
||||
// CMP <destReg64_lowDWORD>, DWORD [<memRegister>+<immS32>]
|
||||
if( memRegister == REG_RSP )
|
||||
if( memRegister == X86_REG_RSP )
|
||||
{
|
||||
if( memImmS32 >= -128 && memImmS32 <= 127 )
|
||||
assert_dbg(); // todo -> Shorter instruction form
|
||||
|
@ -1112,7 +994,7 @@ void x64Gen_or_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegist
|
|||
}
|
||||
else
|
||||
{
|
||||
if( srcRegister == REG_RAX )
|
||||
if( srcRegister == X86_REG_RAX )
|
||||
{
|
||||
// special EAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0x0D);
|
||||
|
@ -1172,7 +1054,7 @@ void x64Gen_xor_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis
|
|||
}
|
||||
else
|
||||
{
|
||||
if( srcRegister == REG_RAX )
|
||||
if( srcRegister == X86_REG_RAX )
|
||||
{
|
||||
// special EAX short form
|
||||
x64Gen_writeU8(x64GenContext, 0x35);
|
||||
|
@ -1326,16 +1208,6 @@ void x64Gen_cdq(x64GenContext_t* x64GenContext)
|
|||
x64Gen_writeU8(x64GenContext, 0x99);
|
||||
}
|
||||
|
||||
void x64Gen_bswap_reg64(x64GenContext_t* x64GenContext, sint32 destRegister)
|
||||
{
|
||||
if( destRegister >= 8 )
|
||||
x64Gen_writeU8(x64GenContext, 0x41|8);
|
||||
else
|
||||
x64Gen_writeU8(x64GenContext, 0x40|8);
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
x64Gen_writeU8(x64GenContext, 0xC8+(destRegister&7));
|
||||
}
|
||||
|
||||
void x64Gen_bswap_reg64Lower32bit(x64GenContext_t* x64GenContext, sint32 destRegister)
|
||||
{
|
||||
if( destRegister >= 8 )
|
||||
|
@ -1344,16 +1216,6 @@ void x64Gen_bswap_reg64Lower32bit(x64GenContext_t* x64GenContext, sint32 destReg
|
|||
x64Gen_writeU8(x64GenContext, 0xC8+(destRegister&7));
|
||||
}
|
||||
|
||||
void x64Gen_bswap_reg64Lower16bit(x64GenContext_t* x64GenContext, sint32 destRegister)
|
||||
{
|
||||
assert_dbg(); // do not use this instruction, it's result is always undefined. Instead use ROL <reg16>, 8
|
||||
//x64Gen_writeU8(x64GenContext, 0x66);
|
||||
//if( destRegister >= 8 )
|
||||
// x64Gen_writeU8(x64GenContext, 0x41);
|
||||
//x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
//x64Gen_writeU8(x64GenContext, 0xC8+(destRegister&7));
|
||||
}
|
||||
|
||||
void x64Gen_lzcnt_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister)
|
||||
{
|
||||
// SSE4
|
||||
|
@ -1388,7 +1250,7 @@ void x64Gen_setcc_mem8(x64GenContext_t* x64GenContext, sint32 conditionType, sin
|
|||
{
|
||||
// SETcc [<reg64>+imm]
|
||||
sint32 memoryImmS32 = (sint32)memoryImmU32;
|
||||
if( memoryRegister != REG_RSP )
|
||||
if( memoryRegister != X86_REG_RSP )
|
||||
assert_dbg(); // not supported
|
||||
if( memoryRegister >= 8 )
|
||||
assert_dbg(); // not supported
|
||||
|
@ -1627,7 +1489,7 @@ void x64Gen_bt_mem8(x64GenContext_t* x64GenContext, sint32 memoryRegister, uint3
|
|||
{
|
||||
// BT [<reg64>+imm], bitIndex (bit test)
|
||||
sint32 memoryImmS32 = (sint32)memoryImmU32;
|
||||
if( memoryRegister != REG_RSP )
|
||||
if( memoryRegister != X86_REG_RSP )
|
||||
assert_dbg(); // not supported yet
|
||||
if( memoryImmS32 >= -128 && memoryImmS32 <= 127 )
|
||||
{
|
||||
|
@ -1662,7 +1524,7 @@ void x64Gen_jmp_imm32(x64GenContext_t* x64GenContext, uint32 destImm32)
|
|||
|
||||
void x64Gen_jmp_memReg64(x64GenContext_t* x64GenContext, sint32 memRegister, uint32 immU32)
|
||||
{
|
||||
if( memRegister == REG_NONE )
|
||||
if( memRegister == X86_REG_NONE )
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
#include "PPCRecompiler.h"
|
||||
#include "PPCRecompilerIml.h"
|
||||
#include "PPCRecompilerX64.h"
|
||||
#include "BackendX64.h"
|
||||
|
||||
void x64Gen_genSSEVEXPrefix2(x64GenContext_t* x64GenContext, sint32 xmmRegister1, sint32 xmmRegister2, bool use64BitMode)
|
||||
{
|
||||
|
@ -44,7 +42,7 @@ void x64Gen_movupd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
// SSE2
|
||||
// move two doubles from memory into xmm register
|
||||
// MOVUPD <xmm>, [<reg>+<imm>]
|
||||
if( memRegister == REG_ESP )
|
||||
if( memRegister == X86_REG_ESP )
|
||||
{
|
||||
// todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range
|
||||
// 66 0F 10 84 E4 23 01 00 00
|
||||
|
@ -56,7 +54,7 @@ void x64Gen_movupd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
x64Gen_writeU8(x64GenContext, 0xE4);
|
||||
x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
}
|
||||
else if( memRegister == REG_NONE )
|
||||
else if( memRegister == X86_REG_NONE )
|
||||
{
|
||||
assert_dbg();
|
||||
//x64Gen_writeU8(x64GenContext, 0x66);
|
||||
|
@ -76,7 +74,7 @@ void x64Gen_movupd_memReg128_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
// SSE2
|
||||
// move two doubles from memory into xmm register
|
||||
// MOVUPD [<reg>+<imm>], <xmm>
|
||||
if( memRegister == REG_ESP )
|
||||
if( memRegister == X86_REG_ESP )
|
||||
{
|
||||
// todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range
|
||||
x64Gen_writeU8(x64GenContext, 0x66);
|
||||
|
@ -87,7 +85,7 @@ void x64Gen_movupd_memReg128_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
x64Gen_writeU8(x64GenContext, 0xE4);
|
||||
x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
}
|
||||
else if( memRegister == REG_NONE )
|
||||
else if( memRegister == X86_REG_NONE )
|
||||
{
|
||||
assert_dbg();
|
||||
//x64Gen_writeU8(x64GenContext, 0x66);
|
||||
|
@ -106,7 +104,7 @@ void x64Gen_movddup_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
{
|
||||
// SSE3
|
||||
// move one double from memory into lower and upper half of a xmm register
|
||||
if( memRegister == REG_RSP )
|
||||
if( memRegister == X86_REG_RSP )
|
||||
{
|
||||
// MOVDDUP <xmm>, [<reg>+<imm>]
|
||||
// todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range
|
||||
|
@ -119,7 +117,7 @@ void x64Gen_movddup_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
x64Gen_writeU8(x64GenContext, 0xE4);
|
||||
x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
}
|
||||
else if( memRegister == REG_R15 )
|
||||
else if( memRegister == X86_REG_R15 )
|
||||
{
|
||||
// MOVDDUP <xmm>, [<reg>+<imm>]
|
||||
// todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range
|
||||
|
@ -131,7 +129,7 @@ void x64Gen_movddup_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
x64Gen_writeU8(x64GenContext, 0x87+(xmmRegister&7)*8);
|
||||
x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
}
|
||||
else if( memRegister == REG_NONE )
|
||||
else if( memRegister == X86_REG_NONE )
|
||||
{
|
||||
// MOVDDUP <xmm>, [<imm>]
|
||||
// 36 F2 0F 12 05 - 00 00 00 00
|
||||
|
@ -185,7 +183,7 @@ void x64Gen_movsd_memReg64_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegi
|
|||
{
|
||||
// SSE2
|
||||
// move lower 64bits (double) of xmm register to memory location
|
||||
if( memRegister == REG_NONE )
|
||||
if( memRegister == X86_REG_NONE )
|
||||
{
|
||||
// MOVSD [<imm>], <xmm>
|
||||
// F2 0F 11 05 - 45 23 01 00
|
||||
|
@ -197,7 +195,7 @@ void x64Gen_movsd_memReg64_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegi
|
|||
//x64Gen_writeU8(x64GenContext, 0x05+xmmRegister*8);
|
||||
//x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
}
|
||||
else if( memRegister == REG_RSP )
|
||||
else if( memRegister == X86_REG_RSP )
|
||||
{
|
||||
// MOVSD [RSP+<imm>], <xmm>
|
||||
// F2 0F 11 84 24 - 33 22 11 00
|
||||
|
@ -215,11 +213,42 @@ void x64Gen_movsd_memReg64_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegi
|
|||
}
|
||||
}
|
||||
|
||||
void x64Gen_movsd_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32)
|
||||
{
|
||||
// SSE2
|
||||
if( memRegister == X86_REG_RSP )
|
||||
{
|
||||
// MOVSD <xmm>, [RSP+<imm>]
|
||||
x64Gen_writeU8(x64GenContext, 0xF2);
|
||||
x64Gen_genSSEVEXPrefix2(x64GenContext, 0, xmmRegister, false);
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
x64Gen_writeU8(x64GenContext, 0x10);
|
||||
x64Gen_writeU8(x64GenContext, 0x84+(xmmRegister&7)*8);
|
||||
x64Gen_writeU8(x64GenContext, 0x24);
|
||||
x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
}
|
||||
else if( memRegister == 15 )
|
||||
{
|
||||
// MOVSD <xmm>, [R15+<imm>]
|
||||
x64Gen_writeU8(x64GenContext, 0x36);
|
||||
x64Gen_writeU8(x64GenContext, 0xF2);
|
||||
x64Gen_genSSEVEXPrefix2(x64GenContext, memRegister, xmmRegister, false);
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
x64Gen_writeU8(x64GenContext, 0x10);
|
||||
x64Gen_writeU8(x64GenContext, 0x87+(xmmRegister&7)*8);
|
||||
x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
||||
}
|
||||
|
||||
void x64Gen_movlpd_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32)
|
||||
{
|
||||
// SSE3
|
||||
// move one double from memory into lower half of a xmm register, leave upper half unchanged(?)
|
||||
if( memRegister == REG_NONE )
|
||||
if( memRegister == X86_REG_NONE )
|
||||
{
|
||||
// MOVLPD <xmm>, [<imm>]
|
||||
//x64Gen_writeU8(x64GenContext, 0x66);
|
||||
|
@ -229,7 +258,7 @@ void x64Gen_movlpd_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmReg
|
|||
//x64Gen_writeU32(x64GenContext, memImmU32);
|
||||
assert_dbg();
|
||||
}
|
||||
else if( memRegister == REG_RSP )
|
||||
else if( memRegister == X86_REG_RSP )
|
||||
{
|
||||
// MOVLPD <xmm>, [<reg64>+<imm>]
|
||||
// 66 0F 12 84 24 - 33 22 11 00
|
||||
|
@ -348,11 +377,11 @@ void x64Gen_mulpd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegist
|
|||
void x64Gen_mulpd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32)
|
||||
{
|
||||
// SSE2
|
||||
if (memRegister == REG_NONE)
|
||||
if (memRegister == X86_REG_NONE)
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
||||
else if (memRegister == REG_R14)
|
||||
else if (memRegister == X86_REG_R14)
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x66);
|
||||
x64Gen_writeU8(x64GenContext, (xmmRegister < 8) ? 0x41 : 0x45);
|
||||
|
@ -404,7 +433,7 @@ void x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext_t* x64GenContext, sint32 xmmR
|
|||
{
|
||||
// SSE2
|
||||
// compare bottom double with double from memory location
|
||||
if( memoryReg == REG_R15 )
|
||||
if( memoryReg == X86_REG_R15 )
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x66);
|
||||
x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true);
|
||||
|
@ -432,7 +461,7 @@ void x64Gen_comiss_xmmReg_mem64Reg64(x64GenContext_t* x64GenContext, sint32 xmmR
|
|||
{
|
||||
// SSE2
|
||||
// compare bottom float with float from memory location
|
||||
if (memoryReg == REG_R15)
|
||||
if (memoryReg == X86_REG_R15)
|
||||
{
|
||||
x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true);
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
|
@ -448,7 +477,7 @@ void x64Gen_orps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmRe
|
|||
{
|
||||
// SSE2
|
||||
// and xmm register with 128 bit value from memory
|
||||
if( memReg == REG_R15 )
|
||||
if( memReg == X86_REG_R15 )
|
||||
{
|
||||
x64Gen_genSSEVEXPrefix2(x64GenContext, memReg, xmmRegisterDest, false);
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
|
@ -464,7 +493,7 @@ void x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmR
|
|||
{
|
||||
// SSE2
|
||||
// xor xmm register with 128 bit value from memory
|
||||
if( memReg == REG_R15 )
|
||||
if( memReg == X86_REG_R15 )
|
||||
{
|
||||
x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true); // todo: should be x64Gen_genSSEVEXPrefix2() with memReg?
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
|
@ -479,11 +508,11 @@ void x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmR
|
|||
void x64Gen_andpd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32)
|
||||
{
|
||||
// SSE2
|
||||
if (memRegister == REG_NONE)
|
||||
if (memRegister == X86_REG_NONE)
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
||||
else if (memRegister == REG_R14)
|
||||
else if (memRegister == X86_REG_R14)
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x66);
|
||||
x64Gen_writeU8(x64GenContext, (xmmRegister < 8) ? 0x41 : 0x45);
|
||||
|
@ -502,7 +531,7 @@ void x64Gen_andps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmR
|
|||
{
|
||||
// SSE2
|
||||
// and xmm register with 128 bit value from memory
|
||||
if( memReg == REG_R15 )
|
||||
if( memReg == X86_REG_R15 )
|
||||
{
|
||||
x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true); // todo: should be x64Gen_genSSEVEXPrefix2() with memReg?
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
|
@ -528,7 +557,7 @@ void x64Gen_pcmpeqd_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xm
|
|||
{
|
||||
// SSE2
|
||||
// doubleword integer compare
|
||||
if( memReg == REG_R15 )
|
||||
if( memReg == X86_REG_R15 )
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x66);
|
||||
x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true);
|
||||
|
@ -563,6 +592,16 @@ void x64Gen_cvttsd2si_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 regis
|
|||
x64Gen_writeU8(x64GenContext, 0xC0+(registerDest&7)*8+(xmmRegisterSrc&7));
|
||||
}
|
||||
|
||||
void x64Gen_cvtsi2sd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 registerSrc)
|
||||
{
|
||||
// SSE2
|
||||
x64Gen_writeU8(x64GenContext, 0xF2);
|
||||
x64Gen_genSSEVEXPrefix2(x64GenContext, registerSrc, xmmRegisterDest, false);
|
||||
x64Gen_writeU8(x64GenContext, 0x0F);
|
||||
x64Gen_writeU8(x64GenContext, 0x2A);
|
||||
x64Gen_writeU8(x64GenContext, 0xC0+(xmmRegisterDest&7)*8+(registerSrc&7));
|
||||
}
|
||||
|
||||
void x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegisterDest, sint32 xmmRegisterSrc)
|
||||
{
|
||||
// SSE2
|
||||
|
@ -610,7 +649,7 @@ void x64Gen_cvtpi2pd_xmmReg_mem64Reg64(x64GenContext_t* x64GenContext, sint32 xm
|
|||
{
|
||||
// SSE2
|
||||
// converts two signed 32bit integers to two doubles
|
||||
if( memReg == REG_RSP )
|
||||
if( memReg == X86_REG_RSP )
|
||||
{
|
||||
x64Gen_writeU8(x64GenContext, 0x66);
|
||||
x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, false);
|
||||
|
@ -684,7 +723,7 @@ void x64Gen_rcpss_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegist
|
|||
void x64Gen_mulss_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32)
|
||||
{
|
||||
// SSE2
|
||||
if( memRegister == REG_NONE )
|
||||
if( memRegister == X86_REG_NONE )
|
||||
{
|
||||
assert_dbg();
|
||||
}
|
|
@ -203,7 +203,6 @@ template<class opcodeBytes, typename TA, typename TB>
|
|||
void _x64Gen_writeMODRM_internal(x64GenContext_t* x64GenContext, TA opA, TB opB)
|
||||
{
|
||||
static_assert(TA::getType() == MODRM_OPR_TYPE::REG);
|
||||
x64Gen_checkBuffer(x64GenContext);
|
||||
// REX prefix
|
||||
// 0100 WRXB
|
||||
if constexpr (TA::getType() == MODRM_OPR_TYPE::REG && TB::getType() == MODRM_OPR_TYPE::REG)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue