mirror of
https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git
synced 2025-04-02 11:11:52 -04:00
Compare commits
325 commits
CI-2833314
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
dd36dd598c | ||
|
87634a2e27 | ||
|
6f32d89545 | ||
|
ec0c288bc4 | ||
|
8bfbcb56fd | ||
|
8965d2443b | ||
|
b33ed95c5b | ||
|
8ee17b512c | ||
|
50334cbc31 | ||
|
41454b8c26 | ||
|
204dcf8801 | ||
|
77c63ceec3 | ||
|
2cfaba893e | ||
|
17b0cb81d4 | ||
|
daa6a816ff | ||
|
6caf3ea679 | ||
|
9a58823b70 | ||
|
3edd8d168b | ||
|
f894d31332 | ||
|
9241bec768 | ||
|
c50a0c5c7d | ||
|
87bab04932 | ||
|
ad6769bbf3 | ||
|
0e63131fc3 | ||
|
889040c56a | ||
|
86542c9f2e | ||
|
c9edbd1003 | ||
|
ebb122f2a0 | ||
|
c158a472ff | ||
|
6961d1c7a1 | ||
|
2f7cfe7e95 | ||
|
46d0173673 | ||
|
c7b028b3e7 | ||
|
3d12edc77d | ||
|
08ab4b9164 | ||
|
4fca5c7007 | ||
|
e26f20108a | ||
|
8475124e5b | ||
|
9b2ae106e5 | ||
|
b3bfeca3a8 | ||
|
b77a13b708 | ||
|
1b5e111ae3 | ||
|
1504a75a46 | ||
|
87496ab873 | ||
|
5b37a7ec21 | ||
|
639f42c318 | ||
|
8d92992a6b | ||
|
7323eed73e | ||
|
b47c1f195c | ||
|
7c73bfc525 | ||
|
1b4a3bb54f | ||
|
750d202fa8 | ||
|
e7bca5e1bf | ||
|
937ab9e1c2 | ||
|
8006f55cf3 | ||
|
1828ddfd6f | ||
|
bc42cfaa6b | ||
|
b1235b7733 | ||
|
1615ecc976 | ||
|
0007d20b03 | ||
|
bfb10092c0 | ||
|
f5b4878245 | ||
|
4174fbc23f | ||
|
0560ed6955 | ||
|
875015164c | ||
|
aedb5ba87b | ||
|
e5dcdebe7f | ||
|
ad0b8340da | ||
|
7298b6c4dc | ||
|
b20db36e15 | ||
|
a03d50df56 | ||
|
0e25897f77 | ||
|
c6049b768c | ||
|
399baccadb | ||
|
3edc388abf | ||
|
796e8d2beb | ||
|
282c5f5622 | ||
|
8e5b27d054 | ||
|
b64a3b6faa | ||
|
d64e172c9f | ||
|
d6b96b8ea1 | ||
|
131b330a85 | ||
|
bf6193202a | ||
|
06c28a847e | ||
|
8cc9c73f58 | ||
|
2452965580 | ||
|
eddc14e151 | ||
|
31ff15ba1d | ||
|
4d9151ca26 | ||
|
1f1d1ac631 | ||
|
e5043dbc05 | ||
|
a2fb41856d | ||
|
b09d3ca69a | ||
|
684d3338f2 | ||
|
ae140bb6bf | ||
|
a5b8f15a14 | ||
|
2c8a764fc7 | ||
|
605271245c | ||
|
93e36f7be3 | ||
|
a7bc6a307d | ||
|
7ad047bcea | ||
|
0f21e25d7d | ||
|
79884bdf3d | ||
|
260e2fb7c8 | ||
|
4d221c3c81 | ||
|
712d3bee2f | ||
|
3cd551d827 | ||
|
397f33143d | ||
|
c7e75d7c5c | ||
|
4808be65c4 | ||
|
def10ff466 | ||
|
e1ea10c4cb | ||
|
67f21d5c30 | ||
|
971318a89a | ||
|
b62d39da7d | ||
|
6c530fbf86 | ||
|
a8f6d0496e | ||
|
6389cb6524 | ||
|
ef3439e46f | ||
|
ed8a6124e4 | ||
|
b1bd9dd5d0 | ||
|
062752e1a7 | ||
|
99ab34ac82 | ||
|
8e0df988a8 | ||
|
58041c95b4 | ||
|
827a3212f8 | ||
|
d0890d588d | ||
|
9e9d3f390f | ||
|
4821a72b6f | ||
|
bf1483ae56 | ||
|
b2f05b8b0b | ||
|
111728f170 | ||
|
ce05ea1397 | ||
|
f4488c0270 | ||
|
65a5ad6591 | ||
|
0b695637ce | ||
|
fe9a706a8e | ||
|
8ac5d14cd2 | ||
|
628323218a | ||
|
f7c09ddc4f | ||
|
09e744ecc4 | ||
|
a37124e2dc | ||
|
5f58ae918c | ||
|
f17b7f7fa6 | ||
|
4dccf6d5b9 | ||
|
6bbe6cefe8 | ||
|
547c3ae663 | ||
|
c594e34ac5 | ||
|
caae99952c | ||
|
aeeb67dc6a | ||
|
4204640052 | ||
|
3c78dd29a7 | ||
|
42ff76ce0f | ||
|
bf931d2d81 | ||
|
a2a4fb35a4 | ||
|
f570c05e95 | ||
|
0b76da7c00 | ||
|
9ee5b45b88 | ||
|
ee7be21bbb | ||
|
8fcc2f5e0c | ||
|
56610cd899 | ||
|
9fab1d5bed | ||
|
0043e45531 | ||
|
bc9cbec518 | ||
|
4edd3feb3e | ||
|
0717c0166b | ||
|
16ffe3a80f | ||
|
5f3cfdeb77 | ||
|
a650fd2078 | ||
|
cfa7be71cf | ||
|
4076a5b758 | ||
|
6e3635d90a | ||
|
ce55fe8627 | ||
|
b2f63918de | ||
|
f6274cc59f | ||
|
8546d7c10d | ||
|
4c5995af0c | ||
|
c981ff23b1 | ||
|
186b5fa8ee | ||
|
ce4f4a07f0 | ||
|
4d110bad6e | ||
|
6f79b035bd | ||
|
7bc95d7a67 | ||
|
5a454aad5c | ||
|
6cbb385b89 | ||
|
31a47cde37 | ||
|
aba8fc8341 | ||
|
46b1f24153 | ||
|
b43f6bbcdf | ||
|
33aad02f93 | ||
|
1710f01c35 | ||
|
f8e4f59eae | ||
|
4b5edbdc94 | ||
|
3bf2effa4d | ||
|
6ab30793ed | ||
|
3d244b78b3 | ||
|
2512840968 | ||
|
ecef7aec39 | ||
|
95b789eb27 | ||
|
5e928e508a | ||
|
39ced81d58 | ||
|
b7006e2b01 | ||
|
fa85d3dad4 | ||
|
05a7acf13e | ||
|
8b35389c71 | ||
|
e8f943ebbc | ||
|
87042c6bcc | ||
|
88a37ac496 | ||
|
2168b033c6 | ||
|
a1cffc79f8 | ||
|
1077115038 | ||
|
774ef7c9e3 | ||
|
9973ec7b6f | ||
|
5e42d181f2 | ||
|
23488ad22b | ||
|
8b0b016aec | ||
|
cd09cf8dfd | ||
|
374ba5ec70 | ||
|
3a50d7e136 | ||
|
696d49820c | ||
|
2a3656bf24 | ||
|
469352e72a | ||
|
72908248eb | ||
|
e9212c35f7 | ||
|
4b5bef273d | ||
|
a338a12d77 | ||
|
88b686c27d | ||
|
b84e80663f | ||
|
351c9a7c86 | ||
|
6d395bf2ee | ||
|
57640cad49 | ||
|
1d030aa41f | ||
|
c723c56e4a | ||
|
5f7b9417b0 | ||
|
877c87537e | ||
|
9f87854040 | ||
|
38eeef7c0d | ||
|
5d87ad46c6 | ||
|
7b9634a3ec | ||
|
8dd54df4a8 | ||
|
c5fe198bb2 | ||
|
a81ec74d06 | ||
|
82427abcb8 | ||
|
f0a1301b64 | ||
|
d9785e7225 | ||
|
067aa82cc7 | ||
|
5d5fc992da | ||
|
dc1f93b120 | ||
|
d0b4c6abac | ||
|
09c5eb405d | ||
|
3908a8c7b2 | ||
|
51ddb6c1d1 | ||
|
bfa9abbc1b | ||
|
aac207c78e | ||
|
49b4988953 | ||
|
da27e1456b | ||
|
baa1cf5470 | ||
|
feb1f0383d | ||
|
6788bf16f3 | ||
|
b1446a5f6b | ||
|
0a72fbf7f7 | ||
|
4186d4ba8b | ||
|
4c30264136 | ||
|
7528d9a4e3 | ||
|
60dbf241e8 | ||
|
852adf0d21 | ||
|
1e300d63ec | ||
|
5b0cf41507 | ||
|
0c8dd778d0 | ||
|
25b9a2efcc | ||
|
3a59c62753 | ||
|
884a9080b5 | ||
|
f857593f77 | ||
|
d3b2554b20 | ||
|
f174872702 | ||
|
c6ea72dcf4 | ||
|
bc98e164b2 | ||
|
6867907a3c | ||
|
7589f0a94c | ||
|
a769e896c6 | ||
|
44ed2ee3aa | ||
|
06f34134ff | ||
|
6320dd5539 | ||
|
8c7247abf5 | ||
|
0b90a48434 | ||
|
e85af190d5 | ||
|
733670c7f8 | ||
|
79ac0c3019 | ||
|
ec6b16c68a | ||
|
e9cc351bba | ||
|
e208c73586 | ||
|
7e5f9a7cb7 | ||
|
b39801df11 | ||
|
a791b7609c | ||
|
b664488274 | ||
|
114be1b7c9 | ||
|
607a48e3ea | ||
|
9082891903 | ||
|
484a2c3f47 | ||
|
c883034372 | ||
|
41d45dd88d | ||
|
86022747f0 | ||
|
1125c1c45d | ||
|
7d116628c2 | ||
|
f41cc02c6c | ||
|
da72da4d03 | ||
|
db1bae2d4e | ||
|
59fe8eb6db | ||
|
be4fb1deb6 | ||
|
1e05973b81 | ||
|
79b3b4e803 | ||
|
02b9d75b38 | ||
|
349f28c6cb | ||
|
4625a34eec | ||
|
5ac2d3e152 | ||
|
1b10e1b9d4 | ||
|
822f4f9b9a | ||
|
c65d26a284 | ||
|
54d3ee11bb | ||
|
50b969549f | ||
|
f8b449d6b1 | ||
|
a844dffa95 | ||
|
a25e455289 | ||
|
f41f73f6c3 | ||
|
c1fb2d665b |
173 changed files with 9062 additions and 4980 deletions
113
.github/labeler.yml
vendored
113
.github/labeler.yml
vendored
|
@ -1,74 +1,111 @@
|
|||
#labels are sorted by alphabet order.
|
||||
# Labels are in alphabetical order.
|
||||
|
||||
cmake:
|
||||
- CMake*
|
||||
- '**/CMakeLists.txt'
|
||||
- '**/*.cmake'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'CMake*'
|
||||
- '**/CMakeLists.txt'
|
||||
- '**/*.cmake'
|
||||
|
||||
cpu-emulation:
|
||||
- src/devices/x86/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/devices/x86/**'
|
||||
|
||||
deployment:
|
||||
- '*.yml'
|
||||
- .github/workflows/CI.yml
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- '*.yml'
|
||||
- '.github/workflows/CI.yml'
|
||||
|
||||
file-system:
|
||||
- src/core/kernel/support/EmuFile*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/support/EmuFile*'
|
||||
|
||||
graphics:
|
||||
- src/core/hle/D3D8/**
|
||||
- src/core/hle/XGRAPHIC/**
|
||||
- src/devices/video/**
|
||||
- src/gui/*Video*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/hle/D3D8/**'
|
||||
- 'src/core/hle/XGRAPHIC/**'
|
||||
- 'src/devices/video/**'
|
||||
- 'src/gui/*Video*'
|
||||
|
||||
HLE:
|
||||
- src/core/hle/**
|
||||
- src/core/kernel/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/hle/**'
|
||||
- 'src/core/kernel/**'
|
||||
|
||||
informational:
|
||||
- '**/*Logging*'
|
||||
- '**/*Logging*/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- '**/*Logging*'
|
||||
- '**/*Logging*/**'
|
||||
- '**/README.md'
|
||||
|
||||
input:
|
||||
- src/common/input/**
|
||||
- src/core/hle/XAPI/input/**
|
||||
- src/devices/usb/**
|
||||
- src/gui/controllers/**
|
||||
- src/gui/*Input*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/common/input/**'
|
||||
- 'src/core/hle/XAPI/input/**'
|
||||
- 'src/devices/usb/**'
|
||||
- 'src/gui/controllers/**'
|
||||
- 'src/gui/*Input*'
|
||||
|
||||
kernel:
|
||||
- src/core/kernel/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/**'
|
||||
|
||||
LLE:
|
||||
- src/devices/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/devices/**'
|
||||
|
||||
memory:
|
||||
- src/core/kernel/memory-manager/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/memory-manager/**'
|
||||
|
||||
networking:
|
||||
- src/core/hle/XONLINE/**
|
||||
- src/devices/network/**
|
||||
- src/gui/*Network*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/hle/XONLINE/**'
|
||||
- 'src/devices/network/**'
|
||||
- 'src/gui/*Network*'
|
||||
|
||||
sound:
|
||||
- src/common/audio/**
|
||||
- src/core/hle/DSOUND/**
|
||||
- src/core/hle/XACTENG/**
|
||||
- src/devices/audio/**
|
||||
- src/gui/*Audio*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/common/audio/**'
|
||||
- 'src/core/hle/DSOUND/**'
|
||||
- 'src/core/hle/XACTENG/**'
|
||||
- 'src/devices/audio/**'
|
||||
- 'src/gui/*Audio*'
|
||||
|
||||
modules:
|
||||
- import/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'import/**'
|
||||
|
||||
threading:
|
||||
- src/core/kernel/support/EmuFS*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/support/EmuFS*'
|
||||
|
||||
timing:
|
||||
- src/common/Timer*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/common/Timer*'
|
||||
|
||||
user interface:
|
||||
- src/gui/**
|
||||
# - TODO: Dear Imgui location, maybe in src/core/gui path.
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/common/imgui/*'
|
||||
- 'src/gui/**'
|
||||
|
||||
xbdm:
|
||||
- src/common/xbdm/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/common/xbdm/**'
|
||||
|
|
50
.github/workflows/CI.yml
vendored
50
.github/workflows/CI.yml
vendored
|
@ -3,51 +3,51 @@ name: GitHub CI
|
|||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '.gitattributes'
|
||||
- '.github/*'
|
||||
- '.github/*_TEMPLATE/**'
|
||||
- '.gitignore'
|
||||
- '*.bat'
|
||||
- '*.yml'
|
||||
- 'doc/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.gitattributes'
|
||||
- '.github/*'
|
||||
- '.github/*_TEMPLATE/**'
|
||||
- '.gitignore'
|
||||
- '*.bat'
|
||||
- '*.yml'
|
||||
- 'doc/**'
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Build (Windows, ${{ matrix.configuration }}, VS${{ matrix.vsver }}) # runner.os doesn't work here
|
||||
runs-on: windows-${{ matrix.vsver }}
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration: [Release, Debug]
|
||||
vsver: [VS2019, VS2017]
|
||||
include:
|
||||
- vsver: VS2019
|
||||
os: windows-latest
|
||||
- vsver: VS2017
|
||||
os: windows-2016 # https://github.com/actions/virtual-environments/issues/68#issuecomment-602652021
|
||||
vsver: [2019]
|
||||
winver: [7]
|
||||
sdkver: [10.0.22621.0]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Generate CMake files
|
||||
run: cmake -B build -A Win32
|
||||
run: cmake -B build -A Win32,version=${{ matrix.sdkver }} -DCMAKE_SYSTEM_VERSION=${{ matrix.winver }}
|
||||
- name: Build
|
||||
working-directory: build
|
||||
run: cmake --build . --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS
|
||||
run: cmake --build build --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS
|
||||
- name: Prepare artifacts
|
||||
if: matrix.configuration == 'Release'
|
||||
working-directory: build
|
||||
run: cmake --install . --config ${{ matrix.configuration }} --prefix ../artifacts
|
||||
- uses: actions/upload-artifact@v2
|
||||
run: cmake --install build --config ${{ matrix.configuration }} --prefix artifacts
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.configuration == 'Release'
|
||||
with:
|
||||
name: CxbxReloaded-${{ matrix.configuration }}-${{ matrix.vsver }}
|
||||
name: CxbxReloaded-${{ matrix.configuration }}-VS${{ matrix.vsver }}
|
||||
path: artifacts/bin
|
||||
if-no-files-found: error
|
||||
|
||||
|
@ -59,23 +59,23 @@ jobs:
|
|||
needs: build-windows
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Re-zip Artifacts
|
||||
id: git
|
||||
- name: Re-zip artifacts
|
||||
id: zip
|
||||
run: |
|
||||
for artifact in artifacts/*; do
|
||||
7z a $artifact.zip "./$artifact/*"
|
||||
if [[ $(stat -c %s $artifact.zip) -le 1000 ]]; then
|
||||
echo "Error: Archive $artifact.zip too small!"
|
||||
7z a ${artifact}.zip "./${artifact}/*"
|
||||
if [ $(stat -c %s ${artifact}.zip) -le 100000 ]; then
|
||||
echo "Error: Archive ${artifact}.zip too small!"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "::set-output name=tag_name::CI-$(git rev-parse --short HEAD)"
|
||||
echo "tag_name=CI-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||||
- name: Create Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh release create ${{ steps.git.outputs.tag_name }} artifacts/*.zip -p --target $GITHUB_SHA --title '${{ steps.git.outputs.tag_name }}'
|
||||
run: gh release create ${{ steps.zip.outputs.tag_name }} artifacts/*.zip -p --target $GITHUB_SHA --title '${{ steps.zip.outputs.tag_name }}'
|
||||
|
|
6
.github/workflows/autoclose.yml
vendored
6
.github/workflows/autoclose.yml
vendored
|
@ -1,15 +1,15 @@
|
|||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
types: opened
|
||||
|
||||
jobs:
|
||||
auto_close_issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
- name: Automatically close issues that don't follow the issue template
|
||||
uses: ergo720/auto-close-issues@v1.0.4
|
||||
uses: ergo720/auto-close-issues@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-close-message: "@${issue.user.login}: your issue has been automatically closed because it does not follow the issue template." # optional property
|
||||
|
|
5
.github/workflows/pull-request.yml
vendored
5
.github/workflows/pull-request.yml
vendored
|
@ -1,14 +1,13 @@
|
|||
name: Pull Request Manager
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
on: pull_request_target
|
||||
|
||||
jobs:
|
||||
pr_manager:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Labeler
|
||||
uses: actions/labeler@v3
|
||||
uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sync-labels: true
|
||||
|
|
12
.gitmodules
vendored
12
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
|||
[submodule "import/subhook"]
|
||||
path = import/subhook
|
||||
url = https://github.com/Zeex/subhook.git
|
||||
url = https://github.com/Cxbx-Reloaded/subhook.git
|
||||
shallow = true
|
||||
[submodule "import/cs_x86"]
|
||||
path = import/cs_x86
|
||||
|
@ -37,5 +37,13 @@
|
|||
shallow = true
|
||||
[submodule "import/libusb"]
|
||||
path = import/libusb
|
||||
url = https://github.com/libusb/libusb
|
||||
url = https://github.com/Cxbx-Reloaded/libusb
|
||||
branch = deadlock_fix
|
||||
shallow = true
|
||||
[submodule "import/nv2a_vsh_cpu"]
|
||||
path = import/nv2a_vsh_cpu
|
||||
url = https://github.com/abaire/nv2a_vsh_cpu.git
|
||||
[submodule "import/mio"]
|
||||
path = import/mio
|
||||
url = https://github.com/mandreyel/mio.git
|
||||
shadow = true
|
||||
|
|
|
@ -22,9 +22,7 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/XbSymbolDatabase")
|
|||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/SDL2" EXCLUDE_FROM_ALL)
|
||||
|
||||
if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])")
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/cs_x86")
|
||||
endif()
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/mio" EXCLUDE_FROM_ALL)
|
||||
|
||||
# Cxbx-Reloaded projects
|
||||
set(CXBXR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
@ -43,6 +41,9 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/imgui")
|
|||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libusb")
|
||||
|
||||
set(nv2a_vsh_cpu_UNIT_TEST OFF)
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/nv2a_vsh_cpu" EXCLUDE_FROM_ALL)
|
||||
|
||||
# Split the files into group for which project is likely
|
||||
# going to be used for both header and source files.
|
||||
# Then move only specific project files into their
|
||||
|
@ -53,13 +54,16 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libusb")
|
|||
|
||||
# Common (GUI and Emulator)
|
||||
file (GLOB CXBXR_HEADER_COMMON
|
||||
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuRsa.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuSha.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/LibRc4.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/CxbxDebugger.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/cxbxr.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/EmuEEPROM.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Error.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/FilePaths.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardCodes.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/layout_xbox_device.h"
|
||||
|
@ -71,13 +75,10 @@ file (GLOB CXBXR_HEADER_COMMON
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/IPCHybrid.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Logging.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/ReservedMemory.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Settings.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Timer.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/CPUID.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/std_extend.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/strConverter.hpp"
|
||||
|
@ -85,15 +86,20 @@ file (GLOB CXBXR_HEADER_COMMON
|
|||
"${CXBXR_ROOT_DIR}/src/common/win32/AlignPrefix1.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Mutex.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Util.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/WineEnv.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbdm/CxbxXbdm.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbe/Xbe.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbe/XbePrinter.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox/Types.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox_types.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/buffered_io.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/xdvdfs.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Types.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.h"
|
||||
"${CXBXR_ROOT_DIR}/src/Cxbx.h"
|
||||
"${CXBXR_ROOT_DIR}/src/CxbxVersion.h"
|
||||
"${CXBXR_ROOT_DIR}/src/version.h"
|
||||
|
@ -126,13 +132,12 @@ file (GLOB CXBXR_HEADER_EMU_IMPORT
|
|||
"${CXBXR_ROOT_DIR}/import/imgui/backends/imgui_impl_win32.h"
|
||||
)
|
||||
file (GLOB CXBXR_HEADER_EMU
|
||||
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/FilePaths.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/audio/converter.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/audio/XADPCM.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Timer.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/glextensions.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/audio/XADPCM.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/common/imgui/audio.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/common/imgui/ui.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/common/imgui/settings.h"
|
||||
|
@ -140,6 +145,7 @@ file (GLOB CXBXR_HEADER_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/common/video/RenderBase.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxPixelShaderTemplate.hlsl"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderTemplate.hlsl"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Direct3D9.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
|
||||
|
@ -148,13 +154,11 @@ file (GLOB CXBXR_HEADER_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/PixelShader.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Shader.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Types.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPixelShader.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPushBuffer.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbState.h"
|
||||
|
@ -173,14 +177,13 @@ file (GLOB CXBXR_HEADER_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XONLINE/XOnline.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/common/strings.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlAvModes.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKe.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKi.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPs.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/init/CxbxKrnl.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/init/KrnlPatches.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PhysicalMemory.h"
|
||||
|
@ -230,13 +233,16 @@ list(FILTER CXBXR_HEADER_HLSL INCLUDE REGEX ".*\\.hlsl$")
|
|||
|
||||
# Common (GUI and Emulator)
|
||||
file (GLOB CXBXR_SOURCE_COMMON
|
||||
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuRsa.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuSha.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/LibRc4.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/CxbxDebugger.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/cxbxr.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/EmuEEPROM.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Error.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/FilePaths.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.cpp"
|
||||
|
@ -246,25 +252,28 @@ file (GLOB CXBXR_SOURCE_COMMON
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Logging.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Settings.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Timer.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/hasher.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/InlineFunc.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/IPCWindows.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Mutex.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Util.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/WineEnv.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbdm/CxbxXbdm.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbe/Xbe.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbe/XbePrinter.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox/Types.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/buffered_io.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/xdvdfs.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXbox.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXc.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXe.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/CxbxVersion.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/HighPerformanceGraphicsEnabler.c"
|
||||
)
|
||||
|
@ -302,12 +311,12 @@ file (GLOB CXBXR_SOURCE_EMU_IMPORT
|
|||
)
|
||||
file (GLOB CXBXR_SOURCE_EMU
|
||||
"${CXBXR_KRNL_CPP}"
|
||||
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/VerifyAddressRanges.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Timer.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/glextensions.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen_common.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen_wgl.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/VerifyAddressRanges.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/common/imgui/audio.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/common/imgui/ui.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/common/imgui/video.cpp"
|
||||
|
@ -318,11 +327,10 @@ file (GLOB CXBXR_SOURCE_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Shader.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/TextureStates.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPixelShader.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPushBuffer.cpp"
|
||||
|
@ -340,9 +348,9 @@ file (GLOB CXBXR_SOURCE_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalDSVoice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalStruct.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/JVS/JVS.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnl.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlAv.cpp"
|
||||
|
@ -354,16 +362,12 @@ file (GLOB CXBXR_SOURCE_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKd.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKe.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKi.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlMm.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlNt.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlOb.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPhy.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPs.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlRtl.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXbox.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXc.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXe.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/KernelThunk.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PhysicalMemory.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PoolManager.cpp"
|
||||
|
@ -375,6 +379,8 @@ file (GLOB CXBXR_SOURCE_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/JvsIO.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/MediaBoard.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/EEPROMDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/MCPXDevice.cpp"
|
||||
|
@ -437,6 +443,10 @@ set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymb
|
|||
PROPERTIES FOLDER Cxbx-Reloaded
|
||||
)
|
||||
|
||||
set_target_properties(nv2a_vsh_emulator nv2a_vsh_disassembler nv2a_vsh_cpu
|
||||
PROPERTIES FOLDER Cxbx-Reloaded/nv2a_vsh
|
||||
)
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# Configure startup project
|
||||
set_property(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" PROPERTY VS_STARTUP_PROJECT cxbx)
|
||||
|
@ -464,14 +474,19 @@ install(FILES ${cxbxr_INSTALL_files}
|
|||
DESTINATION bin
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/CxbxPixelShaderTemplate.hlsl"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
|
||||
DESTINATION bin/hlsl
|
||||
# Copy HLSL files to the output directory, which are loaded at runtime
|
||||
set(CXBXR_HLSL_FILES ${CXBXR_HEADER_EMU})
|
||||
list(FILTER CXBXR_HLSL_FILES INCLUDE REGEX ".*/src/core/hle/D3D8/Direct3D9/[^/]+\.hlsli?")
|
||||
add_custom_command(
|
||||
TARGET misc-batch POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl"
|
||||
# These files can be edited.
|
||||
# Create backup copies for convenience of restoring original shader behaviour.
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup"
|
||||
)
|
||||
install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl DESTINATION bin)
|
||||
|
||||
set(cxbxr_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")
|
||||
|
||||
|
|
48
README.md
48
README.md
|
@ -8,30 +8,32 @@ Cxbx-Reloaded is an emulator for running Microsoft Xbox (and eventually, Chihiro
|
|||
## System Requirements
|
||||
### Minimum
|
||||
* OS: Windows 7+ x64, or x86-64 Linux with Wine. 32-bit is not supported.
|
||||
* MacOS with Wine is known not to work, and BSD-based systems are untested.
|
||||
* Also note that Wine is relatively unstable, and it might break compatibility with Cxbx-Reloaded at any time without warning.
|
||||
* GPU: Direct3D 9.0c with Pixel Shader Model 2.x, and Vertex Shader Model 3.0.
|
||||
|
||||
## Prerequisites
|
||||
### Windows
|
||||
* [32-bit (x86) Visual C++ 2019 Redistributable](https://aka.ms/vs/16/release/vc_redist.x86.exe)
|
||||
* [32-bit (x86) Visual C++ 2022 Redistributable](https://aka.ms/vs/17/release/vc_redist.x86.exe)
|
||||
* [Npcap *(used for network emulation)*](https://nmap.org/npcap/#download)
|
||||
* Make sure to enable winpcap compatibility mode!
|
||||
* WinUSB compliant driver *(optional, only needed for USB pass-through of original xbox controllers and the steel battalion controller)*
|
||||
* Make sure to enable winpcap compatibility mode.
|
||||
* [WinUSB compliant driver](https://github.com/libusb/libusb/wiki/Windows#Driver_Installation)
|
||||
* *Optional, only needed for USB pass-through of original Xbox and Steel Battalion controllers.*
|
||||
|
||||
### Wine
|
||||
**NOTICE: Please use the latest stable release version of Wine. If it does not work for you, then roll back to Wine 5.0.3 which is the last known working version.**
|
||||
**Please use the latest stable release version of Wine. If it does not work for you, then roll back to Wine 7.0 which is the last known working version.**<br/>
|
||||
**There also exists this known [issue](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/2314) which currently prevents savings in some games with the most recent Wine 6.8 and later versions.**
|
||||
* Winetricks
|
||||
* `vcrun2017` or `vcrun2019`
|
||||
* NOTE: vcrun2019 requires the latest winetricks script!
|
||||
* `vcrun2019`
|
||||
* Requires the latest winetricks script.
|
||||
* `d3dcompiler_47`
|
||||
* NOTE: May be subject to change over time.
|
||||
* This may be subject to change.
|
||||
* Winpcap is built-in, no installation is required.
|
||||
|
||||
|
||||
## Automated Builds
|
||||
Cxbx-Reloaded doesn't currently have stable builds, but you can obtain pre-release builds from the Releases tab, or the links below:
|
||||
Cxbx-Reloaded doesn't currently have stable builds, but you can obtain pre-release builds from our official website's download page, or the links below:
|
||||
|
||||
* **[Release Builds](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/releases)**
|
||||
* **Wine users will need to use `CxbxReloaded-Release-VS2017.zip` for it to run correctly.**
|
||||
* **[Release Builds](https://cxbx-reloaded.co.uk/download)**
|
||||
* *[Full build history](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/actions?query=workflow%3A%22GitHub+CI%22)*
|
||||
|
||||
## Compatibility
|
||||
|
@ -43,15 +45,13 @@ If you would like to submit compatibility reports, please request permission in
|
|||
Game or software specific issues can be reported in the [compatibility website](https://cxbx-reloaded.co.uk/compatibility).
|
||||
|
||||
For emulation issues that are not specific to any single piece of software, a bug report can be submitted at [the Cxbx-Reloaded issue tracker](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues).
|
||||
|
||||
Make sure to follow the issue template and that it contains:
|
||||
<!--Make sure to follow the issue template and that it contains:
|
||||
* The build tested with, error message displayed (if any)
|
||||
* **(You can copy and paste any popup messages. However, please keep it clean by paste and trimming down to only the message itself.)**
|
||||
* **You can copy and paste any popup messages. However, please keep it clean by pasting and trimming down to only the message itself.**
|
||||
* Screenshots
|
||||
* (optional unless has any graphic bug for references)
|
||||
|
||||
**NOTICE: Failure to follow template will auto close your ticket.**
|
||||
* Optional unless there are graphic bugs for reference.
|
||||
|
||||
**Failure to follow the template will auto close your ticket.**-->
|
||||
|
||||
## Additional information
|
||||
Cxbx-Reloaded has a [wiki](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/wiki) containing various subjects and background information.
|
||||
|
@ -74,8 +74,7 @@ Please contact us before you start working on something, so we can make sure you
|
|||
|
||||
### Fetching the code
|
||||
1. Run the following command in the command line:
|
||||
|
||||
`git clone --recurse-submodules https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git`
|
||||
<br>`git clone --recurse-submodules https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git`
|
||||
* Please note the `--recurse-submodules` parameter. This is required to fetch submodules.
|
||||
* If Cxbx-Reloaded was checked out without submodules, they can be updated/fetched with the following command:
|
||||
|
||||
|
@ -84,10 +83,10 @@ Please contact us before you start working on something, so we can make sure you
|
|||
### Compiling
|
||||
|
||||
#### Windows
|
||||
**NOTE:** Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in the `build` directory.
|
||||
Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in the `build` directory.
|
||||
|
||||
##### Prerequisites
|
||||
1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2017 or later
|
||||
1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2022
|
||||
* C++ and C# desktop development
|
||||
* Windows Universal CRT SDK
|
||||
* C++ CMake tools for Windows
|
||||
|
@ -99,9 +98,8 @@ Please contact us before you start working on something, so we can make sure you
|
|||
2. `cd` to the Cxbx-Reloaded directory.
|
||||
3. Run these commands.
|
||||
1. `mkdir build & cd build`
|
||||
2. `cmake .. -G "Visual Studio 16 2019" -A Win32`
|
||||
* Visual Studio 2019 16.1 or later has CMake 3.14 bundled, and is required for the Visual Studio 2019 generator.
|
||||
* Use `cmake .. -G "Visual Studio 15 2017" -A Win32` for Visual Studio 2017.
|
||||
2. `cmake .. -G "Visual Studio 17 2022" -A Win32`
|
||||
* VS2022 17.0 or later is required.
|
||||
4. Open `Cxbx-Reloaded.sln` from the `build` directory.
|
||||
5. Select the Release configuration, then click Build.
|
||||
* Debug builds are **significantly slower, and only for developers**.
|
||||
|
@ -116,3 +114,5 @@ You can support [Luke Usher](https://github.com/LukeUsher), initiator of Cxbx-Re
|
|||
* All contributors to the original Cxbx and [Dxbx](https://github.com/PatrickvL/Dxbx) projects. Without them Cxbx-Reloaded would not exist at all.
|
||||
* [XQEMU](https://github.com/xqemu/xqemu) - While the majority of Cxbx-R is our own work (Kernel, HLE, etc), the NV2A LLE and NVNet implementation are primarily the work of the XQEMU developers.
|
||||
* [XboxDev](https://github.com/xboxdev) - Providing Xbox hardware research & useful tooling.
|
||||
* [XbSymbolDatabase](https://github.com/Cxbx-Reloaded/XbSymbolDatabase) - Providing support to detect symbols across XDK builds from reverse engineered retail titles.
|
||||
* [Xbox Kernel Test Suite](https://github.com/Cxbx-Reloaded/xbox_kernel_test_suite) - Making accurate tests on hardware to compare against cxbxr's kernel implementation.
|
||||
|
|
2
import/SDL2
vendored
2
import/SDL2
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 0e9560aea22818884921e5e5064953257bfe7fa7
|
||||
Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd
|
2
import/XbSymbolDatabase
vendored
2
import/XbSymbolDatabase
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 02353e8aa552f3db60804626e29838406f206443
|
||||
Subproject commit 774111351210e6f340246d6fb32741b09708f381
|
2
import/cs_x86
vendored
2
import/cs_x86
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 5fb27bcf6745c35d8889e48ead4314022b8ec931
|
||||
Subproject commit f8a95b7afa963c90b01a9e7cd758346f95c90f50
|
2
import/libusb
vendored
2
import/libusb
vendored
|
@ -1 +1 @@
|
|||
Subproject commit c6a35c56016ea2ab2f19115d2ea1e85e0edae155
|
||||
Subproject commit cf178f1fac38426990425cc034f7d4b8c9e1e388
|
1
import/mio
vendored
Submodule
1
import/mio
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 3f86a95c0784d73ce6815237ec33ed25f233b643
|
1
import/nv2a_vsh_cpu
vendored
Submodule
1
import/nv2a_vsh_cpu
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit ead0af5dee49e408c005151393f8a7dbcd27026c
|
2
import/subhook
vendored
2
import/subhook
vendored
|
@ -1 +1 @@
|
|||
Subproject commit a86a253e6e764275a21ab7c9348f84d1f95e409f
|
||||
Subproject commit 83d4e1ebef3588fae48b69a7352cc21801cb70bc
|
|
@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
|
|||
project(cxbx)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
# Suppress extra stuff from generated solution
|
||||
set(CMAKE_SUPPRESS_REGENERATION true)
|
||||
|
@ -42,8 +42,11 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
LTC_NO_PRNGS
|
||||
LTC_NO_MISC
|
||||
LTC_NO_PROTOTYPES
|
||||
|
||||
# Enable Chihiro work
|
||||
CHIHIRO_WORK
|
||||
)
|
||||
|
||||
|
||||
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
|
||||
add_compile_options(
|
||||
# Catch synchronous (C++) exceptions only
|
||||
|
@ -65,7 +68,7 @@ XXH_INLINE_ALL
|
|||
)
|
||||
|
||||
file (GLOB RESOURCES
|
||||
|
||||
|
||||
"${CXBXR_ROOT_DIR}/CONTRIBUTORS"
|
||||
"${CXBXR_ROOT_DIR}/COPYING"
|
||||
"${CXBXR_ROOT_DIR}/README.md"
|
||||
|
@ -78,24 +81,18 @@ file (GLOB RESOURCES
|
|||
"${CXBXR_ROOT_DIR}/src/.editorconfig"
|
||||
)
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
|
||||
${CXBXR_HEADER_EMU_IMPORT}
|
||||
)
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX header FILES
|
||||
${CXBXR_HEADER_GUIv1}
|
||||
${CXBXR_HEADER_COMMON}
|
||||
${CXBXR_HEADER_EMU}
|
||||
)
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
|
||||
${CXBXR_SOURCE_EMU_IMPORT}
|
||||
)
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX source FILES
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX source FILES
|
||||
${CXBXR_SOURCE_GUIv1}
|
||||
${CXBXR_SOURCE_COMMON}
|
||||
${CXBXR_SOURCE_EMU}
|
||||
)
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR} FILES ${RESOURCES})
|
||||
|
@ -103,12 +100,8 @@ source_group(TREE ${CXBXR_ROOT_DIR} FILES ${RESOURCES})
|
|||
add_executable(cxbx WIN32 ${RESOURCES}
|
||||
${CXBXR_HEADER_GUIv1}
|
||||
${CXBXR_HEADER_COMMON}
|
||||
${CXBXR_HEADER_EMU_IMPORT}
|
||||
${CXBXR_HEADER_EMU}
|
||||
${CXBXR_SOURCE_GUIv1}
|
||||
${CXBXR_SOURCE_COMMON}
|
||||
${CXBXR_SOURCE_EMU_IMPORT}
|
||||
${CXBXR_SOURCE_EMU}
|
||||
${CXBXR_GIT_VERSION_H}
|
||||
)
|
||||
|
||||
|
@ -118,15 +111,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/linker-options
|
||||
set_target_properties(cxbx PROPERTIES
|
||||
LINK_FLAGS "
|
||||
/INCREMENTAL:NO \
|
||||
/LARGEADDRESSAWARE \
|
||||
/FIXED \
|
||||
/SAFESEH:NO \
|
||||
/DYNAMICBASE:NO \
|
||||
/BASE:0x10000 \
|
||||
/STACK:65536,65536 \
|
||||
/NODEFAULTLIB:libcmt \
|
||||
/DELAYLOAD:wpcap.dll \
|
||||
"
|
||||
LINK_FLAGS_RELEASE "
|
||||
/LTCG \
|
||||
|
@ -136,7 +121,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
|
||||
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
|
||||
# /Zi = create a PDB file without affecting optimization
|
||||
# /Ob2 = Controls inline expansion of functions.
|
||||
# /Ob3 = Controls inline expansion of functions.
|
||||
# /Oi = Generate intrinsic functions
|
||||
# /Ot = In favor of using fast code than small code
|
||||
# /GL = Whole program optimization
|
||||
|
@ -147,7 +132,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
# Set optimization options for release build
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
|
||||
/Zi \
|
||||
/Ob2 \
|
||||
/Ob3 \
|
||||
/Oi \
|
||||
/Ot \
|
||||
/GL \
|
||||
|
@ -157,7 +142,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
/Qpar \
|
||||
"
|
||||
)
|
||||
|
||||
|
||||
# disable optimization for CxbxKrnl.cpp file
|
||||
set_source_files_properties(
|
||||
${CXBXR_KRNL_CPP} PROPERTIES COMPILE_FLAGS "/Od /GL-"
|
||||
|
@ -165,7 +150,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -185,7 +170,7 @@ set(WINS_LIB
|
|||
comctl32
|
||||
XINPUT9_1_0
|
||||
Iphlpapi
|
||||
wpcap
|
||||
Dwmapi
|
||||
)
|
||||
|
||||
target_link_libraries(cxbx
|
||||
|
@ -195,6 +180,7 @@ target_link_libraries(cxbx
|
|||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
|
|||
project(cxbxr-emu)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
# Suppress extra stuff from generated solution
|
||||
set(CMAKE_SUPPRESS_REGENERATION true)
|
||||
|
@ -48,6 +48,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
|
||||
# Use inline XXHash version
|
||||
XXH_INLINE_ALL
|
||||
|
||||
# Enable Chihiro work
|
||||
CHIHIRO_WORK
|
||||
)
|
||||
add_compile_options(
|
||||
/EHs
|
||||
|
@ -128,7 +131,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
# Set optimization options for release build
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
|
||||
/Zi \
|
||||
/Ob2 \
|
||||
/Ob3 \
|
||||
/Oi \
|
||||
/Ot \
|
||||
/GL \
|
||||
|
@ -141,7 +144,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -171,6 +174,8 @@ target_link_libraries(cxbxr-emu
|
|||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
nv2a_vsh_emulator
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
|
|||
project(cxbxr-ldr)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
# Suppress extra stuff from generated solution
|
||||
set(CMAKE_SUPPRESS_REGENERATION true)
|
||||
|
|
|
@ -27,14 +27,3 @@ message("Runtime Build Directory: ${TargetRunTimeDir}")
|
|||
# Copy glew32.dll to build type's folder.
|
||||
set(CXBXR_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")
|
||||
file(COPY ${CXBXR_GLEW_DLL} DESTINATION ${TargetRunTimeDir})
|
||||
|
||||
# Copy certain HLSL files to the output directory, which we will load at runtime
|
||||
set(CXBXR_HLSL_FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
|
||||
)
|
||||
set(HlslOutputDir ${TargetRunTimeDir}/hlsl)
|
||||
file(MAKE_DIRECTORY ${HlslOutputDir})
|
||||
file(COPY ${CXBXR_HLSL_FILES} DESTINATION ${HlslOutputDir})
|
||||
|
|
|
@ -3,5 +3,5 @@ root = true
|
|||
[*]
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = crlf
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
|
11
src/Cxbx.h
11
src/Cxbx.h
|
@ -61,13 +61,14 @@ enum DebugMode { DM_NONE, DM_CONSOLE, DM_FILE };
|
|||
/*! debugger enable state */
|
||||
enum DebuggerState { debuggerOff, debuggerOn };
|
||||
|
||||
/*! indicates emulation of an Chihiro (arcade, instead of Xbox console) executable */
|
||||
/*! indicates emulation of a Chihiro system */
|
||||
extern bool g_bIsChihiro;
|
||||
|
||||
/*! indicates emulation of a Debug xbe executable */
|
||||
extern bool g_bIsDebug;
|
||||
/*! indicates emulation of a DevKit system */
|
||||
/* Note: Our DevKit emulation lacks the kernel debugging interface and virtual dvd-rom emulator card, so this is actually a Debug Kit */
|
||||
extern bool g_bIsDevKit;
|
||||
|
||||
/*! indicates emulation of a Retail xbe executable*/
|
||||
/*! indicates emulation of a Retail system */
|
||||
extern bool g_bIsRetail;
|
||||
|
||||
/*! indicates ability to save on exit (needed for settings reset) */
|
||||
|
@ -83,6 +84,4 @@ extern volatile bool g_bPrintfOn;
|
|||
#define CxbxSetThreadName(Name)
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
|
||||
<supportedRuntime version="v4.8" sku=".NETFramework,Version=v4.8"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
|
|
|
@ -13,6 +13,12 @@ add_compile_options(
|
|||
/langversion:6
|
||||
)
|
||||
|
||||
# First, we must define .NET Framework version before include cs_x86's projects.
|
||||
# Which then will pass down version we want unified.
|
||||
set(DOTNET_TARGET_FRAMEWORK_VERSION "v4.8")
|
||||
|
||||
add_subdirectory("${CXBXR_ROOT_DIR}/import/cs_x86" "${CMAKE_BINARY_DIR}/import/cs_x86")
|
||||
|
||||
set(CXBXR_DEBUGGER_SRC_DIR "${CXBXR_ROOT_DIR}/src/CxbxDebugger")
|
||||
|
||||
file (GLOB SOURCES
|
||||
|
@ -128,16 +134,15 @@ source_group(TREE ${CXBXR_ROOT_DIR} FILES ${SOURCES})
|
|||
add_executable(cxbxr-debugger WIN32 ${SOURCES} ${PROPERTIES} #Test WIN32 like cxbx does if doesn't need compile option set
|
||||
)
|
||||
|
||||
set_target_properties(cxbxr-debugger PROPERTIES
|
||||
set_target_properties(cxbxr-debugger PROPERTIES
|
||||
VS_DOTNET_REFERENCES
|
||||
"Microsoft.CSharp;System;System.Core;System.Data;System.Data.DataSetExtensions;System.Deployment;System.Drawing;System.Windows;System.Windows.Forms;System.Xml;System.Xml.Linq;System.Net.Http"
|
||||
VS_GLOBAL_ApplicationIcon "${CXBXR_ROOT_DIR}/src/gui/resource/Cxbx-R.ico"
|
||||
|
||||
VS_GLOBAL_ROOTNAMESPACE "CxbxDebugger"
|
||||
|
||||
)
|
||||
|
||||
set_property(TARGET cxbxr-debugger PROPERTY DOTNET_TARGET_FRAMEWORK_VERSION "v4.5")
|
||||
VS_GLOBAL_ROOTNAMESPACE "CxbxDebugger"
|
||||
|
||||
DOTNET_TARGET_FRAMEWORK_VERSION ${DOTNET_TARGET_FRAMEWORK_VERSION}
|
||||
)
|
||||
|
||||
target_link_libraries(cxbxr-debugger cs_x86)
|
||||
|
||||
|
|
|
@ -163,6 +163,14 @@ inline constexpr uint32_t FLASH_DEVICE4_END = (FLASH_DEVICE4_BASE - 1 + FLA
|
|||
#define XBOX_MEMORY_SIZE (MiB(64))
|
||||
#define CHIHIRO_MEMORY_SIZE (MiB(128))
|
||||
|
||||
// Common page calculations
|
||||
#define ROUND_UP_4K(size) (((size) + PAGE_MASK) & (~PAGE_MASK))
|
||||
#define ROUND_UP(size, alignment) (((size) + (alignment - 1)) & (~(alignment - 1)))
|
||||
#define ROUND_DOWN_4K(size) ((size) & (~PAGE_MASK))
|
||||
#define ROUND_DOWN(size, alignment) ((size) & (~(alignment - 1)))
|
||||
#define CHECK_ALIGNMENT(size, alignment) (((size) % (alignment)) == 0)
|
||||
#define PAGE_ALIGN(address) ROUND_DOWN_4K(address)
|
||||
|
||||
// Windows' address space allocation granularity;
|
||||
// See https://blogs.msdn.microsoft.com/oldnewthing/20031008-00/?p=42223
|
||||
const int BLOCK_SIZE = KiB(64);
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
#define LOG_PREFIX CXBXR_MODULE::EEPR
|
||||
#define LOG_PREFIX_INIT CXBXR_MODULE::INIT
|
||||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h> // For XC_VALUE_INDEX and XBOX_EEPROM
|
||||
#include "cxbxr.hpp" // For CxbxrAbort
|
||||
#include <stdio.h> // For printf
|
||||
#include <shlobj.h> // For HANDLE, CreateFile, CreateFileMapping, MapViewOfFile
|
||||
#include <random>
|
||||
|
@ -36,7 +36,6 @@
|
|||
#include "EmuEEPROM.h" // For EEPROMInfo, EEPROMInfos
|
||||
#include "core\kernel\support\Emu.h" // For EmuWarning
|
||||
#include "..\..\src\devices\LED.h" // For SetLEDSequence
|
||||
#include "..\core\kernel\init\CxbxKrnl.h"
|
||||
|
||||
xbox::XBOX_EEPROM *EEPROM = nullptr; // Set using CxbxRestoreEEPROM()
|
||||
|
||||
|
@ -90,6 +89,7 @@ void gen_section_CRCs(xbox::XBOX_EEPROM* eeprom) {
|
|||
);
|
||||
}
|
||||
|
||||
#ifdef CXBXR_EMU
|
||||
xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
|
||||
{
|
||||
xbox::XBOX_EEPROM *pEEPROM;
|
||||
|
@ -141,10 +141,8 @@ xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
|
|||
LARGE_INTEGER len_li;
|
||||
GetFileSizeEx(hFileEEPROM, &len_li);
|
||||
unsigned int FileSize = len_li.u.LowPart;
|
||||
if (FileSize != 256)
|
||||
{
|
||||
CxbxrKrnlAbort("%s : EEPROM.bin file is not 256 bytes large!\n", __func__);
|
||||
return nullptr;
|
||||
if (FileSize != 256) {
|
||||
CxbxrAbort("%s : EEPROM.bin file is not 256 bytes large!\n", __func__);
|
||||
}
|
||||
|
||||
// Map EEPROM.bin contents into memory :
|
||||
|
@ -194,6 +192,7 @@ xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
|
|||
|
||||
return pEEPROM;
|
||||
}
|
||||
#endif
|
||||
|
||||
void EmuEEPROMReset(xbox::XBOX_EEPROM* eeprom)
|
||||
{
|
||||
|
|
187
src/common/FilePaths.cpp
Normal file
187
src/common/FilePaths.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx-Reloaded project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#define LOG_PREFIX CXBXR_MODULE::FILE
|
||||
#define LOG_PREFIX_INIT CXBXR_MODULE::INIT
|
||||
|
||||
#include <filesystem>
|
||||
#include "common/cxbxr.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include "EmuShared.h"
|
||||
#include "xxhash.h" // for XXH3_64bits
|
||||
#include "core/kernel/common/xbox.h"
|
||||
#include "Logging.h"
|
||||
|
||||
char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 };
|
||||
char szFilePath_EEPROM_bin[MAX_PATH] = { 0 };
|
||||
|
||||
std::string g_DataFilePath;
|
||||
std::string g_DiskBasePath;
|
||||
std::string g_MuBasePath;
|
||||
|
||||
//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere.
|
||||
// Let filesystem library clean it up for us, including resolve host's symbolic link path.
|
||||
// Since internal kernel do translate to full path than preserved host symoblic link path.
|
||||
void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence) {
|
||||
std::error_code error;
|
||||
std::filesystem::path sanityPath = std::filesystem::canonical(file_path, error);
|
||||
if (error) {
|
||||
|
||||
// The MS implementation of std::filesystem::canonical internally calls GetFinalPathNameByHandleW, which fails with ERROR_FILE_NOT_FOUND when called
|
||||
// on a file inside a mounted xiso under Windows with the xbox-iso-vfs tool because of this known dokany bug https://github.com/dokan-dev/dokany/issues/343
|
||||
EmuLogInit(LOG_LEVEL::WARNING, "Could not resolve to %s: %s, dokany in use? The error was: %s",
|
||||
finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
|
||||
|
||||
sanityPath = std::filesystem::absolute(std::filesystem::weakly_canonical(file_path, error), error);
|
||||
if (error) {
|
||||
CxbxrAbortEx(LOG_PREFIX_INIT, "Could not resolve to %s: %s. The error was: %s", finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
|
||||
}
|
||||
}
|
||||
file_path = sanityPath;
|
||||
}
|
||||
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
|
||||
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence) {
|
||||
std::filesystem::path sanityPath(file_path);
|
||||
CxbxResolveHostToFullPath(sanityPath, finish_error_sentence);
|
||||
file_path = sanityPath.string();
|
||||
}
|
||||
|
||||
// NOTE: Do NOT modify g_<custom>BasePath variables after this call!
|
||||
void CxbxrInitFilePaths()
|
||||
{
|
||||
if (g_Settings) {
|
||||
g_DataFilePath = g_Settings->GetDataLocation();
|
||||
}
|
||||
else {
|
||||
char dataLoc[MAX_PATH];
|
||||
g_EmuShared->GetDataLocation(dataLoc);
|
||||
g_DataFilePath = dataLoc;
|
||||
}
|
||||
|
||||
// Make sure our data folder exists :
|
||||
bool result = std::filesystem::exists(g_DataFilePath);
|
||||
if (!result && !std::filesystem::create_directory(g_DataFilePath)) {
|
||||
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded's data folder!", __func__);
|
||||
}
|
||||
|
||||
// Make sure the EmuDisk folder exists
|
||||
g_DiskBasePath = g_DataFilePath + "\\EmuDisk";
|
||||
result = std::filesystem::exists(g_DiskBasePath);
|
||||
if (!result && !std::filesystem::create_directory(g_DiskBasePath)) {
|
||||
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__);
|
||||
}
|
||||
CxbxResolveHostToFullPath(g_DiskBasePath, "Cxbx-Reloaded's EmuDisk directory");
|
||||
g_DiskBasePath = std::filesystem::path(g_DiskBasePath).append("").string();
|
||||
|
||||
// Make sure the EmuDMu folder exists
|
||||
g_MuBasePath = g_DataFilePath + "\\EmuMu";
|
||||
result = std::filesystem::exists(g_MuBasePath);
|
||||
if (!result && !std::filesystem::create_directory(g_MuBasePath)) {
|
||||
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded EmuMu folder!", __func__);
|
||||
}
|
||||
CxbxResolveHostToFullPath(g_MuBasePath, "Cxbx-Reloaded's EmuMu directory");
|
||||
g_MuBasePath = std::filesystem::path(g_MuBasePath).append("").string();
|
||||
|
||||
snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", g_DataFilePath.c_str());
|
||||
|
||||
GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH);
|
||||
}
|
||||
|
||||
// Loads a keys.bin file as generated by dump-xbox
|
||||
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
|
||||
void LoadXboxKeys()
|
||||
{
|
||||
std::string keys_path = g_DataFilePath + "\\keys.bin";
|
||||
|
||||
// Attempt to open Keys.bin
|
||||
FILE* fp = fopen(keys_path.c_str(), "rb");
|
||||
|
||||
if (fp != nullptr) {
|
||||
// Determine size of Keys.bin
|
||||
xbox::XBOX_KEY_DATA keys[2];
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long size = ftell(fp);
|
||||
rewind(fp);
|
||||
|
||||
// If the size of Keys.bin is correct (two keys), read it
|
||||
if (size == xbox::XBOX_KEY_LENGTH * 2) {
|
||||
fread(keys, xbox::XBOX_KEY_LENGTH, 2, fp);
|
||||
|
||||
memcpy(xbox::XboxEEPROMKey, &keys[0], xbox::XBOX_KEY_LENGTH);
|
||||
memcpy(xbox::XboxCertificateKey, &keys[1], xbox::XBOX_KEY_LENGTH);
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Keys.bin has an incorrect filesize. Should be %d bytes", xbox::XBOX_KEY_LENGTH * 2);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we didn't already exit the function, keys.bin could not be loaded
|
||||
EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox");
|
||||
}
|
||||
|
||||
static HANDLE hMapDataHash = nullptr;
|
||||
|
||||
bool CxbxrLockFilePath()
|
||||
{
|
||||
std::stringstream filePathHash("Local\\");
|
||||
uint64_t hashValue = XXH3_64bits(g_DataFilePath.c_str(), g_DataFilePath.length() + 1);
|
||||
if (!hashValue) {
|
||||
CxbxrAbort("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__);
|
||||
}
|
||||
|
||||
filePathHash << std::hex << hashValue;
|
||||
|
||||
hMapDataHash = CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE, // Paging file
|
||||
nullptr, // default security attributes
|
||||
PAGE_READONLY, // readonly access
|
||||
0, // size: high 32 bits
|
||||
/*Dummy size*/4, // size: low 32 bits
|
||||
filePathHash.str().c_str() // name of map object
|
||||
);
|
||||
|
||||
if (hMapDataHash == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
PopupError(nullptr, "Data path directory is currently in use.\nUse a different data path directory or stop emulation from another process.");
|
||||
CloseHandle(hMapDataHash);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CxbxrUnlockFilePath()
|
||||
{
|
||||
// Close opened file path lockdown shared memory.
|
||||
if (hMapDataHash) {
|
||||
CloseHandle(hMapDataHash);
|
||||
hMapDataHash = nullptr;
|
||||
}
|
||||
}
|
|
@ -24,148 +24,30 @@
|
|||
// ******************************************************************
|
||||
#pragma once
|
||||
|
||||
extern char szFilePath_CxbxReloaded_Exe[MAX_PATH];
|
||||
extern char szFilePath_EEPROM_bin[MAX_PATH];
|
||||
|
||||
extern std::string g_DataFilePath;
|
||||
extern std::string g_DiskBasePath;
|
||||
extern std::string g_MuBasePath;
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere.
|
||||
// Let filesystem library clean it up for us, including resolve host's symbolic link path.
|
||||
// Since internal kernel do translate to full path than preserved host symoblic link path.
|
||||
static inline void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence) {
|
||||
std::error_code error;
|
||||
std::filesystem::path sanityPath = std::filesystem::canonical(file_path, error);
|
||||
if (error) {
|
||||
void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence);
|
||||
|
||||
// The MS implementation of std::filesystem::canonical internally calls GetFinalPathNameByHandleW, which fails with ERROR_FILE_NOT_FOUND when called
|
||||
// on a file inside a mounted xiso under Windows with the xbox-iso-vfs tool because of this known dokany bug https://github.com/dokan-dev/dokany/issues/343
|
||||
EmuLogInit(LOG_LEVEL::WARNING, "Could not resolve to %s: %s, dokany in use? The error was: %s",
|
||||
finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
|
||||
|
||||
sanityPath = std::filesystem::absolute(std::filesystem::weakly_canonical(file_path, error), error);
|
||||
if (error) {
|
||||
CxbxrKrnlAbortEx(LOG_PREFIX_INIT, "Could not resolve to %s: %s. The error was: %s", finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
|
||||
}
|
||||
}
|
||||
file_path = sanityPath;
|
||||
}
|
||||
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
|
||||
static inline void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence) {
|
||||
std::filesystem::path sanityPath(file_path);
|
||||
CxbxResolveHostToFullPath(sanityPath, finish_error_sentence);
|
||||
file_path = sanityPath.string();
|
||||
}
|
||||
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence);
|
||||
|
||||
// NOTE: Do NOT modify g_<custom>BasePath variables after this call!
|
||||
void CxbxrInitFilePaths()
|
||||
{
|
||||
if (g_Settings) {
|
||||
g_DataFilePath = g_Settings->GetDataLocation();
|
||||
}
|
||||
else {
|
||||
char dataLoc[MAX_PATH];
|
||||
g_EmuShared->GetDataLocation(dataLoc);
|
||||
g_DataFilePath = dataLoc;
|
||||
}
|
||||
|
||||
// Make sure our data folder exists :
|
||||
bool result = std::filesystem::exists(g_DataFilePath);
|
||||
if (!result && !std::filesystem::create_directory(g_DataFilePath)) {
|
||||
CxbxrKrnlAbort("%s : Couldn't create Cxbx-Reloaded's data folder!", __func__);
|
||||
}
|
||||
|
||||
// Make sure the EmuDisk folder exists
|
||||
g_DiskBasePath = g_DataFilePath + "\\EmuDisk";
|
||||
result = std::filesystem::exists(g_DiskBasePath);
|
||||
if (!result && !std::filesystem::create_directory(g_DiskBasePath)) {
|
||||
CxbxrKrnlAbort("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__);
|
||||
}
|
||||
CxbxResolveHostToFullPath(g_DiskBasePath, "Cxbx-Reloaded's EmuDisk directory");
|
||||
g_DiskBasePath = std::filesystem::path(g_DiskBasePath).append("").string();
|
||||
|
||||
// Make sure the EmuDMu folder exists
|
||||
g_MuBasePath = g_DataFilePath + "\\EmuMu";
|
||||
result = std::filesystem::exists(g_MuBasePath);
|
||||
if (!result && !std::filesystem::create_directory(g_MuBasePath)) {
|
||||
CxbxrKrnlAbort("%s : Couldn't create Cxbx-Reloaded EmuMu folder!", __func__);
|
||||
}
|
||||
CxbxResolveHostToFullPath(g_MuBasePath, "Cxbx-Reloaded's EmuMu directory");
|
||||
g_MuBasePath = std::filesystem::path(g_MuBasePath).append("").string();
|
||||
|
||||
snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", g_DataFilePath.c_str());
|
||||
|
||||
GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH);
|
||||
}
|
||||
void CxbxrInitFilePaths();
|
||||
|
||||
// Loads a keys.bin file as generated by dump-xbox
|
||||
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
|
||||
static void LoadXboxKeys()
|
||||
{
|
||||
std::string keys_path = g_DataFilePath + "\\keys.bin";
|
||||
void LoadXboxKeys();
|
||||
|
||||
// Attempt to open Keys.bin
|
||||
FILE* fp = fopen(keys_path.c_str(), "rb");
|
||||
bool CxbxrLockFilePath();
|
||||
|
||||
if (fp != nullptr) {
|
||||
// Determine size of Keys.bin
|
||||
xbox::XBOX_KEY_DATA keys[2];
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long size = ftell(fp);
|
||||
rewind(fp);
|
||||
|
||||
// If the size of Keys.bin is correct (two keys), read it
|
||||
if (size == xbox::XBOX_KEY_LENGTH * 2) {
|
||||
fread(keys, xbox::XBOX_KEY_LENGTH, 2, fp);
|
||||
|
||||
memcpy(xbox::XboxEEPROMKey, &keys[0], xbox::XBOX_KEY_LENGTH);
|
||||
memcpy(xbox::XboxCertificateKey, &keys[1], xbox::XBOX_KEY_LENGTH);
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Keys.bin has an incorrect filesize. Should be %d bytes", xbox::XBOX_KEY_LENGTH * 2);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we didn't already exit the function, keys.bin could not be loaded
|
||||
EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox");
|
||||
}
|
||||
|
||||
static HANDLE hMapDataHash = nullptr;
|
||||
|
||||
static bool CxbxLockFilePath()
|
||||
{
|
||||
std::stringstream filePathHash("Local\\");
|
||||
uint64_t hashValue = XXH3_64bits(g_DataFilePath.c_str(), g_DataFilePath.length() + 1);
|
||||
if (!hashValue) {
|
||||
CxbxrKrnlAbort("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__);
|
||||
}
|
||||
|
||||
filePathHash << std::hex << hashValue;
|
||||
|
||||
hMapDataHash = CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE, // Paging file
|
||||
nullptr, // default security attributes
|
||||
PAGE_READONLY, // readonly access
|
||||
0, // size: high 32 bits
|
||||
/*Dummy size*/4, // size: low 32 bits
|
||||
filePathHash.str().c_str() // name of map object
|
||||
);
|
||||
|
||||
if (hMapDataHash == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
PopupError(nullptr, "Data path directory is currently in use.\nUse a different data path directory or stop emulation from another process.");
|
||||
CloseHandle(hMapDataHash);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CxbxUnlockFilePath()
|
||||
{
|
||||
// Close opened file path lockdown shared memory.
|
||||
if (hMapDataHash) {
|
||||
CloseHandle(hMapDataHash);
|
||||
hMapDataHash = nullptr;
|
||||
}
|
||||
}
|
||||
void CxbxrUnlockFilePath();
|
||||
|
|
|
@ -49,8 +49,9 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value);
|
|||
// ******************************************************************
|
||||
|
||||
typedef enum class _IPC_UPDATE_KERNEL {
|
||||
CONFIG_LOGGING_SYNC = 0
|
||||
, CONFIG_INPUT_SYNC
|
||||
CONFIG_LOGGING_SYNC = 0,
|
||||
CONFIG_INPUT_SYNC,
|
||||
CONFIG_CHANGE_TIME
|
||||
} IPC_UPDATE_KERNEL;
|
||||
|
||||
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd);
|
||||
|
|
|
@ -313,7 +313,7 @@ PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, con
|
|||
uType |= MB_OKCANCEL;
|
||||
break;
|
||||
case PopupButtons::AbortRetryIgnore:
|
||||
uType |= MB_RETRYCANCEL;
|
||||
uType |= MB_ABORTRETRYIGNORE;
|
||||
break;
|
||||
case PopupButtons::YesNoCancel:
|
||||
uType |= MB_YESNOCANCEL;
|
||||
|
@ -396,8 +396,13 @@ inline void output_wchar(std::ostream& os, wchar_t c)
|
|||
default: os << "\\x" << std::setfill('0') << std::setw(4) << std::right << std::hex << std::uppercase << (wint_t)c;
|
||||
}
|
||||
}
|
||||
else
|
||||
os << c;
|
||||
else {
|
||||
const wchar_t *wc = reinterpret_cast<const wchar_t *>(&c);
|
||||
std::string dst(2, '\0');
|
||||
std::mbstate_t ps{};
|
||||
std::wcsrtombs(dst.data(), &wc, 1, &ps);
|
||||
os << dst;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_SANITIZE_HEADER(hex1, uint8_t)
|
||||
|
|
|
@ -225,9 +225,30 @@ extern inline void output_wchar(std::ostream& os, wchar_t c);
|
|||
// By default, sanitization functions simply return the given argument
|
||||
// (type and value) which results in calls to standard output writers.
|
||||
template<class T>
|
||||
inline T _log_sanitize(T value, int ignored_length = 0)
|
||||
inline auto _log_sanitize(T value, int ignored_length = 0)
|
||||
{
|
||||
return value;
|
||||
// This is necessary because C++20 has deleted the overloaded operator<< of ostream for wchar_t, char8_t, char16_t and char32_t.
|
||||
// The proper solution is to use wide strings in all our logging macros/functions, see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/743
|
||||
if constexpr (std::is_same_v<xbox::wchar_xt, std::remove_cvref_t<std::remove_pointer_t<T>>>) {
|
||||
if constexpr (std::is_pointer_v<T>) {
|
||||
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(value);
|
||||
std::size_t len = std::wcslen(wstr);
|
||||
std::string dst(len + 1, '\0');
|
||||
std::mbstate_t ps{};
|
||||
std::wcsrtombs(dst.data(), &wstr, len, &ps);
|
||||
return dst;
|
||||
}
|
||||
else {
|
||||
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(&value);
|
||||
std::string dst(2, '\0');
|
||||
std::mbstate_t ps{};
|
||||
std::wcsrtombs(dst.data(), &wstr, 1, &ps);
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // TODO FIXME : Disabled for now, as this is incorrectly called for INT types too
|
||||
|
|
|
@ -319,12 +319,6 @@ bool isSystemFlagSupport(unsigned int reserved_systems, unsigned int assign_syst
|
|||
if (reserved_systems & assign_system) {
|
||||
return true;
|
||||
}
|
||||
// TODO: Once host's standalone emulation is remove from GUI, remove below as well.
|
||||
#ifndef CXBXR_EMU
|
||||
if (reserved_systems == 0) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx is free software; you can redistribute it
|
||||
// * and/or modify it under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2016 Patrick van Logchem <pvanlogchem@gmail.com>
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#ifndef RESERVEDMEMORY_H
|
||||
#define RESERVEDMEMORY_H
|
||||
|
||||
#ifndef CXBXR_EMU
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "EmuShared.h" // For XBE_MAX_VA, XBE_IMAGE_BASE and CXBX_BASE_OF_CODE
|
||||
|
||||
// The following code reserves virtual addresses from 0x00011000 upwards;
|
||||
#define VM_PLACEHOLDER_SIZE (XBE_MAX_VA - XBE_IMAGE_BASE - CXBX_BASE_OF_CODE)
|
||||
|
||||
// First, declare the '.text' section again :
|
||||
#pragma section(".text") // Note : 'read,write,execute' would cause a warning
|
||||
// Then place the following variable into the '.text' section :
|
||||
__declspec(allocate(".text"))
|
||||
// This variable *MUST* be this large, for it to take up address space
|
||||
// so that all other code and data in this module are placed outside of the
|
||||
// maximum virtual memory range.
|
||||
unsigned char virtual_memory_placeholder[VM_PLACEHOLDER_SIZE]; // = { OPCODE_NOP_90 };
|
||||
// TODO : Try to get the same result without enlarging our executable by 128 MB!
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // RESERVEDMEMORY_H
|
|
@ -52,7 +52,12 @@ static_assert(false, "Please implement support for cross-platform's user profile
|
|||
uint16_t g_LibVersion_D3D8 = 0;
|
||||
uint16_t g_LibVersion_DSOUND = 0;
|
||||
|
||||
// NOTE: Update settings_version when add/edit/delete setting's structure.
|
||||
// NOTE: Update settings_version when conversion to setting's structure is required.
|
||||
// UPDATE: When settings are removed, use "if (use false && settings_version < {next_version}) {" statement
|
||||
// until existing settings require replacement or conversion. next_version input is a hardcode number.
|
||||
// Settings version 10 and later should consider as not backward compatible.
|
||||
// TODO: Add read-only state when using an older build and add a notification for will not be able save to file.
|
||||
// The sooner we do this, the better before version upgrade.
|
||||
///////////////////////////
|
||||
// * History:
|
||||
// * 2: (RadWolfie), initial version
|
||||
|
@ -96,7 +101,6 @@ static struct {
|
|||
const char* AllowAdminPrivilege = "AllowAdminPrivilege";
|
||||
const char* LoggedModules = "LoggedModules";
|
||||
const char* LogLevel = "LogLevel";
|
||||
const char* LoaderExecutable = "LoaderExecutable";
|
||||
const char* LogPopupTestCase = "LogPopupTestCase";
|
||||
} sect_core_keys;
|
||||
|
||||
|
@ -113,8 +117,11 @@ static struct {
|
|||
|
||||
static const char* section_overlay = "overlay";
|
||||
static struct {
|
||||
const char* build_hash = "Build Hash";
|
||||
const char* FPS = "FPS";
|
||||
const char* hle_lle_stats = "HLE/LLE Stats";
|
||||
const char* title_name = "Title Name";
|
||||
const char* file_name = "File Name";
|
||||
} sect_overlay_keys;
|
||||
|
||||
static const char* section_audio = "audio";
|
||||
|
@ -378,8 +385,6 @@ bool Settings::LoadConfig()
|
|||
}
|
||||
m_core.bLogPopupTestCase = m_si.GetBoolValue(section_core, sect_core_keys.LogPopupTestCase, /*Default=*/true);
|
||||
|
||||
m_core.bUseLoaderExec = m_si.GetBoolValue(section_core, sect_core_keys.LoaderExecutable, /*Default=*/true);
|
||||
|
||||
// ==== Core End ============
|
||||
|
||||
// Delete/update legacy configs from previous revisions
|
||||
|
@ -545,8 +550,11 @@ bool Settings::LoadConfig()
|
|||
|
||||
// ==== Overlay Begin =========
|
||||
|
||||
m_overlay.build_hash = m_si.GetBoolValue(section_overlay, sect_overlay_keys.build_hash, false);
|
||||
m_overlay.fps = m_si.GetBoolValue(section_overlay, sect_overlay_keys.FPS, false);
|
||||
m_overlay.hle_lle_stats = m_si.GetBoolValue(section_overlay, sect_overlay_keys.hle_lle_stats, false);
|
||||
m_overlay.title_name = m_si.GetBoolValue(section_overlay, sect_overlay_keys.title_name, false);
|
||||
m_overlay.file_name = m_si.GetBoolValue(section_overlay, sect_overlay_keys.file_name, false);
|
||||
|
||||
// ==== Overlay End ===========
|
||||
|
||||
|
@ -601,8 +609,6 @@ bool Settings::Save(std::string file_path)
|
|||
}
|
||||
m_si.SetBoolValue(section_core, sect_core_keys.LogPopupTestCase, m_core.bLogPopupTestCase, nullptr, true);
|
||||
|
||||
m_si.SetBoolValue(section_core, sect_core_keys.LoaderExecutable, m_core.bUseLoaderExec, nullptr, true);
|
||||
|
||||
// ==== Core End ============
|
||||
|
||||
// ==== Video Begin =========
|
||||
|
@ -735,8 +741,11 @@ bool Settings::Save(std::string file_path)
|
|||
|
||||
// ==== Overlay Begin =======
|
||||
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.build_hash, m_overlay.build_hash, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.FPS, m_overlay.fps, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.hle_lle_stats, m_overlay.hle_lle_stats, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.title_name, m_overlay.title_name, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.file_name, m_overlay.file_name, nullptr, true);
|
||||
|
||||
// ==== Overlay End =========
|
||||
|
||||
|
@ -1000,7 +1009,7 @@ void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
|
|||
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
|
||||
std::string device_name = m_si.GetValue(current_section.c_str(), sect_input_port.device, "");
|
||||
|
||||
if (StrEndsWith(device_name, kb_str)) {
|
||||
if (device_name.ends_with(kb_str)) {
|
||||
device_name += "Mouse";
|
||||
m_si.SetValue(current_section.c_str(), sect_input_port.device, device_name.c_str(), nullptr, true);
|
||||
}
|
||||
|
@ -1027,4 +1036,9 @@ void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
|
|||
if(CurrentRevision < 9) {
|
||||
m_si.Delete(section_video, "HardwareYUV", true);
|
||||
}
|
||||
|
||||
// see settings_version for details.
|
||||
if(false && CurrentRevision < 10) {
|
||||
m_si.Delete(section_core, "LoaderExecutable", true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
// ******************************************************************
|
||||
#ifndef SETTINGS_HPP
|
||||
#define SETTINGS_HPP
|
||||
#include "Cxbx.h"
|
||||
|
||||
#include "SimpleIni.h"
|
||||
#include "common\input\InputManager.h"
|
||||
|
@ -103,7 +102,7 @@ public:
|
|||
char szStorageLocation[xbox::max_path] = "";
|
||||
unsigned int LoggedModules[NUM_INTEGERS_LOG];
|
||||
int LogLevel = 1;
|
||||
bool bUseLoaderExec;
|
||||
bool bUnused_WasUseLoaderExec;
|
||||
bool allowAdminPrivilege;
|
||||
bool bLogPopupTestCase;
|
||||
bool Reserved4 = 0;
|
||||
|
|
|
@ -25,18 +25,45 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include "Timer.h"
|
||||
#include "common\util\CxbxUtil.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#ifdef __linux__
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include "core\kernel\exports\EmuKrnlPs.hpp"
|
||||
#include "core\kernel\exports\EmuKrnl.h"
|
||||
#include "devices\Xbox.h"
|
||||
#include "devices\usb\OHCI.h"
|
||||
#include "core\hle\DSOUND\DirectSound\DirectSoundGlobal.hpp"
|
||||
|
||||
|
||||
static std::atomic_uint64_t last_qpc; // last time when QPC was called
|
||||
static std::atomic_uint64_t exec_time; // total execution time in us since the emulation started
|
||||
static uint64_t pit_last; // last time when the pit time was updated
|
||||
static uint64_t pit_last_qpc; // last QPC time of the pit
|
||||
// The frequency of the high resolution clock of the host, and the start time
|
||||
int64_t HostQPCFrequency, HostQPCStartTime;
|
||||
|
||||
|
||||
void timer_init()
|
||||
{
|
||||
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *>(&HostQPCFrequency));
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&HostQPCStartTime));
|
||||
pit_last_qpc = last_qpc = HostQPCStartTime;
|
||||
pit_last = get_now();
|
||||
|
||||
// Synchronize xbox system time with host time
|
||||
LARGE_INTEGER HostSystemTime;
|
||||
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
|
||||
xbox::KeSystemTime.High2Time = HostSystemTime.u.HighPart;
|
||||
xbox::KeSystemTime.LowPart = HostSystemTime.u.LowPart;
|
||||
xbox::KeSystemTime.High1Time = HostSystemTime.u.HighPart;
|
||||
}
|
||||
|
||||
// More precise sleep, but with increased CPU usage
|
||||
void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
|
||||
|
@ -68,139 +95,83 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
|
|||
}
|
||||
}
|
||||
|
||||
// Virtual clocks will probably become useful once LLE CPU is implemented, but for now we don't need them.
|
||||
// See the QEMUClockType QEMU_CLOCK_VIRTUAL of XQEMU for more info.
|
||||
#define CLOCK_REALTIME 0
|
||||
//#define CLOCK_VIRTUALTIME 1
|
||||
|
||||
|
||||
// Vector storing all the timers created
|
||||
static std::vector<TimerObject*> TimerList;
|
||||
// The frequency of the high resolution clock of the host, and the start time
|
||||
int64_t HostQPCFrequency, HostQPCStartTime;
|
||||
// Lock to acquire when accessing TimerList
|
||||
std::mutex TimerMtx;
|
||||
|
||||
|
||||
// Returns the current time of the timer
|
||||
uint64_t GetTime_NS(TimerObject* Timer)
|
||||
// NOTE: the pit device is not implemented right now, so we put this here
|
||||
static uint64_t pit_next(uint64_t now)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
uint64_t Ret = Timer_GetScaledPerformanceCounter(SCALE_S_IN_NS);
|
||||
#elif __linux__
|
||||
static struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
uint64_t Ret = Muldiv64(ts.tv_sec, SCALE_S_IN_NS, 1) + ts.tv_nsec;
|
||||
#else
|
||||
#error "Unsupported OS"
|
||||
#endif
|
||||
return Ret;
|
||||
constexpr uint64_t pit_period = 1000;
|
||||
uint64_t next = pit_last + pit_period;
|
||||
|
||||
if (now >= next) {
|
||||
xbox::KiClockIsr(now - pit_last);
|
||||
pit_last = get_now();
|
||||
return pit_period;
|
||||
}
|
||||
|
||||
return pit_last + pit_period - now; // time remaining until next clock interrupt
|
||||
}
|
||||
|
||||
// Calculates the next expire time of the timer
|
||||
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
|
||||
static void update_non_periodic_events()
|
||||
{
|
||||
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load();
|
||||
}
|
||||
// update dsound
|
||||
dsound_worker();
|
||||
|
||||
// Deallocates the memory of the timer
|
||||
void Timer_Destroy(TimerObject* Timer)
|
||||
{
|
||||
unsigned int index, i;
|
||||
std::lock_guard<std::mutex>lock(TimerMtx);
|
||||
|
||||
index = TimerList.size();
|
||||
for (i = 0; i < index; i++) {
|
||||
if (Timer == TimerList[i]) {
|
||||
index = i;
|
||||
// check for hw interrupts
|
||||
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
|
||||
// If the interrupt is pending and connected, process it
|
||||
if (g_bEnableAllInterrupts && HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
|
||||
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
assert(index != TimerList.size());
|
||||
delete Timer;
|
||||
TimerList.erase(TimerList.begin() + index);
|
||||
}
|
||||
|
||||
// Thread that runs the timer
|
||||
void ClockThread(TimerObject* Timer)
|
||||
uint64_t get_now()
|
||||
{
|
||||
uint64_t NewExpireTime;
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
uint64_t elapsed_us = now.QuadPart - last_qpc;
|
||||
last_qpc = now.QuadPart;
|
||||
elapsed_us *= 1000000;
|
||||
elapsed_us /= HostQPCFrequency;
|
||||
exec_time += elapsed_us;
|
||||
return exec_time;
|
||||
}
|
||||
|
||||
if (!Timer->Name.empty()) {
|
||||
CxbxSetThreadName(Timer->Name.c_str());
|
||||
}
|
||||
if (Timer->IsXboxTimer) {
|
||||
InitXboxThread();
|
||||
g_AffinityPolicy->SetAffinityXbox();
|
||||
} else {
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
}
|
||||
static uint64_t get_next(uint64_t now)
|
||||
{
|
||||
std::array<uint64_t, 5> next = {
|
||||
pit_next(now),
|
||||
g_NV2A->vblank_next(now),
|
||||
g_NV2A->ptimer_next(now),
|
||||
g_USB0->m_HostController->OHCI_next(now),
|
||||
dsound_next(now)
|
||||
};
|
||||
return *std::min_element(next.begin(), next.end());
|
||||
}
|
||||
|
||||
NewExpireTime = GetNextExpireTime(Timer);
|
||||
xbox::void_xt NTAPI system_events(xbox::PVOID arg)
|
||||
{
|
||||
// Testing shows that, if this thread has the same priority of the other xbox threads, it can take tens, even hundreds of ms to complete a single loop.
|
||||
// So we increase its priority to above normal, so that it scheduled more often
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
||||
|
||||
// Always run this thread at dpc level to prevent it from ever executing APCs/DPCs
|
||||
xbox::KeRaiseIrqlToDpcLevel();
|
||||
|
||||
while (true) {
|
||||
if (GetTime_NS(Timer) > NewExpireTime) {
|
||||
if (Timer->Exit.load()) {
|
||||
Timer_Destroy(Timer);
|
||||
return;
|
||||
const uint64_t last_time = get_now();
|
||||
const uint64_t nearest_next = get_next(last_time);
|
||||
|
||||
while (true) {
|
||||
update_non_periodic_events();
|
||||
uint64_t elapsed_us = get_now() - last_time;
|
||||
if (elapsed_us >= nearest_next) {
|
||||
break;
|
||||
}
|
||||
Timer->Callback(Timer->Opaque);
|
||||
NewExpireTime = GetNextExpireTime(Timer);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
Sleep(1); // prevent burning the cpu
|
||||
}
|
||||
}
|
||||
|
||||
// Changes the expire time of a timer
|
||||
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms)
|
||||
{
|
||||
Timer->ExpireTime_MS.store(Expire_ms);
|
||||
}
|
||||
|
||||
// Destroys the timer
|
||||
void Timer_Exit(TimerObject* Timer)
|
||||
{
|
||||
Timer->Exit.store(true);
|
||||
}
|
||||
|
||||
// Allocates the memory for the timer object
|
||||
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer)
|
||||
{
|
||||
std::lock_guard<std::mutex>lock(TimerMtx);
|
||||
TimerObject* pTimer = new TimerObject;
|
||||
pTimer->Type = CLOCK_REALTIME;
|
||||
pTimer->Callback = Callback;
|
||||
pTimer->ExpireTime_MS.store(0);
|
||||
pTimer->Exit.store(false);
|
||||
pTimer->Opaque = Arg;
|
||||
pTimer->Name = Name.empty() ? "Unnamed thread" : std::move(Name);
|
||||
pTimer->IsXboxTimer = IsXboxTimer;
|
||||
TimerList.emplace_back(pTimer);
|
||||
|
||||
return pTimer;
|
||||
}
|
||||
|
||||
// Starts the timer
|
||||
// Expire_MS must be expressed in NS
|
||||
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
|
||||
{
|
||||
Timer->ExpireTime_MS.store(Expire_MS);
|
||||
std::thread(ClockThread, Timer).detach();
|
||||
}
|
||||
|
||||
// Retrives the frequency of the high resolution clock of the host
|
||||
void Timer_Init()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&HostQPCFrequency));
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&HostQPCStartTime));
|
||||
#elif __linux__
|
||||
ClockFrequency = 0;
|
||||
#else
|
||||
#error "Unsupported OS"
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period)
|
||||
{
|
||||
LARGE_INTEGER currentQPC;
|
||||
|
|
|
@ -40,32 +40,12 @@
|
|||
#define SCALE_MS_IN_US 1000
|
||||
#define SCALE_US_IN_US 1
|
||||
|
||||
/* typedef of the timer object and the callback function */
|
||||
typedef void(*TimerCB)(void*);
|
||||
typedef struct _TimerObject
|
||||
{
|
||||
int Type; // timer type
|
||||
std::atomic_uint64_t ExpireTime_MS; // when the timer expires (ms)
|
||||
std::atomic_bool Exit; // indicates that the timer should be destroyed
|
||||
TimerCB Callback; // function to call when the timer expires
|
||||
void* Opaque; // opaque argument to pass to the callback
|
||||
std::string Name; // the name of the timer thread (if any)
|
||||
bool IsXboxTimer; // indicates that the timer should run on the Xbox CPU
|
||||
}
|
||||
TimerObject;
|
||||
|
||||
extern int64_t HostQPCFrequency;
|
||||
|
||||
/* Timer exported functions */
|
||||
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer);
|
||||
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS);
|
||||
void Timer_Exit(TimerObject* Timer);
|
||||
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
|
||||
uint64_t GetTime_NS(TimerObject* Timer);
|
||||
void Timer_Init();
|
||||
|
||||
void timer_init();
|
||||
uint64_t get_now();
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
|
||||
|
||||
void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
|
||||
|
||||
#endif
|
||||
|
|
169
src/common/cxbxr.cpp
Normal file
169
src/common/cxbxr.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx-Reloaded project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#define LOG_PREFIX CXBXR_MODULE::FILE
|
||||
|
||||
#ifdef CXBXR_EMU
|
||||
#include "core/kernel/support/EmuFile.h" // For g_io_mu_metadata
|
||||
#include "common/Timer.h" // For Timer_Shutdown
|
||||
#include "core/common/video/RenderBase.hpp" // For g_renderbase
|
||||
#include "core/kernel/memory-manager/VMManager.h"
|
||||
extern void CxbxrKrnlSuspendThreads();
|
||||
#endif
|
||||
|
||||
#include "cxbxr.hpp"
|
||||
|
||||
#include "EmuShared.h"
|
||||
#include "Settings.hpp"
|
||||
#include "Logging.h"
|
||||
#include "win32/WineEnv.h"
|
||||
|
||||
#include "Settings.hpp"
|
||||
|
||||
volatile bool g_bPrintfOn = true;
|
||||
|
||||
bool CreateSettings()
|
||||
{
|
||||
g_Settings = new Settings();
|
||||
if (g_Settings == nullptr) {
|
||||
PopupError(nullptr, szSettings_alloc_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_Settings->Init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_get_settings();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleFirstLaunch()
|
||||
{
|
||||
bool bFirstLaunch;
|
||||
g_EmuShared->GetIsFirstLaunch(&bFirstLaunch);
|
||||
|
||||
/* check if process is launch with elevated access then prompt for continue on or not. */
|
||||
if (!bFirstLaunch) {
|
||||
if (!CreateSettings()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wine will always run programs as administrator by default, it can be safely disregard.
|
||||
// Since Wine doesn't use root permission. Unless user is running Wine as root.
|
||||
bool bElevated = CxbxrIsElevated();
|
||||
if (bElevated && !isWineEnv() && !g_Settings->m_core.allowAdminPrivilege) {
|
||||
PopupReturn ret = PopupWarningEx(nullptr, PopupButtons::YesNo, PopupReturn::No,
|
||||
"Cxbx-Reloaded has detected that it has been launched with Administrator rights.\n"
|
||||
"\nThis is dangerous, as a maliciously modified Xbox titles could take control of your system.\n"
|
||||
"\nAre you sure you want to continue?");
|
||||
if (ret != PopupReturn::Yes) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
g_EmuShared->SetIsFirstLaunch(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[noreturn]] void CxbxrShutDown(bool is_reboot)
|
||||
{
|
||||
if (!is_reboot) {
|
||||
// Clear all kernel boot flags. These (together with the shared memory) persist until Cxbx-Reloaded is closed otherwise.
|
||||
int BootFlags = 0;
|
||||
g_EmuShared->SetBootFlags(&BootFlags);
|
||||
}
|
||||
|
||||
// NOTE: This causes a hang when exiting while NV2A is processing
|
||||
// This is okay for now: It won't leak memory or resources since TerminateProcess will free everything
|
||||
// delete g_NV2A; // TODO : g_pXbox
|
||||
|
||||
// Shutdown the input device manager
|
||||
g_InputDeviceManager.Shutdown();
|
||||
|
||||
#ifdef CXBXR_EMU
|
||||
// NOTE: this code causes freezes/crashes at shutdown, so avoid for now
|
||||
// This is very important process to prevent false positive report and allow IDEs to continue debug multiple reboots.
|
||||
//CxbxrKrnlSuspendThreads();
|
||||
|
||||
if (g_io_mu_metadata) {
|
||||
delete g_io_mu_metadata;
|
||||
g_io_mu_metadata = nullptr;
|
||||
}
|
||||
|
||||
// Shutdown the render manager
|
||||
if (g_renderbase != nullptr) {
|
||||
g_renderbase->Shutdown();
|
||||
g_renderbase = nullptr;
|
||||
}
|
||||
|
||||
// NOTE: Require to be after g_renderbase's shutdown process.
|
||||
// NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call!
|
||||
// Shutdown the memory manager
|
||||
g_VMManager.Shutdown();
|
||||
|
||||
CxbxrUnlockFilePath();
|
||||
|
||||
if (CxbxKrnl_hEmuParent != NULL && !is_reboot) {
|
||||
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
EmuShared::Cleanup();
|
||||
|
||||
TerminateProcess(GetCurrentProcess(), 0);
|
||||
}
|
||||
|
||||
[[noreturn]] void CxbxrAbortEx(CXBXR_MODULE cxbxr_module, const char* szErrorMessage, ...)
|
||||
{
|
||||
// print out error message (if exists)
|
||||
if (szErrorMessage != NULL)
|
||||
{
|
||||
char szBuffer2[1024];
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, szErrorMessage);
|
||||
vsprintf(szBuffer2, szErrorMessage, argp);
|
||||
va_end(argp);
|
||||
|
||||
(void)PopupCustomEx(nullptr, cxbxr_module, LOG_LEVEL::FATAL, PopupIcon::Error, PopupButtons::Ok, PopupReturn::Ok, "Received Fatal Message:\n\n* %s\n", szBuffer2); // Will also EmuLogEx
|
||||
}
|
||||
|
||||
EmuLogInit(LOG_LEVEL::INFO, "MAIN: Terminating Process");
|
||||
fflush(stdout);
|
||||
|
||||
// cleanup debug output
|
||||
{
|
||||
FreeConsole();
|
||||
|
||||
char buffer[16];
|
||||
|
||||
if (GetConsoleTitle(buffer, 16) != NULL)
|
||||
freopen("nul", "w", stdout);
|
||||
}
|
||||
|
||||
CxbxrShutDown();
|
||||
}
|
58
src/common/cxbxr.hpp
Normal file
58
src/common/cxbxr.hpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx-Reloaded project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
bool CreateSettings();
|
||||
|
||||
bool HandleFirstLaunch();
|
||||
|
||||
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
|
||||
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence);
|
||||
|
||||
// Loads a keys.bin file as generated by dump-xbox
|
||||
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
|
||||
void LoadXboxKeys();
|
||||
|
||||
bool CxbxrLockFilePath();
|
||||
|
||||
void CxbxrUnlockFilePath();
|
||||
|
||||
// Hybrid functions depending on specific platforms
|
||||
bool CxbxrIsElevated();
|
||||
|
||||
std::optional<std::string> CxbxrExec(bool useDebugger, void** hProcess, bool requestHandleProcess);
|
||||
|
||||
/*! cleanup emulation */
|
||||
[[noreturn]] void CxbxrAbortEx(CXBXR_MODULE cxbxr_module, const char* szErrorMessage, ...);
|
||||
|
||||
#define CxbxrAbort(fmt, ...) CxbxrAbortEx(LOG_PREFIX, fmt, ##__VA_ARGS__)
|
||||
|
||||
/*! terminate gracefully the emulation */
|
||||
[[noreturn]] void CxbxrShutDown(bool is_reboot = false);
|
|
@ -51,16 +51,17 @@ void Button::GetText(char* const text, size_t size) const
|
|||
SendMessage(m_button_hwnd, WM_GETTEXT, size, reinterpret_cast<LPARAM>(text));
|
||||
}
|
||||
|
||||
void Button::AddTooltip(HWND hwnd, HWND tooltip_hwnd, char *text) const
|
||||
void Button::AddTooltip(HWND hwnd, HWND tooltip_hwnd, std::string_view text) const
|
||||
{
|
||||
assert((hwnd != NULL) && (tooltip_hwnd != NULL));
|
||||
|
||||
std::string tooltip_text(text);
|
||||
TOOLINFO tool = { 0 };
|
||||
tool.cbSize = sizeof(tool);
|
||||
tool.hwnd = hwnd;
|
||||
tool.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
|
||||
tool.uId = reinterpret_cast<UINT_PTR>(m_button_hwnd);
|
||||
tool.lpszText = text;
|
||||
tool.lpszText = tooltip_text.data();
|
||||
SendMessage(tooltip_hwnd, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&tool));
|
||||
}
|
||||
|
||||
|
@ -106,13 +107,8 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
|
|||
|
||||
case WM_RBUTTONDOWN: {
|
||||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
if (wParam & MK_SHIFT) {
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
|
||||
}
|
||||
else if (!(wParam & ~MK_RBUTTON)) {
|
||||
button->ClearText();
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
}
|
||||
button->ClearText();
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -127,7 +123,7 @@ LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam,
|
|||
{
|
||||
// Remove the window subclass when this window is destroyed
|
||||
case WM_NCDESTROY: {
|
||||
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
|
||||
RemoveWindowSubclass(hWnd, ButtonLightgunSubclassProc, uIdSubclass);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
int GetId() const { return m_id; }
|
||||
int GetIndex() const { return m_index; }
|
||||
void *GetWnd() const { return m_wnd; }
|
||||
void AddTooltip(HWND hwnd, HWND tooltip_hwnd, char *text) const;
|
||||
void AddTooltip(HWND hwnd, HWND tooltip_hwnd, std::string_view text) const;
|
||||
|
||||
|
||||
private:
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "DInputKeyboardMouse.h"
|
||||
#include "InputManager.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include <algorithm>
|
||||
|
||||
// Unfortunately, sdl doesn't seem to be able to capture keyboard/mouse input from windows it didn't create (we currently use
|
||||
// win32 for that). So unless we create sdl windows, we will have to keep dinput around to handle keyboard/mouse input.
|
||||
|
|
|
@ -25,14 +25,15 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#include <algorithm>
|
||||
#include "Button.h"
|
||||
#include "InputManager.h"
|
||||
#include "layout_xbox_device.h"
|
||||
#include "gui/resource/ResCxbx.h"
|
||||
|
||||
|
||||
static char *tooltip_text_toggle = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
|
||||
static char *tooltip_text_no_toggle = "Left-click: start input detection\nRight-click: clear binding";
|
||||
static const char *tooltip_text_toggle = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
|
||||
static const char *tooltip_text_no_toggle = "Left-click: start input detection\nRight-click: clear binding";
|
||||
|
||||
EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
||||
{
|
||||
|
@ -57,7 +58,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
|||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(button_sbc_id); i++) {
|
||||
m_buttons.push_back(new Button(button_sbc_id[i], i, hwnd, wnd));
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_no_toggle);
|
||||
|
||||
// Install the subclass for the button control
|
||||
SetWindowSubclass(GetDlgItem(hwnd, button_sbc_id[i]), ButtonSbcSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
|
||||
|
|
|
@ -35,8 +35,9 @@
|
|||
#define _XBOXKRNL_DEFEXTRN_
|
||||
#define LOG_PREFIX CXBXR_MODULE::INPSYS
|
||||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h> // For PKINTERRUPT, etc.
|
||||
#include "common/cxbxr.hpp"
|
||||
|
||||
#include "SdlJoystick.h"
|
||||
#include "XInputPad.h"
|
||||
#include "RawDevice.h"
|
||||
|
@ -47,12 +48,17 @@
|
|||
#include "core\kernel\exports\EmuKrnl.h" // For EmuLog
|
||||
#include "EmuShared.h"
|
||||
#include "devices\usb\OHCI.h"
|
||||
#ifdef CXBXR_EMU
|
||||
#include "core/common/video/RenderBase.hpp"
|
||||
#endif
|
||||
#include <charconv>
|
||||
|
||||
// hle input specific
|
||||
#include "core\hle\XAPI\Xapi.h"
|
||||
|
||||
// Allocate enough memory for the max number of devices we can support simultaneously
|
||||
// 4 duke / S / sbc / arcade joystick / lightgun (mutually exclusive) + 8 memory units
|
||||
DeviceState g_devs[MAX_DEVS];
|
||||
|
||||
int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = {
|
||||
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_DUKE
|
||||
|
@ -78,11 +84,13 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
m_hwnd = hwnd;
|
||||
|
||||
m_PollingThread = std::thread([this, is_gui]() {
|
||||
#ifdef CXBXR_EMU
|
||||
// This code can run in both cxbx.exe and cxbxr-ldr.exe, but will not have
|
||||
// the affinity policy when running in the former.
|
||||
if (g_AffinityPolicy) {
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
}
|
||||
#endif
|
||||
|
||||
XInput::Init(m_Mtx);
|
||||
RawInput::Init(m_Mtx, is_gui, m_hwnd);
|
||||
|
@ -99,7 +107,7 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
lck.unlock();
|
||||
|
||||
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0 || Libusb::InitStatus < 0) {
|
||||
CxbxrKrnlAbort("Failed to initialize input subsystem! Consult debug log for more information");
|
||||
CxbxrAbort("Failed to initialize input subsystem! Consult debug log for more information");
|
||||
}
|
||||
|
||||
UpdateOpt(is_gui);
|
||||
|
@ -367,7 +375,11 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
|
|||
|
||||
// First check if ImGui is focus, then ignore any input update occur.
|
||||
// If somebody else is currently holding the lock, we won't wait and instead report no input changes
|
||||
if (!g_renderbase->IsImGuiFocus() && m_Mtx.try_lock()) {
|
||||
if (
|
||||
#ifdef CXBXR_EMU
|
||||
!g_renderbase->IsImGuiFocus() &&
|
||||
#endif
|
||||
m_Mtx.try_lock()) {
|
||||
for (auto &dev : m_Devices) {
|
||||
std::string port_str = std::to_string(port);
|
||||
if (dev->GetPort(port_str)) {
|
||||
|
@ -776,7 +788,7 @@ void InputDeviceManager::RefreshDevices()
|
|||
return Sdl::PopulateOK;
|
||||
});
|
||||
for (auto &dev : m_Devices) {
|
||||
if (StrStartsWith(dev->GetDeviceName(), "KeyboardMouse")) {
|
||||
if (dev->GetDeviceName().starts_with("KeyboardMouse")) {
|
||||
static_cast<DInput::KeyboardMouse *>(dev.get())->SetHwnd(m_hwnd);
|
||||
break;
|
||||
}
|
||||
|
@ -876,7 +888,7 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
|
|||
std::unique_lock<std::mutex> lck(m_Mtx);
|
||||
|
||||
auto it = std::remove_if(m_Devices.begin(), m_Devices.end(), [](const auto &Device) {
|
||||
if (Device->IsLibusb() || StrStartsWith(Device->GetAPI(), "XInput")) {
|
||||
if (Device->IsLibusb() || Device->GetAPI().starts_with("XInput")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -352,11 +352,11 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
// Axis X- <-> Cursor X-
|
||||
// Axis Y+ <-> Cursor Y-
|
||||
// Axis Y- <-> Cursor Y+
|
||||
if (StrEndsWith(m_host_dev, "KeyboardMouse")) {
|
||||
if (m_host_dev.ends_with("KeyboardMouse")) {
|
||||
assert(button != nullptr);
|
||||
char control_name[HOST_BUTTON_NAME_LENGTH];
|
||||
button->GetText(control_name, sizeof(control_name));
|
||||
if (StrStartsWith(control_name, "Axis")) {
|
||||
if (std::string_view(control_name).starts_with("Axis")) {
|
||||
switch (control_name[5])
|
||||
{
|
||||
case 'X':
|
||||
|
@ -384,7 +384,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
return;
|
||||
}
|
||||
|
||||
if (StrStartsWith(control_name, "Cursor")) {
|
||||
if (std::string_view(control_name).starts_with("Cursor")) {
|
||||
switch (control_name[7])
|
||||
{
|
||||
case 'X':
|
||||
|
|
|
@ -170,7 +170,7 @@ namespace Libusb
|
|||
}
|
||||
else {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(SupportedDevices_VidPid); ++i) {
|
||||
if ((Desc->idVendor = SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
|
||||
if ((Desc->idVendor == SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
|
||||
m_Type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
|
||||
m_UcType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
|
@ -185,9 +185,18 @@ namespace Libusb
|
|||
|
||||
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
|
||||
|
||||
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
|
||||
// The code below assumes that third-party controllers follow suit.
|
||||
if (libusb_open(Dev, &m_hDev) == 0) {
|
||||
// check if we are able to open device through libusb
|
||||
if (int err = libusb_open(Dev, &m_hDev)) {
|
||||
// Couldn't open device, create an error log report then don't use it.
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Unable to open original xbox device \"%s\" (%hX:%hX), libusb's error was: %s",
|
||||
m_Name.c_str(), Desc->idVendor, Desc->idProduct, libusb_strerror(err));
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
return;
|
||||
}
|
||||
// If we are able to open device, continue with query process.
|
||||
else {
|
||||
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
|
||||
// The code below assumes that third-party controllers follow suit.
|
||||
libusb_config_descriptor *ConfDesc;
|
||||
if (libusb_get_active_config_descriptor(Dev, &ConfDesc) == 0) {
|
||||
if (ConfDesc->bNumInterfaces == 1) {
|
||||
|
@ -211,7 +220,7 @@ namespace Libusb
|
|||
}
|
||||
}
|
||||
EmuLog(LOG_LEVEL::INFO, "Out endpoint %s", m_HasEndpointOut ? "present" : "not present");
|
||||
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum) != 0) {
|
||||
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum)) {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because libusb could not claim its interface. The error was: %s",
|
||||
m_Name.c_str(), libusb_strerror(err));
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
|
|
|
@ -35,9 +35,9 @@
|
|||
#define LOG_PREFIX CXBXR_MODULE::SDL
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "SdlJoystick.h"
|
||||
#include "XInputPad.h"
|
||||
#include "DInputKeyboardMouse.h"
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
|
||||
#include <cstring> // For memcpy
|
||||
#include "common\util\CxbxUtil.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
@ -280,27 +279,3 @@ std::string StripQuotes(const std::string& str)
|
|||
{
|
||||
return StripChars(str, "\"");
|
||||
}
|
||||
|
||||
// NOTE: with C++20, this can be replaced by simply calling full_str.ends_with()
|
||||
bool StrEndsWith(const std::string &full_str, const std::string &substr)
|
||||
{
|
||||
if (full_str.length() >= substr.length()) {
|
||||
if (full_str.compare(full_str.length() - substr.length(), substr.length(), substr) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: with C++20, this can be replaced by simply calling full_str.starts_with()
|
||||
bool StrStartsWith(const std::string &full_str, const std::string &substr)
|
||||
{
|
||||
if (!full_str.empty()) {
|
||||
if (full_str.rfind(substr, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -26,12 +26,15 @@
|
|||
#ifndef CXBXUTIL_H
|
||||
#define CXBXUTIL_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include "xbox_types.h"
|
||||
#include "Cxbx.h"
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "std_extend.hpp" // for ARRAY_SIZE
|
||||
|
||||
/* This is a linux struct for vectored I/O. See readv() and writev() */
|
||||
|
@ -67,8 +70,6 @@ bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite);
|
|||
void unix2dos(std::string& string);
|
||||
std::string StripSpaces(const std::string& str);
|
||||
std::string StripQuotes(const std::string& str);
|
||||
bool StrEndsWith(const std::string &full_str, const std::string &substr);
|
||||
bool StrStartsWith(const std::string &full_str, const std::string &substr);
|
||||
|
||||
// Retrieves the underlying integer value of a scoped enumerator. It allows to avoid using static_cast every time
|
||||
template <typename E>
|
||||
|
@ -92,4 +93,17 @@ static uint32_t RoundUp(uint32_t dwValue, uint32_t dwMult)
|
|||
return dwValue + dwMult - remainder;
|
||||
}
|
||||
|
||||
constexpr std::size_t longest_str(const std::vector<std::string_view> &vec)
|
||||
{
|
||||
if (!vec.empty()) {
|
||||
return std::max_element(vec.begin(), vec.end(),
|
||||
[](const auto &a, const auto &b) {
|
||||
return a.length() < b.length();
|
||||
})->length();
|
||||
}
|
||||
else {
|
||||
throw std::logic_error("No strings to compare!");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,335 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2013 - 2014, 2016 Mark Adler, Robert Vazan, Max Vysokikh
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "crc32c.h"
|
||||
#include <intrin.h>
|
||||
#include <algorithm>
|
||||
|
||||
#define POLY 0x82f63b78
|
||||
#define LONG_SHIFT 8192
|
||||
#define SHORT_SHIFT 256
|
||||
|
||||
typedef const uint8_t *buffer;
|
||||
|
||||
static uint32_t table[16][256];
|
||||
|
||||
static uint32_t long_shifts[4][256];
|
||||
|
||||
static uint32_t short_shifts[4][256];
|
||||
|
||||
static bool _tableInitialized;
|
||||
|
||||
void calculate_table();
|
||||
|
||||
/* Table-driven software version as a fall-back. This is about 15 times slower
|
||||
than using the hardware instructions. This assumes little-endian integers,
|
||||
as is the case on Intel processors that the assembler code here is for. */
|
||||
extern "C" CRC32C_API uint32_t crc32c_append_sw(uint32_t crci, buffer input, size_t length)
|
||||
{
|
||||
buffer next = input;
|
||||
#ifdef _M_X64
|
||||
uint64_t crc;
|
||||
#else
|
||||
uint32_t crc;
|
||||
#endif
|
||||
|
||||
crc = crci ^ 0xffffffff;
|
||||
#ifdef _M_X64
|
||||
while (length && ((uintptr_t)next & 7) != 0)
|
||||
{
|
||||
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
--length;
|
||||
}
|
||||
while (length >= 16)
|
||||
{
|
||||
crc ^= *(uint64_t *)next;
|
||||
uint64_t high = *(uint64_t *)(next + 8);
|
||||
crc = table[15][crc & 0xff]
|
||||
^ table[14][(crc >> 8) & 0xff]
|
||||
^ table[13][(crc >> 16) & 0xff]
|
||||
^ table[12][(crc >> 24) & 0xff]
|
||||
^ table[11][(crc >> 32) & 0xff]
|
||||
^ table[10][(crc >> 40) & 0xff]
|
||||
^ table[9][(crc >> 48) & 0xff]
|
||||
^ table[8][crc >> 56]
|
||||
^ table[7][high & 0xff]
|
||||
^ table[6][(high >> 8) & 0xff]
|
||||
^ table[5][(high >> 16) & 0xff]
|
||||
^ table[4][(high >> 24) & 0xff]
|
||||
^ table[3][(high >> 32) & 0xff]
|
||||
^ table[2][(high >> 40) & 0xff]
|
||||
^ table[1][(high >> 48) & 0xff]
|
||||
^ table[0][high >> 56];
|
||||
next += 16;
|
||||
length -= 16;
|
||||
}
|
||||
#else
|
||||
while (length && ((uintptr_t)next & 3) != 0)
|
||||
{
|
||||
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
--length;
|
||||
}
|
||||
while (length >= 12)
|
||||
{
|
||||
crc ^= *(uint32_t *)next;
|
||||
uint32_t high = *(uint32_t *)(next + 4);
|
||||
uint32_t high2 = *(uint32_t *)(next + 8);
|
||||
crc = table[11][crc & 0xff]
|
||||
^ table[10][(crc >> 8) & 0xff]
|
||||
^ table[9][(crc >> 16) & 0xff]
|
||||
^ table[8][crc >> 24]
|
||||
^ table[7][high & 0xff]
|
||||
^ table[6][(high >> 8) & 0xff]
|
||||
^ table[5][(high >> 16) & 0xff]
|
||||
^ table[4][high >> 24]
|
||||
^ table[3][high2 & 0xff]
|
||||
^ table[2][(high2 >> 8) & 0xff]
|
||||
^ table[1][(high2 >> 16) & 0xff]
|
||||
^ table[0][high2 >> 24];
|
||||
next += 12;
|
||||
length -= 12;
|
||||
}
|
||||
#endif
|
||||
while (length)
|
||||
{
|
||||
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
--length;
|
||||
}
|
||||
return (uint32_t)crc ^ 0xffffffff;
|
||||
}
|
||||
|
||||
/* Apply the zeros operator table to crc. */
|
||||
static inline uint32_t shift_crc(uint32_t shift_table[][256], uint32_t crc)
|
||||
{
|
||||
return shift_table[0][crc & 0xff]
|
||||
^ shift_table[1][(crc >> 8) & 0xff]
|
||||
^ shift_table[2][(crc >> 16) & 0xff]
|
||||
^ shift_table[3][crc >> 24];
|
||||
}
|
||||
|
||||
/* Compute CRC-32C using the Intel hardware instruction. */
|
||||
extern "C" CRC32C_API uint32_t crc32c_append_hw(uint32_t crc, buffer buf, size_t len)
|
||||
{
|
||||
buffer next = buf;
|
||||
buffer end;
|
||||
#ifdef _M_X64
|
||||
uint64_t crc0, crc1, crc2; /* need to be 64 bits for crc32q */
|
||||
#else
|
||||
uint32_t crc0, crc1, crc2;
|
||||
#endif
|
||||
|
||||
/* pre-process the crc */
|
||||
crc0 = crc ^ 0xffffffff;
|
||||
|
||||
/* compute the crc for up to seven leading bytes to bring the data pointer
|
||||
to an eight-byte boundary */
|
||||
while (len && ((uintptr_t)next & 7) != 0)
|
||||
{
|
||||
crc0 = _mm_crc32_u8(static_cast<uint32_t>(crc0), *next);
|
||||
++next;
|
||||
--len;
|
||||
}
|
||||
|
||||
#ifdef _M_X64
|
||||
/* compute the crc on sets of LONG_SHIFT*3 bytes, executing three independent crc
|
||||
instructions, each on LONG_SHIFT bytes -- this is optimized for the Nehalem,
|
||||
Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
|
||||
throughput of one crc per cycle, but a latency of three cycles */
|
||||
while (len >= 3 * LONG_SHIFT)
|
||||
{
|
||||
crc1 = 0;
|
||||
crc2 = 0;
|
||||
end = next + LONG_SHIFT;
|
||||
do
|
||||
{
|
||||
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
|
||||
crc1 = _mm_crc32_u64(crc1, *reinterpret_cast<const uint64_t *>(next + LONG_SHIFT));
|
||||
crc2 = _mm_crc32_u64(crc2, *reinterpret_cast<const uint64_t *>(next + 2 * LONG_SHIFT));
|
||||
next += 8;
|
||||
} while (next < end);
|
||||
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
|
||||
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
|
||||
next += 2 * LONG_SHIFT;
|
||||
len -= 3 * LONG_SHIFT;
|
||||
}
|
||||
|
||||
/* do the same thing, but now on SHORT_SHIFT*3 blocks for the remaining data less
|
||||
than a LONG_SHIFT*3 block */
|
||||
while (len >= 3 * SHORT_SHIFT)
|
||||
{
|
||||
crc1 = 0;
|
||||
crc2 = 0;
|
||||
end = next + SHORT_SHIFT;
|
||||
do
|
||||
{
|
||||
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
|
||||
crc1 = _mm_crc32_u64(crc1, *reinterpret_cast<const uint64_t *>(next + SHORT_SHIFT));
|
||||
crc2 = _mm_crc32_u64(crc2, *reinterpret_cast<const uint64_t *>(next + 2 * SHORT_SHIFT));
|
||||
next += 8;
|
||||
} while (next < end);
|
||||
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
|
||||
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
|
||||
next += 2 * SHORT_SHIFT;
|
||||
len -= 3 * SHORT_SHIFT;
|
||||
}
|
||||
|
||||
/* compute the crc on the remaining eight-byte units less than a SHORT_SHIFT*3
|
||||
block */
|
||||
end = next + (len - (len & 7));
|
||||
while (next < end)
|
||||
{
|
||||
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
|
||||
next += 8;
|
||||
}
|
||||
#else
|
||||
/* compute the crc on sets of LONG_SHIFT*3 bytes, executing three independent crc
|
||||
instructions, each on LONG_SHIFT bytes -- this is optimized for the Nehalem,
|
||||
Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
|
||||
throughput of one crc per cycle, but a latency of three cycles */
|
||||
while (len >= 3 * LONG_SHIFT)
|
||||
{
|
||||
crc1 = 0;
|
||||
crc2 = 0;
|
||||
end = next + LONG_SHIFT;
|
||||
do
|
||||
{
|
||||
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
|
||||
crc1 = _mm_crc32_u32(crc1, *reinterpret_cast<const uint32_t *>(next + LONG_SHIFT));
|
||||
crc2 = _mm_crc32_u32(crc2, *reinterpret_cast<const uint32_t *>(next + 2 * LONG_SHIFT));
|
||||
next += 4;
|
||||
} while (next < end);
|
||||
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
|
||||
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
|
||||
next += 2 * LONG_SHIFT;
|
||||
len -= 3 * LONG_SHIFT;
|
||||
}
|
||||
|
||||
/* do the same thing, but now on SHORT_SHIFT*3 blocks for the remaining data less
|
||||
than a LONG_SHIFT*3 block */
|
||||
while (len >= 3 * SHORT_SHIFT)
|
||||
{
|
||||
crc1 = 0;
|
||||
crc2 = 0;
|
||||
end = next + SHORT_SHIFT;
|
||||
do
|
||||
{
|
||||
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
|
||||
crc1 = _mm_crc32_u32(crc1, *reinterpret_cast<const uint32_t *>(next + SHORT_SHIFT));
|
||||
crc2 = _mm_crc32_u32(crc2, *reinterpret_cast<const uint32_t *>(next + 2 * SHORT_SHIFT));
|
||||
next += 4;
|
||||
} while (next < end);
|
||||
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
|
||||
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
|
||||
next += 2 * SHORT_SHIFT;
|
||||
len -= 3 * SHORT_SHIFT;
|
||||
}
|
||||
|
||||
/* compute the crc on the remaining eight-byte units less than a SHORT_SHIFT*3
|
||||
block */
|
||||
end = next + (len - (len & 7));
|
||||
while (next < end)
|
||||
{
|
||||
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
|
||||
next += 4;
|
||||
}
|
||||
#endif
|
||||
len &= 7;
|
||||
|
||||
/* compute the crc for up to seven trailing bytes */
|
||||
while (len)
|
||||
{
|
||||
crc0 = _mm_crc32_u8(static_cast<uint32_t>(crc0), *next);
|
||||
++next;
|
||||
--len;
|
||||
}
|
||||
|
||||
/* return a post-processed crc */
|
||||
return static_cast<uint32_t>(crc0) ^ 0xffffffff;
|
||||
}
|
||||
|
||||
extern "C" CRC32C_API int crc32c_hw_available()
|
||||
{
|
||||
int info[4];
|
||||
__cpuid(info, 1);
|
||||
return (info[2] & (1 << 20)) != 0;
|
||||
|
||||
}
|
||||
|
||||
void calculate_table()
|
||||
{
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
uint32_t res = (uint32_t)i;
|
||||
for(int t = 0; t < 16; t++) {
|
||||
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
|
||||
table[t][i] = res;
|
||||
}
|
||||
}
|
||||
|
||||
_tableInitialized = true;
|
||||
}
|
||||
|
||||
void calculate_table_hw()
|
||||
{
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
uint32_t res = (uint32_t)i;
|
||||
for (int k = 0; k < 8 * (SHORT_SHIFT - 4); k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
|
||||
for(int t = 0; t < 4; t++) {
|
||||
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
|
||||
short_shifts[3 - t][i] = res;
|
||||
}
|
||||
for (int k = 0; k < 8 * (LONG_SHIFT - 4 - SHORT_SHIFT); k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
|
||||
for(int t = 0; t < 4; t++) {
|
||||
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
|
||||
long_shifts[3 - t][i] = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t (*append_func)(uint32_t, buffer, size_t);
|
||||
|
||||
void __crc32_init()
|
||||
{
|
||||
if (append_func == NULL)
|
||||
{
|
||||
// somebody can call sw version directly, so, precalculate table for this version
|
||||
calculate_table();
|
||||
if (crc32c_hw_available()) {
|
||||
calculate_table_hw();
|
||||
append_func = crc32c_append_hw;
|
||||
} else {
|
||||
append_func = crc32c_append_sw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" CRC32C_API uint32_t crc32c_append(uint32_t crc, buffer input, size_t length)
|
||||
{
|
||||
if (append_func == NULL) {
|
||||
__crc32_init();
|
||||
}
|
||||
|
||||
return append_func(crc, input, length);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#ifndef CRC32C_H
|
||||
#define CRC32C_H
|
||||
|
||||
#define CRC32C_API
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
Computes CRC-32C (Castagnoli) checksum. Uses Intel's CRC32 instruction if it is available.
|
||||
Otherwise it uses a very fast software fallback.
|
||||
*/
|
||||
extern "C" CRC32C_API uint32_t crc32c_append(
|
||||
uint32_t crc, // Initial CRC value. Typically it's 0.
|
||||
// You can supply non-trivial initial value here.
|
||||
// Initial value can be used to chain CRC from multiple buffers.
|
||||
const uint8_t *input, // Data to be put through the CRC algorithm.
|
||||
size_t length); // Length of the data in the input buffer.
|
||||
|
||||
|
||||
/*
|
||||
Software fallback version of CRC-32C (Castagnoli) checksum.
|
||||
*/
|
||||
extern "C" CRC32C_API uint32_t crc32c_append_sw(uint32_t crc, const uint8_t *input, size_t length);
|
||||
|
||||
/*
|
||||
Hardware version of CRC-32C (Castagnoli) checksum. Will fail, if CPU does not support related instructions. Use a crc32c_append version instead of.
|
||||
*/
|
||||
extern "C" CRC32C_API uint32_t crc32c_append_hw(uint32_t crc, const uint8_t *input, size_t length);
|
||||
|
||||
/*
|
||||
Checks is hardware version of CRC-32C is available.
|
||||
*/
|
||||
extern "C" CRC32C_API int crc32c_hw_available();
|
||||
|
||||
#endif
|
|
@ -1,42 +0,0 @@
|
|||
#include "hasher.h"
|
||||
|
||||
#include "xxhash.h"
|
||||
#include "crc32c.h"
|
||||
#include <cstdio>
|
||||
|
||||
enum {
|
||||
HASH_NONE = 0,
|
||||
HASH_XXH3,
|
||||
HASH_CRC32C
|
||||
};
|
||||
|
||||
static int g_HashAlgorithm = HASH_NONE;
|
||||
|
||||
void InitHasher()
|
||||
{
|
||||
// Detect the best hashing algorithm to use for the host machine
|
||||
// TODO/Future Improvement: This could be expanded to support even more hash algorithims
|
||||
// And we could hash a random buffer to calculate the fastest hash to use on a given host
|
||||
printf("Selecting hash algorithm: ");
|
||||
if (crc32c_hw_available()) {
|
||||
printf("CRC32C\n");
|
||||
g_HashAlgorithm = HASH_CRC32C;
|
||||
} else {
|
||||
printf("XXH3\n");
|
||||
g_HashAlgorithm = HASH_XXH3;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ComputeHash(const void* data, size_t len)
|
||||
{
|
||||
if (g_HashAlgorithm == HASH_NONE) {
|
||||
InitHasher();
|
||||
}
|
||||
|
||||
switch (g_HashAlgorithm) {
|
||||
case HASH_XXH3: return XXH3_64bits(data, len);
|
||||
case HASH_CRC32C: return crc32c_append(0, (uint8_t*)data, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -27,8 +27,7 @@
|
|||
#ifndef _HASHER_H
|
||||
#define _HASHER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t ComputeHash(const void* data, size_t len);
|
||||
#include "xxhash.h"
|
||||
#define ComputeHash XXH3_64bits
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "cxbxr.hpp"
|
||||
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "EmuShared.h"
|
||||
|
||||
|
@ -83,8 +84,9 @@ bool EmuShared::Init(long long sessionID)
|
|||
emuSharedStr.c_str() // name of map object
|
||||
);
|
||||
|
||||
if(hMapObject == NULL)
|
||||
return false; // CxbxrKrnlAbortEx(CXBXR_MODULE::INIT, "Could not map shared memory!");
|
||||
if (hMapObject == NULL) {
|
||||
CxbxrAbortEx(CXBXR_MODULE::INIT, "Could not map shared memory!");
|
||||
}
|
||||
|
||||
if(GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
bRequireConstruction = false;
|
||||
|
@ -105,7 +107,7 @@ bool EmuShared::Init(long long sessionID)
|
|||
|
||||
if (g_EmuShared == nullptr) {
|
||||
CloseHandle(hMapObject);
|
||||
return false; // CxbxrKrnlAbortEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
|
||||
CxbxrAbortEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -272,7 +272,7 @@ class EmuShared : public Mutex
|
|||
void GetImGuiIniSettings(char value[IMGUI_INI_SIZE_MAX]) {
|
||||
Lock();
|
||||
if (m_imgui_general.ini_size < IMGUI_INI_SIZE_MAX) {
|
||||
value = '\0';
|
||||
value[0] = '\0';
|
||||
return;
|
||||
}
|
||||
strcpy_s(value, IMGUI_INI_SIZE_MAX, m_imgui_general.ini_settings);
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include "Cxbx.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "gui/resource/ResCxbx.h"
|
||||
|
||||
#include "common\IPCHybrid.hpp"
|
||||
|
@ -38,6 +37,9 @@
|
|||
#include "common\Settings.hpp"
|
||||
#include "Logging.h"
|
||||
|
||||
#ifdef CXBXR_EMU
|
||||
/*! parent window handle */
|
||||
extern "C" HWND CxbxKrnl_hEmuParent = NULL;
|
||||
|
||||
void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value)
|
||||
{
|
||||
|
@ -79,6 +81,7 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value)
|
|||
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, MAKEWPARAM(WM_COMMAND, cmdParam), value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd)
|
||||
{
|
||||
|
@ -98,6 +101,10 @@ void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const un
|
|||
cmdParam = ID_SYNC_CONFIG_INPUT;
|
||||
break;
|
||||
|
||||
case IPC_UPDATE_KERNEL::CONFIG_CHANGE_TIME:
|
||||
cmdParam = ID_SYNC_TIME_CHANGE;
|
||||
break;
|
||||
|
||||
default:
|
||||
cmdParam = 0;
|
||||
break;
|
||||
|
|
|
@ -27,12 +27,13 @@
|
|||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include <windows.h>
|
||||
#include <optional>
|
||||
#include "common/util/cliConfig.hpp"
|
||||
#include "Util.h"
|
||||
|
||||
// Source: https://stackoverflow.com/questions/8046097/how-to-check-if-a-process-has-the-administrative-rights
|
||||
bool CxbxIsElevated() {
|
||||
bool CxbxrIsElevated() {
|
||||
bool fRet = false;
|
||||
HANDLE hToken = NULL;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
|
||||
|
@ -48,7 +49,7 @@ bool CxbxIsElevated() {
|
|||
return fRet;
|
||||
}
|
||||
|
||||
std::optional<std::string> CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) {
|
||||
std::optional<std::string> CxbxrExec(bool useDebugger, void** hProcess, bool requestHandleProcess) {
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
|
|
|
@ -151,12 +151,16 @@ public:
|
|||
cpuSets.erase(cpuSets.begin());
|
||||
}
|
||||
// Otherwise: Multiple physical cores
|
||||
// Assign the first logical and physical core to Xbox, leave the rest of that
|
||||
// physical core unassigned (if hyperthreading is active), the remaining
|
||||
// physical cores to other threads
|
||||
// Assign the first highest performance logical and physical core to Xbox,
|
||||
// leave the rest of that physical core unassigned (if hyperthreading is active),
|
||||
// give the remaining physical cores to other threads
|
||||
else {
|
||||
const BYTE physicalCore = cpuSets[0]->CoreIndex;
|
||||
m_xboxCPUSet = cpuSets[0]->Id;
|
||||
auto highPerfCore = std::max_element(cpuSets.begin(), cpuSets.end(), [](const auto* left, const auto* right)
|
||||
{
|
||||
return left->EfficiencyClass < right->EfficiencyClass;
|
||||
});
|
||||
const BYTE physicalCore = (*highPerfCore)->CoreIndex;
|
||||
m_xboxCPUSet = (*highPerfCore)->Id;
|
||||
for (auto it = cpuSets.begin(); it != cpuSets.end(); ) {
|
||||
if ((*it)->CoreIndex == physicalCore) {
|
||||
it = cpuSets.erase(it);
|
||||
|
@ -196,7 +200,7 @@ class Win7Policy final : public AffinityPolicy
|
|||
public:
|
||||
bool Initialize() {
|
||||
if (!GetProcessAffinityMask(g_CurrentProcessHandle, &CPUXbox, &CPUOthers))
|
||||
CxbxrKrnlAbortEx(CXBXR_MODULE::INIT, "GetProcessAffinityMask failed.");
|
||||
CxbxrAbortEx(CXBXR_MODULE::INIT, "GetProcessAffinityMask failed.");
|
||||
|
||||
// For the other threads, remove one bit from the processor mask:
|
||||
CPUOthers = ((CPUXbox - 1) & CPUXbox);
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include <locale> // For ctime
|
||||
#include <array>
|
||||
#include "devices\LED.h" // For LED::Sequence
|
||||
#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlPrintUEM
|
||||
#include "common\crypto\EmuSha.h" // For the SHA functions
|
||||
#include "common\crypto\EmuRsa.h" // For the RSA functions
|
||||
#include "core\hle\XAPI\Xapi.h" // For LDT_FROM_DASHBOARD
|
||||
|
@ -42,12 +41,12 @@
|
|||
#include "common/AddressRanges.h"
|
||||
#include "common/xbox/Types.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
#ifdef CXBXR_EMU
|
||||
extern "C" void CxbxKrnlPrintUEM(ULONG);
|
||||
#endif
|
||||
|
||||
// construct via Xbe file
|
||||
Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
|
||||
Xbe::Xbe(const char *x_szFilename)
|
||||
{
|
||||
char szBuffer[MAX_PATH];
|
||||
|
||||
|
@ -57,22 +56,23 @@ Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
|
|||
|
||||
FILE *XbeFile = fopen(x_szFilename, "rb");
|
||||
|
||||
// verify Xbe file was opened successfully
|
||||
if(XbeFile == 0)
|
||||
{
|
||||
using namespace fs; // limit its scope inside here
|
||||
std::string XbeName = std::filesystem::path(x_szFilename).filename().string(); // recover the xbe name
|
||||
|
||||
std::string XbeName = path(x_szFilename).filename().string(); // recover the xbe name
|
||||
// verify Xbe file was opened successfully
|
||||
if(XbeFile == 0) {
|
||||
|
||||
// NOTE: the check for the existence of the child window is necessary because the user could have previously loaded the dashboard,
|
||||
// removed/changed the path and attempt to load it again from the recent list, which will crash CxbxInitWindow below
|
||||
// Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons
|
||||
if (XbeName.compare(std::string("xboxdash.xbe")) == 0 && !bFromGUI)
|
||||
{
|
||||
// Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons
|
||||
#ifdef CXBXR_EMU
|
||||
if (XbeName.compare(std::string("xboxdash.xbe")) == 0) {
|
||||
// The dashboard could not be found on partition2. This is a fatal error on the Xbox so we display the UEM. The
|
||||
// error code is different if we have a launch data page
|
||||
|
||||
CxbxInitWindow(false);
|
||||
// This is necessary because CxbxInitWindow internally calls g_AffinityPolicy->SetAffinityOther. If we are launched directly from the command line and the dashboard
|
||||
// cannot be opened, we will crash below because g_AffinityPolicy will be empty
|
||||
g_AffinityPolicy = AffinityPolicy::InitPolicy();
|
||||
CxbxInitWindow();
|
||||
|
||||
ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC;
|
||||
|
||||
|
@ -86,13 +86,15 @@ Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
|
|||
|
||||
// TODO: FATAL_ERROR_XBE_DASH_X2_PASS (requires DVD drive authentication emulation...)
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
#endif
|
||||
// Report which xbe could not be found
|
||||
SetFatalError(std::string("Could not open the Xbe file ") + XbeName);
|
||||
return;
|
||||
#ifdef CXBXR_EMU
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
printf("OK\n");
|
||||
|
||||
|
@ -107,6 +109,14 @@ Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
|
|||
*(++c) = '\0';
|
||||
}
|
||||
|
||||
printf("OK\n");
|
||||
|
||||
// remember the Xbe file name
|
||||
{
|
||||
printf("Xbe::Xbe: Storing Xbe File Name...");
|
||||
strncpy(m_szFileName, XbeName.c_str(), ARRAY_SIZE(m_szFileName) * sizeof(char));
|
||||
}
|
||||
|
||||
printf("OK\n");
|
||||
|
||||
// read Xbe image header
|
||||
|
@ -722,25 +732,29 @@ void Xbe::PurgeBadChar(std::string& s, const std::string& illegalChars)
|
|||
}
|
||||
}
|
||||
|
||||
const char *Xbe::GameRegionToString()
|
||||
const char *Xbe::GameRegionToString(uint32_t dwRegionFlags)
|
||||
{
|
||||
if (!dwRegionFlags) {
|
||||
dwRegionFlags = m_Certificate.dwGameRegion;
|
||||
}
|
||||
|
||||
const char *Region_text[] = {
|
||||
"Unknown", "NTSC", "JAP", "NTSC+JAP",
|
||||
"PAL", "PAL+NTSC", "PAL+JAP", "Region Free",
|
||||
"DEBUG", "NTSC (DEBUG)", "JAP (DEBUG)", "NTSC+JAP (DEBUG)",
|
||||
"PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAP (DEBUG)", "Region Free (DEBUG)"
|
||||
"Unknown", "NTSC", "JAPAN", "NTSC+JAPAN",
|
||||
"PAL", "PAL+NTSC", "PAL+JAPAN", "Region Free",
|
||||
"DEBUG", "NTSC (DEBUG)", "JAPAN (DEBUG)", "NTSC+JAPAN (DEBUG)",
|
||||
"PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAPAN (DEBUG)", "Region Free (DEBUG)"
|
||||
};
|
||||
const uint32_t all_regions = XBEIMAGE_GAME_REGION_NA |
|
||||
XBEIMAGE_GAME_REGION_JAPAN |
|
||||
XBEIMAGE_GAME_REGION_RESTOFWORLD |
|
||||
XBEIMAGE_GAME_REGION_MANUFACTURING;
|
||||
|
||||
if(m_Certificate.dwGameRegion & ~all_regions) {
|
||||
if(dwRegionFlags & ~all_regions) {
|
||||
return "REGION ERROR";
|
||||
}
|
||||
|
||||
uint8_t index = (m_Certificate.dwGameRegion & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
|
||||
index |= (m_Certificate.dwGameRegion & 0x7);
|
||||
uint8_t index = (dwRegionFlags & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
|
||||
index |= (dwRegionFlags & 0x7);
|
||||
return Region_text[index];
|
||||
}
|
||||
|
||||
|
@ -815,5 +829,15 @@ XbeType Xbe::GetXbeType()
|
|||
return XbeType::xtRetail;
|
||||
}
|
||||
|
||||
uint32_t Xbe::GetDiscVersion()
|
||||
{
|
||||
return m_Certificate.dwVersion & 0xFF;
|
||||
}
|
||||
|
||||
uint32_t Xbe::GetPatchVersion()
|
||||
{
|
||||
return (m_Certificate.dwVersion & 0xFFFFFF00) >> 8;
|
||||
}
|
||||
|
||||
template auto Xbe::FindSection<true>(const char *zsSectionName);
|
||||
template auto Xbe::FindSection<false>(const char *zsSectionName);
|
||||
|
|
|
@ -50,7 +50,7 @@ class Xbe : public Error
|
|||
{
|
||||
public:
|
||||
// construct via Xbe file
|
||||
Xbe(const char *x_szFilename, bool bFromGUI);
|
||||
Xbe(const char *x_szFilename);
|
||||
|
||||
// deconstructor
|
||||
~Xbe();
|
||||
|
@ -81,10 +81,13 @@ class Xbe : public Error
|
|||
void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|");
|
||||
|
||||
// Convert game region field to string
|
||||
const char *GameRegionToString();
|
||||
const char *GameRegionToString(uint32_t dwRegionFlags = 0);
|
||||
|
||||
XbeType GetXbeType();
|
||||
|
||||
uint32_t GetDiscVersion();
|
||||
uint32_t GetPatchVersion();
|
||||
|
||||
// Xbe header
|
||||
#include "AlignPrefix1.h"
|
||||
struct Header
|
||||
|
@ -157,7 +160,7 @@ class Xbe : public Error
|
|||
uint32_t dwAllowedMedia; // 0x009C - allowed media types
|
||||
uint32_t dwGameRegion; // 0x00A0 - game region
|
||||
uint32_t dwGameRatings; // 0x00A4 - game ratings
|
||||
uint32_t dwDiskNumber; // 0x00A8 - disk number
|
||||
uint32_t dwDiscNumber; // 0x00A8 - disc number
|
||||
uint32_t dwVersion; // 0x00AC - version
|
||||
uint8_t bzLanKey[16]; // 0x00B0 - lan key
|
||||
uint8_t bzSignatureKey[16]; // 0x00C0 - signature key
|
||||
|
@ -258,6 +261,9 @@ class Xbe : public Error
|
|||
// Xbe original path
|
||||
char m_szPath[MAX_PATH];
|
||||
|
||||
// Xbe original file name
|
||||
char m_szFileName[MAX_PATH];
|
||||
|
||||
// Xbe ascii title, translated from certificate title
|
||||
char m_szAsciiTitle[41];
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#include <iomanip> // For std::setfill, std::uppercase, std::hex
|
||||
#include "common/util/strConverter.hpp" // for utf16le_to_ascii
|
||||
|
||||
extern std::string FormatTitleId(uint32_t title_id); // Exposed in Emu.cpp
|
||||
|
||||
// better time
|
||||
static char *BetterTime(uint32_t x_timeDate)
|
||||
{
|
||||
|
@ -60,6 +58,34 @@ std::string DumpInformation(Xbe* Xbe_object)
|
|||
}
|
||||
|
||||
#define SSTREAM_SET_HEX(stream_name) stream_name << std::setfill('0') << std::uppercase << std::hex;
|
||||
#define SSTREAM_SET_DEC(stream_name) stream_name << std::setfill('0') << std::uppercase << std::dec;
|
||||
|
||||
std::string FormatTitleId(uint32_t title_id)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
// If the Title ID prefix is a printable character, parse it
|
||||
// This shows the correct game serial number for retail titles!
|
||||
// EG: MS-001 for 1st tile published by MS, EA-002 for 2nd title by EA, etc
|
||||
// Some special Xbes (Dashboard, XDK Samples) use non-alphanumeric serials
|
||||
// We fall back to Hex for those
|
||||
// ergo720: we cannot use isalnum() here because it will treat chars in the range -1 - 255 as valid ascii chars which can
|
||||
// lead to unicode characters being printed in the title (e.g.: dashboard uses 0xFE and 0xFF)
|
||||
uint8_t pTitleId1 = (title_id >> 24) & 0xFF;
|
||||
uint8_t pTitleId2 = (title_id >> 16) & 0xFF;
|
||||
|
||||
if ((pTitleId1 < 65 || pTitleId1 > 90) || (pTitleId2 < 65 || pTitleId2 > 90)) {
|
||||
// Prefix was non-printable, so we need to print a hex reprentation of the entire title_id
|
||||
ss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << title_id;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
ss << pTitleId1 << pTitleId2;
|
||||
ss << "-";
|
||||
ss << std::setfill('0') << std::setw(3) << std::dec << (title_id & 0x0000FFFF);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
XbePrinter::XbePrinter(Xbe* Xbe_object)
|
||||
{
|
||||
|
@ -334,8 +360,12 @@ std::string XbePrinter::GenMediaInfo()
|
|||
text << "Allowed Media : 0x" << std::setw(8) << Xbe_certificate->dwAllowedMedia << " (" << AllowedMediaToString() << ")\n";
|
||||
text << "Game Region : 0x" << std::setw(8) << Xbe_certificate->dwGameRegion << " (" << Xbe_to_print->GameRegionToString() << ")\n";
|
||||
text << "Game Ratings : 0x" << std::setw(8) << Xbe_certificate->dwGameRatings << " (" << GameRatingToString() << ")\n";
|
||||
text << "Disk Number : 0x" << std::setw(8) << Xbe_certificate->dwDiskNumber << "\n";
|
||||
text << "Version : 1." << std::dec << std::setw(2) << Xbe_certificate->dwVersion << "\n";
|
||||
text << "Disc Number : 0x" << std::setw(8) << Xbe_certificate->dwDiscNumber << "\n";
|
||||
text << "Version : 0x" << std::setw(8) << Xbe_certificate->dwVersion << "\n";
|
||||
SSTREAM_SET_DEC(text);
|
||||
text << "Disc Version : " << std::setw(0) << Xbe_to_print->GetDiscVersion() << "\n";
|
||||
text << "Patch Version : " << std::setw(0) << Xbe_to_print->GetPatchVersion() << "\n";
|
||||
|
||||
return text.str();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <cstdio>
|
||||
|
||||
extern std::string FormatTitleId(uint32_t title_id);
|
||||
|
||||
extern std::string DumpInformation(Xbe* Xbe_object);
|
||||
|
||||
class XbePrinter
|
||||
|
|
|
@ -25,6 +25,7 @@ void ImGuiAudio::DrawMenu()
|
|||
{
|
||||
if (ImGui::BeginMenu("Audio")) {
|
||||
ImGui::MenuItem("Debug General Cache Stats", NULL, &m_windows.cache_stats_general);
|
||||
ImGui::MenuItem("SoundBuffer Visualization", NULL, &m_windows.cache_visualization);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +33,9 @@ void ImGuiAudio::DrawMenu()
|
|||
void ImGuiAudio::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
|
||||
{
|
||||
//TODO: In need of make interface class to return generic info in some way.
|
||||
extern void DSound_PrintStats(bool, ImGuiWindowFlags, bool m_show_audio_stats);
|
||||
extern void DSound_PrintStats(bool, ImGuiWindowFlags, bool m_show_audio_stats);
|
||||
DSound_PrintStats(is_focus, input_handler, m_windows.cache_stats_general);
|
||||
|
||||
extern void DSound_DrawBufferVisualization(bool, bool *p_show, ImGuiWindowFlags);
|
||||
DSound_DrawBufferVisualization(is_focus, &m_windows.cache_visualization, input_handler);
|
||||
}
|
||||
|
|
|
@ -27,8 +27,11 @@
|
|||
// Intended to store as permanent settings
|
||||
|
||||
typedef struct {
|
||||
bool build_hash;
|
||||
bool fps;
|
||||
bool hle_lle_stats;
|
||||
bool title_name;
|
||||
bool file_name;
|
||||
} overlay_settings;
|
||||
|
||||
// Intended for EmuShared only below
|
||||
|
@ -44,6 +47,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
bool cache_stats_general;
|
||||
bool cache_visualization;
|
||||
bool Reserved[3];
|
||||
} imgui_audio_windows;
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ bool ImGuiUI::Initialize()
|
|||
IMGUI_CHECKVERSION();
|
||||
m_imgui_context = ImGui::CreateContext();
|
||||
if (!m_imgui_context) {
|
||||
CxbxrKrnlAbort("Unable to create ImGui context!");
|
||||
CxbxrAbort("Unable to create ImGui context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ void ImGuiUI::Shutdown()
|
|||
size_t ini_size = IMGUI_INI_SIZE_MAX;
|
||||
const char* temp_ini_settings = ImGui::SaveIniSettingsToMemory(&ini_size);
|
||||
if (ini_size > IMGUI_INI_SIZE_MAX) {
|
||||
CxbxrKrnlAbort("ImGui ini settings is too large: %d > %d (IMGUI_INI_SIZE_MAX)", ini_size, IMGUI_INI_SIZE_MAX);
|
||||
CxbxrAbort("ImGui ini settings is too large: %d > %d (IMGUI_INI_SIZE_MAX)", ini_size, IMGUI_INI_SIZE_MAX);
|
||||
}
|
||||
g_EmuShared->SetImGuiIniSettings(temp_ini_settings);
|
||||
g_EmuShared->SetOverlaySettings(&m_settings);
|
||||
|
@ -120,8 +120,11 @@ void ImGuiUI::DrawMenu()
|
|||
if (ImGui::BeginMenu("Settings")) {
|
||||
if (ImGui::BeginMenu("Overlay")) {
|
||||
bool bChanged = false;
|
||||
bChanged |= ImGui::MenuItem("Show Build Hash", NULL, &m_settings.build_hash);
|
||||
bChanged |= ImGui::MenuItem("Show FPS", NULL, &m_settings.fps);
|
||||
bChanged |= ImGui::MenuItem("Show HLE/LLE Stats", NULL, &m_settings.hle_lle_stats);
|
||||
bChanged |= ImGui::MenuItem("Show Title Name", NULL, &m_settings.title_name);
|
||||
bChanged |= ImGui::MenuItem("Show File Name", NULL, &m_settings.file_name);
|
||||
if (bChanged) {
|
||||
g_EmuShared->SetOverlaySettings(&m_settings);
|
||||
ipc_send_gui_update(IPC_UPDATE_GUI::OVERLAY, 1);
|
||||
|
@ -139,17 +142,19 @@ void ImGuiUI::DrawMenu()
|
|||
|
||||
void ImGuiUI::DrawWidgets()
|
||||
{
|
||||
if (m_settings.fps || m_settings.hle_lle_stats) {
|
||||
|
||||
constexpr auto overlay_window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing;
|
||||
bool overlay_show_topright = m_settings.fps
|
||||
|| m_settings.hle_lle_stats
|
||||
|| m_settings.title_name
|
||||
|| m_settings.file_name;
|
||||
if (overlay_show_topright) {
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - (IMGUI_MIN_DIST_SIDE/* * m_backbuffer_scale*/),
|
||||
IMGUI_MIN_DIST_TOP/* * m_backbuffer_scale*/), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(200.0f/* * m_backbuffer_scale*/, 0.0f));
|
||||
ImGui::SetNextWindowBgAlpha(0.5f);
|
||||
if (ImGui::Begin("overlay_stats", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) {
|
||||
|
||||
if (ImGui::Begin("overlay_stats_topright", nullptr, overlay_window_flags)) {
|
||||
if (m_settings.fps) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "FPS: %.2f MS / F : %.2f", fps_counter, (float)(1000.0 / fps_counter));
|
||||
}
|
||||
|
@ -180,8 +185,28 @@ void ImGuiUI::DrawWidgets()
|
|||
- ImGui::GetScrollX() - 2 * ImGui::GetStyle().ItemSpacing.x);
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), flagString.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (m_settings.title_name) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Title: %.41s", CxbxKrnl_Xbe->m_szAsciiTitle);
|
||||
}
|
||||
|
||||
if (m_settings.file_name) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "File: %.260s", CxbxKrnl_Xbe->m_szFileName);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (m_settings.build_hash) {
|
||||
ImGui::SetNextWindowPos(ImVec2(IMGUI_MIN_DIST_SIDE, ImGui::GetIO().DisplaySize.y - IMGUI_MIN_DIST_SIDE/* * m_backbuffer_scale*/),
|
||||
ImGuiCond_Always, ImVec2(0.0f, 1.0f));
|
||||
ImGui::SetNextWindowBgAlpha(0.5f);
|
||||
if (ImGui::Begin("overlay_stats_bottom", nullptr, overlay_window_flags)) {
|
||||
if (m_settings.build_hash) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Build: %s", GetGitVersionStr());
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGuiWindowFlags input_handler = m_is_focus ? ImGuiWindowFlags_None : ImGuiWindowFlags_NoInputs;
|
||||
|
|
|
@ -50,8 +50,8 @@ void ImGuiVideo::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
|
|||
if (ImGui::CollapsingHeader("Vertex Buffer Cache", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
VertexBufferConverter.DrawCacheStats();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Render the lightgun laser
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
|
||||
R"DELIMITER(
|
||||
|
||||
struct PS_INPUT // Declared identical to vertex shader output (see VS_OUTPUT)
|
||||
{
|
||||
float2 iPos : VPOS; // Screen space x,y pixel location
|
||||
|
@ -54,7 +51,8 @@ uniform const float4 FC1 : register(c17); // Note : Maps to PSH_XBOX_CONSTANT_FC
|
|||
uniform const float4 BEM[4] : register(c19); // Note : PSH_XBOX_CONSTANT_BEM for 4 texture stages
|
||||
uniform const float4 LUM[4] : register(c23); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
|
||||
uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
|
||||
|
||||
uniform const float4 FOGINFO : register(c28);
|
||||
uniform const float FOGENABLE : register(c29);
|
||||
|
||||
#define CM_LT(c) if(c < 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_LT
|
||||
#define CM_GE(c) if(c >= 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_GE
|
||||
|
@ -92,10 +90,9 @@ uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTA
|
|||
#define PS_FINALCOMBINERSETTING_CLAMP_SUM
|
||||
#endif
|
||||
|
||||
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
|
||||
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
|
||||
// Second raw string :
|
||||
R"DELIMITER(
|
||||
// Hardcoded state will be inserted here
|
||||
// <HARDCODED STATE GOES HERE>
|
||||
// End hardcoded state
|
||||
|
||||
// PS_COMBINERCOUNT_UNIQUE_C0 steers whether for C0 to use combiner stage-specific constants c0_0 .. c0_7, or c0_0 for all stages
|
||||
#ifdef PS_COMBINERCOUNT_UNIQUE_C0
|
||||
|
@ -117,7 +114,7 @@ R"DELIMITER(
|
|||
#ifdef PS_COMBINERCOUNT_MUX_MSB
|
||||
#define FCS_MUX (r0.a >= 0.5) // Check r0.a MSB; Having range upto 1 this should be equal to : (((r0.a * 255) /*mod 256*/) >= 128)
|
||||
#else // PS_COMBINERCOUNT_MUX_LSB
|
||||
#define FCS_MUX (((r0.a * 255) mod 2) >= 1) // Check r0.b LSB; Get LSB by converting 1 into 255 (highest 8-bit value) and using modulo 2. TODO : Verify correctness
|
||||
#define FCS_MUX (((r0.a * 255) % 2) >= 1) // Check r0.b LSB; Get LSB by converting 1 into 255 (highest 8-bit value) and using modulo 2. TODO : Verify correctness
|
||||
#endif
|
||||
|
||||
// PS_FINALCOMBINERSETTING_COMPLEMENT_V1, when defined, applies a modifier to the v1 input when calculating the sum register
|
||||
|
@ -141,15 +138,17 @@ R"DELIMITER(
|
|||
#define FCS_SUM s_ident // otherwise identity mapping. TODO : Confirm correctness
|
||||
#endif
|
||||
|
||||
#define xdot(s0, s1) dot((s0).rgb, (s1).rgb)
|
||||
|
||||
// Xbox supports only one 'pixel shader' opcode, but bit flags tunes it's function;
|
||||
// Here, effective all 5 Xbox opcodes, extended with a variable macro {xop_m(m,...)} for destination modifier :
|
||||
// Note : Since both d0 AND d1 could be the same output register, calculation of d2 can re-use only one (d0 or d1)
|
||||
#define xmma(d0, d1, d2, s0, s1, s2, s3, m, tmp) tmp = d0 = m(s0 * s1); d1 = m(s2 * s3); d2 = d1 + tmp // PS_COMBINEROUTPUT_AB_CD_SUM= 0x00L, // 3rd output is AB+CD
|
||||
#define xmmc(d0, d1, d2, s0, s1, s2, s3, m, tmp) tmp = d0 = m(s0 * s1); d1 = m(s2 * s3); d2 = FCS_MUX ? d1 : tmp // PS_COMBINEROUTPUT_AB_CD_MUX= 0x04L, // 3rd output is MUX(AB,CD) based on R0.a
|
||||
|
||||
#define xdm(d0, d1, s0, s1, s2, s3, m) d0 = m(dot(s0 , s1)); d1 = m( s2 * s3 ) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
|
||||
#define xdd(d0, d1, s0, s1, s2, s3, m) d0 = m(dot(s0 , s1)); d1 = m(dot(s2 , s3)) // PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only // PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
|
||||
#define xmd(d0, d1, s0, s1, s2, s3, m) d0 = m( s0 * s1 ); d1 = m(dot(s2 , s3)) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x01L,
|
||||
#define xdm(d0, d1, s0, s1, s2, s3, m) d0 = m(xdot(s0 , s1)); d1 = m( s2 * s3 ) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
|
||||
#define xdd(d0, d1, s0, s1, s2, s3, m) d0 = m(xdot(s0 , s1)); d1 = m(xdot(s2 , s3)) // PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only // PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
|
||||
#define xmd(d0, d1, s0, s1, s2, s3, m) d0 = m( s0 * s1 ); d1 = m(xdot(s2 , s3)) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x01L,
|
||||
|
||||
// After the register combiner stages, there's one (optional) final combiner step, consisting of 4 parts;
|
||||
// All the 7 final combiner inputs operate on rgb only and clamp negative input to zero:
|
||||
|
@ -171,10 +170,6 @@ R"DELIMITER(
|
|||
// HLSL : https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp
|
||||
// lerp(x, y, s ) x*(1-s ) + y*s == x + s(y-x)
|
||||
// lerp(s2, s1, s0) s2*(1-s0) + s1*s0
|
||||
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
|
||||
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
|
||||
// Second raw string :
|
||||
R"DELIMITER(
|
||||
|
||||
float m21d(const float input)
|
||||
{
|
||||
|
@ -203,20 +198,39 @@ float m21(const float input)
|
|||
return (float)tmp / 127; // -128 scales to -1.007874016, 0 scales to 0.0, 127 scales to 1.0
|
||||
}
|
||||
|
||||
float hls(float input) // 0..65535 range
|
||||
{
|
||||
float tmp = (float)(input);
|
||||
tmp = (input < 32768) ? tmp / 32767 : (tmp - 65536) / 32767; // -1..1
|
||||
return (float)tmp;
|
||||
}
|
||||
|
||||
float hlu(float input) // 0..65535 range
|
||||
{
|
||||
return (float)input / 65535; // 0..1
|
||||
}
|
||||
|
||||
float p2(float input) // power of 2
|
||||
{
|
||||
return input * input;
|
||||
}
|
||||
|
||||
// Note : each component seems already in range [0..1], but two must be combined into one
|
||||
#define TwoIntoOne(a,b) (((a * 255) * 256) + (b * 255)) / 255 // TODO : Verify whether this works at all !
|
||||
|
||||
#define TwoIntoOne(a,b) (((a * 256) + b) * 255)
|
||||
#define CalcHiLo(in) H = TwoIntoOne(in.x, in.y); L = TwoIntoOne(in.z, in.w) // TODO : Verify whether this works at all !
|
||||
|
||||
|
||||
// Dot mappings over the output value of a (4 component 8 bit unsigned) texture stage register into a (3 component float) vector value, for use in a dot product calculation:
|
||||
#define PS_DOTMAPPING_ZERO_TO_ONE(in) dm = in.rgb // :r8g8b8a8->(r,g,b): 0x00=>0, 0xff=>1 thus : output = (input / 0xff )
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1_D3D(in) dm = float3(m21d(in.x), m21d(in.y), m21d(in.z)) // :r8g8b8a8->(r,g,b): 0x00=>-128/127, 0x01=>-1, 0x80=>0, 0xff=>1 thus : output = ((input - 0x100 ) / 0x7f )
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1_GL(in) dm = float3(m21g(in.x), m21g(in.y), m21g(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x80 ) (see https://en.wikipedia.org/wiki/Two's_complement)
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21(in.x), m21(in.y), m21(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21( in.x), m21( in.y), m21( in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
|
||||
|
||||
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(H, L, 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
|
||||
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(hlu(H), hlu(L), 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
|
||||
|
||||
// Declare one sampler per each {Sampler Type, Texture Stage} combination
|
||||
// TODO : Generate sampler status?
|
||||
|
@ -310,7 +324,7 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
|
|||
/*--23 texbrdf */ #define PS_TEXTUREMODES_BRDF(ts) s = Brdf(ts); v = Sample3D(ts, s); t[ts] = v // TODO : Test (t[ts-2] is 16 bit eyePhi,eyeSigma; t[ts-1] is lightPhi,lightSigma)
|
||||
/*--23 texm3x2tex */ #define PS_TEXTUREMODES_DOT_ST(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample2D(ts, s); t[ts] = v // TODO : Test
|
||||
/*--23 texm3x2depth */ #define PS_TEXTUREMODES_DOT_ZW(ts) CalcDot(ts); n = Normal2(ts); if (n.y==0) v=1;else v = n.x / n.y; t[ts] = v // TODO : Make depth-check use result of division, but how?
|
||||
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
/*---3 texm3x3vspec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC(ts) CalcDot(ts); n = Normal3(ts); s = Reflect(n, Eye); v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_3D(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample3D(ts, s); t[ts] = v // TODO : Test
|
||||
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_CUBE(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
|
@ -323,6 +337,34 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
|
|||
|
||||
PS_OUTPUT main(const PS_INPUT xIn)
|
||||
{
|
||||
// fogging
|
||||
const float fogDepth = xIn.iFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sampl
|
||||
const int fogTableMode = FOGINFO.x;
|
||||
const float fogDensity = FOGINFO.y;
|
||||
const float fogStart = FOGINFO.z;
|
||||
const float fogEnd = FOGINFO.w;
|
||||
|
||||
const int FOG_TABLE_NONE = 0;
|
||||
const int FOG_TABLE_EXP = 1;
|
||||
const int FOG_TABLE_EXP2 = 2;
|
||||
const int FOG_TABLE_LINEAR = 3;
|
||||
|
||||
float fogFactor;
|
||||
|
||||
if(FOGENABLE == 0){
|
||||
fogFactor = 1;
|
||||
}
|
||||
else{
|
||||
if(fogTableMode == FOG_TABLE_NONE)
|
||||
fogFactor = fogDepth;
|
||||
if(fogTableMode == FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(fogDepth * fogDensity); // 1 / e^(d * density)
|
||||
if(fogTableMode == FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); // 1 / e^((d * density)^2)
|
||||
if(fogTableMode == FOG_TABLE_LINEAR)
|
||||
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
|
||||
}
|
||||
|
||||
// Local constants
|
||||
const float4 zero = 0;
|
||||
const float4 half = 0.5; // = s_negbias(zero)
|
||||
|
@ -356,12 +398,11 @@ PS_OUTPUT main(const PS_INPUT xIn)
|
|||
// Note : VFACE/FrontFace has been unreliable, investigate again if some test-case shows bland colors
|
||||
v0 = isFrontFace ? xIn.iD0 : xIn.iB0; // Diffuse front/back
|
||||
v1 = isFrontFace ? xIn.iD1 : xIn.iB1; // Specular front/back
|
||||
fog = float4(c_fog.rgb, xIn.iFog); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
|
||||
fog = float4(c_fog.rgb, saturate(fogFactor)); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
|
||||
|
||||
// Xbox shader program
|
||||
)DELIMITER", /* This terminates the 2nd raw string within the 16380 single-byte characters limit. // */
|
||||
// Third and last raw string, the footer :
|
||||
R"DELIMITER(
|
||||
// Xbox shader program will be inserted here
|
||||
// <XBOX SHADER PROGRAM GOES HERE>
|
||||
// End Xbox shader program
|
||||
|
||||
// Copy r0.rgba to output
|
||||
PS_OUTPUT xOut;
|
||||
|
@ -370,5 +411,3 @@ R"DELIMITER(
|
|||
|
||||
return xOut;
|
||||
}
|
||||
|
||||
// End of pixel shader footer)DELIMITER" /* This terminates the footer raw string" // */
|
||||
|
|
108
src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl
Normal file
108
src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Xbox HLSL pretransformed vertex shader
|
||||
|
||||
// Default values for vertex registers, and whether to use them
|
||||
uniform float4 vRegisterDefaultValues[16] : register(c192);
|
||||
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
|
||||
|
||||
uniform float4 xboxScreenspaceScale : register(c212);
|
||||
uniform float4 xboxScreenspaceOffset : register(c213);
|
||||
|
||||
|
||||
uniform float4 xboxTextureScale[4] : register(c214);
|
||||
|
||||
// Parameters for mapping the shader's fog output value to a fog factor
|
||||
uniform float4 CxbxFogInfo: register(c218); // = CXBX_D3DVS_CONSTREG_FOGINFO
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float4 v[16] : TEXCOORD;
|
||||
};
|
||||
|
||||
// Output registers
|
||||
struct VS_OUTPUT
|
||||
{
|
||||
float4 oPos : POSITION; // Homogeneous clip space position
|
||||
float4 oD0 : COLOR0; // Primary color (front-facing)
|
||||
float4 oD1 : COLOR1; // Secondary color (front-facing)
|
||||
float oFog : FOG; // Fog coordinate
|
||||
float oPts : PSIZE; // Point size
|
||||
float4 oB0 : TEXCOORD4; // Back-facing primary color
|
||||
float4 oB1 : TEXCOORD5; // Back-facing secondary color
|
||||
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
|
||||
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
|
||||
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
|
||||
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
|
||||
};
|
||||
|
||||
float4 reverseScreenspaceTransform(float4 oPos)
|
||||
{
|
||||
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
|
||||
|
||||
// On Xbox, oPos should contain the vertex position in screenspace
|
||||
// We need to reverse this transformation
|
||||
// Conventionally, each Xbox Vertex Shader includes instructions like this
|
||||
// mul oPos.xyz, r12, c-38
|
||||
// +rcc r1.x, r12.w
|
||||
// mad oPos.xyz, r12, r1.x, c-37
|
||||
// where c-37 and c-38 are reserved transform values
|
||||
|
||||
// Reverse screenspace offset
|
||||
oPos -= xboxScreenspaceOffset;
|
||||
// Reverse screenspace scale
|
||||
oPos /= xboxScreenspaceScale;
|
||||
|
||||
// Ensure w is nonzero
|
||||
if(oPos.w == 0) oPos.w = 1;
|
||||
// Reverse perspective divide
|
||||
oPos.xyz *= oPos.w;
|
||||
return oPos;
|
||||
}
|
||||
|
||||
VS_OUTPUT main(const VS_INPUT xIn)
|
||||
{
|
||||
// Input registers
|
||||
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
|
||||
|
||||
// Unpack 16 flags from 4 float4 constant registers
|
||||
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
|
||||
|
||||
// Initialize input registers from the vertex buffer data
|
||||
// Or use the register's default value (which can be changed by the title)
|
||||
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
|
||||
// Note : unroll manually instead of for-loop, because of the ## concatenation
|
||||
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
|
||||
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
|
||||
init_v( 8); init_v( 9); init_v(10); init_v(11);
|
||||
init_v(12); init_v(13); init_v(14); init_v(15);
|
||||
|
||||
// For passthrough, map output variables to their corresponding input registers
|
||||
float4 oPos = v0;
|
||||
float4 oD0 = v3;
|
||||
float4 oD1 = v4;
|
||||
float4 oFog = v5;
|
||||
float4 oPts = v6;
|
||||
float4 oB0 = v7;
|
||||
float4 oB1 = v8;
|
||||
float4 oT0 = v9;
|
||||
float4 oT1 = v10;
|
||||
float4 oT2 = v11;
|
||||
float4 oT3 = v12;
|
||||
|
||||
// Copy variables to output struct
|
||||
VS_OUTPUT xOut;
|
||||
|
||||
xOut.oPos = reverseScreenspaceTransform(oPos);
|
||||
xOut.oD0 = saturate(oD0);
|
||||
xOut.oD1 = saturate(oD1);
|
||||
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader
|
||||
xOut.oPts = oPts.x;
|
||||
xOut.oB0 = saturate(oB0);
|
||||
xOut.oB1 = saturate(oB1);
|
||||
// Scale textures (TODO: or should we apply this to the input register values?)
|
||||
xOut.oT0 = oT0 / xboxTextureScale[0];
|
||||
xOut.oT1 = oT1 / xboxTextureScale[1];
|
||||
xOut.oT2 = oT2 / xboxTextureScale[2];
|
||||
xOut.oT3 = oT3 / xboxTextureScale[3];
|
||||
|
||||
return xOut;
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
|
||||
R"DELIMITER(// Xbox HLSL vertex shader (template populated at runtime)
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float4 v[16] : TEXCOORD;
|
||||
|
@ -323,40 +320,20 @@ VS_OUTPUT main(const VS_INPUT xIn)
|
|||
init_v( 8); init_v( 9); init_v(10); init_v(11);
|
||||
init_v(12); init_v(13); init_v(14); init_v(15);
|
||||
|
||||
// Xbox shader program)DELIMITER", /* This terminates the header raw string" // */
|
||||
// Temp variable for paired VS instruction
|
||||
float4 temp;
|
||||
|
||||
R"DELIMITER(
|
||||
// Xbox shader program will be inserted here
|
||||
// <XBOX SHADER PROGRAM GOES HERE>
|
||||
// End Xbox shader program
|
||||
|
||||
// Copy variables to output struct
|
||||
VS_OUTPUT xOut;
|
||||
|
||||
// Fogging
|
||||
// TODO deduplicate
|
||||
const float fogDepth = oFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sample
|
||||
const float fogTableMode = CxbxFogInfo.x;
|
||||
const float fogDensity = CxbxFogInfo.y;
|
||||
const float fogStart = CxbxFogInfo.z;
|
||||
const float fogEnd = CxbxFogInfo.w;
|
||||
|
||||
const float FOG_TABLE_NONE = 0;
|
||||
const float FOG_TABLE_EXP = 1;
|
||||
const float FOG_TABLE_EXP2 = 2;
|
||||
const float FOG_TABLE_LINEAR = 3;
|
||||
|
||||
float fogFactor;
|
||||
if(fogTableMode == FOG_TABLE_NONE)
|
||||
fogFactor = fogDepth;
|
||||
if(fogTableMode == FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(fogDepth * fogDensity); /* / 1 / e^(d * density)*/
|
||||
if(fogTableMode == FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); /* / 1 / e^((d * density)^2)*/
|
||||
if(fogTableMode == FOG_TABLE_LINEAR)
|
||||
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
|
||||
|
||||
|
||||
xOut.oPos = reverseScreenspaceTransform(oPos);
|
||||
xOut.oD0 = saturate(oD0);
|
||||
xOut.oD1 = saturate(oD1);
|
||||
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
|
||||
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
|
||||
xOut.oPts = oPts.x;
|
||||
xOut.oB0 = saturate(oB0);
|
||||
xOut.oB1 = saturate(oB1);
|
||||
|
@ -368,5 +345,3 @@ R"DELIMITER(
|
|||
|
||||
return xOut;
|
||||
}
|
||||
|
||||
// End of vertex shader footer)DELIMITER" /* This terminates the footer raw string" // */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4256,3 +4256,27 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BlockUntilVerticalBlank
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank)()
|
||||
{
|
||||
LOG_FUNC();
|
||||
|
||||
std::unique_lock<std::mutex> lk(g_VBConditionMutex);
|
||||
g_VBConditionVariable.wait(lk);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_SetVerticalBlankCallback
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback)
|
||||
(
|
||||
X_D3DVBLANKCALLBACK pCallback
|
||||
)
|
||||
{
|
||||
LOG_FUNC_ONE_ARG(pCallback);
|
||||
|
||||
g_pXbox_VerticalBlankCallback = pCallback;
|
||||
}
|
||||
|
|
|
@ -40,26 +40,7 @@
|
|||
void LookupTrampolinesD3D();
|
||||
|
||||
// initialize render window
|
||||
extern void CxbxInitWindow(bool bFullInit);
|
||||
|
||||
extern void CxbxSetPixelContainerHeader
|
||||
(
|
||||
xbox::X_D3DPixelContainer* pPixelContainer,
|
||||
DWORD Common,
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
UINT Levels,
|
||||
xbox::X_D3DFORMAT Format,
|
||||
UINT Dimensions,
|
||||
UINT Pitch
|
||||
);
|
||||
|
||||
extern uint8_t *ConvertD3DTextureToARGB(
|
||||
xbox::X_D3DPixelContainer *pXboxPixelContainer,
|
||||
uint8_t *pSrc,
|
||||
int *pWidth, int *pHeight,
|
||||
int TextureStage = 0
|
||||
);
|
||||
extern void CxbxInitWindow();
|
||||
|
||||
void CxbxUpdateNativeD3DResources();
|
||||
|
||||
|
@ -163,14 +144,14 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetDisplayFieldStatus)
|
|||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BeginPush
|
||||
// * patch: D3DDevice_BeginPush_4
|
||||
// ******************************************************************
|
||||
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush)(dword_xt Count);
|
||||
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BeginPush2 //two arg version for xdk before 4531
|
||||
// * patch: D3DDevice_BeginPush_8
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush2)(dword_xt Count, dword_xt **ppPush);
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush_8)(dword_xt Count, dword_xt **ppPush);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_EndPush
|
||||
|
@ -232,8 +213,8 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader)
|
|||
dword_xt Address
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1)
|
||||
(
|
||||
dword_xt Address
|
||||
);
|
||||
|
@ -321,6 +302,8 @@ X_D3DSurface* WINAPI EMUPATCH(D3DDevice_GetBackBuffer2)
|
|||
int_xt BackBuffer
|
||||
);
|
||||
|
||||
X_D3DSurface* WINAPI EMUPATCH(D3DDevice_GetBackBuffer2_0__LTCG_eax1)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_GetBackBuffer
|
||||
// ******************************************************************
|
||||
|
@ -366,7 +349,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetShaderConstantMode)
|
|||
X_VERTEXSHADERCONSTANTMODE Mode
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetShaderConstantMode_0)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetShaderConstantMode_0__LTCG_eax1)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_Reset
|
||||
|
@ -376,6 +359,10 @@ xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset)
|
|||
X_D3DPRESENT_PARAMETERS *pPresentationParameters
|
||||
);
|
||||
|
||||
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset_0__LTCG_edi1)();
|
||||
|
||||
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset_0__LTCG_ebx1)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_GetRenderTarget
|
||||
// ******************************************************************
|
||||
|
@ -522,7 +509,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader)
|
|||
dword_xt Handle
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_CreateTexture2
|
||||
|
@ -628,7 +615,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture)
|
|||
dword_xt Stage
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage)
|
||||
(
|
||||
X_D3DBaseTexture *pTexture
|
||||
);
|
||||
|
@ -1310,7 +1297,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform)
|
|||
CONST D3DMATRIX *pMatrix
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform_0)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform_0__LTCG_eax1_edx2)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_MultiplyTransform
|
||||
|
@ -1407,18 +1394,27 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShader_0)();
|
|||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices)
|
||||
(
|
||||
X_D3DPRIMITIVETYPE PrimitiveType,
|
||||
uint_xt StartVertex,
|
||||
uint_xt VertexCount
|
||||
uint_xt StartVertex,
|
||||
uint_xt VertexCount
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_DrawVertices_4
|
||||
// * patch: D3DDevice_DrawVertices_4__LTCG_ecx2_eax3
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3)
|
||||
(
|
||||
X_D3DPRIMITIVETYPE PrimitiveType
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_DrawVertices_8__LTCG_eax3
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_8__LTCG_eax3)
|
||||
(
|
||||
X_D3DPRIMITIVETYPE PrimitiveType,
|
||||
uint_xt StartVertex
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_DrawVerticesUP
|
||||
// ******************************************************************
|
||||
|
@ -1430,11 +1426,11 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP)
|
|||
uint_xt VertexStreamZeroStride
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3)
|
||||
(
|
||||
X_D3DPRIMITIVETYPE PrimitiveType,
|
||||
uint_xt VertexCount,
|
||||
uint_xt VertexStreamZeroStride
|
||||
uint_xt VertexCount,
|
||||
uint_xt VertexStreamZeroStride
|
||||
);
|
||||
|
||||
|
||||
|
@ -1914,12 +1910,15 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetTexture)
|
|||
// ******************************************************************
|
||||
// * patch: CDevice_SetStateVB (D3D::CDevice::SetStateVB)
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)( xbox::ulong_xt Unknown1 );
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)(xbox::ulong_xt Unknown1);
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB_8)(xbox::addr_xt _this, xbox::ulong_xt Unknown1);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: CDevice_SetStateUP (D3D::CDevice::SetStateUP)
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP)();
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_4)(xbox::addr_xt _this);
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_0__LTCG_esi1)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_SetStipple
|
||||
|
|
|
@ -237,6 +237,24 @@ TextureArgs ExecuteTextureStage(
|
|||
|
||||
float4 main(const PS_INPUT input) : COLOR {
|
||||
|
||||
// Calculate the fog factor
|
||||
float fogFactor;
|
||||
|
||||
if (state.FogEnable == 0){
|
||||
fogFactor = 1;
|
||||
}
|
||||
else{
|
||||
if (state.FogTableMode == FOG_TABLE_NONE)
|
||||
fogFactor = input.iFog;
|
||||
if (state.FogTableMode == FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(input.iFog * state.FogDensity); // 1 / e^(d * density)
|
||||
if (state.FogTableMode == FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(input.iFog * state.FogDensity, 2)); // 1 / e^((d * density)^2)
|
||||
if (state.FogTableMode == FOG_TABLE_LINEAR)
|
||||
fogFactor = (state.FogEnd - input.iFog) / (state.FogEnd - state.FogStart); // (end - d) / (end - start)
|
||||
if (state.FogEnable == 0)
|
||||
fogFactor = 1;
|
||||
}
|
||||
TexCoords = input.iT;
|
||||
|
||||
// Each stage is passed and returns
|
||||
|
@ -284,7 +302,7 @@ float4 main(const PS_INPUT input) : COLOR {
|
|||
|
||||
// Add fog if enabled
|
||||
if (state.FogEnable) {
|
||||
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(input.iFog));
|
||||
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(fogFactor));
|
||||
}
|
||||
|
||||
// Add specular if enabled
|
||||
|
|
|
@ -63,10 +63,16 @@ namespace FixedFunctionPixelShader {
|
|||
const float X_D3DTA_COMPLEMENT = 0x00000010; // take 1.0 - x (read modifier)
|
||||
const float X_D3DTA_ALPHAREPLICATE = 0x00000020; // replicate alpha to color components (read modifier)
|
||||
|
||||
const int SAMPLE_NONE = 0;
|
||||
const int SAMPLE_2D = 1;
|
||||
const int SAMPLE_3D = 2;
|
||||
const int SAMPLE_CUBE = 3;
|
||||
const int SAMPLE_NONE = 0;
|
||||
const int SAMPLE_2D = 1;
|
||||
const int SAMPLE_3D = 2;
|
||||
const int SAMPLE_CUBE = 3;
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
|
||||
const float FOG_TABLE_NONE = 0;
|
||||
const float FOG_TABLE_EXP = 1;
|
||||
const float FOG_TABLE_EXP2 = 2;
|
||||
const float FOG_TABLE_LINEAR = 3;
|
||||
|
||||
// This state is passed to the shader
|
||||
struct PsTextureStageState {
|
||||
|
@ -125,7 +131,11 @@ namespace FixedFunctionPixelShader {
|
|||
alignas(16) float SpecularEnable;
|
||||
alignas(16) float FogEnable;
|
||||
alignas(16) float3 FogColor;
|
||||
};
|
||||
alignas(16) float FogTableMode;
|
||||
alignas(16) float FogDensity;
|
||||
alignas(16) float FogStart;
|
||||
alignas(16) float FogEnd;
|
||||
};
|
||||
#ifdef __cplusplus
|
||||
} // FixedFunctionPixelShader namespace
|
||||
#endif
|
||||
|
|
|
@ -288,20 +288,8 @@ float DoFog(const VS_INPUT xIn)
|
|||
fogDepth = abs(Projection.Position.z);
|
||||
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W)
|
||||
fogDepth = Projection.Position.w;
|
||||
|
||||
// Calculate the fog factor
|
||||
// Some of this might be better done in the pixel shader?
|
||||
float fogFactor;
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_NONE)
|
||||
fogFactor = fogDepth;
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(fogDepth * state.Fog.Density); // 1 / e^(d * density)
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(fogDepth * state.Fog.Density, 2)); // 1 / e^((d * density)^2)
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_LINEAR)
|
||||
fogFactor = (state.Fog.End - fogDepth) / (state.Fog.End - state.Fog.Start); // (end - d) / (end - start)
|
||||
|
||||
return fogFactor;
|
||||
|
||||
return fogDepth;
|
||||
}
|
||||
|
||||
float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
||||
|
@ -351,9 +339,9 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
|||
// TODO move alongside the texture transformation when it stops angering the HLSL compiler
|
||||
const float componentCount = state.TexCoordComponentCount[texCoordIndex];
|
||||
if (componentCount == 1)
|
||||
texCoord.yzw = float3(1, 0, 0);
|
||||
texCoord.yzw = float3(0, 0, 1);
|
||||
if (componentCount == 2)
|
||||
texCoord.zw = float2(1, 0);
|
||||
texCoord.zw = float2(0, 1);
|
||||
if (componentCount == 3)
|
||||
texCoord.w = 1;
|
||||
} // Generate texture coordinates
|
||||
|
@ -390,14 +378,15 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
|||
// Test case: ProjectedTexture sample, which uses 3 coordinates
|
||||
// We'll need to implement the divide when D3D stops handling it for us?
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtexturetransformflags
|
||||
// padding makes no differences in LLE. LLE works with ProjectedTexture sample without padding, but to be devided by w is necessary.
|
||||
|
||||
if (projected)
|
||||
{
|
||||
if (countFlag == 1)
|
||||
texCoord.yzw = texCoord.x;
|
||||
if (countFlag == 2)
|
||||
texCoord.zw = texCoord.y;
|
||||
if (countFlag == 3)
|
||||
texCoord.w = texCoord.z;
|
||||
//if (countFlag == 1)
|
||||
//texCoord.yz = texCoord.x;
|
||||
//if (countFlag == 2)
|
||||
//texCoord.z = texCoord.y;
|
||||
//texCoord.xyzw = texCoord.xyzw / texCoord.w;
|
||||
}
|
||||
|
||||
return texCoord;
|
||||
|
|
|
@ -289,12 +289,7 @@ bool IsTextureSampled(DecodedRegisterCombiner* pShader, int reg)
|
|||
|
||||
void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
||||
{
|
||||
// Include HLSL header and footer as raw strings :
|
||||
static const std::string hlsl_template[4] = {
|
||||
#include "core\hle\D3D8\Direct3D9\CxbxPixelShaderTemplate.hlsl"
|
||||
};
|
||||
|
||||
hlsl << hlsl_template[0]; // Start with the HLSL template header
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[0]; // Start with the HLSL template header
|
||||
|
||||
hlsl << "\n#define ALPHAKILL {"
|
||||
<< (pShader->AlphaKill[0] ? "true, " : "false, ")
|
||||
|
@ -341,9 +336,9 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
|||
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementV1, "PS_FINALCOMBINERSETTING_COMPLEMENT_V1");
|
||||
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementR0, "PS_FINALCOMBINERSETTING_COMPLEMENT_R0");
|
||||
OutputDefineFlag(hlsl, pShader->FinalCombiner.ClampSum, "PS_FINALCOMBINERSETTING_CLAMP_SUM");
|
||||
hlsl << '\n';
|
||||
|
||||
hlsl << hlsl_template[1];
|
||||
hlsl << hlsl_template[2];
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[1];
|
||||
|
||||
// Generate all four texture stages
|
||||
for (unsigned i = 0; i < PSH_XBOX_MAX_T_REGISTER_COUNT; i++) {
|
||||
|
@ -390,7 +385,7 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
|||
|
||||
FinalCombinerStageHlsl(hlsl, pShader->FinalCombiner, pShader->hasFinalCombiner);
|
||||
|
||||
hlsl << hlsl_template[3]; // Finish with the HLSL template footer
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[2]; // Finish with the HLSL template footer
|
||||
}
|
||||
|
||||
// recompile xbox pixel shader function
|
||||
|
|
|
@ -36,20 +36,14 @@ void SetXboxMultiSampleType(xbox::X_D3DMULTISAMPLE_TYPE value);
|
|||
|
||||
bool XboxRenderStateConverter::Init()
|
||||
{
|
||||
if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) {
|
||||
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3DDeferredRenderState"];
|
||||
// Get render state
|
||||
if (g_SymbolAddresses.find("D3D_g_RenderState") != g_SymbolAddresses.end()) {
|
||||
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3D_g_RenderState"];
|
||||
} else {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_RenderState was not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, D3D__RenderState points to the first Deferred render state
|
||||
// Do a little magic to verify that it's correct, then count back to determine the
|
||||
// start offset of the entire structure
|
||||
VerifyAndFixDeferredRenderStateOffset();
|
||||
|
||||
// Now use the verified Deferred offset to derive the D3D__RenderState offset
|
||||
DeriveRenderStateOffsetFromDeferredRenderStateOffset();
|
||||
|
||||
// Build a mapping of Cxbx Render State indexes to indexes within the current XDK
|
||||
BuildRenderStateMappingTable();
|
||||
|
||||
|
@ -70,54 +64,6 @@ bool IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo& aRenderStateInf
|
|||
return bIsRenderStateAvailable;
|
||||
}
|
||||
|
||||
void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset()
|
||||
{
|
||||
DWORD CullModeOffset = g_SymbolAddresses["D3DRS_CULLMODE"];
|
||||
// If we found a valid CullMode offset, verify the symbol location
|
||||
if (CullModeOffset == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "D3DRS_CULLMODE could not be found. Please update the XbSymbolDatabase submodule");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate index of D3DRS_CULLMODE for this XDK. We start counting from the first deferred state (D3DRS_FOGENABLE)
|
||||
DWORD CullModeIndex = 0;
|
||||
for (int i = xbox::X_D3DRS_DEFERRED_FIRST; i < xbox::X_D3DRS_CULLMODE; i++) {
|
||||
auto RenderStateInfo = GetDxbxRenderStateInfo(i);
|
||||
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
|
||||
CullModeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// If the offset was incorrect, calculate the correct offset, log it, and fix it
|
||||
if ((DWORD)(&D3D__RenderState[CullModeIndex]) != CullModeOffset) {
|
||||
DWORD CorrectOffset = CullModeOffset - (CullModeIndex * sizeof(DWORD));
|
||||
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState returned by XboxSymbolDatabase (0x%08X) was incorrect. Correcting to be 0x%08X.\nPlease file an issue with the XbSymbolDatabase project", D3D__RenderState, CorrectOffset);
|
||||
D3D__RenderState = (uint32_t*)CorrectOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void XboxRenderStateConverter::DeriveRenderStateOffsetFromDeferredRenderStateOffset()
|
||||
{
|
||||
// When this function is called. D3D__RenderState actually points to the first deferred render state
|
||||
// (this is X_D3DRS_FOGENABLE). We can count back from this using our RenderStateInfo table to find
|
||||
// the start of D3D__RenderStates.
|
||||
|
||||
// Count the number of render states (for this XDK) between 0 and the first deferred render state (D3DRS_FOGENABLE)
|
||||
int FirstDeferredRenderStateOffset = 0;
|
||||
for (unsigned int RenderState = xbox::X_D3DRS_FIRST; RenderState < xbox::X_D3DRS_DEFERRED_FIRST; RenderState++) {
|
||||
// if the current renderstate exists in this XDK version, count it
|
||||
auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState);
|
||||
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
|
||||
FirstDeferredRenderStateOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, FirstDeferredRenderStateOffset should point to the index of D3DRS_FOGENABLE for the given XDK
|
||||
// This will be correct as long as our table DxbxRenderStateInfo is correct
|
||||
// We can get the correct 0 offset by using a negative index
|
||||
D3D__RenderState = &D3D__RenderState[-FirstDeferredRenderStateOffset];
|
||||
}
|
||||
|
||||
void XboxRenderStateConverter::BuildRenderStateMappingTable()
|
||||
{
|
||||
EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Render State Mapping Table");
|
||||
|
|
|
@ -29,10 +29,18 @@
|
|||
|
||||
#include <d3dcompiler.h>
|
||||
#include "Shader.h"
|
||||
#include "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
|
||||
#include "core\kernel\init\CxbxKrnl.h" // LOG_TEST_CASE
|
||||
#include "core\kernel\support\Emu.h" // EmuLog
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
//#include <sstream>
|
||||
|
||||
ShaderSources g_ShaderSources;
|
||||
|
||||
std::string DebugPrependLineNumbers(std::string shaderString) {
|
||||
std::stringstream shader(shaderString);
|
||||
auto debugShader = std::stringstream();
|
||||
|
@ -140,3 +148,173 @@ extern HRESULT EmuCompileShader
|
|||
|
||||
return hRet;
|
||||
}
|
||||
|
||||
std::ifstream OpenWithRetry(const std::string& path) {
|
||||
auto fstream = std::ifstream(path);
|
||||
int failures = 0;
|
||||
while (fstream.fail()) {
|
||||
Sleep(50);
|
||||
fstream = std::ifstream(path);
|
||||
|
||||
if (failures++ > 10) {
|
||||
// crash?
|
||||
CxbxrAbort("Error opening shader file: %s", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fstream;
|
||||
}
|
||||
|
||||
int ShaderSources::Update() {
|
||||
int versionOnDisk = shaderVersionOnDisk;
|
||||
if (shaderVersionLoadedFromDisk != versionOnDisk) {
|
||||
LoadShadersFromDisk();
|
||||
shaderVersionLoadedFromDisk = versionOnDisk;
|
||||
}
|
||||
|
||||
return shaderVersionLoadedFromDisk;
|
||||
}
|
||||
|
||||
void ShaderSources::LoadShadersFromDisk() {
|
||||
const auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
|
||||
.parent_path()
|
||||
.append("hlsl");
|
||||
|
||||
// Pixel Shader Template
|
||||
{
|
||||
std::stringstream tmp;
|
||||
auto dir = hlslDir;
|
||||
dir.append("CxbxPixelShaderTemplate.hlsl");
|
||||
tmp << OpenWithRetry(dir.string()).rdbuf();
|
||||
std::string hlsl = tmp.str();
|
||||
|
||||
// Split the HLSL file on insertion points
|
||||
std::array<std::string, 2> insertionPoints = {
|
||||
"// <HARDCODED STATE GOES HERE>\n",
|
||||
"// <XBOX SHADER PROGRAM GOES HERE>\n",
|
||||
};
|
||||
int pos = 0;
|
||||
for (int i = 0; i < insertionPoints.size(); i++) {
|
||||
auto insertionPoint = insertionPoints[i];
|
||||
auto index = hlsl.find(insertionPoint, pos);
|
||||
|
||||
if (index == std::string::npos) {
|
||||
// Handle broken shaders
|
||||
this->pixelShaderTemplateHlsl[i] = "";
|
||||
}
|
||||
else {
|
||||
this->pixelShaderTemplateHlsl[i] = hlsl.substr(pos, index - pos);
|
||||
pos = index + insertionPoint.length();
|
||||
}
|
||||
}
|
||||
this->pixelShaderTemplateHlsl[insertionPoints.size()] = hlsl.substr(pos);
|
||||
}
|
||||
|
||||
// Fixed Function Pixel Shader
|
||||
{
|
||||
auto dir = hlslDir;
|
||||
this->fixedFunctionPixelShaderPath = dir.append("FixedFunctionPixelShader.hlsl").string();
|
||||
std::stringstream tmp;
|
||||
tmp << OpenWithRetry(this->fixedFunctionPixelShaderPath).rdbuf();
|
||||
this->fixedFunctionPixelShaderHlsl = tmp.str();
|
||||
}
|
||||
|
||||
// Vertex Shader Template
|
||||
{
|
||||
std::stringstream tmp;
|
||||
auto dir = hlslDir;
|
||||
dir.append("CxbxVertexShaderTemplate.hlsl");
|
||||
tmp << OpenWithRetry(dir.string()).rdbuf();
|
||||
std::string hlsl = tmp.str();
|
||||
|
||||
const std::string insertionPoint = "// <XBOX SHADER PROGRAM GOES HERE>\n";
|
||||
auto index = hlsl.find(insertionPoint);
|
||||
|
||||
if (index == std::string::npos) {
|
||||
// Handle broken shaders
|
||||
this->vertexShaderTemplateHlsl[0] = hlsl;
|
||||
this->vertexShaderTemplateHlsl[1] = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
this->vertexShaderTemplateHlsl[0] = hlsl.substr(0, index);
|
||||
this->vertexShaderTemplateHlsl[1] = hlsl.substr(index + insertionPoint.length());
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed Function Vertex Shader
|
||||
{
|
||||
auto dir = hlslDir;
|
||||
this->fixedFunctionVertexShaderPath = dir.append("FixedFunctionVertexShader.hlsl").string();
|
||||
std::stringstream tmp;
|
||||
tmp << OpenWithRetry(this->fixedFunctionVertexShaderPath).rdbuf();
|
||||
this->fixedFunctionVertexShaderHlsl = tmp.str();
|
||||
}
|
||||
|
||||
// Passthrough Vertex Shader
|
||||
{
|
||||
auto dir = hlslDir;
|
||||
this->vertexShaderPassthroughPath = dir.append("CxbxVertexShaderPassthrough.hlsl").string();
|
||||
std::stringstream tmp;
|
||||
tmp << OpenWithRetry(this->vertexShaderPassthroughPath).rdbuf();
|
||||
this->vertexShaderPassthroughHlsl = tmp.str();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderSources::InitShaderHotloading() {
|
||||
static std::jthread fsWatcherThread;
|
||||
|
||||
if (fsWatcherThread.joinable()) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Ignoring request to start shader file watcher - it has already been started.");
|
||||
return;
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Starting shader file watcher...");
|
||||
|
||||
fsWatcherThread = std::jthread([]{
|
||||
// Determine the filename and directory for the fixed function shader
|
||||
char cxbxExePath[MAX_PATH];
|
||||
GetModuleFileName(GetModuleHandle(nullptr), cxbxExePath, MAX_PATH);
|
||||
auto hlslDir = std::filesystem::path(cxbxExePath).parent_path().append("hlsl/");
|
||||
|
||||
HANDLE changeHandle = FindFirstChangeNotification(hlslDir.string().c_str(), false, FILE_NOTIFY_CHANGE_LAST_WRITE);
|
||||
|
||||
if (changeHandle == INVALID_HANDLE_VALUE) {
|
||||
DWORD errorCode = GetLastError();
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Error initializing shader file watcher: %d", errorCode);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (FindNextChangeNotification(changeHandle)) {
|
||||
WaitForSingleObject(changeHandle, INFINITE);
|
||||
|
||||
// Wait for changes to stop..
|
||||
// Will usually be at least two - one for the file and one for the directory
|
||||
while (true) {
|
||||
FindNextChangeNotification(changeHandle);
|
||||
if (WaitForSingleObject(changeHandle, 100) == WAIT_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Change detected in shader folder");
|
||||
|
||||
g_ShaderSources.shaderVersionOnDisk++;
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Shader filewatcher failed to get the next notification");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Shader file watcher exiting...");
|
||||
|
||||
// until there is a way to disable hotloading
|
||||
// this is always an error
|
||||
FindCloseChangeNotification(changeHandle);
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string> // std::string
|
||||
#include <d3dcompiler.h> // ID3DBlob (via d3d9.h > d3d11shader.h > d3dcommon.h)
|
||||
|
||||
|
@ -10,3 +11,38 @@ extern HRESULT EmuCompileShader
|
|||
ID3DBlob** ppHostShader,
|
||||
const char* pSourceName = nullptr
|
||||
);
|
||||
|
||||
struct ShaderSources {
|
||||
// Pixel Shader
|
||||
std::string pixelShaderTemplateHlsl[3];
|
||||
|
||||
std::string fixedFunctionPixelShaderHlsl;
|
||||
std::string fixedFunctionPixelShaderPath;
|
||||
|
||||
// Vertex Shader
|
||||
std::string vertexShaderTemplateHlsl[2];
|
||||
|
||||
std::string fixedFunctionVertexShaderHlsl;
|
||||
std::string fixedFunctionVertexShaderPath;
|
||||
|
||||
std::string vertexShaderPassthroughHlsl;
|
||||
std::string vertexShaderPassthroughPath;
|
||||
|
||||
// Load shaders from disk (if out-of-date)
|
||||
// and return the current loaded shader version
|
||||
int Update();
|
||||
|
||||
// Start a thread to watch for changes in the shader folder
|
||||
void InitShaderHotloading();
|
||||
|
||||
private:
|
||||
void LoadShadersFromDisk();
|
||||
|
||||
// counts upwards on every change detected to the shader source files at runtime
|
||||
std::atomic_int shaderVersionOnDisk = 0;
|
||||
// current loaded shader version
|
||||
// Initialized to < shaderVersionOnDisk
|
||||
int shaderVersionLoadedFromDisk = -1;
|
||||
};
|
||||
|
||||
extern ShaderSources g_ShaderSources;
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#include <optional>
|
||||
|
||||
typedef struct {
|
||||
char* S; // String representation.
|
||||
const char* S; // String representation.
|
||||
bool IsSamplerState; // True if the state maps to a Sampler State instead of Texture Stage
|
||||
DWORD PC; // PC Index
|
||||
} TextureStateInfo;
|
||||
|
@ -79,11 +79,12 @@ TextureStateInfo CxbxTextureStateInfo[] = {
|
|||
|
||||
bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState)
|
||||
{
|
||||
// Deferred states start at 0, this means that D3DDeferredTextureState IS D3D__TextureState
|
||||
// Deferred states start at 0, this means that D3D_g_DeferredTextureState IS D3D__TextureState
|
||||
// No further works is required to derive the offset
|
||||
if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) {
|
||||
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"];
|
||||
if (g_SymbolAddresses.find("D3D_g_DeferredTextureState") != g_SymbolAddresses.end()) {
|
||||
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3D_g_DeferredTextureState"];
|
||||
} else {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_DeferredTextureState was not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -361,9 +362,9 @@ DWORD NormalizeValue(DWORD xboxState, DWORD value) {
|
|||
|
||||
uint32_t XboxTextureStateConverter::Get(int textureStage, DWORD xboxState) {
|
||||
if (textureStage < 0 || textureStage > 3)
|
||||
CxbxrKrnlAbort("Requested texture stage was out of range: %d", textureStage);
|
||||
CxbxrAbort("Requested texture stage was out of range: %d", textureStage);
|
||||
if (xboxState < xbox::X_D3DTSS_FIRST || xboxState > xbox::X_D3DTSS_LAST)
|
||||
CxbxrKrnlAbort("Requested texture state was out of range: %d", xboxState);
|
||||
CxbxrAbort("Requested texture state was out of range: %d", xboxState);
|
||||
|
||||
// Read the value of the current stage/state from the Xbox data structure
|
||||
DWORD rawValue = D3D__TextureState[(textureStage * xbox::X_D3DTS_STAGESIZE) + XboxTextureStateOffsets[xboxState]];
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
#include "core\kernel\init\CxbxKrnl.h" // implicit CxbxKrnl_Xbe used in LOG_TEST_CASE
|
||||
#include "core\kernel\support\Emu.h" // LOG_TEST_CASE (via Logging.h)
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream> // std::stringstream
|
||||
|
||||
extern const char* g_vs_model = vs_model_3_0;
|
||||
|
||||
// HLSL generation
|
||||
void OutputHlsl(std::stringstream& hlsl, VSH_IMD_OUTPUT& dest)
|
||||
{
|
||||
void DestRegisterHlsl(std::stringstream& hlsl, VSH_IMD_DEST& dest) {
|
||||
static const char* OReg_Name[/*VSH_OREG_NAME*/] = {
|
||||
"oPos",
|
||||
"???",
|
||||
|
@ -33,34 +30,37 @@ void OutputHlsl(std::stringstream& hlsl, VSH_IMD_OUTPUT& dest)
|
|||
};
|
||||
|
||||
switch (dest.Type) {
|
||||
case IMD_OUTPUT_C:
|
||||
case IMD_DEST_C:
|
||||
// Access the HLSL capital C[] constants array, with the index bias applied :
|
||||
// TODO : Avoid out-of-bound writes (perhaps writing to a reserved index?)
|
||||
hlsl << "C[" << dest.Address + X_D3DSCM_CORRECTION << "]";
|
||||
LOG_TEST_CASE("Vertex shader writes to constant table");
|
||||
break;
|
||||
case IMD_OUTPUT_R:
|
||||
case IMD_DEST_R:
|
||||
hlsl << "r" << dest.Address;
|
||||
break;
|
||||
case IMD_OUTPUT_O:
|
||||
case IMD_DEST_O:
|
||||
assert(dest.Address < OREG_A0X);
|
||||
hlsl << OReg_Name[dest.Address];
|
||||
break;
|
||||
case IMD_OUTPUT_A0X:
|
||||
case IMD_DEST_A0X:
|
||||
hlsl << "a0";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DestMaskHlsl(std::stringstream& hlsl, VSH_IMD_DEST& dest)
|
||||
{
|
||||
// Write the mask as a separate argument to the opcode defines
|
||||
// (No space, so that "dest,mask, ..." looks close to "dest.mask, ...")
|
||||
hlsl << ",";
|
||||
|
||||
// Detect oFog masks other than x
|
||||
// Test case: Lego Star Wars II (menu)
|
||||
if (dest.Type == IMD_OUTPUT_O &&
|
||||
if (dest.Type == IMD_DEST_O &&
|
||||
dest.Address == OREG_OFOG &&
|
||||
dest.Mask != MASK_X)
|
||||
{
|
||||
|
@ -77,9 +77,9 @@ void OutputHlsl(std::stringstream& hlsl, VSH_IMD_OUTPUT& dest)
|
|||
if (dest.Mask & MASK_W) hlsl << "w";
|
||||
}
|
||||
|
||||
void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool IndexesWithA0_X)
|
||||
void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool IndexesWithA0_X, bool useTemp)
|
||||
{
|
||||
static char* RegisterName[/*VSH_PARAMETER_TYPE*/] = {
|
||||
static const char* RegisterName[/*VSH_PARAMETER_TYPE*/] = {
|
||||
"?", // PARAM_UNKNOWN = 0,
|
||||
"r", // PARAM_R, // Temporary (scRatch) registers
|
||||
"v", // PARAM_V, // Vertex registers
|
||||
|
@ -91,7 +91,10 @@ void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool Index
|
|||
hlsl << "-";
|
||||
}
|
||||
|
||||
if (param.ParameterType == PARAM_C) {
|
||||
if (useTemp) {
|
||||
hlsl << "temp";
|
||||
}
|
||||
else if (param.Type == PARAM_C) {
|
||||
// Access constant registers through our HLSL c() function,
|
||||
// which allows dumping negative indices (like Xbox shaders),
|
||||
// and which returns zero when out-of-bounds indices are passed in:
|
||||
|
@ -111,7 +114,7 @@ void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool Index
|
|||
}
|
||||
}
|
||||
else {
|
||||
hlsl << RegisterName[param.ParameterType] << param.Address;
|
||||
hlsl << RegisterName[param.Type] << param.Address;
|
||||
}
|
||||
|
||||
// Write the swizzle if we need to
|
||||
|
@ -174,25 +177,107 @@ void BuildShader(IntermediateVertexShader* pShader, std::stringstream& hlsl)
|
|||
/*ILU_LIT:*/"x_lit" // = 7 - all values of the 3 bits are used
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < pShader->Instructions.size(); i++) {
|
||||
VSH_INTERMEDIATE_FORMAT& IntermediateInstruction = pShader->Instructions[i];
|
||||
auto WriteOp = [&](
|
||||
const std::string& opcode,
|
||||
VSH_IMD_DEST dest,
|
||||
int paramCount, VSH_IMD_PARAMETER* params,
|
||||
bool indexesWithA0_X,
|
||||
bool iluUseTempParam
|
||||
) {
|
||||
// opcode(dest, a, b, c);
|
||||
hlsl << "\n " << opcode << "(";
|
||||
|
||||
std::string str;
|
||||
if (IntermediateInstruction.MAC > MAC_NOP) {
|
||||
str = VSH_MAC_HLSL[IntermediateInstruction.MAC];
|
||||
}
|
||||
else {
|
||||
str = VSH_ILU_HLSL[IntermediateInstruction.ILU];
|
||||
}
|
||||
DestRegisterHlsl(hlsl, dest);
|
||||
DestMaskHlsl(hlsl, dest);
|
||||
|
||||
hlsl << "\n " << str << "("; // opcode
|
||||
OutputHlsl(hlsl, IntermediateInstruction.Output);
|
||||
for (unsigned i = 0; i < IntermediateInstruction.ParamCount; i++) {
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
hlsl << ", ";
|
||||
ParameterHlsl(hlsl, IntermediateInstruction.Parameters[i], IntermediateInstruction.IndexesWithA0_X);
|
||||
ParameterHlsl(hlsl, params[i], indexesWithA0_X, iluUseTempParam);
|
||||
}
|
||||
|
||||
hlsl << ");";
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < pShader->Instructions.size(); i++) {
|
||||
VSH_IMD_INSTR& in = pShader->Instructions[i];
|
||||
|
||||
// Paired if both MAC and ILU write to a dest register
|
||||
bool isPaired =
|
||||
in.MAC.Opcode != MAC_NOP &&
|
||||
in.ILU.Opcode != ILU_NOP &&
|
||||
(in.MAC.Dest.Mask || in.ORegSource == SRC_MAC) &&
|
||||
(in.ILU.Dest.Mask || in.ORegSource == SRC_ILU);
|
||||
|
||||
// If there are two "paired" instructions that need to run "simultaneously",
|
||||
// we need to prevent the output of the first instruction interfering
|
||||
// with the input of the second instruction
|
||||
// If the MAC output is the same as the ILU input
|
||||
// we will use a temp variable to hold the ILU input
|
||||
VSH_IMD_DEST* iluTemp = nullptr;
|
||||
if (isPaired) {
|
||||
if (in.MAC.Dest.Address == in.ILU.Parameter.Address &&
|
||||
(in.MAC.Dest.Type == IMD_DEST_C && in.ILU.Parameter.Type == PARAM_C ||
|
||||
in.MAC.Dest.Type == IMD_DEST_R && in.ILU.Parameter.Type == PARAM_R ||
|
||||
in.MAC.Dest.Type == IMD_DEST_A0X && in.ILU.Parameter.Type == PARAM_C && in.IndexesWithA0_X)) {
|
||||
// Normal MAC output matches ILU input
|
||||
iluTemp = &in.MAC.Dest;
|
||||
}
|
||||
else if (in.ORegSource == SRC_MAC &&
|
||||
in.ORegDest.Type == IMD_DEST_O && in.ORegDest.Address == 0 &&
|
||||
in.ILU.Parameter.Type == PARAM_R && in.ILU.Parameter.Address == 12) {
|
||||
// OReg MAC output matches ILU input
|
||||
// Note oPos is the same as r12
|
||||
iluTemp = &in.ORegDest;
|
||||
}
|
||||
|
||||
if (iluTemp) {
|
||||
// MAC and ILU use the same register.
|
||||
// This is fine unless the ILU op uses a component written to by the MAC op
|
||||
bool conflict = false;
|
||||
for (int s = 0; s < 4; s++) {
|
||||
auto swizzle = in.ILU.Parameter.Swizzle[s];
|
||||
if (iluTemp->Mask & MASK_X && swizzle == SWIZZLE_X ||
|
||||
iluTemp->Mask & MASK_Y && swizzle == SWIZZLE_Y ||
|
||||
iluTemp->Mask & MASK_Z && swizzle == SWIZZLE_Z ||
|
||||
iluTemp->Mask & MASK_W && swizzle == SWIZZLE_W) {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!conflict) {
|
||||
iluTemp = nullptr; // We don't need a temp after all
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iluTemp) {
|
||||
// Write the ILU input to a temp
|
||||
hlsl << "\n " << "temp = ";
|
||||
DestRegisterHlsl(hlsl, *iluTemp);
|
||||
hlsl << ";";
|
||||
}
|
||||
|
||||
// Write MAC op
|
||||
if (in.MAC.Opcode != MAC_NOP) {
|
||||
if (in.MAC.Dest.Mask) {
|
||||
WriteOp(VSH_MAC_HLSL[in.MAC.Opcode], in.MAC.Dest, in.MAC.ParamCount, in.MAC.Parameters, in.IndexesWithA0_X, false);
|
||||
}
|
||||
if (in.ORegSource == SRC_MAC && in.ORegDest.Mask) {
|
||||
WriteOp(VSH_MAC_HLSL[in.MAC.Opcode], in.ORegDest, in.MAC.ParamCount, in.MAC.Parameters, in.IndexesWithA0_X, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Write ILU op
|
||||
if (in.ILU.Opcode != ILU_NOP) {
|
||||
if (in.ILU.Dest.Mask) {
|
||||
WriteOp(VSH_ILU_HLSL[in.ILU.Opcode], in.ILU.Dest, 1, &in.ILU.Parameter, in.IndexesWithA0_X, iluTemp);
|
||||
}
|
||||
if (in.ORegSource == SRC_ILU && in.ORegDest.Mask) {
|
||||
WriteOp(VSH_ILU_HLSL[in.ILU.Opcode], in.ORegDest, 1, &in.ILU.Parameter, in.IndexesWithA0_X, iluTemp);
|
||||
}
|
||||
}
|
||||
|
||||
hlsl << "\n"; // Group operations by instruction
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,26 +288,23 @@ extern HRESULT EmuCompileVertexShader
|
|||
ID3DBlob** ppHostShader
|
||||
)
|
||||
{
|
||||
// Include HLSL header and footer as raw strings :
|
||||
static std::string hlsl_template[2] = {
|
||||
#include "core\hle\D3D8\Direct3D9\CxbxVertexShaderTemplate.hlsl"
|
||||
};
|
||||
|
||||
auto hlsl_stream = std::stringstream();
|
||||
hlsl_stream << hlsl_template[0]; // Start with the HLSL template header
|
||||
assert(pIntermediateShader->Instructions.size() > 0);
|
||||
BuildShader(pIntermediateShader, hlsl_stream);
|
||||
|
||||
hlsl_stream << hlsl_template[1]; // Finish with the HLSL template footer
|
||||
// Combine the shader template with the shader program
|
||||
auto hlsl_stream = std::stringstream();
|
||||
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[0]; // Start with the HLSL template header
|
||||
BuildShader(pIntermediateShader, hlsl_stream);
|
||||
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[1]; // Finish with the HLSL template footer
|
||||
std::string hlsl_str = hlsl_stream.str();
|
||||
|
||||
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
|
||||
const char* notionalSourceName = "CxbxVertexShaderTemplate.hlsl";
|
||||
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, notionalSourceName);
|
||||
|
||||
if (FAILED(hRet) && (g_vs_model != vs_model_3_0)) {
|
||||
// If the shader failed in the default vertex shader model, retry in vs_model_3_0
|
||||
// This allows shaders too large for 2_a to be compiled (Test Case: Shenmue 2)
|
||||
EmuLog(LOG_LEVEL::WARNING, "Shader compile failed. Retrying with shader model 3.0");
|
||||
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
|
||||
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, notionalSourceName);
|
||||
}
|
||||
|
||||
return hRet;
|
||||
|
@ -230,174 +312,10 @@ extern HRESULT EmuCompileVertexShader
|
|||
|
||||
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader)
|
||||
{
|
||||
static ID3DBlob* pShader = nullptr;
|
||||
|
||||
// TODO does this need to be thread safe?
|
||||
if (pShader == nullptr) {
|
||||
// Determine the filename and directory for the fixed function shader
|
||||
auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
|
||||
.parent_path()
|
||||
.append("hlsl");
|
||||
|
||||
auto sourceFile = hlslDir.append("FixedFunctionVertexShader.hlsl").string();
|
||||
|
||||
// Load the shader into a string
|
||||
std::ifstream hlslStream(sourceFile);
|
||||
std::stringstream hlsl;
|
||||
hlsl << hlslStream.rdbuf();
|
||||
|
||||
// Compile the shader
|
||||
EmuCompileShader(hlsl.str(), g_vs_model, &pShader, sourceFile.c_str());
|
||||
}
|
||||
|
||||
*ppHostShader = pShader;
|
||||
EmuCompileShader(g_ShaderSources.fixedFunctionVertexShaderHlsl, g_vs_model, ppHostShader, g_ShaderSources.fixedFunctionVertexShaderPath.c_str());
|
||||
};
|
||||
|
||||
static ID3DBlob* pPassthroughShader = nullptr;
|
||||
|
||||
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
|
||||
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
|
||||
{
|
||||
// TODO does this need to be thread safe?
|
||||
if (pPassthroughShader == nullptr) {
|
||||
auto hlsl =
|
||||
R"(
|
||||
// Xbox HLSL pretransformed vertex shader
|
||||
|
||||
// Default values for vertex registers, and whether to use them
|
||||
uniform float4 vRegisterDefaultValues[16] : register(c192);
|
||||
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
|
||||
|
||||
uniform float4 xboxScreenspaceScale : register(c212);
|
||||
uniform float4 xboxScreenspaceOffset : register(c213);
|
||||
|
||||
|
||||
uniform float4 xboxTextureScale[4] : register(c214);
|
||||
|
||||
// Parameters for mapping the shader's fog output value to a fog factor
|
||||
uniform float4 CxbxFogInfo: register(c218); // = CXBX_D3DVS_CONSTREG_FOGINFO
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float4 v[16] : TEXCOORD;
|
||||
};
|
||||
|
||||
// Output registers
|
||||
struct VS_OUTPUT
|
||||
{
|
||||
float4 oPos : POSITION; // Homogeneous clip space position
|
||||
float4 oD0 : COLOR0; // Primary color (front-facing)
|
||||
float4 oD1 : COLOR1; // Secondary color (front-facing)
|
||||
float oFog : FOG; // Fog coordinate
|
||||
float oPts : PSIZE; // Point size
|
||||
float4 oB0 : TEXCOORD4; // Back-facing primary color
|
||||
float4 oB1 : TEXCOORD5; // Back-facing secondary color
|
||||
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
|
||||
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
|
||||
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
|
||||
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
|
||||
};
|
||||
|
||||
float4 reverseScreenspaceTransform(float4 oPos)
|
||||
{
|
||||
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
|
||||
|
||||
// On Xbox, oPos should contain the vertex position in screenspace
|
||||
// We need to reverse this transformation
|
||||
// Conventionally, each Xbox Vertex Shader includes instructions like this
|
||||
// mul oPos.xyz, r12, c-38
|
||||
// +rcc r1.x, r12.w
|
||||
// mad oPos.xyz, r12, r1.x, c-37
|
||||
// where c-37 and c-38 are reserved transform values
|
||||
|
||||
// Reverse screenspace offset
|
||||
oPos -= xboxScreenspaceOffset;
|
||||
// Reverse screenspace scale
|
||||
oPos /= xboxScreenspaceScale;
|
||||
|
||||
// Ensure w is nonzero
|
||||
if(oPos.w == 0) oPos.w = 1;
|
||||
// Reverse perspective divide
|
||||
oPos.xyz *= oPos.w;
|
||||
return oPos;
|
||||
}
|
||||
|
||||
VS_OUTPUT main(const VS_INPUT xIn)
|
||||
{
|
||||
// Input registers
|
||||
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
|
||||
|
||||
// Unpack 16 flags from 4 float4 constant registers
|
||||
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
|
||||
|
||||
// Initialize input registers from the vertex buffer data
|
||||
// Or use the register's default value (which can be changed by the title)
|
||||
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
|
||||
// Note : unroll manually instead of for-loop, because of the ## concatenation
|
||||
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
|
||||
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
|
||||
init_v( 8); init_v( 9); init_v(10); init_v(11);
|
||||
init_v(12); init_v(13); init_v(14); init_v(15);
|
||||
|
||||
// For passthrough, map output variables to their corresponding input registers
|
||||
float4 oPos = v0;
|
||||
float4 oD0 = v3;
|
||||
float4 oD1 = v4;
|
||||
float4 oFog = v5;
|
||||
float4 oPts = v6;
|
||||
float4 oB0 = v7;
|
||||
float4 oB1 = v8;
|
||||
float4 oT0 = v9;
|
||||
float4 oT1 = v10;
|
||||
float4 oT2 = v11;
|
||||
float4 oT3 = v12;
|
||||
|
||||
// Copy variables to output struct
|
||||
VS_OUTPUT xOut;
|
||||
|
||||
// Fogging
|
||||
// TODO deduplicate
|
||||
const float fogDepth = abs(oFog.x);
|
||||
const float fogTableMode = CxbxFogInfo.x;
|
||||
const float fogDensity = CxbxFogInfo.y;
|
||||
const float fogStart = CxbxFogInfo.z;
|
||||
const float fogEnd = CxbxFogInfo.w;
|
||||
|
||||
const float FOG_TABLE_NONE = 0;
|
||||
const float FOG_TABLE_EXP = 1;
|
||||
const float FOG_TABLE_EXP2 = 2;
|
||||
const float FOG_TABLE_LINEAR = 3;
|
||||
|
||||
float fogFactor;
|
||||
if(fogTableMode == FOG_TABLE_NONE)
|
||||
fogFactor = fogDepth;
|
||||
if(fogTableMode == FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(fogDepth * fogDensity); /* / 1 / e^(d * density)*/
|
||||
if(fogTableMode == FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); /* / 1 / e^((d * density)^2)*/
|
||||
if(fogTableMode == FOG_TABLE_LINEAR)
|
||||
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
|
||||
|
||||
xOut.oPos = reverseScreenspaceTransform(oPos);
|
||||
xOut.oD0 = saturate(oD0);
|
||||
xOut.oD1 = saturate(oD1);
|
||||
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader
|
||||
xOut.oPts = oPts.x;
|
||||
xOut.oB0 = saturate(oB0);
|
||||
xOut.oB1 = saturate(oB1);
|
||||
// Scale textures (TODO : or should we apply this to the input register values?)
|
||||
xOut.oT0 = oT0 / xboxTextureScale[0];
|
||||
xOut.oT1 = oT1 / xboxTextureScale[1];
|
||||
xOut.oT2 = oT2 / xboxTextureScale[2];
|
||||
xOut.oT3 = oT3 / xboxTextureScale[3];
|
||||
|
||||
return xOut;
|
||||
}
|
||||
)";
|
||||
|
||||
EmuCompileShader(hlsl, g_vs_model, &pPassthroughShader, "passthrough.hlsl");
|
||||
}
|
||||
|
||||
*ppHostShader = pPassthroughShader;
|
||||
|
||||
return 0;
|
||||
EmuCompileShader(g_ShaderSources.vertexShaderPassthroughHlsl, g_vs_model, ppHostShader, g_ShaderSources.vertexShaderPassthroughPath.c_str());
|
||||
}
|
||||
|
|
|
@ -21,5 +21,5 @@ extern HRESULT EmuCompileVertexShader
|
|||
|
||||
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader);
|
||||
|
||||
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
|
||||
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
#define LOG_PREFIX CXBXR_MODULE::VSHCACHE
|
||||
|
||||
#include "VertexShaderSource.h"
|
||||
#include "VertexShaderCache.h"
|
||||
|
||||
#include "core/kernel/init/CxbxKrnl.h"
|
||||
#include "util/hasher.h"
|
||||
#include "core/kernel/support/Emu.h"
|
||||
|
||||
VertexShaderSource g_VertexShaderSource = VertexShaderSource();
|
||||
VertexShaderCache g_VertexShaderCache = VertexShaderCache();
|
||||
// FIXME : This should really be released and created in step with the D3D device lifecycle rather than being a thing on its own
|
||||
// (And the ResetD3DDevice method should be removed)
|
||||
|
||||
|
||||
ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, ShaderKey key) {
|
||||
ID3DBlob* pCompiledShader;
|
||||
|
||||
|
@ -25,7 +26,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
|
|||
|
||||
// Find a shader
|
||||
// Return true if the shader was found
|
||||
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
|
||||
bool VertexShaderCache::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
|
||||
auto it = cache.find(key);
|
||||
if (it == cache.end()) {
|
||||
// We didn't find anything! Was CreateShader called?
|
||||
|
@ -39,7 +40,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
|
|||
|
||||
// Create a new shader
|
||||
// If the shader was already created, just increase its reference count
|
||||
ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
|
||||
ShaderKey VertexShaderCache::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
|
||||
IntermediateVertexShader intermediateShader;
|
||||
|
||||
*pXboxFunctionSize = GetVshFunctionSize(pXboxFunction);
|
||||
|
@ -86,7 +87,7 @@ ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction,
|
|||
}
|
||||
|
||||
// Get a shader using the given key
|
||||
IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
|
||||
IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
|
||||
{
|
||||
LazyVertexShader* pLazyShader = nullptr;
|
||||
|
||||
|
@ -113,6 +114,12 @@ IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
|
|||
EmuLog(LOG_LEVEL::DEBUG, "Waiting for shader %llx...", key);
|
||||
pCompiledShader = pLazyShader->compileResult.get();
|
||||
|
||||
if (!pCompiledShader) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to compile vertex shader for %llx", key);
|
||||
pLazyShader->isReady = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the shader
|
||||
auto hRet = pD3DDevice->CreateVertexShader
|
||||
(
|
||||
|
@ -145,7 +152,7 @@ IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
|
|||
}
|
||||
|
||||
// Release a shader. Doesn't actually release any resources for now
|
||||
void VertexShaderSource::ReleaseShader(ShaderKey key)
|
||||
void VertexShaderCache::ReleaseShader(ShaderKey key)
|
||||
{
|
||||
// For now, don't bother releasing any shaders
|
||||
LazyVertexShader* pLazyShader;
|
||||
|
@ -165,8 +172,25 @@ void VertexShaderSource::ReleaseShader(ShaderKey key)
|
|||
}
|
||||
}
|
||||
|
||||
void VertexShaderSource::ResetD3DDevice(IDirect3DDevice9* newDevice)
|
||||
void VertexShaderCache::ResetD3DDevice(IDirect3DDevice9* newDevice)
|
||||
{
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Resetting D3D device");
|
||||
cache.clear();
|
||||
this->pD3DDevice = newDevice;
|
||||
}
|
||||
|
||||
void VertexShaderCache::Clear()
|
||||
{
|
||||
for (auto& x : cache) {
|
||||
if (!x.second.isReady) {
|
||||
auto pBlob = x.second.compileResult.get();
|
||||
if (pBlob) {
|
||||
pBlob->Release();
|
||||
}
|
||||
}
|
||||
else if(x.second.pHostVertexShader) {
|
||||
x.second.pHostVertexShader->Release();
|
||||
}
|
||||
}
|
||||
cache.clear();
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
typedef uint64_t ShaderKey;
|
||||
|
||||
// Manages creation and caching of vertex shaders
|
||||
class VertexShaderSource {
|
||||
class VertexShaderCache {
|
||||
|
||||
public:
|
||||
ShaderKey CreateShader(const xbox::dword_xt* pXboxFunction, DWORD* pXboxFunctionSize);
|
||||
|
@ -16,6 +16,7 @@ public:
|
|||
void ReleaseShader(ShaderKey key);
|
||||
|
||||
void ResetD3DDevice(IDirect3DDevice9* pD3DDevice);
|
||||
void Clear();
|
||||
|
||||
// TODO
|
||||
// WriteCacheToDisk
|
||||
|
@ -39,9 +40,9 @@ private:
|
|||
std::mutex cacheMutex;
|
||||
std::map<ShaderKey, LazyVertexShader> cache;
|
||||
|
||||
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
|
||||
bool _FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
|
||||
};
|
||||
|
||||
extern VertexShaderSource g_VertexShaderSource;
|
||||
extern VertexShaderCache g_VertexShaderCache;
|
||||
|
||||
#endif
|
|
@ -144,6 +144,22 @@ D3DMATRIX* D3D8TransformState::GetWorldView(unsigned i)
|
|||
return &WorldView[i];
|
||||
}
|
||||
|
||||
void D3D8TransformState::SetWorldView(unsigned i, const D3DMATRIX* pMatrix)
|
||||
{
|
||||
assert(i < 4);
|
||||
|
||||
if (!pMatrix) {
|
||||
// null indicates the title is done with setting
|
||||
// the worldview matrix explicitly
|
||||
bWorldViewDirty[i] = true;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
bWorldViewDirty[i] = false;
|
||||
WorldView[i] = *pMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
D3DMATRIX* D3D8TransformState::GetWorldViewInverseTranspose(unsigned i)
|
||||
{
|
||||
assert(i < 4);
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
D3D8TransformState();
|
||||
void SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix);
|
||||
D3DMATRIX* GetWorldView(unsigned i);
|
||||
void SetWorldView(unsigned i, const D3DMATRIX* pMatrix);
|
||||
D3DMATRIX* GetWorldViewInverseTranspose(unsigned i);
|
||||
|
||||
// The transforms set by the Xbox title
|
||||
|
|
|
@ -861,7 +861,7 @@ typedef struct _FormatInfo {
|
|||
_ComponentEncoding components;
|
||||
D3DFORMAT pc;
|
||||
_FormatUsage usage;
|
||||
char *warning;
|
||||
const char *warning;
|
||||
} FormatInfo;
|
||||
|
||||
static const FormatInfo FormatInfos[] = {
|
||||
|
@ -1047,7 +1047,7 @@ D3DFORMAT EmuXB2PC_D3DFormat(xbox::X_D3DFORMAT Format)
|
|||
case ((xbox::X_D3DFORMAT)0xffffffff):
|
||||
return D3DFMT_UNKNOWN; // TODO -oCXBX: Not sure if this counts as swizzled or not...
|
||||
default:
|
||||
CxbxrKrnlAbort("EmuXB2PC_D3DFormat: Unknown Format (0x%.08X)", Format);
|
||||
CxbxrAbort("EmuXB2PC_D3DFormat: Unknown Format (0x%.08X)", Format);
|
||||
}
|
||||
|
||||
return D3DFMT_UNKNOWN;
|
||||
|
@ -1056,8 +1056,8 @@ D3DFORMAT EmuXB2PC_D3DFormat(xbox::X_D3DFORMAT Format)
|
|||
xbox::X_D3DFORMAT EmuPC2XB_D3DFormat(D3DFORMAT Format, bool bPreferLinear)
|
||||
{
|
||||
xbox::X_D3DFORMAT result;
|
||||
switch(Format)
|
||||
{
|
||||
switch(Format)
|
||||
{
|
||||
case D3DFMT_YUY2:
|
||||
result = xbox::X_D3DFMT_YUY2;
|
||||
break;
|
||||
|
@ -1129,10 +1129,10 @@ xbox::X_D3DFORMAT EmuPC2XB_D3DFormat(D3DFORMAT Format, bool bPreferLinear)
|
|||
result = xbox::X_D3DFMT_VERTEXDATA;
|
||||
break;
|
||||
default:
|
||||
CxbxrKrnlAbort("EmuPC2XB_D3DFormat: Unknown Format (%d)", Format);
|
||||
}
|
||||
CxbxrAbort("EmuPC2XB_D3DFormat: Unknown Format (%d)", Format);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD EmuXB2PC_D3DLock(DWORD Flags)
|
||||
|
@ -1591,6 +1591,220 @@ Direct3D9 states unused :
|
|||
D3DRS_BLENDOPALPHA = 209 // Blending operation for the alpha channel when D3DRS_SEPARATEDESTALPHAENABLE is TRUE
|
||||
*/
|
||||
|
||||
xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::dword_xt XboxPixelContainer_Format)
|
||||
{
|
||||
xbox::X_D3DFORMAT d3d_format = (xbox::X_D3DFORMAT)((XboxPixelContainer_Format & X_D3DFORMAT_FORMAT_MASK) >> X_D3DFORMAT_FORMAT_SHIFT);
|
||||
return d3d_format;
|
||||
}
|
||||
|
||||
xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::X_D3DPixelContainer* pXboxPixelContainer)
|
||||
{
|
||||
// Don't pass in unassigned Xbox pixel container
|
||||
assert(pXboxPixelContainer != xbox::zeroptr);
|
||||
|
||||
return GetXboxPixelContainerFormat(pXboxPixelContainer->Format);
|
||||
}
|
||||
|
||||
void CxbxGetPixelContainerMeasures
|
||||
(
|
||||
xbox::X_D3DPixelContainer* pPixelContainer,
|
||||
// TODO : Add X_D3DCUBEMAP_FACES argument
|
||||
DWORD dwMipMapLevel, // unused - TODO : Use
|
||||
UINT* pWidth,
|
||||
UINT* pHeight,
|
||||
UINT* pDepth,
|
||||
UINT* pRowPitch,
|
||||
// Slice pitch (does not include mipmaps!)
|
||||
UINT* pSlicePitch
|
||||
)
|
||||
{
|
||||
DWORD Size = pPixelContainer->Size;
|
||||
xbox::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer);
|
||||
|
||||
if (Size != 0)
|
||||
{
|
||||
*pDepth = 1;
|
||||
*pWidth = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1;
|
||||
*pHeight = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1;
|
||||
*pRowPitch = (((Size & X_D3DSIZE_PITCH_MASK) >> X_D3DSIZE_PITCH_SHIFT) + 1) * X_D3DTEXTURE_PITCH_ALIGNMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT;
|
||||
DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT;
|
||||
DWORD l2d = (pPixelContainer->Format & X_D3DFORMAT_PSIZE_MASK) >> X_D3DFORMAT_PSIZE_SHIFT;
|
||||
DWORD dwBPP = EmuXBFormatBitsPerPixel(X_Format);
|
||||
|
||||
*pDepth = 1 << l2d;
|
||||
*pHeight = 1 << l2h;
|
||||
*pWidth = 1 << l2w;
|
||||
*pRowPitch = (*pWidth) * dwBPP / 8;
|
||||
}
|
||||
|
||||
*pSlicePitch = (*pRowPitch) * (*pHeight);
|
||||
|
||||
if (EmuXBFormatIsCompressed(X_Format)) {
|
||||
*pRowPitch *= 4;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConvertD3DTextureToARGBBuffer(
|
||||
xbox::X_D3DFORMAT X_Format,
|
||||
uint8_t* pSrc,
|
||||
int SrcWidth, int SrcHeight, int SrcRowPitch, int SrcSlicePitch,
|
||||
uint8_t* pDst, int DstRowPitch, int DstSlicePitch,
|
||||
unsigned int uiDepth ,
|
||||
xbox::PVOID pPalleteData
|
||||
)
|
||||
{
|
||||
const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format);
|
||||
if (ConvertRowToARGB == nullptr)
|
||||
return false; // Unhandled conversion
|
||||
|
||||
uint8_t* unswizleBuffer = nullptr;
|
||||
if (EmuXBFormatIsSwizzled(X_Format)) {
|
||||
unswizleBuffer = (uint8_t*)malloc(SrcSlicePitch * uiDepth); // TODO : Reuse buffer when performance is important
|
||||
// First we need to unswizzle the texture data
|
||||
EmuUnswizzleBox(
|
||||
pSrc, SrcWidth, SrcHeight, uiDepth,
|
||||
EmuXBFormatBytesPerPixel(X_Format),
|
||||
// Note : use src pitch on dest, because this is an intermediate step :
|
||||
unswizleBuffer, SrcRowPitch, SrcSlicePitch
|
||||
);
|
||||
// Convert colors from the unswizzled buffer
|
||||
pSrc = unswizleBuffer;
|
||||
}
|
||||
|
||||
int AdditionalArgument;
|
||||
if (X_Format == xbox::X_D3DFMT_P8)
|
||||
AdditionalArgument = (int)pPalleteData;
|
||||
else
|
||||
AdditionalArgument = DstRowPitch;
|
||||
|
||||
if (EmuXBFormatIsCompressed(X_Format)) {
|
||||
if (SrcWidth < 4 || SrcHeight < 4) {
|
||||
// HACK: The compressed DXT conversion code currently writes more pixels than it should, which can cause a crash.
|
||||
// This code will get hit when converting compressed texture mipmaps on hardware that somehow doesn't support DXT natively
|
||||
// (or lied when Cxbx asked it if it does!)
|
||||
EmuLog(LOG_LEVEL::WARNING, "Converting DXT textures smaller than a block is not currently implemented. Ignoring conversion!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// All compressed formats (DXT1, DXT3 and DXT5) encode blocks of 4 pixels on 4 lines
|
||||
SrcHeight = (SrcHeight + 3) / 4;
|
||||
DstRowPitch *= 4;
|
||||
}
|
||||
|
||||
uint8_t* pSrcSlice = pSrc;
|
||||
uint8_t* pDstSlice = pDst;
|
||||
for (unsigned int z = 0; z < uiDepth; z++) {
|
||||
uint8_t* pSrcRow = pSrcSlice;
|
||||
uint8_t* pDstRow = pDstSlice;
|
||||
for (int y = 0; y < SrcHeight; y++) {
|
||||
*(int*)pDstRow = AdditionalArgument; // Dirty hack, to avoid an extra parameter to all conversion callbacks
|
||||
ConvertRowToARGB(pSrcRow, pDstRow, SrcWidth);
|
||||
pSrcRow += SrcRowPitch;
|
||||
pDstRow += DstRowPitch;
|
||||
}
|
||||
|
||||
pSrcSlice += SrcSlicePitch;
|
||||
pDstSlice += DstSlicePitch;
|
||||
}
|
||||
|
||||
if (unswizleBuffer)
|
||||
free(unswizleBuffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called by WndMain::LoadGameLogo() to load game logo bitmap
|
||||
uint8_t* ConvertD3DTextureToARGB(
|
||||
xbox::X_D3DPixelContainer* pXboxPixelContainer,
|
||||
uint8_t* pSrc,
|
||||
int* pWidth, int* pHeight,
|
||||
xbox::PVOID pPalleteData // default = zeroptr
|
||||
)
|
||||
{
|
||||
// Avoid allocating pDest when ConvertD3DTextureToARGBBuffer will fail anyway
|
||||
xbox::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pXboxPixelContainer);
|
||||
const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format);
|
||||
if (ConvertRowToARGB == nullptr)
|
||||
return nullptr; // Unhandled conversion
|
||||
|
||||
unsigned int SrcDepth, SrcRowPitch, SrcSlicePitch;
|
||||
CxbxGetPixelContainerMeasures(
|
||||
pXboxPixelContainer,
|
||||
0, // dwMipMapLevel
|
||||
(UINT*)pWidth,
|
||||
(UINT*)pHeight,
|
||||
&SrcDepth,
|
||||
&SrcRowPitch,
|
||||
&SrcSlicePitch
|
||||
);
|
||||
|
||||
// Now we know ConvertD3DTextureToARGBBuffer will do it's thing, allocate the resulting buffer
|
||||
int DstDepth = 1; // for now TODO : Use SrcDepth when supporting volume textures
|
||||
int DstRowPitch = (*pWidth) * sizeof(DWORD); // = sizeof ARGB pixel. TODO : Is this correct?
|
||||
int DstSlicePitch = DstRowPitch * (*pHeight); // TODO : Is this correct?
|
||||
int DstSize = DstSlicePitch * DstDepth;
|
||||
uint8_t* pDst = (uint8_t*)malloc(DstSize);
|
||||
|
||||
// And convert the source towards that buffer
|
||||
/*ignore result*/ConvertD3DTextureToARGBBuffer(
|
||||
X_Format,
|
||||
pSrc, *pWidth, *pHeight, SrcRowPitch, SrcSlicePitch,
|
||||
pDst, DstRowPitch, DstSlicePitch,
|
||||
DstDepth,
|
||||
pPalleteData);
|
||||
|
||||
// NOTE : Caller must take ownership!
|
||||
return pDst;
|
||||
}
|
||||
|
||||
void CxbxSetPixelContainerHeader
|
||||
(
|
||||
xbox::X_D3DPixelContainer* pPixelContainer,
|
||||
DWORD Common,
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
UINT Levels,
|
||||
xbox::X_D3DFORMAT Format,
|
||||
UINT Dimensions,
|
||||
UINT Pitch
|
||||
)
|
||||
{
|
||||
// Set X_D3DResource field(s) :
|
||||
pPixelContainer->Common = Common;
|
||||
// DON'T SET pPixelContainer->Data
|
||||
// DON'T SET pPixelContainer->Lock
|
||||
|
||||
// Are Width and Height both a power of two?
|
||||
DWORD l2w; _BitScanReverse(&l2w, Width); // MSVC intrinsic; GCC has __builtin_clz
|
||||
DWORD l2h; _BitScanReverse(&l2h, Height);
|
||||
DWORD l2d = 0; // TODO : Set this via input argument
|
||||
if (((1 << l2w) == Width) && ((1 << l2h) == Height)) {
|
||||
Width = Height = Pitch = 1; // When setting Format, clear Size field
|
||||
}
|
||||
else {
|
||||
l2w = l2h = l2d = 0; // When setting Size, clear D3DFORMAT_USIZE, VSIZE and PSIZE
|
||||
}
|
||||
|
||||
// Set X_D3DPixelContainer field(s) :
|
||||
pPixelContainer->Format = 0
|
||||
| ((Dimensions << X_D3DFORMAT_DIMENSION_SHIFT) & X_D3DFORMAT_DIMENSION_MASK)
|
||||
| (((DWORD)Format << X_D3DFORMAT_FORMAT_SHIFT) & X_D3DFORMAT_FORMAT_MASK)
|
||||
| ((Levels << X_D3DFORMAT_MIPMAP_SHIFT) & X_D3DFORMAT_MIPMAP_MASK)
|
||||
| ((l2w << X_D3DFORMAT_USIZE_SHIFT) & X_D3DFORMAT_USIZE_MASK)
|
||||
| ((l2h << X_D3DFORMAT_VSIZE_SHIFT) & X_D3DFORMAT_VSIZE_MASK)
|
||||
| ((l2d << X_D3DFORMAT_PSIZE_SHIFT) & X_D3DFORMAT_PSIZE_MASK)
|
||||
;
|
||||
pPixelContainer->Size = 0
|
||||
| (((Width - 1) /*X_D3DSIZE_WIDTH_SHIFT*/) & X_D3DSIZE_WIDTH_MASK)
|
||||
| (((Height - 1) << X_D3DSIZE_HEIGHT_SHIFT) & X_D3DSIZE_HEIGHT_MASK)
|
||||
| (((Pitch - 1) << X_D3DSIZE_PITCH_SHIFT) & X_D3DSIZE_PITCH_MASK)
|
||||
;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Generic swizzle function, usable for both x and y dimensions.
|
||||
When passing x, Max should be 2*height, and Shift should be 0
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#ifndef XBCONVERT_H
|
||||
#define XBCONVERT_H
|
||||
|
||||
#include "common/cxbxr.hpp"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
|
||||
#include "core\hle\D3D8\XbD3D8Types.h"
|
||||
|
@ -90,7 +91,7 @@ else if((uint32)State < 20)
|
|||
else if((uint32)State > 255)
|
||||
State = (D3DTRANSFORMSTATETYPE)(State - 250);
|
||||
else
|
||||
CxbxrKrnlAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
|
||||
CxbxrAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
|
||||
//*/
|
||||
|
||||
// convert from xbox to pc texture transform state types
|
||||
|
@ -115,7 +116,7 @@ inline D3DTRANSFORMSTATETYPE EmuXB2PC_D3DTS(xbox::X_D3DTRANSFORMSTATETYPE State)
|
|||
return D3DTS_WORLDMATRIX(State - 256);
|
||||
}
|
||||
|
||||
CxbxrKrnlAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
|
||||
CxbxrAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
|
||||
return (D3DTRANSFORMSTATETYPE)0;
|
||||
}
|
||||
|
||||
|
@ -1805,12 +1806,12 @@ typedef enum _TXBType {
|
|||
} TXBType;
|
||||
|
||||
typedef struct _RenderStateInfo {
|
||||
char *S; // String representation.
|
||||
const char *S; // String representation.
|
||||
WORD V; // The XDK version since which a render state was introduced (using the 5911 declarations as a base).
|
||||
TXBType T = xt_Unknown; // The Xbox data type. Defaults to xt_Unknown.
|
||||
xbox::NV2AMETHOD M; // The related push buffer method. Not always a 1-to-1 mapping. Needs push-buffer interpretation & conversion code.
|
||||
D3DRENDERSTATETYPE PC = (D3DRENDERSTATETYPE)0; // Map XBox to PC render state
|
||||
char *N; // XDK notes. Defaults to ''.
|
||||
const char *N; // XDK notes. Defaults to ''.
|
||||
WORD R; // The XDK version since which a render state was removed
|
||||
}
|
||||
RenderStateInfo;
|
||||
|
@ -1819,5 +1820,36 @@ RenderStateInfo;
|
|||
|
||||
extern const RenderStateInfo& GetDxbxRenderStateInfo(int State);
|
||||
|
||||
extern xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::dword_xt XboxPixelContainer_Format);
|
||||
|
||||
extern xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::X_D3DPixelContainer* pXboxPixelContainer);
|
||||
|
||||
extern bool ConvertD3DTextureToARGBBuffer(
|
||||
xbox::X_D3DFORMAT X_Format,
|
||||
uint8_t* pSrc,
|
||||
int SrcWidth, int SrcHeight, int SrcRowPitch, int SrcSlicePitch,
|
||||
uint8_t* pDst, int DstRowPitch, int DstSlicePitch,
|
||||
unsigned int uiDepth = 1,
|
||||
xbox::PVOID pPalleteData = xbox::zeroptr
|
||||
);
|
||||
|
||||
extern void CxbxSetPixelContainerHeader
|
||||
(
|
||||
xbox::X_D3DPixelContainer* pPixelContainer,
|
||||
DWORD Common,
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
UINT Levels,
|
||||
xbox::X_D3DFORMAT Format,
|
||||
UINT Dimensions,
|
||||
UINT Pitch
|
||||
);
|
||||
|
||||
extern uint8_t* ConvertD3DTextureToARGB(
|
||||
xbox::X_D3DPixelContainer* pXboxPixelContainer,
|
||||
uint8_t* pSrc,
|
||||
int* pWidth, int* pHeight,
|
||||
xbox::PVOID pPalleteData = xbox::zeroptr
|
||||
);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -39,17 +39,20 @@
|
|||
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice, g_pXbox_PixelShader
|
||||
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
|
||||
#include "core\hle\D3D8\XbPixelShader.h"
|
||||
#include "core\hle\D3D8\Direct3D9\PixelShader.h" // EmuCompilePixelShader
|
||||
#include "core\hle\D3D8\XbD3D8Logging.h" // For D3DErrorString()
|
||||
|
||||
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort()
|
||||
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrAbort()
|
||||
#include "util\hasher.h"
|
||||
#include "core\hle\D3D8\Direct3D9\FixedFunctionPixelShader.hlsli"
|
||||
#include "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
|
||||
|
||||
#include <assert.h> // assert()
|
||||
#include <process.h>
|
||||
#include <locale.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -489,62 +492,79 @@ typedef struct s_CxbxPSDef {
|
|||
void AdjustTextureModes(DecodedRegisterCombiner &RC)
|
||||
{
|
||||
// if this flag is set, the texture mode for each texture stage is adjusted as follows:
|
||||
if (!RC.TexModeAdjust) return;
|
||||
if (RC.TexModeAdjust) {
|
||||
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
|
||||
// First, disable not-assigned textures
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_NONE) {
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
|
||||
// First, disable not-assigned textures
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_NONE) {
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Then adjust some texture mode according to the currently active textures, so that the shader will use the appropriate sampling method
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT2D:
|
||||
case PS_TEXTUREMODES_PROJECT3D:
|
||||
case PS_TEXTUREMODES_CUBEMAP:
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_CUBETEXTURE)
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP;
|
||||
else
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_VOLUMETEXTURE)
|
||||
// TODO : Also do this for DepthBuffers (but not EmuXBFormatIsLinear!) :
|
||||
// || EmuXBFormatIsDepthBuffer(GetXboxPixelContainerFormat(g_pXbox_SetTexture[i])) in { X_D3DFMT_D24S8, X_D3DFMT_F24S8, X_D3DFMT_D16, X_D3DFMT_F16}
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT3D;
|
||||
// Then adjust some texture mode according to the currently active textures, so that the shader will use the appropriate sampling method
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT2D:
|
||||
case PS_TEXTUREMODES_PROJECT3D:
|
||||
case PS_TEXTUREMODES_CUBEMAP:
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_CUBETEXTURE)
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP;
|
||||
else
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT2D;
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_STR_3D:
|
||||
case PS_TEXTUREMODES_DOT_STR_CUBE:
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_CUBETEXTURE)
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_VOLUMETEXTURE)
|
||||
// TODO : Also do this for DepthBuffers (but not EmuXBFormatIsLinear!) :
|
||||
// || EmuXBFormatIsDepthBuffer(GetXboxPixelContainerFormat(g_pXbox_SetTexture[i])) in { X_D3DFMT_D24S8, X_D3DFMT_F24S8, X_D3DFMT_D16, X_D3DFMT_F16}
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT3D;
|
||||
else
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT2D;
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_STR_3D:
|
||||
case PS_TEXTUREMODES_DOT_STR_CUBE:
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_CUBETEXTURE)
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_CUBE;
|
||||
else
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_3D;
|
||||
break;
|
||||
}
|
||||
/* Was :
|
||||
switch (ActiveTextureTypes[i]) {
|
||||
case xbox::X_D3DRTYPE_CUBETEXTURE:
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT2D: RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP; break;
|
||||
case PS_TEXTUREMODES_PROJECT3D: RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP; break;
|
||||
case PS_TEXTUREMODES_DOT_STR_3D: RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_CUBE; break;
|
||||
} break;
|
||||
case xbox::X_D3DRTYPE_VOLUMETEXTURE:
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT2D: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT3D; break;
|
||||
case PS_TEXTUREMODES_CUBEMAP: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT3D; break;
|
||||
case PS_TEXTUREMODES_DOT_STR_CUBE: RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_3D; break;
|
||||
} break;
|
||||
case xbox::X_D3DRTYPE_TEXTURE:
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT3D: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT2D; break;
|
||||
case PS_TEXTUREMODES_CUBEMAP: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT2D; break;
|
||||
} break;
|
||||
case xbox::X_D3DRTYPE_NONE:
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_NONE;
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Texture modes were specified manually - but fix them up if necessary
|
||||
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
|
||||
// Fixup sampling cube textures with PROJECT2D
|
||||
// Test case: Splinter Cell Chaos Theory (lighting)
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_CUBETEXTURE && RC.PSTextureModes[i] == PS_TEXTUREMODES_PROJECT2D) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "PROJECT2D sampling is used with a cubemap texture - using CUBEMAP sampling instead");
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP;
|
||||
}
|
||||
|
||||
// Test-case: MS-033 Crimson Skies (Plane texturing in-game and selection menu)
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_CUBETEXTURE && RC.PSTextureModes[i] == PS_TEXTUREMODES_DOT_STR_3D) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "DOT_STR_3D sampling is used with a cubemap texture - using DOT_STR_CUBE sampling instead");
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_CUBE;
|
||||
else
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_3D;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Was :
|
||||
switch (ActiveTextureTypes[i]) {
|
||||
case xbox::X_D3DRTYPE_CUBETEXTURE:
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT2D: RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP; break;
|
||||
case PS_TEXTUREMODES_PROJECT3D: RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP; break;
|
||||
case PS_TEXTUREMODES_DOT_STR_3D: RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_CUBE; break;
|
||||
} break;
|
||||
case xbox::X_D3DRTYPE_VOLUMETEXTURE:
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT2D: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT3D; break;
|
||||
case PS_TEXTUREMODES_CUBEMAP: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT3D; break;
|
||||
case PS_TEXTUREMODES_DOT_STR_CUBE: RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_3D; break;
|
||||
} break;
|
||||
case xbox::X_D3DRTYPE_TEXTURE:
|
||||
switch (RC.PSTextureModes[i]) {
|
||||
case PS_TEXTUREMODES_PROJECT3D: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT2D; break;
|
||||
case PS_TEXTUREMODES_CUBEMAP: RC.PSTextureModes[i] = PS_TEXTUREMODES_PROJECT2D; break;
|
||||
} break;
|
||||
case xbox::X_D3DRTYPE_NONE:
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_NONE;
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,35 +661,12 @@ constexpr int PSH_XBOX_CONSTANT_BEM = PSH_XBOX_CONSTANT_FOG + 1; // = 19..22
|
|||
constexpr int PSH_XBOX_CONSTANT_LUM = PSH_XBOX_CONSTANT_BEM + 4; // = 23..26
|
||||
// Which winding order to consider as the front face
|
||||
constexpr int PSH_XBOX_CONSTANT_FRONTFACE_FACTOR = PSH_XBOX_CONSTANT_LUM + 4; // = 27
|
||||
// Fog table information {Table mode, density, start and end}
|
||||
constexpr int CXBX_D3DPS_CONSTREG_FOGINFO = PSH_XBOX_CONSTANT_FRONTFACE_FACTOR + 1; // = 28
|
||||
//Fog enable flag
|
||||
constexpr int PSH_XBOX_CONSTANT_FOGENABLE = CXBX_D3DPS_CONSTREG_FOGINFO + 1; // = 29
|
||||
// This concludes the set of constants that need to be set on host :
|
||||
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FRONTFACE_FACTOR + 1; // = 28
|
||||
|
||||
std::string GetFixedFunctionShaderTemplate() {
|
||||
static bool loaded = false;
|
||||
static std::string hlslString;
|
||||
|
||||
// TODO does this need to be thread safe?
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
|
||||
// Determine the filename and directory for the fixed function shader
|
||||
// TODO make this a relative path so we guarantee an LPCSTR for D3DCompile
|
||||
auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
|
||||
.parent_path()
|
||||
.append("hlsl");
|
||||
|
||||
auto sourceFile = hlslDir.append("FixedFunctionPixelShader.hlsl").string();
|
||||
|
||||
// Load the shader into a string
|
||||
std::ifstream hlslStream(sourceFile);
|
||||
std::stringstream hlsl;
|
||||
hlsl << hlslStream.rdbuf();
|
||||
|
||||
hlslString = hlsl.str();
|
||||
}
|
||||
|
||||
return hlslString;
|
||||
}
|
||||
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FOGENABLE + 1; // = 30
|
||||
|
||||
std::string_view GetD3DTOPString(int d3dtop) {
|
||||
static constexpr std::string_view opToString[] = {
|
||||
|
@ -771,6 +768,21 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
|
|||
// TODO move this cache elsewhere - and flush it when the device is released!
|
||||
static std::unordered_map<uint64_t, IDirect3DPixelShader9*> ffPsCache = {};
|
||||
|
||||
// Support hotloading hlsl
|
||||
static int pixelShaderVersion = -1;
|
||||
int shaderVersion = g_ShaderSources.Update();
|
||||
if (pixelShaderVersion != shaderVersion) {
|
||||
pixelShaderVersion = shaderVersion;
|
||||
g_pD3DDevice->SetPixelShader(nullptr);
|
||||
|
||||
for (auto& hostShader : ffPsCache) {
|
||||
if (hostShader.second)
|
||||
hostShader.second->Release();
|
||||
}
|
||||
|
||||
ffPsCache.clear();
|
||||
}
|
||||
|
||||
// Create a key from state that will be baked in to the shader
|
||||
PsTextureHardcodedState states[4] = {};
|
||||
int sampleType[4] = { SAMPLE_NONE, SAMPLE_NONE, SAMPLE_NONE, SAMPLE_NONE };
|
||||
|
@ -853,68 +865,74 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
|
|||
}
|
||||
|
||||
// Build and compile a new shader
|
||||
auto hlslTemplate = GetFixedFunctionShaderTemplate();
|
||||
std::string hlslTemplate = g_ShaderSources.fixedFunctionPixelShaderHlsl;
|
||||
|
||||
// In D3D9 it seems we need to know hardcode if we're doing a 2D or 3D lookup
|
||||
const std::string sampleTypePattern = "TEXTURE_SAMPLE_TYPE;";
|
||||
auto sampleTypeReplace = hlslTemplate.find(sampleTypePattern);
|
||||
std::string finalShader = hlslTemplate;
|
||||
|
||||
static constexpr std::string_view typeToString[] = {
|
||||
"SAMPLE_NONE",
|
||||
"SAMPLE_2D",
|
||||
"SAMPLE_3D",
|
||||
"SAMPLE_CUBE"
|
||||
};
|
||||
if (sampleTypeReplace != std::string::npos) {
|
||||
static constexpr std::string_view typeToString[] = {
|
||||
"SAMPLE_NONE",
|
||||
"SAMPLE_2D",
|
||||
"SAMPLE_3D",
|
||||
"SAMPLE_CUBE"
|
||||
};
|
||||
|
||||
std::stringstream sampleTypeString;
|
||||
sampleTypeString << "{"
|
||||
<< typeToString[sampleType[0]] << ", "
|
||||
<< typeToString[sampleType[1]] << ", "
|
||||
<< typeToString[sampleType[2]] << ", "
|
||||
<< typeToString[sampleType[3]] << "};";
|
||||
std::stringstream sampleTypeString;
|
||||
sampleTypeString << "{"
|
||||
<< typeToString[sampleType[0]] << ", "
|
||||
<< typeToString[sampleType[1]] << ", "
|
||||
<< typeToString[sampleType[2]] << ", "
|
||||
<< typeToString[sampleType[3]] << "};";
|
||||
|
||||
auto finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
|
||||
finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
|
||||
}
|
||||
|
||||
// Hardcode the texture stage operations and arguments
|
||||
// So the shader handles exactly one combination of values
|
||||
const std::string stageDef = "// STAGE DEFINITIONS";
|
||||
auto stageDefInsert = finalShader.find(stageDef) + stageDef.size();
|
||||
auto stageDefInsert = finalShader.find(stageDef);
|
||||
if (stageDefInsert != std::string::npos) {
|
||||
stageDefInsert += stageDef.size();
|
||||
|
||||
std::stringstream stageSetup;
|
||||
stageSetup << '\n';
|
||||
std::stringstream stageSetup;
|
||||
stageSetup << '\n';
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
#ifdef ENABLE_FF_ALPHAKILL
|
||||
// Even when a stage is disabled, we still have to fully initialize it's values, to prevent
|
||||
// "error X4000: variable 'stages' used without having been completely initialized"
|
||||
// Even when a stage is disabled, we still have to fully initialize it's values, to prevent
|
||||
// "error X4000: variable 'stages' used without having been completely initialized"
|
||||
#else
|
||||
// The stage is initialized to be disabled
|
||||
// We don't have to output anything
|
||||
if (states[i].COLOROP == X_D3DTOP_DISABLE)
|
||||
continue;
|
||||
// The stage is initialized to be disabled
|
||||
// We don't have to output anything
|
||||
if (states[i].COLOROP == X_D3DTOP_DISABLE)
|
||||
continue;
|
||||
|
||||
#endif
|
||||
std::string target = "stages[" + std::to_string(i) + "].";
|
||||
std::string target = "stages[" + std::to_string(i) + "].";
|
||||
|
||||
auto s = states[i];
|
||||
stageSetup << target << "COLOROP = " << GetD3DTOPString(s.COLOROP) << ";\n";
|
||||
auto s = states[i];
|
||||
stageSetup << target << "COLOROP = " << GetD3DTOPString(s.COLOROP) << ";\n";
|
||||
|
||||
stageSetup << target << "COLORARG0 = " << GetD3DTASumString(s.COLORARG0) << ";\n";
|
||||
stageSetup << target << "COLORARG1 = " << GetD3DTASumString(s.COLORARG1) << ";\n";
|
||||
stageSetup << target << "COLORARG2 = " << GetD3DTASumString(s.COLORARG2) << ";\n";
|
||||
stageSetup << target << "COLORARG0 = " << GetD3DTASumString(s.COLORARG0) << ";\n";
|
||||
stageSetup << target << "COLORARG1 = " << GetD3DTASumString(s.COLORARG1) << ";\n";
|
||||
stageSetup << target << "COLORARG2 = " << GetD3DTASumString(s.COLORARG2) << ";\n";
|
||||
|
||||
stageSetup << target << "ALPHAOP = " << GetD3DTOPString(s.ALPHAOP) << ";\n";
|
||||
stageSetup << target << "ALPHAOP = " << GetD3DTOPString(s.ALPHAOP) << ";\n";
|
||||
|
||||
stageSetup << target << "ALPHAARG0 = " << GetD3DTASumString(s.ALPHAARG0) << ";\n";
|
||||
stageSetup << target << "ALPHAARG1 = " << GetD3DTASumString(s.ALPHAARG1) << ";\n";
|
||||
stageSetup << target << "ALPHAARG2 = " << GetD3DTASumString(s.ALPHAARG2) << ";\n";
|
||||
stageSetup << target << "ALPHAARG0 = " << GetD3DTASumString(s.ALPHAARG0) << ";\n";
|
||||
stageSetup << target << "ALPHAARG1 = " << GetD3DTASumString(s.ALPHAARG1) << ";\n";
|
||||
stageSetup << target << "ALPHAARG2 = " << GetD3DTASumString(s.ALPHAARG2) << ";\n";
|
||||
|
||||
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
|
||||
stageSetup << '\n';
|
||||
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
|
||||
stageSetup << '\n';
|
||||
}
|
||||
|
||||
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
|
||||
}
|
||||
|
||||
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
|
||||
|
||||
// Compile the shader
|
||||
ID3DBlob* pShaderBlob;
|
||||
|
||||
|
@ -926,12 +944,15 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
|
|||
auto pseudoSourceFile = hlslDir.append(pseudoFileName).string();
|
||||
EmuCompileShader(finalShader, "ps_3_0", &pShaderBlob, pseudoSourceFile.c_str());
|
||||
|
||||
// Create shader object for the device
|
||||
IDirect3DPixelShader9* pShader = nullptr;
|
||||
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
|
||||
if (hRet != S_OK)
|
||||
CxbxrKrnlAbort("Failed to compile fixed function pixel shader");
|
||||
pShaderBlob->Release();
|
||||
if (pShaderBlob) {
|
||||
// Create shader object for the device
|
||||
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
|
||||
if (hRet != S_OK) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to compile fixed function pixel shader");
|
||||
}
|
||||
pShaderBlob->Release();
|
||||
}
|
||||
|
||||
// Insert the shader into the cache
|
||||
ffPsCache[key] = pShader;
|
||||
|
@ -954,7 +975,10 @@ void UpdateFixedFunctionPixelShaderState()
|
|||
ffPsState.SpecularEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_SPECULARENABLE);
|
||||
ffPsState.FogEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGENABLE);
|
||||
ffPsState.FogColor = (D3DXVECTOR3)((D3DXCOLOR)XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGCOLOR));
|
||||
|
||||
ffPsState.FogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
|
||||
ffPsState.FogDensity = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGDENSITY);
|
||||
ffPsState.FogStart = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGSTART);
|
||||
ffPsState.FogEnd = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGEND);
|
||||
// Texture state
|
||||
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
|
||||
|
||||
|
@ -1011,6 +1035,21 @@ void DxbxUpdateActivePixelShader() // NOPATCH
|
|||
// Fetch all other values that are used in the IsEquivalent check :
|
||||
CompletePSDef.SnapshotRuntimeVariables();
|
||||
|
||||
// Support hotloading hlsl
|
||||
static int pixelShaderVersion = -1;
|
||||
int shaderVersion = g_ShaderSources.Update();
|
||||
if (pixelShaderVersion != shaderVersion) {
|
||||
pixelShaderVersion = shaderVersion;
|
||||
g_pD3DDevice->SetPixelShader(nullptr);
|
||||
|
||||
for (auto& hostShader : g_RecompiledPixelShaders) {
|
||||
if (hostShader.ConvertedPixelShader)
|
||||
hostShader.ConvertedPixelShader->Release();
|
||||
}
|
||||
|
||||
g_RecompiledPixelShaders.clear();
|
||||
}
|
||||
|
||||
// Now, see if we already have a shader compiled for this definition :
|
||||
// TODO : Change g_RecompiledPixelShaders into an unordered_map, hash just the identifying PSDef members, and add cache eviction (clearing host resources when pruning)
|
||||
const PSH_RECOMPILED_SHADER* RecompiledPixelShader = nullptr;
|
||||
|
@ -1132,7 +1171,18 @@ void DxbxUpdateActivePixelShader() // NOPATCH
|
|||
frontfaceFactor = cwFrontface ? 1.0 : -1.0;
|
||||
}
|
||||
fColor[PSH_XBOX_CONSTANT_FRONTFACE_FACTOR].r = frontfaceFactor;
|
||||
|
||||
float fogEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGENABLE) > 0;
|
||||
const float fogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
|
||||
const float fogDensity = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGDENSITY);
|
||||
const float fogStart = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGSTART);
|
||||
const float fogEnd = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGEND);
|
||||
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].r = fogTableMode;
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].g = fogDensity;
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].b = fogStart;
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].a = fogEnd;
|
||||
fColor[PSH_XBOX_CONSTANT_FOGENABLE].r = fogEnable;
|
||||
|
||||
// Assume all constants are in use (this is much easier than tracking them for no other purpose than to skip a few here)
|
||||
// Read the color from the corresponding render state slot :
|
||||
// Set all host constant values using a single call:
|
||||
|
|
|
@ -469,12 +469,15 @@ extern void EmuExecutePushBufferRaw
|
|||
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_JUMP_LONG");
|
||||
dma_get_jmp_shadow = dma_get;
|
||||
dma_get = (uint32_t *)(CONTIGUOUS_MEMORY_BASE | (word & COMMAND_WORD_MASK_JUMP_LONG));
|
||||
//NV2A uses COMMAND_TYPE_JUMP_LONG as return for COMMAND_TYPE_CALL. we clear the subr_active here to indicate we have returned from COMMAND_TYPE_CALL.
|
||||
subr_active = false;
|
||||
continue; // while
|
||||
case COMMAND_TYPE_CALL: // Note : NV2A return is said not to work?
|
||||
if (subr_active) {
|
||||
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL while another call was active!");
|
||||
// TODO : throw DMA_PUSHER(CALL_SUBR_ACTIVE);
|
||||
return; // For now, don't even attempt to run through
|
||||
// For now, don't even attempt to run through, this should never happened, if it happened, the pgraph handler will go crazy.
|
||||
CxbxrAbort("Pushbuffer COMMAND_TYPE_CALL called without return!");
|
||||
}
|
||||
else {
|
||||
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL");
|
||||
|
|
|
@ -84,7 +84,7 @@ void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT HostStreamN
|
|||
uiCachedHostVertexStride);
|
||||
//DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetStreamSource");
|
||||
if (FAILED(hRet)) {
|
||||
CxbxrKrnlAbort("Failed to set the type patched buffer as the new stream source!\n");
|
||||
CxbxrAbort("Failed to set the type patched buffer as the new stream source!\n");
|
||||
// TODO : test-case : XDK Cartoon hits the above case when the vertex cache size is 0.
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ void CxbxVertexBufferConverter::ConvertStream
|
|||
if (pDrawContext->pXboxVertexStreamZeroData != xbox::zeroptr) {
|
||||
// There should only be one stream (stream zero) in this case
|
||||
if (XboxStreamNumber != 0) {
|
||||
CxbxrKrnlAbort("Trying to patch a Draw..UP with more than stream zero!");
|
||||
CxbxrAbort("Trying to patch a Draw..UP with more than stream zero!");
|
||||
}
|
||||
|
||||
pXboxVertexData = (uint8_t *)pDrawContext->pXboxVertexStreamZeroData;
|
||||
|
@ -350,7 +350,7 @@ void CxbxVertexBufferConverter::ConvertStream
|
|||
pHostVertexData = (uint8_t*)malloc(dwHostVertexDataSize);
|
||||
|
||||
if (pHostVertexData == nullptr) {
|
||||
CxbxrKrnlAbort("Couldn't allocate the new stream zero buffer");
|
||||
CxbxrAbort("Couldn't allocate the new stream zero buffer");
|
||||
}
|
||||
} else {
|
||||
HRESULT hRet = g_pD3DDevice->CreateVertexBuffer(
|
||||
|
@ -363,14 +363,14 @@ void CxbxVertexBufferConverter::ConvertStream
|
|||
);
|
||||
|
||||
if (FAILED(hRet)) {
|
||||
CxbxrKrnlAbort("Failed to create vertex buffer");
|
||||
CxbxrAbort("Failed to create vertex buffer");
|
||||
}
|
||||
}
|
||||
|
||||
// If we need to lock a host vertex buffer, do so now
|
||||
if (pHostVertexData == nullptr && pNewHostVertexBuffer != nullptr) {
|
||||
if (FAILED(pNewHostVertexBuffer->Lock(0, 0, (D3DLockData **)&pHostVertexData, D3DLOCK_DISCARD))) {
|
||||
CxbxrKrnlAbort("Couldn't lock vertex buffer");
|
||||
CxbxrAbort("Couldn't lock vertex buffer");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -609,7 +609,7 @@ void CxbxVertexBufferConverter::ConvertStream
|
|||
void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
|
||||
{
|
||||
if ((pDrawContext->XboxPrimitiveType < xbox::X_D3DPT_POINTLIST) || (pDrawContext->XboxPrimitiveType > xbox::X_D3DPT_POLYGON))
|
||||
CxbxrKrnlAbort("Unknown primitive type: 0x%.02X\n", pDrawContext->XboxPrimitiveType);
|
||||
CxbxrAbort("Unknown primitive type: 0x%.02X\n", pDrawContext->XboxPrimitiveType);
|
||||
|
||||
CxbxVertexDeclaration* pCxbxVertexDeclaration = CxbxGetVertexDeclaration();
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle
|
||||
#include "core\hle\D3D8\Direct3D9\RenderStates.h" // For XboxRenderStateConverter
|
||||
#include "core\hle\D3D8\Direct3D9\VertexShaderSource.h" // For g_VertexShaderSource
|
||||
#include "core\hle\D3D8\Direct3D9\VertexShaderCache.h" // For g_VertexShaderCache
|
||||
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
|
||||
#include "core\hle\D3D8\XbVertexBuffer.h" // For CxbxImpl_SetVertexData4f
|
||||
#include "core\hle\D3D8\XbVertexShader.h"
|
||||
#include "core\hle\D3D8\XbD3D8Logging.h" // For DEBUG_D3DRESULT
|
||||
|
@ -49,6 +50,7 @@
|
|||
#include <unordered_map>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <filesystem>
|
||||
|
||||
// External symbols :
|
||||
extern xbox::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS]; // Declared in XbVertexBuffer.cpp
|
||||
|
@ -528,7 +530,7 @@ namespace XboxVertexShaderDecoder
|
|||
return ((((CReg >> 5) & 7) - 3) * 32) + (CReg & 31);
|
||||
}
|
||||
|
||||
static void VshConvertIntermediateParam(VSH_IMD_PARAMETER& Param,
|
||||
static VSH_IMD_PARAMETER VshGetIntermediateParam(
|
||||
uint32_t* pShaderToken,
|
||||
VSH_FIELD_NAME FLD_MUX,
|
||||
VSH_FIELD_NAME FLD_NEG,
|
||||
|
@ -536,80 +538,30 @@ namespace XboxVertexShaderDecoder
|
|||
uint16_t V,
|
||||
uint16_t C)
|
||||
{
|
||||
Param.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX);
|
||||
switch (Param.ParameterType) {
|
||||
VSH_IMD_PARAMETER param{};
|
||||
param.Type = (VSH_IMD_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX);
|
||||
switch (param.Type) {
|
||||
case PARAM_R:
|
||||
Param.Address = R;
|
||||
param.Address = R;
|
||||
break;
|
||||
case PARAM_V:
|
||||
Param.Address = V;
|
||||
param.Address = V;
|
||||
break;
|
||||
case PARAM_C:
|
||||
Param.Address = C;
|
||||
param.Address = C;
|
||||
break;
|
||||
default:
|
||||
LOG_TEST_CASE("parameter type unknown");
|
||||
}
|
||||
|
||||
int d = FLD_NEG - FLD_A_NEG;
|
||||
Param.Neg = VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_NEG)) > 0;
|
||||
Param.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_X));
|
||||
Param.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Y));
|
||||
Param.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Z));
|
||||
Param.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_W));
|
||||
}
|
||||
param.Neg = VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_NEG)) > 0;
|
||||
param.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_X));
|
||||
param.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Y));
|
||||
param.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Z));
|
||||
param.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_W));
|
||||
|
||||
static void VshAddIntermediateInstruction(
|
||||
uint32_t* pShaderToken,
|
||||
IntermediateVertexShader* pShader,
|
||||
VSH_MAC MAC,
|
||||
VSH_ILU ILU,
|
||||
VSH_IMD_OUTPUT_TYPE output_type,
|
||||
int16_t output_address,
|
||||
int8_t output_mask)
|
||||
{
|
||||
// Is the output mask set?
|
||||
if (output_mask == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pShader->Instructions.size() >= VSH_MAX_INTERMEDIATE_COUNT) {
|
||||
CxbxrKrnlAbort("Shader exceeds conversion buffer!");
|
||||
}
|
||||
|
||||
VSH_INTERMEDIATE_FORMAT intermediate;
|
||||
intermediate.MAC = MAC;
|
||||
intermediate.ILU = ILU;
|
||||
intermediate.Output.Type = output_type;
|
||||
intermediate.Output.Address = output_address;
|
||||
intermediate.Output.Mask = output_mask;
|
||||
// Get a0.x indirect constant addressing
|
||||
intermediate.IndexesWithA0_X = VshGetField(pShaderToken, FLD_A0X) > 0; // Applies to PARAM_C parameter reads
|
||||
|
||||
int16_t R;
|
||||
int16_t V = VshGetField(pShaderToken, FLD_V);
|
||||
int16_t C = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST));
|
||||
intermediate.ParamCount = 0;
|
||||
if (MAC >= MAC_MOV) {
|
||||
// Get parameter A
|
||||
R = VshGetField(pShaderToken, FLD_A_R);
|
||||
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_A_MUX, FLD_A_NEG, R, V, C);
|
||||
}
|
||||
|
||||
if ((MAC == MAC_MUL) || ((MAC >= MAC_MAD) && (MAC <= MAC_SGE))) {
|
||||
// Get parameter B
|
||||
R = VshGetField(pShaderToken, FLD_B_R);
|
||||
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_B_MUX, FLD_B_NEG, R, V, C);
|
||||
}
|
||||
|
||||
if ((ILU >= ILU_MOV) || (MAC == MAC_ADD) || (MAC == MAC_MAD)) {
|
||||
// Get parameter C
|
||||
R = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | VshGetField(pShaderToken, FLD_C_R_LOW);
|
||||
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_C_MUX, FLD_C_NEG, R, V, C);
|
||||
}
|
||||
|
||||
// Add the instruction to the shader
|
||||
pShader->Instructions.push_back(intermediate);
|
||||
return param;
|
||||
}
|
||||
|
||||
static bool VshConvertToIntermediate(uint32_t* pShaderToken, IntermediateVertexShader* pShader)
|
||||
|
@ -619,52 +571,86 @@ namespace XboxVertexShaderDecoder
|
|||
VSH_MAC MAC = (VSH_MAC)VshGetField(pShaderToken, FLD_MAC);
|
||||
if (MAC > MAC_ARL) LOG_TEST_CASE("Unknown MAC");
|
||||
|
||||
// Output register
|
||||
VSH_OUTPUT_MUX OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX);
|
||||
int16_t OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS);
|
||||
VSH_IMD_OUTPUT_TYPE OutputType;
|
||||
if ((VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB) == OUTPUT_C) {
|
||||
OutputType = IMD_OUTPUT_C;
|
||||
OutputAddress = ConvertCRegister(OutputAddress);
|
||||
} else { // OUTPUT_O:
|
||||
OutputType = IMD_OUTPUT_O;
|
||||
OutputAddress = OutputAddress & 0xF;
|
||||
}
|
||||
|
||||
// MAC,ILU output R register
|
||||
int16_t RAddress = VshGetField(pShaderToken, FLD_OUT_R);
|
||||
|
||||
// Test for paired opcodes
|
||||
bool bIsPaired = (MAC != MAC_NOP) && (ILU != ILU_NOP);
|
||||
|
||||
VSH_IMD_MAC_OP MacOp{};
|
||||
VSH_IMD_ILU_OP IluOp{};
|
||||
|
||||
// Set up input registers
|
||||
int16_t AR = VshGetField(pShaderToken, FLD_A_R);
|
||||
int16_t BR = VshGetField(pShaderToken, FLD_B_R);
|
||||
int16_t CR = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | VshGetField(pShaderToken, FLD_C_R_LOW);
|
||||
int16_t V = VshGetField(pShaderToken, FLD_V);
|
||||
int16_t C = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST));
|
||||
|
||||
// Check if there's a MAC opcode
|
||||
if (MAC > MAC_NOP && MAC <= MAC_ARL) {
|
||||
if (MAC != MAC_NOP && MAC <= MAC_ARL) {
|
||||
MacOp.Opcode = MAC;
|
||||
|
||||
if (bIsPaired && RAddress == 1) {
|
||||
// Ignore paired MAC opcodes that write to R1
|
||||
} else {
|
||||
if (MAC == MAC_ARL) {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_A0X, 0, MASK_X);
|
||||
} else {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_R, RAddress, VshGetField(pShaderToken, FLD_OUT_MAC_MASK));
|
||||
}
|
||||
}
|
||||
else if (MAC == MAC_ARL) {
|
||||
MacOp.Dest.Type = IMD_DEST_A0X;
|
||||
MacOp.Dest.Mask = MASK_X;
|
||||
}
|
||||
else {
|
||||
MacOp.Dest.Type = IMD_DEST_R;
|
||||
MacOp.Dest.Address = RAddress;
|
||||
MacOp.Dest.Mask = VshGetField(pShaderToken, FLD_OUT_MAC_MASK);
|
||||
}
|
||||
|
||||
// Check if we must add a muxed MAC opcode as well
|
||||
if (OutputMux == OMUX_MAC) {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK));
|
||||
if (MAC >= MAC_MOV) {
|
||||
MacOp.Parameters[MacOp.ParamCount++] = VshGetIntermediateParam(pShaderToken, FLD_A_MUX, FLD_A_NEG, AR, V, C);
|
||||
}
|
||||
|
||||
if (MAC == MAC_MUL || (MAC >= MAC_MAD && MAC <= MAC_SGE)) {
|
||||
MacOp.Parameters[MacOp.ParamCount++] = VshGetIntermediateParam(pShaderToken, FLD_B_MUX, FLD_B_NEG, BR, V, C);
|
||||
}
|
||||
|
||||
if (MAC == MAC_ADD || MAC == MAC_MAD) {
|
||||
MacOp.Parameters[MacOp.ParamCount++] = VshGetIntermediateParam(pShaderToken, FLD_C_MUX, FLD_C_NEG, CR, V, C);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there's an ILU opcode
|
||||
if (ILU != ILU_NOP) {
|
||||
// Paired ILU opcodes will only write to R1
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, IMD_OUTPUT_R, bIsPaired ? 1 : RAddress, VshGetField(pShaderToken, FLD_OUT_ILU_MASK));
|
||||
// Check if we must add a muxed ILU opcode as well
|
||||
if (OutputMux == OMUX_ILU) {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK));
|
||||
}
|
||||
IluOp.Opcode = ILU;
|
||||
IluOp.Dest.Type = IMD_DEST_R;
|
||||
IluOp.Dest.Address = bIsPaired ? 1 : RAddress;
|
||||
IluOp.Dest.Mask = VshGetField(pShaderToken, FLD_OUT_ILU_MASK);
|
||||
IluOp.Parameter = VshGetIntermediateParam(pShaderToken, FLD_C_MUX, FLD_C_NEG, CR, V, C);
|
||||
}
|
||||
|
||||
// Output register
|
||||
VSH_OUTPUT_MUX OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX);
|
||||
int16_t OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS);
|
||||
VSH_IMD_DEST_TYPE OutputType;
|
||||
if ((VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB) == OUTPUT_C) {
|
||||
OutputType = IMD_DEST_C;
|
||||
OutputAddress = ConvertCRegister(OutputAddress);
|
||||
}
|
||||
else { // OUTPUT_O:
|
||||
OutputType = IMD_DEST_O;
|
||||
OutputAddress = OutputAddress & 0xF;
|
||||
}
|
||||
|
||||
VSH_IMD_INSTR imd{};
|
||||
imd.MAC = MacOp;
|
||||
imd.ILU = IluOp;
|
||||
imd.IndexesWithA0_X = VshGetField(pShaderToken, FLD_A0X) > 0;
|
||||
imd.ORegSource = OutputMux == OMUX_MAC ? SRC_MAC : SRC_ILU;
|
||||
imd.ORegDest.Type = OutputType;
|
||||
imd.ORegDest.Address = OutputAddress;
|
||||
imd.ORegDest.Mask = VshGetField(pShaderToken, FLD_OUT_O_MASK);
|
||||
|
||||
pShader->Instructions.push_back(imd);
|
||||
|
||||
return VshGetField(pShaderToken, FLD_FINAL) == 0;
|
||||
}
|
||||
};
|
||||
|
@ -905,7 +891,7 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
assert(HostVertexElementDataType > 0);
|
||||
assert(HostVertexElementDataType < D3DDECLTYPE_UNUSED);
|
||||
assert(HostVertexElementByteSize > 0);
|
||||
|
||||
// Select new stream, if needed
|
||||
|
@ -1140,10 +1126,49 @@ IDirect3DVertexDeclaration* CxbxCreateHostVertexDeclaration(D3DVERTEXELEMENT *pD
|
|||
return pHostVertexDeclaration;
|
||||
}
|
||||
|
||||
static IDirect3DVertexShader* passthroughshader;
|
||||
IDirect3DVertexShader* InitShader(void (*compileFunc)(ID3DBlob**), const char* label) {
|
||||
IDirect3DVertexShader* shader = nullptr;
|
||||
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
compileFunc(&pBlob);
|
||||
if (pBlob) {
|
||||
HRESULT hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &shader);
|
||||
pBlob->Release();
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to create shader: %s", label);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
void CxbxUpdateHostVertexShader()
|
||||
{
|
||||
extern bool g_bUsePassthroughHLSL; // TMP glue
|
||||
// TODO: move d3d9 state to VertexShader.cpp
|
||||
static IDirect3DVertexShader* fixedFunctionShader = nullptr; // TODO: move to shader cache
|
||||
static IDirect3DVertexShader* passthroughShader = nullptr;
|
||||
static int vertexShaderVersion = -1;
|
||||
|
||||
int shaderVersion = g_ShaderSources.Update();
|
||||
if (vertexShaderVersion != shaderVersion) {
|
||||
vertexShaderVersion = shaderVersion;
|
||||
g_pD3DDevice->SetVertexShader(nullptr);
|
||||
|
||||
EmuLog(LOG_LEVEL::INFO, "Loading vertex shaders...");
|
||||
|
||||
g_VertexShaderCache.Clear();
|
||||
|
||||
if (fixedFunctionShader) {
|
||||
fixedFunctionShader->Release();
|
||||
fixedFunctionShader = nullptr;
|
||||
}
|
||||
fixedFunctionShader = InitShader(EmuCompileFixedFunction, "Fixed Function Vertex Shader");
|
||||
|
||||
if (passthroughShader) {
|
||||
passthroughShader->Release();
|
||||
passthroughShader = nullptr;
|
||||
}
|
||||
passthroughShader = InitShader(EmuCompileXboxPassthrough, "Passthrough Vertex Shader");
|
||||
}
|
||||
|
||||
// TODO Call this when state is dirty
|
||||
// Rather than every time state changes
|
||||
|
@ -1151,43 +1176,20 @@ void CxbxUpdateHostVertexShader()
|
|||
LOG_INIT; // Allows use of DEBUG_D3DRESULT
|
||||
|
||||
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
|
||||
IDirect3DVertexShader* fixedFunctionShader = nullptr;
|
||||
HRESULT hRet;
|
||||
|
||||
if (g_UseFixedFunctionVertexShader) {
|
||||
static IDirect3DVertexShader* ffHlsl = nullptr;
|
||||
if (ffHlsl == nullptr) {
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
EmuCompileFixedFunction(&pBlob);
|
||||
if (pBlob) {
|
||||
hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &ffHlsl);
|
||||
if (FAILED(hRet)) CxbxrKrnlAbort("Failed to create fixed-function shader");
|
||||
}
|
||||
}
|
||||
fixedFunctionShader = ffHlsl;
|
||||
}
|
||||
|
||||
hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
|
||||
if (FAILED(hRet)) CxbxrKrnlAbort("Failed to set fixed-function shader");
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to set fixed-function shader");
|
||||
}
|
||||
else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) {
|
||||
if (passthroughshader == nullptr) {
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
EmuCompileXboxPassthrough(&pBlob);
|
||||
if (pBlob) {
|
||||
g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &passthroughshader);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughshader);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughShader);
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to set passthrough shader");
|
||||
}
|
||||
else {
|
||||
auto pTokens = GetCxbxVertexShaderSlotPtr(g_Xbox_VertexShader_FunctionSlots_StartAddress);
|
||||
assert(pTokens);
|
||||
// Create a vertex shader from the tokens
|
||||
DWORD shaderSize;
|
||||
auto VertexShaderKey = g_VertexShaderSource.CreateShader(pTokens, &shaderSize);
|
||||
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderSource.GetShader(VertexShaderKey);
|
||||
auto VertexShaderKey = g_VertexShaderCache.CreateShader(pTokens, &shaderSize);
|
||||
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderCache.GetShader(VertexShaderKey);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(pHostVertexShader);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
|
||||
}
|
||||
|
@ -1575,7 +1577,7 @@ void CxbxImpl_DeleteVertexShader(DWORD Handle)
|
|||
RegisterCxbxVertexDeclaration(pCxbxVertexDeclaration->Key, nullptr); // Remove from cache (which will free present pCxbxVertexDeclaration)
|
||||
|
||||
// Release the host vertex shader
|
||||
g_VertexShaderSource.ReleaseShader(pCxbxVertexShader->Key);
|
||||
g_VertexShaderCache.ReleaseShader(pCxbxVertexShader->Key);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -147,18 +147,18 @@ enum VSH_MAC { // Dxbx note : MAC stands for 'Multiply And Accumulate' opcodes
|
|||
// ??? 15 - 2 values of the 4 bits are undefined
|
||||
};
|
||||
|
||||
enum VSH_IMD_OUTPUT_TYPE {
|
||||
IMD_OUTPUT_C,
|
||||
IMD_OUTPUT_R,
|
||||
IMD_OUTPUT_O,
|
||||
IMD_OUTPUT_A0X
|
||||
enum VSH_IMD_DEST_TYPE {
|
||||
IMD_DEST_C,
|
||||
IMD_DEST_R,
|
||||
IMD_DEST_O,
|
||||
IMD_DEST_A0X
|
||||
};
|
||||
|
||||
typedef struct _VSH_IMD_OUTPUT {
|
||||
VSH_IMD_OUTPUT_TYPE Type;
|
||||
int16_t Address;
|
||||
int8_t Mask;
|
||||
} VSH_IMD_OUTPUT;
|
||||
typedef struct {
|
||||
VSH_IMD_DEST_TYPE Type;
|
||||
int16_t Address;
|
||||
int8_t Mask; // If 0 skip writing to this output
|
||||
} VSH_IMD_DEST;
|
||||
|
||||
enum VSH_SWIZZLE {
|
||||
SWIZZLE_X = 0,
|
||||
|
@ -167,7 +167,7 @@ enum VSH_SWIZZLE {
|
|||
SWIZZLE_W
|
||||
};
|
||||
|
||||
enum VSH_PARAMETER_TYPE {
|
||||
enum VSH_IMD_PARAMETER_TYPE {
|
||||
PARAM_UNKNOWN = 0,
|
||||
PARAM_R, // Temporary (scRatch) registers
|
||||
PARAM_V, // Vertex registers
|
||||
|
@ -175,28 +175,49 @@ enum VSH_PARAMETER_TYPE {
|
|||
PARAM_O // = 0??
|
||||
};
|
||||
|
||||
typedef struct _VSH_IMD_PARAMETER {
|
||||
VSH_PARAMETER_TYPE ParameterType; // Parameter type, R, V or C
|
||||
typedef struct {
|
||||
VSH_IMD_PARAMETER_TYPE Type; // Parameter type, R, V or C
|
||||
bool Neg; // true if negated, false if not
|
||||
VSH_SWIZZLE Swizzle[4]; // The four swizzles
|
||||
int16_t Address; // Register address
|
||||
} VSH_IMD_PARAMETER;
|
||||
|
||||
typedef struct _VSH_INTERMEDIATE_FORMAT {
|
||||
VSH_MAC MAC;
|
||||
VSH_ILU ILU;
|
||||
VSH_IMD_OUTPUT Output;
|
||||
unsigned ParamCount;
|
||||
VSH_IMD_PARAMETER Parameters[3];
|
||||
// There is only a single address register in Microsoft DirectX 8.0.
|
||||
// The address register, designated as a0.x, may be used as signed
|
||||
// integer offset in relative addressing into the constant register file.
|
||||
// c[a0.x + n]
|
||||
bool IndexesWithA0_X;
|
||||
} VSH_INTERMEDIATE_FORMAT;
|
||||
typedef struct {
|
||||
VSH_ILU Opcode;
|
||||
VSH_IMD_DEST Dest;
|
||||
VSH_IMD_PARAMETER Parameter;
|
||||
} VSH_IMD_ILU_OP;
|
||||
|
||||
typedef struct _IntermediateVertexShader {
|
||||
std::vector<VSH_INTERMEDIATE_FORMAT> Instructions;
|
||||
typedef struct {
|
||||
VSH_MAC Opcode;
|
||||
VSH_IMD_DEST Dest;
|
||||
uint8_t ParamCount;
|
||||
VSH_IMD_PARAMETER Parameters[3];
|
||||
} VSH_IMD_MAC_OP;
|
||||
|
||||
enum VSH_IMD_OREG_SOURCE {
|
||||
SRC_MAC,
|
||||
SRC_ILU,
|
||||
};
|
||||
|
||||
// Intermediate decoded VSH instruction
|
||||
// Up to two operations (MAC and ILU)
|
||||
// Writes to up to 3 destination registers
|
||||
// One dest per op + one output register
|
||||
typedef struct {
|
||||
VSH_IMD_MAC_OP MAC;
|
||||
VSH_IMD_ILU_OP ILU;
|
||||
|
||||
VSH_IMD_OREG_SOURCE ORegSource;
|
||||
VSH_IMD_DEST ORegDest;
|
||||
|
||||
// True if the constant input C should use the index register a0
|
||||
// c[a0.x + n]
|
||||
bool IndexesWithA0_X;
|
||||
} VSH_IMD_INSTR;
|
||||
|
||||
typedef struct {
|
||||
std::vector<VSH_IMD_INSTR> Instructions;
|
||||
} IntermediateVertexShader;
|
||||
|
||||
// parse xbox vertex shader function into an intermediate format
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
// TODO: Move these to LLE APUDevice once we have one!
|
||||
|
||||
static constexpr uint32_t APU_TIMER_FREQUENCY = 48000;
|
||||
static uint64_t dsound_last;
|
||||
|
||||
uint32_t GetAPUTime()
|
||||
{
|
||||
|
@ -85,10 +86,6 @@ uint32_t GetAPUTime()
|
|||
there is chance of failure which contain value greater than 0.
|
||||
*/
|
||||
|
||||
// Managed memory xbox audio variables
|
||||
static std::thread dsound_thread;
|
||||
static void dsound_thread_worker(LPVOID);
|
||||
|
||||
#include "DirectSoundInline.hpp"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -98,6 +95,7 @@ extern "C" {
|
|||
void CxbxInitAudio()
|
||||
{
|
||||
g_EmuShared->GetAudioSettings(&g_XBAudio);
|
||||
dsound_last = get_now();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -125,10 +123,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
|
|||
|
||||
HRESULT hRet = DS_OK;
|
||||
|
||||
if (!initialized) {
|
||||
dsound_thread = std::thread(dsound_thread_worker, nullptr);
|
||||
}
|
||||
|
||||
// Set this flag when this function is called
|
||||
g_bDSoundCreateCalled = TRUE;
|
||||
|
||||
|
@ -166,13 +160,13 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
|
|||
}
|
||||
|
||||
if (dsErrorMsg != nullptr) {
|
||||
CxbxrKrnlAbort(dsErrorMsg, hRet);
|
||||
CxbxrAbort(dsErrorMsg, hRet);
|
||||
}
|
||||
|
||||
hRet = g_pDSound8->SetCooperativeLevel(GET_FRONT_WINDOW_HANDLE, DSSCL_PRIORITY);
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("g_pDSound8->SetCooperativeLevel Failed!");
|
||||
CxbxrAbort("g_pDSound8->SetCooperativeLevel Failed!");
|
||||
}
|
||||
|
||||
// clear sound buffer cache
|
||||
|
@ -200,7 +194,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
|
|||
hRet = g_pDSound8->CreateSoundBuffer(&bufferDesc, &g_pDSoundPrimaryBuffer, nullptr);
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("Creating primary buffer for DirectSound Failed!");
|
||||
CxbxrAbort("Creating primary buffer for DirectSound Failed!");
|
||||
}
|
||||
|
||||
/* Quote from MDSN "For the primary buffer, you must use the
|
||||
|
@ -213,7 +207,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
|
|||
hRet = g_pDSoundPrimaryBuffer->QueryInterface(IID_IDirectSound3DListener8, (LPVOID*)&g_pDSoundPrimary3DListener8);
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("Creating primary 3D Listener for DirectSound Failed!");
|
||||
CxbxrAbort("Creating primary 3D Listener for DirectSound Failed!");
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
@ -365,24 +359,140 @@ xbox::void_xt WINAPI xbox::EMUPATCH(DirectSoundDoWork)()
|
|||
|
||||
return;
|
||||
}
|
||||
// For Async process purpose only
|
||||
static void dsound_thread_worker(LPVOID nullPtr)
|
||||
|
||||
void StreamBufferAudio(xbox::XbHybridDSBuffer* pHybridBuffer, float msToCopy) {
|
||||
auto pThis = pHybridBuffer->emuDSBuffer;
|
||||
auto dsb = pThis->EmuDirectSoundBuffer8;
|
||||
bool isAdpcm = pThis->EmuFlags & DSE_FLAG_XADPCM;
|
||||
|
||||
DWORD xBufferRangeStart;
|
||||
DWORD xBufferRangeSize;
|
||||
DSoundBufferRegionCurrentLocation(pHybridBuffer, pThis->EmuPlayFlags, xBufferRangeStart, xBufferRangeSize);
|
||||
|
||||
DWORD hostBufferSize = pThis->EmuBufferDesc.dwBufferBytes;
|
||||
|
||||
DWORD playCursor;
|
||||
DWORD writeCursor;
|
||||
dsb->GetCurrentPosition(&playCursor, &writeCursor);
|
||||
|
||||
DWORD cursorGap = writeCursor >= playCursor
|
||||
? (writeCursor - playCursor)
|
||||
: hostBufferSize - playCursor + writeCursor;
|
||||
|
||||
// Determine where to copy data from.
|
||||
// Note: The DirectSound write cursor can sit quite far ahead of the play cursor,
|
||||
// but copying closer to the play cursor can introduce weird looping or
|
||||
// latency issues
|
||||
// Test case: NBA Live 2005 (writes to a very small buffer expecting low latency, can crackle at > 1ms stream interval)
|
||||
// Test case: Halo (intro video delay when writing from play cursor)
|
||||
DWORD writeOffset = writeCursor + cursorGap * g_dsBufferStreaming.tweakCopyOffset;
|
||||
DWORD writeSize = std::min(
|
||||
(DWORD)(pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec * msToCopy / 1000),
|
||||
hostBufferSize
|
||||
);
|
||||
|
||||
DWORD blockSize = isAdpcm
|
||||
? XBOX_ADPCM_DSTSIZE * pThis->EmuBufferDesc.lpwfxFormat->nChannels
|
||||
: pThis->EmuBufferDesc.lpwfxFormat->nBlockAlign;
|
||||
|
||||
// ADPCM block alignment
|
||||
writeOffset = ((writeOffset + blockSize / 2) / blockSize) * blockSize;
|
||||
writeSize = ((writeSize + blockSize / 2) / blockSize) * blockSize;
|
||||
writeOffset %= hostBufferSize;
|
||||
|
||||
DWORD xWriteOffset = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, writeOffset);
|
||||
|
||||
assert(xBufferRangeStart + xBufferRangeSize > xWriteOffset);
|
||||
if (isAdpcm) {
|
||||
assert(writeOffset % XBOX_ADPCM_DSTSIZE == 0);
|
||||
assert(xWriteOffset % XBOX_ADPCM_SRCSIZE == 0);
|
||||
}
|
||||
|
||||
LPVOID lplpvAudioPtr1, lplpvAudioPtr2;
|
||||
DWORD lplpvAudioBytes1, lplpvAudioBytes2;
|
||||
HRESULT hRet = pThis->EmuDirectSoundBuffer8->Lock(writeOffset, writeSize,
|
||||
&lplpvAudioPtr1, &lplpvAudioBytes1,
|
||||
&lplpvAudioPtr2, &lplpvAudioBytes2,
|
||||
0);
|
||||
|
||||
if (hRet != 0) {
|
||||
CxbxrAbort("DirectSoundBuffer Lock Failed!");
|
||||
}
|
||||
|
||||
if (lplpvAudioPtr1 && pThis->X_BufferCache != nullptr) {
|
||||
DSoundBufferOutputXBtoHost(
|
||||
pThis->EmuFlags,
|
||||
pThis->EmuBufferDesc,
|
||||
((PBYTE)pThis->X_BufferCache + xBufferRangeStart + xWriteOffset),
|
||||
DSoundBufferGetXboxBufferSize(pThis->EmuFlags, lplpvAudioBytes1),
|
||||
lplpvAudioPtr1,
|
||||
lplpvAudioBytes1
|
||||
);
|
||||
|
||||
if (lplpvAudioPtr2) {
|
||||
DSoundBufferOutputXBtoHost(
|
||||
pThis->EmuFlags,
|
||||
pThis->EmuBufferDesc,
|
||||
((PBYTE)pThis->X_BufferCache + xBufferRangeStart + 0),
|
||||
DSoundBufferGetXboxBufferSize(pThis->EmuFlags, lplpvAudioBytes2),
|
||||
lplpvAudioPtr2,
|
||||
lplpvAudioBytes2
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT hRet = dsb->Unlock(lplpvAudioPtr1, lplpvAudioBytes1, lplpvAudioPtr2, lplpvAudioBytes2);
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrAbort("DirectSoundBuffer Unlock Failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dsound_async_worker()
|
||||
{
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
while (true) {
|
||||
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
|
||||
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
|
||||
Sleep(300);
|
||||
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
|
||||
{
|
||||
DSoundMutexGuardLock;
|
||||
xbox::LARGE_INTEGER getTime;
|
||||
xbox::KeQuerySystemTime(&getTime);
|
||||
DirectSoundDoWork_Stream(getTime);
|
||||
}
|
||||
|
||||
xbox::LARGE_INTEGER getTime;
|
||||
xbox::KeQuerySystemTime(&getTime);
|
||||
DirectSoundDoWork_Stream(getTime);
|
||||
}
|
||||
void dsound_worker()
|
||||
{
|
||||
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
|
||||
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
|
||||
|
||||
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
// Stream sound buffer audio
|
||||
// because the title may change the content of sound buffers at any time
|
||||
for (auto& pBuffer : g_pDSoundBufferCache) {
|
||||
// Avoid expensive calls to DirectSound on buffers unless they've been played at least once
|
||||
// Since some titles create a large amount of buffers, but only use a few
|
||||
if (pBuffer->emuDSBuffer->EmuStreamingInfo.playRequested) {
|
||||
DWORD status;
|
||||
HRESULT hRet = pBuffer->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&status);
|
||||
if (hRet == 0 && status & DSBSTATUS_PLAYING) {
|
||||
auto streamMs = g_dsBufferStreaming.streamInterval + g_dsBufferStreaming.streamAhead;
|
||||
StreamBufferAudio(pBuffer, streamMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t dsound_next(uint64_t now)
|
||||
{
|
||||
constexpr uint64_t dsound_period = 300 * 1000;
|
||||
uint64_t next = dsound_last + dsound_period;
|
||||
|
||||
if (now >= next) {
|
||||
dsound_async_worker();
|
||||
dsound_last = get_now();
|
||||
return dsound_period;
|
||||
}
|
||||
|
||||
return dsound_last + dsound_period - now; // time remaining until next dsound async event
|
||||
}
|
||||
|
||||
// Kismet given name for RadWolfie's experiment major issue in the mutt.
|
||||
|
@ -945,6 +1055,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(CDirectSound_SynchPlayback)
|
|||
DSoundBufferSynchPlaybackFlagRemove(pDSBuffer->EmuFlags);
|
||||
EmuLog(LOG_LEVEL::DEBUG, "SynchPlayback - pDSBuffer: %08X; EmuPlayFlags: %08X", *ppDSBuffer, pDSBuffer->EmuPlayFlags);
|
||||
pDSBuffer->EmuDirectSoundBuffer8->Play(0, 0, pDSBuffer->EmuPlayFlags);
|
||||
pDSBuffer->EmuStreamingInfo.playRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,10 @@ struct EmuDirectSoundBuffer
|
|||
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
|
||||
X_DSVOICEPROPS Xb_VoiceProperties;
|
||||
DWORD Xb_Flags;
|
||||
struct {
|
||||
// True if the buffer has been played, and should be considered for streaming
|
||||
bool playRequested = false;
|
||||
} EmuStreamingInfo;
|
||||
};
|
||||
|
||||
struct XbHybridDSBuffer : DSBUFFER_S::DSBUFFER_I {
|
||||
|
|
|
@ -80,6 +80,7 @@ void DirectSoundDoWork_Buffer(xbox::LARGE_INTEGER &time)
|
|||
pThis->Xb_rtPauseEx = 0LL;
|
||||
pThis->EmuFlags &= ~DSE_FLAG_PAUSE;
|
||||
pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
|
||||
pThis->EmuStreamingInfo.playRequested = true;
|
||||
}
|
||||
|
||||
if (pThis->Xb_rtStopEx != 0LL && pThis->Xb_rtStopEx <= time.QuadPart) {
|
||||
|
@ -173,7 +174,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateBuffer)
|
|||
|
||||
hRet = xbox::EMUPATCH(DirectSoundCreate)(nullptr, &g_pDSound8, nullptr);
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("Unable to initialize DirectSound!");
|
||||
CxbxrAbort("Unable to initialize DirectSound!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,7 +239,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateBuffer)
|
|||
EmuLog(LOG_LEVEL::WARNING, output.str().c_str());
|
||||
output.str("");
|
||||
output << static_cast<DS_RESULT>(hRet);
|
||||
CxbxrKrnlAbort("DSB: DSoundBufferCreate error: %s", output.str().c_str());
|
||||
CxbxrAbort("DSB: DSoundBufferCreate error: %s", output.str().c_str());
|
||||
}
|
||||
else {
|
||||
if (pdsbd->dwFlags & DSBCAPS_CTRL3D) {
|
||||
|
@ -437,55 +438,26 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Lock)
|
|||
LOG_FUNC_END;
|
||||
|
||||
EmuDirectSoundBuffer* pThis = pHybridThis->emuDSBuffer;
|
||||
HRESULT hRet = D3D_OK;
|
||||
DWORD pcmSize = DSoundBufferGetPCMBufferSize(pThis->EmuFlags, dwBytes);
|
||||
DWORD pcmOffset = DSoundBufferGetPCMBufferSize(pThis->EmuFlags, dwOffset);
|
||||
|
||||
DSoundGenericUnlock(pThis->EmuFlags,
|
||||
pThis->EmuDirectSoundBuffer8,
|
||||
pThis->EmuBufferDesc,
|
||||
pThis->Host_lock,
|
||||
pThis->X_BufferCache,
|
||||
pThis->X_lock.dwLockOffset,
|
||||
pThis->X_lock.dwLockBytes1,
|
||||
pThis->X_lock.dwLockBytes2);
|
||||
// Xbox directsound doesn't require locking buffers
|
||||
// This Xbox api only exists to match PC
|
||||
|
||||
if (ppvAudioPtr2 == xbox::zeroptr) {
|
||||
hRet = pThis->EmuDirectSoundBuffer8->Lock(pcmOffset, pcmSize, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
|
||||
nullptr, 0, dwFlags);
|
||||
pThis->Host_lock.pLockPtr2 = nullptr;
|
||||
} else {
|
||||
hRet = pThis->EmuDirectSoundBuffer8->Lock(pcmOffset, pcmSize, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
|
||||
&pThis->Host_lock.pLockPtr2, &pThis->Host_lock.dwLockBytes2, dwFlags);
|
||||
}
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("IDirectSoundBuffer_Lock Failed!");
|
||||
}
|
||||
|
||||
// Host lock position
|
||||
pThis->Host_lock.dwLockOffset = pcmOffset;
|
||||
pThis->Host_lock.dwLockFlags = dwFlags;
|
||||
pThis->X_lock.dwLockFlags = dwFlags;
|
||||
|
||||
// Emulate to xbox's lock position
|
||||
pThis->X_lock.dwLockOffset = dwOffset;
|
||||
*ppvAudioPtr1 = pThis->X_lock.pLockPtr1 = ((LPBYTE)pThis->X_BufferCache + dwOffset);
|
||||
*pdwAudioBytes1 = pThis->X_lock.dwLockBytes1 = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, pThis->Host_lock.dwLockBytes1);
|
||||
if (pThis->Host_lock.pLockPtr2 != nullptr) {
|
||||
*ppvAudioPtr2 = pThis->X_lock.pLockPtr2 = pThis->X_BufferCache;
|
||||
*pdwAudioBytes2 = pThis->X_lock.dwLockBytes2 = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, pThis->Host_lock.dwLockBytes2);
|
||||
} else {
|
||||
// If secondary pointers are not used, then set them as zero.
|
||||
// There are applications bug didn't check for audio pointer that is null pointer which should not use invalid audio bytes.
|
||||
// Since internal functions do set them zero. We'll set them here as well.
|
||||
if (ppvAudioPtr2 != xbox::zeroptr) {
|
||||
*ppvAudioPtr2 = xbox::zeroptr;
|
||||
}
|
||||
if (pdwAudioBytes2 != xbox::zeroptr) {
|
||||
*pdwAudioBytes2 = 0;
|
||||
}
|
||||
}
|
||||
if (dwOffset + dwBytes <= pThis->X_BufferCacheSize) {
|
||||
*pdwAudioBytes1 = dwBytes;
|
||||
*ppvAudioPtr1 = (PBYTE)pThis->X_BufferCache + dwOffset;
|
||||
if (ppvAudioPtr2 != nullptr) {
|
||||
*ppvAudioPtr2 = nullptr;
|
||||
*pdwAudioBytes2 = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*pdwAudioBytes1 = pThis->X_BufferCacheSize - dwOffset;
|
||||
*ppvAudioPtr1 = (PBYTE)pThis->X_BufferCache + dwOffset;
|
||||
if (ppvAudioPtr2 != nullptr) {
|
||||
*pdwAudioBytes2 = dwBytes - *pdwAudioBytes1;
|
||||
*ppvAudioPtr2 = (PBYTE)pThis->X_BufferCache;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_FUNC_BEGIN_ARG_RESULT
|
||||
LOG_FUNC_ARG_RESULT(ppvAudioPtr1)
|
||||
|
@ -494,7 +466,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Lock)
|
|||
LOG_FUNC_ARG_RESULT(pdwAudioBytes2)
|
||||
LOG_FUNC_END_ARG_RESULT;
|
||||
|
||||
RETURN_RESULT_CHECK(hRet);
|
||||
RETURN(DS_OK);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -509,7 +481,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Unlock)
|
|||
dword_xt pdwAudioBytes2
|
||||
)
|
||||
{
|
||||
DSoundMutexGuardLock;
|
||||
// DSoundMutexGuardLock;
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(pHybridThis)
|
||||
|
@ -519,28 +491,8 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Unlock)
|
|||
LOG_FUNC_ARG(pdwAudioBytes2)
|
||||
LOG_FUNC_END;
|
||||
|
||||
EmuDirectSoundBuffer* pThis = pHybridThis->emuDSBuffer;
|
||||
// TODO: Find out why pThis->EmuLockPtr1 is nullptr... (workaround atm is to check if it is not a nullptr.)
|
||||
if (pThis->X_BufferCache != xbox::zeroptr && pThis->Host_lock.pLockPtr1 != nullptr) {
|
||||
|
||||
memcpy_s((PBYTE)pThis->X_BufferCache + pThis->X_lock.dwLockOffset,
|
||||
pThis->X_BufferCacheSize - pThis->X_lock.dwLockOffset,
|
||||
pThis->X_lock.pLockPtr1,
|
||||
pThis->X_lock.dwLockBytes1);
|
||||
|
||||
if (pThis->Host_lock.pLockPtr2 != nullptr) {
|
||||
memcpy_s(pThis->X_BufferCache, pThis->X_BufferCacheSize, pThis->X_lock.pLockPtr2, pThis->X_lock.dwLockBytes2);
|
||||
}
|
||||
}
|
||||
|
||||
DSoundGenericUnlock(pThis->EmuFlags,
|
||||
pThis->EmuDirectSoundBuffer8,
|
||||
pThis->EmuBufferDesc,
|
||||
pThis->Host_lock,
|
||||
pThis->X_BufferCache,
|
||||
pThis->X_lock.dwLockOffset,
|
||||
pThis->X_lock.dwLockBytes1,
|
||||
pThis->X_lock.dwLockBytes2);
|
||||
// Xbox directsound doesn't require locking buffers
|
||||
// This Xbox api only exists to match PC
|
||||
|
||||
return DS_OK;
|
||||
}
|
||||
|
@ -632,7 +584,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Play)
|
|||
pThis->X_lock.dwLockBytes2);
|
||||
|
||||
if (dwFlags & ~(X_DSBPLAY_LOOPING | X_DSBPLAY_FROMSTART | X_DSBPLAY_SYNCHPLAYBACK)) {
|
||||
CxbxrKrnlAbort("Unsupported Playing Flags");
|
||||
CxbxrAbort("Unsupported Playing Flags");
|
||||
}
|
||||
pThis->EmuPlayFlags = dwFlags;
|
||||
|
||||
|
@ -660,6 +612,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Play)
|
|||
}
|
||||
if ((pThis->EmuFlags & DSE_FLAG_SYNCHPLAYBACK_CONTROL) == 0) {
|
||||
hRet = pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
|
||||
pThis->EmuStreamingInfo.playRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1594,52 +1547,54 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_StopEx)
|
|||
hRet = pThis->EmuDirectSoundBuffer8->Stop();
|
||||
pThis->Xb_rtStopEx = 0LL;
|
||||
}
|
||||
else {
|
||||
bool isLooping;
|
||||
if ((pThis->EmuPlayFlags & X_DSBPLAY_LOOPING) > 0) {
|
||||
isLooping = true;
|
||||
}
|
||||
else {
|
||||
isLooping = false;
|
||||
}
|
||||
else if(dwFlags & X_DSBSTOPEX_ENVELOPE) {
|
||||
bool isLooping = pThis->EmuPlayFlags & X_DSBPLAY_LOOPING;
|
||||
|
||||
if ((dwFlags & X_DSBSTOPEX_ENVELOPE) > 0) {
|
||||
if (rtTimeStamp == 0LL) {
|
||||
xbox::LARGE_INTEGER getTime;
|
||||
xbox::KeQuerySystemTime(&getTime);
|
||||
pThis->Xb_rtStopEx = getTime.QuadPart;
|
||||
}
|
||||
else {
|
||||
pThis->Xb_rtStopEx = rtTimeStamp;
|
||||
}
|
||||
pThis->Xb_rtStopEx += (pThis->Xb_EnvolopeDesc.dwRelease * 512) / 48000;
|
||||
double releaseSamples = pThis->Xb_EnvolopeDesc.dwRelease * 512.0;
|
||||
|
||||
if (rtTimeStamp == 0LL) {
|
||||
xbox::LARGE_INTEGER getTime;
|
||||
xbox::KeQuerySystemTime(&getTime);
|
||||
pThis->Xb_rtStopEx = getTime.QuadPart;
|
||||
}
|
||||
else {
|
||||
pThis->Xb_rtStopEx = rtTimeStamp;
|
||||
}
|
||||
const double samplesToTicks = 10000000 / 48000.0;
|
||||
xbox::REFERENCE_TIME releaseTicks = static_cast<xbox::REFERENCE_TIME>(releaseSamples * samplesToTicks);
|
||||
pThis->Xb_rtStopEx += releaseTicks;
|
||||
|
||||
if ((dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) > 0) {
|
||||
// Release from loop region.
|
||||
pThis->EmuPlayFlags &= ~X_DSBPLAY_LOOPING;
|
||||
}
|
||||
|
||||
DWORD dwValue, dwStatus;
|
||||
DWORD currentPos, dwStatus;
|
||||
pThis->EmuDirectSoundBuffer8->GetStatus(&dwStatus);
|
||||
|
||||
if (pThis->EmuBufferToggle != X_DSB_TOGGLE_DEFAULT) {
|
||||
|
||||
pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &dwValue);
|
||||
pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, ¤tPos);
|
||||
hRet = pThis->EmuDirectSoundBuffer8->Stop();
|
||||
|
||||
DSoundBufferResizeUpdate(pHybridThis, pThis->EmuPlayFlags, hRet, 0, pThis->X_BufferCacheSize);
|
||||
// Determine the range of bytes we need to play
|
||||
// Test case: Outrun 2006 - converting large buffers tanks the FPS
|
||||
// Is set within DSoundBufferRegionCurrentLocation function
|
||||
DWORD bufferRangeStart;
|
||||
DWORD bufferRangeSize;
|
||||
DSoundBufferRegionCurrentLocation(pHybridThis, pThis->EmuPlayFlags, bufferRangeStart, bufferRangeSize);
|
||||
|
||||
dwValue += pThis->EmuRegionPlayStartOffset;
|
||||
if (isLooping) {
|
||||
dwValue += pThis->EmuRegionLoopStartOffset;
|
||||
if (pThis->EmuBufferToggle == X_DSB_TOGGLE_LOOP) {
|
||||
// if we are to release from loop region, then we need change the size to end of actual buffer cache.
|
||||
if (dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) {
|
||||
bufferRangeSize = pThis->X_BufferCacheSize - bufferRangeStart;
|
||||
}
|
||||
}
|
||||
|
||||
DSoundBufferResizeUpdate(pHybridThis, pThis->EmuPlayFlags, hRet, bufferRangeStart, bufferRangeSize);
|
||||
|
||||
pThis->EmuBufferToggle = X_DSB_TOGGLE_DEFAULT;
|
||||
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(dwValue);
|
||||
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(currentPos);
|
||||
}
|
||||
|
||||
if (dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) {
|
||||
// Release from loop region.
|
||||
pThis->EmuPlayFlags &= ~X_DSBPLAY_LOOPING;
|
||||
}
|
||||
|
||||
if (dwStatus & DSBSTATUS_PLAYING && rtTimeStamp != 0LL) {
|
||||
|
@ -1650,6 +1605,9 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_StopEx)
|
|||
pThis->Xb_rtStopEx = 0LL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_TEST_CASE("Expected X_DSBSTOPEX_ENVELOPE");
|
||||
}
|
||||
}
|
||||
|
||||
return hRet;
|
||||
|
|
|
@ -27,11 +27,13 @@
|
|||
#define LOG_PREFIX CXBXR_MODULE::DSOUND
|
||||
|
||||
#include <imgui.h>
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui_internal.h" // For ImVec
|
||||
#include "core/common/imgui/ui.hpp"
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
#include <dsound.h>
|
||||
#include "DirectSoundGlobal.hpp"
|
||||
#include "DirectSoundInline.hpp" // For GetCurrentPosition, RegionCurrentLocation
|
||||
|
||||
Settings::s_audio g_XBAudio = { 0 };
|
||||
std::recursive_mutex g_DSoundMutex;
|
||||
|
@ -54,6 +56,105 @@ DWORD g_dwXbMemAllocated = 0;
|
|||
DWORD g_dwFree2DBuffers = 0;
|
||||
DWORD g_dwFree3DBuffers = 0;
|
||||
|
||||
DsBufferStreaming g_dsBufferStreaming;
|
||||
|
||||
void DrawAudioProgress(xbox::XbHybridDSBuffer* pHybrid, float scaleWidth, ImDrawList* drawList) {
|
||||
const auto& pBuffer = pHybrid->emuDSBuffer;
|
||||
|
||||
auto cursor = ImGui::GetCursorScreenPos();
|
||||
auto width = ImGui::GetContentRegionAvail().x;
|
||||
|
||||
DWORD rawCursor;
|
||||
HybridDirectSoundBuffer_GetCurrentPosition(pBuffer->EmuDirectSoundBuffer8, &rawCursor, nullptr, pBuffer->EmuFlags);
|
||||
float scale = (width / pBuffer->X_BufferCacheSize) / scaleWidth;
|
||||
float playCursor = rawCursor * scale;
|
||||
|
||||
bool isLooping = pBuffer->EmuPlayFlags & X_DSBPLAY_LOOPING;
|
||||
|
||||
auto colSpan = ImColor(0.8f, 0.1f, 0.1f, 0.3f);
|
||||
auto colRegion = ImColor(0.1f, 0.8f, 0.1, 0.3f);
|
||||
auto colRegionLoop = ImColor(0.1f, 0.2f, 0.8, 0.3f);
|
||||
auto colPlay = ImColor(0.8f, 0.8f, 0.1f, 0.6);
|
||||
float height = 8;
|
||||
|
||||
float sBuf = height * 0.4;
|
||||
float sReg = height * 1;
|
||||
float sPlay = height * 0.4;
|
||||
|
||||
// Buffer
|
||||
auto start = cursor + ImVec2(0, (height - sBuf) / 2);
|
||||
drawList->AddRectFilled(start, start + ImVec2(pBuffer->X_BufferCacheSize * scale, sBuf), colSpan, 0);
|
||||
|
||||
DWORD bufferRangeStart;
|
||||
DWORD bufferRangeSize;
|
||||
DSoundBufferRegionCurrentLocation(pHybrid, pBuffer->EmuPlayFlags, bufferRangeStart, bufferRangeSize);
|
||||
|
||||
bufferRangeStart *= scale;
|
||||
bufferRangeSize *= scale;
|
||||
|
||||
// Region
|
||||
start = cursor + ImVec2(bufferRangeStart, (height - sReg) / 2);
|
||||
drawList->AddRectFilled(start, start + ImVec2(bufferRangeSize, sReg), isLooping ? colRegionLoop : colRegion);
|
||||
|
||||
// Play area
|
||||
start = cursor + ImVec2(bufferRangeStart, (height - sPlay) / 2);
|
||||
drawList->AddRectFilled(start, start + ImVec2(playCursor, sPlay), colPlay);
|
||||
// Play cursor
|
||||
start = cursor + ImVec2(bufferRangeStart + playCursor, 0);
|
||||
drawList->AddLine(start, start + ImVec2(0, height), colPlay);
|
||||
|
||||
ImGui::Dummy(ImVec2(pBuffer->X_BufferCacheSize * scale, height));
|
||||
}
|
||||
|
||||
void DSound_DrawBufferVisualization(bool is_focus, bool* p_show, ImGuiWindowFlags input_handler) {
|
||||
if (!*p_show) return;
|
||||
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(IMGUI_MIN_DIST_SIDE, IMGUI_MIN_DIST_TOP), ImGuiCond_FirstUseEver, ImVec2(0.0f, 0.0f));
|
||||
ImGui::SetNextWindowSize(ImVec2(200, 275), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin("DSBuffer Visualization", p_show, input_handler)) {
|
||||
|
||||
static bool showPlayingOnly = true;
|
||||
ImGui::Checkbox("Show playing only", &showPlayingOnly);
|
||||
|
||||
static float bufferScale = 1;
|
||||
ImGui::PushItemWidth(100);
|
||||
ImGui::DragFloat("Audio Scale", &bufferScale, 1 / 1000.f, 1 / 24000.f, 1.f, "%.7f", ImGuiSliderFlags_Logarithmic);
|
||||
|
||||
if (ImGui::CollapsingHeader("Buffering Controls")) {
|
||||
ImGui::SliderInt("Stream interval (ms)", (int*)&g_dsBufferStreaming.streamInterval, 0, 50);
|
||||
ImGui::SliderInt("Stream ahead (ms)", (int*)&g_dsBufferStreaming.streamAhead, 0, 1000);
|
||||
ImGui::SliderFloat("Tweak copy offset", &g_dsBufferStreaming.tweakCopyOffset, -1, 1);
|
||||
}
|
||||
|
||||
if (ImGui::BeginChild("DSBuffer Graph", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
|
||||
int index = 0;
|
||||
for (const auto& i : g_pDSoundBufferCache) {
|
||||
if (showPlayingOnly) {
|
||||
DWORD dwStatus;
|
||||
auto hRet = i->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&dwStatus);
|
||||
if (hRet != DS_OK || !(dwStatus & DSBSTATUS_PLAYING)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Required to add controls inside the loop
|
||||
ImGui::PushID(index++);
|
||||
|
||||
DrawAudioProgress(i, bufferScale, drawList);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void DSound_PrintStats(bool is_focus, ImGuiWindowFlags input_handler, bool m_show_audio_stats)
|
||||
{
|
||||
DSoundMutexGuardLock;
|
||||
|
@ -149,7 +250,7 @@ void DSound_PrintStats(bool is_focus, ImGuiWindowFlags input_handler, bool m_sho
|
|||
ImGui::Text("Total active DSStream = %u", isActive);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,13 @@ extern DWORD g_dwXbMemAllocated;
|
|||
extern DWORD g_dwFree2DBuffers;
|
||||
extern DWORD g_dwFree3DBuffers;
|
||||
|
||||
struct DsBufferStreaming {
|
||||
DWORD streamInterval = 1;
|
||||
DWORD streamAhead = 50;
|
||||
float tweakCopyOffset = 0;
|
||||
};
|
||||
extern DsBufferStreaming g_dsBufferStreaming;
|
||||
|
||||
// size of DirectSound cache max size
|
||||
#define X_DIRECTSOUND_CACHE_MAX 0x800
|
||||
|
||||
|
@ -74,3 +81,6 @@ extern DWORD g_dwFree3DBuffers;
|
|||
|
||||
extern void DirectSoundDoWork_Buffer(xbox::LARGE_INTEGER& time);
|
||||
extern void DirectSoundDoWork_Stream(xbox::LARGE_INTEGER& time);
|
||||
extern void dsound_async_worker();
|
||||
extern void dsound_worker();
|
||||
extern uint64_t dsound_next(uint64_t now);
|
||||
|
|
|
@ -274,7 +274,7 @@ static inline void GeneratePCMFormat(
|
|||
}
|
||||
|
||||
if (DSBufferDesc.lpwfxFormat == nullptr) {
|
||||
CxbxrKrnlAbort("Unable to allocate DSBufferDesc.Xb_lpwfxFormat");
|
||||
CxbxrAbort("Unable to allocate DSBufferDesc.Xb_lpwfxFormat");
|
||||
}
|
||||
|
||||
if (Xb_lpwfxFormat != xbox::zeroptr) {
|
||||
|
@ -358,7 +358,7 @@ static inline void DSoundGenericUnlock(
|
|||
HRESULT hRet = pDSBuffer->Unlock(Host_lock.pLockPtr1, Host_lock.dwLockBytes1, Host_lock.pLockPtr2, Host_lock.dwLockBytes2);
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("DirectSoundBuffer Unlock Failed!");
|
||||
CxbxrAbort("DirectSoundBuffer Unlock Failed!");
|
||||
}
|
||||
|
||||
Host_lock.pLockPtr1 = nullptr;
|
||||
|
@ -485,7 +485,7 @@ static inline void DSoundBufferRelease(
|
|||
if (pDS3DBuffer != nullptr) {
|
||||
refCount = pDS3DBuffer->Release();
|
||||
if (refCount > 0) {
|
||||
CxbxrKrnlAbort("Nope, wasn't fully cleaned up.");
|
||||
CxbxrAbort("Nope, wasn't fully cleaned up.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -554,7 +554,7 @@ static inline void DSoundBufferResizeUpdate(
|
|||
hRet = pThis->EmuDirectSoundBuffer8->Lock(0, 0, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
|
||||
nullptr, nullptr, DSBLOCK_ENTIREBUFFER);
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("Unable to lock region buffer!");
|
||||
CxbxrAbort("Unable to lock region buffer!");
|
||||
}
|
||||
DSoundGenericUnlock(pThis->EmuFlags,
|
||||
pThis->EmuDirectSoundBuffer8,
|
||||
|
@ -569,7 +569,6 @@ static inline void DSoundBufferResizeUpdate(
|
|||
static inline void DSoundBufferRegionCurrentLocation(
|
||||
xbox::XbHybridDSBuffer* pHybridThis,
|
||||
DWORD dwPlayFlags,
|
||||
HRESULT &hRet,
|
||||
DWORD &Xb_dwStartOffset,
|
||||
DWORD &Xb_dwByteLength)
|
||||
{
|
||||
|
@ -608,7 +607,7 @@ static inline void DSoundBufferUpdate(
|
|||
DWORD Xb_dwByteLength;
|
||||
DWORD Xb_dwStartOffset;
|
||||
|
||||
DSoundBufferRegionCurrentLocation(pHybridThis, dwPlayFlags, hRet, Xb_dwStartOffset, Xb_dwByteLength);
|
||||
DSoundBufferRegionCurrentLocation(pHybridThis, dwPlayFlags, Xb_dwStartOffset, Xb_dwByteLength);
|
||||
|
||||
DSoundBufferResizeUpdate(pHybridThis, dwPlayFlags, hRet, Xb_dwStartOffset, Xb_dwByteLength);
|
||||
}
|
||||
|
@ -630,7 +629,7 @@ static inline void DSoundBufferRegenWithNewFormat(
|
|||
HRESULT hRet = pDSBuffer->GetStatus(&dwStatus);
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("Unable to retrieve current status for replace DS buffer!");
|
||||
CxbxrAbort("Unable to retrieve current status for replace DS buffer!");
|
||||
}
|
||||
|
||||
pDSBuffer->Stop();
|
||||
|
@ -638,7 +637,7 @@ static inline void DSoundBufferRegenWithNewFormat(
|
|||
hRet = pDSBuffer->GetCurrentPosition(&dwPlayCursor, nullptr);
|
||||
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("Unable to retrieve current position for replace DS buffer!");
|
||||
CxbxrAbort("Unable to retrieve current position for replace DS buffer!");
|
||||
}
|
||||
|
||||
// TODO: Untested if transfer buffer to new audio buffer is necessary.
|
||||
|
@ -922,7 +921,7 @@ static inline HRESULT HybridDirectSoundBuffer_Play(
|
|||
{
|
||||
|
||||
if (dwFlags & ~(X_DSBPLAY_LOOPING | X_DSBPLAY_FROMSTART | X_DSBPLAY_SYNCHPLAYBACK)) {
|
||||
CxbxrKrnlAbort("Unsupported Playing Flags");
|
||||
CxbxrAbort("Unsupported Playing Flags");
|
||||
}
|
||||
// rewind buffer
|
||||
if ((dwFlags & X_DSBPLAY_FROMSTART)) {
|
||||
|
|
|
@ -222,7 +222,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateStream)
|
|||
|
||||
hRet = xbox::EMUPATCH(DirectSoundCreate)(nullptr, &g_pDSound8, nullptr);
|
||||
if (hRet != DS_OK) {
|
||||
CxbxrKrnlAbort("Unable to initialize DirectSound!");
|
||||
CxbxrAbort("Unable to initialize DirectSound!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,7 +299,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateStream)
|
|||
EmuLog(LOG_LEVEL::WARNING, output.str().c_str());
|
||||
output.str("");
|
||||
output << static_cast<DS_RESULT>(hRet);
|
||||
CxbxrKrnlAbort("DSS: DSoundBufferCreate error: %s", output.str().c_str());
|
||||
CxbxrAbort("DSS: DSoundBufferCreate error: %s", output.str().c_str());
|
||||
}
|
||||
else {
|
||||
if (DSBufferDesc.dwFlags & DSBCAPS_CTRL3D) {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <iomanip> // For std::setfill and std::setw
|
||||
#include <filesystem>
|
||||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include "Intercept.hpp"
|
||||
#include "Patches.hpp"
|
||||
#include "common\util\hasher.h"
|
||||
#include "common/FilePaths.hpp"
|
||||
|
||||
#include <Shlwapi.h>
|
||||
#include <shlobj.h>
|
||||
|
@ -191,7 +193,7 @@ void CDECL EmuOutputMessage(xb_output_message mFlag,
|
|||
break;
|
||||
}
|
||||
case XB_OUTPUT_MESSAGE_ERROR: {
|
||||
CxbxrKrnlAbort("%s", message);
|
||||
CxbxrAbort("%s", message);
|
||||
break;
|
||||
}
|
||||
case XB_OUTPUT_MESSAGE_DEBUG:
|
||||
|
@ -204,6 +206,7 @@ void CDECL EmuOutputMessage(xb_output_message mFlag,
|
|||
|
||||
void CDECL EmuRegisterSymbol(const char* library_str,
|
||||
uint32_t library_flag,
|
||||
uint32_t xref_index,
|
||||
const char* symbol_str,
|
||||
uint32_t func_addr,
|
||||
uint32_t revision)
|
||||
|
@ -383,7 +386,7 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
|
|||
// Make sure the Symbol Cache directory exists
|
||||
std::string cachePath = g_DataFilePath + "\\SymbolCache\\";
|
||||
if (!std::filesystem::exists(cachePath) && !std::filesystem::create_directory(cachePath)) {
|
||||
CxbxrKrnlAbort("Couldn't create Cxbx-Reloaded SymbolCache folder!");
|
||||
CxbxrAbort("Couldn't create Cxbx-Reloaded SymbolCache folder!");
|
||||
}
|
||||
|
||||
// Hash the loaded XBE's header, use it as a filename
|
||||
|
@ -437,22 +440,6 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
|
|||
<< " -> " << functionName << "\n";
|
||||
std::printf(output.str().c_str());
|
||||
}
|
||||
|
||||
// Fix up Render state and Texture States
|
||||
if (g_SymbolAddresses.find("D3DDeferredRenderState") == g_SymbolAddresses.end()
|
||||
|| g_SymbolAddresses["D3DDeferredRenderState"] == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState was not found!");
|
||||
}
|
||||
|
||||
if (g_SymbolAddresses.find("D3DDeferredTextureState") == g_SymbolAddresses.end()
|
||||
|| g_SymbolAddresses["D3DDeferredTextureState"] == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredTextureState was not found!");
|
||||
}
|
||||
|
||||
if (g_SymbolAddresses.find("D3DDEVICE") == g_SymbolAddresses.end()
|
||||
|| g_SymbolAddresses["D3DDEVICE"] == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "D3DDEVICE was not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
670
src/core/hle/JVS/JVS.cpp
Normal file
670
src/core/hle/JVS/JVS.cpp
Normal file
|
@ -0,0 +1,670 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2019 Luke Usher <luke.usher@outlook.com>
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#define _XBOXKRNL_DEFEXTRN_
|
||||
|
||||
#define LOG_PREFIX CXBXR_MODULE::JVS
|
||||
|
||||
#undef FIELD_OFFSET // prevent macro redefinition warnings
|
||||
|
||||
#include "EmuShared.h"
|
||||
#include "common\Logging.h"
|
||||
#include "common\FilePaths.hpp"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\JVS\JVS.h"
|
||||
#include "core\hle\Intercept.hpp"
|
||||
#include "devices\chihiro\JvsIo.h"
|
||||
#include "devices\Xbox.h"
|
||||
#include <thread>
|
||||
|
||||
#pragma warning(disable:4244) // Silence mio compiler warnings
|
||||
#include <mio/mmap.hpp>
|
||||
#pragma warning(default:4244)
|
||||
|
||||
// Global variables used to store JVS related firmware/eeproms
|
||||
mio::mmap_sink g_BaseBoardQcFirmware; // QC Microcontroller firmware
|
||||
mio::mmap_sink g_BaseBoardScFirmware; // SC Microcontroller firmware
|
||||
mio::mmap_sink g_BaseBoardEeprom; // Config EEPROM
|
||||
mio::mmap_sink g_BaseBoardBackupMemory; // Backup Memory (high-scores, etc)
|
||||
|
||||
typedef struct _baseboard_state_t {
|
||||
// Switch 1: Horizontal Display, On = Vertical Display
|
||||
// Switch 2-3: D3D Resolution Configuraton
|
||||
// Switch 4: 0 = Hardware Vertex Processing, 1 = Software Vertex processing (Causes D3D to fail).. Horizontal frequency?
|
||||
// Switch 5: Unknown
|
||||
// Switch 6-8: Connected AV Pack flag
|
||||
bool DipSwitch[8];
|
||||
bool TestButton;
|
||||
bool ServiceButton;
|
||||
uint8_t JvsSense;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
// TODO: Make this configurable
|
||||
DipSwitch[0] = false;
|
||||
DipSwitch[1] = false;
|
||||
DipSwitch[2] = true;
|
||||
DipSwitch[3] = true;
|
||||
DipSwitch[4] = false;
|
||||
DipSwitch[5] = true;
|
||||
DipSwitch[6] = true;
|
||||
DipSwitch[7] = true;
|
||||
TestButton = false;
|
||||
ServiceButton = false;
|
||||
JvsSense = 0;
|
||||
}
|
||||
|
||||
uint8_t GetAvPack()
|
||||
{
|
||||
uint8_t avpack = 0;
|
||||
|
||||
// Dip Switches 6,7,8 combine to form the Av Pack ID
|
||||
// TODO: Verify the order, these might need to be reversed
|
||||
avpack &= ~((DipSwitch[5] ? 1 : 0) << 2);
|
||||
avpack &= ~((DipSwitch[6] ? 1 : 0) << 1);
|
||||
avpack &= ~ (DipSwitch[7] ? 1 : 0);
|
||||
|
||||
return avpack;
|
||||
}
|
||||
|
||||
uint8_t GetPINSA()
|
||||
{
|
||||
uint8_t PINSA = 0b11101111; // 1 = Off, 0 = On
|
||||
|
||||
// Dip Switches 1-3 are set on PINSA bits 0-2
|
||||
PINSA &= ~ (DipSwitch[0] ? 1 : 0);
|
||||
PINSA &= ~((DipSwitch[1] ? 1 : 0) << 1);
|
||||
PINSA &= ~((DipSwitch[2] ? 1 : 0) << 2);
|
||||
|
||||
// Bit 3 is currently unknown, so we don't modify that bit
|
||||
|
||||
// Dip Switches 4,5 are set on bits 4,5
|
||||
PINSA &= ~((DipSwitch[3] ? 1 : 0) << 4);
|
||||
PINSA &= ~((DipSwitch[4] ? 1 : 0) << 5);
|
||||
|
||||
// Bit 6 = Test, Bit 7 = Service
|
||||
PINSA &= ~((TestButton ? 1 : 0) << 6);
|
||||
PINSA &= ~((ServiceButton ? 1 : 0) << 7);
|
||||
|
||||
return PINSA;
|
||||
}
|
||||
|
||||
uint8_t GetPINSB()
|
||||
{
|
||||
// PINSB bits 0-1 represent the JVS Sense line
|
||||
return JvsSense;
|
||||
}
|
||||
|
||||
} baseboard_state_t;
|
||||
|
||||
baseboard_state_t ChihiroBaseBoardState = {};
|
||||
DWORD* g_pPINSA = nullptr; // Qc PINSA Register: Contains Filter Board DIP Switches + Test/Service buttons
|
||||
DWORD* g_pPINSB = nullptr; // Qc PINSB Register: Contains JVS Sense Pin state
|
||||
|
||||
bool JVS_LoadFile(std::string path, mio::mmap_sink& data)
|
||||
{
|
||||
FILE* fp = fopen(path.c_str(), "rb");
|
||||
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code error;
|
||||
data = mio::make_mmap_sink(path, error);
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JvsInputThread()
|
||||
{
|
||||
g_AffinityPolicy->SetAffinityOther(GetCurrentThread());
|
||||
|
||||
while (true) {
|
||||
// This thread is responsible for reading the emulated Baseboard state
|
||||
// and setting the correct internal variables
|
||||
ChihiroBaseBoardState.TestButton = GetAsyncKeyState(VK_F1);
|
||||
ChihiroBaseBoardState.ServiceButton = GetAsyncKeyState(VK_F2);
|
||||
|
||||
// Call into the Jvs I/O board update function
|
||||
g_pJvsIo->Update();
|
||||
|
||||
if (g_pPINSA != nullptr) {
|
||||
*g_pPINSA = ChihiroBaseBoardState.GetPINSA();
|
||||
}
|
||||
|
||||
if (g_pPINSB != nullptr) {
|
||||
*g_pPINSB = ChihiroBaseBoardState.GetPINSB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JVS_Init()
|
||||
{
|
||||
// Init Jvs IO board
|
||||
g_pJvsIo = new JvsIo(&ChihiroBaseBoardState.JvsSense);
|
||||
|
||||
std::string romPath = g_DataFilePath + std::string("\\EmuDisk\\Chihiro");
|
||||
std::string baseBoardQcFirmwarePath = "ic10_g24lc64.bin";
|
||||
std::string baseBoardScFirmwarePath = "pc20_g24lc64.bin";
|
||||
std::string baseBoardEepromPath = "ic11_24lc024.bin";
|
||||
std::string baseBoardBackupRamPath = "backup_ram.bin";
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardQcFirmwarePath).c_str(), g_BaseBoardQcFirmware)) {
|
||||
CxbxrAbort("Failed to load base board firmware: %s", baseBoardQcFirmwarePath.c_str());
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardScFirmwarePath).c_str(), g_BaseBoardScFirmware)) {
|
||||
CxbxrAbort("Failed to load base board qc firmware: %s", baseBoardScFirmwarePath.c_str());
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardEepromPath).c_str(), g_BaseBoardEeprom)) {
|
||||
CxbxrAbort("Failed to load base board EEPROM: %s", baseBoardEepromPath.c_str());
|
||||
}
|
||||
|
||||
// backup ram is a special case, we can create it automatically if it doesn't exist
|
||||
if (!std::filesystem::exists(romPath + "\\" + baseBoardBackupRamPath)) {
|
||||
FILE *fp = fopen((romPath + "\\" + baseBoardBackupRamPath).c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
CxbxrAbort("Could not create Backup File: %s", baseBoardBackupRamPath.c_str());
|
||||
}
|
||||
|
||||
// Create 128kb empty file for backup ram
|
||||
fseek(fp, (128 * 1024) - 1, SEEK_SET);
|
||||
fputc('\0', fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardBackupRamPath).c_str(), g_BaseBoardBackupMemory)) {
|
||||
CxbxrAbort("Failed to load base board BACKUP RAM: %s", baseBoardBackupRamPath.c_str());
|
||||
}
|
||||
|
||||
// Determine which version of JVS_SendCommand this title is using and derive the offset
|
||||
// TODO: Extract this into a function and also locate PINSB
|
||||
static int JvsSendCommandVersion = -1;
|
||||
g_pPINSA = nullptr;
|
||||
g_pPINSB = nullptr;
|
||||
|
||||
auto JvsSendCommandOffset1 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand");
|
||||
auto JvsSendCommandOffset2 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand2");
|
||||
auto JvsSendCommandOffset3 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand3");
|
||||
|
||||
if (JvsSendCommandOffset1) {
|
||||
JvsSendCommandVersion = 1;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset1 + 0x2A0);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
|
||||
if (JvsSendCommandOffset2) {
|
||||
JvsSendCommandVersion = 2;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset2 + 0x312);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
|
||||
if (JvsSendCommandOffset3) {
|
||||
JvsSendCommandVersion = 3;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x307);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
|
||||
if ((DWORD)g_pPINSA > XBE_MAX_VA) {
|
||||
// This was invalid, we must have the other varient of SendCommand3 (SEGABOOT)
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x302);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Set state to a sane initial default
|
||||
ChihiroBaseBoardState.Reset();
|
||||
|
||||
// Auto-Patch Chihiro Region Flag to match the desired game
|
||||
uint8_t ®ion = (uint8_t &)g_BaseBoardQcFirmware[0x1F00];
|
||||
auto regionFlags = g_MediaBoard->GetBootId().regionFlags;
|
||||
|
||||
// The region of the system can be converted to a game region flag by doing 1 << region
|
||||
// This gives a bitmask that can be ANDed with the BootID region flags to check the games support
|
||||
if ((regionFlags & (1 << region)) == 0) {
|
||||
// The region was not compatible, so we need to patch the region flag
|
||||
// This avoids "Error 05: This game is not acceptable by main board."
|
||||
// We use USA,EXPORT,JAPAN to make sure mutiple-language games default to English first
|
||||
if (regionFlags & MB_CHIHIRO_REGION_FLAG_USA) {
|
||||
region = 2;
|
||||
}
|
||||
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_EXPORT) {
|
||||
region = 3;
|
||||
}
|
||||
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_JAPAN) {
|
||||
region = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn the Chihiro/JVS Input Thread
|
||||
std::thread(JvsInputThread).detach();
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JVS_SendCommand)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD Command,
|
||||
DWORD a3,
|
||||
DWORD Length,
|
||||
DWORD a5,
|
||||
DWORD a6,
|
||||
DWORD a7,
|
||||
DWORD a8
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(Command)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a5)
|
||||
LOG_FUNC_ARG(a6)
|
||||
LOG_FUNC_ARG(a7)
|
||||
LOG_FUNC_ARG(a8)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardBackupMemory[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardBackupMemory[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardEeprom[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardEeprom[Offset], (void*)Buffer, Length);
|
||||
|
||||
std::error_code error;
|
||||
g_BaseBoardEeprom.sync(error);
|
||||
|
||||
if (error) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Couldn't sync EEPROM to disk");
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardQcFirmware[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardQcFirmware[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsNodeReceivePacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
PDWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG_OUT(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
// Receive the packet from the connected IO board
|
||||
uint8_t DeviceId = g_pJvsIo->GetDeviceId();
|
||||
|
||||
// TODO : "Number of packets received" below might imply multiple packets might need receiving here...
|
||||
uint16_t payloadSize = (uint16_t)g_pJvsIo->ReceivePacket(&Buffer[6]);
|
||||
if (payloadSize > 0) {
|
||||
Buffer[0] = 0; // Empty header byte, ignored
|
||||
Buffer[1] = 1; // Number of packets received
|
||||
Buffer[2] = DeviceId;
|
||||
Buffer[3] = 0; // Unused
|
||||
|
||||
*Length = payloadSize + 6;
|
||||
|
||||
// Write the payload size header field
|
||||
*((uint16_t*)&Buffer[4]) = payloadSize; // Packet Length (bytes 4-5)
|
||||
// TODO : Prevent little/big endian issues here by explicitly setting Buffer[4] and Buffer[5]
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsNodeSendPacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
// Buffer contains two opening bytes, '00' and 'XX', where XX is the number of JVS packets to send
|
||||
// Each JVS packet is prepended with a '00' byte, the rest of the packet is as-per the JVS I/O standard.
|
||||
|
||||
// Ignore Buffer[0] (should be 0x00)
|
||||
unsigned packetCount = Buffer[1];
|
||||
uint8_t* packetPtr = &Buffer[2]; // First JVS packet starts at offset 2;
|
||||
|
||||
for (unsigned i = 0; i < packetCount; i++) {
|
||||
// Skip the separator byte (should be 0x00)
|
||||
packetPtr++;
|
||||
|
||||
// Send the packet to the connected I/O board
|
||||
size_t bytes = g_pJvsIo->SendPacket(packetPtr);
|
||||
|
||||
// Set packetPtr to the next packet
|
||||
packetPtr += bytes;
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
// Binary Coded Decimal to Decimal conversion
|
||||
uint8_t BcdToUint8(uint8_t value)
|
||||
{
|
||||
return value - 6 * (value >> 4);
|
||||
}
|
||||
|
||||
uint8_t Uint8ToBcd(uint8_t value)
|
||||
{
|
||||
return value + 6 * (value / 10);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Read)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime* pTime,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG_OUT(time)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
time_t hostTime;
|
||||
struct tm* hostTimeInfo;
|
||||
time(&hostTime);
|
||||
hostTimeInfo = localtime(&hostTime);
|
||||
|
||||
memset(pTime, 0, sizeof(JvsRTCTime));
|
||||
|
||||
pTime->day = Uint8ToBcd(hostTimeInfo->tm_mday);
|
||||
pTime->month = Uint8ToBcd(hostTimeInfo->tm_mon + 1); // Chihiro month counter stats at 1
|
||||
pTime->year = Uint8ToBcd(hostTimeInfo->tm_year - 100); // Chihiro starts counting from year 2000
|
||||
|
||||
pTime->hour = Uint8ToBcd(hostTimeInfo->tm_hour);
|
||||
pTime->minute = Uint8ToBcd(hostTimeInfo->tm_min);
|
||||
pTime->second = Uint8ToBcd(hostTimeInfo->tm_sec);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Write)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime* pTime,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG_OUT(time)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardScFirmware[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardScFirmware[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScSendMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScSendRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue