mirror of
https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git
synced 2025-04-02 11:11:52 -04:00
Compare commits
413 commits
CI-6e68433
...
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 | ||
|
283331412a | ||
|
6078193fec | ||
|
922bd4e9d2 | ||
|
a7cf67cd71 | ||
|
c1fb2d665b | ||
|
3e5272d91a | ||
|
e77404472e | ||
|
ef5c9ad4fb | ||
|
0c28aca19c | ||
|
7c630d6104 | ||
|
29e1dc865d | ||
|
c9fe07ab98 | ||
|
0b2c0a2e33 | ||
|
66b83d98e4 | ||
|
caf7927445 | ||
|
c658777645 | ||
|
2c1f5bd430 | ||
|
4645d42130 | ||
|
41c9d7a352 | ||
|
84ea340da1 | ||
|
6d08f2a99b | ||
|
886bef5c8a | ||
|
4457d110bf | ||
|
8e424759df | ||
|
f7042be933 | ||
|
7791c9dd78 | ||
|
c76de904dc | ||
|
5a01ce5297 | ||
|
1886819a35 | ||
|
6a9e242fad | ||
|
9092f7bbe6 | ||
|
006af26a6e | ||
|
b748c5f61a | ||
|
94f02583ba | ||
|
ac5289d83a | ||
|
51c3f37596 | ||
|
ca5a1aaa71 | ||
|
23c72d825e | ||
|
4f26ab927f | ||
|
309975da61 | ||
|
be56eac811 | ||
|
326a5bb714 | ||
|
3bdd689e03 | ||
|
9b65924898 | ||
|
b863432904 | ||
|
32fa33ddab | ||
|
629d6d2054 | ||
|
b52f5655e4 | ||
|
35072da2ea | ||
|
b804ed1f03 | ||
|
ac68fd481c | ||
|
9b02cac7ad | ||
|
b405153c7e | ||
|
bc6c017b7a | ||
|
9bf21223f2 | ||
|
2c46ea3d98 | ||
|
cc6041d2de | ||
|
5e26b938fb | ||
|
f3dc44ebae | ||
|
5692f79d78 | ||
|
093c23d5bc | ||
|
249d6b2169 | ||
|
0e5e791a55 | ||
|
16ea4519c8 | ||
|
93b5e88754 | ||
|
08ee4a15d3 | ||
|
cefea8ad83 | ||
|
094256ef43 | ||
|
8de8f411c1 | ||
|
0c2b7b4220 | ||
|
a5fa40956a | ||
|
edffb3a128 | ||
|
0fb2e6208d | ||
|
e1a8391170 | ||
|
b11cb57b0b | ||
|
50d50288a5 | ||
|
9439b9f54c | ||
|
de16fe345a | ||
|
5e7c6a082f | ||
|
a6e17bc4de | ||
|
660e6bca1e | ||
|
b4dadb1dff | ||
|
e37bb218ba | ||
|
30956c1044 | ||
|
cb3330a9e4 | ||
|
d42e3ee271 | ||
|
cf8ff008a8 | ||
|
74c8b0cde1 | ||
|
390466e615 |
209 changed files with 12694 additions and 6458 deletions
8
.github/ISSUE_TEMPLATE/issue-template.md
vendored
8
.github/ISSUE_TEMPLATE/issue-template.md
vendored
|
@ -4,14 +4,16 @@ about: Issue template.
|
|||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
ISSUES NOT UTILIZING THE TEMPLATE BELOW WILL BE CLOSED!
|
||||
-->
|
||||
<!--
|
||||
Please read https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/blob/master/README.md and https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/wiki/Frequently-Asked-Questions-(FAQ) before opening an issue.
|
||||
Remember, the GitHub Issue Tracker is not the place to ask for support or to submit game compatibility https://github.com/Cxbx-Reloaded/game-compatibility/blob/master/README.md reports.
|
||||
You must use our forum on Discord https://discord.gg/26Xjx23 for that.
|
||||
Remember, the GitHub Issue Tracker is not the place to ask for support. You must use our forum on Discord https://discord.gg/26Xjx23 for that.
|
||||
Compatibility Reports should be submitted at the website: https://cxbx-reloaded.co.uk
|
||||
Otherwise, for any other emulation/general issues like crashes when a controller is connected, or regressions across several titles, feel free to report your issue here.
|
||||
-->
|
||||
|
||||
|
@ -40,4 +42,4 @@ Please provide your system configuration
|
|||
### Additional Information (if any):
|
||||
<!--
|
||||
Anything else you deem to be important
|
||||
-->
|
||||
-->
|
||||
|
|
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
|
||||
|
|
14
.gitmodules
vendored
14
.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
|
||||
|
@ -35,3 +35,15 @@
|
|||
path = import/SDL2
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
shallow = true
|
||||
[submodule "import/libusb"]
|
||||
path = import/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
|
||||
|
|
113
CMakeLists.txt
113
CMakeLists.txt
|
@ -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})
|
||||
|
@ -41,6 +39,11 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libtom")
|
|||
|
||||
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
|
||||
|
@ -51,16 +54,20 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/imgui")
|
|||
|
||||
# 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"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.h"
|
||||
|
@ -68,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"
|
||||
|
@ -82,14 +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"
|
||||
|
@ -100,8 +110,10 @@ file (GLOB CXBXR_HEADER_GUIv1
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/Button.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgDukeControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.h"
|
||||
|
@ -120,12 +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/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"
|
||||
|
@ -133,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"
|
||||
|
@ -141,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"
|
||||
|
@ -166,15 +177,15 @@ 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"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PoolManager.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/VMManager.h"
|
||||
|
@ -182,6 +193,8 @@ file (GLOB CXBXR_HEADER_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFile.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFS.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuNtDll.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.h"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/EEPROMDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.h"
|
||||
|
@ -220,39 +233,47 @@ 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"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/XInputPad.cpp"
|
||||
"${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"
|
||||
)
|
||||
|
@ -262,8 +283,10 @@ file (GLOB CXBXR_SOURCE_GUIv1
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/Button.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgDukeControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.cpp"
|
||||
|
@ -288,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"
|
||||
|
@ -304,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"
|
||||
|
@ -326,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"
|
||||
|
@ -340,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"
|
||||
|
@ -358,7 +376,11 @@ file (GLOB CXBXR_SOURCE_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFile.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFS.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuNtDll.cpp"
|
||||
"${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"
|
||||
|
@ -417,10 +439,14 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/cxbxr-emu")
|
|||
set(cxbxr_INSTALL_files "COPYING" "README.md")
|
||||
|
||||
# Cxbx-Reloaded project with third-party libraries
|
||||
set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymbolDatabase libtommath libtomcrypt imgui
|
||||
set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymbolDatabase libtommath libtomcrypt imgui libusb
|
||||
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)
|
||||
|
@ -448,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")
|
||||
|
||||
|
|
49
README.md
49
README.md
|
@ -1,5 +1,5 @@
|
|||
# Cxbx-Reloaded - Original Xbox Emulator
|
||||
[](https://img.shields.io/badge/License-GPL%20v2-blue.svg)
|
||||
[](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/blob/master/COPYING)
|
||||
[](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/actions?query=event%3Apush+workflow%3A%22GitHub+CI%22)
|
||||
[](https://discord.gg/26Xjx23)
|
||||
|
||||
|
@ -8,29 +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!
|
||||
* 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
|
||||
|
@ -42,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.
|
||||
|
@ -73,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:
|
||||
|
||||
|
@ -83,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
|
||||
|
@ -98,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**.
|
||||
|
@ -115,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 8b000c0ca7f20d88dddd95e80ad257ba2a0cffaa
|
||||
Subproject commit 774111351210e6f340246d6fb32741b09708f381
|
2
import/cs_x86
vendored
2
import/cs_x86
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 5fb27bcf6745c35d8889e48ead4314022b8ec931
|
||||
Subproject commit f8a95b7afa963c90b01a9e7cd758346f95c90f50
|
1
import/libusb
vendored
Submodule
1
import/libusb
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
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)
|
||||
|
@ -12,9 +12,10 @@ include_directories(
|
|||
"${CXBXR_ROOT_DIR}/src/common"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Win32"
|
||||
"${CXBXR_ROOT_DIR}/import/OpenXDK/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/distorm/include"
|
||||
"${CXBXR_ROOT_DIR}/import/glew-2.0.0/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb"
|
||||
"${CXBXR_ROOT_DIR}/import/simpleini"
|
||||
"${CXBXR_ROOT_DIR}/import/winpcap/Include"
|
||||
"${CXBXR_ROOT_DIR}/import/xxHash"
|
||||
|
@ -41,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
|
||||
|
@ -64,7 +68,7 @@ XXH_INLINE_ALL
|
|||
)
|
||||
|
||||
file (GLOB RESOURCES
|
||||
|
||||
|
||||
"${CXBXR_ROOT_DIR}/CONTRIBUTORS"
|
||||
"${CXBXR_ROOT_DIR}/COPYING"
|
||||
"${CXBXR_ROOT_DIR}/README.md"
|
||||
|
@ -77,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})
|
||||
|
@ -102,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}
|
||||
)
|
||||
|
||||
|
@ -117,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 \
|
||||
|
@ -135,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
|
||||
|
@ -146,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 \
|
||||
|
@ -156,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-"
|
||||
|
@ -164,7 +150,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -184,7 +170,7 @@ set(WINS_LIB
|
|||
comctl32
|
||||
XINPUT9_1_0
|
||||
Iphlpapi
|
||||
wpcap
|
||||
Dwmapi
|
||||
)
|
||||
|
||||
target_link_libraries(cxbx
|
||||
|
@ -193,6 +179,8 @@ target_link_libraries(cxbx
|
|||
libtomcrypt
|
||||
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)
|
||||
|
@ -15,9 +15,10 @@ include_directories(
|
|||
"${CXBXR_ROOT_DIR}/src/common"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Win32"
|
||||
"${CXBXR_ROOT_DIR}/import/OpenXDK/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/distorm/include"
|
||||
"${CXBXR_ROOT_DIR}/import/glew-2.0.0/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb"
|
||||
"${CXBXR_ROOT_DIR}/import/simpleini"
|
||||
"${CXBXR_ROOT_DIR}/import/winpcap/Include"
|
||||
"${CXBXR_ROOT_DIR}/import/xxHash"
|
||||
|
@ -47,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
|
||||
|
@ -127,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 \
|
||||
|
@ -140,7 +144,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -169,6 +173,9 @@ target_link_libraries(cxbxr-emu
|
|||
libtomcrypt
|
||||
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)
|
||||
|
|
51
projects/libusb/CMakeLists.txt
Normal file
51
projects/libusb/CMakeLists.txt
Normal file
|
@ -0,0 +1,51 @@
|
|||
cmake_minimum_required (VERSION 3.8)
|
||||
project(libusb LANGUAGES CXX)
|
||||
# Since libusb doesn't have CMake, we'll make an interface project here.
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
add_compile_definitions(
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/msvc"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb")
|
||||
|
||||
file (GLOB HEADERS
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/libusb.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/libusbi.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/version.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/version_nano.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/msvc/config.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/events_windows.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/threads_windows.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_common.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_usbdk.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_winusb.h"
|
||||
)
|
||||
|
||||
file (GLOB SOURCES
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/core.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/descriptor.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/hotplug.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/io.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/strerror.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/sync.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/events_windows.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/threads_windows.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_common.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_usbdk.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_winusb.c"
|
||||
)
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/import/libusb/libusb PREFIX header FILES ${HEADERS})
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/import/libusb/libusb PREFIX source FILES ${SOURCES})
|
||||
|
||||
add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES})
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC "${CXBXR_ROOT_DIR}/import/libusb"
|
||||
)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
/*! version string dependent on trace flag */
|
||||
#ifndef _DEBUG_TRACE
|
||||
const char* CxbxVersionStr = _GIT_VERSION " (" __DATE__ ")";
|
||||
const char *CxbxrHashBuild = _GIT_VERSION;
|
||||
#else
|
||||
const char* CxbxVersionStr = _GIT_VERSION "-Trace (" __DATE__ ")";
|
||||
const char *CxbxrHashBuild = _GIT_VERSION "-Trace";
|
||||
#endif
|
||||
|
||||
static constexpr const char *GitVersionStr = _GIT_VERSION;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
extern const char* CxbxVersionStr;
|
||||
extern const char *CxbxrHashBuild;
|
||||
|
||||
// Note: GitVersionMaxLength should be large enough to accomodate the longest git version string we can practically expect to have. This is necessary
|
||||
// to avoid possible mismatches in the string length which can happen if the user mixes different cxbxr versions
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
CxbxKrnlCleanup("%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;
|
||||
}
|
||||
}
|
53
src/common/FilePaths.hpp
Normal file
53
src/common/FilePaths.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
// 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
|
||||
|
||||
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.
|
||||
void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence);
|
||||
|
||||
// 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);
|
||||
|
||||
// NOTE: Do NOT modify g_<custom>BasePath variables after this call!
|
||||
void CxbxrInitFilePaths();
|
||||
|
||||
// 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();
|
|
@ -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);
|
||||
|
|
|
@ -82,6 +82,7 @@ const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = {
|
|||
"XMO ",
|
||||
"RINP ",
|
||||
"JVS ",
|
||||
"LIBUSB ",
|
||||
"KRNL ",
|
||||
"LOG ",
|
||||
"XBOX ",
|
||||
|
@ -159,7 +160,7 @@ inline void EmuLogOutputEx(const CXBXR_MODULE cxbxr_module, const LOG_LEVEL leve
|
|||
}
|
||||
|
||||
// print out a custom message to the console or kernel debug log file
|
||||
void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...)
|
||||
void EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...)
|
||||
{
|
||||
if (szWarningMessage == NULL) {
|
||||
return;
|
||||
|
@ -180,7 +181,7 @@ void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWa
|
|||
}
|
||||
}
|
||||
|
||||
void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...)
|
||||
void EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...)
|
||||
{
|
||||
if (szWarningMessage == NULL) {
|
||||
return;
|
||||
|
@ -312,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;
|
||||
|
@ -328,11 +329,11 @@ PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, con
|
|||
va_list argp;
|
||||
va_start(argp, message);
|
||||
// allocate predicted buffer size then write to buffer afterward.
|
||||
std::vector<char> Buffer(1+std::vsnprintf(nullptr, 0, message, argp));
|
||||
std::string Buffer(1+std::vsnprintf(nullptr, 0, message, argp), '\0');
|
||||
vsnprintf(Buffer.data(), Buffer.size(), message, argp);
|
||||
va_end(argp);
|
||||
|
||||
EmuLogOutputEx(cxbxr_module, level, "Popup : %s", Buffer);
|
||||
EmuLogOutputEx(cxbxr_module, level, "Popup : %s", Buffer.c_str());
|
||||
|
||||
// If user is using exclusive fullscreen, we need to refrain all popups.
|
||||
if (g_disablePopupMessages) {
|
||||
|
@ -395,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)
|
||||
|
|
|
@ -90,6 +90,7 @@ typedef enum class _CXBXR_MODULE: unsigned int {
|
|||
XMO,
|
||||
RINP,
|
||||
JVS,
|
||||
LIBUSB,
|
||||
// kernel
|
||||
KRNL,
|
||||
LOG,
|
||||
|
@ -121,8 +122,8 @@ extern std::atomic_int g_CurrentLogLevel;
|
|||
extern std::atomic_bool g_CurrentLogPopupTestCase;
|
||||
|
||||
// print out a log message to the console or kernel debug log file if level is high enough
|
||||
void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...);
|
||||
void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...);
|
||||
void EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...);
|
||||
void EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...);
|
||||
|
||||
#define EmuLog(level, fmt, ...) EmuLogEx(LOG_PREFIX, level, fmt, ##__VA_ARGS__)
|
||||
|
||||
|
@ -224,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
|
||||
|
@ -293,8 +315,8 @@ constexpr const char* remove_prefix(const char* str, const char *prefix) {
|
|||
return (str_skip_prefix(str, prefix) == str + str_length(prefix)) ? str_skip_prefix(str, prefix) : str;
|
||||
}
|
||||
|
||||
static const char* xbox_prefix = "xbox::";
|
||||
static const char* emupatch_prefix = "EmuPatch_"; // See #define EMUPATCH
|
||||
static constexpr const char* xbox_prefix = "xbox::";
|
||||
static constexpr const char* emupatch_prefix = "EmuPatch_"; // See #define EMUPATCH
|
||||
|
||||
constexpr const char* remove_emupatch_prefix(const char* str) {
|
||||
// return an empty string when str isn't given
|
||||
|
|
|
@ -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
|
||||
|
@ -496,7 +501,7 @@ bool Settings::LoadConfig()
|
|||
continue;
|
||||
}
|
||||
|
||||
auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
|
||||
const auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
|
||||
for (int i = 0; i < num_buttons; i++) {
|
||||
char control_name[XBOX_BUTTON_NAME_LENGTH];
|
||||
std::sprintf(control_name, sect_input_profiles.control, ctrl_names[i]);
|
||||
|
@ -516,6 +521,10 @@ bool Settings::LoadConfig()
|
|||
lambda(dev_num_buttons[device], button_sbc_names);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
lambda(dev_num_buttons[device], button_lightgun_names);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -541,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 ===========
|
||||
|
||||
|
@ -597,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 =========
|
||||
|
@ -668,7 +678,7 @@ bool Settings::Save(std::string file_path)
|
|||
continue;
|
||||
}
|
||||
|
||||
auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
|
||||
const auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
|
||||
for (int i = 0; i < num_buttons; i++) {
|
||||
char control_name[XBOX_BUTTON_NAME_LENGTH];
|
||||
std::sprintf(control_name, sect_input_profiles.control, ctrl_names[i]);
|
||||
|
@ -688,6 +698,9 @@ bool Settings::Save(std::string file_path)
|
|||
lambda(dev_num_buttons[device], button_sbc_names);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
lambda(dev_num_buttons[device], button_lightgun_names);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -728,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 =========
|
||||
|
||||
|
@ -785,19 +801,21 @@ void Settings::SyncToEmulator()
|
|||
g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_BOTTOM], i, SLOT_BOTTOM);
|
||||
if (m_input_port[i].Type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
g_EmuShared->SetInputDevNameSettings(m_input_port[i].DeviceName.c_str(), i);
|
||||
auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(),
|
||||
m_input_profiles[m_input_port[i].Type].end(), [this, i](const auto& profile) {
|
||||
if (profile.ProfileName == m_input_port[i].ProfileName) {
|
||||
return true;
|
||||
if (m_input_port[i].Type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)) {
|
||||
auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(),
|
||||
m_input_profiles[m_input_port[i].Type].end(), [this, i](const auto &profile) {
|
||||
if (profile.ProfileName == m_input_port[i].ProfileName) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (it != m_input_profiles[m_input_port[i].Type].end()) {
|
||||
char controls_name[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
for (int index = 0; index < dev_num_buttons[m_input_port[i].Type]; index++) {
|
||||
strncpy(controls_name[index], it->ControlList[index].c_str(), 30);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (it != m_input_profiles[m_input_port[i].Type].end()) {
|
||||
char controls_name[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
for (int index = 0; index < dev_num_buttons[m_input_port[i].Type]; index++) {
|
||||
strncpy(controls_name[index], it->ControlList[index].c_str(), 30);
|
||||
g_EmuShared->SetInputBindingsSettings(controls_name, dev_num_buttons[m_input_port[i].Type], i);
|
||||
}
|
||||
g_EmuShared->SetInputBindingsSettings(controls_name, dev_num_buttons[m_input_port[i].Type], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -809,7 +827,7 @@ void Settings::SyncToEmulator()
|
|||
g_EmuShared->SetHackSettings(&m_hacks);
|
||||
|
||||
// register data location setting
|
||||
g_EmuShared->SetStorageLocation(GetDataLocation().c_str());
|
||||
g_EmuShared->SetDataLocation(GetDataLocation().c_str());
|
||||
|
||||
// reset title mount path
|
||||
g_EmuShared->SetTitleMountPath("");
|
||||
|
@ -991,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);
|
||||
}
|
||||
|
@ -1018,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"
|
||||
|
@ -58,7 +57,7 @@ typedef enum _CXBX_DATA {
|
|||
// ******************************************************************
|
||||
// * Define number of integers required to store logging settings
|
||||
// ******************************************************************
|
||||
#define NUM_INTEGERS_LOG 2
|
||||
#define NUM_INTEGERS_LOG 3
|
||||
|
||||
enum {
|
||||
LLE_NONE = 0,
|
||||
|
@ -103,13 +102,13 @@ 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;
|
||||
int Reserved99[10] = { 0 };
|
||||
} m_core;
|
||||
static_assert(sizeof(s_core) == 0x24C, assert_check_shared_memory(s_core));
|
||||
static_assert(sizeof(s_core) == 0x250, assert_check_shared_memory(s_core));
|
||||
|
||||
// Video settings
|
||||
struct s_video {
|
||||
|
|
|
@ -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,172 +95,94 @@ 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
|
||||
uint64_t HostClockFrequency;
|
||||
// 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
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
uint64_t Ret = Muldiv64(li.QuadPart, SCALE_S_IN_NS, (uint32_t)HostClockFrequency);
|
||||
#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)
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period)
|
||||
{
|
||||
Timer->ExpireTime_MS.store(Expire_ms);
|
||||
LARGE_INTEGER currentQPC;
|
||||
QueryPerformanceCounter(¤tQPC);
|
||||
|
||||
// Scale frequency with overflow avoidance, like in std::chrono
|
||||
// https://github.com/microsoft/STL/blob/6d2f8b0ed88ea6cba26cc2151f47f678442c1663/stl/inc/chrono#L703
|
||||
const int64_t currentTime = currentQPC.QuadPart - HostQPCStartTime;
|
||||
const int64_t whole = (currentTime / HostQPCFrequency) * Period;
|
||||
const int64_t part = (currentTime % HostQPCFrequency) * Period / HostQPCFrequency;
|
||||
|
||||
return whole + part;
|
||||
}
|
||||
|
||||
// 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
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
HostClockFrequency = freq.QuadPart;
|
||||
#elif __linux__
|
||||
ClockFrequency = 0;
|
||||
#else
|
||||
#error "Unsupported OS"
|
||||
#endif
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
||||
void ScaledPerformanceCounter::Reset(uint32_t frequency)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
m_frequencyFactor = Muldiv64(HostClockFrequency, SCALE_S_IN_NS, frequency);
|
||||
|
||||
LARGE_INTEGER tsc;
|
||||
QueryPerformanceCounter(&tsc);
|
||||
m_lastQPC = tsc.QuadPart;
|
||||
|
||||
m_currentCount = 0;
|
||||
m_currentRemainder = 0;
|
||||
}
|
||||
|
||||
uint64_t ScaledPerformanceCounter::Tick()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
LARGE_INTEGER qpc;
|
||||
QueryPerformanceCounter(&qpc);
|
||||
|
||||
int64_t lastQpc = std::exchange(m_lastQPC, qpc.QuadPart);
|
||||
qpc.QuadPart -= lastQpc;
|
||||
qpc.QuadPart *= SCALE_S_IN_NS;
|
||||
qpc.QuadPart += m_currentRemainder;
|
||||
uint64_t quotient = qpc.QuadPart / m_frequencyFactor;
|
||||
uint64_t remainder = qpc.QuadPart % m_frequencyFactor;
|
||||
|
||||
m_currentRemainder = remainder;
|
||||
return m_currentCount += quotient;
|
||||
}
|
||||
|
|
|
@ -40,49 +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 uint64_t HostClockFrequency;
|
||||
|
||||
/* 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();
|
||||
extern int64_t HostQPCFrequency;
|
||||
|
||||
void timer_init();
|
||||
uint64_t get_now();
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
|
||||
void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
|
||||
|
||||
// A stateful replacement for QueryPerformanceCounter, ticking at an arbitrary frequency
|
||||
// Thread-safe and designed to avoid overflows at all cost
|
||||
class ScaledPerformanceCounter
|
||||
{
|
||||
public:
|
||||
ScaledPerformanceCounter() = default;
|
||||
void Reset(uint32_t frequency);
|
||||
uint64_t Tick();
|
||||
|
||||
private:
|
||||
std::mutex m_mutex;
|
||||
|
||||
uint64_t m_frequencyFactor;
|
||||
int64_t m_lastQPC;
|
||||
|
||||
uint64_t m_currentCount;
|
||||
uint64_t m_currentRemainder;
|
||||
};
|
||||
|
||||
#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));
|
||||
}
|
||||
|
||||
|
@ -78,7 +79,6 @@ LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA
|
|||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
if (wParam & MK_SHIFT) {
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
|
||||
}
|
||||
else if (!(wParam & ~MK_RBUTTON)) {
|
||||
button->ClearText();
|
||||
|
@ -99,7 +99,7 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
|
|||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
// Remove the window subclass when this window is destroyed
|
||||
// Remove the window subclass when this window is destroyed
|
||||
case WM_NCDESTROY: {
|
||||
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
|
||||
}
|
||||
|
@ -107,14 +107,30 @@ 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);
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
// Remove the window subclass when this window is destroyed
|
||||
case WM_NCDESTROY: {
|
||||
RemoveWindowSubclass(hWnd, ButtonLightgunSubclassProc, uIdSubclass);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_RBUTTONDOWN: {
|
||||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
button->ClearText();
|
||||
static_cast<LightgunInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <Commctrl.h>
|
||||
#include <string>
|
||||
|
||||
#define LIGHTGUN_NUM_BUTTONS 17
|
||||
#define XBOX_CTRL_NUM_BUTTONS 25
|
||||
#define SBC_NUM_BUTTONS 56
|
||||
#define HIGHEST_NUM_BUTTONS SBC_NUM_BUTTONS
|
||||
|
@ -50,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:
|
||||
|
@ -62,3 +63,4 @@ private:
|
|||
|
||||
LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
|
||||
LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
|
||||
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
|
||||
|
|
|
@ -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,13 +25,15 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#include <algorithm>
|
||||
#include "Button.h"
|
||||
#include "InputManager.h"
|
||||
#include "layout_xbox_device.h"
|
||||
#include "gui/resource/ResCxbx.h"
|
||||
|
||||
|
||||
static char *tooltip_text = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
|
||||
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)
|
||||
{
|
||||
|
@ -45,7 +47,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
|||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(button_xbox_ctrl_id); i++) {
|
||||
m_buttons.push_back(new Button(button_xbox_ctrl_id[i], i, hwnd, wnd));
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text);
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
|
||||
|
||||
// Install the subclass for the button control
|
||||
SetWindowSubclass(GetDlgItem(hwnd, button_xbox_ctrl_id[i]), ButtonDukeSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
|
||||
|
@ -56,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);
|
||||
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]));
|
||||
|
@ -64,6 +66,17 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
|||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN): {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(button_lightgun_id); i++) {
|
||||
m_buttons.push_back(new Button(button_lightgun_id[i], i, hwnd, wnd));
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_no_toggle);
|
||||
|
||||
// Install the subclass for the button control
|
||||
SetWindowSubclass(GetDlgItem(hwnd, button_lightgun_id[i]), ButtonLightgunSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,3 +133,6 @@ void EmuDevice::CreateTooltipWindow()
|
|||
SendMessage(m_tooltip_hwnd, TTM_SETMAXTIPWIDTH, 0, 500);
|
||||
SendMessage(m_tooltip_hwnd, TTM_SETDELAYTIME, TTDT_AUTOPOP, 15000);
|
||||
}
|
||||
|
||||
template void EmuDevice::BindDefault(const std::array<const char*, XBOX_CTRL_NUM_BUTTONS>& arr);
|
||||
template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);
|
||||
|
|
|
@ -53,4 +53,5 @@ private:
|
|||
HWND m_tooltip_hwnd;
|
||||
};
|
||||
|
||||
template void EmuDevice::BindDefault(const std::array<const char *, XBOX_CTRL_NUM_BUTTONS> &arr);
|
||||
extern template void EmuDevice::BindDefault(const std::array<const char *, XBOX_CTRL_NUM_BUTTONS> &arr);
|
||||
extern template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);
|
||||
|
|
|
@ -53,8 +53,8 @@ std::string GetInputDeviceName(int dev_type)
|
|||
str = "MS Gamepad S";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
str = "Light gun";
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
str = "EMS TopGun II";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
|
@ -77,6 +77,14 @@ std::string GetInputDeviceName(int dev_type)
|
|||
str = "Arcade joystick";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
|
||||
str = "Passthrough steel battalion controller";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
str = "Passthrough original xbox gamepad";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID):
|
||||
str = "None";
|
||||
break;
|
||||
|
@ -109,7 +117,7 @@ std::string PortUserFormat(std::string_view port)
|
|||
void PortStr2Int(std::string_view port, int *port_num, int *slot)
|
||||
{
|
||||
*slot = PORT_INVALID;
|
||||
auto &ret = std::from_chars(port.data(), port.data() + port.size(), *port_num);
|
||||
auto ret = std::from_chars(port.data(), port.data() + port.size(), *port_num);
|
||||
assert(ret.ec != std::errc::invalid_argument);
|
||||
if (ret.ptr != port.data() + port.size()) {
|
||||
++ret.ptr;
|
||||
|
@ -170,7 +178,7 @@ const auto InputDevice::FindPort(std::string_view Port) const
|
|||
});
|
||||
}
|
||||
|
||||
void InputDevice::SetPort(std::string_view Port, bool Connect)
|
||||
void InputDevice::SetPort2(std::string_view Port, bool Connect)
|
||||
{
|
||||
if (Connect) {
|
||||
m_XboxPort.emplace_back(Port);
|
||||
|
|
|
@ -49,13 +49,16 @@ typedef enum class _XBOX_INPUT_DEVICE : int {
|
|||
DEVICE_INVALID = -1,
|
||||
MS_CONTROLLER_DUKE,
|
||||
MS_CONTROLLER_S,
|
||||
LIGHT_GUN,
|
||||
LIGHTGUN,
|
||||
STEERING_WHEEL,
|
||||
MEMORY_UNIT,
|
||||
IR_DONGLE,
|
||||
STEEL_BATTALION_CONTROLLER,
|
||||
ARCADE_STICK,
|
||||
DEVICE_MAX,
|
||||
// Devices with the HW_ prefix (= hardware) indicate a real xbox device. Always add these after DEVICE_MAX
|
||||
HW_STEEL_BATTALION_CONTROLLER,
|
||||
HW_XBOX_CONTROLLER,
|
||||
}
|
||||
XBOX_INPUT_DEVICE;
|
||||
|
||||
|
@ -119,7 +122,10 @@ public:
|
|||
// retrieves the port this device is attached to
|
||||
bool GetPort(std::string_view Port) const;
|
||||
// sets the port this device is attached to
|
||||
void SetPort(std::string_view Port, bool Connect);
|
||||
// NOTE: using SetPort2 to avoid a collision with the SetPort macro provided by Windows headers
|
||||
void SetPort2(std::string_view Port, bool Connect);
|
||||
// retuns true if it is a libusb device, false otherwise
|
||||
virtual bool IsLibusb() const { return false; };
|
||||
|
||||
|
||||
protected:
|
||||
|
|
|
@ -35,28 +35,35 @@
|
|||
#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"
|
||||
#include "DInputKeyboardMouse.h"
|
||||
#include "LibusbDevice.h"
|
||||
#include "InputManager.h"
|
||||
#include "..\devices\usb\XidGamepad.h"
|
||||
#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
|
||||
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_S
|
||||
0,
|
||||
LIGHTGUN_NUM_BUTTONS, // LIGHTGUN
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
@ -77,26 +84,30 @@ 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);
|
||||
Libusb::Init(m_Mtx);
|
||||
Sdl::Init(m_Mtx, m_Cv, is_gui);
|
||||
});
|
||||
|
||||
m_Cv.wait(lck, []() {
|
||||
return (Sdl::InitStatus != Sdl::NOT_INIT) &&
|
||||
(XInput::InitStatus != XInput::NOT_INIT) &&
|
||||
(RawInput::InitStatus != RawInput::NOT_INIT);
|
||||
(RawInput::InitStatus != RawInput::NOT_INIT) &&
|
||||
(Libusb::InitStatus != Libusb::NOT_INIT);
|
||||
});
|
||||
lck.unlock();
|
||||
|
||||
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0) {
|
||||
CxbxKrnlCleanup("Failed to initialize input subsystem! Consult debug log for more information");
|
||||
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0 || Libusb::InitStatus < 0) {
|
||||
CxbxrAbort("Failed to initialize input subsystem! Consult debug log for more information");
|
||||
}
|
||||
|
||||
UpdateOpt(is_gui);
|
||||
|
@ -129,8 +140,11 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
|
||||
ConstructHleInputDevice(&g_devs[CTRL_OFFSET + i], nullptr, type, port);
|
||||
BindHostDevice(type, port);
|
||||
break;
|
||||
|
@ -162,6 +176,7 @@ void InputDeviceManager::Shutdown()
|
|||
|
||||
XInput::DeInit();
|
||||
RawInput::DeInit();
|
||||
Libusb::DeInit();
|
||||
Sdl::DeInit(m_PollingThread);
|
||||
}
|
||||
|
||||
|
@ -266,7 +281,7 @@ void InputDeviceManager::UpdateDevices(std::string_view port, bool ack)
|
|||
else {
|
||||
auto host_dev = g_InputDeviceManager.FindDevice(port);
|
||||
if (host_dev != nullptr) {
|
||||
host_dev->SetPort(port, false);
|
||||
host_dev->SetPort2(port, false);
|
||||
}
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
if (type != to_underlying(dev->type)) {
|
||||
|
@ -303,7 +318,7 @@ void InputDeviceManager::DisconnectDevice(DeviceState *dev, std::string_view por
|
|||
}
|
||||
auto host_dev = g_InputDeviceManager.FindDevice(port);
|
||||
if (host_dev != nullptr) {
|
||||
host_dev->SetPort(port, false);
|
||||
host_dev->SetPort2(port, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,6 +328,19 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
|
|||
// MUs don't have any host devices bound, so we just return
|
||||
return;
|
||||
}
|
||||
else if ((type == to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER)) ||
|
||||
(type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER))) {
|
||||
// libusb devices don't have any profiles, but we still need to attach them to the xbox port
|
||||
char dev_name[50];
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
|
||||
auto dev = FindDevice(std::string(dev_name));
|
||||
if (dev != nullptr) {
|
||||
dev->SetPort2(port, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char dev_name[50];
|
||||
char dev_control_names[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
|
@ -336,20 +364,22 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
|
|||
});
|
||||
dev->SetBindings(index, (it != controls.end()) ? *it : nullptr, port_str);
|
||||
}
|
||||
dev->SetPort(port, true);
|
||||
dev->SetPort2(port, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int direction, int type)
|
||||
{
|
||||
assert(direction == DIRECTION_IN || direction == DIRECTION_OUT);
|
||||
assert(type > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX));
|
||||
bool has_changed = false;
|
||||
|
||||
// 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)) {
|
||||
|
@ -362,6 +392,11 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
|
|||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
has_changed = UpdateInputLightgun(dev, buffer, direction, port, port_str);
|
||||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
has_changed = UpdateInputSBC(dev, buffer, direction, port, port_str);
|
||||
m_Mtx.unlock();
|
||||
|
@ -371,7 +406,12 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
|
|||
assert(0);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
|
||||
has_changed = UpdateInputHw(dev, buffer, direction);
|
||||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
|
||||
EmuLog(LOG_LEVEL::ERROR2, "An unsupported device is attached at port %d! The device was %s",
|
||||
|
@ -397,8 +437,7 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
|
|||
return false;
|
||||
}
|
||||
|
||||
//XpadInput* in_buf = reinterpret_cast<XpadInput*>(static_cast<uint8_t*>(Buffer) + 2); lle usb
|
||||
XpadInput *in_buf = static_cast<XpadInput *>(Buffer);
|
||||
XpadInput* in_buf = reinterpret_cast<XpadInput*>(static_cast<uint8_t*>(Buffer) + XID_PACKET_HEADER);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input*>(bindings[i])->GetState() : 0.0;
|
||||
if (state) {
|
||||
|
@ -441,8 +480,7 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
|
|||
}
|
||||
else {
|
||||
if (bindings[24] != nullptr) {
|
||||
//XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(static_cast<uint8_t*>(Buffer) + 2); lle usb
|
||||
XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(Buffer);
|
||||
XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(static_cast<uint8_t*>(Buffer) + XID_PACKET_HEADER);
|
||||
dynamic_cast<InputDevice::Output*>(bindings[24])->SetState(out_buf->left_actuator_strength / static_cast<ControlState>(0xFFFF),
|
||||
out_buf->right_actuator_strength / static_cast<ControlState>(0xFFFF));
|
||||
}
|
||||
|
@ -451,6 +489,130 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port)
|
||||
{
|
||||
std::map<int, InputDevice::IoControl *> bindings = Device->GetBindings(Port);
|
||||
assert(bindings.size() == static_cast<size_t>(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN)]));
|
||||
|
||||
// NOTE: the output state is not supported
|
||||
if (Direction == DIRECTION_IN) {
|
||||
if (!Device->UpdateInput()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We change the toggle buttons only when a press -> release input transaction is completed
|
||||
// 0 -> Turbo left
|
||||
// 1 -> Turbo right
|
||||
// 2 -> Laser
|
||||
XpadInput *in_buf = reinterpret_cast<XpadInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
|
||||
g_devs[Port_num].info.ligthgun.last_turbo = g_devs[Port_num].info.ligthgun.turbo;
|
||||
for (int i = 14, j = 0; i < 17; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint8_t curr_state = static_cast<uint8_t>(!!state);
|
||||
if ((~curr_state) & ((g_devs[Port_num].info.ligthgun.last_in_state >> j) & 1)) {
|
||||
switch (j)
|
||||
{
|
||||
case 0:
|
||||
if (g_devs[Port_num].info.ligthgun.turbo != 2) {
|
||||
g_devs[Port_num].info.ligthgun.turbo += 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (g_devs[Port_num].info.ligthgun.turbo != 0) {
|
||||
g_devs[Port_num].info.ligthgun.turbo -= 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
g_devs[Port_num].info.ligthgun.laser ^= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(g_devs[Port_num].info.ligthgun.last_in_state &= ~(1 << j)) |= (curr_state << j);
|
||||
}
|
||||
|
||||
in_buf->wButtons = XINPUT_LIGHTGUN_ONSCREEN;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
if (state) {
|
||||
in_buf->wButtons |= (1 << i);
|
||||
}
|
||||
else {
|
||||
in_buf->wButtons &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_devs[Port_num].info.ligthgun.turbo) {
|
||||
ControlState trigger_state = (bindings[6] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[6])->GetState() : 0.0;
|
||||
if (trigger_state) {
|
||||
// Turbo mode 1
|
||||
in_buf->bAnalogButtons[0] ^= 0xFF;
|
||||
int start_idx = 7;
|
||||
if (g_devs[Port_num].info.ligthgun.turbo == 2) {
|
||||
// Turbo mode 2
|
||||
start_idx = 8;
|
||||
++g_devs[Port_num].info.ligthgun.turbo_delay;
|
||||
if (g_devs[Port_num].info.ligthgun.last_turbo != g_devs[Port_num].info.ligthgun.turbo) {
|
||||
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
|
||||
}
|
||||
if (g_devs[Port_num].info.ligthgun.turbo_delay == LIGHTGUN_GRIP_DELAY) {
|
||||
in_buf->bAnalogButtons[1] ^= 0xFF;
|
||||
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
|
||||
}
|
||||
}
|
||||
for (int i = start_idx, j = start_idx - 6; i < 10; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Turbo active but trigger not pressed
|
||||
in_buf->bAnalogButtons[0] = 0;
|
||||
for (int i = 7, j = 1; i < 10; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Turbo mode 0 (no turbo)
|
||||
for (int i = 6, j = 0; i < 10; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
in_buf->bAnalogButtons[4] = 0;
|
||||
in_buf->bAnalogButtons[5] = 0;
|
||||
in_buf->bAnalogButtons[6] = 0;
|
||||
in_buf->bAnalogButtons[7] = 0;
|
||||
|
||||
for (int i = 10; i < 14; i += 2) {
|
||||
ControlState state_plus = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
ControlState state_minus = (bindings[i + 1] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i + 1])->GetState() : 0.0;
|
||||
ControlState state = state_plus ? state_plus * 0x7FFF : state_minus ? -state_minus * 0x8000 : 0.0;
|
||||
switch (i)
|
||||
{
|
||||
case 10: {
|
||||
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_x : g_devs[Port_num].info.ligthgun.offset_x;
|
||||
in_buf->sThumbLX = static_cast<int16_t>(state) + offset;
|
||||
}
|
||||
break;
|
||||
|
||||
case 12: {
|
||||
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_y : g_devs[Port_num].info.ligthgun.offset_y;
|
||||
in_buf->sThumbLY = static_cast<int16_t>(state) + offset;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
in_buf->sThumbRX = in_buf->sThumbRY = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port)
|
||||
{
|
||||
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port);
|
||||
|
@ -474,8 +636,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
// 8 -> TunerDial Down
|
||||
// 9 -> GearLever Up
|
||||
// 10 -> GearLever Down
|
||||
static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 };
|
||||
SBCInput *in_buf = static_cast<SBCInput *>(Buffer);
|
||||
SBCInput *in_buf = reinterpret_cast<SBCInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
if (state) {
|
||||
|
@ -490,10 +651,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 4, j = 0; i < 6; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
|
||||
in_buf->wButtons[0] ^= (1 << i);
|
||||
}
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
|
||||
for (int i = 6; i < 34; i++) {
|
||||
|
@ -510,10 +671,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 34, j = 2; i < 39; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
|
||||
in_buf->wButtons[2] ^= (1 << (i % 16));
|
||||
}
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
|
||||
for (int i = 39; i < 49; i += 2) {
|
||||
|
@ -572,7 +733,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 52, j = 7; i < 56; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
|
||||
switch (i)
|
||||
{
|
||||
case 52:
|
||||
|
@ -600,13 +761,18 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
break;
|
||||
}
|
||||
}
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateInputHw(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction)
|
||||
{
|
||||
return dynamic_cast<Libusb::LibusbDevice *>(Device.get())->ExecuteIo(Buffer, Direction);
|
||||
}
|
||||
|
||||
void InputDeviceManager::RefreshDevices()
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(m_Mtx);
|
||||
|
@ -616,25 +782,28 @@ void InputDeviceManager::RefreshDevices()
|
|||
XInput::PopulateDevices();
|
||||
DInput::PopulateDevices();
|
||||
Sdl::PopulateDevices();
|
||||
Libusb::PopulateDevices();
|
||||
lck.lock();
|
||||
m_Cv.wait(lck, []() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> InputDeviceManager::GetDeviceList() const
|
||||
std::vector<std::string> InputDeviceManager::GetDeviceList(std::function<bool(const InputDevice *)> Callback) const
|
||||
{
|
||||
std::vector<std::string> dev_list;
|
||||
std::lock_guard<std::mutex> lck(m_Mtx);
|
||||
|
||||
std::for_each(m_Devices.begin(), m_Devices.end(), [&dev_list](const auto& Device) {
|
||||
dev_list.push_back(Device->GetQualifiedName());
|
||||
std::for_each(m_Devices.begin(), m_Devices.end(), [&dev_list, &Callback](const auto& Device) {
|
||||
if (Callback(Device.get())) {
|
||||
dev_list.push_back(Device->GetQualifiedName());
|
||||
}
|
||||
});
|
||||
|
||||
return dev_list;
|
||||
|
@ -719,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 (StrStartsWith(Device->GetAPI(), "XInput")) {
|
||||
if (Device->IsLibusb() || Device->GetAPI().starts_with("XInput")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -730,6 +899,9 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
|
|||
|
||||
lck.unlock();
|
||||
XInput::PopulateDevices();
|
||||
// When this was written, libusb did not yet support device hotplug on Windows, as documented in this issue https://github.com/libusb/libusb/issues/86.
|
||||
// So we add the below call here. This will only work if rawinput detects the libusb device.
|
||||
Libusb::PopulateDevices();
|
||||
}
|
||||
|
||||
for (int port = PORT_1; port <= PORT_4; ++port) {
|
||||
|
@ -740,3 +912,35 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImVec2 InputDeviceManager::CalcLaserPos(int port)
|
||||
{
|
||||
static ImVec2 laser_coord[XBOX_NUM_PORTS] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
|
||||
|
||||
// If somebody else is currently holding the lock, we won't wait and instead we report the last known laser position
|
||||
if (m_Mtx.try_lock()) {
|
||||
|
||||
// NOTE: even when switching to faux fullscreen, imgui will still use the original window size. Because of this, we only need to do this once.
|
||||
// If in the future the above is fixed, then this code will have to recalculate the new window size after a resizing has occured.
|
||||
static long width, height = -1;
|
||||
if (height == -1) {
|
||||
|
||||
RECT rect;
|
||||
GetClientRect(m_hwnd, &rect);
|
||||
width = std::max(rect.right - rect.left, 1l);
|
||||
height = std::max(rect.bottom - rect.top, 1l);
|
||||
}
|
||||
|
||||
// We convert the laser input coordinates given by xinput (in the sThumbLXY members of XpadInput) with linear interpolation y = y0 + (x - x0) * (y1 - y0) / (x1 - x0)
|
||||
// For laser_x x0 = -32768; x1 = 32767; y0 = 0; y1 = width
|
||||
// For laser_y x0 = -32768; x1 = 32767; y0 = height; y1 = 0
|
||||
int16_t laser_x = g_devs[port].info.buff.ctrl.InBuffer.sThumbLX;
|
||||
int16_t laser_y = g_devs[port].info.buff.ctrl.InBuffer.sThumbLY;
|
||||
laser_coord[port].x = ((laser_x + 32768) * width) / 65535.0f;
|
||||
laser_coord[port].y = height - ((laser_y + 32768) * height) / 65535.0f;
|
||||
|
||||
m_Mtx.unlock();
|
||||
}
|
||||
|
||||
return laser_coord[port];
|
||||
}
|
||||
|
|
|
@ -31,11 +31,7 @@
|
|||
#include <thread>
|
||||
#include "InputDevice.h"
|
||||
#include "EmuDevice.h"
|
||||
|
||||
// Prevent a collision with the SetPort provided by Windows
|
||||
#ifdef WIN32
|
||||
#undef SetPort
|
||||
#endif
|
||||
#include <imgui.h>
|
||||
|
||||
#define PORT_INVALID -1
|
||||
#define PORT_1 0
|
||||
|
@ -52,14 +48,21 @@
|
|||
#define MU_OFFSET 4
|
||||
#define MAX_DEVS (XBOX_NUM_PORTS + XBOX_CTRL_NUM_SLOTS * XBOX_NUM_PORTS)
|
||||
|
||||
#define XID_PACKET_HEADER 2
|
||||
|
||||
#define LIGHTGUN_GRIP_DELAY 30
|
||||
|
||||
extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)];
|
||||
|
||||
inline XBOX_INPUT_DEVICE input_support_list[] = {
|
||||
XBOX_INPUT_DEVICE::DEVICE_INVALID,
|
||||
XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE,
|
||||
XBOX_INPUT_DEVICE::MS_CONTROLLER_S,
|
||||
XBOX_INPUT_DEVICE::LIGHTGUN,
|
||||
XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER,
|
||||
XBOX_INPUT_DEVICE::ARCADE_STICK,
|
||||
XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER,
|
||||
XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER,
|
||||
};
|
||||
|
||||
inline XBOX_INPUT_DEVICE slot_support_list[] = {
|
||||
|
@ -71,7 +74,19 @@ inline XBOX_INPUT_DEVICE slot_support_list[] = {
|
|||
|
||||
#pragma pack(1)
|
||||
|
||||
// xpad in/out buffers stripped of the first two bytes
|
||||
// Class-specific xid descriptor, used by libusb
|
||||
struct XidDesc {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdXid;
|
||||
uint8_t bType;
|
||||
uint8_t bSubType;
|
||||
uint8_t bMaxInputReportSize;
|
||||
uint8_t bMaxOutputReportSize;
|
||||
uint16_t wAlternateProductIds[4];
|
||||
};
|
||||
|
||||
// xpad in/out buffers stripped of the first two bytes as used by xinput
|
||||
struct XpadInput {
|
||||
uint16_t wButtons;
|
||||
uint8_t bAnalogButtons[8];
|
||||
|
@ -86,6 +101,20 @@ struct XpadOutput {
|
|||
uint16_t right_actuator_strength;
|
||||
};
|
||||
|
||||
// xpad in/out buffers as used by xid
|
||||
struct XidGamepadInput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
XpadInput InBuffer;
|
||||
};
|
||||
|
||||
struct XidGamepadOutput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
XpadOutput OutBuffer;
|
||||
};
|
||||
|
||||
// same as above, but for the SBC
|
||||
struct SBCInput {
|
||||
uint16_t wButtons[3];
|
||||
uint8_t bPad1;
|
||||
|
@ -112,11 +141,39 @@ struct SBCOutput {
|
|||
uint8_t LedState[20];
|
||||
};
|
||||
|
||||
struct XidSBCInput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
SBCInput InBuffer;
|
||||
};
|
||||
|
||||
struct XidSBCOutput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
SBCOutput OutBuffer;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
struct LightGunData {
|
||||
xbox::short_xt offset_x;
|
||||
xbox::short_xt offset_y;
|
||||
xbox::short_xt offset_upp_x;
|
||||
xbox::short_xt offset_upp_y;
|
||||
uint8_t last_in_state;
|
||||
uint8_t last_turbo;
|
||||
uint8_t turbo_delay;
|
||||
uint8_t turbo;
|
||||
uint8_t laser;
|
||||
};
|
||||
|
||||
struct SbcData {
|
||||
uint16_t last_in_state;
|
||||
};
|
||||
|
||||
union InputBuff {
|
||||
XpadInput ctrl;
|
||||
SBCInput sbc;
|
||||
XidGamepadInput ctrl;
|
||||
XidSBCInput sbc;
|
||||
};
|
||||
|
||||
struct DeviceInfo {
|
||||
|
@ -127,8 +184,13 @@ struct DeviceInfo {
|
|||
uint8_t ucSubType; // xapi subtype
|
||||
uint8_t ucInputStateSize; // input state size in bytes, does not include dwPacketNumber
|
||||
uint8_t ucFeedbackSize; // feedback size in bytes, does not include FeedbackHeader
|
||||
uint32_t dwPacketNumber;
|
||||
InputBuff buff;
|
||||
uint32_t dwPacketNumber; // increases by one when the input state changes
|
||||
InputBuff buff; // input buffer
|
||||
// device-specific additional data
|
||||
union {
|
||||
LightGunData ligthgun;
|
||||
SbcData sbc;
|
||||
};
|
||||
};
|
||||
|
||||
struct DeviceState {
|
||||
|
@ -159,7 +221,7 @@ public:
|
|||
// update device list
|
||||
void RefreshDevices();
|
||||
// get the name of the devices currently detected
|
||||
std::vector<std::string> GetDeviceList() const;
|
||||
std::vector<std::string> GetDeviceList(std::function<bool(const InputDevice *)> Callback) const;
|
||||
// find device from its gui name
|
||||
std::shared_ptr<InputDevice> FindDevice(const std::string& QualifiedName) const;
|
||||
// find device from its sdl id
|
||||
|
@ -172,13 +234,19 @@ public:
|
|||
void UpdateOpt(bool is_gui);
|
||||
// device hotplug event handler
|
||||
void HotplugHandler(bool is_sdl);
|
||||
// converts xinput -> screen coordinates to display the lightgun laser on the rendering window
|
||||
ImVec2 CalcLaserPos(int port);
|
||||
|
||||
|
||||
private:
|
||||
// update input for an xbox controller
|
||||
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port);
|
||||
// update input for a lightgun
|
||||
bool UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port);
|
||||
// update input for a Steel Battalion controller
|
||||
bool UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port);
|
||||
// update input for a passthrough xbox device
|
||||
bool UpdateInputHw(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction);
|
||||
// bind a host device to an emulated device
|
||||
void BindHostDevice(int type, std::string_view port);
|
||||
// connect a device to the emulated machine
|
||||
|
|
|
@ -78,16 +78,22 @@ void InputWindow::UpdateDeviceList()
|
|||
g_InputDeviceManager.RefreshDevices();
|
||||
|
||||
// Populate device list
|
||||
LRESULT num_devices = SendMessage(m_hwnd_device_list, CB_GETCOUNT, 0, 0);
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
for (int i = 0; i < m_num_devices; i++) {
|
||||
SendMessage(m_hwnd_device_list, CB_DELETESTRING, 0, 0);
|
||||
}
|
||||
|
||||
std::vector<std::string> dev_list = g_InputDeviceManager.GetDeviceList();
|
||||
// Add everything but libusb devices
|
||||
std::vector<std::string> dev_list = g_InputDeviceManager.GetDeviceList([](const auto &Device) {
|
||||
if (Device->IsLibusb()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
for (const auto& str : dev_list) {
|
||||
SendMessage(m_hwnd_device_list, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(str.c_str()));
|
||||
++m_num_devices;
|
||||
}
|
||||
if (!dev_list.empty()) {
|
||||
if (m_num_devices) {
|
||||
SendMessage(m_hwnd_device_list, CB_SETCURSEL, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -144,7 +150,23 @@ InputDevice::Input* InputWindow::DetectInput(InputDevice* const Device, int ms)
|
|||
return nullptr; // no input
|
||||
}
|
||||
|
||||
void InputWindow::BindButton(int ControlID)
|
||||
int InputWindow::EnableDefaultButton()
|
||||
{
|
||||
if (std::strncmp(m_host_dev.c_str(), "XInput", std::strlen("XInput")) == 0) {
|
||||
EnableWindow(m_hwnd_default, TRUE);
|
||||
return XINPUT_DEFAULT;
|
||||
}
|
||||
else if (std::strncmp(m_host_dev.c_str(), "DInput", std::strlen("DInput")) == 0) {
|
||||
EnableWindow(m_hwnd_default, TRUE);
|
||||
return DINPUT_DEFAULT;
|
||||
}
|
||||
else {
|
||||
EnableWindow(m_hwnd_default, FALSE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void InputWindow::BindButton(int ControlID, bool auto_swap)
|
||||
{
|
||||
// Check if binding thread is still active
|
||||
// testcase: spacebar and enter keys; without this fix will cause repeat binding result.
|
||||
|
@ -157,7 +179,7 @@ void InputWindow::BindButton(int ControlID)
|
|||
m_bIsBinding = true;
|
||||
|
||||
// Don't block the message processing loop
|
||||
std::thread([this, dev, ControlID]() {
|
||||
std::thread([this, dev, ControlID, auto_swap]() {
|
||||
EnableWindow(m_hwnd_window, FALSE);
|
||||
char current_text[HOST_BUTTON_NAME_LENGTH];
|
||||
Button* xbox_button = m_DeviceConfig->FindButtonById(ControlID);
|
||||
|
@ -167,6 +189,9 @@ void InputWindow::BindButton(int ControlID)
|
|||
InputDevice::Input* dev_button = fut.get();
|
||||
if (dev_button) {
|
||||
xbox_button->UpdateText(dev_button->GetName().c_str());
|
||||
if (auto_swap) {
|
||||
SwapMoCursorAxis(xbox_button);
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
else {
|
||||
|
@ -195,7 +220,6 @@ void InputWindow::UpdateProfile(const std::string &name, int command)
|
|||
break;
|
||||
|
||||
case BUTTON_CLEAR:
|
||||
case BUTTON_SWAP:
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
}
|
||||
|
@ -310,9 +334,15 @@ void InputWindow::LoadDefaultProfile()
|
|||
|
||||
void InputWindow::UpdateCurrentDevice()
|
||||
{
|
||||
char device_name[50];
|
||||
SendMessage(m_hwnd_device_list, WM_GETTEXT, sizeof(device_name), reinterpret_cast<LPARAM>(device_name));
|
||||
m_host_dev = device_name;
|
||||
if (m_num_devices) {
|
||||
char device_name[50];
|
||||
SendMessage(m_hwnd_device_list, WM_GETTEXT, sizeof(device_name), reinterpret_cast<LPARAM>(device_name));
|
||||
m_host_dev = device_name;
|
||||
}
|
||||
else {
|
||||
m_host_dev = "";
|
||||
}
|
||||
|
||||
EnableDefaultButton();
|
||||
}
|
||||
|
||||
|
@ -322,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':
|
||||
|
@ -336,6 +366,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Cursor X-");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
|
@ -345,6 +376,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Cursor Y+");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -352,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':
|
||||
|
@ -362,6 +394,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Axis X-");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
|
@ -371,6 +404,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Axis Y+");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#define RUMBLE_TEST 6
|
||||
#define RUMBLE_CLEAR 7
|
||||
#define BUTTON_CLEAR 8
|
||||
#define BUTTON_SWAP 9
|
||||
|
||||
#define XINPUT_DEFAULT 0
|
||||
#define DINPUT_DEFAULT 1
|
||||
|
@ -55,8 +54,8 @@ class InputWindow
|
|||
public:
|
||||
virtual void Initialize(HWND hwnd, int port_num, int dev_type) = 0;
|
||||
~InputWindow();
|
||||
void UpdateDeviceList();
|
||||
void BindButton(int ControlID);
|
||||
virtual void UpdateDeviceList();
|
||||
void BindButton(int ControlID, bool auto_swap = false);
|
||||
virtual void ClearBindings() = 0;
|
||||
virtual void UpdateProfile(const std::string& name, int command);
|
||||
void UpdateCurrentDevice();
|
||||
|
@ -73,8 +72,8 @@ protected:
|
|||
void DeleteProfile(const std::string& name);
|
||||
void OverwriteProfile(const std::string& name);
|
||||
void LoadDefaultProfile();
|
||||
virtual int EnableDefaultButton() = 0;
|
||||
virtual void SaveSlotConfig() = 0;
|
||||
virtual int EnableDefaultButton();
|
||||
|
||||
|
||||
// xbox device under configuration
|
||||
EmuDevice* m_DeviceConfig;
|
||||
|
@ -84,6 +83,10 @@ protected:
|
|||
HWND m_hwnd_device_list;
|
||||
// handle of the profile list combobox
|
||||
HWND m_hwnd_profile_list;
|
||||
// handle of the default bindings button
|
||||
HWND m_hwnd_default;
|
||||
// number of devices displayed in the device list combobox
|
||||
int m_num_devices;
|
||||
// type of the device
|
||||
int m_dev_type;
|
||||
// num of buttons of device under configuration
|
||||
|
@ -107,15 +110,12 @@ public:
|
|||
void BindDefault();
|
||||
void ClearBindings() override;
|
||||
void UpdateProfile(const std::string &name, int command) override;
|
||||
void SaveSlotConfig() override;
|
||||
void SaveSlotConfig();
|
||||
|
||||
|
||||
private:
|
||||
int EnableDefaultButton() override;
|
||||
void DetectOutput(int ms);
|
||||
|
||||
// handle of the default bindings button
|
||||
HWND m_hwnd_default;
|
||||
// handle of the rumble window
|
||||
HWND m_hwnd_rumble;
|
||||
// handle of the rumble combobox
|
||||
|
@ -130,10 +130,36 @@ class SbcInputWindow : public InputWindow
|
|||
{
|
||||
public:
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
~SbcInputWindow();
|
||||
void ClearBindings() override;
|
||||
void SaveSlotConfig() override;
|
||||
|
||||
|
||||
private:
|
||||
int EnableDefaultButton() override;
|
||||
};
|
||||
|
||||
class LibusbInputWindow : public InputWindow
|
||||
{
|
||||
public:
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
~LibusbInputWindow();
|
||||
void ClearBindings() override;
|
||||
void UpdateDeviceList() override;
|
||||
void TestInput();
|
||||
|
||||
|
||||
private:
|
||||
int EnableDefaultButton() override;
|
||||
|
||||
// handle of the test button
|
||||
HWND m_hwnd_device_test;
|
||||
};
|
||||
|
||||
class LightgunInputWindow : public InputWindow
|
||||
{
|
||||
public:
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
~LightgunInputWindow();
|
||||
void BindDefault();
|
||||
void ClearBindings() override;
|
||||
};
|
||||
|
|
325
src/common/input/LibusbDevice.cpp
Normal file
325
src/common/input/LibusbDevice.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
// 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) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#define LOG_PREFIX CXBXR_MODULE::LIBUSB
|
||||
|
||||
#include "LibusbDevice.h"
|
||||
#include "InputManager.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\XAPI\Xapi.h"
|
||||
|
||||
// Sanity check: ensure that our libusb version is high enough for libusb_get_device_descriptor to succeed and to pass nullptr to libusb_interrupt_transfer
|
||||
static_assert(LIBUSB_API_VERSION >= 0x01000105);
|
||||
|
||||
|
||||
namespace Libusb
|
||||
{
|
||||
int InitStatus = NOT_INIT;
|
||||
|
||||
// These come from here https://github.com/xboxdrv/xboxdrv/blob/ac6ebb1228962220482ea03743cadbe18754246c/src/xpad_device.cpp#L29
|
||||
static constexpr uint16_t SupportedDevices_VidPid[][2] = { // vid, pid
|
||||
0x0d2f, 0x0002,
|
||||
0x045e, 0x0202,
|
||||
0x045e, 0x0285,
|
||||
0x045e, 0x0287,
|
||||
0x045e, 0x0289,
|
||||
0x046d, 0xca84,
|
||||
0x046d, 0xca88,
|
||||
0x05fd, 0x1007,
|
||||
0x05fd, 0x107a,
|
||||
0x0738, 0x4516,
|
||||
0x0738, 0x4522,
|
||||
0x0738, 0x4526,
|
||||
0x0738, 0x4536,
|
||||
0x0738, 0x4556,
|
||||
0x0c12, 0x8802,
|
||||
0x0c12, 0x8810,
|
||||
0x0c12, 0x9902,
|
||||
0x0e4c, 0x1097,
|
||||
0x0e4c, 0x2390,
|
||||
0x0e6f, 0x0003,
|
||||
0x0e6f, 0x0005,
|
||||
0x0e6f, 0x0006,
|
||||
0x0f30, 0x0202,
|
||||
0x0f30, 0x8888,
|
||||
0x102c, 0xff0c,
|
||||
0x044f, 0x0f07,
|
||||
0x0e8f, 0x3008,
|
||||
};
|
||||
|
||||
static constexpr const char *SupportedDevices_Name[] = {
|
||||
"Andamiro Pump It Up pad",
|
||||
"Microsoft X-Box pad v1 (US)",
|
||||
"Microsoft X-Box pad (Japan)",
|
||||
"Microsoft Xbox Controller S",
|
||||
"Microsoft X-Box pad v2 (US)",
|
||||
"Logitech Xbox Cordless Controller",
|
||||
"Logitech Compact Controller for Xbox",
|
||||
"Mad Catz Controller (unverified)",
|
||||
"InterAct 'PowerPad Pro' X-Box pad (Germany)",
|
||||
"Mad Catz Control Pad",
|
||||
"Mad Catz LumiCON",
|
||||
"Mad Catz Control Pad Pro",
|
||||
"Mad Catz MicroCON",
|
||||
"Mad Catz Lynx Wireless Controller",
|
||||
"Zeroplus Xbox Controller",
|
||||
"Zeroplus Xbox Controller",
|
||||
"HAMA VibraX - *FAULTY HARDWARE*",
|
||||
"Radica Gamester Controller",
|
||||
"Radica Games Jtech Controller",
|
||||
"Logic3 Freebird wireless Controller",
|
||||
"Eclipse wireless Controller",
|
||||
"Edge wireless Controller",
|
||||
"Joytech Advanced Controller",
|
||||
"BigBen XBMiniPad Controller",
|
||||
"Joytech Wireless Advanced Controller",
|
||||
"Thrustmaster, Inc. Controller",
|
||||
"Generic xbox control (dealextreme)",
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(SupportedDevices_VidPid) == ARRAY_SIZE(SupportedDevices_Name));
|
||||
|
||||
void Init(std::mutex &Mtx)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(Mtx);
|
||||
|
||||
// We only use a single libusb session per cxbxr process, so we do not need to use a libusb context
|
||||
if (libusb_init(nullptr) != 0) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize Libusb!");
|
||||
InitStatus = INIT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
InitStatus = INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
InitStatus = NOT_INIT;
|
||||
libusb_exit(nullptr);
|
||||
}
|
||||
|
||||
void PopulateDevices()
|
||||
{
|
||||
// NOTE: the libusb docs say that the list is always appended with a NULL element at the end
|
||||
libusb_device **List;
|
||||
ssize_t DevicesConnected = libusb_get_device_list(nullptr, &List) - 1;
|
||||
if (DevicesConnected < 0) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to enumerate devices. The error was: %s", libusb_strerror(DevicesConnected));
|
||||
return;
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < DevicesConnected; ++i) {
|
||||
libusb_device *LibusbDev = List[i];
|
||||
libusb_device_descriptor Desc;
|
||||
libusb_get_device_descriptor(LibusbDev, &Desc); // always succeeds when LIBUSB_API_VERSION >= 0x01000102
|
||||
auto Device = std::make_shared<LibusbDevice>(&Desc, LibusbDev);
|
||||
if (Device->IsLibusb()) {
|
||||
g_InputDeviceManager.AddDevice(std::move(Device));
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(List, 1);
|
||||
}
|
||||
|
||||
void GetDeviceChanges()
|
||||
{
|
||||
g_InputDeviceManager.RemoveDevice([](const auto &Device) {
|
||||
return Device->IsLibusb();
|
||||
});
|
||||
PopulateDevices();
|
||||
}
|
||||
|
||||
LibusbDevice::LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev)
|
||||
{
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
|
||||
// The SBC's VID and PID are taken from https://xboxdevwiki.net/Xbox_Input_Devices#Steel_Battalion_Controller
|
||||
if ((Desc->idVendor == 0x0a7b) && (Desc->idProduct == 0xd000)) {
|
||||
m_Type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
|
||||
m_UcType = XINPUT_DEVTYPE_STEELBATTALION;
|
||||
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
m_Name = "Steel battalion controller";
|
||||
m_BufferInSize = sizeof(XidSBCInput);
|
||||
m_BufferOutSize = sizeof(XidSBCOutput);
|
||||
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
|
||||
}
|
||||
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])) {
|
||||
m_Type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
|
||||
m_UcType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
m_Name = SupportedDevices_Name[i];
|
||||
m_BufferInSize = sizeof(XidGamepadInput);
|
||||
m_BufferOutSize = sizeof(XidGamepadOutput);
|
||||
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
|
||||
|
||||
// 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) {
|
||||
auto Iface = ConfDesc->interface[0];
|
||||
if (Iface.num_altsetting == 1) {
|
||||
auto Setting = Iface.altsetting[0];
|
||||
m_IfaceNum = Setting.bInterfaceNumber;
|
||||
if (Setting.bNumEndpoints >= 1) {
|
||||
m_HasEndpointOut = false;
|
||||
for (uint8_t i = 0; i < Setting.bNumEndpoints; ++i) {
|
||||
auto Endpoint = Setting.endpoint[i];
|
||||
if (Endpoint.bEndpointAddress & 0x80) {
|
||||
m_EndpointIn = Endpoint.bEndpointAddress;
|
||||
m_IntervalIn = Endpoint.bInterval;
|
||||
}
|
||||
else {
|
||||
// third-party controllers that don't support rumble probably won't have an out endpoint
|
||||
m_EndpointOut = Endpoint.bEndpointAddress;
|
||||
m_IntervalOut = Endpoint.bInterval;
|
||||
m_HasEndpointOut = true;
|
||||
}
|
||||
}
|
||||
EmuLog(LOG_LEVEL::INFO, "Out endpoint %s", m_HasEndpointOut ? "present" : "not present");
|
||||
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;
|
||||
}
|
||||
else {
|
||||
// Grab the xid descriptor so that we can report real type/subtype values back to the title when it calls XInputGetCapabilities
|
||||
XidDesc XidDesc;
|
||||
if (libusb_control_transfer(m_hDev, 0xC1, 6, 0x4200, m_IfaceNum, reinterpret_cast<uint8_t *>(&XidDesc), sizeof(XidDesc), m_IntervalIn)
|
||||
== sizeof(XidDesc)) { // submit a GET_DESCRIPTOR request
|
||||
|
||||
// Dump the xid descriptor to the log
|
||||
EmuLog(LOG_LEVEL::INFO, "Xid descriptor dump:\nbLength: %#010X\nbDescriptorType: %#010X\nbcdXid: %#010X\nbType: %#010X\n"
|
||||
"bSubType: %#010X\nbMaxInputReportSize: %#010X\nbMaxOutputReportSize: %#010X\nwAlternateProductIds[0]: %#010X\n"
|
||||
"wAlternateProductIds[1]: %#010X\nwAlternateProductIds[2]: %#010X\nwAlternateProductIds[3]: %#010X\n",
|
||||
XidDesc.bLength, XidDesc.bDescriptorType, XidDesc.bcdXid, XidDesc.bType, XidDesc.bSubType, XidDesc.bMaxInputReportSize, XidDesc.bMaxOutputReportSize,
|
||||
XidDesc.wAlternateProductIds[0], XidDesc.wAlternateProductIds[1], XidDesc.wAlternateProductIds[2], XidDesc.wAlternateProductIds[3]);
|
||||
|
||||
if (XidDesc.bDescriptorType == 0x42) {
|
||||
m_UcType = XidDesc.bType;
|
||||
m_UcSubType = XidDesc.bSubType;
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "The xid descriptor for device %s reported an unexpected descriptor type, assuming a default subtype", m_Name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Could not retrieve the xid descriptor for device %s, assuming a default subtype", m_Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of endpoints, bNumEndpoints: %d",
|
||||
m_Name.c_str(), Setting.bNumEndpoints);
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of alternative settings, num_altsetting: %d",
|
||||
m_Name.c_str(), Iface.num_altsetting);
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of interfaces, bNumInterfaces: %d",
|
||||
m_Name.c_str(), ConfDesc->bNumInterfaces);
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
}
|
||||
libusb_free_config_descriptor(ConfDesc);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { libusb_close(m_hDev); }
|
||||
}
|
||||
|
||||
LibusbDevice::~LibusbDevice()
|
||||
{
|
||||
if (m_Type != XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
libusb_release_interface(m_hDev, m_IfaceNum);
|
||||
libusb_close(m_hDev);
|
||||
}
|
||||
}
|
||||
|
||||
bool LibusbDevice::UpdateInput()
|
||||
{
|
||||
// Dummy, it should never be called. It's only here to override the pure function UpdateInput in InputDevice
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LibusbDevice::ExecuteIo(void *Buffer, int Direction)
|
||||
{
|
||||
// NOTE: a SET_REPORT control transfer to the SBC doesn't seem to work, the parameters might not be appropriate for it... So, we use
|
||||
// the interrupt pipes for everything instead
|
||||
*static_cast<uint8_t *>(Buffer) = 0; // write bReportId
|
||||
if (Direction == DIRECTION_IN) {
|
||||
*(static_cast<uint8_t *>(Buffer) + 1) = m_BufferInSize; // write bLength
|
||||
if (libusb_interrupt_transfer(m_hDev, m_EndpointIn, static_cast<uint8_t *>(Buffer), m_BufferInSize, nullptr, m_IntervalIn) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_HasEndpointOut) {
|
||||
*(static_cast<uint8_t *>(Buffer) + 1) = m_BufferOutSize; // write bLength
|
||||
if (libusb_interrupt_transfer(m_hDev, m_EndpointOut, static_cast<uint8_t *>(Buffer), m_BufferOutSize, nullptr, m_IntervalOut) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string LibusbDevice::GetDeviceName() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
|
||||
std::string LibusbDevice::GetAPI() const
|
||||
{
|
||||
return "Libusb";
|
||||
}
|
||||
}
|
91
src/common/input/LibusbDevice.h
Normal file
91
src/common/input/LibusbDevice.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
// 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) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InputDevice.h"
|
||||
// Suppress warning in libusb about zero sized array
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4200)
|
||||
#include "libusb.h"
|
||||
#pragma warning(pop)
|
||||
|
||||
|
||||
namespace Libusb
|
||||
{
|
||||
typedef enum _INIT_STATUS : int
|
||||
{
|
||||
NOT_INIT = -2,
|
||||
INIT_ERROR,
|
||||
INIT_SUCCESS,
|
||||
}
|
||||
INIT_STATUS;
|
||||
|
||||
extern int InitStatus;
|
||||
|
||||
// initialize Libusb
|
||||
void Init(std::mutex &Mtx);
|
||||
// shutdown Libusb
|
||||
void DeInit();
|
||||
// refresh the device list in response to a refresh command from the input GUI
|
||||
void PopulateDevices();
|
||||
// update the device list
|
||||
void GetDeviceChanges();
|
||||
|
||||
class LibusbDevice : public InputDevice
|
||||
{
|
||||
public:
|
||||
~LibusbDevice();
|
||||
bool UpdateInput() override;
|
||||
bool ExecuteIo(void *Buffer, int Direction);
|
||||
|
||||
LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev);
|
||||
|
||||
std::string GetDeviceName() const override;
|
||||
std::string GetAPI() const override;
|
||||
bool IsLibusb() const override { return m_Type != XBOX_INPUT_DEVICE::DEVICE_INVALID; }
|
||||
XBOX_INPUT_DEVICE GetLibusbType() const { return m_Type; }
|
||||
uint8_t GetUcType() { return m_UcType; }
|
||||
uint8_t GetUcSubType() { return m_UcSubType; }
|
||||
|
||||
|
||||
private:
|
||||
XBOX_INPUT_DEVICE m_Type;
|
||||
uint8_t m_UcType;
|
||||
uint8_t m_UcSubType;
|
||||
std::string m_Name;
|
||||
libusb_device_handle *m_hDev;
|
||||
unsigned char m_EndpointIn;
|
||||
unsigned char m_EndpointOut;
|
||||
uint8_t m_IntervalIn;
|
||||
uint8_t m_IntervalOut;
|
||||
uint8_t m_BufferInSize;
|
||||
uint8_t m_BufferOutSize;
|
||||
uint8_t m_IfaceNum;
|
||||
bool m_HasEndpointOut;
|
||||
};
|
||||
}
|
|
@ -35,12 +35,13 @@
|
|||
#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"
|
||||
#include "LibusbDevice.h"
|
||||
#include "InputManager.h"
|
||||
|
||||
// These values are those used by Dolphin!
|
||||
|
@ -156,6 +157,7 @@ namespace Sdl
|
|||
else {
|
||||
XInput::GetDeviceChanges();
|
||||
DInput::GetDeviceChanges();
|
||||
Libusb::GetDeviceChanges();
|
||||
std::string port = std::to_string(*static_cast<int *>(Event.user.data1));
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
|
|
|
@ -118,6 +118,27 @@ inline int button_sbc_id[SBC_NUM_BUTTONS] = {
|
|||
IDC_GEAR_UP,
|
||||
IDC_GEAR_DOWN,
|
||||
};
|
||||
|
||||
// Must have the same button order as defined in button_lightgun_names
|
||||
inline int button_lightgun_id[LIGHTGUN_NUM_BUTTONS] = {
|
||||
IDC_LG_STICK_UP,
|
||||
IDC_LG_STICK_DOWN,
|
||||
IDC_LG_STICK_LEFT,
|
||||
IDC_LG_STICK_RIGHT,
|
||||
IDC_LG_START,
|
||||
IDC_LG_SEBA,
|
||||
IDC_LG_TRIGGER,
|
||||
IDC_LG_GRIP,
|
||||
IDC_LG_A,
|
||||
IDC_LG_B,
|
||||
IDC_LG_AIM_POSX,
|
||||
IDC_LG_AIM_NEGX,
|
||||
IDC_LG_AIM_POSY,
|
||||
IDC_LG_AIM_NEGY,
|
||||
IDC_TURBO_LEFT,
|
||||
IDC_TURBO_RIGHT,
|
||||
IDC_LASER,
|
||||
};
|
||||
#endif
|
||||
|
||||
inline constexpr const char* button_xbox_ctrl_names[XBOX_CTRL_NUM_BUTTONS] = {
|
||||
|
@ -207,6 +228,26 @@ inline constexpr const char *button_sbc_names[SBC_NUM_BUTTONS] = {
|
|||
"GearLever Down",
|
||||
};
|
||||
|
||||
inline constexpr const char *button_lightgun_names[LIGHTGUN_NUM_BUTTONS] = {
|
||||
"Stick Up",
|
||||
"Stick Down",
|
||||
"Stick Left",
|
||||
"Stick Right",
|
||||
"START",
|
||||
"SE/BA",
|
||||
"Trigger",
|
||||
"Grip",
|
||||
"A",
|
||||
"B",
|
||||
"Aim X+",
|
||||
"Aim X-",
|
||||
"Aim Y+",
|
||||
"Aim Y-",
|
||||
"Turbo Left",
|
||||
"Turbo Right",
|
||||
"Laser",
|
||||
};
|
||||
|
||||
constexpr bool check_button_name_size(unsigned max_num_buttons)
|
||||
{
|
||||
switch (max_num_buttons)
|
||||
|
|
|
@ -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; // CxbxKrnlCleanupEx(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; // CxbxKrnlCleanupEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
|
||||
CxbxrAbortEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +154,7 @@ EmuShared::EmuShared()
|
|||
m_bEmulating_status = false;
|
||||
m_bFirstLaunch = false;
|
||||
m_bClipCursor = false;
|
||||
m_LightgunLaser = 0xF; // laser on by default on all ports
|
||||
|
||||
std::memset(m_DeviceControlNames, '\0', sizeof(m_DeviceControlNames));
|
||||
std::memset(m_DeviceName, '\0', sizeof(m_DeviceName));
|
||||
|
|
|
@ -246,10 +246,10 @@ class EmuShared : public Mutex
|
|||
void SetLogPopupTestCase(const bool value) { Lock(); m_core.bLogPopupTestCase = value; Unlock(); }
|
||||
|
||||
// ******************************************************************
|
||||
// * File storage location
|
||||
// * Data location path
|
||||
// ******************************************************************
|
||||
void GetStorageLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, xbox::max_path); Unlock(); }
|
||||
void SetStorageLocation(const char *path) { Lock(); strncpy(m_core.szStorageLocation, path, xbox::max_path); Unlock(); }
|
||||
void GetDataLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, xbox::max_path); Unlock(); }
|
||||
void SetDataLocation(const char *path) { Lock(); strncpy(m_core.szStorageLocation, path, xbox::max_path); Unlock(); }
|
||||
|
||||
// ******************************************************************
|
||||
// * ClipCursor flag Accessors
|
||||
|
@ -257,6 +257,12 @@ class EmuShared : public Mutex
|
|||
void GetClipCursorFlag(bool *value) { Lock(); *value = m_bClipCursor; Unlock(); }
|
||||
void SetClipCursorFlag(const bool value) { Lock(); m_bClipCursor = value; Unlock(); }
|
||||
|
||||
// ******************************************************************
|
||||
// * LightgunLaser flag Accessors
|
||||
// ******************************************************************
|
||||
void GetLightgunLaser(uint8_t *value, int port) { Lock(); *value = (m_LightgunLaser >> port) & 1; Unlock(); }
|
||||
void SetLightgunLaser(const uint8_t *value, int port) { Lock(); (m_LightgunLaser &= ~(1 << port)) |= ((*value) << port); Unlock(); }
|
||||
|
||||
// ******************************************************************
|
||||
// * ImGui Accessors
|
||||
// ******************************************************************
|
||||
|
@ -266,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);
|
||||
|
@ -364,6 +370,7 @@ class EmuShared : public Mutex
|
|||
#else
|
||||
unsigned int m_Reserved;
|
||||
#endif
|
||||
uint8_t m_LightgunLaser;
|
||||
bool m_bFirstLaunch;
|
||||
bool m_bClipCursor;
|
||||
unsigned int m_dwKrnlProcID; // Only used for kernel mode level.
|
||||
|
|
|
@ -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,11 +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)) {
|
||||
|
@ -47,13 +49,13 @@ bool CxbxIsElevated() {
|
|||
return fRet;
|
||||
}
|
||||
|
||||
bool 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 };
|
||||
std::string szProcArgsBuffer;
|
||||
if (!cli_config::GenCMD(szProcArgsBuffer)) {
|
||||
return false;
|
||||
return std::make_optional<std::string>("Failed to retrieve the command line arguments to launch the new emulation process");
|
||||
}
|
||||
|
||||
// TODO: Set a configuration variable for this. For now it will be within the same folder as Cxbx.exe
|
||||
|
@ -67,9 +69,12 @@ bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) {
|
|||
Using ShellExecute has proper implement. Unfortunately, we need created process handle for Debugger monitor.
|
||||
Plus ShellExecute is high level whilst CreateProcess is low level. We want to use official low level functions as possible to reduce
|
||||
cpu load cycles to get the task done.
|
||||
|
||||
Without the DETACHED_PROCESS flag, the default behavior would be for the new process to inherit the console of the parent process,
|
||||
which is wrong since we want the emulation process to have its own console allocated with AllocConsole instead.
|
||||
*/
|
||||
if (CreateProcess(nullptr, const_cast<LPSTR>(szProcArgsBuffer.c_str()), nullptr, nullptr, false, 0, nullptr, nullptr, &startupInfo, &processInfo) == 0) {
|
||||
return 0;
|
||||
if (CreateProcess(nullptr, const_cast<LPSTR>(szProcArgsBuffer.c_str()), nullptr, nullptr, false, DETACHED_PROCESS, nullptr, nullptr, &startupInfo, &processInfo) == 0) {
|
||||
return std::make_optional<std::string>("Failed to create the new emulation process. CreateProcess failed because: " + WinError2Str());
|
||||
}
|
||||
CloseHandle(processInfo.hThread);
|
||||
|
||||
|
@ -80,7 +85,7 @@ bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) {
|
|||
CloseHandle(processInfo.hProcess);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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))
|
||||
CxbxKrnlCleanupEx(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);
|
||||
|
|
45
src/common/win32/Util.cpp
Normal file
45
src/common/win32/Util.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
// ******************************************************************
|
||||
// *
|
||||
// * 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) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#include "Util.h"
|
||||
#include "Windows.h"
|
||||
|
||||
|
||||
std::string WinError2Str()
|
||||
{
|
||||
DWORD error_id = GetLastError();
|
||||
if (error_id == ERROR_SUCCESS) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
LPSTR msg_buff = nullptr;
|
||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPSTR>(&msg_buff), 0, nullptr);
|
||||
|
||||
std::string msg(msg_buff, size);
|
||||
LocalFree(msg_buff);
|
||||
|
||||
return msg;
|
||||
}
|
30
src/common/win32/Util.h
Normal file
30
src/common/win32/Util.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
// ******************************************************************
|
||||
// *
|
||||
// * 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) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string WinError2Str();
|
|
@ -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);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "common\Error.h"
|
||||
#include "common/xbox/Types.hpp"
|
||||
#include "core/kernel/common/types.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
|
@ -49,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();
|
||||
|
@ -80,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
|
||||
|
@ -156,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
|
||||
|
@ -257,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,14 +20,14 @@ bool ImGuiUI::Initialize()
|
|||
IMGUI_CHECKVERSION();
|
||||
m_imgui_context = ImGui::CreateContext();
|
||||
if (!m_imgui_context) {
|
||||
CxbxKrnlCleanup("Unable to create ImGui context!");
|
||||
CxbxrAbort("Unable to create ImGui context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
#if 0 // TODO: Currently most voted for memory, so this block of code is disabled. And may will add an option between file vs memory.
|
||||
// May be best ideal to do manual update call than ImGui's internal auto update.
|
||||
g_EmuShared->GetStorageLocation(m_file_path);
|
||||
g_EmuShared->GetDataLocation(m_file_path);
|
||||
if (m_file_path[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
@ -45,6 +45,11 @@ bool ImGuiUI::Initialize()
|
|||
|
||||
g_EmuShared->GetOverlaySettings(&m_settings);
|
||||
g_EmuShared->GetFlagsLLE(&m_lle_flags);
|
||||
|
||||
// Internal initialize (when necessary, move into its own function.)
|
||||
fps_counter = 30.0f;
|
||||
|
||||
// Miscs
|
||||
m_audio.Initialize();
|
||||
m_video.Initialize();
|
||||
|
||||
|
@ -56,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) {
|
||||
CxbxKrnlCleanup("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);
|
||||
|
@ -76,6 +81,36 @@ void ImGuiUI::ToggleImGui()
|
|||
g_EmuShared->SetImGuiFocusFlag(m_is_focus);
|
||||
}
|
||||
|
||||
static clock_t g_DeltaTime = 0; // Used for benchmarking/fps count
|
||||
static unsigned int g_Frames = 0;
|
||||
|
||||
// ******************************************************************
|
||||
// * update the current milliseconds per frame
|
||||
// ******************************************************************
|
||||
void ImGuiUI::UpdateCurrentMSpFAndFPS() {
|
||||
if (g_EmuShared) {
|
||||
|
||||
fps_counter = (float)(g_Frames * 0.5 + fps_counter * 0.5);
|
||||
g_EmuShared->SetCurrentFPS(&fps_counter);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiUI::UpdateFPSCounter()
|
||||
{
|
||||
static clock_t lastDrawFunctionCallTime = 0;
|
||||
clock_t currentDrawFunctionCallTime = clock();
|
||||
|
||||
g_DeltaTime += currentDrawFunctionCallTime - lastDrawFunctionCallTime;
|
||||
lastDrawFunctionCallTime = currentDrawFunctionCallTime;
|
||||
g_Frames++;
|
||||
|
||||
if (g_DeltaTime >= CLOCKS_PER_SEC) {
|
||||
UpdateCurrentMSpFAndFPS();
|
||||
g_Frames = 0;
|
||||
g_DeltaTime -= CLOCKS_PER_SEC;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiUI::DrawMenu()
|
||||
{
|
||||
if (!m_is_focus) {
|
||||
|
@ -85,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);
|
||||
|
@ -104,21 +142,20 @@ 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) {
|
||||
|
||||
float fps_counter;
|
||||
g_EmuShared->GetCurrentFPS(&fps_counter);
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "FPS: %.2f MS / F : %.2f", fps_counter, (float)(1000.0 / fps_counter));
|
||||
}
|
||||
|
||||
|
@ -148,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;
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
void ToggleImGui();
|
||||
bool IsImGuiFocus();
|
||||
void UpdateFPSCounter();
|
||||
|
||||
void DrawMenu();
|
||||
void DrawWidgets();
|
||||
|
@ -48,6 +49,8 @@ protected:
|
|||
callback(this, arg);
|
||||
}
|
||||
|
||||
void UpdateCurrentMSpFAndFPS();
|
||||
|
||||
std::mutex m_imgui_mutex;
|
||||
ImGuiContext* m_imgui_context;
|
||||
char m_file_path[FILENAME_MAX+1];
|
||||
|
@ -57,6 +60,7 @@ protected:
|
|||
ImGuiVideo m_video;
|
||||
overlay_settings m_settings;
|
||||
unsigned int m_lle_flags;
|
||||
float fps_counter;
|
||||
// Make them as settings storage.
|
||||
/*bool m_show_fps;
|
||||
bool m_show_LLE_stats;
|
||||
|
|
|
@ -14,6 +14,13 @@
|
|||
#include "core/kernel/init/CxbxKrnl.h"
|
||||
#include "core/hle/D3D8/XbVertexBuffer.h"
|
||||
|
||||
const ImColor ImGuiVideo::m_laser_col[4] = {
|
||||
ImColor(ImVec4(1.0f, 0.0f, 0.0f, 1.0f)), // player1: red
|
||||
ImColor(ImVec4(0.0f, 1.0f, 0.0f, 1.0f)), // player2: green
|
||||
ImColor(ImVec4(0.0f, 0.0f, 1.0f, 1.0f)), // player3: blue
|
||||
ImColor(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)) // player4: yellow
|
||||
};
|
||||
|
||||
bool ImGuiVideo::Initialize()
|
||||
{
|
||||
g_EmuShared->GetImGuiVideoWindows(&m_windows);
|
||||
|
@ -43,6 +50,15 @@ void ImGuiVideo::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
|
|||
if (ImGui::CollapsingHeader("Vertex Buffer Cache", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
VertexBufferConverter.DrawCacheStats();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Render the lightgun laser
|
||||
for (int port = PORT_1; port < XBOX_NUM_PORTS; ++port) {
|
||||
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN && g_devs[port].info.ligthgun.laser) {
|
||||
ImGui::Begin("Laser", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::GetForegroundDrawList()->AddCircleFilled(g_InputDeviceManager.CalcLaserPos(port), 5, m_laser_col[port], 0);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,5 @@ public:
|
|||
|
||||
protected:
|
||||
imgui_video_windows m_windows;
|
||||
static const ImColor m_laser_col[4];
|
||||
};
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
||||
|
@ -1912,14 +1908,17 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetTexture)
|
|||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_SetStateVB (D3D::CDevice::SetStateVB)
|
||||
// * patch: CDevice_SetStateVB (D3D::CDevice::SetStateVB)
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_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: D3DDevice_SetStateUP (D3D::CDevice::SetStateUP)
|
||||
// * patch: CDevice_SetStateUP (D3D::CDevice::SetStateUP)
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_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)
|
||||
CxbxKrnlCleanup("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)
|
||||
CxbxKrnlCleanup("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:
|
||||
CxbxKrnlCleanup("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:
|
||||
CxbxKrnlCleanup("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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue