mirror of
https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git
synced 2025-04-02 11:11:52 -04:00
Compare commits
211 commits
CI-1077115
...
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 | ||
|
9973ec7b6f |
122 changed files with 5271 additions and 2638 deletions
112
.github/labeler.yml
vendored
112
.github/labeler.yml
vendored
|
@ -1,75 +1,111 @@
|
|||
# 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*/**'
|
||||
- '**/README.md'
|
||||
- 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/core/common/imgui/*'
|
||||
- 'src/gui/**'
|
||||
- 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/**'
|
||||
|
|
16
.github/workflows/CI.yml
vendored
16
.github/workflows/CI.yml
vendored
|
@ -30,19 +30,21 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
configuration: [Release, Debug]
|
||||
vsver: [2022]
|
||||
vsver: [2019]
|
||||
winver: [7]
|
||||
sdkver: [10.0.22621.0]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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
|
||||
run: cmake --build build --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS
|
||||
- name: Prepare artifacts
|
||||
if: matrix.configuration == 'Release'
|
||||
run: cmake --install build --config ${{ matrix.configuration }} --prefix artifacts
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.configuration == 'Release'
|
||||
with:
|
||||
name: CxbxReloaded-${{ matrix.configuration }}-VS${{ matrix.vsver }}
|
||||
|
@ -57,9 +59,9 @@ jobs:
|
|||
needs: build-windows
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Re-zip artifacts
|
||||
|
@ -72,7 +74,7 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "::set-output name=tag_name::CI-${GITHUB_SHA::7}"
|
||||
echo "tag_name=CI-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||||
- name: Create Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
4
.github/workflows/autoclose.yml
vendored
4
.github/workflows/autoclose.yml
vendored
|
@ -7,9 +7,9 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
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
|
||||
|
|
2
.github/workflows/pull-request.yml
vendored
2
.github/workflows/pull-request.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Labeler
|
||||
uses: actions/labeler@v4
|
||||
uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sync-labels: true
|
||||
|
|
12
.gitmodules
vendored
12
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
|||
[submodule "import/subhook"]
|
||||
path = import/subhook
|
||||
url = https://github.com/Zeex/subhook.git
|
||||
url = https://github.com/Cxbx-Reloaded/subhook.git
|
||||
shallow = true
|
||||
[submodule "import/cs_x86"]
|
||||
path = import/cs_x86
|
||||
|
@ -37,5 +37,13 @@
|
|||
shallow = true
|
||||
[submodule "import/libusb"]
|
||||
path = import/libusb
|
||||
url = https://github.com/libusb/libusb
|
||||
url = https://github.com/Cxbx-Reloaded/libusb
|
||||
branch = deadlock_fix
|
||||
shallow = true
|
||||
[submodule "import/nv2a_vsh_cpu"]
|
||||
path = import/nv2a_vsh_cpu
|
||||
url = https://github.com/abaire/nv2a_vsh_cpu.git
|
||||
[submodule "import/mio"]
|
||||
path = import/mio
|
||||
url = https://github.com/mandreyel/mio.git
|
||||
shadow = true
|
||||
|
|
|
@ -22,6 +22,8 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/XbSymbolDatabase")
|
|||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/SDL2" EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/mio" EXCLUDE_FROM_ALL)
|
||||
|
||||
# Cxbx-Reloaded projects
|
||||
set(CXBXR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
|
@ -39,6 +41,9 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/imgui")
|
|||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libusb")
|
||||
|
||||
set(nv2a_vsh_cpu_UNIT_TEST OFF)
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/nv2a_vsh_cpu" EXCLUDE_FROM_ALL)
|
||||
|
||||
# Split the files into group for which project is likely
|
||||
# going to be used for both header and source files.
|
||||
# Then move only specific project files into their
|
||||
|
@ -74,7 +79,6 @@ file (GLOB CXBXR_HEADER_COMMON
|
|||
"${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"
|
||||
|
@ -141,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"
|
||||
|
@ -149,7 +154,7 @@ 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"
|
||||
|
@ -249,9 +254,7 @@ file (GLOB CXBXR_SOURCE_COMMON
|
|||
"${CXBXR_ROOT_DIR}/src/common/Settings.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"
|
||||
|
@ -324,7 +327,7 @@ 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"
|
||||
|
@ -345,6 +348,7 @@ 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/XGRAPHIC/XGraphic.cpp"
|
||||
|
@ -375,6 +379,8 @@ file (GLOB CXBXR_SOURCE_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/JvsIO.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/MediaBoard.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/EEPROMDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/MCPXDevice.cpp"
|
||||
|
@ -437,6 +443,10 @@ set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymb
|
|||
PROPERTIES FOLDER Cxbx-Reloaded
|
||||
)
|
||||
|
||||
set_target_properties(nv2a_vsh_emulator nv2a_vsh_disassembler nv2a_vsh_cpu
|
||||
PROPERTIES FOLDER Cxbx-Reloaded/nv2a_vsh
|
||||
)
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# Configure startup project
|
||||
set_property(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" PROPERTY VS_STARTUP_PROJECT cxbx)
|
||||
|
@ -464,14 +474,19 @@ install(FILES ${cxbxr_INSTALL_files}
|
|||
DESTINATION bin
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/CxbxPixelShaderTemplate.hlsl"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
|
||||
DESTINATION bin/hlsl
|
||||
# Copy HLSL files to the output directory, which are loaded at runtime
|
||||
set(CXBXR_HLSL_FILES ${CXBXR_HEADER_EMU})
|
||||
list(FILTER CXBXR_HLSL_FILES INCLUDE REGEX ".*/src/core/hle/D3D8/Direct3D9/[^/]+\.hlsli?")
|
||||
add_custom_command(
|
||||
TARGET misc-batch POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl"
|
||||
# These files can be edited.
|
||||
# Create backup copies for convenience of restoring original shader behaviour.
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup"
|
||||
)
|
||||
install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl DESTINATION bin)
|
||||
|
||||
set(cxbxr_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ Cxbx-Reloaded is an emulator for running Microsoft Xbox (and eventually, Chihiro
|
|||
* [32-bit (x86) Visual C++ 2022 Redistributable](https://aka.ms/vs/17/release/vc_redist.x86.exe)
|
||||
* [Npcap *(used for network emulation)*](https://nmap.org/npcap/#download)
|
||||
* Make sure to enable winpcap compatibility mode.
|
||||
* WinUSB compliant driver
|
||||
* [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
|
||||
|
|
2
import/SDL2
vendored
2
import/SDL2
vendored
|
@ -1 +1 @@
|
|||
Subproject commit b424665e0899769b200231ba943353a5fee1b6b6
|
||||
Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd
|
2
import/XbSymbolDatabase
vendored
2
import/XbSymbolDatabase
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 75ce58fa8d135ef0a75bee729cde9542eda393b6
|
||||
Subproject commit 774111351210e6f340246d6fb32741b09708f381
|
2
import/libusb
vendored
2
import/libusb
vendored
|
@ -1 +1 @@
|
|||
Subproject commit c6a35c56016ea2ab2f19115d2ea1e85e0edae155
|
||||
Subproject commit cf178f1fac38426990425cc034f7d4b8c9e1e388
|
1
import/mio
vendored
Submodule
1
import/mio
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 3f86a95c0784d73ce6815237ec33ed25f233b643
|
1
import/nv2a_vsh_cpu
vendored
Submodule
1
import/nv2a_vsh_cpu
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit ead0af5dee49e408c005151393f8a7dbcd27026c
|
2
import/subhook
vendored
2
import/subhook
vendored
|
@ -1 +1 @@
|
|||
Subproject commit a86a253e6e764275a21ab7c9348f84d1f95e409f
|
||||
Subproject commit 83d4e1ebef3588fae48b69a7352cc21801cb70bc
|
|
@ -42,8 +42,11 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
LTC_NO_PRNGS
|
||||
LTC_NO_MISC
|
||||
LTC_NO_PROTOTYPES
|
||||
|
||||
# Enable Chihiro work
|
||||
CHIHIRO_WORK
|
||||
)
|
||||
|
||||
|
||||
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
|
||||
add_compile_options(
|
||||
# Catch synchronous (C++) exceptions only
|
||||
|
@ -65,7 +68,7 @@ XXH_INLINE_ALL
|
|||
)
|
||||
|
||||
file (GLOB RESOURCES
|
||||
|
||||
|
||||
"${CXBXR_ROOT_DIR}/CONTRIBUTORS"
|
||||
"${CXBXR_ROOT_DIR}/COPYING"
|
||||
"${CXBXR_ROOT_DIR}/README.md"
|
||||
|
@ -87,7 +90,7 @@ 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}
|
||||
)
|
||||
|
@ -118,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
|
||||
|
@ -129,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 \
|
||||
|
@ -139,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-"
|
||||
|
@ -147,7 +150,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -167,6 +170,7 @@ set(WINS_LIB
|
|||
comctl32
|
||||
XINPUT9_1_0
|
||||
Iphlpapi
|
||||
Dwmapi
|
||||
)
|
||||
|
||||
target_link_libraries(cxbx
|
||||
|
@ -176,6 +180,7 @@ target_link_libraries(cxbx
|
|||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -48,6 +48,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
|
||||
# Use inline XXHash version
|
||||
XXH_INLINE_ALL
|
||||
|
||||
# Enable Chihiro work
|
||||
CHIHIRO_WORK
|
||||
)
|
||||
add_compile_options(
|
||||
/EHs
|
||||
|
@ -128,7 +131,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
# Set optimization options for release build
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
|
||||
/Zi \
|
||||
/Ob2 \
|
||||
/Ob3 \
|
||||
/Oi \
|
||||
/Ot \
|
||||
/GL \
|
||||
|
@ -141,7 +144,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -171,6 +174,8 @@ target_link_libraries(cxbxr-emu
|
|||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
nv2a_vsh_emulator
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -84,6 +84,4 @@ extern volatile bool g_bPrintfOn;
|
|||
#define CxbxSetThreadName(Name)
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#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"
|
||||
|
|
|
@ -31,6 +31,8 @@ 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.
|
||||
|
|
|
@ -49,8 +49,9 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value);
|
|||
// ******************************************************************
|
||||
|
||||
typedef enum class _IPC_UPDATE_KERNEL {
|
||||
CONFIG_LOGGING_SYNC = 0
|
||||
, CONFIG_INPUT_SYNC
|
||||
CONFIG_LOGGING_SYNC = 0,
|
||||
CONFIG_INPUT_SYNC,
|
||||
CONFIG_CHANGE_TIME
|
||||
} IPC_UPDATE_KERNEL;
|
||||
|
||||
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd);
|
||||
|
|
|
@ -313,7 +313,7 @@ PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, con
|
|||
uType |= MB_OKCANCEL;
|
||||
break;
|
||||
case PopupButtons::AbortRetryIgnore:
|
||||
uType |= MB_RETRYCANCEL;
|
||||
uType |= MB_ABORTRETRYIGNORE;
|
||||
break;
|
||||
case PopupButtons::YesNoCancel:
|
||||
uType |= MB_YESNOCANCEL;
|
||||
|
|
|
@ -117,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";
|
||||
|
@ -547,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 ===========
|
||||
|
||||
|
@ -735,8 +741,11 @@ bool Settings::Save(std::string file_path)
|
|||
|
||||
// ==== Overlay Begin =======
|
||||
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.build_hash, m_overlay.build_hash, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.FPS, m_overlay.fps, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.hle_lle_stats, m_overlay.hle_lle_stats, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.title_name, m_overlay.title_name, nullptr, true);
|
||||
m_si.SetBoolValue(section_overlay, sect_overlay_keys.file_name, m_overlay.file_name, nullptr, true);
|
||||
|
||||
// ==== Overlay End =========
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
// ******************************************************************
|
||||
#ifndef SETTINGS_HPP
|
||||
#define SETTINGS_HPP
|
||||
#include "Cxbx.h"
|
||||
|
||||
#include "SimpleIni.h"
|
||||
#include "common\input\InputManager.h"
|
||||
|
|
|
@ -27,18 +27,43 @@
|
|||
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include "Timer.h"
|
||||
#include "common\util\CxbxUtil.h"
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#ifdef __linux__
|
||||
#include <time.h>
|
||||
#endif
|
||||
#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)
|
||||
|
@ -70,174 +95,83 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
|
|||
}
|
||||
}
|
||||
|
||||
// Virtual clocks will probably become useful once LLE CPU is implemented, but for now we don't need them.
|
||||
// See the QEMUClockType QEMU_CLOCK_VIRTUAL of XQEMU for more info.
|
||||
#define CLOCK_REALTIME 0
|
||||
//#define CLOCK_VIRTUALTIME 1
|
||||
|
||||
|
||||
// Vector storing all the timers created
|
||||
static std::vector<TimerObject*> TimerList;
|
||||
// The frequency of the high resolution clock of the host, and the start time
|
||||
int64_t HostQPCFrequency, HostQPCStartTime;
|
||||
// Lock to acquire when accessing TimerList
|
||||
std::mutex TimerMtx;
|
||||
|
||||
|
||||
// Returns the current time of the timer
|
||||
uint64_t GetTime_NS(TimerObject* Timer)
|
||||
// NOTE: the pit device is not implemented right now, so we put this here
|
||||
static uint64_t pit_next(uint64_t now)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
uint64_t Ret = Timer_GetScaledPerformanceCounter(SCALE_S_IN_NS);
|
||||
#elif __linux__
|
||||
static struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
uint64_t Ret = Muldiv64(ts.tv_sec, SCALE_S_IN_NS, 1) + ts.tv_nsec;
|
||||
#else
|
||||
#error "Unsupported OS"
|
||||
#endif
|
||||
return Ret;
|
||||
constexpr uint64_t pit_period = 1000;
|
||||
uint64_t next = pit_last + pit_period;
|
||||
|
||||
if (now >= next) {
|
||||
xbox::KiClockIsr(now - pit_last);
|
||||
pit_last = get_now();
|
||||
return pit_period;
|
||||
}
|
||||
|
||||
return pit_last + pit_period - now; // time remaining until next clock interrupt
|
||||
}
|
||||
|
||||
// Calculates the next expire time of the timer
|
||||
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
|
||||
static void update_non_periodic_events()
|
||||
{
|
||||
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load();
|
||||
// update dsound
|
||||
dsound_worker();
|
||||
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deallocates the memory of the timer
|
||||
void Timer_Destroy(TimerObject* Timer)
|
||||
uint64_t get_now()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
assert(index != TimerList.size());
|
||||
delete Timer;
|
||||
TimerList.erase(TimerList.begin() + index);
|
||||
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;
|
||||
}
|
||||
|
||||
void Timer_Shutdown()
|
||||
static uint64_t get_next(uint64_t now)
|
||||
{
|
||||
unsigned i, iXboxThreads = 0;
|
||||
TimerMtx.lock();
|
||||
|
||||
for (i = 0; i < TimerList.size(); i++) {
|
||||
TimerObject* Timer = TimerList[i];
|
||||
// We only need to terminate host threads.
|
||||
if (!Timer->IsXboxTimer) {
|
||||
Timer_Exit(Timer);
|
||||
}
|
||||
// If the thread is xbox, we need to increment for while statement check
|
||||
else {
|
||||
iXboxThreads++;
|
||||
}
|
||||
}
|
||||
|
||||
// Only perform wait for host threads, otherwise xbox threads are
|
||||
// already handled within xbox kernel for shutdown process. See CxbxrKrnlSuspendThreads function.
|
||||
int counter = 0;
|
||||
while (iXboxThreads != TimerList.size()) {
|
||||
if (counter >= 8) {
|
||||
break;
|
||||
}
|
||||
TimerMtx.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
TimerMtx.lock();
|
||||
counter++;
|
||||
}
|
||||
TimerList.clear();
|
||||
TimerMtx.unlock();
|
||||
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());
|
||||
}
|
||||
|
||||
// Thread that runs the timer
|
||||
void NTAPI ClockThread(void *TimerArg)
|
||||
xbox::void_xt NTAPI system_events(xbox::PVOID arg)
|
||||
{
|
||||
TimerObject *Timer = static_cast<TimerObject *>(TimerArg);
|
||||
if (!Timer->Name.empty()) {
|
||||
CxbxSetThreadName(Timer->Name.c_str());
|
||||
}
|
||||
if (!Timer->IsXboxTimer) {
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
}
|
||||
// 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);
|
||||
|
||||
uint64_t NewExpireTime = GetNextExpireTime(Timer);
|
||||
// Always run this thread at dpc level to prevent it from ever executing APCs/DPCs
|
||||
xbox::KeRaiseIrqlToDpcLevel();
|
||||
|
||||
while (true) {
|
||||
if (GetTime_NS(Timer) > NewExpireTime) {
|
||||
if (Timer->Exit.load()) {
|
||||
Timer_Destroy(Timer);
|
||||
return;
|
||||
const uint64_t last_time = get_now();
|
||||
const uint64_t nearest_next = get_next(last_time);
|
||||
|
||||
while (true) {
|
||||
update_non_periodic_events();
|
||||
uint64_t elapsed_us = get_now() - last_time;
|
||||
if (elapsed_us >= nearest_next) {
|
||||
break;
|
||||
}
|
||||
Timer->Callback(Timer->Opaque);
|
||||
NewExpireTime = GetNextExpireTime(Timer);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
Sleep(1); // prevent burning the cpu
|
||||
}
|
||||
}
|
||||
|
||||
// Changes the expire time of a timer
|
||||
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms)
|
||||
{
|
||||
Timer->ExpireTime_MS.store(Expire_ms);
|
||||
}
|
||||
|
||||
// Destroys the timer
|
||||
void Timer_Exit(TimerObject* Timer)
|
||||
{
|
||||
Timer->Exit.store(true);
|
||||
}
|
||||
|
||||
// Allocates the memory for the timer object
|
||||
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer)
|
||||
{
|
||||
std::lock_guard<std::mutex>lock(TimerMtx);
|
||||
TimerObject* pTimer = new TimerObject;
|
||||
pTimer->Type = CLOCK_REALTIME;
|
||||
pTimer->Callback = Callback;
|
||||
pTimer->ExpireTime_MS.store(0);
|
||||
pTimer->Exit.store(false);
|
||||
pTimer->Opaque = Arg;
|
||||
pTimer->Name = Name.empty() ? "Unnamed thread" : std::move(Name);
|
||||
pTimer->IsXboxTimer = IsXboxTimer;
|
||||
TimerList.emplace_back(pTimer);
|
||||
|
||||
return pTimer;
|
||||
}
|
||||
|
||||
// Starts the timer
|
||||
// Expire_MS must be expressed in NS
|
||||
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
|
||||
{
|
||||
Timer->ExpireTime_MS.store(Expire_MS);
|
||||
if (Timer->IsXboxTimer) {
|
||||
xbox::HANDLE hThread;
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, ClockThread, Timer, FALSE);
|
||||
}
|
||||
else {
|
||||
std::thread(ClockThread, Timer).detach();
|
||||
}
|
||||
}
|
||||
|
||||
// Retrives the frequency of the high resolution clock of the host
|
||||
void Timer_Init()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&HostQPCFrequency));
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&HostQPCStartTime));
|
||||
#elif __linux__
|
||||
ClockFrequency = 0;
|
||||
#else
|
||||
#error "Unsupported OS"
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period)
|
||||
{
|
||||
LARGE_INTEGER currentQPC;
|
||||
|
|
|
@ -40,33 +40,12 @@
|
|||
#define SCALE_MS_IN_US 1000
|
||||
#define SCALE_US_IN_US 1
|
||||
|
||||
/* typedef of the timer object and the callback function */
|
||||
typedef void(*TimerCB)(void*);
|
||||
typedef struct _TimerObject
|
||||
{
|
||||
int Type; // timer type
|
||||
std::atomic_uint64_t ExpireTime_MS; // when the timer expires (ms)
|
||||
std::atomic_bool Exit; // indicates that the timer should be destroyed
|
||||
TimerCB Callback; // function to call when the timer expires
|
||||
void* Opaque; // opaque argument to pass to the callback
|
||||
std::string Name; // the name of the timer thread (if any)
|
||||
bool IsXboxTimer; // indicates that the timer should run on the Xbox CPU
|
||||
}
|
||||
TimerObject;
|
||||
|
||||
extern int64_t HostQPCFrequency;
|
||||
|
||||
/* Timer exported functions */
|
||||
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer);
|
||||
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS);
|
||||
void Timer_Exit(TimerObject* Timer);
|
||||
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
|
||||
uint64_t GetTime_NS(TimerObject* Timer);
|
||||
void Timer_Init();
|
||||
void Timer_Shutdown();
|
||||
|
||||
void timer_init();
|
||||
uint64_t get_now();
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
|
||||
|
||||
void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -105,8 +105,9 @@ bool HandleFirstLaunch()
|
|||
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();
|
||||
//CxbxrKrnlSuspendThreads();
|
||||
|
||||
if (g_io_mu_metadata) {
|
||||
delete g_io_mu_metadata;
|
||||
|
@ -120,9 +121,6 @@ bool HandleFirstLaunch()
|
|||
}
|
||||
|
||||
// NOTE: Require to be after g_renderbase's shutdown process.
|
||||
// Next thing we need to do is shutdown our timer threads.
|
||||
Timer_Shutdown();
|
||||
|
||||
// NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call!
|
||||
// Shutdown the memory manager
|
||||
g_VMManager.Shutdown();
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
// ******************************************************************
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
|
|
|
@ -107,13 +107,8 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
|
|||
|
||||
case WM_RBUTTONDOWN: {
|
||||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
if (wParam & MK_SHIFT) {
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
|
||||
}
|
||||
else if (!(wParam & ~MK_RBUTTON)) {
|
||||
button->ClearText();
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
}
|
||||
button->ClearText();
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -128,7 +123,7 @@ LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam,
|
|||
{
|
||||
// Remove the window subclass when this window is destroyed
|
||||
case WM_NCDESTROY: {
|
||||
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
|
||||
RemoveWindowSubclass(hWnd, ButtonLightgunSubclassProc, uIdSubclass);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -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,6 +25,7 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#include <algorithm>
|
||||
#include "Button.h"
|
||||
#include "InputManager.h"
|
||||
#include "layout_xbox_device.h"
|
||||
|
@ -57,7 +58,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
|||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(button_sbc_id); i++) {
|
||||
m_buttons.push_back(new Button(button_sbc_id[i], i, hwnd, wnd));
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_no_toggle);
|
||||
|
||||
// Install the subclass for the button control
|
||||
SetWindowSubclass(GetDlgItem(hwnd, button_sbc_id[i]), ButtonSbcSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
|
||||
|
|
|
@ -170,7 +170,7 @@ namespace Libusb
|
|||
}
|
||||
else {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(SupportedDevices_VidPid); ++i) {
|
||||
if ((Desc->idVendor = SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
|
||||
if ((Desc->idVendor == SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
|
||||
m_Type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
|
||||
m_UcType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
|
@ -185,9 +185,18 @@ namespace Libusb
|
|||
|
||||
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
|
||||
|
||||
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
|
||||
// The code below assumes that third-party controllers follow suit.
|
||||
if (libusb_open(Dev, &m_hDev) == 0) {
|
||||
// check if we are able to open device through libusb
|
||||
if (int err = libusb_open(Dev, &m_hDev)) {
|
||||
// Couldn't open device, create an error log report then don't use it.
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Unable to open original xbox device \"%s\" (%hX:%hX), libusb's error was: %s",
|
||||
m_Name.c_str(), Desc->idVendor, Desc->idProduct, libusb_strerror(err));
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
return;
|
||||
}
|
||||
// If we are able to open device, continue with query process.
|
||||
else {
|
||||
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
|
||||
// The code below assumes that third-party controllers follow suit.
|
||||
libusb_config_descriptor *ConfDesc;
|
||||
if (libusb_get_active_config_descriptor(Dev, &ConfDesc) == 0) {
|
||||
if (ConfDesc->bNumInterfaces == 1) {
|
||||
|
@ -211,7 +220,7 @@ namespace Libusb
|
|||
}
|
||||
}
|
||||
EmuLog(LOG_LEVEL::INFO, "Out endpoint %s", m_HasEndpointOut ? "present" : "not present");
|
||||
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum) != 0) {
|
||||
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum)) {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because libusb could not claim its interface. The error was: %s",
|
||||
m_Name.c_str(), libusb_strerror(err));
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define LOG_PREFIX CXBXR_MODULE::SDL
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "SdlJoystick.h"
|
||||
|
|
|
@ -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() */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -101,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;
|
||||
|
|
|
@ -41,14 +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];
|
||||
|
||||
|
@ -58,23 +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
|
||||
|
||||
// verify Xbe file was opened successfully
|
||||
if(XbeFile == 0) {
|
||||
|
||||
std::string XbeName = path(x_szFilename).filename().string(); // recover the xbe name
|
||||
// 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
|
||||
// Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons
|
||||
#ifdef CXBXR_EMU
|
||||
&& !bFromGUI
|
||||
) {
|
||||
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;
|
||||
|
||||
|
@ -89,14 +87,14 @@ 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");
|
||||
|
||||
|
@ -111,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
|
||||
|
@ -726,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];
|
||||
}
|
||||
|
||||
|
@ -819,5 +829,15 @@ XbeType Xbe::GetXbeType()
|
|||
return XbeType::xtRetail;
|
||||
}
|
||||
|
||||
uint32_t Xbe::GetDiscVersion()
|
||||
{
|
||||
return m_Certificate.dwVersion & 0xFF;
|
||||
}
|
||||
|
||||
uint32_t Xbe::GetPatchVersion()
|
||||
{
|
||||
return (m_Certificate.dwVersion & 0xFFFFFF00) >> 8;
|
||||
}
|
||||
|
||||
template auto Xbe::FindSection<true>(const char *zsSectionName);
|
||||
template auto Xbe::FindSection<false>(const char *zsSectionName);
|
||||
|
|
|
@ -50,7 +50,7 @@ class Xbe : public Error
|
|||
{
|
||||
public:
|
||||
// construct via Xbe file
|
||||
Xbe(const char *x_szFilename, bool bFromGUI);
|
||||
Xbe(const char *x_szFilename);
|
||||
|
||||
// deconstructor
|
||||
~Xbe();
|
||||
|
@ -81,10 +81,13 @@ class Xbe : public Error
|
|||
void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|");
|
||||
|
||||
// Convert game region field to string
|
||||
const char *GameRegionToString();
|
||||
const char *GameRegionToString(uint32_t dwRegionFlags = 0);
|
||||
|
||||
XbeType GetXbeType();
|
||||
|
||||
uint32_t GetDiscVersion();
|
||||
uint32_t GetPatchVersion();
|
||||
|
||||
// Xbe header
|
||||
#include "AlignPrefix1.h"
|
||||
struct Header
|
||||
|
@ -157,7 +160,7 @@ class Xbe : public Error
|
|||
uint32_t dwAllowedMedia; // 0x009C - allowed media types
|
||||
uint32_t dwGameRegion; // 0x00A0 - game region
|
||||
uint32_t dwGameRatings; // 0x00A4 - game ratings
|
||||
uint32_t dwDiskNumber; // 0x00A8 - disk number
|
||||
uint32_t dwDiscNumber; // 0x00A8 - disc number
|
||||
uint32_t dwVersion; // 0x00AC - version
|
||||
uint8_t bzLanKey[16]; // 0x00B0 - lan key
|
||||
uint8_t bzSignatureKey[16]; // 0x00C0 - signature key
|
||||
|
@ -258,6 +261,9 @@ class Xbe : public Error
|
|||
// Xbe original path
|
||||
char m_szPath[MAX_PATH];
|
||||
|
||||
// Xbe original file name
|
||||
char m_szFileName[MAX_PATH];
|
||||
|
||||
// Xbe ascii title, translated from certificate title
|
||||
char m_szAsciiTitle[41];
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ 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)
|
||||
{
|
||||
|
@ -359,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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -120,8 +120,11 @@ void ImGuiUI::DrawMenu()
|
|||
if (ImGui::BeginMenu("Settings")) {
|
||||
if (ImGui::BeginMenu("Overlay")) {
|
||||
bool bChanged = false;
|
||||
bChanged |= ImGui::MenuItem("Show Build Hash", NULL, &m_settings.build_hash);
|
||||
bChanged |= ImGui::MenuItem("Show FPS", NULL, &m_settings.fps);
|
||||
bChanged |= ImGui::MenuItem("Show HLE/LLE Stats", NULL, &m_settings.hle_lle_stats);
|
||||
bChanged |= ImGui::MenuItem("Show Title Name", NULL, &m_settings.title_name);
|
||||
bChanged |= ImGui::MenuItem("Show File Name", NULL, &m_settings.file_name);
|
||||
if (bChanged) {
|
||||
g_EmuShared->SetOverlaySettings(&m_settings);
|
||||
ipc_send_gui_update(IPC_UPDATE_GUI::OVERLAY, 1);
|
||||
|
@ -139,17 +142,19 @@ void ImGuiUI::DrawMenu()
|
|||
|
||||
void ImGuiUI::DrawWidgets()
|
||||
{
|
||||
if (m_settings.fps || m_settings.hle_lle_stats) {
|
||||
|
||||
constexpr auto overlay_window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing;
|
||||
bool overlay_show_topright = m_settings.fps
|
||||
|| m_settings.hle_lle_stats
|
||||
|| m_settings.title_name
|
||||
|| m_settings.file_name;
|
||||
if (overlay_show_topright) {
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - (IMGUI_MIN_DIST_SIDE/* * m_backbuffer_scale*/),
|
||||
IMGUI_MIN_DIST_TOP/* * m_backbuffer_scale*/), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(200.0f/* * m_backbuffer_scale*/, 0.0f));
|
||||
ImGui::SetNextWindowBgAlpha(0.5f);
|
||||
if (ImGui::Begin("overlay_stats", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) {
|
||||
|
||||
if (ImGui::Begin("overlay_stats_topright", nullptr, overlay_window_flags)) {
|
||||
if (m_settings.fps) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "FPS: %.2f MS / F : %.2f", fps_counter, (float)(1000.0 / fps_counter));
|
||||
}
|
||||
|
@ -180,8 +185,28 @@ void ImGuiUI::DrawWidgets()
|
|||
- ImGui::GetScrollX() - 2 * ImGui::GetStyle().ItemSpacing.x);
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), flagString.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (m_settings.title_name) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Title: %.41s", CxbxKrnl_Xbe->m_szAsciiTitle);
|
||||
}
|
||||
|
||||
if (m_settings.file_name) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "File: %.260s", CxbxKrnl_Xbe->m_szFileName);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (m_settings.build_hash) {
|
||||
ImGui::SetNextWindowPos(ImVec2(IMGUI_MIN_DIST_SIDE, ImGui::GetIO().DisplaySize.y - IMGUI_MIN_DIST_SIDE/* * m_backbuffer_scale*/),
|
||||
ImGuiCond_Always, ImVec2(0.0f, 1.0f));
|
||||
ImGui::SetNextWindowBgAlpha(0.5f);
|
||||
if (ImGui::Begin("overlay_stats_bottom", nullptr, overlay_window_flags)) {
|
||||
if (m_settings.build_hash) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Build: %s", GetGitVersionStr());
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGuiWindowFlags input_handler = m_is_focus ? ImGuiWindowFlags_None : ImGuiWindowFlags_NoInputs;
|
||||
|
|
|
@ -50,8 +50,8 @@ void ImGuiVideo::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
|
|||
if (ImGui::CollapsingHeader("Vertex Buffer Cache", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
VertexBufferConverter.DrawCacheStats();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Render the lightgun laser
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
|
||||
R"DELIMITER(
|
||||
|
||||
struct PS_INPUT // Declared identical to vertex shader output (see VS_OUTPUT)
|
||||
{
|
||||
float2 iPos : VPOS; // Screen space x,y pixel location
|
||||
|
@ -54,7 +51,8 @@ uniform const float4 FC1 : register(c17); // Note : Maps to PSH_XBOX_CONSTANT_FC
|
|||
uniform const float4 BEM[4] : register(c19); // Note : PSH_XBOX_CONSTANT_BEM for 4 texture stages
|
||||
uniform const float4 LUM[4] : register(c23); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
|
||||
uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
|
||||
|
||||
uniform const float4 FOGINFO : register(c28);
|
||||
uniform const float FOGENABLE : register(c29);
|
||||
|
||||
#define CM_LT(c) if(c < 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_LT
|
||||
#define CM_GE(c) if(c >= 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_GE
|
||||
|
@ -92,10 +90,9 @@ uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTA
|
|||
#define PS_FINALCOMBINERSETTING_CLAMP_SUM
|
||||
#endif
|
||||
|
||||
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
|
||||
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
|
||||
// Second raw string :
|
||||
R"DELIMITER(
|
||||
// Hardcoded state will be inserted here
|
||||
// <HARDCODED STATE GOES HERE>
|
||||
// End hardcoded state
|
||||
|
||||
// PS_COMBINERCOUNT_UNIQUE_C0 steers whether for C0 to use combiner stage-specific constants c0_0 .. c0_7, or c0_0 for all stages
|
||||
#ifdef PS_COMBINERCOUNT_UNIQUE_C0
|
||||
|
@ -117,7 +114,7 @@ R"DELIMITER(
|
|||
#ifdef PS_COMBINERCOUNT_MUX_MSB
|
||||
#define FCS_MUX (r0.a >= 0.5) // Check r0.a MSB; Having range upto 1 this should be equal to : (((r0.a * 255) /*mod 256*/) >= 128)
|
||||
#else // PS_COMBINERCOUNT_MUX_LSB
|
||||
#define FCS_MUX (((r0.a * 255) mod 2) >= 1) // Check r0.b LSB; Get LSB by converting 1 into 255 (highest 8-bit value) and using modulo 2. TODO : Verify correctness
|
||||
#define FCS_MUX (((r0.a * 255) % 2) >= 1) // Check r0.b LSB; Get LSB by converting 1 into 255 (highest 8-bit value) and using modulo 2. TODO : Verify correctness
|
||||
#endif
|
||||
|
||||
// PS_FINALCOMBINERSETTING_COMPLEMENT_V1, when defined, applies a modifier to the v1 input when calculating the sum register
|
||||
|
@ -141,15 +138,17 @@ R"DELIMITER(
|
|||
#define FCS_SUM s_ident // otherwise identity mapping. TODO : Confirm correctness
|
||||
#endif
|
||||
|
||||
#define xdot(s0, s1) dot((s0).rgb, (s1).rgb)
|
||||
|
||||
// Xbox supports only one 'pixel shader' opcode, but bit flags tunes it's function;
|
||||
// Here, effective all 5 Xbox opcodes, extended with a variable macro {xop_m(m,...)} for destination modifier :
|
||||
// Note : Since both d0 AND d1 could be the same output register, calculation of d2 can re-use only one (d0 or d1)
|
||||
#define xmma(d0, d1, d2, s0, s1, s2, s3, m, tmp) tmp = d0 = m(s0 * s1); d1 = m(s2 * s3); d2 = d1 + tmp // PS_COMBINEROUTPUT_AB_CD_SUM= 0x00L, // 3rd output is AB+CD
|
||||
#define xmmc(d0, d1, d2, s0, s1, s2, s3, m, tmp) tmp = d0 = m(s0 * s1); d1 = m(s2 * s3); d2 = FCS_MUX ? d1 : tmp // PS_COMBINEROUTPUT_AB_CD_MUX= 0x04L, // 3rd output is MUX(AB,CD) based on R0.a
|
||||
|
||||
#define xdm(d0, d1, s0, s1, s2, s3, m) d0 = m(dot(s0 , s1)); d1 = m( s2 * s3 ) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
|
||||
#define xdd(d0, d1, s0, s1, s2, s3, m) d0 = m(dot(s0 , s1)); d1 = m(dot(s2 , s3)) // PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only // PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
|
||||
#define xmd(d0, d1, s0, s1, s2, s3, m) d0 = m( s0 * s1 ); d1 = m(dot(s2 , s3)) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x01L,
|
||||
#define xdm(d0, d1, s0, s1, s2, s3, m) d0 = m(xdot(s0 , s1)); d1 = m( s2 * s3 ) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
|
||||
#define xdd(d0, d1, s0, s1, s2, s3, m) d0 = m(xdot(s0 , s1)); d1 = m(xdot(s2 , s3)) // PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only // PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
|
||||
#define xmd(d0, d1, s0, s1, s2, s3, m) d0 = m( s0 * s1 ); d1 = m(xdot(s2 , s3)) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x01L,
|
||||
|
||||
// After the register combiner stages, there's one (optional) final combiner step, consisting of 4 parts;
|
||||
// All the 7 final combiner inputs operate on rgb only and clamp negative input to zero:
|
||||
|
@ -171,10 +170,6 @@ R"DELIMITER(
|
|||
// HLSL : https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp
|
||||
// lerp(x, y, s ) x*(1-s ) + y*s == x + s(y-x)
|
||||
// lerp(s2, s1, s0) s2*(1-s0) + s1*s0
|
||||
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
|
||||
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
|
||||
// Second raw string :
|
||||
R"DELIMITER(
|
||||
|
||||
float m21d(const float input)
|
||||
{
|
||||
|
@ -203,20 +198,39 @@ float m21(const float input)
|
|||
return (float)tmp / 127; // -128 scales to -1.007874016, 0 scales to 0.0, 127 scales to 1.0
|
||||
}
|
||||
|
||||
float hls(float input) // 0..65535 range
|
||||
{
|
||||
float tmp = (float)(input);
|
||||
tmp = (input < 32768) ? tmp / 32767 : (tmp - 65536) / 32767; // -1..1
|
||||
return (float)tmp;
|
||||
}
|
||||
|
||||
float hlu(float input) // 0..65535 range
|
||||
{
|
||||
return (float)input / 65535; // 0..1
|
||||
}
|
||||
|
||||
float p2(float input) // power of 2
|
||||
{
|
||||
return input * input;
|
||||
}
|
||||
|
||||
// Note : each component seems already in range [0..1], but two must be combined into one
|
||||
#define TwoIntoOne(a,b) (((a * 255) * 256) + (b * 255)) / 255 // TODO : Verify whether this works at all !
|
||||
|
||||
#define TwoIntoOne(a,b) (((a * 256) + b) * 255)
|
||||
#define CalcHiLo(in) H = TwoIntoOne(in.x, in.y); L = TwoIntoOne(in.z, in.w) // TODO : Verify whether this works at all !
|
||||
|
||||
|
||||
// Dot mappings over the output value of a (4 component 8 bit unsigned) texture stage register into a (3 component float) vector value, for use in a dot product calculation:
|
||||
#define PS_DOTMAPPING_ZERO_TO_ONE(in) dm = in.rgb // :r8g8b8a8->(r,g,b): 0x00=>0, 0xff=>1 thus : output = (input / 0xff )
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1_D3D(in) dm = float3(m21d(in.x), m21d(in.y), m21d(in.z)) // :r8g8b8a8->(r,g,b): 0x00=>-128/127, 0x01=>-1, 0x80=>0, 0xff=>1 thus : output = ((input - 0x100 ) / 0x7f )
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1_GL(in) dm = float3(m21g(in.x), m21g(in.y), m21g(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x80 ) (see https://en.wikipedia.org/wiki/Two's_complement)
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21(in.x), m21(in.y), m21(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
|
||||
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21( in.x), m21( in.y), m21( in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
|
||||
|
||||
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(H, L, 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
|
||||
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(hlu(H), hlu(L), 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
|
||||
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
|
||||
|
||||
// Declare one sampler per each {Sampler Type, Texture Stage} combination
|
||||
// TODO : Generate sampler status?
|
||||
|
@ -310,7 +324,7 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
|
|||
/*--23 texbrdf */ #define PS_TEXTUREMODES_BRDF(ts) s = Brdf(ts); v = Sample3D(ts, s); t[ts] = v // TODO : Test (t[ts-2] is 16 bit eyePhi,eyeSigma; t[ts-1] is lightPhi,lightSigma)
|
||||
/*--23 texm3x2tex */ #define PS_TEXTUREMODES_DOT_ST(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample2D(ts, s); t[ts] = v // TODO : Test
|
||||
/*--23 texm3x2depth */ #define PS_TEXTUREMODES_DOT_ZW(ts) CalcDot(ts); n = Normal2(ts); if (n.y==0) v=1;else v = n.x / n.y; t[ts] = v // TODO : Make depth-check use result of division, but how?
|
||||
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
/*---3 texm3x3vspec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC(ts) CalcDot(ts); n = Normal3(ts); s = Reflect(n, Eye); v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_3D(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample3D(ts, s); t[ts] = v // TODO : Test
|
||||
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_CUBE(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
|
@ -323,6 +337,34 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
|
|||
|
||||
PS_OUTPUT main(const PS_INPUT xIn)
|
||||
{
|
||||
// fogging
|
||||
const float fogDepth = xIn.iFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sampl
|
||||
const int fogTableMode = FOGINFO.x;
|
||||
const float fogDensity = FOGINFO.y;
|
||||
const float fogStart = FOGINFO.z;
|
||||
const float fogEnd = FOGINFO.w;
|
||||
|
||||
const int FOG_TABLE_NONE = 0;
|
||||
const int FOG_TABLE_EXP = 1;
|
||||
const int FOG_TABLE_EXP2 = 2;
|
||||
const int FOG_TABLE_LINEAR = 3;
|
||||
|
||||
float fogFactor;
|
||||
|
||||
if(FOGENABLE == 0){
|
||||
fogFactor = 1;
|
||||
}
|
||||
else{
|
||||
if(fogTableMode == FOG_TABLE_NONE)
|
||||
fogFactor = fogDepth;
|
||||
if(fogTableMode == FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(fogDepth * fogDensity); // 1 / e^(d * density)
|
||||
if(fogTableMode == FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); // 1 / e^((d * density)^2)
|
||||
if(fogTableMode == FOG_TABLE_LINEAR)
|
||||
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
|
||||
}
|
||||
|
||||
// Local constants
|
||||
const float4 zero = 0;
|
||||
const float4 half = 0.5; // = s_negbias(zero)
|
||||
|
@ -356,12 +398,11 @@ PS_OUTPUT main(const PS_INPUT xIn)
|
|||
// Note : VFACE/FrontFace has been unreliable, investigate again if some test-case shows bland colors
|
||||
v0 = isFrontFace ? xIn.iD0 : xIn.iB0; // Diffuse front/back
|
||||
v1 = isFrontFace ? xIn.iD1 : xIn.iB1; // Specular front/back
|
||||
fog = float4(c_fog.rgb, xIn.iFog); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
|
||||
fog = float4(c_fog.rgb, saturate(fogFactor)); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
|
||||
|
||||
// Xbox shader program
|
||||
)DELIMITER", /* This terminates the 2nd raw string within the 16380 single-byte characters limit. // */
|
||||
// Third and last raw string, the footer :
|
||||
R"DELIMITER(
|
||||
// Xbox shader program will be inserted here
|
||||
// <XBOX SHADER PROGRAM GOES HERE>
|
||||
// End Xbox shader program
|
||||
|
||||
// Copy r0.rgba to output
|
||||
PS_OUTPUT xOut;
|
||||
|
@ -370,5 +411,3 @@ R"DELIMITER(
|
|||
|
||||
return xOut;
|
||||
}
|
||||
|
||||
// End of pixel shader footer)DELIMITER" /* This terminates the footer raw string" // */
|
||||
|
|
108
src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl
Normal file
108
src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Xbox HLSL pretransformed vertex shader
|
||||
|
||||
// Default values for vertex registers, and whether to use them
|
||||
uniform float4 vRegisterDefaultValues[16] : register(c192);
|
||||
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
|
||||
|
||||
uniform float4 xboxScreenspaceScale : register(c212);
|
||||
uniform float4 xboxScreenspaceOffset : register(c213);
|
||||
|
||||
|
||||
uniform float4 xboxTextureScale[4] : register(c214);
|
||||
|
||||
// Parameters for mapping the shader's fog output value to a fog factor
|
||||
uniform float4 CxbxFogInfo: register(c218); // = CXBX_D3DVS_CONSTREG_FOGINFO
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float4 v[16] : TEXCOORD;
|
||||
};
|
||||
|
||||
// Output registers
|
||||
struct VS_OUTPUT
|
||||
{
|
||||
float4 oPos : POSITION; // Homogeneous clip space position
|
||||
float4 oD0 : COLOR0; // Primary color (front-facing)
|
||||
float4 oD1 : COLOR1; // Secondary color (front-facing)
|
||||
float oFog : FOG; // Fog coordinate
|
||||
float oPts : PSIZE; // Point size
|
||||
float4 oB0 : TEXCOORD4; // Back-facing primary color
|
||||
float4 oB1 : TEXCOORD5; // Back-facing secondary color
|
||||
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
|
||||
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
|
||||
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
|
||||
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
|
||||
};
|
||||
|
||||
float4 reverseScreenspaceTransform(float4 oPos)
|
||||
{
|
||||
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
|
||||
|
||||
// On Xbox, oPos should contain the vertex position in screenspace
|
||||
// We need to reverse this transformation
|
||||
// Conventionally, each Xbox Vertex Shader includes instructions like this
|
||||
// mul oPos.xyz, r12, c-38
|
||||
// +rcc r1.x, r12.w
|
||||
// mad oPos.xyz, r12, r1.x, c-37
|
||||
// where c-37 and c-38 are reserved transform values
|
||||
|
||||
// Reverse screenspace offset
|
||||
oPos -= xboxScreenspaceOffset;
|
||||
// Reverse screenspace scale
|
||||
oPos /= xboxScreenspaceScale;
|
||||
|
||||
// Ensure w is nonzero
|
||||
if(oPos.w == 0) oPos.w = 1;
|
||||
// Reverse perspective divide
|
||||
oPos.xyz *= oPos.w;
|
||||
return oPos;
|
||||
}
|
||||
|
||||
VS_OUTPUT main(const VS_INPUT xIn)
|
||||
{
|
||||
// Input registers
|
||||
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
|
||||
|
||||
// Unpack 16 flags from 4 float4 constant registers
|
||||
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
|
||||
|
||||
// Initialize input registers from the vertex buffer data
|
||||
// Or use the register's default value (which can be changed by the title)
|
||||
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
|
||||
// Note : unroll manually instead of for-loop, because of the ## concatenation
|
||||
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
|
||||
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
|
||||
init_v( 8); init_v( 9); init_v(10); init_v(11);
|
||||
init_v(12); init_v(13); init_v(14); init_v(15);
|
||||
|
||||
// For passthrough, map output variables to their corresponding input registers
|
||||
float4 oPos = v0;
|
||||
float4 oD0 = v3;
|
||||
float4 oD1 = v4;
|
||||
float4 oFog = v5;
|
||||
float4 oPts = v6;
|
||||
float4 oB0 = v7;
|
||||
float4 oB1 = v8;
|
||||
float4 oT0 = v9;
|
||||
float4 oT1 = v10;
|
||||
float4 oT2 = v11;
|
||||
float4 oT3 = v12;
|
||||
|
||||
// Copy variables to output struct
|
||||
VS_OUTPUT xOut;
|
||||
|
||||
xOut.oPos = reverseScreenspaceTransform(oPos);
|
||||
xOut.oD0 = saturate(oD0);
|
||||
xOut.oD1 = saturate(oD1);
|
||||
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader
|
||||
xOut.oPts = oPts.x;
|
||||
xOut.oB0 = saturate(oB0);
|
||||
xOut.oB1 = saturate(oB1);
|
||||
// Scale textures (TODO: or should we apply this to the input register values?)
|
||||
xOut.oT0 = oT0 / xboxTextureScale[0];
|
||||
xOut.oT1 = oT1 / xboxTextureScale[1];
|
||||
xOut.oT2 = oT2 / xboxTextureScale[2];
|
||||
xOut.oT3 = oT3 / xboxTextureScale[3];
|
||||
|
||||
return xOut;
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
|
||||
R"DELIMITER(// Xbox HLSL vertex shader (template populated at runtime)
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float4 v[16] : TEXCOORD;
|
||||
|
@ -323,40 +320,20 @@ VS_OUTPUT main(const VS_INPUT xIn)
|
|||
init_v( 8); init_v( 9); init_v(10); init_v(11);
|
||||
init_v(12); init_v(13); init_v(14); init_v(15);
|
||||
|
||||
// Xbox shader program)DELIMITER", /* This terminates the header raw string" // */
|
||||
// Temp variable for paired VS instruction
|
||||
float4 temp;
|
||||
|
||||
R"DELIMITER(
|
||||
// Xbox shader program will be inserted here
|
||||
// <XBOX SHADER PROGRAM GOES HERE>
|
||||
// End Xbox shader program
|
||||
|
||||
// Copy variables to output struct
|
||||
VS_OUTPUT xOut;
|
||||
|
||||
// Fogging
|
||||
// TODO deduplicate
|
||||
const float fogDepth = oFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sample
|
||||
const float fogTableMode = CxbxFogInfo.x;
|
||||
const float fogDensity = CxbxFogInfo.y;
|
||||
const float fogStart = CxbxFogInfo.z;
|
||||
const float fogEnd = CxbxFogInfo.w;
|
||||
|
||||
const float FOG_TABLE_NONE = 0;
|
||||
const float FOG_TABLE_EXP = 1;
|
||||
const float FOG_TABLE_EXP2 = 2;
|
||||
const float FOG_TABLE_LINEAR = 3;
|
||||
|
||||
float fogFactor;
|
||||
if(fogTableMode == FOG_TABLE_NONE)
|
||||
fogFactor = fogDepth;
|
||||
if(fogTableMode == FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(fogDepth * fogDensity); /* / 1 / e^(d * density)*/
|
||||
if(fogTableMode == FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); /* / 1 / e^((d * density)^2)*/
|
||||
if(fogTableMode == FOG_TABLE_LINEAR)
|
||||
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
|
||||
|
||||
|
||||
xOut.oPos = reverseScreenspaceTransform(oPos);
|
||||
xOut.oD0 = saturate(oD0);
|
||||
xOut.oD1 = saturate(oD1);
|
||||
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
|
||||
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
|
||||
xOut.oPts = oPts.x;
|
||||
xOut.oB0 = saturate(oB0);
|
||||
xOut.oB1 = saturate(oB1);
|
||||
|
@ -368,5 +345,3 @@ R"DELIMITER(
|
|||
|
||||
return xOut;
|
||||
}
|
||||
|
||||
// End of vertex shader footer)DELIMITER" /* This terminates the footer raw string" // */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4256,3 +4256,27 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BlockUntilVerticalBlank
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank)()
|
||||
{
|
||||
LOG_FUNC();
|
||||
|
||||
std::unique_lock<std::mutex> lk(g_VBConditionMutex);
|
||||
g_VBConditionVariable.wait(lk);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_SetVerticalBlankCallback
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback)
|
||||
(
|
||||
X_D3DVBLANKCALLBACK pCallback
|
||||
)
|
||||
{
|
||||
LOG_FUNC_ONE_ARG(pCallback);
|
||||
|
||||
g_pXbox_VerticalBlankCallback = pCallback;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
void LookupTrampolinesD3D();
|
||||
|
||||
// initialize render window
|
||||
extern void CxbxInitWindow(bool bFullInit);
|
||||
extern void CxbxInitWindow();
|
||||
|
||||
void CxbxUpdateNativeD3DResources();
|
||||
|
||||
|
@ -144,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
|
||||
|
@ -213,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
|
||||
);
|
||||
|
@ -359,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
|
||||
// ******************************************************************
|
||||
|
@ -505,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
|
||||
|
@ -611,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
|
||||
);
|
||||
|
@ -1390,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
|
||||
// ******************************************************************
|
||||
|
@ -1413,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
|
||||
);
|
||||
|
||||
|
||||
|
@ -1897,12 +1910,15 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetTexture)
|
|||
// ******************************************************************
|
||||
// * patch: CDevice_SetStateVB (D3D::CDevice::SetStateVB)
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)( xbox::ulong_xt Unknown1 );
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)(xbox::ulong_xt Unknown1);
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB_8)(xbox::addr_xt _this, xbox::ulong_xt Unknown1);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: CDevice_SetStateUP (D3D::CDevice::SetStateUP)
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP)();
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_4)(xbox::addr_xt _this);
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_0__LTCG_esi1)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_SetStipple
|
||||
|
|
|
@ -237,6 +237,24 @@ TextureArgs ExecuteTextureStage(
|
|||
|
||||
float4 main(const PS_INPUT input) : COLOR {
|
||||
|
||||
// Calculate the fog factor
|
||||
float fogFactor;
|
||||
|
||||
if (state.FogEnable == 0){
|
||||
fogFactor = 1;
|
||||
}
|
||||
else{
|
||||
if (state.FogTableMode == FOG_TABLE_NONE)
|
||||
fogFactor = input.iFog;
|
||||
if (state.FogTableMode == FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(input.iFog * state.FogDensity); // 1 / e^(d * density)
|
||||
if (state.FogTableMode == FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(input.iFog * state.FogDensity, 2)); // 1 / e^((d * density)^2)
|
||||
if (state.FogTableMode == FOG_TABLE_LINEAR)
|
||||
fogFactor = (state.FogEnd - input.iFog) / (state.FogEnd - state.FogStart); // (end - d) / (end - start)
|
||||
if (state.FogEnable == 0)
|
||||
fogFactor = 1;
|
||||
}
|
||||
TexCoords = input.iT;
|
||||
|
||||
// Each stage is passed and returns
|
||||
|
@ -284,7 +302,7 @@ float4 main(const PS_INPUT input) : COLOR {
|
|||
|
||||
// Add fog if enabled
|
||||
if (state.FogEnable) {
|
||||
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(input.iFog));
|
||||
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(fogFactor));
|
||||
}
|
||||
|
||||
// Add specular if enabled
|
||||
|
|
|
@ -63,10 +63,16 @@ namespace FixedFunctionPixelShader {
|
|||
const float X_D3DTA_COMPLEMENT = 0x00000010; // take 1.0 - x (read modifier)
|
||||
const float X_D3DTA_ALPHAREPLICATE = 0x00000020; // replicate alpha to color components (read modifier)
|
||||
|
||||
const int SAMPLE_NONE = 0;
|
||||
const int SAMPLE_2D = 1;
|
||||
const int SAMPLE_3D = 2;
|
||||
const int SAMPLE_CUBE = 3;
|
||||
const int SAMPLE_NONE = 0;
|
||||
const int SAMPLE_2D = 1;
|
||||
const int SAMPLE_3D = 2;
|
||||
const int SAMPLE_CUBE = 3;
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
|
||||
const float FOG_TABLE_NONE = 0;
|
||||
const float FOG_TABLE_EXP = 1;
|
||||
const float FOG_TABLE_EXP2 = 2;
|
||||
const float FOG_TABLE_LINEAR = 3;
|
||||
|
||||
// This state is passed to the shader
|
||||
struct PsTextureStageState {
|
||||
|
@ -125,7 +131,11 @@ namespace FixedFunctionPixelShader {
|
|||
alignas(16) float SpecularEnable;
|
||||
alignas(16) float FogEnable;
|
||||
alignas(16) float3 FogColor;
|
||||
};
|
||||
alignas(16) float FogTableMode;
|
||||
alignas(16) float FogDensity;
|
||||
alignas(16) float FogStart;
|
||||
alignas(16) float FogEnd;
|
||||
};
|
||||
#ifdef __cplusplus
|
||||
} // FixedFunctionPixelShader namespace
|
||||
#endif
|
||||
|
|
|
@ -288,20 +288,8 @@ float DoFog(const VS_INPUT xIn)
|
|||
fogDepth = abs(Projection.Position.z);
|
||||
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W)
|
||||
fogDepth = Projection.Position.w;
|
||||
|
||||
// Calculate the fog factor
|
||||
// Some of this might be better done in the pixel shader?
|
||||
float fogFactor;
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_NONE)
|
||||
fogFactor = fogDepth;
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP)
|
||||
fogFactor = 1 / exp(fogDepth * state.Fog.Density); // 1 / e^(d * density)
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP2)
|
||||
fogFactor = 1 / exp(pow(fogDepth * state.Fog.Density, 2)); // 1 / e^((d * density)^2)
|
||||
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_LINEAR)
|
||||
fogFactor = (state.Fog.End - fogDepth) / (state.Fog.End - state.Fog.Start); // (end - d) / (end - start)
|
||||
|
||||
return fogFactor;
|
||||
|
||||
return fogDepth;
|
||||
}
|
||||
|
||||
float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
||||
|
@ -351,9 +339,9 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
|||
// TODO move alongside the texture transformation when it stops angering the HLSL compiler
|
||||
const float componentCount = state.TexCoordComponentCount[texCoordIndex];
|
||||
if (componentCount == 1)
|
||||
texCoord.yzw = float3(1, 0, 0);
|
||||
texCoord.yzw = float3(0, 0, 1);
|
||||
if (componentCount == 2)
|
||||
texCoord.zw = float2(1, 0);
|
||||
texCoord.zw = float2(0, 1);
|
||||
if (componentCount == 3)
|
||||
texCoord.w = 1;
|
||||
} // Generate texture coordinates
|
||||
|
@ -390,14 +378,15 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
|||
// Test case: ProjectedTexture sample, which uses 3 coordinates
|
||||
// We'll need to implement the divide when D3D stops handling it for us?
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtexturetransformflags
|
||||
// padding makes no differences in LLE. LLE works with ProjectedTexture sample without padding, but to be devided by w is necessary.
|
||||
|
||||
if (projected)
|
||||
{
|
||||
if (countFlag == 1)
|
||||
texCoord.yzw = texCoord.x;
|
||||
if (countFlag == 2)
|
||||
texCoord.zw = texCoord.y;
|
||||
if (countFlag == 3)
|
||||
texCoord.w = texCoord.z;
|
||||
//if (countFlag == 1)
|
||||
//texCoord.yz = texCoord.x;
|
||||
//if (countFlag == 2)
|
||||
//texCoord.z = texCoord.y;
|
||||
//texCoord.xyzw = texCoord.xyzw / texCoord.w;
|
||||
}
|
||||
|
||||
return texCoord;
|
||||
|
|
|
@ -289,12 +289,7 @@ bool IsTextureSampled(DecodedRegisterCombiner* pShader, int reg)
|
|||
|
||||
void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
||||
{
|
||||
// Include HLSL header and footer as raw strings :
|
||||
static const std::string hlsl_template[4] = {
|
||||
#include "core\hle\D3D8\Direct3D9\CxbxPixelShaderTemplate.hlsl"
|
||||
};
|
||||
|
||||
hlsl << hlsl_template[0]; // Start with the HLSL template header
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[0]; // Start with the HLSL template header
|
||||
|
||||
hlsl << "\n#define ALPHAKILL {"
|
||||
<< (pShader->AlphaKill[0] ? "true, " : "false, ")
|
||||
|
@ -341,9 +336,9 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
|||
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementV1, "PS_FINALCOMBINERSETTING_COMPLEMENT_V1");
|
||||
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementR0, "PS_FINALCOMBINERSETTING_COMPLEMENT_R0");
|
||||
OutputDefineFlag(hlsl, pShader->FinalCombiner.ClampSum, "PS_FINALCOMBINERSETTING_CLAMP_SUM");
|
||||
hlsl << '\n';
|
||||
|
||||
hlsl << hlsl_template[1];
|
||||
hlsl << hlsl_template[2];
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[1];
|
||||
|
||||
// Generate all four texture stages
|
||||
for (unsigned i = 0; i < PSH_XBOX_MAX_T_REGISTER_COUNT; i++) {
|
||||
|
@ -390,7 +385,7 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
|||
|
||||
FinalCombinerStageHlsl(hlsl, pShader->FinalCombiner, pShader->hasFinalCombiner);
|
||||
|
||||
hlsl << hlsl_template[3]; // Finish with the HLSL template footer
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[2]; // Finish with the HLSL template footer
|
||||
}
|
||||
|
||||
// recompile xbox pixel shader function
|
||||
|
|
|
@ -36,20 +36,14 @@ void SetXboxMultiSampleType(xbox::X_D3DMULTISAMPLE_TYPE value);
|
|||
|
||||
bool XboxRenderStateConverter::Init()
|
||||
{
|
||||
if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) {
|
||||
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3DDeferredRenderState"];
|
||||
// Get render state
|
||||
if (g_SymbolAddresses.find("D3D_g_RenderState") != g_SymbolAddresses.end()) {
|
||||
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3D_g_RenderState"];
|
||||
} else {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_RenderState was not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, D3D__RenderState points to the first Deferred render state
|
||||
// Do a little magic to verify that it's correct, then count back to determine the
|
||||
// start offset of the entire structure
|
||||
VerifyAndFixDeferredRenderStateOffset();
|
||||
|
||||
// Now use the verified Deferred offset to derive the D3D__RenderState offset
|
||||
DeriveRenderStateOffsetFromDeferredRenderStateOffset();
|
||||
|
||||
// Build a mapping of Cxbx Render State indexes to indexes within the current XDK
|
||||
BuildRenderStateMappingTable();
|
||||
|
||||
|
@ -70,54 +64,6 @@ bool IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo& aRenderStateInf
|
|||
return bIsRenderStateAvailable;
|
||||
}
|
||||
|
||||
void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset()
|
||||
{
|
||||
DWORD CullModeOffset = g_SymbolAddresses["D3DRS_CULLMODE"];
|
||||
// If we found a valid CullMode offset, verify the symbol location
|
||||
if (CullModeOffset == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "D3DRS_CULLMODE could not be found. Please update the XbSymbolDatabase submodule");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate index of D3DRS_CULLMODE for this XDK. We start counting from the first deferred state (D3DRS_FOGENABLE)
|
||||
DWORD CullModeIndex = 0;
|
||||
for (int i = xbox::X_D3DRS_DEFERRED_FIRST; i < xbox::X_D3DRS_CULLMODE; i++) {
|
||||
auto RenderStateInfo = GetDxbxRenderStateInfo(i);
|
||||
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
|
||||
CullModeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// If the offset was incorrect, calculate the correct offset, log it, and fix it
|
||||
if ((DWORD)(&D3D__RenderState[CullModeIndex]) != CullModeOffset) {
|
||||
DWORD CorrectOffset = CullModeOffset - (CullModeIndex * sizeof(DWORD));
|
||||
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState returned by XboxSymbolDatabase (0x%08X) was incorrect. Correcting to be 0x%08X.\nPlease file an issue with the XbSymbolDatabase project", D3D__RenderState, CorrectOffset);
|
||||
D3D__RenderState = (uint32_t*)CorrectOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void XboxRenderStateConverter::DeriveRenderStateOffsetFromDeferredRenderStateOffset()
|
||||
{
|
||||
// When this function is called. D3D__RenderState actually points to the first deferred render state
|
||||
// (this is X_D3DRS_FOGENABLE). We can count back from this using our RenderStateInfo table to find
|
||||
// the start of D3D__RenderStates.
|
||||
|
||||
// Count the number of render states (for this XDK) between 0 and the first deferred render state (D3DRS_FOGENABLE)
|
||||
int FirstDeferredRenderStateOffset = 0;
|
||||
for (unsigned int RenderState = xbox::X_D3DRS_FIRST; RenderState < xbox::X_D3DRS_DEFERRED_FIRST; RenderState++) {
|
||||
// if the current renderstate exists in this XDK version, count it
|
||||
auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState);
|
||||
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
|
||||
FirstDeferredRenderStateOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, FirstDeferredRenderStateOffset should point to the index of D3DRS_FOGENABLE for the given XDK
|
||||
// This will be correct as long as our table DxbxRenderStateInfo is correct
|
||||
// We can get the correct 0 offset by using a negative index
|
||||
D3D__RenderState = &D3D__RenderState[-FirstDeferredRenderStateOffset];
|
||||
}
|
||||
|
||||
void XboxRenderStateConverter::BuildRenderStateMappingTable()
|
||||
{
|
||||
EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Render State Mapping Table");
|
||||
|
|
|
@ -29,10 +29,18 @@
|
|||
|
||||
#include <d3dcompiler.h>
|
||||
#include "Shader.h"
|
||||
#include "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
|
||||
#include "core\kernel\init\CxbxKrnl.h" // LOG_TEST_CASE
|
||||
#include "core\kernel\support\Emu.h" // EmuLog
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
//#include <sstream>
|
||||
|
||||
ShaderSources g_ShaderSources;
|
||||
|
||||
std::string DebugPrependLineNumbers(std::string shaderString) {
|
||||
std::stringstream shader(shaderString);
|
||||
auto debugShader = std::stringstream();
|
||||
|
@ -140,3 +148,173 @@ extern HRESULT EmuCompileShader
|
|||
|
||||
return hRet;
|
||||
}
|
||||
|
||||
std::ifstream OpenWithRetry(const std::string& path) {
|
||||
auto fstream = std::ifstream(path);
|
||||
int failures = 0;
|
||||
while (fstream.fail()) {
|
||||
Sleep(50);
|
||||
fstream = std::ifstream(path);
|
||||
|
||||
if (failures++ > 10) {
|
||||
// crash?
|
||||
CxbxrAbort("Error opening shader file: %s", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fstream;
|
||||
}
|
||||
|
||||
int ShaderSources::Update() {
|
||||
int versionOnDisk = shaderVersionOnDisk;
|
||||
if (shaderVersionLoadedFromDisk != versionOnDisk) {
|
||||
LoadShadersFromDisk();
|
||||
shaderVersionLoadedFromDisk = versionOnDisk;
|
||||
}
|
||||
|
||||
return shaderVersionLoadedFromDisk;
|
||||
}
|
||||
|
||||
void ShaderSources::LoadShadersFromDisk() {
|
||||
const auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
|
||||
.parent_path()
|
||||
.append("hlsl");
|
||||
|
||||
// Pixel Shader Template
|
||||
{
|
||||
std::stringstream tmp;
|
||||
auto dir = hlslDir;
|
||||
dir.append("CxbxPixelShaderTemplate.hlsl");
|
||||
tmp << OpenWithRetry(dir.string()).rdbuf();
|
||||
std::string hlsl = tmp.str();
|
||||
|
||||
// Split the HLSL file on insertion points
|
||||
std::array<std::string, 2> insertionPoints = {
|
||||
"// <HARDCODED STATE GOES HERE>\n",
|
||||
"// <XBOX SHADER PROGRAM GOES HERE>\n",
|
||||
};
|
||||
int pos = 0;
|
||||
for (int i = 0; i < insertionPoints.size(); i++) {
|
||||
auto insertionPoint = insertionPoints[i];
|
||||
auto index = hlsl.find(insertionPoint, pos);
|
||||
|
||||
if (index == std::string::npos) {
|
||||
// Handle broken shaders
|
||||
this->pixelShaderTemplateHlsl[i] = "";
|
||||
}
|
||||
else {
|
||||
this->pixelShaderTemplateHlsl[i] = hlsl.substr(pos, index - pos);
|
||||
pos = index + insertionPoint.length();
|
||||
}
|
||||
}
|
||||
this->pixelShaderTemplateHlsl[insertionPoints.size()] = hlsl.substr(pos);
|
||||
}
|
||||
|
||||
// Fixed Function Pixel Shader
|
||||
{
|
||||
auto dir = hlslDir;
|
||||
this->fixedFunctionPixelShaderPath = dir.append("FixedFunctionPixelShader.hlsl").string();
|
||||
std::stringstream tmp;
|
||||
tmp << OpenWithRetry(this->fixedFunctionPixelShaderPath).rdbuf();
|
||||
this->fixedFunctionPixelShaderHlsl = tmp.str();
|
||||
}
|
||||
|
||||
// Vertex Shader Template
|
||||
{
|
||||
std::stringstream tmp;
|
||||
auto dir = hlslDir;
|
||||
dir.append("CxbxVertexShaderTemplate.hlsl");
|
||||
tmp << OpenWithRetry(dir.string()).rdbuf();
|
||||
std::string hlsl = tmp.str();
|
||||
|
||||
const std::string insertionPoint = "// <XBOX SHADER PROGRAM GOES HERE>\n";
|
||||
auto index = hlsl.find(insertionPoint);
|
||||
|
||||
if (index == std::string::npos) {
|
||||
// Handle broken shaders
|
||||
this->vertexShaderTemplateHlsl[0] = hlsl;
|
||||
this->vertexShaderTemplateHlsl[1] = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
this->vertexShaderTemplateHlsl[0] = hlsl.substr(0, index);
|
||||
this->vertexShaderTemplateHlsl[1] = hlsl.substr(index + insertionPoint.length());
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed Function Vertex Shader
|
||||
{
|
||||
auto dir = hlslDir;
|
||||
this->fixedFunctionVertexShaderPath = dir.append("FixedFunctionVertexShader.hlsl").string();
|
||||
std::stringstream tmp;
|
||||
tmp << OpenWithRetry(this->fixedFunctionVertexShaderPath).rdbuf();
|
||||
this->fixedFunctionVertexShaderHlsl = tmp.str();
|
||||
}
|
||||
|
||||
// Passthrough Vertex Shader
|
||||
{
|
||||
auto dir = hlslDir;
|
||||
this->vertexShaderPassthroughPath = dir.append("CxbxVertexShaderPassthrough.hlsl").string();
|
||||
std::stringstream tmp;
|
||||
tmp << OpenWithRetry(this->vertexShaderPassthroughPath).rdbuf();
|
||||
this->vertexShaderPassthroughHlsl = tmp.str();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderSources::InitShaderHotloading() {
|
||||
static std::jthread fsWatcherThread;
|
||||
|
||||
if (fsWatcherThread.joinable()) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Ignoring request to start shader file watcher - it has already been started.");
|
||||
return;
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Starting shader file watcher...");
|
||||
|
||||
fsWatcherThread = std::jthread([]{
|
||||
// Determine the filename and directory for the fixed function shader
|
||||
char cxbxExePath[MAX_PATH];
|
||||
GetModuleFileName(GetModuleHandle(nullptr), cxbxExePath, MAX_PATH);
|
||||
auto hlslDir = std::filesystem::path(cxbxExePath).parent_path().append("hlsl/");
|
||||
|
||||
HANDLE changeHandle = FindFirstChangeNotification(hlslDir.string().c_str(), false, FILE_NOTIFY_CHANGE_LAST_WRITE);
|
||||
|
||||
if (changeHandle == INVALID_HANDLE_VALUE) {
|
||||
DWORD errorCode = GetLastError();
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Error initializing shader file watcher: %d", errorCode);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (FindNextChangeNotification(changeHandle)) {
|
||||
WaitForSingleObject(changeHandle, INFINITE);
|
||||
|
||||
// Wait for changes to stop..
|
||||
// Will usually be at least two - one for the file and one for the directory
|
||||
while (true) {
|
||||
FindNextChangeNotification(changeHandle);
|
||||
if (WaitForSingleObject(changeHandle, 100) == WAIT_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Change detected in shader folder");
|
||||
|
||||
g_ShaderSources.shaderVersionOnDisk++;
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Shader filewatcher failed to get the next notification");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Shader file watcher exiting...");
|
||||
|
||||
// until there is a way to disable hotloading
|
||||
// this is always an error
|
||||
FindCloseChangeNotification(changeHandle);
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string> // std::string
|
||||
#include <d3dcompiler.h> // ID3DBlob (via d3d9.h > d3d11shader.h > d3dcommon.h)
|
||||
|
||||
|
@ -10,3 +11,38 @@ extern HRESULT EmuCompileShader
|
|||
ID3DBlob** ppHostShader,
|
||||
const char* pSourceName = nullptr
|
||||
);
|
||||
|
||||
struct ShaderSources {
|
||||
// Pixel Shader
|
||||
std::string pixelShaderTemplateHlsl[3];
|
||||
|
||||
std::string fixedFunctionPixelShaderHlsl;
|
||||
std::string fixedFunctionPixelShaderPath;
|
||||
|
||||
// Vertex Shader
|
||||
std::string vertexShaderTemplateHlsl[2];
|
||||
|
||||
std::string fixedFunctionVertexShaderHlsl;
|
||||
std::string fixedFunctionVertexShaderPath;
|
||||
|
||||
std::string vertexShaderPassthroughHlsl;
|
||||
std::string vertexShaderPassthroughPath;
|
||||
|
||||
// Load shaders from disk (if out-of-date)
|
||||
// and return the current loaded shader version
|
||||
int Update();
|
||||
|
||||
// Start a thread to watch for changes in the shader folder
|
||||
void InitShaderHotloading();
|
||||
|
||||
private:
|
||||
void LoadShadersFromDisk();
|
||||
|
||||
// counts upwards on every change detected to the shader source files at runtime
|
||||
std::atomic_int shaderVersionOnDisk = 0;
|
||||
// current loaded shader version
|
||||
// Initialized to < shaderVersionOnDisk
|
||||
int shaderVersionLoadedFromDisk = -1;
|
||||
};
|
||||
|
||||
extern ShaderSources g_ShaderSources;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,16 +4,12 @@
|
|||
#include "VertexShader.h" // EmuCompileVertexShader
|
||||
#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 "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
|
||||
|
||||
#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",
|
||||
"???",
|
||||
|
@ -34,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)
|
||||
{
|
||||
|
@ -78,7 +77,7 @@ 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 const char* RegisterName[/*VSH_PARAMETER_TYPE*/] = {
|
||||
"?", // PARAM_UNKNOWN = 0,
|
||||
|
@ -92,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:
|
||||
|
@ -112,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
|
||||
|
@ -175,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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,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;
|
||||
|
@ -231,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
|
||||
|
@ -42,6 +43,6 @@ private:
|
|||
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
|
||||
|
|
|
@ -1614,6 +1614,7 @@ void CxbxGetPixelContainerMeasures
|
|||
UINT* pHeight,
|
||||
UINT* pDepth,
|
||||
UINT* pRowPitch,
|
||||
// Slice pitch (does not include mipmaps!)
|
||||
UINT* pSlicePitch
|
||||
)
|
||||
{
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice, g_pXbox_PixelShader
|
||||
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
|
||||
#include "core\hle\D3D8\XbPixelShader.h"
|
||||
#include "core\hle\D3D8\Direct3D9\PixelShader.h" // EmuCompilePixelShader
|
||||
#include "core\hle\D3D8\XbD3D8Logging.h" // For D3DErrorString()
|
||||
|
@ -51,6 +52,7 @@
|
|||
#include <assert.h> // assert()
|
||||
#include <process.h>
|
||||
#include <locale.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -556,6 +558,12 @@ typedef struct s_CxbxPSDef {
|
|||
EmuLog(LOG_LEVEL::WARNING, "PROJECT2D sampling is used with a cubemap texture - using CUBEMAP sampling instead");
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_CUBEMAP;
|
||||
}
|
||||
|
||||
// Test-case: MS-033 Crimson Skies (Plane texturing in-game and selection menu)
|
||||
if (ActiveTextureTypes[i] == xbox::X_D3DRTYPE_CUBETEXTURE && RC.PSTextureModes[i] == PS_TEXTUREMODES_DOT_STR_3D) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "DOT_STR_3D sampling is used with a cubemap texture - using DOT_STR_CUBE sampling instead");
|
||||
RC.PSTextureModes[i] = PS_TEXTUREMODES_DOT_STR_CUBE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -653,35 +661,12 @@ constexpr int PSH_XBOX_CONSTANT_BEM = PSH_XBOX_CONSTANT_FOG + 1; // = 19..22
|
|||
constexpr int PSH_XBOX_CONSTANT_LUM = PSH_XBOX_CONSTANT_BEM + 4; // = 23..26
|
||||
// Which winding order to consider as the front face
|
||||
constexpr int PSH_XBOX_CONSTANT_FRONTFACE_FACTOR = PSH_XBOX_CONSTANT_LUM + 4; // = 27
|
||||
// Fog table information {Table mode, density, start and end}
|
||||
constexpr int CXBX_D3DPS_CONSTREG_FOGINFO = PSH_XBOX_CONSTANT_FRONTFACE_FACTOR + 1; // = 28
|
||||
//Fog enable flag
|
||||
constexpr int PSH_XBOX_CONSTANT_FOGENABLE = CXBX_D3DPS_CONSTREG_FOGINFO + 1; // = 29
|
||||
// This concludes the set of constants that need to be set on host :
|
||||
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FRONTFACE_FACTOR + 1; // = 28
|
||||
|
||||
std::string GetFixedFunctionShaderTemplate() {
|
||||
static bool loaded = false;
|
||||
static std::string hlslString;
|
||||
|
||||
// TODO does this need to be thread safe?
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
|
||||
// Determine the filename and directory for the fixed function shader
|
||||
// TODO make this a relative path so we guarantee an LPCSTR for D3DCompile
|
||||
auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
|
||||
.parent_path()
|
||||
.append("hlsl");
|
||||
|
||||
auto sourceFile = hlslDir.append("FixedFunctionPixelShader.hlsl").string();
|
||||
|
||||
// Load the shader into a string
|
||||
std::ifstream hlslStream(sourceFile);
|
||||
std::stringstream hlsl;
|
||||
hlsl << hlslStream.rdbuf();
|
||||
|
||||
hlslString = hlsl.str();
|
||||
}
|
||||
|
||||
return hlslString;
|
||||
}
|
||||
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FOGENABLE + 1; // = 30
|
||||
|
||||
std::string_view GetD3DTOPString(int d3dtop) {
|
||||
static constexpr std::string_view opToString[] = {
|
||||
|
@ -783,6 +768,21 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
|
|||
// TODO move this cache elsewhere - and flush it when the device is released!
|
||||
static std::unordered_map<uint64_t, IDirect3DPixelShader9*> ffPsCache = {};
|
||||
|
||||
// Support hotloading hlsl
|
||||
static int pixelShaderVersion = -1;
|
||||
int shaderVersion = g_ShaderSources.Update();
|
||||
if (pixelShaderVersion != shaderVersion) {
|
||||
pixelShaderVersion = shaderVersion;
|
||||
g_pD3DDevice->SetPixelShader(nullptr);
|
||||
|
||||
for (auto& hostShader : ffPsCache) {
|
||||
if (hostShader.second)
|
||||
hostShader.second->Release();
|
||||
}
|
||||
|
||||
ffPsCache.clear();
|
||||
}
|
||||
|
||||
// Create a key from state that will be baked in to the shader
|
||||
PsTextureHardcodedState states[4] = {};
|
||||
int sampleType[4] = { SAMPLE_NONE, SAMPLE_NONE, SAMPLE_NONE, SAMPLE_NONE };
|
||||
|
@ -865,68 +865,74 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
|
|||
}
|
||||
|
||||
// Build and compile a new shader
|
||||
auto hlslTemplate = GetFixedFunctionShaderTemplate();
|
||||
std::string hlslTemplate = g_ShaderSources.fixedFunctionPixelShaderHlsl;
|
||||
|
||||
// In D3D9 it seems we need to know hardcode if we're doing a 2D or 3D lookup
|
||||
const std::string sampleTypePattern = "TEXTURE_SAMPLE_TYPE;";
|
||||
auto sampleTypeReplace = hlslTemplate.find(sampleTypePattern);
|
||||
std::string finalShader = hlslTemplate;
|
||||
|
||||
static constexpr std::string_view typeToString[] = {
|
||||
"SAMPLE_NONE",
|
||||
"SAMPLE_2D",
|
||||
"SAMPLE_3D",
|
||||
"SAMPLE_CUBE"
|
||||
};
|
||||
if (sampleTypeReplace != std::string::npos) {
|
||||
static constexpr std::string_view typeToString[] = {
|
||||
"SAMPLE_NONE",
|
||||
"SAMPLE_2D",
|
||||
"SAMPLE_3D",
|
||||
"SAMPLE_CUBE"
|
||||
};
|
||||
|
||||
std::stringstream sampleTypeString;
|
||||
sampleTypeString << "{"
|
||||
<< typeToString[sampleType[0]] << ", "
|
||||
<< typeToString[sampleType[1]] << ", "
|
||||
<< typeToString[sampleType[2]] << ", "
|
||||
<< typeToString[sampleType[3]] << "};";
|
||||
std::stringstream sampleTypeString;
|
||||
sampleTypeString << "{"
|
||||
<< typeToString[sampleType[0]] << ", "
|
||||
<< typeToString[sampleType[1]] << ", "
|
||||
<< typeToString[sampleType[2]] << ", "
|
||||
<< typeToString[sampleType[3]] << "};";
|
||||
|
||||
auto finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
|
||||
finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
|
||||
}
|
||||
|
||||
// Hardcode the texture stage operations and arguments
|
||||
// So the shader handles exactly one combination of values
|
||||
const std::string stageDef = "// STAGE DEFINITIONS";
|
||||
auto stageDefInsert = finalShader.find(stageDef) + stageDef.size();
|
||||
auto stageDefInsert = finalShader.find(stageDef);
|
||||
if (stageDefInsert != std::string::npos) {
|
||||
stageDefInsert += stageDef.size();
|
||||
|
||||
std::stringstream stageSetup;
|
||||
stageSetup << '\n';
|
||||
std::stringstream stageSetup;
|
||||
stageSetup << '\n';
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
#ifdef ENABLE_FF_ALPHAKILL
|
||||
// Even when a stage is disabled, we still have to fully initialize it's values, to prevent
|
||||
// "error X4000: variable 'stages' used without having been completely initialized"
|
||||
// Even when a stage is disabled, we still have to fully initialize it's values, to prevent
|
||||
// "error X4000: variable 'stages' used without having been completely initialized"
|
||||
#else
|
||||
// The stage is initialized to be disabled
|
||||
// We don't have to output anything
|
||||
if (states[i].COLOROP == X_D3DTOP_DISABLE)
|
||||
continue;
|
||||
// The stage is initialized to be disabled
|
||||
// We don't have to output anything
|
||||
if (states[i].COLOROP == X_D3DTOP_DISABLE)
|
||||
continue;
|
||||
|
||||
#endif
|
||||
std::string target = "stages[" + std::to_string(i) + "].";
|
||||
std::string target = "stages[" + std::to_string(i) + "].";
|
||||
|
||||
auto s = states[i];
|
||||
stageSetup << target << "COLOROP = " << GetD3DTOPString(s.COLOROP) << ";\n";
|
||||
auto s = states[i];
|
||||
stageSetup << target << "COLOROP = " << GetD3DTOPString(s.COLOROP) << ";\n";
|
||||
|
||||
stageSetup << target << "COLORARG0 = " << GetD3DTASumString(s.COLORARG0) << ";\n";
|
||||
stageSetup << target << "COLORARG1 = " << GetD3DTASumString(s.COLORARG1) << ";\n";
|
||||
stageSetup << target << "COLORARG2 = " << GetD3DTASumString(s.COLORARG2) << ";\n";
|
||||
stageSetup << target << "COLORARG0 = " << GetD3DTASumString(s.COLORARG0) << ";\n";
|
||||
stageSetup << target << "COLORARG1 = " << GetD3DTASumString(s.COLORARG1) << ";\n";
|
||||
stageSetup << target << "COLORARG2 = " << GetD3DTASumString(s.COLORARG2) << ";\n";
|
||||
|
||||
stageSetup << target << "ALPHAOP = " << GetD3DTOPString(s.ALPHAOP) << ";\n";
|
||||
stageSetup << target << "ALPHAOP = " << GetD3DTOPString(s.ALPHAOP) << ";\n";
|
||||
|
||||
stageSetup << target << "ALPHAARG0 = " << GetD3DTASumString(s.ALPHAARG0) << ";\n";
|
||||
stageSetup << target << "ALPHAARG1 = " << GetD3DTASumString(s.ALPHAARG1) << ";\n";
|
||||
stageSetup << target << "ALPHAARG2 = " << GetD3DTASumString(s.ALPHAARG2) << ";\n";
|
||||
stageSetup << target << "ALPHAARG0 = " << GetD3DTASumString(s.ALPHAARG0) << ";\n";
|
||||
stageSetup << target << "ALPHAARG1 = " << GetD3DTASumString(s.ALPHAARG1) << ";\n";
|
||||
stageSetup << target << "ALPHAARG2 = " << GetD3DTASumString(s.ALPHAARG2) << ";\n";
|
||||
|
||||
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
|
||||
stageSetup << '\n';
|
||||
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
|
||||
stageSetup << '\n';
|
||||
}
|
||||
|
||||
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
|
||||
}
|
||||
|
||||
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
|
||||
|
||||
// Compile the shader
|
||||
ID3DBlob* pShaderBlob;
|
||||
|
||||
|
@ -938,12 +944,15 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
|
|||
auto pseudoSourceFile = hlslDir.append(pseudoFileName).string();
|
||||
EmuCompileShader(finalShader, "ps_3_0", &pShaderBlob, pseudoSourceFile.c_str());
|
||||
|
||||
// Create shader object for the device
|
||||
IDirect3DPixelShader9* pShader = nullptr;
|
||||
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
|
||||
if (hRet != S_OK)
|
||||
CxbxrAbort("Failed to compile fixed function pixel shader");
|
||||
pShaderBlob->Release();
|
||||
if (pShaderBlob) {
|
||||
// Create shader object for the device
|
||||
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
|
||||
if (hRet != S_OK) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to compile fixed function pixel shader");
|
||||
}
|
||||
pShaderBlob->Release();
|
||||
}
|
||||
|
||||
// Insert the shader into the cache
|
||||
ffPsCache[key] = pShader;
|
||||
|
@ -966,7 +975,10 @@ void UpdateFixedFunctionPixelShaderState()
|
|||
ffPsState.SpecularEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_SPECULARENABLE);
|
||||
ffPsState.FogEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGENABLE);
|
||||
ffPsState.FogColor = (D3DXVECTOR3)((D3DXCOLOR)XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGCOLOR));
|
||||
|
||||
ffPsState.FogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
|
||||
ffPsState.FogDensity = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGDENSITY);
|
||||
ffPsState.FogStart = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGSTART);
|
||||
ffPsState.FogEnd = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGEND);
|
||||
// Texture state
|
||||
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
|
||||
|
||||
|
@ -1023,6 +1035,21 @@ void DxbxUpdateActivePixelShader() // NOPATCH
|
|||
// Fetch all other values that are used in the IsEquivalent check :
|
||||
CompletePSDef.SnapshotRuntimeVariables();
|
||||
|
||||
// Support hotloading hlsl
|
||||
static int pixelShaderVersion = -1;
|
||||
int shaderVersion = g_ShaderSources.Update();
|
||||
if (pixelShaderVersion != shaderVersion) {
|
||||
pixelShaderVersion = shaderVersion;
|
||||
g_pD3DDevice->SetPixelShader(nullptr);
|
||||
|
||||
for (auto& hostShader : g_RecompiledPixelShaders) {
|
||||
if (hostShader.ConvertedPixelShader)
|
||||
hostShader.ConvertedPixelShader->Release();
|
||||
}
|
||||
|
||||
g_RecompiledPixelShaders.clear();
|
||||
}
|
||||
|
||||
// Now, see if we already have a shader compiled for this definition :
|
||||
// TODO : Change g_RecompiledPixelShaders into an unordered_map, hash just the identifying PSDef members, and add cache eviction (clearing host resources when pruning)
|
||||
const PSH_RECOMPILED_SHADER* RecompiledPixelShader = nullptr;
|
||||
|
@ -1144,7 +1171,18 @@ void DxbxUpdateActivePixelShader() // NOPATCH
|
|||
frontfaceFactor = cwFrontface ? 1.0 : -1.0;
|
||||
}
|
||||
fColor[PSH_XBOX_CONSTANT_FRONTFACE_FACTOR].r = frontfaceFactor;
|
||||
|
||||
float fogEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGENABLE) > 0;
|
||||
const float fogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
|
||||
const float fogDensity = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGDENSITY);
|
||||
const float fogStart = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGSTART);
|
||||
const float fogEnd = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGEND);
|
||||
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].r = fogTableMode;
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].g = fogDensity;
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].b = fogStart;
|
||||
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].a = fogEnd;
|
||||
fColor[PSH_XBOX_CONSTANT_FOGENABLE].r = fogEnable;
|
||||
|
||||
// Assume all constants are in use (this is much easier than tracking them for no other purpose than to skip a few here)
|
||||
// Read the color from the corresponding render state slot :
|
||||
// Set all host constant values using a single call:
|
||||
|
|
|
@ -469,12 +469,15 @@ extern void EmuExecutePushBufferRaw
|
|||
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_JUMP_LONG");
|
||||
dma_get_jmp_shadow = dma_get;
|
||||
dma_get = (uint32_t *)(CONTIGUOUS_MEMORY_BASE | (word & COMMAND_WORD_MASK_JUMP_LONG));
|
||||
//NV2A uses COMMAND_TYPE_JUMP_LONG as return for COMMAND_TYPE_CALL. we clear the subr_active here to indicate we have returned from COMMAND_TYPE_CALL.
|
||||
subr_active = false;
|
||||
continue; // while
|
||||
case COMMAND_TYPE_CALL: // Note : NV2A return is said not to work?
|
||||
if (subr_active) {
|
||||
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL while another call was active!");
|
||||
// TODO : throw DMA_PUSHER(CALL_SUBR_ACTIVE);
|
||||
return; // For now, don't even attempt to run through
|
||||
// For now, don't even attempt to run through, this should never happened, if it happened, the pgraph handler will go crazy.
|
||||
CxbxrAbort("Pushbuffer COMMAND_TYPE_CALL called without return!");
|
||||
}
|
||||
else {
|
||||
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL");
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle
|
||||
#include "core\hle\D3D8\Direct3D9\RenderStates.h" // For XboxRenderStateConverter
|
||||
#include "core\hle\D3D8\Direct3D9\VertexShaderSource.h" // For g_VertexShaderSource
|
||||
#include "core\hle\D3D8\Direct3D9\VertexShaderCache.h" // For g_VertexShaderCache
|
||||
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
|
||||
#include "core\hle\D3D8\XbVertexBuffer.h" // For CxbxImpl_SetVertexData4f
|
||||
#include "core\hle\D3D8\XbVertexShader.h"
|
||||
#include "core\hle\D3D8\XbD3D8Logging.h" // For DEBUG_D3DRESULT
|
||||
|
@ -49,6 +50,7 @@
|
|||
#include <unordered_map>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <filesystem>
|
||||
|
||||
// External symbols :
|
||||
extern xbox::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS]; // Declared in XbVertexBuffer.cpp
|
||||
|
@ -528,7 +530,7 @@ namespace XboxVertexShaderDecoder
|
|||
return ((((CReg >> 5) & 7) - 3) * 32) + (CReg & 31);
|
||||
}
|
||||
|
||||
static void VshConvertIntermediateParam(VSH_IMD_PARAMETER& Param,
|
||||
static VSH_IMD_PARAMETER VshGetIntermediateParam(
|
||||
uint32_t* pShaderToken,
|
||||
VSH_FIELD_NAME FLD_MUX,
|
||||
VSH_FIELD_NAME FLD_NEG,
|
||||
|
@ -536,80 +538,30 @@ namespace XboxVertexShaderDecoder
|
|||
uint16_t V,
|
||||
uint16_t C)
|
||||
{
|
||||
Param.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX);
|
||||
switch (Param.ParameterType) {
|
||||
VSH_IMD_PARAMETER param{};
|
||||
param.Type = (VSH_IMD_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX);
|
||||
switch (param.Type) {
|
||||
case PARAM_R:
|
||||
Param.Address = R;
|
||||
param.Address = R;
|
||||
break;
|
||||
case PARAM_V:
|
||||
Param.Address = V;
|
||||
param.Address = V;
|
||||
break;
|
||||
case PARAM_C:
|
||||
Param.Address = C;
|
||||
param.Address = C;
|
||||
break;
|
||||
default:
|
||||
LOG_TEST_CASE("parameter type unknown");
|
||||
}
|
||||
|
||||
int d = FLD_NEG - FLD_A_NEG;
|
||||
Param.Neg = VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_NEG)) > 0;
|
||||
Param.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_X));
|
||||
Param.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Y));
|
||||
Param.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Z));
|
||||
Param.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_W));
|
||||
}
|
||||
param.Neg = VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_NEG)) > 0;
|
||||
param.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_X));
|
||||
param.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Y));
|
||||
param.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Z));
|
||||
param.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_W));
|
||||
|
||||
static void VshAddIntermediateInstruction(
|
||||
uint32_t* pShaderToken,
|
||||
IntermediateVertexShader* pShader,
|
||||
VSH_MAC MAC,
|
||||
VSH_ILU ILU,
|
||||
VSH_IMD_OUTPUT_TYPE output_type,
|
||||
int16_t output_address,
|
||||
int8_t output_mask)
|
||||
{
|
||||
// Is the output mask set?
|
||||
if (output_mask == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pShader->Instructions.size() >= VSH_MAX_INTERMEDIATE_COUNT) {
|
||||
CxbxrAbort("Shader exceeds conversion buffer!");
|
||||
}
|
||||
|
||||
VSH_INTERMEDIATE_FORMAT intermediate;
|
||||
intermediate.MAC = MAC;
|
||||
intermediate.ILU = ILU;
|
||||
intermediate.Output.Type = output_type;
|
||||
intermediate.Output.Address = output_address;
|
||||
intermediate.Output.Mask = output_mask;
|
||||
// Get a0.x indirect constant addressing
|
||||
intermediate.IndexesWithA0_X = VshGetField(pShaderToken, FLD_A0X) > 0; // Applies to PARAM_C parameter reads
|
||||
|
||||
int16_t R;
|
||||
int16_t V = VshGetField(pShaderToken, FLD_V);
|
||||
int16_t C = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST));
|
||||
intermediate.ParamCount = 0;
|
||||
if (MAC >= MAC_MOV) {
|
||||
// Get parameter A
|
||||
R = VshGetField(pShaderToken, FLD_A_R);
|
||||
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_A_MUX, FLD_A_NEG, R, V, C);
|
||||
}
|
||||
|
||||
if ((MAC == MAC_MUL) || ((MAC >= MAC_MAD) && (MAC <= MAC_SGE))) {
|
||||
// Get parameter B
|
||||
R = VshGetField(pShaderToken, FLD_B_R);
|
||||
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_B_MUX, FLD_B_NEG, R, V, C);
|
||||
}
|
||||
|
||||
if ((ILU >= ILU_MOV) || (MAC == MAC_ADD) || (MAC == MAC_MAD)) {
|
||||
// Get parameter C
|
||||
R = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | VshGetField(pShaderToken, FLD_C_R_LOW);
|
||||
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_C_MUX, FLD_C_NEG, R, V, C);
|
||||
}
|
||||
|
||||
// Add the instruction to the shader
|
||||
pShader->Instructions.push_back(intermediate);
|
||||
return param;
|
||||
}
|
||||
|
||||
static bool VshConvertToIntermediate(uint32_t* pShaderToken, IntermediateVertexShader* pShader)
|
||||
|
@ -619,52 +571,86 @@ namespace XboxVertexShaderDecoder
|
|||
VSH_MAC MAC = (VSH_MAC)VshGetField(pShaderToken, FLD_MAC);
|
||||
if (MAC > MAC_ARL) LOG_TEST_CASE("Unknown MAC");
|
||||
|
||||
// Output register
|
||||
VSH_OUTPUT_MUX OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX);
|
||||
int16_t OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS);
|
||||
VSH_IMD_OUTPUT_TYPE OutputType;
|
||||
if ((VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB) == OUTPUT_C) {
|
||||
OutputType = IMD_OUTPUT_C;
|
||||
OutputAddress = ConvertCRegister(OutputAddress);
|
||||
} else { // OUTPUT_O:
|
||||
OutputType = IMD_OUTPUT_O;
|
||||
OutputAddress = OutputAddress & 0xF;
|
||||
}
|
||||
|
||||
// MAC,ILU output R register
|
||||
int16_t RAddress = VshGetField(pShaderToken, FLD_OUT_R);
|
||||
|
||||
// Test for paired opcodes
|
||||
bool bIsPaired = (MAC != MAC_NOP) && (ILU != ILU_NOP);
|
||||
|
||||
VSH_IMD_MAC_OP MacOp{};
|
||||
VSH_IMD_ILU_OP IluOp{};
|
||||
|
||||
// Set up input registers
|
||||
int16_t AR = VshGetField(pShaderToken, FLD_A_R);
|
||||
int16_t BR = VshGetField(pShaderToken, FLD_B_R);
|
||||
int16_t CR = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | VshGetField(pShaderToken, FLD_C_R_LOW);
|
||||
int16_t V = VshGetField(pShaderToken, FLD_V);
|
||||
int16_t C = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST));
|
||||
|
||||
// Check if there's a MAC opcode
|
||||
if (MAC > MAC_NOP && MAC <= MAC_ARL) {
|
||||
if (MAC != MAC_NOP && MAC <= MAC_ARL) {
|
||||
MacOp.Opcode = MAC;
|
||||
|
||||
if (bIsPaired && RAddress == 1) {
|
||||
// Ignore paired MAC opcodes that write to R1
|
||||
} else {
|
||||
if (MAC == MAC_ARL) {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_A0X, 0, MASK_X);
|
||||
} else {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_R, RAddress, VshGetField(pShaderToken, FLD_OUT_MAC_MASK));
|
||||
}
|
||||
}
|
||||
else if (MAC == MAC_ARL) {
|
||||
MacOp.Dest.Type = IMD_DEST_A0X;
|
||||
MacOp.Dest.Mask = MASK_X;
|
||||
}
|
||||
else {
|
||||
MacOp.Dest.Type = IMD_DEST_R;
|
||||
MacOp.Dest.Address = RAddress;
|
||||
MacOp.Dest.Mask = VshGetField(pShaderToken, FLD_OUT_MAC_MASK);
|
||||
}
|
||||
|
||||
// Check if we must add a muxed MAC opcode as well
|
||||
if (OutputMux == OMUX_MAC) {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK));
|
||||
if (MAC >= MAC_MOV) {
|
||||
MacOp.Parameters[MacOp.ParamCount++] = VshGetIntermediateParam(pShaderToken, FLD_A_MUX, FLD_A_NEG, AR, V, C);
|
||||
}
|
||||
|
||||
if (MAC == MAC_MUL || (MAC >= MAC_MAD && MAC <= MAC_SGE)) {
|
||||
MacOp.Parameters[MacOp.ParamCount++] = VshGetIntermediateParam(pShaderToken, FLD_B_MUX, FLD_B_NEG, BR, V, C);
|
||||
}
|
||||
|
||||
if (MAC == MAC_ADD || MAC == MAC_MAD) {
|
||||
MacOp.Parameters[MacOp.ParamCount++] = VshGetIntermediateParam(pShaderToken, FLD_C_MUX, FLD_C_NEG, CR, V, C);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there's an ILU opcode
|
||||
if (ILU != ILU_NOP) {
|
||||
// Paired ILU opcodes will only write to R1
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, IMD_OUTPUT_R, bIsPaired ? 1 : RAddress, VshGetField(pShaderToken, FLD_OUT_ILU_MASK));
|
||||
// Check if we must add a muxed ILU opcode as well
|
||||
if (OutputMux == OMUX_ILU) {
|
||||
VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK));
|
||||
}
|
||||
IluOp.Opcode = ILU;
|
||||
IluOp.Dest.Type = IMD_DEST_R;
|
||||
IluOp.Dest.Address = bIsPaired ? 1 : RAddress;
|
||||
IluOp.Dest.Mask = VshGetField(pShaderToken, FLD_OUT_ILU_MASK);
|
||||
IluOp.Parameter = VshGetIntermediateParam(pShaderToken, FLD_C_MUX, FLD_C_NEG, CR, V, C);
|
||||
}
|
||||
|
||||
// Output register
|
||||
VSH_OUTPUT_MUX OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX);
|
||||
int16_t OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS);
|
||||
VSH_IMD_DEST_TYPE OutputType;
|
||||
if ((VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB) == OUTPUT_C) {
|
||||
OutputType = IMD_DEST_C;
|
||||
OutputAddress = ConvertCRegister(OutputAddress);
|
||||
}
|
||||
else { // OUTPUT_O:
|
||||
OutputType = IMD_DEST_O;
|
||||
OutputAddress = OutputAddress & 0xF;
|
||||
}
|
||||
|
||||
VSH_IMD_INSTR imd{};
|
||||
imd.MAC = MacOp;
|
||||
imd.ILU = IluOp;
|
||||
imd.IndexesWithA0_X = VshGetField(pShaderToken, FLD_A0X) > 0;
|
||||
imd.ORegSource = OutputMux == OMUX_MAC ? SRC_MAC : SRC_ILU;
|
||||
imd.ORegDest.Type = OutputType;
|
||||
imd.ORegDest.Address = OutputAddress;
|
||||
imd.ORegDest.Mask = VshGetField(pShaderToken, FLD_OUT_O_MASK);
|
||||
|
||||
pShader->Instructions.push_back(imd);
|
||||
|
||||
return VshGetField(pShaderToken, FLD_FINAL) == 0;
|
||||
}
|
||||
};
|
||||
|
@ -1140,10 +1126,49 @@ IDirect3DVertexDeclaration* CxbxCreateHostVertexDeclaration(D3DVERTEXELEMENT *pD
|
|||
return pHostVertexDeclaration;
|
||||
}
|
||||
|
||||
static IDirect3DVertexShader* passthroughshader;
|
||||
IDirect3DVertexShader* InitShader(void (*compileFunc)(ID3DBlob**), const char* label) {
|
||||
IDirect3DVertexShader* shader = nullptr;
|
||||
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
compileFunc(&pBlob);
|
||||
if (pBlob) {
|
||||
HRESULT hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &shader);
|
||||
pBlob->Release();
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to create shader: %s", label);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
void CxbxUpdateHostVertexShader()
|
||||
{
|
||||
extern bool g_bUsePassthroughHLSL; // TMP glue
|
||||
// TODO: move d3d9 state to VertexShader.cpp
|
||||
static IDirect3DVertexShader* fixedFunctionShader = nullptr; // TODO: move to shader cache
|
||||
static IDirect3DVertexShader* passthroughShader = nullptr;
|
||||
static int vertexShaderVersion = -1;
|
||||
|
||||
int shaderVersion = g_ShaderSources.Update();
|
||||
if (vertexShaderVersion != shaderVersion) {
|
||||
vertexShaderVersion = shaderVersion;
|
||||
g_pD3DDevice->SetVertexShader(nullptr);
|
||||
|
||||
EmuLog(LOG_LEVEL::INFO, "Loading vertex shaders...");
|
||||
|
||||
g_VertexShaderCache.Clear();
|
||||
|
||||
if (fixedFunctionShader) {
|
||||
fixedFunctionShader->Release();
|
||||
fixedFunctionShader = nullptr;
|
||||
}
|
||||
fixedFunctionShader = InitShader(EmuCompileFixedFunction, "Fixed Function Vertex Shader");
|
||||
|
||||
if (passthroughShader) {
|
||||
passthroughShader->Release();
|
||||
passthroughShader = nullptr;
|
||||
}
|
||||
passthroughShader = InitShader(EmuCompileXboxPassthrough, "Passthrough Vertex Shader");
|
||||
}
|
||||
|
||||
// TODO Call this when state is dirty
|
||||
// Rather than every time state changes
|
||||
|
@ -1151,43 +1176,20 @@ void CxbxUpdateHostVertexShader()
|
|||
LOG_INIT; // Allows use of DEBUG_D3DRESULT
|
||||
|
||||
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
|
||||
IDirect3DVertexShader* fixedFunctionShader = nullptr;
|
||||
HRESULT hRet;
|
||||
|
||||
if (g_UseFixedFunctionVertexShader) {
|
||||
static IDirect3DVertexShader* ffHlsl = nullptr;
|
||||
if (ffHlsl == nullptr) {
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
EmuCompileFixedFunction(&pBlob);
|
||||
if (pBlob) {
|
||||
hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &ffHlsl);
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to create fixed-function shader");
|
||||
}
|
||||
}
|
||||
fixedFunctionShader = ffHlsl;
|
||||
}
|
||||
|
||||
hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to set fixed-function shader");
|
||||
}
|
||||
else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) {
|
||||
if (passthroughshader == nullptr) {
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
EmuCompileXboxPassthrough(&pBlob);
|
||||
if (pBlob) {
|
||||
g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &passthroughshader);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughshader);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughShader);
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to set passthrough shader");
|
||||
}
|
||||
else {
|
||||
auto pTokens = GetCxbxVertexShaderSlotPtr(g_Xbox_VertexShader_FunctionSlots_StartAddress);
|
||||
assert(pTokens);
|
||||
// Create a vertex shader from the tokens
|
||||
DWORD shaderSize;
|
||||
auto VertexShaderKey = g_VertexShaderSource.CreateShader(pTokens, &shaderSize);
|
||||
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderSource.GetShader(VertexShaderKey);
|
||||
auto VertexShaderKey = g_VertexShaderCache.CreateShader(pTokens, &shaderSize);
|
||||
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderCache.GetShader(VertexShaderKey);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(pHostVertexShader);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
|
||||
}
|
||||
|
@ -1575,7 +1577,7 @@ void CxbxImpl_DeleteVertexShader(DWORD Handle)
|
|||
RegisterCxbxVertexDeclaration(pCxbxVertexDeclaration->Key, nullptr); // Remove from cache (which will free present pCxbxVertexDeclaration)
|
||||
|
||||
// Release the host vertex shader
|
||||
g_VertexShaderSource.ReleaseShader(pCxbxVertexShader->Key);
|
||||
g_VertexShaderCache.ReleaseShader(pCxbxVertexShader->Key);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -147,18 +147,18 @@ enum VSH_MAC { // Dxbx note : MAC stands for 'Multiply And Accumulate' opcodes
|
|||
// ??? 15 - 2 values of the 4 bits are undefined
|
||||
};
|
||||
|
||||
enum VSH_IMD_OUTPUT_TYPE {
|
||||
IMD_OUTPUT_C,
|
||||
IMD_OUTPUT_R,
|
||||
IMD_OUTPUT_O,
|
||||
IMD_OUTPUT_A0X
|
||||
enum VSH_IMD_DEST_TYPE {
|
||||
IMD_DEST_C,
|
||||
IMD_DEST_R,
|
||||
IMD_DEST_O,
|
||||
IMD_DEST_A0X
|
||||
};
|
||||
|
||||
typedef struct _VSH_IMD_OUTPUT {
|
||||
VSH_IMD_OUTPUT_TYPE Type;
|
||||
int16_t Address;
|
||||
int8_t Mask;
|
||||
} VSH_IMD_OUTPUT;
|
||||
typedef struct {
|
||||
VSH_IMD_DEST_TYPE Type;
|
||||
int16_t Address;
|
||||
int8_t Mask; // If 0 skip writing to this output
|
||||
} VSH_IMD_DEST;
|
||||
|
||||
enum VSH_SWIZZLE {
|
||||
SWIZZLE_X = 0,
|
||||
|
@ -167,7 +167,7 @@ enum VSH_SWIZZLE {
|
|||
SWIZZLE_W
|
||||
};
|
||||
|
||||
enum VSH_PARAMETER_TYPE {
|
||||
enum VSH_IMD_PARAMETER_TYPE {
|
||||
PARAM_UNKNOWN = 0,
|
||||
PARAM_R, // Temporary (scRatch) registers
|
||||
PARAM_V, // Vertex registers
|
||||
|
@ -175,28 +175,49 @@ enum VSH_PARAMETER_TYPE {
|
|||
PARAM_O // = 0??
|
||||
};
|
||||
|
||||
typedef struct _VSH_IMD_PARAMETER {
|
||||
VSH_PARAMETER_TYPE ParameterType; // Parameter type, R, V or C
|
||||
typedef struct {
|
||||
VSH_IMD_PARAMETER_TYPE Type; // Parameter type, R, V or C
|
||||
bool Neg; // true if negated, false if not
|
||||
VSH_SWIZZLE Swizzle[4]; // The four swizzles
|
||||
int16_t Address; // Register address
|
||||
} VSH_IMD_PARAMETER;
|
||||
|
||||
typedef struct _VSH_INTERMEDIATE_FORMAT {
|
||||
VSH_MAC MAC;
|
||||
VSH_ILU ILU;
|
||||
VSH_IMD_OUTPUT Output;
|
||||
unsigned ParamCount;
|
||||
VSH_IMD_PARAMETER Parameters[3];
|
||||
// There is only a single address register in Microsoft DirectX 8.0.
|
||||
// The address register, designated as a0.x, may be used as signed
|
||||
// integer offset in relative addressing into the constant register file.
|
||||
// c[a0.x + n]
|
||||
bool IndexesWithA0_X;
|
||||
} VSH_INTERMEDIATE_FORMAT;
|
||||
typedef struct {
|
||||
VSH_ILU Opcode;
|
||||
VSH_IMD_DEST Dest;
|
||||
VSH_IMD_PARAMETER Parameter;
|
||||
} VSH_IMD_ILU_OP;
|
||||
|
||||
typedef struct _IntermediateVertexShader {
|
||||
std::vector<VSH_INTERMEDIATE_FORMAT> Instructions;
|
||||
typedef struct {
|
||||
VSH_MAC Opcode;
|
||||
VSH_IMD_DEST Dest;
|
||||
uint8_t ParamCount;
|
||||
VSH_IMD_PARAMETER Parameters[3];
|
||||
} VSH_IMD_MAC_OP;
|
||||
|
||||
enum VSH_IMD_OREG_SOURCE {
|
||||
SRC_MAC,
|
||||
SRC_ILU,
|
||||
};
|
||||
|
||||
// Intermediate decoded VSH instruction
|
||||
// Up to two operations (MAC and ILU)
|
||||
// Writes to up to 3 destination registers
|
||||
// One dest per op + one output register
|
||||
typedef struct {
|
||||
VSH_IMD_MAC_OP MAC;
|
||||
VSH_IMD_ILU_OP ILU;
|
||||
|
||||
VSH_IMD_OREG_SOURCE ORegSource;
|
||||
VSH_IMD_DEST ORegDest;
|
||||
|
||||
// True if the constant input C should use the index register a0
|
||||
// c[a0.x + n]
|
||||
bool IndexesWithA0_X;
|
||||
} VSH_IMD_INSTR;
|
||||
|
||||
typedef struct {
|
||||
std::vector<VSH_IMD_INSTR> Instructions;
|
||||
} IntermediateVertexShader;
|
||||
|
||||
// parse xbox vertex shader function into an intermediate format
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
// TODO: Move these to LLE APUDevice once we have one!
|
||||
|
||||
static constexpr uint32_t APU_TIMER_FREQUENCY = 48000;
|
||||
static uint64_t dsound_last;
|
||||
|
||||
uint32_t GetAPUTime()
|
||||
{
|
||||
|
@ -85,10 +86,6 @@ uint32_t GetAPUTime()
|
|||
there is chance of failure which contain value greater than 0.
|
||||
*/
|
||||
|
||||
// Managed memory xbox audio variables
|
||||
static std::thread dsound_thread;
|
||||
static void dsound_thread_worker(LPVOID);
|
||||
|
||||
#include "DirectSoundInline.hpp"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -98,6 +95,7 @@ extern "C" {
|
|||
void CxbxInitAudio()
|
||||
{
|
||||
g_EmuShared->GetAudioSettings(&g_XBAudio);
|
||||
dsound_last = get_now();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -125,10 +123,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
|
|||
|
||||
HRESULT hRet = DS_OK;
|
||||
|
||||
if (!initialized) {
|
||||
dsound_thread = std::thread(dsound_thread_worker, nullptr);
|
||||
}
|
||||
|
||||
// Set this flag when this function is called
|
||||
g_bDSoundCreateCalled = TRUE;
|
||||
|
||||
|
@ -454,51 +448,51 @@ void StreamBufferAudio(xbox::XbHybridDSBuffer* pHybridBuffer, float msToCopy) {
|
|||
}
|
||||
}
|
||||
|
||||
static void dsound_thread_worker(LPVOID nullPtr)
|
||||
void dsound_async_worker()
|
||||
{
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
const int dsStreamInterval = 300;
|
||||
int waitCounter = 0;
|
||||
xbox::LARGE_INTEGER getTime;
|
||||
xbox::KeQuerySystemTime(&getTime);
|
||||
DirectSoundDoWork_Stream(getTime);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// FIXME time this loop more accurately
|
||||
// and account for variation in the length of Sleep calls
|
||||
void dsound_worker()
|
||||
{
|
||||
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
|
||||
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
|
||||
|
||||
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
|
||||
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
|
||||
Sleep(g_dsBufferStreaming.streamInterval);
|
||||
waitCounter += g_dsBufferStreaming.streamInterval;
|
||||
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
|
||||
{
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
if (waitCounter > dsStreamInterval) {
|
||||
waitCounter = 0;
|
||||
|
||||
// For Async process purpose only
|
||||
xbox::LARGE_INTEGER getTime;
|
||||
xbox::KeQuerySystemTime(&getTime);
|
||||
DirectSoundDoWork_Stream(getTime);
|
||||
// Stream sound buffer audio
|
||||
// because the title may change the content of sound buffers at any time
|
||||
for (auto& pBuffer : g_pDSoundBufferCache) {
|
||||
// Avoid expensive calls to DirectSound on buffers unless they've been played at least once
|
||||
// Since some titles create a large amount of buffers, but only use a few
|
||||
if (pBuffer->emuDSBuffer->EmuStreamingInfo.playRequested) {
|
||||
DWORD status;
|
||||
HRESULT hRet = pBuffer->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&status);
|
||||
if (hRet == 0 && status & DSBSTATUS_PLAYING) {
|
||||
auto streamMs = g_dsBufferStreaming.streamInterval + g_dsBufferStreaming.streamAhead;
|
||||
StreamBufferAudio(pBuffer, streamMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stream sound buffer audio
|
||||
// because the title may change the content of sound buffers at any time
|
||||
for (auto& pBuffer : g_pDSoundBufferCache) {
|
||||
// Avoid expensive calls to DirectSound on buffers unless they've been played at least once
|
||||
// Since some titles create a large amount of buffers, but only use a few
|
||||
if (pBuffer->emuDSBuffer->EmuStreamingInfo.playRequested) {
|
||||
DWORD status;
|
||||
HRESULT hRet = pBuffer->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&status);
|
||||
if (hRet == 0 && status & DSBSTATUS_PLAYING) {
|
||||
auto streamMs = g_dsBufferStreaming.streamInterval + g_dsBufferStreaming.streamAhead;
|
||||
StreamBufferAudio(pBuffer, streamMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t dsound_next(uint64_t now)
|
||||
{
|
||||
constexpr uint64_t dsound_period = 300 * 1000;
|
||||
uint64_t next = dsound_last + dsound_period;
|
||||
|
||||
if (now >= next) {
|
||||
dsound_async_worker();
|
||||
dsound_last = get_now();
|
||||
return dsound_period;
|
||||
}
|
||||
|
||||
return dsound_last + dsound_period - now; // time remaining until next dsound async event
|
||||
}
|
||||
|
||||
// Kismet given name for RadWolfie's experiment major issue in the mutt.
|
||||
|
|
|
@ -81,3 +81,6 @@ extern DsBufferStreaming g_dsBufferStreaming;
|
|||
|
||||
extern void DirectSoundDoWork_Buffer(xbox::LARGE_INTEGER& time);
|
||||
extern void DirectSoundDoWork_Stream(xbox::LARGE_INTEGER& time);
|
||||
extern void dsound_async_worker();
|
||||
extern void dsound_worker();
|
||||
extern uint64_t dsound_next(uint64_t now);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <iomanip> // For std::setfill and std::setw
|
||||
#include <filesystem>
|
||||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
|
@ -205,6 +206,7 @@ void CDECL EmuOutputMessage(xb_output_message mFlag,
|
|||
|
||||
void CDECL EmuRegisterSymbol(const char* library_str,
|
||||
uint32_t library_flag,
|
||||
uint32_t xref_index,
|
||||
const char* symbol_str,
|
||||
uint32_t func_addr,
|
||||
uint32_t revision)
|
||||
|
@ -438,22 +440,6 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
|
|||
<< " -> " << functionName << "\n";
|
||||
std::printf(output.str().c_str());
|
||||
}
|
||||
|
||||
// Fix up Render state and Texture States
|
||||
if (g_SymbolAddresses.find("D3DDeferredRenderState") == g_SymbolAddresses.end()
|
||||
|| g_SymbolAddresses["D3DDeferredRenderState"] == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState was not found!");
|
||||
}
|
||||
|
||||
if (g_SymbolAddresses.find("D3DDeferredTextureState") == g_SymbolAddresses.end()
|
||||
|| g_SymbolAddresses["D3DDeferredTextureState"] == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredTextureState was not found!");
|
||||
}
|
||||
|
||||
if (g_SymbolAddresses.find("D3DDEVICE") == g_SymbolAddresses.end()
|
||||
|| g_SymbolAddresses["D3DDEVICE"] == 0) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "D3DDEVICE was not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
670
src/core/hle/JVS/JVS.cpp
Normal file
670
src/core/hle/JVS/JVS.cpp
Normal file
|
@ -0,0 +1,670 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2019 Luke Usher <luke.usher@outlook.com>
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#define _XBOXKRNL_DEFEXTRN_
|
||||
|
||||
#define LOG_PREFIX CXBXR_MODULE::JVS
|
||||
|
||||
#undef FIELD_OFFSET // prevent macro redefinition warnings
|
||||
|
||||
#include "EmuShared.h"
|
||||
#include "common\Logging.h"
|
||||
#include "common\FilePaths.hpp"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\JVS\JVS.h"
|
||||
#include "core\hle\Intercept.hpp"
|
||||
#include "devices\chihiro\JvsIo.h"
|
||||
#include "devices\Xbox.h"
|
||||
#include <thread>
|
||||
|
||||
#pragma warning(disable:4244) // Silence mio compiler warnings
|
||||
#include <mio/mmap.hpp>
|
||||
#pragma warning(default:4244)
|
||||
|
||||
// Global variables used to store JVS related firmware/eeproms
|
||||
mio::mmap_sink g_BaseBoardQcFirmware; // QC Microcontroller firmware
|
||||
mio::mmap_sink g_BaseBoardScFirmware; // SC Microcontroller firmware
|
||||
mio::mmap_sink g_BaseBoardEeprom; // Config EEPROM
|
||||
mio::mmap_sink g_BaseBoardBackupMemory; // Backup Memory (high-scores, etc)
|
||||
|
||||
typedef struct _baseboard_state_t {
|
||||
// Switch 1: Horizontal Display, On = Vertical Display
|
||||
// Switch 2-3: D3D Resolution Configuraton
|
||||
// Switch 4: 0 = Hardware Vertex Processing, 1 = Software Vertex processing (Causes D3D to fail).. Horizontal frequency?
|
||||
// Switch 5: Unknown
|
||||
// Switch 6-8: Connected AV Pack flag
|
||||
bool DipSwitch[8];
|
||||
bool TestButton;
|
||||
bool ServiceButton;
|
||||
uint8_t JvsSense;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
// TODO: Make this configurable
|
||||
DipSwitch[0] = false;
|
||||
DipSwitch[1] = false;
|
||||
DipSwitch[2] = true;
|
||||
DipSwitch[3] = true;
|
||||
DipSwitch[4] = false;
|
||||
DipSwitch[5] = true;
|
||||
DipSwitch[6] = true;
|
||||
DipSwitch[7] = true;
|
||||
TestButton = false;
|
||||
ServiceButton = false;
|
||||
JvsSense = 0;
|
||||
}
|
||||
|
||||
uint8_t GetAvPack()
|
||||
{
|
||||
uint8_t avpack = 0;
|
||||
|
||||
// Dip Switches 6,7,8 combine to form the Av Pack ID
|
||||
// TODO: Verify the order, these might need to be reversed
|
||||
avpack &= ~((DipSwitch[5] ? 1 : 0) << 2);
|
||||
avpack &= ~((DipSwitch[6] ? 1 : 0) << 1);
|
||||
avpack &= ~ (DipSwitch[7] ? 1 : 0);
|
||||
|
||||
return avpack;
|
||||
}
|
||||
|
||||
uint8_t GetPINSA()
|
||||
{
|
||||
uint8_t PINSA = 0b11101111; // 1 = Off, 0 = On
|
||||
|
||||
// Dip Switches 1-3 are set on PINSA bits 0-2
|
||||
PINSA &= ~ (DipSwitch[0] ? 1 : 0);
|
||||
PINSA &= ~((DipSwitch[1] ? 1 : 0) << 1);
|
||||
PINSA &= ~((DipSwitch[2] ? 1 : 0) << 2);
|
||||
|
||||
// Bit 3 is currently unknown, so we don't modify that bit
|
||||
|
||||
// Dip Switches 4,5 are set on bits 4,5
|
||||
PINSA &= ~((DipSwitch[3] ? 1 : 0) << 4);
|
||||
PINSA &= ~((DipSwitch[4] ? 1 : 0) << 5);
|
||||
|
||||
// Bit 6 = Test, Bit 7 = Service
|
||||
PINSA &= ~((TestButton ? 1 : 0) << 6);
|
||||
PINSA &= ~((ServiceButton ? 1 : 0) << 7);
|
||||
|
||||
return PINSA;
|
||||
}
|
||||
|
||||
uint8_t GetPINSB()
|
||||
{
|
||||
// PINSB bits 0-1 represent the JVS Sense line
|
||||
return JvsSense;
|
||||
}
|
||||
|
||||
} baseboard_state_t;
|
||||
|
||||
baseboard_state_t ChihiroBaseBoardState = {};
|
||||
DWORD* g_pPINSA = nullptr; // Qc PINSA Register: Contains Filter Board DIP Switches + Test/Service buttons
|
||||
DWORD* g_pPINSB = nullptr; // Qc PINSB Register: Contains JVS Sense Pin state
|
||||
|
||||
bool JVS_LoadFile(std::string path, mio::mmap_sink& data)
|
||||
{
|
||||
FILE* fp = fopen(path.c_str(), "rb");
|
||||
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code error;
|
||||
data = mio::make_mmap_sink(path, error);
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JvsInputThread()
|
||||
{
|
||||
g_AffinityPolicy->SetAffinityOther(GetCurrentThread());
|
||||
|
||||
while (true) {
|
||||
// This thread is responsible for reading the emulated Baseboard state
|
||||
// and setting the correct internal variables
|
||||
ChihiroBaseBoardState.TestButton = GetAsyncKeyState(VK_F1);
|
||||
ChihiroBaseBoardState.ServiceButton = GetAsyncKeyState(VK_F2);
|
||||
|
||||
// Call into the Jvs I/O board update function
|
||||
g_pJvsIo->Update();
|
||||
|
||||
if (g_pPINSA != nullptr) {
|
||||
*g_pPINSA = ChihiroBaseBoardState.GetPINSA();
|
||||
}
|
||||
|
||||
if (g_pPINSB != nullptr) {
|
||||
*g_pPINSB = ChihiroBaseBoardState.GetPINSB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JVS_Init()
|
||||
{
|
||||
// Init Jvs IO board
|
||||
g_pJvsIo = new JvsIo(&ChihiroBaseBoardState.JvsSense);
|
||||
|
||||
std::string romPath = g_DataFilePath + std::string("\\EmuDisk\\Chihiro");
|
||||
std::string baseBoardQcFirmwarePath = "ic10_g24lc64.bin";
|
||||
std::string baseBoardScFirmwarePath = "pc20_g24lc64.bin";
|
||||
std::string baseBoardEepromPath = "ic11_24lc024.bin";
|
||||
std::string baseBoardBackupRamPath = "backup_ram.bin";
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardQcFirmwarePath).c_str(), g_BaseBoardQcFirmware)) {
|
||||
CxbxrAbort("Failed to load base board firmware: %s", baseBoardQcFirmwarePath.c_str());
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardScFirmwarePath).c_str(), g_BaseBoardScFirmware)) {
|
||||
CxbxrAbort("Failed to load base board qc firmware: %s", baseBoardScFirmwarePath.c_str());
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardEepromPath).c_str(), g_BaseBoardEeprom)) {
|
||||
CxbxrAbort("Failed to load base board EEPROM: %s", baseBoardEepromPath.c_str());
|
||||
}
|
||||
|
||||
// backup ram is a special case, we can create it automatically if it doesn't exist
|
||||
if (!std::filesystem::exists(romPath + "\\" + baseBoardBackupRamPath)) {
|
||||
FILE *fp = fopen((romPath + "\\" + baseBoardBackupRamPath).c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
CxbxrAbort("Could not create Backup File: %s", baseBoardBackupRamPath.c_str());
|
||||
}
|
||||
|
||||
// Create 128kb empty file for backup ram
|
||||
fseek(fp, (128 * 1024) - 1, SEEK_SET);
|
||||
fputc('\0', fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardBackupRamPath).c_str(), g_BaseBoardBackupMemory)) {
|
||||
CxbxrAbort("Failed to load base board BACKUP RAM: %s", baseBoardBackupRamPath.c_str());
|
||||
}
|
||||
|
||||
// Determine which version of JVS_SendCommand this title is using and derive the offset
|
||||
// TODO: Extract this into a function and also locate PINSB
|
||||
static int JvsSendCommandVersion = -1;
|
||||
g_pPINSA = nullptr;
|
||||
g_pPINSB = nullptr;
|
||||
|
||||
auto JvsSendCommandOffset1 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand");
|
||||
auto JvsSendCommandOffset2 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand2");
|
||||
auto JvsSendCommandOffset3 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand3");
|
||||
|
||||
if (JvsSendCommandOffset1) {
|
||||
JvsSendCommandVersion = 1;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset1 + 0x2A0);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
|
||||
if (JvsSendCommandOffset2) {
|
||||
JvsSendCommandVersion = 2;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset2 + 0x312);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
|
||||
if (JvsSendCommandOffset3) {
|
||||
JvsSendCommandVersion = 3;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x307);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
|
||||
if ((DWORD)g_pPINSA > XBE_MAX_VA) {
|
||||
// This was invalid, we must have the other varient of SendCommand3 (SEGABOOT)
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x302);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Set state to a sane initial default
|
||||
ChihiroBaseBoardState.Reset();
|
||||
|
||||
// Auto-Patch Chihiro Region Flag to match the desired game
|
||||
uint8_t ®ion = (uint8_t &)g_BaseBoardQcFirmware[0x1F00];
|
||||
auto regionFlags = g_MediaBoard->GetBootId().regionFlags;
|
||||
|
||||
// The region of the system can be converted to a game region flag by doing 1 << region
|
||||
// This gives a bitmask that can be ANDed with the BootID region flags to check the games support
|
||||
if ((regionFlags & (1 << region)) == 0) {
|
||||
// The region was not compatible, so we need to patch the region flag
|
||||
// This avoids "Error 05: This game is not acceptable by main board."
|
||||
// We use USA,EXPORT,JAPAN to make sure mutiple-language games default to English first
|
||||
if (regionFlags & MB_CHIHIRO_REGION_FLAG_USA) {
|
||||
region = 2;
|
||||
}
|
||||
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_EXPORT) {
|
||||
region = 3;
|
||||
}
|
||||
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_JAPAN) {
|
||||
region = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn the Chihiro/JVS Input Thread
|
||||
std::thread(JvsInputThread).detach();
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JVS_SendCommand)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD Command,
|
||||
DWORD a3,
|
||||
DWORD Length,
|
||||
DWORD a5,
|
||||
DWORD a6,
|
||||
DWORD a7,
|
||||
DWORD a8
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(Command)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a5)
|
||||
LOG_FUNC_ARG(a6)
|
||||
LOG_FUNC_ARG(a7)
|
||||
LOG_FUNC_ARG(a8)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardBackupMemory[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardBackupMemory[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardEeprom[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardEeprom[Offset], (void*)Buffer, Length);
|
||||
|
||||
std::error_code error;
|
||||
g_BaseBoardEeprom.sync(error);
|
||||
|
||||
if (error) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Couldn't sync EEPROM to disk");
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardQcFirmware[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardQcFirmware[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsNodeReceivePacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
PDWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG_OUT(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
// Receive the packet from the connected IO board
|
||||
uint8_t DeviceId = g_pJvsIo->GetDeviceId();
|
||||
|
||||
// TODO : "Number of packets received" below might imply multiple packets might need receiving here...
|
||||
uint16_t payloadSize = (uint16_t)g_pJvsIo->ReceivePacket(&Buffer[6]);
|
||||
if (payloadSize > 0) {
|
||||
Buffer[0] = 0; // Empty header byte, ignored
|
||||
Buffer[1] = 1; // Number of packets received
|
||||
Buffer[2] = DeviceId;
|
||||
Buffer[3] = 0; // Unused
|
||||
|
||||
*Length = payloadSize + 6;
|
||||
|
||||
// Write the payload size header field
|
||||
*((uint16_t*)&Buffer[4]) = payloadSize; // Packet Length (bytes 4-5)
|
||||
// TODO : Prevent little/big endian issues here by explicitly setting Buffer[4] and Buffer[5]
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsNodeSendPacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
// Buffer contains two opening bytes, '00' and 'XX', where XX is the number of JVS packets to send
|
||||
// Each JVS packet is prepended with a '00' byte, the rest of the packet is as-per the JVS I/O standard.
|
||||
|
||||
// Ignore Buffer[0] (should be 0x00)
|
||||
unsigned packetCount = Buffer[1];
|
||||
uint8_t* packetPtr = &Buffer[2]; // First JVS packet starts at offset 2;
|
||||
|
||||
for (unsigned i = 0; i < packetCount; i++) {
|
||||
// Skip the separator byte (should be 0x00)
|
||||
packetPtr++;
|
||||
|
||||
// Send the packet to the connected I/O board
|
||||
size_t bytes = g_pJvsIo->SendPacket(packetPtr);
|
||||
|
||||
// Set packetPtr to the next packet
|
||||
packetPtr += bytes;
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
// Binary Coded Decimal to Decimal conversion
|
||||
uint8_t BcdToUint8(uint8_t value)
|
||||
{
|
||||
return value - 6 * (value >> 4);
|
||||
}
|
||||
|
||||
uint8_t Uint8ToBcd(uint8_t value)
|
||||
{
|
||||
return value + 6 * (value / 10);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Read)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime* pTime,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG_OUT(time)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
time_t hostTime;
|
||||
struct tm* hostTimeInfo;
|
||||
time(&hostTime);
|
||||
hostTimeInfo = localtime(&hostTime);
|
||||
|
||||
memset(pTime, 0, sizeof(JvsRTCTime));
|
||||
|
||||
pTime->day = Uint8ToBcd(hostTimeInfo->tm_mday);
|
||||
pTime->month = Uint8ToBcd(hostTimeInfo->tm_mon + 1); // Chihiro month counter stats at 1
|
||||
pTime->year = Uint8ToBcd(hostTimeInfo->tm_year - 100); // Chihiro starts counting from year 2000
|
||||
|
||||
pTime->hour = Uint8ToBcd(hostTimeInfo->tm_hour);
|
||||
pTime->minute = Uint8ToBcd(hostTimeInfo->tm_min);
|
||||
pTime->second = Uint8ToBcd(hostTimeInfo->tm_sec);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Write)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime* pTime,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG_OUT(time)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardScFirmware[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardScFirmware[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScSendMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScSendRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
182
src/core/hle/JVS/JVS.h
Normal file
182
src/core/hle/JVS/JVS.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
// ******************************************************************
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2019 Luke Usher <luke.usher@outlook.com>
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#ifndef JVS_H
|
||||
#define JVS_H
|
||||
|
||||
// Used by CxbxKrnl to setup JVS roms
|
||||
void JVS_Init();
|
||||
|
||||
#include "core\hle\XAPI\Xapi.h" // For EMUPATCH
|
||||
|
||||
namespace xbox {
|
||||
|
||||
DWORD WINAPI EMUPATCH(JVS_SendCommand)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD Command,
|
||||
DWORD a3,
|
||||
DWORD Length,
|
||||
DWORD a5,
|
||||
DWORD a6,
|
||||
DWORD a7,
|
||||
DWORD a8
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsBACKUP_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsBACKUP_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsEEPROM_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsEEPROM_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsNodeReceivePacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
PDWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsNodeSendPacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
typedef struct {
|
||||
UCHAR second;
|
||||
UCHAR minute;
|
||||
UCHAR hour;
|
||||
UCHAR unused_2;
|
||||
|
||||
UCHAR day;
|
||||
UCHAR month;
|
||||
UCHAR year;
|
||||
UCHAR unused_1;
|
||||
} JvsRTCTime;
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsRTC_Read)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime *time,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsRTC_Write)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime *time,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScReceiveMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScSendMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScReceiveRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScSendRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -29,6 +29,7 @@
|
|||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\D3D8\Direct3D9/Direct3D9.h"
|
||||
#include "core\hle\JVS\JVS.h"
|
||||
#include "core\hle\DSOUND\DirectSound\DirectSound.hpp"
|
||||
#include "Patches.hpp"
|
||||
#include "Intercept.hpp"
|
||||
|
@ -57,13 +58,16 @@ const uint32_t PATCH_IS_FIBER = 1 << 4;
|
|||
std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
||||
// Direct3D
|
||||
PATCH_ENTRY("CDevice_SetStateUP", xbox::EMUPATCH(CDevice_SetStateUP), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("CDevice_SetStateUP_4", xbox::EMUPATCH(CDevice_SetStateUP_4), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("CDevice_SetStateUP_0__LTCG_esi1", xbox::EMUPATCH(CDevice_SetStateUP_0__LTCG_esi1), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("CDevice_SetStateVB", xbox::EMUPATCH(CDevice_SetStateVB), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("CDevice_SetStateVB_8", xbox::EMUPATCH(CDevice_SetStateVB_8), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_Begin", xbox::EMUPATCH(D3DDevice_Begin), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BeginPush", xbox::EMUPATCH(D3DDevice_BeginPush), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BeginPush2", xbox::EMUPATCH(D3DDevice_BeginPush2), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BeginPush_4", xbox::EMUPATCH(D3DDevice_BeginPush_4), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BeginPush_8", xbox::EMUPATCH(D3DDevice_BeginPush_8), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BeginVisibilityTest", xbox::EMUPATCH(D3DDevice_BeginVisibilityTest), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BlockOnFence", xbox::EMUPATCH(D3DDevice_BlockOnFence), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BlockUntilVerticalBlank", xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank), PATCH_HLE_D3D),
|
||||
//PATCH_ENTRY("D3DDevice_BlockUntilVerticalBlank", xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_Clear", xbox::EMUPATCH(D3DDevice_Clear), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_CopyRects", xbox::EMUPATCH(D3DDevice_CopyRects), PATCH_HLE_D3D),
|
||||
// PATCH_ENTRY("D3DDevice_CreateVertexShader", xbox::EMUPATCH(D3DDevice_CreateVertexShader), PATCH_HLE_D3D),
|
||||
|
@ -74,9 +78,10 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
PATCH_ENTRY("D3DDevice_DrawRectPatch", xbox::EMUPATCH(D3DDevice_DrawRectPatch), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawTriPatch", xbox::EMUPATCH(D3DDevice_DrawTriPatch), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVertices", xbox::EMUPATCH(D3DDevice_DrawVertices), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVertices_4", xbox::EMUPATCH(D3DDevice_DrawVertices_4), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVertices_4__LTCG_ecx2_eax3", xbox::EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVertices_8__LTCG_eax3", xbox::EMUPATCH(D3DDevice_DrawVertices_8__LTCG_eax3), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVerticesUP", xbox::EMUPATCH(D3DDevice_DrawVerticesUP), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12__LTCG_ebx3", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_EnableOverlay", xbox::EMUPATCH(D3DDevice_EnableOverlay), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_End", xbox::EMUPATCH(D3DDevice_End), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_EndPush", xbox::EMUPATCH(D3DDevice_EndPush), PATCH_HLE_D3D),
|
||||
|
@ -118,12 +123,14 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
PATCH_ENTRY("D3DDevice_Present", xbox::EMUPATCH(D3DDevice_Present), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_PrimeVertexCache", xbox::EMUPATCH(D3DDevice_PrimeVertexCache), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_Reset", xbox::EMUPATCH(D3DDevice_Reset), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_Reset_0__LTCG_edi1", xbox::EMUPATCH(D3DDevice_Reset_0__LTCG_edi1), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_Reset_0__LTCG_ebx1", xbox::EMUPATCH(D3DDevice_Reset_0__LTCG_ebx1), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_RunPushBuffer", xbox::EMUPATCH(D3DDevice_RunPushBuffer), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_RunVertexStateShader", xbox::EMUPATCH(D3DDevice_RunVertexStateShader), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SelectVertexShader", xbox::EMUPATCH(D3DDevice_SelectVertexShader), PATCH_HLE_D3D),
|
||||
//PATCH_ENTRY("D3DDevice_SelectVertexShaderDirect", xbox::EMUPATCH(D3DDevice_SelectVertexShaderDirect), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SelectVertexShader_0", xbox::EMUPATCH(D3DDevice_SelectVertexShader_0), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SelectVertexShader_4", xbox::EMUPATCH(D3DDevice_SelectVertexShader_4), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2", xbox::EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SelectVertexShader_4__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetBackBufferScale", xbox::EMUPATCH(D3DDevice_SetBackBufferScale), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetDepthClipPlanes", xbox::EMUPATCH(D3DDevice_SetDepthClipPlanes), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetFlickerFilter", xbox::EMUPATCH(D3DDevice_SetFlickerFilter), PATCH_HLE_D3D),
|
||||
|
@ -138,7 +145,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
PATCH_ENTRY("D3DDevice_SetPalette_4", xbox::EMUPATCH(D3DDevice_SetPalette_4), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetPixelShader", xbox::EMUPATCH(D3DDevice_SetPixelShader), PATCH_HLE_D3D),
|
||||
//PATCH_ENTRY("D3DDevice_SetPixelShaderConstant_4", xbox::EMUPATCH(D3DDevice_SetPixelShaderConstant_4), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetPixelShader_0", xbox::EMUPATCH(D3DDevice_SetPixelShader_0), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetPixelShader_0__LTCG_eax_handle", xbox::EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetRenderState_Simple", xbox::EMUPATCH(D3DDevice_SetRenderState_Simple), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetRenderTarget", xbox::EMUPATCH(D3DDevice_SetRenderTarget), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetRenderTargetFast", xbox::EMUPATCH(D3DDevice_SetRenderTargetFast), PATCH_HLE_D3D),
|
||||
|
@ -156,7 +163,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
PATCH_ENTRY("D3DDevice_SetSwapCallback", xbox::EMUPATCH(D3DDevice_SetSwapCallback), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetTexture", xbox::EMUPATCH(D3DDevice_SetTexture), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax_pTexture", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetTexture_4", xbox::EMUPATCH(D3DDevice_SetTexture_4), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax_Stage", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetTransform", xbox::EMUPATCH(D3DDevice_SetTransform), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetTransform_0__LTCG_eax1_edx2", xbox::EMUPATCH(D3DDevice_SetTransform_0__LTCG_eax1_edx2), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetVertexData2f", xbox::EMUPATCH(D3DDevice_SetVertexData2f), PATCH_HLE_D3D),
|
||||
|
@ -177,7 +184,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant_8", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant_8), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetVertexShaderInput", xbox::EMUPATCH(D3DDevice_SetVertexShaderInput), PATCH_HLE_D3D),
|
||||
//PATCH_ENTRY("D3DDevice_SetVertexShaderInputDirect", xbox::EMUPATCH(D3DDevice_SetVertexShaderInputDirect), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetVerticalBlankCallback", xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback), PATCH_HLE_D3D),
|
||||
//PATCH_ENTRY("D3DDevice_SetVerticalBlankCallback", xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SetViewport", xbox::EMUPATCH(D3DDevice_SetViewport), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_Swap", xbox::EMUPATCH(D3DDevice_Swap), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_Swap_0", xbox::EMUPATCH(D3DDevice_Swap_0), PATCH_HLE_D3D),
|
||||
|
@ -369,6 +376,54 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
//PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XUnmountMU", xbox::EMUPATCH(XUnmountMU), PATCH_ALWAYS),
|
||||
|
||||
// JVS Functions
|
||||
PATCH_ENTRY("JVS_SendCommand", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JVS_SendCommand2", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JVS_SendCommand3", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Read", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Read2", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Read3", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Write", xbox::EMUPATCH(JvsBACKUP_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Write2", xbox::EMUPATCH(JvsBACKUP_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Read", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Read2", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Read3", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Write", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Write2", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Write3", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload2", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload3", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload4", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload2", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload3", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload4", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeReceivePacket", xbox::EMUPATCH(JvsNodeReceivePacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeReceivePacket2", xbox::EMUPATCH(JvsNodeReceivePacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeSendPacket", xbox::EMUPATCH(JvsNodeSendPacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeSendPacket2", xbox::EMUPATCH(JvsNodeSendPacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Read", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Read2", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Read3", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Write", xbox::EMUPATCH(JvsRTC_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Write2", xbox::EMUPATCH(JvsRTC_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload2", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload3", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload4", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareUpload", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareUpload2", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareUpload3", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveMidi", xbox::EMUPATCH(JvsScReceiveMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveMidi2", xbox::EMUPATCH(JvsScReceiveMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveRs323c", xbox::EMUPATCH(JvsScReceiveRs323c), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveRs323c2", xbox::EMUPATCH(JvsScReceiveRs323c), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendMidi", xbox::EMUPATCH(JvsScSendMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendMidi2", xbox::EMUPATCH(JvsScSendMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendRs323c", xbox::EMUPATCH(JvsScSendRs323c), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendRs323c2", xbox::EMUPATCH(JvsScSendRs323c), PATCH_ALWAYS),
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, subhook::Hook> g_FunctionHooks;
|
||||
|
|
|
@ -399,91 +399,34 @@ void DestructHleInputDevice(DeviceState *dev)
|
|||
|
||||
void SetupXboxDeviceTypes()
|
||||
{
|
||||
// If we don't yet have the offset to gDeviceType_Gamepad, work it out!
|
||||
if (g_DeviceType_Gamepad == nullptr) {
|
||||
// First, attempt to find GetTypeInformation
|
||||
auto typeInformation = g_SymbolAddresses.find("GetTypeInformation");
|
||||
if (typeInformation != g_SymbolAddresses.end() && typeInformation->second != xbox::zero) {
|
||||
EmuLog(LOG_LEVEL::INFO, "Deriving XDEVICE_TYPE_GAMEPAD from DeviceTable (via GetTypeInformation)");
|
||||
// Read the offset values of the device table structure from GetTypeInformation
|
||||
xbox::addr_xt deviceTableStartOffset = *(uint32_t*)((uint32_t)typeInformation->second + 0x01);
|
||||
xbox::addr_xt deviceTableEndOffset = *(uint32_t*)((uint32_t)typeInformation->second + 0x09);
|
||||
|
||||
// Calculate the number of device entires in the table
|
||||
size_t deviceTableEntryCount = (deviceTableEndOffset - deviceTableStartOffset) / sizeof(uint32_t);
|
||||
|
||||
EmuLog(LOG_LEVEL::INFO, "DeviceTableStart: 0x%08X", deviceTableStartOffset);
|
||||
EmuLog(LOG_LEVEL::INFO, "DeviceTableEnd: 0x%08X", deviceTableEndOffset);
|
||||
EmuLog(LOG_LEVEL::INFO, "DeviceTable Entires: %u", deviceTableEntryCount);
|
||||
|
||||
// Sanity check: Where all these device offsets within Xbox memory
|
||||
if ((deviceTableStartOffset >= g_SystemMaxMemory) || (deviceTableEndOffset >= g_SystemMaxMemory)) {
|
||||
CxbxrAbort("DeviceTable Location is outside of Xbox Memory range");
|
||||
}
|
||||
|
||||
// Iterate through the table until we find gamepad
|
||||
xbox::PXID_TYPE_INFORMATION* deviceTable = (xbox::PXID_TYPE_INFORMATION*)(deviceTableStartOffset);
|
||||
for (unsigned int i = 0; i < deviceTableEntryCount; i++) {
|
||||
// Skip empty table entries
|
||||
if (deviceTable[i] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::INFO, "----------------------------------------");
|
||||
EmuLog(LOG_LEVEL::INFO, "DeviceTable[%u]->ucType = %d", i, deviceTable[i]->ucType);
|
||||
|
||||
switch (deviceTable[i]->ucType) {
|
||||
case XINPUT_DEVTYPE_GAMEPAD:
|
||||
g_DeviceType_Gamepad = deviceTable[i]->XppType;
|
||||
EmuLog(LOG_LEVEL::INFO, "DeviceTable[%u]->XppType = 0x%08X (XDEVICE_TYPE_GAMEPAD)", i, (uintptr_t)g_DeviceType_Gamepad);
|
||||
break;
|
||||
|
||||
case XINPUT_DEVTYPE_STEELBATTALION:
|
||||
g_DeviceType_SBC = deviceTable[i]->XppType;
|
||||
EmuLog(LOG_LEVEL::INFO, "DeviceTable[%u]->XppType = 0x%08X (XDEVICE_TYPE_STEELBATTALION)", i, (uintptr_t)g_DeviceType_SBC);
|
||||
break;
|
||||
|
||||
default:
|
||||
EmuLog(LOG_LEVEL::WARNING, "DeviceTable[%u]->XppType = 0x%08X (Unknown device type)", i, (uintptr_t)deviceTable[i]->XppType);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// XDKs without GetTypeInformation have the GamePad address hardcoded in XInputOpen
|
||||
// Only the earliest XDKs use this code path, and the offset never changed between them
|
||||
// so this works well for us.
|
||||
void* XInputOpenAddr = (void*)g_SymbolAddresses["XInputOpen"];
|
||||
if (XInputOpenAddr != nullptr) {
|
||||
EmuLog(LOG_LEVEL::INFO, "Deriving XDEVICE_TYPE_GAMEPAD from XInputOpen (0x%08X)", (uintptr_t)XInputOpenAddr);
|
||||
g_DeviceType_Gamepad = *(xbox::PXPP_DEVICE_TYPE*)((uint32_t)XInputOpenAddr + 0x0B);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_DeviceType_Gamepad == nullptr) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "XDEVICE_TYPE_GAMEPAD was not found");
|
||||
return;
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_GAMEPAD found at 0x%08X", (uintptr_t)g_DeviceType_Gamepad);
|
||||
// Get address to xpp type's devices
|
||||
if (xbox::addr_xt gamepad_xpp_type = g_SymbolAddresses["g_DeviceType_Gamepad"]) {
|
||||
g_DeviceType_Gamepad = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(gamepad_xpp_type);
|
||||
}
|
||||
#if 0 // Not implemented
|
||||
if (xbox::addr_xt ir_dongle_xpp_type = g_SymbolAddresses["g_DeviceType_IRDongle"]) {
|
||||
g_DeviceType_IRDongle = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(ir_dongle_xpp_type);
|
||||
}
|
||||
if (xbox::addr_xt keyboard_xpp_type = g_SymbolAddresses["g_DeviceType_Keyboard"]) {
|
||||
g_DeviceType_Keyboard = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(keyboard_xpp_type);
|
||||
}
|
||||
if (xbox::addr_xt mouse_xpp_type = g_SymbolAddresses["g_DeviceType_Mouse"]) {
|
||||
g_DeviceType_Mouse = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(mouse_xpp_type);
|
||||
}
|
||||
#endif
|
||||
if (xbox::addr_xt sbc_xpp_type = g_SymbolAddresses["g_DeviceType_SBC"]) {
|
||||
g_DeviceType_SBC = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(sbc_xpp_type);
|
||||
}
|
||||
|
||||
if (xbox::addr_xt mu_xpp_type = g_SymbolAddresses["g_DeviceType_MU"]) {
|
||||
g_DeviceType_MU = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(mu_xpp_type);
|
||||
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT found at 0x%08X", reinterpret_cast<uintptr_t>(g_DeviceType_MU));
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT was not found by XbSymbolDatabase");
|
||||
}
|
||||
|
||||
// Get additional variables relative to Memory Unit
|
||||
if (xbox::addr_xt xapi_mounted_mu = g_SymbolAddresses["g_XapiMountedMUs"]) {
|
||||
g_XapiMountedMUs = reinterpret_cast<xbox::ulong_xt *>(xapi_mounted_mu);
|
||||
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs found at 0x%08X", reinterpret_cast<uintptr_t>(g_XapiMountedMUs));
|
||||
|
||||
g_XapiAltLett_MU = reinterpret_cast<xbox::char_xt *>(g_XapiMountedMUs - 1);
|
||||
EmuLog(LOG_LEVEL::INFO, "XapiAltLett_MU found at 0x%08X", reinterpret_cast<uintptr_t>(g_XapiAltLett_MU));
|
||||
g_XapiMountedMUs = reinterpret_cast<xbox::ulong_xt*>(xapi_mounted_mu);
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs was not found by XbSymbolDatabase");
|
||||
if (xbox::addr_xt xapi_alt_lett_mu = g_SymbolAddresses["g_XapiAltLett_MU"]) {
|
||||
g_XapiAltLett_MU = reinterpret_cast<xbox::char_xt *>(xapi_alt_lett_mu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1003,23 +946,55 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
|
|||
|
||||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
LARGE_INTEGER NewTime;
|
||||
PLARGE_INTEGER Timeout;
|
||||
if (dwMilliseconds == INFINITE) {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
Timeout = nullptr;
|
||||
}
|
||||
else if (dwMilliseconds == 0) {
|
||||
Timeout = &NewTime;
|
||||
NewTime.QuadPart = 0;
|
||||
}
|
||||
else {
|
||||
Timeout = &NewTime;
|
||||
NewTime.QuadPart = xbox::KeQueryInterruptTime();
|
||||
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT);
|
||||
}
|
||||
|
||||
xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
|
||||
PKTHREAD kThread = KeGetCurrentThread();
|
||||
kThread->WaitStatus = X_STATUS_SUCCESS;
|
||||
if (!AddWaitObject(kThread, Timeout)) {
|
||||
RETURN(WAIT_TIMEOUT);
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt status = WaitApc<true>([hObjectToSignal, hObjectToWaitOn, bAlertable](xbox::PKTHREAD kThread) -> std::optional<DWORD> {
|
||||
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
|
||||
if (dwRet == WAIT_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<DWORD>(dwRet);
|
||||
}, &NewTime, bAlertable, UserMode);
|
||||
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
|
||||
// to the thread
|
||||
xbox::ntstatus_xt Status;
|
||||
switch (dwRet)
|
||||
{
|
||||
case WAIT_ABANDONED: Status = X_STATUS_ABANDONED; break;
|
||||
case WAIT_IO_COMPLETION: Status = X_STATUS_USER_APC; break;
|
||||
case WAIT_OBJECT_0: Status = X_STATUS_SUCCESS; break;
|
||||
default: Status = X_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
|
||||
return std::make_optional<ntstatus_xt>(kThread->WaitStatus);
|
||||
}, Timeout, bAlertable, UserMode, kThread);
|
||||
|
||||
RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
|
||||
xbox::dword_xt ret;
|
||||
switch (status)
|
||||
{
|
||||
case X_STATUS_ABANDONED: ret = WAIT_ABANDONED; break;
|
||||
case X_STATUS_USER_APC: ret = WAIT_IO_COMPLETION; break;
|
||||
case X_STATUS_SUCCESS: ret = WAIT_OBJECT_0; break;
|
||||
case X_STATUS_TIMEOUT: ret = WAIT_TIMEOUT; break;
|
||||
default: ret = WAIT_FAILED;
|
||||
}
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -605,6 +605,9 @@ XBSYSAPI EXPORTNUM(352) void_xt NTAPI RtlRip
|
|||
PCHAR Message
|
||||
);
|
||||
|
||||
void_xt RtlInitSystem();
|
||||
extern RTL_CRITICAL_SECTION NtSystemTimeCritSec;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -98,6 +98,8 @@ typedef void* LPSECURITY_ATTRIBUTES;
|
|||
#define X_STATUS_FILE_IS_A_DIRECTORY 0xC00000BAL
|
||||
#define X_STATUS_END_OF_FILE 0xC0000011L
|
||||
#define X_STATUS_INVALID_PAGE_PROTECTION 0xC0000045L
|
||||
#define X_STATUS_SUSPEND_COUNT_EXCEEDED 0xC000004AL
|
||||
#define X_STATUS_THREAD_IS_TERMINATING 0xC000004BL
|
||||
#define X_STATUS_CONFLICTING_ADDRESSES 0xC0000018L
|
||||
#define X_STATUS_UNABLE_TO_FREE_VM 0xC000001AL
|
||||
#define X_STATUS_FREE_VM_NOT_AT_BASE 0xC000009FL
|
||||
|
@ -266,7 +268,7 @@ typedef struct _UNICODE_STRING
|
|||
{
|
||||
ushort_xt Length;
|
||||
ushort_xt MaximumLength;
|
||||
ushort_xt *Buffer;
|
||||
wchar_xt *Buffer;
|
||||
}
|
||||
UNICODE_STRING, *PUNICODE_STRING;
|
||||
|
||||
|
@ -1945,7 +1947,7 @@ typedef struct _KTHREAD
|
|||
/* 0x56/86 */ char_xt WaitNext;
|
||||
/* 0x57/87 */ char_xt WaitReason;
|
||||
/* 0x58/88 */ PKWAIT_BLOCK WaitBlockList;
|
||||
/* 0x5C/92 */ LIST_ENTRY WaitListEntry;
|
||||
/* 0x5C/92 */ LIST_ENTRY WaitListEntry; // Used to place the thread in the ready list of the scheduler
|
||||
/* 0x64/100 */ ulong_xt WaitTime;
|
||||
/* 0x68/104 */ ulong_xt KernelApcDisable;
|
||||
/* 0x6C/108 */ ulong_xt Quantum;
|
||||
|
@ -1969,6 +1971,8 @@ typedef struct _KTHREAD
|
|||
}
|
||||
KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD;
|
||||
|
||||
#define X_MAXIMUM_SUSPEND_COUNT 0x7F
|
||||
|
||||
// ******************************************************************
|
||||
// * ETHREAD
|
||||
// ******************************************************************
|
||||
|
@ -2081,6 +2085,11 @@ typedef enum _XC_VALUE_INDEX
|
|||
}
|
||||
XC_VALUE_INDEX, *PXC_VALUE_INDEX;
|
||||
|
||||
#define XBOX_HW_FLAG_INTERNAL_USB_HUB 0x00000001
|
||||
#define XBOX_HW_FLAG_DEVKIT_KERNEL 0x00000002
|
||||
#define XBOX_480P_MACROVISION_ENABLED 0x00000004
|
||||
#define XBOX_HW_FLAG_ARCADE 0x00000008
|
||||
|
||||
// ******************************************************************
|
||||
// * XBOX_HARDWARE_INFO
|
||||
// ******************************************************************
|
||||
|
|
|
@ -85,6 +85,8 @@ void InsertTailList(xbox::PLIST_ENTRY pListHead, xbox::PLIST_ENTRY pEntry)
|
|||
//#define RemoveEntryList(e) do { PLIST_ENTRY f = (e)->Flink, b = (e)->Blink; f->Blink = b; b->Flink = f; (e)->Flink = (e)->Blink = NULL; } while (0)
|
||||
|
||||
// Returns TRUE if the list has become empty after removing the element, FALSE otherwise.
|
||||
// NOTE: this function is a mess. _EX_Flink and _EX_Flink should never be nullptr, and it should never be called on a detached element either. Try to fix
|
||||
// the bugs in the caller instead of trying to handle it here with these hacks
|
||||
xbox::boolean_xt RemoveEntryList(xbox::PLIST_ENTRY pEntry)
|
||||
{
|
||||
xbox::PLIST_ENTRY _EX_Flink = pEntry->Flink;
|
||||
|
@ -140,8 +142,6 @@ void RestoreInterruptMode(bool value)
|
|||
g_bInterruptsEnabled = value;
|
||||
}
|
||||
|
||||
extern void ExecuteDpcQueue();
|
||||
|
||||
void KiUnexpectedInterrupt()
|
||||
{
|
||||
xbox::KeBugCheck(TRAP_CAUSE_UNKNOWN); // see
|
||||
|
@ -158,7 +158,10 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
|
|||
xbox::KiExecuteKernelApc();
|
||||
break;
|
||||
case DISPATCH_LEVEL: // = 2
|
||||
ExecuteDpcQueue();
|
||||
// This can be recursively called by KiUnlockDispatcherDatabase and KfLowerIrql, so avoid calling DPCs again if the current one has queued yet another one
|
||||
if (!IsDpcActive()) { // Avoid KeIsExecutingDpc(), as that logs
|
||||
ExecuteDpcQueue();
|
||||
}
|
||||
break;
|
||||
case APC_LEVEL | DISPATCH_LEVEL: // = 3
|
||||
KiUnexpectedInterrupt();
|
||||
|
@ -175,6 +178,34 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
|
|||
HalInterruptRequestRegister ^= (1 << SoftwareIrql);
|
||||
}
|
||||
|
||||
bool AddWaitObject(xbox::PKTHREAD kThread, xbox::PLARGE_INTEGER Timeout)
|
||||
{
|
||||
// Use the built-in ktimer as a dummy wait object, so that KiUnwaitThreadAndLock can still work
|
||||
xbox::KiTimerLock();
|
||||
xbox::PKWAIT_BLOCK WaitBlock = &kThread->TimerWaitBlock;
|
||||
kThread->WaitBlockList = WaitBlock;
|
||||
xbox::PKTIMER Timer = &kThread->Timer;
|
||||
WaitBlock->NextWaitBlock = WaitBlock;
|
||||
Timer->Header.WaitListHead.Flink = &WaitBlock->WaitListEntry;
|
||||
Timer->Header.WaitListHead.Blink = &WaitBlock->WaitListEntry;
|
||||
if (Timeout && Timeout->QuadPart) {
|
||||
// Setup a timer so that KiTimerExpiration can discover the timeout and yield to us.
|
||||
// Otherwise, we will only be able to discover the timeout when Windows decides to schedule us again, and testing shows that
|
||||
// tends to happen much later than the due time
|
||||
if (xbox::KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
||||
// Sanity check: set WaitBlockList to nullptr so that we can catch the case where a waiter starts a new wait but forgets to setup a new wait block. This
|
||||
// way, we will crash instead of silently using the pointer to the old block
|
||||
kThread->WaitBlockList = xbox::zeroptr;
|
||||
xbox::KiTimerUnlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
kThread->State = xbox::Waiting;
|
||||
xbox::KiTimerUnlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
// This masks have been verified to be correct against a kernel dump
|
||||
const DWORD IrqlMasks[] = {
|
||||
0xFFFFFFFE, // IRQL 0
|
||||
0xFFFFFFFC, // IRQL 1 (APC_LEVEL)
|
||||
|
@ -183,18 +214,18 @@ const DWORD IrqlMasks[] = {
|
|||
0x03FFFFF0, // IRQL 4
|
||||
0x01FFFFF0, // IRQL 5
|
||||
0x00FFFFF0, // IRQL 6
|
||||
0x00EFFFF0, // IRQL 7
|
||||
0x007FFFF0, // IRQL 8
|
||||
0x003FFFF0, // IRQL 9
|
||||
0x001FFFF0, // IRQL 10
|
||||
0x000EFFF0, // IRQL 11
|
||||
0x0007FFF0, // IRQL 12
|
||||
0x0003FFF0, // IRQL 13
|
||||
0x0001FFF0, // IRQL 14
|
||||
0x007FFFF0, // IRQL 7
|
||||
0x003FFFF0, // IRQL 8
|
||||
0x001FFFF0, // IRQL 9
|
||||
0x000FFFF0, // IRQL 10
|
||||
0x0007FFF0, // IRQL 11
|
||||
0x0003FFF0, // IRQL 12
|
||||
0x0001FFF0, // IRQL 13 (same as IRQL 14)
|
||||
0x0001FFF0, // IRQL 14 (same as IRQL 13)
|
||||
0x00017FF0, // IRQL 15
|
||||
0x00013FF0, // IRQL 16
|
||||
0x00011FF0, // IRQL 17
|
||||
0x00011FF0, // IRQL 18
|
||||
0x00011FF0, // IRQL 17 (same as IRQL 18)
|
||||
0x00011FF0, // IRQL 18 (same as IRQL 17)
|
||||
0x000117F0, // IRQL 19
|
||||
0x000113F0, // IRQL 20
|
||||
0x000111F0, // IRQL 21
|
||||
|
@ -449,7 +480,8 @@ XBSYSAPI EXPORTNUM(163) xbox::void_xt FASTCALL xbox::KiUnlockDispatcherDatabase
|
|||
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, OldIrql);
|
||||
|
||||
// Wrong, this should only happen when OldIrql >= DISPATCH_LEVEL
|
||||
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) { // Avoid KeIsExecutingDpc(), as that logs
|
||||
// Checking DpcRoutineActive doesn't work because our Prcb is per-thread instead of being per-processor
|
||||
if (!IsDpcActive()) { // Avoid KeIsExecutingDpc(), as that logs
|
||||
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,8 +52,8 @@ xbox::PLIST_ENTRY RemoveTailList(xbox::PLIST_ENTRY pListHead);
|
|||
|
||||
extern xbox::LAUNCH_DATA_PAGE DefaultLaunchDataPage;
|
||||
extern xbox::PKINTERRUPT EmuInterruptList[MAX_BUS_INTERRUPT_LEVEL + 1];
|
||||
inline std::condition_variable g_InterruptSignal;
|
||||
inline std::atomic_bool g_AnyInterruptAsserted = false;
|
||||
// Indicates to disable/enable all interrupts when cli and sti instructions are executed
|
||||
inline std::atomic_bool g_bEnableAllInterrupts = true;
|
||||
|
||||
class HalSystemInterrupt {
|
||||
public:
|
||||
|
@ -64,8 +64,6 @@ public:
|
|||
}
|
||||
|
||||
m_Asserted = state;
|
||||
g_AnyInterruptAsserted = true;
|
||||
g_InterruptSignal.notify_one();
|
||||
};
|
||||
|
||||
void Enable() {
|
||||
|
@ -110,45 +108,87 @@ extern HalSystemInterrupt HalSystemInterrupts[MAX_BUS_INTERRUPT_LEVEL + 1];
|
|||
bool DisableInterrupts();
|
||||
void RestoreInterruptMode(bool value);
|
||||
void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql);
|
||||
bool AddWaitObject(xbox::PKTHREAD kThread, xbox::PLARGE_INTEGER Timeout);
|
||||
|
||||
template<typename T>
|
||||
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER AbsoluteExpireTime, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
std::optional<xbox::ntstatus_xt> SatisfyWait(T &&Lambda, xbox::PKTHREAD kThread, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
{
|
||||
// NOTE: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
|
||||
// also interrupt the wait
|
||||
|
||||
xbox::ulonglong_xt now = xbox::KeQueryInterruptTime();
|
||||
xbox::PETHREAD eThread = reinterpret_cast<xbox::PETHREAD>(EmuKeGetPcr()->Prcb->CurrentThread);
|
||||
|
||||
if (AbsoluteExpireTime->QuadPart == 0) {
|
||||
// This will only happen when the title specifies a zero timeout
|
||||
AbsoluteExpireTime->QuadPart = now;
|
||||
if (const auto ret = Lambda(kThread)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (now <= static_cast<xbox::ulonglong_xt>(AbsoluteExpireTime->QuadPart)) {
|
||||
if (const auto ret = Lambda()) {
|
||||
return *ret;
|
||||
}
|
||||
xbox::KiApcListMtx.lock();
|
||||
bool EmptyKernel = IsListEmpty(&kThread->ApcState.ApcListHead[xbox::KernelMode]);
|
||||
bool EmptyUser = IsListEmpty(&kThread->ApcState.ApcListHead[xbox::UserMode]);
|
||||
xbox::KiApcListMtx.unlock();
|
||||
|
||||
xbox::KiApcListMtx.lock();
|
||||
bool EmptyKernel = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::KernelMode]);
|
||||
bool EmptyUser = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::UserMode]);
|
||||
xbox::KiApcListMtx.unlock();
|
||||
if (EmptyKernel == false) {
|
||||
xbox::KiExecuteKernelApc();
|
||||
}
|
||||
if ((EmptyUser == false) &&
|
||||
(Alertable == TRUE) &&
|
||||
(WaitMode == xbox::UserMode)) {
|
||||
xbox::KiExecuteUserApc();
|
||||
return X_STATUS_USER_APC;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
now = xbox::KeQueryInterruptTime();
|
||||
if (EmptyKernel == false) {
|
||||
xbox::KiExecuteKernelApc();
|
||||
}
|
||||
|
||||
return X_STATUS_TIMEOUT;
|
||||
if ((EmptyUser == false) &&
|
||||
(Alertable == TRUE) &&
|
||||
(WaitMode == xbox::UserMode)) {
|
||||
xbox::KiExecuteUserApc();
|
||||
xbox::KiUnwaitThreadAndLock(kThread, X_STATUS_USER_APC, 0);
|
||||
return kThread->WaitStatus;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<bool host_wait, typename T>
|
||||
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER Timeout, xbox::boolean_xt Alertable, xbox::char_xt WaitMode, xbox::PKTHREAD kThread)
|
||||
{
|
||||
// NOTE1: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
|
||||
// also interrupt the wait.
|
||||
|
||||
xbox::ntstatus_xt status;
|
||||
if (Timeout == nullptr) {
|
||||
// No timout specified, so this is an infinite wait until an alert, a user apc or the object(s) become(s) signalled
|
||||
while (true) {
|
||||
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
|
||||
status = *ret;
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
else if (Timeout->QuadPart == 0) {
|
||||
assert(host_wait);
|
||||
// A zero timeout means that we only have to check the conditions once and then return immediately if they are not satisfied
|
||||
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
|
||||
status = *ret;
|
||||
}
|
||||
else {
|
||||
// If the wait failed, then always remove the wait block. Note that this can only happen with host waits, since guest waits never call us at all
|
||||
// when Timeout->QuadPart == 0. Test case: Halo 2 (sporadically when playing the intro video)
|
||||
xbox::KiUnwaitThreadAndLock(kThread, X_STATUS_TIMEOUT, 0);
|
||||
status = kThread->WaitStatus;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A non-zero timeout means we have to check the conditions until we reach the requested time
|
||||
while (true) {
|
||||
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
|
||||
status = *ret;
|
||||
break;
|
||||
}
|
||||
|
||||
if (host_wait && (kThread->State == xbox::Ready)) {
|
||||
status = kThread->WaitStatus;
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (host_wait) {
|
||||
kThread->State = xbox::Running;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include "common\EmuEEPROM.h" // For EEPROM
|
||||
#include "devices\Xbox.h" // For g_SMBus, SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER
|
||||
#include "devices\SMCDevice.h" // For SMC_COMMAND_SCRATCH
|
||||
#include "common/util/strConverter.hpp" // for utf16_to_ascii
|
||||
#include "core\kernel\memory-manager\VMManager.h"
|
||||
|
||||
#include <algorithm> // for std::replace
|
||||
|
@ -583,6 +582,7 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi
|
|||
case ReturnFirmwareFatal:
|
||||
{
|
||||
xbox::HalWriteSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_SCRATCH, 0, SMC_SCRATCH_DISPLAY_FATAL_ERROR);
|
||||
is_reboot = true;
|
||||
|
||||
g_VMManager.SavePersistentMemory();
|
||||
|
||||
|
|
|
@ -248,6 +248,14 @@ XBSYSAPI EXPORTNUM(66) xbox::ntstatus_xt NTAPI xbox::IoCreateFile
|
|||
LOG_FUNC_ARG(Options)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// If we are emulating the Chihiro, we need to hook mbcom, so return an easily identifable handle
|
||||
if (g_bIsChihiro) {
|
||||
if (strncmp(ObjectAttributes->ObjectName->Buffer, DriveMbcom.c_str(), DriveMbcom.length()) == 0) {
|
||||
*FileHandle = CHIHIRO_MBCOM_HANDLE;
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
NativeObjectAttributes nativeObjectAttributes;
|
||||
|
||||
// If we are NOT accessing a directory, and we match a partition path, we need to redirect to a partition.bin file
|
||||
|
@ -273,6 +281,17 @@ XBSYSAPI EXPORTNUM(66) xbox::ntstatus_xt NTAPI xbox::IoCreateFile
|
|||
// Force ShareAccess to all
|
||||
ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
|
||||
// Force set DELETE permission flag if write attributes flag is set.
|
||||
// Testcase: dashupdate.xbe (4928, untested with other versions but newer dashupdate did not call for deletion on fail).
|
||||
if (DesiredAccess & FILE_WRITE_ATTRIBUTES) {
|
||||
DesiredAccess |= DELETE;
|
||||
}
|
||||
|
||||
// Force sanitize before call to NtDll::NtCreateFile
|
||||
// Testcase:
|
||||
// * Exhibition Demo discs - Attempt to create music folder fail internally which then show unable to copy soundtrack dialog.
|
||||
FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS;
|
||||
|
||||
if (SUCCEEDED(ret))
|
||||
{
|
||||
// redirect to NtCreateFile
|
||||
|
|
|
@ -84,6 +84,9 @@ namespace NtDll
|
|||
#include "Timer.h"
|
||||
#include "Util.h"
|
||||
|
||||
#pragma warning(disable:4005) // Ignore redefined status values
|
||||
#include <ntstatus.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
|
@ -93,11 +96,13 @@ namespace NtDll
|
|||
// TODO : Move towards thread-simulation based Dpc emulation
|
||||
typedef struct _DpcData {
|
||||
CRITICAL_SECTION Lock;
|
||||
HANDLE DpcEvent;
|
||||
std::atomic_flag IsDpcActive;
|
||||
std::atomic_flag IsDpcPending;
|
||||
xbox::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead
|
||||
} DpcData;
|
||||
|
||||
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcThread()
|
||||
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcData()
|
||||
std::atomic_flag xbox::KeSystemTimeChanged;
|
||||
|
||||
xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
|
||||
{
|
||||
|
@ -127,6 +132,33 @@ xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
|
|||
break; \
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KeResumeThreadEx
|
||||
(
|
||||
IN PKTHREAD Thread
|
||||
)
|
||||
{
|
||||
// This is only to be used to synchronize new thread creation with the thread that spawned it
|
||||
|
||||
Thread->SuspendSemaphore.Header.SignalState = 1;
|
||||
KiWaitListLock();
|
||||
KiWaitTest(&Thread->SuspendSemaphore, 0);
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KeSuspendThreadEx
|
||||
(
|
||||
IN PKTHREAD Thread
|
||||
)
|
||||
{
|
||||
// This is only to be used to synchronize new thread creation with the thread that spawned it
|
||||
|
||||
Thread->SuspendSemaphore.Header.SignalState = 0;
|
||||
KiInsertQueueApc(&Thread->SuspendApc, 0);
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KeWaitForDpc()
|
||||
{
|
||||
g_DpcData.IsDpcPending.wait(false);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * EmuKeGetPcr()
|
||||
|
@ -163,7 +195,7 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
|
|||
)
|
||||
{
|
||||
KIRQL OldIrql, OldIrql2;
|
||||
LARGE_INTEGER DeltaTime, HostTime;
|
||||
LARGE_INTEGER DeltaTime;
|
||||
PLIST_ENTRY ListHead, NextEntry;
|
||||
PKTIMER Timer;
|
||||
LIST_ENTRY TempList, TempList2;
|
||||
|
@ -181,10 +213,6 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
|
|||
/* Query the system time now */
|
||||
KeQuerySystemTime(OldTime);
|
||||
|
||||
/* Surely, we won't set the system time here, but we CAN remember a delta to the host system time */
|
||||
HostTime.QuadPart = OldTime->QuadPart - HostSystemTimeDelta.load();
|
||||
HostSystemTimeDelta = NewTime->QuadPart - HostTime.QuadPart;
|
||||
|
||||
/* Calculate the difference between the new and the old time */
|
||||
DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart;
|
||||
|
||||
|
@ -243,7 +271,7 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
|
|||
}
|
||||
}
|
||||
|
||||
/* Process expired timers. This releases the dispatcher and timer locks */
|
||||
/* Process expired timers. This releases the dispatcher and timer locks, then it yields */
|
||||
KiTimerListExpire(&TempList2, OldIrql);
|
||||
}
|
||||
|
||||
|
@ -457,8 +485,11 @@ void ExecuteDpcQueue()
|
|||
// Mark it as no longer linked into the DpcQueue
|
||||
pkdpc->Inserted = FALSE;
|
||||
// Set DpcRoutineActive to support KeIsExecutingDpc:
|
||||
g_DpcData.IsDpcActive.test_and_set();
|
||||
KeGetCurrentPrcb()->DpcRoutineActive = TRUE; // Experimental
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC at 0x%.8X", pkdpc->DeferredRoutine);
|
||||
LeaveCriticalSection(&(g_DpcData.Lock));
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC object 0x%.8X at 0x%.8X", pkdpc, pkdpc->DeferredRoutine);
|
||||
|
||||
// Call the Deferred Procedure :
|
||||
pkdpc->DeferredRoutine(
|
||||
|
@ -467,23 +498,30 @@ void ExecuteDpcQueue()
|
|||
pkdpc->SystemArgument1,
|
||||
pkdpc->SystemArgument2);
|
||||
|
||||
EnterCriticalSection(&(g_DpcData.Lock));
|
||||
KeGetCurrentPrcb()->DpcRoutineActive = FALSE; // Experimental
|
||||
g_DpcData.IsDpcActive.clear();
|
||||
}
|
||||
|
||||
g_DpcData.IsDpcPending.clear();
|
||||
|
||||
// Assert(g_DpcData._dwThreadId == GetCurrentThreadId());
|
||||
// Assert(g_DpcData._dwDpcThreadId == g_DpcData._dwThreadId);
|
||||
// g_DpcData._dwDpcThreadId = 0;
|
||||
LeaveCriticalSection(&(g_DpcData.Lock));
|
||||
}
|
||||
|
||||
void InitDpcThread()
|
||||
void InitDpcData()
|
||||
{
|
||||
DWORD dwThreadId = 0;
|
||||
|
||||
// Let's initialize the Dpc handling thread too,
|
||||
// here for now (should be called by our caller)
|
||||
InitializeCriticalSection(&(g_DpcData.Lock));
|
||||
InitializeListHead(&(g_DpcData.DpcQueue));
|
||||
EmuLogEx(CXBXR_MODULE::INIT, LOG_LEVEL::DEBUG, "Creating DPC event\n");
|
||||
g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr);
|
||||
}
|
||||
|
||||
bool IsDpcActive()
|
||||
{
|
||||
return g_DpcData.IsDpcActive.test();
|
||||
}
|
||||
|
||||
static constexpr uint32_t XBOX_TSC_FREQUENCY = 733333333; // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock)
|
||||
|
@ -495,13 +533,6 @@ ULONGLONG CxbxGetPerformanceCounter(bool acpi)
|
|||
return Timer_GetScaledPerformanceCounter(period);
|
||||
}
|
||||
|
||||
void CxbxInitPerformanceCounters()
|
||||
{
|
||||
// Let's initialize the Dpc handling thread too,
|
||||
// here for now (should be called by our caller)
|
||||
InitDpcThread();
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x005C - KeAlertResumeThread()
|
||||
// ******************************************************************
|
||||
|
@ -711,27 +742,31 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
|
|||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
// We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well
|
||||
// Test case: Metal Slug 3
|
||||
LARGE_INTEGER NewTime;
|
||||
PLARGE_INTEGER pNewTime;
|
||||
if (Interval) {
|
||||
LARGE_INTEGER ExpireTime, DueTime;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = Interval->QuadPart;
|
||||
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
pNewTime = &NewTime;
|
||||
|
||||
PKTHREAD kThread = KeGetCurrentThread();
|
||||
kThread->WaitStatus = X_STATUS_SUCCESS;
|
||||
if (!AddWaitObject(kThread, Interval)) {
|
||||
RETURN(X_STATUS_TIMEOUT);
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
|
||||
xbox::ntstatus_xt ret = WaitApc<true>([Alertable](xbox::PKTHREAD kThread) -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime);
|
||||
if (Status == 0) { // STATUS_SUCCESS
|
||||
// Any success codes that are not related to alerting should be masked out
|
||||
if (Status >= 0 && Status != STATUS_ALERTED && Status != STATUS_USER_APC) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, pNewTime, Alertable, WaitMode);
|
||||
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
|
||||
// to the thread. Test case: Steel Battalion
|
||||
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
|
||||
return std::make_optional<ntstatus_xt>(kThread->WaitStatus);
|
||||
}, Interval, Alertable, WaitMode, kThread);
|
||||
|
||||
if (ret == X_STATUS_TIMEOUT) {
|
||||
// NOTE: this function considers a timeout a success
|
||||
ret = X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
@ -1208,35 +1243,9 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
|
|||
Apc->SystemArgument1 = SystemArgument1;
|
||||
Apc->SystemArgument2 = SystemArgument2;
|
||||
|
||||
if (Apc->Inserted) {
|
||||
KfLowerIrql(OldIrql);
|
||||
RETURN(FALSE);
|
||||
}
|
||||
else {
|
||||
KiApcListMtx.lock();
|
||||
InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry);
|
||||
Apc->Inserted = TRUE;
|
||||
KiApcListMtx.unlock();
|
||||
|
||||
// We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD
|
||||
// in the fs selector will not be correct
|
||||
if (kThread == KeGetCurrentThread()) {
|
||||
if (Apc->ApcMode == KernelMode) { // kernel apc
|
||||
// NOTE: this is wrong, we should check the thread state instead of just signaling the kernel apc, but we currently
|
||||
// don't set the appropriate state in kthread
|
||||
kThread->ApcState.KernelApcPending = TRUE;
|
||||
KiExecuteKernelApc();
|
||||
}
|
||||
else if ((kThread->WaitMode == UserMode) && (kThread->Alertable)) { // user apc
|
||||
// NOTE: this should also check the thread state
|
||||
kThread->ApcState.UserApcPending = TRUE;
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
}
|
||||
|
||||
KfLowerIrql(OldIrql);
|
||||
RETURN(TRUE);
|
||||
}
|
||||
boolean_xt result = KiInsertQueueApc(Apc, Increment);
|
||||
KfLowerIrql(OldIrql);
|
||||
RETURN(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1268,16 +1277,25 @@ XBSYSAPI EXPORTNUM(119) xbox::boolean_xt NTAPI xbox::KeInsertQueueDpc
|
|||
Dpc->SystemArgument1 = SystemArgument1;
|
||||
Dpc->SystemArgument2 = SystemArgument2;
|
||||
InsertTailList(&(g_DpcData.DpcQueue), &(Dpc->DpcListEntry));
|
||||
LeaveCriticalSection(&(g_DpcData.Lock));
|
||||
g_DpcData.IsDpcPending.test_and_set();
|
||||
g_DpcData.IsDpcPending.notify_one();
|
||||
|
||||
// TODO : Instead of DpcQueue, add the DPC to KeGetCurrentPrcb()->DpcListHead
|
||||
// Signal the Dpc handling code there's work to do
|
||||
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||||
if (!IsDpcActive()) {
|
||||
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||||
}
|
||||
|
||||
// OpenXbox has this instead:
|
||||
// if (!pKPRCB->DpcRoutineActive && !pKPRCB->DpcInterruptRequested) {
|
||||
// pKPRCB->DpcInterruptRequested = TRUE;
|
||||
}
|
||||
else {
|
||||
LeaveCriticalSection(&(g_DpcData.Lock));
|
||||
}
|
||||
|
||||
// Thread-safety is no longer required anymore
|
||||
LeaveCriticalSection(&(g_DpcData.Lock));
|
||||
// TODO : Instead, enable interrupts - use KeLowerIrql(OldIrql) ?
|
||||
|
||||
RETURN(NeedsInsertion);
|
||||
|
@ -1291,7 +1309,12 @@ XBSYSAPI EXPORTNUM(121) xbox::boolean_xt NTAPI xbox::KeIsExecutingDpc
|
|||
{
|
||||
LOG_FUNC();
|
||||
|
||||
#if 0
|
||||
// This is the correct implementation, but it doesn't work because our Prcb is per-thread instead of being per-processor
|
||||
BOOLEAN ret = (BOOLEAN)KeGetCurrentPrcb()->DpcRoutineActive;
|
||||
#else
|
||||
BOOLEAN ret = (BOOLEAN)IsDpcActive();
|
||||
#endif
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
@ -1348,13 +1371,14 @@ XBSYSAPI EXPORTNUM(123) xbox::long_xt NTAPI xbox::KePulseEvent
|
|||
}
|
||||
|
||||
LONG OldState = Event->Header.SignalState;
|
||||
KiWaitListLock();
|
||||
if ((OldState == 0) && (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) {
|
||||
Event->Header.SignalState = 1;
|
||||
// TODO: KiWaitTest(Event, Increment);
|
||||
// For now, we just sleep to give other threads time to wake
|
||||
// KiWaitTest and related functions require correct thread scheduling to implement first
|
||||
// This will have to wait until CPU emulation at v1.0
|
||||
Sleep(1);
|
||||
KiWaitTest(Event, Increment);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
Event->Header.SignalState = 0;
|
||||
|
@ -1380,9 +1404,7 @@ XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread
|
|||
KIRQL OldIrql;
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
// It cannot fail because all thread handles are created by ob
|
||||
const auto& nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
||||
long_xt ret = GetThreadPriority(*nativeHandle);
|
||||
long_xt ret = Thread->Priority;
|
||||
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
|
||||
|
@ -1535,12 +1557,14 @@ XBSYSAPI EXPORTNUM(132) xbox::long_xt NTAPI xbox::KeReleaseSemaphore
|
|||
}
|
||||
Semaphore->Header.SignalState = adjusted_signalstate;
|
||||
|
||||
//TODO: Implement KiWaitTest
|
||||
#if 0
|
||||
KiWaitListLock();
|
||||
if ((initial_state == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) {
|
||||
KiWaitTest(&Semaphore->Header, Increment);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (Wait) {
|
||||
PKTHREAD current_thread = KeGetCurrentThread();
|
||||
|
@ -1754,11 +1778,29 @@ XBSYSAPI EXPORTNUM(140) xbox::ulong_xt NTAPI xbox::KeResumeThread
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(Thread);
|
||||
|
||||
NTSTATUS ret = X_STATUS_SUCCESS;
|
||||
KIRQL OldIrql;
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
char_xt OldCount = Thread->SuspendCount;
|
||||
if (OldCount != 0) {
|
||||
--Thread->SuspendCount;
|
||||
if (Thread->SuspendCount == 0) {
|
||||
#if 0
|
||||
++Thread->SuspendSemaphore.Header.SignalState;
|
||||
KiWaitListLock();
|
||||
KiWaitTest(&Thread->SuspendSemaphore, 0);
|
||||
std::this_thread::yield();
|
||||
#else
|
||||
if (const auto &nativeHandle = GetNativeHandle<true>(reinterpret_cast<PETHREAD>(Thread)->UniqueThread)) {
|
||||
ResumeThread(*nativeHandle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
|
||||
RETURN(OldCount);
|
||||
}
|
||||
|
||||
XBSYSAPI EXPORTNUM(141) xbox::PLIST_ENTRY NTAPI xbox::KeRundownQueue
|
||||
|
@ -1800,25 +1842,15 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread
|
|||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_OUT(Thread)
|
||||
LOG_FUNC_ARG_OUT(Priority)
|
||||
LOG_FUNC_ARG(Thread)
|
||||
LOG_FUNC_ARG(Priority)
|
||||
LOG_FUNC_END;
|
||||
|
||||
KIRQL oldIRQL;
|
||||
KiLockDispatcherDatabase(&oldIRQL);
|
||||
|
||||
// It cannot fail because all thread handles are created by ob
|
||||
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
||||
LONG ret = GetThreadPriority(*nativeHandle);
|
||||
|
||||
// This would work normally, but it will slow down the emulation,
|
||||
// don't do that if the priority is higher then normal (so our own)!
|
||||
if (Priority <= THREAD_PRIORITY_NORMAL) {
|
||||
BOOL result = SetThreadPriority(*nativeHandle, Priority);
|
||||
if (!result) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority failed: %s", WinError2Str().c_str());
|
||||
}
|
||||
}
|
||||
Thread->Priority = Priority;
|
||||
long_xt ret = Thread->Priority;
|
||||
|
||||
KiUnlockDispatcherDatabase(oldIRQL);
|
||||
|
||||
|
@ -1839,17 +1871,9 @@ XBSYSAPI EXPORTNUM(144) xbox::boolean_xt NTAPI xbox::KeSetDisableBoostThread
|
|||
KIRQL oldIRQL;
|
||||
KiLockDispatcherDatabase(&oldIRQL);
|
||||
|
||||
// It cannot fail because all thread handles are created by ob
|
||||
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
||||
|
||||
boolean_xt prevDisableBoost = Thread->DisableBoost;
|
||||
Thread->DisableBoost = (CHAR)Disable;
|
||||
|
||||
BOOL bRet = SetThreadPriorityBoost(*nativeHandle, Disable);
|
||||
if (!bRet) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost failed: %s", WinError2Str().c_str());
|
||||
}
|
||||
|
||||
KiUnlockDispatcherDatabase(oldIRQL);
|
||||
|
||||
RETURN(prevDisableBoost);
|
||||
|
@ -1884,7 +1908,9 @@ XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
|
|||
}
|
||||
|
||||
LONG OldState = Event->Header.SignalState;
|
||||
KiWaitListLock();
|
||||
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
|
||||
KiWaitListUnlock();
|
||||
Event->Header.SignalState = 1;
|
||||
} else {
|
||||
PKWAIT_BLOCK WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, KWAIT_BLOCK, WaitListEntry);
|
||||
|
@ -1892,16 +1918,14 @@ XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
|
|||
(WaitBlock->WaitType != WaitAny)) {
|
||||
if (OldState == 0) {
|
||||
Event->Header.SignalState = 1;
|
||||
// TODO: KiWaitTest(Event, Increment);
|
||||
// For now, we just sleep to give other threads time to wake
|
||||
// See KePulseEvent
|
||||
Sleep(1);
|
||||
KiWaitTest(Event, Increment);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
} else {
|
||||
// TODO: KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
|
||||
// For now, we just sleep to give other threads time to wake
|
||||
// See KePulseEvent
|
||||
Sleep(1);
|
||||
KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1938,6 +1962,7 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
|
|||
return;
|
||||
}
|
||||
|
||||
KiWaitListLock();
|
||||
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
|
||||
Event->Header.SignalState = 1;
|
||||
} else {
|
||||
|
@ -1948,11 +1973,9 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
|
|||
}
|
||||
|
||||
WaitThread->Quantum = WaitThread->ApcState.Process->ThreadQuantum;
|
||||
// TODO: KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
|
||||
// For now, we just sleep to give other threads time to wake
|
||||
// See KePulseEvent
|
||||
Sleep(1);
|
||||
KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
|
||||
}
|
||||
KiWaitListUnlock();
|
||||
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
}
|
||||
|
@ -1984,8 +2007,8 @@ XBSYSAPI EXPORTNUM(148) xbox::boolean_xt NTAPI xbox::KeSetPriorityThread
|
|||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_OUT(Thread)
|
||||
LOG_FUNC_ARG_OUT(Priority)
|
||||
LOG_FUNC_ARG(Thread)
|
||||
LOG_FUNC_ARG(Priority)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
@ -2098,11 +2121,38 @@ XBSYSAPI EXPORTNUM(152) xbox::ulong_xt NTAPI xbox::KeSuspendThread
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(Thread);
|
||||
|
||||
NTSTATUS ret = X_STATUS_SUCCESS;
|
||||
KIRQL OldIrql;
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
char_xt OldCount = Thread->SuspendCount;
|
||||
if (OldCount == X_MAXIMUM_SUSPEND_COUNT) {
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
RETURN(X_STATUS_SUSPEND_COUNT_EXCEEDED);
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
if (Thread->ApcState.ApcQueueable == TRUE) {
|
||||
++Thread->SuspendCount;
|
||||
if (OldCount == 0) {
|
||||
#if 0
|
||||
if (KiInsertQueueApc(&Thread->SuspendApc, 0) == FALSE) {
|
||||
--Thread->SuspendSemaphore.Header.SignalState;
|
||||
}
|
||||
#else
|
||||
// JSRF creates a thread at 0x0013BC30 and then it attempts to continuously suspend/resume it. Unfortunately, this thread performs a never ending loop (and
|
||||
// terminates if it ever exit the loop), and never calls any kernel functions in the middle. This means that our suspend APC will never be executed and so
|
||||
// we cannot suspend such thread. Thus, we will always have to rely on the host to do the suspension, as long as we do direct execution. Note that this is
|
||||
// a general issue for all kernel APCs too.
|
||||
if (const auto &nativeHandle = GetNativeHandle<true>(reinterpret_cast<PETHREAD>(Thread)->UniqueThread)) {
|
||||
SuspendThread(*nativeHandle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
|
||||
RETURN(OldCount);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -2198,15 +2248,13 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
// Wait Loop
|
||||
// This loop ends
|
||||
PLARGE_INTEGER OriginalTime = Timeout;
|
||||
LARGE_INTEGER DueTime, NewTime;
|
||||
KWAIT_BLOCK StackWaitBlock;
|
||||
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
|
||||
PKWAIT_BLOCK WaitBlock;
|
||||
BOOLEAN WaitSatisfied;
|
||||
NTSTATUS WaitStatus;
|
||||
PKMUTANT ObjectMutant;
|
||||
// Hack variable (remove this when the thread scheduler is here)
|
||||
bool timeout_set = false;
|
||||
do {
|
||||
Thread->WaitBlockList = WaitBlockArray;
|
||||
|
||||
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase
|
||||
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
|
@ -2265,7 +2313,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
// Check if the wait can be satisfied immediately
|
||||
if ((WaitType == WaitAll) && (WaitSatisfied)) {
|
||||
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
|
||||
KiWaitSatisfyAll(WaitBlock);
|
||||
KiWaitSatisfyAllAndLock(WaitBlock);
|
||||
WaitStatus = (NTSTATUS)Thread->WaitStatus;
|
||||
goto NoWait;
|
||||
}
|
||||
|
@ -2280,36 +2328,20 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
goto NoWait;
|
||||
}
|
||||
|
||||
// Setup a timer for the thread but only once (for now)
|
||||
if (!timeout_set) {
|
||||
KiTimerLock();
|
||||
PKTIMER Timer = &Thread->Timer;
|
||||
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
|
||||
WaitBlock->NextWaitBlock = WaitTimer;
|
||||
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
|
||||
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
|
||||
WaitTimer->NextWaitBlock = WaitBlock;
|
||||
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
||||
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
||||
KiTimerUnlock();
|
||||
goto NoWait;
|
||||
}
|
||||
|
||||
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
|
||||
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
|
||||
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
|
||||
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
|
||||
timeout_set = true;
|
||||
DueTime.QuadPart = Timer->DueTime.QuadPart;
|
||||
// Setup a timer for the thread
|
||||
KiTimerLock();
|
||||
PKTIMER Timer = &Thread->Timer;
|
||||
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
|
||||
WaitBlock->NextWaitBlock = WaitTimer;
|
||||
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
|
||||
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
|
||||
WaitTimer->NextWaitBlock = WaitBlock;
|
||||
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
||||
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
||||
KiTimerUnlock();
|
||||
}
|
||||
|
||||
// KiTimerExpiration has removed the timer but the objects were not signaled, so we have a timeout
|
||||
// (remove this when the thread scheduler is here)
|
||||
if (Thread->Timer.Header.Inserted == FALSE) {
|
||||
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
||||
goto NoWait;
|
||||
}
|
||||
KiTimerUnlock();
|
||||
}
|
||||
else {
|
||||
WaitBlock->NextWaitBlock = WaitBlock;
|
||||
|
@ -2317,12 +2349,13 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
|
||||
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
|
||||
WaitBlock = &WaitBlockArray[0];
|
||||
KiWaitListLock();
|
||||
do {
|
||||
ObjectMutant = (PKMUTANT)WaitBlock->Object;
|
||||
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
||||
InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
||||
WaitBlock = WaitBlock->NextWaitBlock;
|
||||
} while (WaitBlock != &WaitBlockArray[0]);
|
||||
|
||||
KiWaitListUnlock();
|
||||
|
||||
/*
|
||||
TODO: We can't implement this and the return values until we have our own thread scheduler
|
||||
|
@ -2330,9 +2363,6 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
This code can all be enabled once we have CPU emulation and our own scheduler in v1.0
|
||||
*/
|
||||
|
||||
// Insert the WaitBlock
|
||||
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
||||
|
||||
// If the current thread is processing a queue object, wake other treads using the same queue
|
||||
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
|
||||
if (Queue != NULL) {
|
||||
|
@ -2344,7 +2374,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
Thread->WaitMode = WaitMode;
|
||||
Thread->WaitReason = (UCHAR)WaitReason;
|
||||
Thread->WaitTime = KeTickCount;
|
||||
//Thread->State = Waiting;
|
||||
Thread->State = Waiting;
|
||||
//KiInsertWaitList(WaitMode, Thread);
|
||||
|
||||
//WaitStatus = (NTSTATUS)KiSwapThread();
|
||||
|
@ -2359,12 +2389,15 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
//}
|
||||
|
||||
// TODO: Remove this after we have our own scheduler and the above is implemented
|
||||
Sleep(0);
|
||||
WaitStatus = WaitApc<false>([](PKTHREAD Thread) -> std::optional<ntstatus_xt> {
|
||||
if (Thread->State == Ready) {
|
||||
// We have been readied to resume execution, so exit the wait
|
||||
return std::make_optional<ntstatus_xt>(Thread->WaitStatus);
|
||||
}
|
||||
return std::nullopt;
|
||||
}, Timeout, Alertable, WaitMode, Thread);
|
||||
|
||||
// Reduce the timout if necessary
|
||||
if (Timeout != nullptr) {
|
||||
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Raise IRQL to DISPATCH_LEVEL and lock the database (only if it's not already at this level)
|
||||
|
@ -2379,10 +2412,14 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
|
||||
// The waiting thead has been alerted, or an APC needs to be delivered
|
||||
// So unlock the dispatcher database, lower the IRQ and return the status
|
||||
Thread->State = Running;
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
#if 0
|
||||
// No need for this at the moment, since WaitApc already executes user APCs
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
#endif
|
||||
|
||||
RETURN(WaitStatus);
|
||||
|
||||
|
@ -2391,14 +2428,7 @@ NoWait:
|
|||
// Unlock the database and return the status
|
||||
//TODO: KiAdjustQuantumThread(Thread);
|
||||
|
||||
// Don't forget to remove the thread timer if the objects were signaled before the timer expired
|
||||
// (remove this when the thread scheduler is here)
|
||||
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
|
||||
KiTimerLock();
|
||||
KxRemoveTreeTimer(&Thread->Timer);
|
||||
KiTimerUnlock();
|
||||
}
|
||||
|
||||
Thread->State = Running;
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
|
@ -2440,12 +2470,9 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
// Wait Loop
|
||||
// This loop ends
|
||||
PLARGE_INTEGER OriginalTime = Timeout;
|
||||
LARGE_INTEGER DueTime, NewTime;
|
||||
KWAIT_BLOCK StackWaitBlock;
|
||||
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
|
||||
NTSTATUS WaitStatus;
|
||||
// Hack variable (remove this when the thread scheduler is here)
|
||||
bool timeout_set = false;
|
||||
ntstatus_xt WaitStatus;
|
||||
do {
|
||||
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase
|
||||
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
||||
|
@ -2493,36 +2520,20 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
goto NoWait;
|
||||
}
|
||||
|
||||
// Setup a timer for the thread but only once (for now)
|
||||
if (!timeout_set) {
|
||||
KiTimerLock();
|
||||
PKTIMER Timer = &Thread->Timer;
|
||||
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
|
||||
WaitBlock->NextWaitBlock = WaitTimer;
|
||||
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
|
||||
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
|
||||
WaitTimer->NextWaitBlock = WaitBlock;
|
||||
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
||||
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
||||
KiTimerUnlock();
|
||||
goto NoWait;
|
||||
}
|
||||
|
||||
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
|
||||
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
|
||||
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
|
||||
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
|
||||
timeout_set = true;
|
||||
DueTime.QuadPart = Timer->DueTime.QuadPart;
|
||||
// Setup a timer for the thread
|
||||
KiTimerLock();
|
||||
PKTIMER Timer = &Thread->Timer;
|
||||
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
|
||||
WaitBlock->NextWaitBlock = WaitTimer;
|
||||
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
|
||||
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
|
||||
WaitTimer->NextWaitBlock = WaitBlock;
|
||||
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
||||
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
||||
KiTimerUnlock();
|
||||
}
|
||||
|
||||
// KiTimerExpiration has removed the timer but the object was not signaled, so we have a timeout
|
||||
// (remove this when the thread scheduler is here)
|
||||
if (Thread->Timer.Header.Inserted == FALSE) {
|
||||
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
||||
goto NoWait;
|
||||
}
|
||||
KiTimerUnlock();
|
||||
}
|
||||
else {
|
||||
WaitBlock->NextWaitBlock = WaitBlock;
|
||||
|
@ -2534,9 +2545,6 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
This code can all be enabled once we have CPU emulation and our own scheduler in v1.0
|
||||
*/
|
||||
|
||||
// Insert the WaitBlock
|
||||
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
||||
|
||||
// If the current thread is processing a queue object, wake other treads using the same queue
|
||||
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
|
||||
if (Queue != NULL) {
|
||||
|
@ -2548,7 +2556,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
Thread->WaitMode = WaitMode;
|
||||
Thread->WaitReason = (UCHAR)WaitReason;
|
||||
Thread->WaitTime = KeTickCount;
|
||||
// TODO: Thread->State = Waiting;
|
||||
Thread->State = Waiting;
|
||||
//KiInsertWaitList(WaitMode, Thread);
|
||||
|
||||
/*
|
||||
|
@ -2563,13 +2571,21 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
return WaitStatus;
|
||||
} */
|
||||
|
||||
// TODO: Remove this after we have our own scheduler and the above is implemented
|
||||
Sleep(0);
|
||||
// Insert the WaitBlock
|
||||
KiWaitListLock();
|
||||
InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
||||
KiWaitListUnlock();
|
||||
|
||||
// Reduce the timout if necessary
|
||||
if (Timeout != nullptr) {
|
||||
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
|
||||
}
|
||||
// TODO: Remove this after we have our own scheduler and the above is implemented
|
||||
WaitStatus = WaitApc<false>([](PKTHREAD Thread) -> std::optional<ntstatus_xt> {
|
||||
if (Thread->State == Ready) {
|
||||
// We have been readied to resume execution, so exit the wait
|
||||
return std::make_optional<ntstatus_xt>(Thread->WaitStatus);
|
||||
}
|
||||
return std::nullopt;
|
||||
}, Timeout, Alertable, WaitMode, Thread);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Raise IRQL to DISPATCH_LEVEL and lock the database
|
||||
|
@ -2584,10 +2600,14 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
|
||||
// The waiting thead has been alerted, or an APC needs to be delivered
|
||||
// So unlock the dispatcher database, lower the IRQ and return the status
|
||||
Thread->State = Running;
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
#if 0
|
||||
// No need for this at the moment, since WaitApc already executes user APCs
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
#endif
|
||||
|
||||
RETURN(WaitStatus);
|
||||
|
||||
|
@ -2596,14 +2616,7 @@ NoWait:
|
|||
// Unlock the database and return the status
|
||||
//TODO: KiAdjustQuantumThread(Thread);
|
||||
|
||||
// Don't forget to remove the thread timer if the object was signaled before the timer expired
|
||||
// (remove this when the thread scheduler is here)
|
||||
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
|
||||
KiTimerLock();
|
||||
KxRemoveTreeTimer(&Thread->Timer);
|
||||
KiTimerUnlock();
|
||||
}
|
||||
|
||||
Thread->State = Running;
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
namespace xbox
|
||||
{
|
||||
extern std::atomic_flag KeSystemTimeChanged;
|
||||
|
||||
void_xt NTAPI KeSetSystemTime
|
||||
(
|
||||
IN PLARGE_INTEGER NewTime,
|
||||
|
@ -50,5 +52,16 @@ namespace xbox
|
|||
IN PKPROCESS Process
|
||||
);
|
||||
|
||||
xbox::void_xt KeResumeThreadEx
|
||||
(
|
||||
IN PKTHREAD Thread
|
||||
);
|
||||
|
||||
xbox::void_xt KeSuspendThreadEx
|
||||
(
|
||||
IN PKTHREAD Thread
|
||||
);
|
||||
|
||||
void_xt KeEmptyQueueApc();
|
||||
void_xt KeWaitForDpc();
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@
|
|||
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
||||
*/
|
||||
|
||||
// Also from ReactOS: KiWaitTest, KiWaitSatisfyAll, KiUnwaitThread, KiUnlinkThread
|
||||
|
||||
// COPYING file:
|
||||
/*
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
@ -84,15 +86,18 @@ the said software).
|
|||
#include "Logging.h" // For LOG_FUNC()
|
||||
#include "EmuKrnl.h" // for the list support functions
|
||||
#include "EmuKrnlKi.h"
|
||||
#include "EmuKrnlKe.h"
|
||||
|
||||
#define MAX_TIMER_DPCS 16
|
||||
|
||||
#define ASSERT_TIMER_LOCKED assert(KiTimerMtx.Acquired > 0)
|
||||
#define ASSERT_WAIT_LIST_LOCKED assert(KiWaitListMtx.Acquired > 0)
|
||||
|
||||
xbox::KPROCESS KiUniqueProcess;
|
||||
const xbox::ulong_xt CLOCK_TIME_INCREMENT = 0x2710;
|
||||
xbox::KDPC KiTimerExpireDpc;
|
||||
xbox::KI_TIMER_LOCK KiTimerMtx;
|
||||
xbox::KI_WAIT_LIST_LOCK KiWaitListMtx;
|
||||
xbox::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
|
||||
xbox::LIST_ENTRY KiWaitInListHead;
|
||||
std::mutex xbox::KiApcListMtx;
|
||||
|
@ -129,55 +134,81 @@ xbox::void_xt xbox::KiTimerUnlock()
|
|||
KiTimerMtx.Mtx.unlock();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiClockIsr
|
||||
(
|
||||
unsigned int ScalingFactor
|
||||
)
|
||||
xbox::void_xt xbox::KiWaitListLock()
|
||||
{
|
||||
KIRQL OldIrql;
|
||||
LARGE_INTEGER InterruptTime;
|
||||
LARGE_INTEGER HostSystemTime;
|
||||
KiWaitListMtx.Mtx.lock();
|
||||
KiWaitListMtx.Acquired++;
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiWaitListUnlock()
|
||||
{
|
||||
KiWaitListMtx.Acquired--;
|
||||
KiWaitListMtx.Mtx.unlock();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiClockIsr(ulonglong_xt TotalUs)
|
||||
{
|
||||
LARGE_INTEGER InterruptTime, SystemTime;
|
||||
ULONG Hand;
|
||||
DWORD OldKeTickCount;
|
||||
|
||||
OldIrql = KfRaiseIrql(CLOCK_LEVEL);
|
||||
static uint64_t LostUs;
|
||||
uint64_t TotalMs = TotalUs / 1000;
|
||||
LostUs += (TotalUs - TotalMs * 1000);
|
||||
uint64_t RecoveredMs = LostUs / 1000;
|
||||
TotalMs += RecoveredMs;
|
||||
LostUs -= (RecoveredMs * 1000);
|
||||
|
||||
// Update the interrupt time
|
||||
InterruptTime.u.LowPart = KeInterruptTime.LowPart;
|
||||
InterruptTime.u.HighPart = KeInterruptTime.High1Time;
|
||||
InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * ScalingFactor);
|
||||
InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * TotalMs);
|
||||
KeInterruptTime.High2Time = InterruptTime.u.HighPart;
|
||||
KeInterruptTime.LowPart = InterruptTime.u.LowPart;
|
||||
KeInterruptTime.High1Time = InterruptTime.u.HighPart;
|
||||
|
||||
// Update the system time
|
||||
// NOTE: I'm not sure if we should round down the host system time to the nearest multiple
|
||||
// of the Xbox clock increment...
|
||||
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
|
||||
HostSystemTime.QuadPart += HostSystemTimeDelta.load();
|
||||
KeSystemTime.High2Time = HostSystemTime.u.HighPart;
|
||||
KeSystemTime.LowPart = HostSystemTime.u.LowPart;
|
||||
KeSystemTime.High1Time = HostSystemTime.u.HighPart;
|
||||
if (KeSystemTimeChanged.test()) [[unlikely]] {
|
||||
KeSystemTimeChanged.clear();
|
||||
LARGE_INTEGER HostSystemTime, OldSystemTime;
|
||||
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
|
||||
xbox::KeSystemTime.High2Time = HostSystemTime.u.HighPart;
|
||||
xbox::KeSystemTime.LowPart = HostSystemTime.u.LowPart;
|
||||
xbox::KeSystemTime.High1Time = HostSystemTime.u.HighPart;
|
||||
KeSetSystemTime(&HostSystemTime, &OldSystemTime);
|
||||
}
|
||||
else {
|
||||
SystemTime.u.LowPart = KeSystemTime.LowPart;
|
||||
SystemTime.u.HighPart = KeSystemTime.High1Time;
|
||||
SystemTime.QuadPart += (CLOCK_TIME_INCREMENT * TotalMs);
|
||||
KeSystemTime.High2Time = SystemTime.u.HighPart;
|
||||
KeSystemTime.LowPart = SystemTime.u.LowPart;
|
||||
KeSystemTime.High1Time = SystemTime.u.HighPart;
|
||||
}
|
||||
|
||||
// Update the tick counter
|
||||
OldKeTickCount = KeTickCount;
|
||||
KeTickCount += ScalingFactor;
|
||||
KeTickCount += static_cast<dword_xt>(TotalMs);
|
||||
|
||||
// Because this function must be fast to continuously update the kernel clocks, if somebody else is currently
|
||||
// holding the lock, we won't wait and instead skip the check of the timers for this cycle
|
||||
if (KiTimerMtx.Mtx.try_lock()) {
|
||||
KiTimerMtx.Acquired++;
|
||||
// Check if a timer has expired
|
||||
Hand = OldKeTickCount & (TIMER_TABLE_SIZE - 1);
|
||||
if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry &&
|
||||
(ULONGLONG)InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) {
|
||||
KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)Hand, 0);
|
||||
// On real hw, this is called every ms, so it only needs to check a single timer index. However, testing on the emulator shows that this can have a delay
|
||||
// larger than a ms. If we only check the index corresponding to OldKeTickCount, then we will miss timers that might have expired already, causing an unpredictable
|
||||
// delay on threads that are waiting with those timeouts
|
||||
dword_xt EndKeTickCount = (KeTickCount - OldKeTickCount) >= TIMER_TABLE_SIZE ? OldKeTickCount + TIMER_TABLE_SIZE : KeTickCount;
|
||||
for (dword_xt i = OldKeTickCount; i < EndKeTickCount; ++i) {
|
||||
Hand = i & (TIMER_TABLE_SIZE - 1);
|
||||
if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry &&
|
||||
(ULONGLONG)InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) {
|
||||
KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)OldKeTickCount, (PVOID)EndKeTickCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
KiTimerMtx.Acquired--;
|
||||
KiTimerMtx.Mtx.unlock();
|
||||
}
|
||||
|
||||
KfLowerIrql(OldIrql);
|
||||
}
|
||||
|
||||
xbox::void_xt NTAPI xbox::KiCheckTimerTable
|
||||
|
@ -328,8 +359,8 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
|
|||
IN xbox::ulong_xt Hand
|
||||
)
|
||||
{
|
||||
LARGE_INTEGER InterruptTime;
|
||||
LONGLONG DueTime = Timer->DueTime.QuadPart;
|
||||
ULARGE_INTEGER InterruptTime;
|
||||
ULONGLONG DueTime = Timer->DueTime.QuadPart;
|
||||
BOOLEAN Expired = FALSE;
|
||||
PLIST_ENTRY ListHead, NextEntry;
|
||||
PKTIMER CurrentTimer;
|
||||
|
@ -352,7 +383,7 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
|
|||
CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
|
||||
|
||||
/* Now check if we can fit it before */
|
||||
if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break;
|
||||
if (DueTime >= CurrentTimer->DueTime.QuadPart) break;
|
||||
|
||||
/* Keep looping */
|
||||
NextEntry = NextEntry->Blink;
|
||||
|
@ -368,6 +399,10 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
|
|||
KiTimerTableListHead[Hand].Time.QuadPart = DueTime;
|
||||
|
||||
/* Make sure it hasn't expired already */
|
||||
// NOTE: DueTime must be unsigned so that we can perform un unsigned comparison with the interrupt time. Otherwise, if DueTime is very large, it will be
|
||||
// interpreted as a very small negative number, which will cause the function to think the timer has already expired, when it didn't. Test case: Metal Slug 3.
|
||||
// It uses KeDelayExecutionThread with a relative timeout of 0x8000000000000000, which is then interpreted here as a negative number that immediately satisfies
|
||||
// the wait. The title crashes shortly after, since the wait was supposed to end with a user APC queued by NtQueueApcThread instead
|
||||
InterruptTime.QuadPart = KeQueryInterruptTime();
|
||||
if (DueTime <= InterruptTime.QuadPart) {
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Timer %p already expired", Timer);
|
||||
|
@ -484,26 +519,20 @@ xbox::boolean_xt FASTCALL xbox::KiSignalTimer
|
|||
Timer->Header.SignalState = TRUE;
|
||||
|
||||
/* Check if the timer has waiters */
|
||||
KiWaitListLock();
|
||||
if (!IsListEmpty(&Timer->Header.WaitListHead))
|
||||
{
|
||||
/* Check the type of event */
|
||||
if (Timer->Header.Type == TimerNotificationObject)
|
||||
{
|
||||
/* Unwait the thread */
|
||||
// KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise unwait the thread and signal the timer */
|
||||
// KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
|
||||
}
|
||||
KiWaitTest(Timer, 0);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
/* Check if we have a period */
|
||||
if (Period)
|
||||
{
|
||||
/* Calculate the interval and insert the timer */
|
||||
Interval.QuadPart = Int32x32To64(Period, -10000);
|
||||
Interval.QuadPart = Period * -10000LL;
|
||||
while (!KiInsertTreeTimer(Timer, Interval));
|
||||
}
|
||||
|
||||
|
@ -532,7 +561,7 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
{
|
||||
ULARGE_INTEGER SystemTime, InterruptTime;
|
||||
LARGE_INTEGER Interval;
|
||||
LONG Limit, Index, i;
|
||||
LONG i;
|
||||
ULONG Timers, ActiveTimers, DpcCalls;
|
||||
PLIST_ENTRY ListHead, NextEntry;
|
||||
KIRQL OldIrql;
|
||||
|
@ -544,34 +573,25 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
/* Query system and interrupt time */
|
||||
KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
|
||||
InterruptTime.QuadPart = KeQueryInterruptTime();
|
||||
Limit = KeTickCount;
|
||||
|
||||
/* Get the index of the timer and normalize it */
|
||||
Index = PtrToLong(SystemArgument1);
|
||||
if ((Limit - Index) >= TIMER_TABLE_SIZE)
|
||||
{
|
||||
/* Normalize it */
|
||||
Limit = Index + TIMER_TABLE_SIZE - 1;
|
||||
}
|
||||
|
||||
/* Setup index and actual limit */
|
||||
Index--;
|
||||
Limit &= (TIMER_TABLE_SIZE - 1);
|
||||
dword_xt OldKeTickCount = PtrToLong(SystemArgument1);
|
||||
dword_xt EndKeTickCount = PtrToLong(SystemArgument2);
|
||||
|
||||
/* Setup accounting data */
|
||||
DpcCalls = 0;
|
||||
Timers = 24;
|
||||
ActiveTimers = 4;
|
||||
|
||||
/* Lock the Database and Raise IRQL */
|
||||
/* Lock the Database */
|
||||
KiTimerLock();
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
/* Start expiration loop */
|
||||
do
|
||||
for (dword_xt i = OldKeTickCount; i < EndKeTickCount; ++i)
|
||||
{
|
||||
/* Get the current index */
|
||||
Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
|
||||
dword_xt Index = i & (TIMER_TABLE_SIZE - 1);
|
||||
|
||||
/* Get list pointers and loop the list */
|
||||
ListHead = &KiTimerTableListHead[Index].Entry;
|
||||
|
@ -599,26 +619,20 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
Period = Timer->Period;
|
||||
|
||||
/* Check if there are any waiters */
|
||||
KiWaitListLock();
|
||||
if (!IsListEmpty(&Timer->Header.WaitListHead))
|
||||
{
|
||||
/* Check the type of event */
|
||||
if (Timer->Header.Type == TimerNotificationObject)
|
||||
{
|
||||
/* Unwait the thread */
|
||||
// KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise unwait the thread and signal the timer */
|
||||
// KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
|
||||
}
|
||||
KiWaitTest(Timer, 0);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
/* Check if we have a period */
|
||||
if (Period)
|
||||
{
|
||||
/* Calculate the interval and insert the timer */
|
||||
Interval.QuadPart = Int32x32To64(Period, -10000);
|
||||
Interval.QuadPart = Period * -10000LL;
|
||||
while (!KiInsertTreeTimer(Timer, Interval));
|
||||
}
|
||||
|
||||
|
@ -709,7 +723,7 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
break;
|
||||
}
|
||||
}
|
||||
} while (Index != Limit);
|
||||
}
|
||||
|
||||
/* Verify the timer table, on a debug kernel only */
|
||||
if (g_bIsDebugKernel) {
|
||||
|
@ -737,17 +751,17 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
);
|
||||
}
|
||||
|
||||
KiTimerUnlock();
|
||||
/* Lower IRQL if we need to */
|
||||
if (OldIrql != DISPATCH_LEVEL) {
|
||||
KfLowerIrql(OldIrql);
|
||||
}
|
||||
KiTimerUnlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unlock the dispatcher */
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
KiTimerUnlock();
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -791,26 +805,20 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
|
|||
Period = Timer->Period;
|
||||
|
||||
/* Check if there's any waiters */
|
||||
KiWaitListLock();
|
||||
if (!IsListEmpty(&Timer->Header.WaitListHead))
|
||||
{
|
||||
/* Check the type of event */
|
||||
if (Timer->Header.Type == TimerNotificationObject)
|
||||
{
|
||||
/* Unwait the thread */
|
||||
// KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise unwait the thread and signal the timer */
|
||||
// KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
|
||||
}
|
||||
KiWaitTest(Timer, 0);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
/* Check if we have a period */
|
||||
if (Period)
|
||||
{
|
||||
/* Calculate the interval and insert the timer */
|
||||
Interval.QuadPart = Int32x32To64(Period, -10000);
|
||||
Interval.QuadPart = Period * -10000LL;
|
||||
while (!KiInsertTreeTimer(Timer, Interval));
|
||||
}
|
||||
|
||||
|
@ -826,6 +834,8 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
|
|||
}
|
||||
}
|
||||
|
||||
KiTimerUnlock();
|
||||
|
||||
/* Check if we still have DPC entries */
|
||||
if (DpcCalls)
|
||||
{
|
||||
|
@ -849,39 +859,14 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
|
|||
|
||||
/* Lower IRQL */
|
||||
KfLowerIrql(OldIrql);
|
||||
KiTimerUnlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unlock the dispatcher */
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
KiTimerUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
xbox::void_xt FASTCALL xbox::KiWaitSatisfyAll
|
||||
(
|
||||
IN xbox::PKWAIT_BLOCK WaitBlock
|
||||
)
|
||||
{
|
||||
PKMUTANT Object;
|
||||
PRKTHREAD Thread;
|
||||
PKWAIT_BLOCK WaitBlock1;
|
||||
|
||||
WaitBlock1 = WaitBlock;
|
||||
Thread = WaitBlock1->Thread;
|
||||
do {
|
||||
if (WaitBlock1->WaitKey != (cshort_xt)STATUS_TIMEOUT) {
|
||||
Object = (PKMUTANT)WaitBlock1->Object;
|
||||
KiWaitSatisfyAny(Object, Thread);
|
||||
}
|
||||
|
||||
WaitBlock1 = WaitBlock1->NextWaitBlock;
|
||||
} while (WaitBlock1 != WaitBlock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template<xbox::MODE ApcMode>
|
||||
static xbox::void_xt KiExecuteApc()
|
||||
{
|
||||
|
@ -907,12 +892,13 @@ static xbox::void_xt KiExecuteApc()
|
|||
Apc->Inserted = FALSE;
|
||||
xbox::KiApcListMtx.unlock();
|
||||
|
||||
// NOTE: we never use KernelRoutine because that is only used for kernel APCs, which we currently don't use
|
||||
// This is either KiFreeUserApc, which frees the memory of the apc, or KiSuspendNop, which does nothing
|
||||
(Apc->KernelRoutine)(Apc, &Apc->NormalRoutine, &Apc->NormalContext, &Apc->SystemArgument1, &Apc->SystemArgument2);
|
||||
|
||||
if (Apc->NormalRoutine != xbox::zeroptr) {
|
||||
(Apc->NormalRoutine)(Apc->NormalContext, Apc->SystemArgument1, Apc->SystemArgument2);
|
||||
}
|
||||
|
||||
xbox::ExFreePool(Apc);
|
||||
xbox::KiApcListMtx.lock();
|
||||
}
|
||||
|
||||
|
@ -929,6 +915,25 @@ xbox::void_xt xbox::KiExecuteUserApc()
|
|||
KiExecuteApc<UserMode>();
|
||||
}
|
||||
|
||||
xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
|
||||
(
|
||||
IN xbox::PLARGE_INTEGER OriginalTime,
|
||||
IN xbox::PLARGE_INTEGER DueTime,
|
||||
IN OUT xbox::PLARGE_INTEGER NewTime,
|
||||
OUT ulonglong_xt *Now
|
||||
)
|
||||
{
|
||||
*Now = xbox::KeQueryInterruptTime();
|
||||
if (OriginalTime->QuadPart >= 0) {
|
||||
return OriginalTime;
|
||||
}
|
||||
else {
|
||||
NewTime->QuadPart = *Now;
|
||||
NewTime->QuadPart -= DueTime->QuadPart;
|
||||
return NewTime;
|
||||
}
|
||||
}
|
||||
|
||||
xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
|
||||
(
|
||||
IN xbox::PLARGE_INTEGER OriginalTime,
|
||||
|
@ -947,7 +952,8 @@ xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
|
|||
}
|
||||
|
||||
// Source: ReactOS
|
||||
xbox::void_xt NTAPI xbox::KiSuspendNop(
|
||||
xbox::void_xt NTAPI xbox::KiSuspendNop
|
||||
(
|
||||
IN PKAPC Apc,
|
||||
IN PKNORMAL_ROUTINE* NormalRoutine,
|
||||
IN PVOID* NormalContext,
|
||||
|
@ -955,7 +961,7 @@ xbox::void_xt NTAPI xbox::KiSuspendNop(
|
|||
IN PVOID* SystemArgument2
|
||||
)
|
||||
{
|
||||
/* Does nothing */
|
||||
/* Does nothing because the memory of the suspend apc is part of kthread */
|
||||
UNREFERENCED_PARAMETER(Apc);
|
||||
UNREFERENCED_PARAMETER(NormalRoutine);
|
||||
UNREFERENCED_PARAMETER(NormalContext);
|
||||
|
@ -963,8 +969,21 @@ xbox::void_xt NTAPI xbox::KiSuspendNop(
|
|||
UNREFERENCED_PARAMETER(SystemArgument2);
|
||||
}
|
||||
|
||||
xbox::void_xt NTAPI xbox::KiFreeUserApc
|
||||
(
|
||||
IN PKAPC Apc,
|
||||
IN PKNORMAL_ROUTINE *NormalRoutine,
|
||||
IN PVOID *NormalContext,
|
||||
IN PVOID *SystemArgument1,
|
||||
IN PVOID *SystemArgument2
|
||||
)
|
||||
{
|
||||
ExFreePool(Apc);
|
||||
}
|
||||
|
||||
// Source: ReactOS
|
||||
xbox::void_xt NTAPI xbox::KiSuspendThread(
|
||||
xbox::void_xt NTAPI xbox::KiSuspendThread
|
||||
(
|
||||
IN PVOID NormalContext,
|
||||
IN PVOID SystemArgument1,
|
||||
IN PVOID SystemArgument2
|
||||
|
@ -1057,3 +1076,210 @@ xbox::void_xt xbox::KiInitializeContextThread(
|
|||
/* Save back the new value of the kernel stack. */
|
||||
Thread->KernelStack = reinterpret_cast<PVOID>(CtxSwitchFrame);
|
||||
}
|
||||
|
||||
xbox::boolean_xt xbox::KiInsertQueueApc
|
||||
(
|
||||
IN PRKAPC Apc,
|
||||
IN KPRIORITY Increment
|
||||
)
|
||||
{
|
||||
PKTHREAD kThread = Apc->Thread;
|
||||
KiApcListMtx.lock();
|
||||
if (Apc->Inserted) {
|
||||
KiApcListMtx.unlock();
|
||||
return FALSE;
|
||||
}
|
||||
InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry);
|
||||
Apc->Inserted = TRUE;
|
||||
KiApcListMtx.unlock();
|
||||
|
||||
// We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD
|
||||
// in the fs selector will not be correct
|
||||
if (Apc->ApcMode == KernelMode) { // kernel apc
|
||||
kThread->ApcState.KernelApcPending = TRUE;
|
||||
// NOTE: this is wrong, we should check the thread state instead of just signaling the kernel apc, but we currently
|
||||
// don't set the appropriate state in kthread
|
||||
if (kThread == KeGetCurrentThread()) {
|
||||
KiExecuteKernelApc();
|
||||
}
|
||||
}
|
||||
else if ((kThread->WaitMode == UserMode) && (kThread->Alertable)) { // user apc
|
||||
kThread->ApcState.UserApcPending = TRUE;
|
||||
// NOTE: this should also check the thread state
|
||||
if (kThread == KeGetCurrentThread()) {
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiWaitTest
|
||||
(
|
||||
IN PVOID Object,
|
||||
IN KPRIORITY Increment
|
||||
)
|
||||
{
|
||||
PLIST_ENTRY WaitEntry, WaitList;
|
||||
PKWAIT_BLOCK WaitBlock, NextBlock;
|
||||
PKTHREAD WaitThread;
|
||||
PKMUTANT FirstObject = (PKMUTANT)Object;
|
||||
|
||||
ASSERT_WAIT_LIST_LOCKED;
|
||||
|
||||
/* Loop the Wait Entries */
|
||||
WaitList = &FirstObject->Header.WaitListHead;
|
||||
WaitEntry = WaitList->Flink;
|
||||
while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList)) {
|
||||
/* Get the current wait block */
|
||||
WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
|
||||
WaitThread = WaitBlock->Thread;
|
||||
|
||||
/* Check the current Wait Mode */
|
||||
if (WaitBlock->WaitType == WaitAny) {
|
||||
/* Easy case, satisfy only this wait */
|
||||
KiWaitSatisfyAny(FirstObject, WaitThread);
|
||||
}
|
||||
else {
|
||||
/* WaitAll, check that all the objects are signalled */
|
||||
NextBlock = WaitBlock->NextWaitBlock;
|
||||
while (NextBlock != WaitBlock) {
|
||||
if (NextBlock->WaitKey != X_STATUS_TIMEOUT) {
|
||||
PKMUTANT Mutant = (PKMUTANT)NextBlock->Object;
|
||||
// NOTE: we ignore mutants because we forward them to ntdll
|
||||
if (Mutant->Header.SignalState <= 0) {
|
||||
// We found at least one object not in the signalled state, so we cannot satisfy the wait
|
||||
goto NextWaitEntry;
|
||||
}
|
||||
}
|
||||
|
||||
NextBlock = NextBlock->NextWaitBlock;
|
||||
}
|
||||
|
||||
KiWaitSatisfyAll(WaitBlock);
|
||||
}
|
||||
|
||||
/* Now do the rest of the unwait */
|
||||
KiUnwaitThread(WaitThread, WaitBlock->WaitKey, Increment);
|
||||
NextWaitEntry:
|
||||
WaitEntry = WaitEntry->Flink;
|
||||
}
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiWaitSatisfyAll
|
||||
(
|
||||
IN PKWAIT_BLOCK FirstBlock
|
||||
)
|
||||
{
|
||||
PKWAIT_BLOCK WaitBlock = FirstBlock;
|
||||
PKTHREAD WaitThread = WaitBlock->Thread;
|
||||
|
||||
ASSERT_WAIT_LIST_LOCKED;
|
||||
|
||||
/* Loop through all the Wait Blocks, and wake each Object */
|
||||
do {
|
||||
/* Make sure it hasn't timed out */
|
||||
if (WaitBlock->WaitKey != X_STATUS_TIMEOUT) {
|
||||
/* Wake the Object */
|
||||
KiWaitSatisfyAny((PKMUTANT)WaitBlock->Object, WaitThread);
|
||||
}
|
||||
|
||||
/* Move to the next block */
|
||||
WaitBlock = WaitBlock->NextWaitBlock;
|
||||
} while (WaitBlock != FirstBlock);
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiWaitSatisfyAllAndLock
|
||||
(
|
||||
IN PKWAIT_BLOCK FirstBlock
|
||||
)
|
||||
{
|
||||
KiWaitListLock();
|
||||
KiWaitSatisfyAll(FirstBlock);
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiUnwaitThread
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN long_ptr_xt WaitStatus,
|
||||
IN KPRIORITY Increment
|
||||
)
|
||||
{
|
||||
ASSERT_WAIT_LIST_LOCKED;
|
||||
|
||||
if (Thread->State != Waiting) {
|
||||
// Don't do anything if it was already unwaited
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unlink the thread */
|
||||
KiUnlinkThread(Thread, WaitStatus);
|
||||
|
||||
// We cannot schedule the thread, so we'll just set its state to Ready
|
||||
Thread->State = Ready;
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiUnwaitThreadAndLock
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN long_ptr_xt WaitStatus,
|
||||
IN KPRIORITY Increment
|
||||
)
|
||||
{
|
||||
KiWaitListLock();
|
||||
KiUnwaitThread(Thread, WaitStatus, Increment);
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiUnlinkThread
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN long_ptr_xt WaitStatus
|
||||
)
|
||||
{
|
||||
PKWAIT_BLOCK WaitBlock;
|
||||
PKTIMER Timer;
|
||||
|
||||
ASSERT_WAIT_LIST_LOCKED;
|
||||
|
||||
/* Update wait status */
|
||||
Thread->WaitStatus |= WaitStatus;
|
||||
|
||||
/* Remove the Wait Blocks from the list */
|
||||
WaitBlock = Thread->WaitBlockList;
|
||||
do {
|
||||
/* Remove it */
|
||||
RemoveEntryList(&WaitBlock->WaitListEntry);
|
||||
|
||||
/* Go to the next one */
|
||||
WaitBlock = WaitBlock->NextWaitBlock;
|
||||
} while (WaitBlock != Thread->WaitBlockList);
|
||||
|
||||
#if 0
|
||||
// Disabled, as we currently don't put threads in the ready list
|
||||
/* Remove the thread from the wait list! */
|
||||
if (Thread->WaitListEntry.Flink) {
|
||||
RemoveEntryList(&Thread->WaitListEntry);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check if there's a Thread Timer */
|
||||
Timer = &Thread->Timer;
|
||||
if (Timer->Header.Inserted) {
|
||||
KiTimerLock();
|
||||
KxRemoveTreeTimer(Timer);
|
||||
KiTimerUnlock();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Disabled, because we don't support queues
|
||||
/* Increment the Queue's active threads */
|
||||
if (Thread->Queue) Thread->Queue->CurrentCount++;
|
||||
#endif
|
||||
|
||||
// Sanity check: set WaitBlockList to nullptr so that we can catch the case where a waiter starts a new wait but forgets to setup a new wait block. This
|
||||
// way, we will crash instead of silently using the pointer to the old block
|
||||
Thread->WaitBlockList = zeroptr;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,12 @@ namespace xbox
|
|||
int Acquired;
|
||||
} KI_TIMER_LOCK;
|
||||
|
||||
typedef struct _KI_WAIT_LIST_LOCK
|
||||
{
|
||||
std::recursive_mutex Mtx;
|
||||
int Acquired;
|
||||
} KI_WAIT_LIST_LOCK;
|
||||
|
||||
// NOTE: since the apc list is per-thread, we could also create a different mutex for each kthread
|
||||
extern std::mutex KiApcListMtx;
|
||||
|
||||
|
@ -56,10 +62,11 @@ namespace xbox
|
|||
|
||||
void_xt KiTimerUnlock();
|
||||
|
||||
void_xt KiClockIsr
|
||||
(
|
||||
IN unsigned int ScalingFactor
|
||||
);
|
||||
void_xt KiWaitListLock();
|
||||
|
||||
void_xt KiWaitListUnlock();
|
||||
|
||||
void_xt KiClockIsr(ulonglong_xt TotalUs);
|
||||
|
||||
xbox::void_xt NTAPI KiCheckTimerTable
|
||||
(
|
||||
|
@ -132,7 +139,7 @@ namespace xbox
|
|||
IN KIRQL OldIrql
|
||||
);
|
||||
|
||||
void_xt FASTCALL KiWaitSatisfyAll
|
||||
void_xt KiWaitSatisfyAll
|
||||
(
|
||||
IN PKWAIT_BLOCK WaitBlock
|
||||
);
|
||||
|
@ -140,6 +147,14 @@ namespace xbox
|
|||
void_xt KiExecuteKernelApc();
|
||||
void_xt KiExecuteUserApc();
|
||||
|
||||
PLARGE_INTEGER FASTCALL KiComputeWaitInterval
|
||||
(
|
||||
IN PLARGE_INTEGER OriginalTime,
|
||||
IN PLARGE_INTEGER DueTime,
|
||||
IN OUT PLARGE_INTEGER NewTime,
|
||||
OUT ulonglong_xt *Now
|
||||
);
|
||||
|
||||
PLARGE_INTEGER FASTCALL KiComputeWaitInterval
|
||||
(
|
||||
IN PLARGE_INTEGER OriginalTime,
|
||||
|
@ -148,7 +163,8 @@ namespace xbox
|
|||
);
|
||||
|
||||
// Source: ReactOS
|
||||
void_xt NTAPI KiSuspendNop(
|
||||
void_xt NTAPI KiSuspendNop
|
||||
(
|
||||
IN PKAPC Apc,
|
||||
IN PKNORMAL_ROUTINE* NormalRoutine,
|
||||
IN PVOID* NormalContext,
|
||||
|
@ -156,6 +172,15 @@ namespace xbox
|
|||
IN PVOID* SystemArgument2
|
||||
);
|
||||
|
||||
void_xt NTAPI KiFreeUserApc
|
||||
(
|
||||
IN PKAPC Apc,
|
||||
IN PKNORMAL_ROUTINE *NormalRoutine,
|
||||
IN PVOID *NormalContext,
|
||||
IN PVOID *SystemArgument1,
|
||||
IN PVOID *SystemArgument2
|
||||
);
|
||||
|
||||
// Source: ReactOS
|
||||
void_xt NTAPI KiSuspendThread(
|
||||
IN PVOID NormalContext,
|
||||
|
@ -172,6 +197,48 @@ namespace xbox
|
|||
IN PKSTART_ROUTINE StartRoutine,
|
||||
IN PVOID StartContext
|
||||
);
|
||||
|
||||
boolean_xt KiInsertQueueApc
|
||||
(
|
||||
IN PRKAPC Apc,
|
||||
IN KPRIORITY Increment
|
||||
);
|
||||
|
||||
void_xt KiWaitTest
|
||||
(
|
||||
IN PVOID Object,
|
||||
IN KPRIORITY Increment
|
||||
);
|
||||
|
||||
void_xt KiWaitSatisfyAll
|
||||
(
|
||||
IN PKWAIT_BLOCK FirstBlock
|
||||
);
|
||||
|
||||
void_xt KiWaitSatisfyAllAndLock
|
||||
(
|
||||
IN PKWAIT_BLOCK FirstBlock
|
||||
);
|
||||
|
||||
void_xt KiUnwaitThread
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN long_ptr_xt WaitStatus,
|
||||
IN KPRIORITY Increment
|
||||
);
|
||||
|
||||
void_xt KiUnwaitThreadAndLock
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN long_ptr_xt WaitStatus,
|
||||
IN KPRIORITY Increment
|
||||
);
|
||||
|
||||
void_xt KiUnlinkThread
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN long_ptr_xt WaitStatus
|
||||
);
|
||||
};
|
||||
|
||||
extern xbox::KPROCESS KiUniqueProcess;
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace NtDll
|
|||
#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc.
|
||||
#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include "devices\Xbox.h"
|
||||
#include "CxbxDebugger.h"
|
||||
|
||||
#pragma warning(disable:4005) // Ignore redefined status values
|
||||
|
@ -58,7 +59,7 @@ namespace NtDll
|
|||
#include <mutex>
|
||||
|
||||
// Prevent setting the system time from multiple threads at the same time
|
||||
std::mutex NtSystemTimeMtx;
|
||||
xbox::RTL_CRITICAL_SECTION xbox::NtSystemTimeCritSec;
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x00B8 - NtAllocateVirtualMemory()
|
||||
|
@ -1039,7 +1040,7 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread
|
|||
|
||||
PKAPC Apc = static_cast<PKAPC>(ExAllocatePoolWithTag(sizeof(KAPC), 'pasP'));
|
||||
if (Apc != zeroptr) {
|
||||
KeInitializeApc(Apc, &Thread->Tcb, zeroptr, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
|
||||
KeInitializeApc(Apc, &Thread->Tcb, KiFreeUserApc, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
|
||||
if (!KeInsertQueueApc(Apc, ApcStatusBlock, ApcReserved, 0)) {
|
||||
ExFreePool(Apc);
|
||||
result = X_STATUS_UNSUCCESSFUL;
|
||||
|
@ -1711,6 +1712,16 @@ XBSYSAPI EXPORTNUM(219) xbox::ntstatus_xt NTAPI xbox::NtReadFile
|
|||
CxbxDebugger::ReportFileRead(FileHandle, Length, Offset);
|
||||
}
|
||||
|
||||
// If we are emulating the Chihiro, we need to hook mbcom
|
||||
if (g_bIsChihiro && FileHandle == CHIHIRO_MBCOM_HANDLE) {
|
||||
g_MediaBoard->ComRead(ByteOffset->QuadPart, Buffer, Length);
|
||||
|
||||
// Update the Status Block
|
||||
IoStatusBlock->Status = STATUS_SUCCESS;
|
||||
IoStatusBlock->Information = Length;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (ApcRoutine != nullptr) {
|
||||
// Pack the original parameters to a wrapped context for a custom APC routine
|
||||
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext);
|
||||
|
@ -1856,15 +1867,20 @@ XBSYSAPI EXPORTNUM(224) xbox::ntstatus_xt NTAPI xbox::NtResumeThread
|
|||
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
|
||||
// Thread handles are created by ob
|
||||
RETURN(NtDll::NtResumeThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
|
||||
}
|
||||
else {
|
||||
RETURN(X_STATUS_INVALID_HANDLE);
|
||||
PETHREAD Thread;
|
||||
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
// TODO : Once we do our own thread-switching, implement NtResumeThread using KetResumeThread
|
||||
ulong_xt PrevSuspendCount = KeResumeThread(&Thread->Tcb);
|
||||
ObfDereferenceObject(Thread);
|
||||
|
||||
if (PreviousSuspendCount) {
|
||||
*PreviousSuspendCount = PrevSuspendCount;
|
||||
}
|
||||
|
||||
RETURN(X_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1971,7 +1987,7 @@ XBSYSAPI EXPORTNUM(228) xbox::ntstatus_xt NTAPI xbox::NtSetSystemTime
|
|||
ret = STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
else {
|
||||
NtSystemTimeMtx.lock();
|
||||
RtlEnterCriticalSectionAndRegion(&NtSystemTimeCritSec);
|
||||
NewSystemTime = *SystemTime;
|
||||
if (NewSystemTime.u.HighPart > 0 && NewSystemTime.u.HighPart <= 0x20000000) {
|
||||
/* Convert the time and set it in HAL */
|
||||
|
@ -1991,7 +2007,7 @@ XBSYSAPI EXPORTNUM(228) xbox::ntstatus_xt NTAPI xbox::NtSetSystemTime
|
|||
else {
|
||||
ret = STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
NtSystemTimeMtx.unlock();
|
||||
RtlLeaveCriticalSectionAndRegion(&NtSystemTimeCritSec);
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
|
@ -2079,15 +2095,30 @@ XBSYSAPI EXPORTNUM(231) xbox::ntstatus_xt NTAPI xbox::NtSuspendThread
|
|||
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
|
||||
// Thread handles are created by ob
|
||||
RETURN(NtDll::NtSuspendThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
|
||||
}
|
||||
else {
|
||||
RETURN(X_STATUS_INVALID_HANDLE);
|
||||
PETHREAD Thread;
|
||||
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
// TODO : Once we do our own thread-switching, implement NtSuspendThread using KeSuspendThread
|
||||
if (Thread != PspGetCurrentThread()) {
|
||||
if (Thread->Tcb.HasTerminated) {
|
||||
ObfDereferenceObject(Thread);
|
||||
RETURN(X_STATUS_THREAD_IS_TERMINATING);
|
||||
}
|
||||
}
|
||||
|
||||
ulong_xt PrevSuspendCount = KeSuspendThread(&Thread->Tcb);
|
||||
ObfDereferenceObject(Thread);
|
||||
if (PrevSuspendCount == X_STATUS_SUSPEND_COUNT_EXCEEDED) {
|
||||
RETURN(X_STATUS_SUSPEND_COUNT_EXCEEDED);
|
||||
}
|
||||
|
||||
if (PreviousSuspendCount) {
|
||||
*PreviousSuspendCount = PrevSuspendCount;
|
||||
}
|
||||
|
||||
RETURN(X_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -2201,26 +2232,23 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
|
|||
if (const auto &nativeHandle = GetNativeHandle(Handles[i])) {
|
||||
// This is a ob handle, so replace it with its native counterpart
|
||||
nativeHandles[i] = *nativeHandle;
|
||||
EmuLog(LOG_LEVEL::DEBUG, "xbox handle: %p", nativeHandles[i]);
|
||||
}
|
||||
else {
|
||||
nativeHandles[i] = Handles[i];
|
||||
EmuLog(LOG_LEVEL::DEBUG, "native handle: %p", nativeHandles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
LARGE_INTEGER NewTime;
|
||||
PLARGE_INTEGER pNewTime;
|
||||
if (Timeout) {
|
||||
LARGE_INTEGER ExpireTime, DueTime;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart;
|
||||
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
pNewTime = &NewTime;
|
||||
|
||||
PKTHREAD kThread = KeGetCurrentThread();
|
||||
kThread->WaitStatus = X_STATUS_SUCCESS;
|
||||
if (!AddWaitObject(kThread, Timeout)) {
|
||||
RETURN(X_STATUS_TIMEOUT);
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Count, &nativeHandles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
|
||||
xbox::ntstatus_xt ret = WaitApc<true>([Count, &nativeHandles, WaitType, Alertable](xbox::PKTHREAD kThread) -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtWaitForMultipleObjects(
|
||||
|
@ -2232,8 +2260,11 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
|
|||
if (Status == STATUS_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, pNewTime, Alertable, WaitMode);
|
||||
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
|
||||
// to the thread. Test case: Steel Battalion
|
||||
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
|
||||
return std::make_optional<ntstatus_xt>(kThread->WaitStatus);
|
||||
}, Timeout, Alertable, WaitMode, kThread);
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
@ -2280,6 +2311,16 @@ XBSYSAPI EXPORTNUM(236) xbox::ntstatus_xt NTAPI xbox::NtWriteFile
|
|||
CxbxDebugger::ReportFileWrite(FileHandle, Length, Offset);
|
||||
}
|
||||
|
||||
// If we are emulating the Chihiro, we need to hook mbcom
|
||||
if (g_bIsChihiro && FileHandle == CHIHIRO_MBCOM_HANDLE) {
|
||||
g_MediaBoard->ComWrite(ByteOffset->QuadPart, Buffer, Length);
|
||||
|
||||
// Update the Status Block
|
||||
IoStatusBlock->Status = STATUS_SUCCESS;
|
||||
IoStatusBlock->Information = Length;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (ApcRoutine != nullptr) {
|
||||
// Pack the original parameters to a wrapped context for a custom APC routine
|
||||
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext);
|
||||
|
|
|
@ -1023,42 +1023,53 @@ XBSYSAPI EXPORTNUM(246) xbox::ntstatus_xt NTAPI xbox::ObReferenceObjectByHandle
|
|||
PVOID Object;
|
||||
POBJECT_HEADER ObjectHeader;
|
||||
|
||||
// Check if Handle contain special handle for current thread.
|
||||
if (Handle == NtCurrentThread()) {
|
||||
if ((ObjectType == &PsThreadObjectType) || (ObjectType == NULL)) {
|
||||
// We only accept either thread or null object type.
|
||||
if ((ObjectType == &PsThreadObjectType) || (!ObjectType)) {
|
||||
Object = PspGetCurrentThread();
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
||||
InterlockedIncrement((::PLONG)(&ObjectHeader->PointerCount));
|
||||
*ReturnedObject = Object;
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
result = STATUS_OBJECT_TYPE_MISMATCH;
|
||||
}
|
||||
} else {
|
||||
Object = ObpGetObjectHandleReference(Handle);
|
||||
|
||||
if (Object != NULL) {
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
||||
|
||||
if ((ObjectType == ObjectHeader->Type) || (ObjectType == NULL)) {
|
||||
*ReturnedObject = Object;
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
ObfDereferenceObject(Object);
|
||||
result = STATUS_OBJECT_TYPE_MISMATCH;
|
||||
// Check if object is null pointer
|
||||
if (!Object) {
|
||||
DWORD flags = 0;
|
||||
if (Handle == (xbox::HANDLE)-1) {
|
||||
// bypass hack below check if special handle is NtCurrentProcess.
|
||||
}
|
||||
} else {
|
||||
// HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own
|
||||
// In this case, we must return the input handle
|
||||
// Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection)
|
||||
DWORD flags = 0;
|
||||
if (GetHandleInformation(Handle, &flags)) {
|
||||
else if (GetHandleInformation(Handle, &flags)) {
|
||||
// This was a Windows Handle, so return it.
|
||||
*ReturnedObject = Handle;
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// TODO: Remove above, inside if statement, to leave only result value set here.
|
||||
result = STATUS_INVALID_HANDLE;
|
||||
}
|
||||
// If object is valid, then return object.
|
||||
else {
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
||||
|
||||
// Verify if object type do match with found object or any if null object type.
|
||||
if ((ObjectType == ObjectHeader->Type) || (!ObjectType)) {
|
||||
*ReturnedObject = Object;
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
else {
|
||||
ObfDereferenceObject(Object);
|
||||
result = STATUS_OBJECT_TYPE_MISMATCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ReturnedObject = NULL;
|
||||
|
|
|
@ -45,8 +45,6 @@
|
|||
#include "core\kernel\support\EmuFS.h" // For EmuGenerateFS
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
|
||||
#include <semaphore>
|
||||
|
||||
// prevent name collisions
|
||||
namespace NtDll
|
||||
{
|
||||
|
@ -60,7 +58,6 @@ typedef struct _PCSTProxyParam
|
|||
{
|
||||
IN xbox::PVOID Ethread;
|
||||
IN xbox::ulong_xt TlsDataSize;
|
||||
IN OUT std::binary_semaphore* signal;
|
||||
}
|
||||
PCSTProxyParam;
|
||||
|
||||
|
@ -115,18 +112,6 @@ static unsigned int WINAPI PCSTProxy
|
|||
#else
|
||||
EmuGenerateFS(eThread);
|
||||
#endif
|
||||
|
||||
// NOTE: Native KTHREAD-SWITCHING will not need notification as it should be already done within
|
||||
// PsCreateSystemThreadEx function. For now, it is an experimental to see if xbox thread setup do work
|
||||
// without reside in host stack.
|
||||
// Initializing ethread is done, we can allow PsCreateSystemThreadEx process to continue.
|
||||
params.signal->release();
|
||||
params.signal = nullptr;
|
||||
SuspendThread(GetCurrentThread());
|
||||
if (xbox::KeGetCurrentThread()->HasTerminated) {
|
||||
xbox::PsTerminateSystemThread(0);
|
||||
}
|
||||
|
||||
xbox::PKSTART_FRAME StartFrame = reinterpret_cast<xbox::PKSTART_FRAME>(reinterpret_cast<xbox::addr_xt>(eThread->Tcb.KernelStack) + sizeof(xbox::KSWITCHFRAME));
|
||||
|
||||
LOG_PCSTProxy(
|
||||
|
@ -136,6 +121,8 @@ static unsigned int WINAPI PCSTProxy
|
|||
params.Ethread,
|
||||
params.TlsDataSize);
|
||||
|
||||
xbox::KiExecuteKernelApc();
|
||||
|
||||
auto routine = (xbox::PKSYSTEM_ROUTINE)StartFrame->SystemRoutine;
|
||||
// Debugging notice : When the below line shows up with an Exception dialog and a
|
||||
// message like: "Exception thrown at 0x00026190 in cxbx.exe: 0xC0000005: Access
|
||||
|
@ -154,7 +141,7 @@ static unsigned int WINAPI PCSTProxy
|
|||
return 0; // will never be reached
|
||||
}
|
||||
|
||||
// Placeholder system function, instead of XapiThreadStartup
|
||||
// Placeholder system function
|
||||
xbox::void_xt NTAPI PspSystemThreadStartup
|
||||
(
|
||||
IN xbox::PKSTART_ROUTINE StartRoutine,
|
||||
|
@ -231,6 +218,35 @@ xbox::void_xt NTAPI PspReaperRoutine(
|
|||
}
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt NTAPI CxbxrCreateThread
|
||||
(
|
||||
OUT xbox::PHANDLE ThreadHandle,
|
||||
OUT xbox::PHANDLE ThreadId OPTIONAL,
|
||||
IN xbox::PKSTART_ROUTINE StartRoutine,
|
||||
IN xbox::PVOID StartContext,
|
||||
IN xbox::boolean_xt DebuggerThread
|
||||
)
|
||||
{
|
||||
Xbe::TLS* XbeTls = (Xbe::TLS*)CxbxKrnl_Xbe->m_Header.dwTLSAddr;
|
||||
xbox::ulong_xt TlsDataSize = (XbeTls->dwDataEndAddr - XbeTls->dwDataStartAddr);
|
||||
TlsDataSize += XbeTls->dwSizeofZeroFill + 15;
|
||||
TlsDataSize = (TlsDataSize & ~15) + 4;
|
||||
// Verify EmuGenerateFS's TlsData's setup is carry over to XapiThreadStartup function.
|
||||
// Instead of using PspSystemThreadStartup which is entirely different function.
|
||||
return xbox::PsCreateSystemThreadEx(
|
||||
/*OUT*/ThreadHandle,
|
||||
/*ThreadExtensionSize=*/0,
|
||||
/*KernelStackSize=*/KERNEL_STACK_SIZE,
|
||||
TlsDataSize,
|
||||
/*OUT*/ThreadId,
|
||||
/*StartRoutine=*/StartRoutine,
|
||||
StartContext,
|
||||
/*CreateSuspended=*/FALSE,
|
||||
/*DebuggerThread=*/DebuggerThread,
|
||||
/*SystemRoutine=*/PspSystemThreadStartup // should use XapiThreadStartup
|
||||
);
|
||||
}
|
||||
|
||||
static xbox::KDPC PsReaperDpc;
|
||||
xbox::void_xt xbox::PsInitSystem()
|
||||
{
|
||||
|
@ -324,57 +340,33 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
|
|||
KernelStackSize = RoundUp(KernelStackSize, PAGE_SIZE);
|
||||
hKernelStackSize = RoundUp(hKernelStackSize, PAGE_SIZE);
|
||||
|
||||
PETHREAD eThread;
|
||||
ntstatus_xt result = ObCreateObject(&PsThreadObjectType, zeroptr, sizeof(ETHREAD) + ThreadExtensionSize, reinterpret_cast<PVOID*>(&eThread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
// create thread, using our special proxy technique
|
||||
{
|
||||
PETHREAD eThread;
|
||||
ntstatus_xt result = ObCreateObject(&PsThreadObjectType, zeroptr, sizeof(ETHREAD) + ThreadExtensionSize, reinterpret_cast<PVOID *>(&eThread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
std::memset(eThread, 0, sizeof(ETHREAD) + ThreadExtensionSize);
|
||||
std::memset(eThread, 0, sizeof(ETHREAD) + ThreadExtensionSize);
|
||||
|
||||
// Create kernel stack for xbox title to able write on stack instead of host.
|
||||
PVOID KernelStack = MmCreateKernelStack(KernelStackSize, DebuggerThread);
|
||||
// Create kernel stack for xbox title to able write on stack instead of host.
|
||||
PVOID KernelStack = MmCreateKernelStack(KernelStackSize, DebuggerThread);
|
||||
|
||||
if (!KernelStack) {
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(X_STATUS_INSUFFICIENT_RESOURCES);
|
||||
}
|
||||
if (!KernelStack) {
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(X_STATUS_INSUFFICIENT_RESOURCES);
|
||||
}
|
||||
|
||||
// Start thread initialization process here before insert and create thread
|
||||
KeInitializeThread(&eThread->Tcb, KernelStack, KernelStackSize, TlsDataSize, SystemRoutine, StartRoutine, StartContext, &KiUniqueProcess);
|
||||
// Start thread initialization process here before insert and create thread
|
||||
KeInitializeThread(&eThread->Tcb, KernelStack, KernelStackSize, TlsDataSize, SystemRoutine, StartRoutine, StartContext, &KiUniqueProcess);
|
||||
|
||||
// Create binary_semaphore to allow new thread setup non-kthread switching.
|
||||
std::binary_semaphore signal{0};
|
||||
|
||||
// PCSTProxy is responsible for cleaning up this pointer
|
||||
PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam;
|
||||
iPCSTProxyParam->Ethread = eThread;
|
||||
iPCSTProxyParam->TlsDataSize = TlsDataSize;
|
||||
iPCSTProxyParam->signal = &signal;
|
||||
|
||||
unsigned int hThreadId;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, hKernelStackSize, PCSTProxy, iPCSTProxyParam, NULL, &hThreadId));
|
||||
if (handle == zeroptr) {
|
||||
delete iPCSTProxyParam;
|
||||
MmDeleteKernelStack(eThread->Tcb.StackBase, eThread->Tcb.StackLimit);
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(X_STATUS_INSUFFICIENT_RESOURCES);
|
||||
}
|
||||
|
||||
// We are waiting on new thread's non-kthread switching process is done before we can continue on.
|
||||
signal.acquire();
|
||||
|
||||
// Increment the ref count of the thread once more. This is to guard against the case the title closes the thread handle
|
||||
// before this thread terminates with PsTerminateSystemThread
|
||||
// Test case: Amped
|
||||
ObfReferenceObject(eThread);
|
||||
|
||||
// The ob handle of the ethread obj is the thread id we return to the title
|
||||
result = ObInsertObject(eThread, zeroptr, 0, &eThread->UniqueThread);
|
||||
if (X_NT_SUCCESS(result)) {
|
||||
HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, hThreadId);
|
||||
assert(dupHandle);
|
||||
RegisterXboxHandle(eThread->UniqueThread, dupHandle);
|
||||
// The ob handle of the ethread obj is the thread id we return to the title
|
||||
result = ObInsertObject(eThread, zeroptr, 0, &eThread->UniqueThread);
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
if (g_iThreadNotificationCount) {
|
||||
PspCallThreadNotificationRoutines(eThread, TRUE);
|
||||
|
@ -382,37 +374,64 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
|
|||
|
||||
// Create another handle to pass back to the title in the ThreadHandle argument
|
||||
result = ObOpenObjectByPointer(eThread, &PsThreadObjectType, ThreadHandle);
|
||||
}
|
||||
|
||||
KeQuerySystemTime(&eThread->CreateTime);
|
||||
|
||||
if (X_NT_SUCCESS(result)) {
|
||||
RegisterXboxHandle(*ThreadHandle, handle);
|
||||
|
||||
g_AffinityPolicy->SetAffinityXbox(handle);
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
if (ThreadId != zeroptr) {
|
||||
*ThreadId = eThread->UniqueThread;
|
||||
}
|
||||
|
||||
// PCSTProxy is responsible for cleaning up this pointer
|
||||
PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam;
|
||||
iPCSTProxyParam->Ethread = eThread;
|
||||
iPCSTProxyParam->TlsDataSize = TlsDataSize;
|
||||
|
||||
unsigned int ThreadId;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, hKernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, &ThreadId));
|
||||
if (handle == zeroptr) {
|
||||
delete iPCSTProxyParam;
|
||||
ObpClose(eThread->UniqueThread);
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(X_STATUS_INSUFFICIENT_RESOURCES);
|
||||
}
|
||||
|
||||
// Increment the ref count of the thread once more. This is to guard against the case the title closes the thread handle
|
||||
// before this thread terminates with PsTerminateSystemThread
|
||||
// Test case: Amped
|
||||
ObfReferenceObject(eThread);
|
||||
|
||||
KeQuerySystemTime(&eThread->CreateTime);
|
||||
RegisterXboxHandle(*ThreadHandle, handle);
|
||||
HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId);
|
||||
assert(dupHandle);
|
||||
RegisterXboxHandle(eThread->UniqueThread, dupHandle);
|
||||
|
||||
eThread->Tcb.Priority = GetThreadPriority(handle);
|
||||
g_AffinityPolicy->SetAffinityXbox(handle);
|
||||
|
||||
// Wait for the initialization of the remaining thread state
|
||||
KeSuspendThreadEx(&eThread->Tcb);
|
||||
ResumeThread(handle);
|
||||
while (eThread->Tcb.State == Initialized) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
// Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED), then wait until the new thread has
|
||||
// finished initialization
|
||||
if (CreateSuspended) {
|
||||
KeSuspendThread(&eThread->Tcb);
|
||||
}
|
||||
|
||||
KeResumeThreadEx(&eThread->Tcb);
|
||||
|
||||
// Log ThreadID identical to how GetCurrentThreadID() is rendered :
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X], Native Handle : 0x%X, Native ThreadId : [0x%.4X]",
|
||||
*ThreadHandle, eThread->UniqueThread, handle, ThreadId);
|
||||
}
|
||||
else {
|
||||
eThread->Tcb.HasTerminated = true;
|
||||
}
|
||||
|
||||
// If thread is set to be terminated, allow thread to run and perform termination process from its own thread.
|
||||
if (eThread->Tcb.HasTerminated) {
|
||||
ResumeThread(handle);
|
||||
}
|
||||
// Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED)
|
||||
else if (!CreateSuspended) {
|
||||
ResumeThread(handle);
|
||||
}
|
||||
|
||||
RETURN(result);
|
||||
RETURN(X_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -487,10 +506,13 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread
|
|||
KeQuerySystemTime(&eThread->ExitTime);
|
||||
eThread->ExitStatus = ExitStatus;
|
||||
eThread->Tcb.Header.SignalState = 1;
|
||||
KiWaitListLock();
|
||||
if (!IsListEmpty(&eThread->Tcb.Header.WaitListHead)) {
|
||||
// TODO: Implement KiWaitTest's relative objects usage
|
||||
//KiWaitTest()
|
||||
assert(0);
|
||||
KiWaitTest((PVOID)&eThread->Tcb, 0);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
if (GetNativeHandle(eThread->UniqueThread)) {
|
||||
|
|
|
@ -26,3 +26,12 @@ namespace xbox
|
|||
{
|
||||
void_xt PsInitSystem();
|
||||
};
|
||||
|
||||
xbox::ntstatus_xt NTAPI CxbxrCreateThread
|
||||
(
|
||||
OUT xbox::PHANDLE ThreadHandle,
|
||||
OUT xbox::PHANDLE ThreadId OPTIONAL,
|
||||
IN xbox::PKSTART_ROUTINE StartRoutine,
|
||||
IN xbox::PVOID StartContext,
|
||||
IN xbox::boolean_xt DebuggerThread
|
||||
);
|
||||
|
|
|
@ -89,6 +89,11 @@ xbox::boolean_xt RtlpCaptureStackLimits(
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::RtlInitSystem()
|
||||
{
|
||||
xbox::RtlInitializeCriticalSection(&NtSystemTimeCritSec);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x0104 - RtlAnsiStringToUnicodeString()
|
||||
// ******************************************************************
|
||||
|
@ -108,23 +113,28 @@ XBSYSAPI EXPORTNUM(260) xbox::ntstatus_xt NTAPI xbox::RtlAnsiStringToUnicodeStri
|
|||
dword_xt total = RtlAnsiStringToUnicodeSize(SourceString);
|
||||
|
||||
if (total > 0xffff) {
|
||||
return X_STATUS_INVALID_PARAMETER_2;
|
||||
RETURN(X_STATUS_INVALID_PARAMETER_2);
|
||||
}
|
||||
|
||||
DestinationString->Length = (USHORT)(total - sizeof(WCHAR));
|
||||
if (AllocateDestinationString) {
|
||||
DestinationString->MaximumLength = (USHORT)total;
|
||||
if (!(DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag(total, 'grtS'))) {
|
||||
return X_STATUS_NO_MEMORY;
|
||||
if (!(DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag(total, 'grtS'))) {
|
||||
RETURN(X_STATUS_NO_MEMORY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (total > DestinationString->MaximumLength) {
|
||||
return X_STATUS_BUFFER_OVERFLOW;
|
||||
RETURN(X_STATUS_BUFFER_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
RtlMultiByteToUnicodeN((PWSTR)DestinationString->Buffer, (ULONG)DestinationString->Length, NULL, SourceString->Buffer, SourceString->Length);
|
||||
RtlMultiByteToUnicodeN((PWSTR)DestinationString->Buffer,
|
||||
(ULONG)DestinationString->Length,
|
||||
NULL,
|
||||
SourceString->Buffer,
|
||||
SourceString->Length);
|
||||
|
||||
DestinationString->Buffer[DestinationString->Length / sizeof(WCHAR)] = 0;
|
||||
|
||||
RETURN(X_STATUS_SUCCESS);
|
||||
|
@ -521,23 +531,31 @@ XBSYSAPI EXPORTNUM(270) xbox::long_xt NTAPI xbox::RtlCompareString
|
|||
LOG_FUNC_ARG(CaseInSensitive)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LONG result;
|
||||
const USHORT l1 = String1->Length;
|
||||
const USHORT l2 = String2->Length;
|
||||
const USHORT maxLen = (l1 <= l2 ? l1 : l2);
|
||||
|
||||
USHORT l1 = String1->Length;
|
||||
USHORT l2 = String2->Length;
|
||||
USHORT maxLen = l1 <= l2 ? l1 : l2;
|
||||
|
||||
CHAR *str1 = String1->Buffer;
|
||||
CHAR *str2 = String2->Buffer;
|
||||
const PCHAR str1 = String1->Buffer;
|
||||
const PCHAR str2 = String2->Buffer;
|
||||
|
||||
if (CaseInSensitive) {
|
||||
result = _strnicmp(str1, str2, maxLen);
|
||||
for (unsigned i = 0; i < maxLen; i++) {
|
||||
UCHAR char1 = RtlLowerChar(str1[i]);
|
||||
UCHAR char2 = RtlLowerChar(str2[i]);
|
||||
if (char1 != char2) {
|
||||
RETURN(char1 - char2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = strncmp(str1, str2, maxLen);
|
||||
for (unsigned i = 0; i < maxLen; i++) {
|
||||
if (str1[i] != str2[i]) {
|
||||
RETURN(str1[i] - str2[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(result);
|
||||
RETURN(l1 - l2);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -556,23 +574,32 @@ XBSYSAPI EXPORTNUM(271) xbox::long_xt NTAPI xbox::RtlCompareUnicodeString
|
|||
LOG_FUNC_ARG(CaseInSensitive)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LONG result;
|
||||
const USHORT l1 = String1->Length;
|
||||
const USHORT l2 = String2->Length;
|
||||
const USHORT maxLen = (l1 <= l2 ? l1 : l2) / sizeof(WCHAR);
|
||||
|
||||
USHORT l1 = String1->Length;
|
||||
USHORT l2 = String2->Length;
|
||||
USHORT maxLen = l1 <= l2 ? l1 : l2;
|
||||
|
||||
WCHAR *str1 = (WCHAR*)(String1->Buffer);
|
||||
WCHAR *str2 = (WCHAR*)(String2->Buffer);
|
||||
const wchar_xt* str1 = String1->Buffer;
|
||||
const wchar_xt* str2 = String2->Buffer;
|
||||
|
||||
if (CaseInSensitive) {
|
||||
result = _wcsnicmp(str1, str2, maxLen);
|
||||
for (unsigned i = 0; i < maxLen; i++) {
|
||||
wchar_xt char1 = towlower(str1[i]);
|
||||
wchar_xt char2 = towlower(str2[i]);
|
||||
|
||||
if (char1 != char2) {
|
||||
RETURN(char1 - char2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = wcsncmp(str1, str2, maxLen);
|
||||
for (unsigned i = 0; i < maxLen; i++) {
|
||||
if (str1[i] != str2[i]) {
|
||||
RETURN(str1[i] - str2[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(result);
|
||||
RETURN(l1 - l2);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -652,7 +679,7 @@ XBSYSAPI EXPORTNUM(274) xbox::boolean_xt NTAPI xbox::RtlCreateUnicodeString
|
|||
BOOLEAN result = TRUE;
|
||||
|
||||
ULONG bufferSize = (std::u16string(SourceString).length() + 1) * sizeof(WCHAR);
|
||||
DestinationString->Buffer = (USHORT *)ExAllocatePoolWithTag(bufferSize, 'grtS');
|
||||
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag(bufferSize, 'grtS');
|
||||
if (!DestinationString->Buffer) {
|
||||
result = FALSE;
|
||||
}
|
||||
|
@ -700,7 +727,7 @@ XBSYSAPI EXPORTNUM(276) xbox::ntstatus_xt NTAPI xbox::RtlDowncaseUnicodeString
|
|||
|
||||
if (AllocateDestinationString) {
|
||||
DestinationString->MaximumLength = SourceString->Length;
|
||||
DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
if (DestinationString->Buffer == NULL) {
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
@ -1173,9 +1200,8 @@ XBSYSAPI EXPORTNUM(290) xbox::void_xt NTAPI xbox::RtlInitUnicodeString
|
|||
LOG_FUNC_ARG(SourceString)
|
||||
LOG_FUNC_END;
|
||||
|
||||
DestinationString->Buffer = (USHORT*)SourceString;
|
||||
DestinationString->Buffer = (wchar_xt*)SourceString;
|
||||
if (SourceString != NULL) {
|
||||
DestinationString->Buffer = (USHORT*)SourceString;
|
||||
DestinationString->Length = (USHORT)std::u16string(SourceString).length() * 2;
|
||||
DestinationString->MaximumLength = DestinationString->Length + 2;
|
||||
}
|
||||
|
@ -1523,6 +1549,7 @@ no_mapping:
|
|||
|
||||
#define TICKSPERSEC 10000000
|
||||
#define TICKSPERMSEC 10000
|
||||
#define MSECSPERSEC 1000
|
||||
#define SECSPERDAY 86400
|
||||
#define SECSPERHOUR 3600
|
||||
#define SECSPERMIN 60
|
||||
|
@ -1541,6 +1568,11 @@ no_mapping:
|
|||
#define SECS_1601_TO_1980 ((379 * 365 + 91) * (ULONGLONG)SECSPERDAY)
|
||||
#define TICKS_1601_TO_1980 (SECS_1601_TO_1980 * TICKSPERSEC)
|
||||
|
||||
const xbox::LARGE_INTEGER Magic10000 = { .QuadPart = 0xd1b71758e219652ci64 };
|
||||
#define SHIFT10000 13
|
||||
xbox::LARGE_INTEGER Magic86400000 = { .QuadPart = 0xc6d750ebfa67b90ei64 };
|
||||
#define SHIFT86400000 26
|
||||
|
||||
|
||||
static const int MonthLengths[2][MONSPERYEAR] =
|
||||
{
|
||||
|
@ -1594,26 +1626,31 @@ XBSYSAPI EXPORTNUM(304) xbox::boolean_xt NTAPI xbox::RtlTimeFieldsToTime
|
|||
OUT PLARGE_INTEGER Time
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(TimeFields)
|
||||
LOG_FUNC_ARG_OUT(Time)
|
||||
LOG_FUNC_END;
|
||||
|
||||
int month, year, cleaps, day;
|
||||
|
||||
/* FIXME: normalize the TIME_FIELDS structure here */
|
||||
/* No, native just returns 0 (error) if the fields are not */
|
||||
if (TimeFields->Millisecond < 0 || TimeFields->Millisecond > 999 ||
|
||||
TimeFields->Second < 0 || TimeFields->Second > 59 ||
|
||||
TimeFields->Minute < 0 || TimeFields->Minute > 59 ||
|
||||
TimeFields->Hour < 0 || TimeFields->Hour > 23 ||
|
||||
TimeFields->Month < 1 || TimeFields->Month > 12 ||
|
||||
TimeFields->Day < 1 ||
|
||||
TimeFields->Day > MonthLengths
|
||||
[TimeFields->Month == 2 || IsLeapYear(TimeFields->Year)]
|
||||
[TimeFields->Month - 1] ||
|
||||
TimeFields->Year < 1601)
|
||||
/* Verify each TimeFields' variables are within range */
|
||||
if (TimeFields->Millisecond < 0 || TimeFields->Millisecond > 999) {
|
||||
return FALSE;
|
||||
}
|
||||
if (TimeFields->Second < 0 || TimeFields->Second > 59) {
|
||||
return FALSE;
|
||||
}
|
||||
if (TimeFields->Minute < 0 || TimeFields->Minute > 59) {
|
||||
return FALSE;
|
||||
}
|
||||
if (TimeFields->Hour < 0 || TimeFields->Hour > 23) {
|
||||
return FALSE;
|
||||
}
|
||||
if (TimeFields->Month < 1 || TimeFields->Month > 12) {
|
||||
return FALSE;
|
||||
}
|
||||
if (TimeFields->Day < 1 ||
|
||||
TimeFields->Day > MonthLengths[IsLeapYear(TimeFields->Year)][TimeFields->Month - 1]) {
|
||||
return FALSE;
|
||||
}
|
||||
if (TimeFields->Year < 1601) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* now calculate a day count from the date
|
||||
* First start counting years from March. This way the leap days
|
||||
|
@ -1629,20 +1666,21 @@ XBSYSAPI EXPORTNUM(304) xbox::boolean_xt NTAPI xbox::RtlTimeFieldsToTime
|
|||
month = TimeFields->Month + 1;
|
||||
year = TimeFields->Year;
|
||||
}
|
||||
cleaps = (3 * (year / 100) + 3) / 4; /* nr of "century leap years"*/
|
||||
day = (36525 * year) / 100 - cleaps + /* year * dayperyr, corrected */
|
||||
(1959 * month) / 64 + /* months * daypermonth */
|
||||
TimeFields->Day - /* day of the month */
|
||||
584817; /* zero that on 1601-01-01 */
|
||||
cleaps = (3 * (year / 100) + 3) / 4; /* nr of "century leap years"*/
|
||||
day = (36525 * year) / 100 - cleaps + /* year * DayPerYear, corrected */
|
||||
(1959 * month) / 64 + /* months * DayPerMonth */
|
||||
TimeFields->Day - /* day of the month */
|
||||
584817; /* zero that on 1601-01-01 */
|
||||
/* done */
|
||||
|
||||
Time->QuadPart = (((((LONGLONG)day * HOURSPERDAY +
|
||||
TimeFields->Hour) * MINSPERHOUR +
|
||||
TimeFields->Minute) * SECSPERMIN +
|
||||
TimeFields->Second) * 1000 +
|
||||
TimeFields->Millisecond) * TICKSPERMSEC;
|
||||
/* Convert into Time format */
|
||||
Time->QuadPart = day * HOURSPERDAY;
|
||||
Time->QuadPart = (Time->QuadPart + TimeFields->Hour) * MINSPERHOUR;
|
||||
Time->QuadPart = (Time->QuadPart + TimeFields->Minute) * SECSPERMIN;
|
||||
Time->QuadPart = (Time->QuadPart + TimeFields->Second) * MSECSPERSEC;
|
||||
Time->QuadPart = (Time->QuadPart + TimeFields->Millisecond) * TICKSPERMSEC;
|
||||
|
||||
RETURN(TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1654,36 +1692,27 @@ XBSYSAPI EXPORTNUM(305) xbox::void_xt NTAPI xbox::RtlTimeToTimeFields
|
|||
OUT PTIME_FIELDS TimeFields
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Time)
|
||||
LOG_FUNC_ARG_OUT(TimeFields)
|
||||
LOG_FUNC_END;
|
||||
LONGLONG Days, cleaps, years, yearday, months;
|
||||
|
||||
int SecondsInDay;
|
||||
long int cleaps, years, yearday, months;
|
||||
long int Days;
|
||||
LONGLONG _Time;
|
||||
|
||||
/* Extract millisecond from time and convert time into seconds */
|
||||
TimeFields->Millisecond =
|
||||
(cshort_xt)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
|
||||
_Time = Time->QuadPart / TICKSPERSEC;
|
||||
|
||||
/* The native version of RtlTimeToTimeFields does not take leap seconds
|
||||
* into account */
|
||||
|
||||
/* Split the time into days and seconds within the day */
|
||||
Days = (long int )(_Time / SECSPERDAY);
|
||||
SecondsInDay = _Time % SECSPERDAY;
|
||||
/* Extract milliseconds from time and days from milliseconds */
|
||||
// NOTE: Reverse engineered native kernel uses RtlExtendedMagicDivide calls.
|
||||
// Using similar code of ReactOS does not emulate native kernel's implement for
|
||||
// one increment over large integer's max value.
|
||||
xbox::LARGE_INTEGER MillisecondsTotal = RtlExtendedMagicDivide(*Time, Magic10000, SHIFT10000);
|
||||
Days = RtlExtendedMagicDivide(MillisecondsTotal, Magic86400000, SHIFT86400000).u.LowPart;
|
||||
MillisecondsTotal.QuadPart -= Days * SECSPERDAY * MSECSPERSEC;
|
||||
|
||||
/* compute time of day */
|
||||
TimeFields->Hour = (cshort_xt)(SecondsInDay / SECSPERHOUR);
|
||||
SecondsInDay = SecondsInDay % SECSPERHOUR;
|
||||
TimeFields->Minute = (cshort_xt)(SecondsInDay / SECSPERMIN);
|
||||
TimeFields->Second = (cshort_xt)(SecondsInDay % SECSPERMIN);
|
||||
TimeFields->Millisecond = MillisecondsTotal.u.LowPart % MSECSPERSEC;
|
||||
dword_xt RemainderTime = MillisecondsTotal.u.LowPart / MSECSPERSEC;
|
||||
TimeFields->Second = RemainderTime % SECSPERMIN;
|
||||
RemainderTime /= SECSPERMIN;
|
||||
TimeFields->Minute = RemainderTime % MINSPERHOUR;
|
||||
RemainderTime /= MINSPERHOUR;
|
||||
TimeFields->Hour = RemainderTime; // NOTE: Remaining hours did not received 24 hours modulo treatment.
|
||||
|
||||
/* compute day of week */
|
||||
TimeFields->Weekday = (cshort_xt)((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
|
||||
TimeFields->Weekday = (EPOCHWEEKDAY + Days) % DAYSPERWEEK;
|
||||
|
||||
/* compute year, month and day of month. */
|
||||
cleaps = (3 * ((4 * Days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
|
||||
|
@ -1692,7 +1721,7 @@ XBSYSAPI EXPORTNUM(305) xbox::void_xt NTAPI xbox::RtlTimeToTimeFields
|
|||
yearday = Days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
|
||||
months = (64 * yearday) / 1959;
|
||||
/* the result is based on a year starting on March.
|
||||
* To convert take 12 from Januari and Februari and
|
||||
* To convert take 12 from January and February and
|
||||
* increase the year by one. */
|
||||
if (months < 14) {
|
||||
TimeFields->Month = (USHORT)(months - 1);
|
||||
|
@ -2025,7 +2054,7 @@ XBSYSAPI EXPORTNUM(314) xbox::ntstatus_xt NTAPI xbox::RtlUpcaseUnicodeString
|
|||
|
||||
if (AllocateDestinationString) {
|
||||
DestinationString->MaximumLength = SourceString->Length;
|
||||
DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
if (DestinationString->Buffer == NULL) {
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
@ -2219,6 +2248,11 @@ XBSYSAPI EXPORTNUM(319) xbox::ulong_xt NTAPI xbox::RtlWalkFrameChain
|
|||
ulong_ptr_xt NewStack = *(ulong_ptr_xt*)Stack;
|
||||
ulong_xt Eip = *(ulong_ptr_xt*)(Stack + sizeof(ulong_ptr_xt));
|
||||
|
||||
/* Check if Eip is not below executable's dos header */
|
||||
if (Eip < KiB(64)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if the new pointer is above the old one and past the end */
|
||||
if (!((Stack < NewStack) && (NewStack < StackEnd))) {
|
||||
/* Stop searching after this entry */
|
||||
|
|
|
@ -41,13 +41,19 @@ XBSYSAPI EXPORTNUM(321) xbox::XBOX_KEY_DATA xbox::XboxEEPROMKey = { 0 };
|
|||
// ******************************************************************
|
||||
// * 0x0142 - XboxHardwareInfo
|
||||
// ******************************************************************
|
||||
// TODO: The main goal is to completely unset custom init values and have
|
||||
// them set from kernel's initialization end and device classes.
|
||||
// Although, device classes does not really set this value but read
|
||||
// from PCI space for Gpu rev, Mcp rev, and possibility INTERNAL_USB flag.
|
||||
XBSYSAPI EXPORTNUM(322) xbox::XBOX_HARDWARE_INFO xbox::XboxHardwareInfo =
|
||||
{
|
||||
0xC0000031, // Flags: 1=INTERNAL_USB, 2=DEVKIT, 4=MACROVISION, 8=CHIHIRO
|
||||
0xA2, // GpuRevision, byte read from NV2A first register, at 0xFD0000000 - see NV_PMC_BOOT_0
|
||||
0xD3, // McpRevision, Retail 1.6 - see https://github.com/JayFoxRox/xqemu-jfr/wiki/MCPX-and-bootloader
|
||||
0, // unknown
|
||||
0 // unknown
|
||||
// TODO: What exactly 0xC0000030 flags are? Might need default to null then set them later properly.
|
||||
// NOTE: Will be set by src/devices/Xbox.cpp and maybe other file(s)...
|
||||
.Flags = 0xC0000030, // Flags: 1=INTERNAL_USB, 2=DEVKIT, 4=MACROVISION, 8=CHIHIRO
|
||||
.GpuRevision = 0xD3, // GpuRevision, byte read from NV2A first register, at 0xFD0000000 - see NV_PMC_BOOT_0
|
||||
.McpRevision = 0, // NOTE: Will be set by src/devices/Xbox.cpp file.
|
||||
.Unknown3 = 0, // unknown
|
||||
.Unknown4 = 0 // unknown
|
||||
};
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -99,9 +99,6 @@ bool g_bIsChihiro = false;
|
|||
bool g_bIsDevKit = false;
|
||||
bool g_bIsRetail = false;
|
||||
|
||||
// Indicates to disable/enable all interrupts when cli and sti instructions are executed
|
||||
std::atomic_bool g_bEnableAllInterrupts = true;
|
||||
|
||||
// Set by the VMManager during initialization. Exported because it's needed in other parts of the emu
|
||||
size_t g_SystemMaxMemory = 0;
|
||||
|
||||
|
@ -113,6 +110,8 @@ ULONG g_CxbxFatalErrorCode = FATAL_ERROR_NONE;
|
|||
// Define function located in EmuXApi so we can call it from here
|
||||
void SetupXboxDeviceTypes();
|
||||
|
||||
extern xbox::void_xt NTAPI system_events(xbox::PVOID arg);
|
||||
|
||||
void SetupPerTitleKeys()
|
||||
{
|
||||
// Generate per-title keys from the XBE Certificate
|
||||
|
@ -329,64 +328,6 @@ void InitSoftwareInterrupts()
|
|||
}
|
||||
#endif
|
||||
|
||||
static xbox::void_xt NTAPI CxbxKrnlInterruptThread(xbox::PVOID param)
|
||||
{
|
||||
CxbxSetThreadName("CxbxKrnl Interrupts");
|
||||
|
||||
#if 0
|
||||
InitSoftwareInterrupts();
|
||||
#endif
|
||||
|
||||
std::mutex m;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
while (true) {
|
||||
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
|
||||
// If the interrupt is pending and connected, process it
|
||||
if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
|
||||
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
|
||||
}
|
||||
}
|
||||
g_InterruptSignal.wait(lock, []() { return g_AnyInterruptAsserted.load() && g_bEnableAllInterrupts.load(); });
|
||||
g_AnyInterruptAsserted = false;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static void CxbxKrnlClockThread(void* pVoid)
|
||||
{
|
||||
LARGE_INTEGER CurrentTicks;
|
||||
uint64_t Delta;
|
||||
uint64_t Microseconds;
|
||||
unsigned int IncrementScaling;
|
||||
static uint64_t LastTicks = 0;
|
||||
static uint64_t Error = 0;
|
||||
static uint64_t UnaccountedMicroseconds = 0;
|
||||
|
||||
// This keeps track of how many us have elapsed between two cycles, so that the xbox clocks are updated
|
||||
// with the proper increment (instead of blindly adding a single increment at every step)
|
||||
|
||||
if (LastTicks == 0) {
|
||||
QueryPerformanceCounter(&CurrentTicks);
|
||||
LastTicks = CurrentTicks.QuadPart;
|
||||
CurrentTicks.QuadPart = 0;
|
||||
}
|
||||
|
||||
QueryPerformanceCounter(&CurrentTicks);
|
||||
Delta = CurrentTicks.QuadPart - LastTicks;
|
||||
LastTicks = CurrentTicks.QuadPart;
|
||||
|
||||
Error += (Delta * SCALE_S_IN_US);
|
||||
Microseconds = Error / HostQPCFrequency;
|
||||
Error -= (Microseconds * HostQPCFrequency);
|
||||
|
||||
UnaccountedMicroseconds += Microseconds;
|
||||
IncrementScaling = (unsigned int)(UnaccountedMicroseconds / 1000); // -> 1 ms = 1000us -> time between two xbox clock interrupts
|
||||
UnaccountedMicroseconds -= (IncrementScaling * 1000);
|
||||
|
||||
xbox::KiClockIsr(IncrementScaling);
|
||||
}
|
||||
|
||||
void MapThunkTable(uint32_t* kt, uint32_t* pThunkTable)
|
||||
{
|
||||
const bool SendDebugReports = (pThunkTable == CxbxKrnl_KernelThunkTable) && CxbxDebugger::CanReport();
|
||||
|
@ -542,7 +483,10 @@ static void CxbxrKrnlSetupMemorySystem(int BootFlags, unsigned emulate_system, u
|
|||
}
|
||||
}
|
||||
|
||||
static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems, blocks_reserved_t blocks_reserved)
|
||||
static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
|
||||
unsigned& reserved_systems,
|
||||
blocks_reserved_t blocks_reserved,
|
||||
HardwareModel &hardwareModel)
|
||||
{
|
||||
unsigned int emulate_system = 0;
|
||||
// Get title path :
|
||||
|
@ -640,7 +584,7 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
}
|
||||
#endif // Chihiro wip block
|
||||
|
||||
CxbxKrnl_Xbe = new Xbe(xbePath.c_str(), false); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock()
|
||||
CxbxKrnl_Xbe = new Xbe(xbePath.c_str()); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock()
|
||||
|
||||
if (CxbxKrnl_Xbe->HasFatalError()) {
|
||||
CxbxrAbort(CxbxKrnl_Xbe->GetError().c_str());
|
||||
|
@ -683,6 +627,7 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
// Detect XBE type :
|
||||
XbeType xbeType = CxbxKrnl_Xbe->GetXbeType();
|
||||
EmuLogInit(LOG_LEVEL::INFO, "Auto detect: XbeType = %s", GetXbeTypeToStr(xbeType));
|
||||
|
||||
// Convert XBE type into corresponding system to emulate.
|
||||
switch (xbeType) {
|
||||
case XbeType::xtChihiro:
|
||||
|
@ -696,6 +641,10 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
break;
|
||||
DEFAULT_UNREACHABLE;
|
||||
}
|
||||
|
||||
if (std::filesystem::exists(xbeDirectory / "boot.id")) {
|
||||
emulate_system = SYSTEM_CHIHIRO;
|
||||
}
|
||||
}
|
||||
|
||||
EmuLogInit(LOG_LEVEL::INFO, "Host's compatible system types: %2X", reserved_systems);
|
||||
|
@ -717,6 +666,17 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
g_bIsChihiro = (emulate_system == SYSTEM_CHIHIRO);
|
||||
g_bIsDevKit = (emulate_system == SYSTEM_DEVKIT);
|
||||
g_bIsRetail = (emulate_system == SYSTEM_XBOX);
|
||||
if (g_bIsChihiro) {
|
||||
hardwareModel = HardwareModel::Chihiro_Type1; // TODO: Make configurable to support Type-3 console.
|
||||
}
|
||||
else if (g_bIsDevKit) {
|
||||
hardwareModel = HardwareModel::DebugKit_r1_2; // Unlikely need to make configurable.
|
||||
}
|
||||
// Retail (default)
|
||||
else {
|
||||
// Should not be configurable. Otherwise, titles compiled with newer XDK will patch older xbox kernel.
|
||||
hardwareModel = HardwareModel::Revision1_6;
|
||||
}
|
||||
|
||||
#ifdef CHIHIRO_WORK
|
||||
// If this is a Chihiro title, we need to patch the init flags to disable HDD setup
|
||||
|
@ -934,7 +894,8 @@ static bool CxbxrKrnlPrepareXbeMap()
|
|||
Xbe::Header* XbeHeader,
|
||||
uint32_t XbeHeaderSize,
|
||||
void (*Entry)(),
|
||||
int BootFlags
|
||||
int BootFlags,
|
||||
HardwareModel hardwareModel
|
||||
);
|
||||
|
||||
void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_reserved)
|
||||
|
@ -1088,7 +1049,8 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res
|
|||
// using XeLoadImage from LaunchDataPage->Header.szLaunchPath
|
||||
|
||||
// Now we can load the XBE :
|
||||
if (!CxbxrKrnlXbeSystemSelector(BootFlags, reserved_systems, blocks_reserved)) {
|
||||
HardwareModel hardwareModel = HardwareModel::Revision1_6; // It is configured by CxbxrKrnlXbeSystemSelector function according to console type.
|
||||
if (!CxbxrKrnlXbeSystemSelector(BootFlags, reserved_systems, blocks_reserved, hardwareModel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1116,7 +1078,8 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res
|
|||
(Xbe::Header*)CxbxKrnl_Xbe->m_Header.dwBaseAddr,
|
||||
CxbxKrnl_Xbe->m_Header.dwSizeofHeaders,
|
||||
(void(*)())EntryPoint,
|
||||
BootFlags
|
||||
BootFlags,
|
||||
hardwareModel
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1141,7 +1104,10 @@ static void CxbxrLogDumpXbeInfo(Xbe::LibraryVersion* libVersionInfo)
|
|||
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID : %s", FormatTitleId(g_pCertificate->dwTitleId).c_str());
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID (Hex) : 0x%s", titleIdHex.str().c_str());
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE Version : 1.%02d", g_pCertificate->dwVersion);
|
||||
if (CxbxKrnl_Xbe != nullptr) {
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE Version : %d.%d", CxbxKrnl_Xbe->GetDiscVersion(), CxbxKrnl_Xbe->GetPatchVersion());
|
||||
}
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE Version (Hex): %08X", g_pCertificate->dwVersion);
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE TitleName : %.40ls", g_pCertificate->wsTitleName);
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE Region : %s", CxbxKrnl_Xbe->GameRegionToString());
|
||||
}
|
||||
|
@ -1175,7 +1141,8 @@ static void CxbxrKrnlInitHacks()
|
|||
Xbe::Header *pXbeHeader,
|
||||
uint32_t dwXbeHeaderSize,
|
||||
void(*Entry)(),
|
||||
int BootFlags)
|
||||
int BootFlags,
|
||||
HardwareModel hardwareModel)
|
||||
{
|
||||
unsigned Host2XbStackBaseReserved = 0;
|
||||
__asm mov Host2XbStackBaseReserved, esp;
|
||||
|
@ -1199,11 +1166,11 @@ static void CxbxrKrnlInitHacks()
|
|||
g_pCertificate = &CxbxKrnl_Xbe->m_Certificate;
|
||||
|
||||
// Initialize timer subsystem
|
||||
Timer_Init();
|
||||
timer_init();
|
||||
// for unicode conversions
|
||||
setlocale(LC_ALL, "English");
|
||||
// Initialize time-related variables for the kernel and the timers
|
||||
CxbxInitPerformanceCounters();
|
||||
// Initialize DPC global
|
||||
InitDpcData();
|
||||
#ifdef _DEBUG
|
||||
// PopupCustom(LOG_LEVEL::INFO, "Attach a Debugger");
|
||||
// Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool
|
||||
|
@ -1341,10 +1308,11 @@ static void CxbxrKrnlInitHacks()
|
|||
}
|
||||
xbox::PsInitSystem();
|
||||
xbox::KiInitSystem();
|
||||
|
||||
xbox::RtlInitSystem();
|
||||
|
||||
// initialize graphics
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window.");
|
||||
CxbxInitWindow(true);
|
||||
CxbxInitWindow();
|
||||
|
||||
// Now process the boot flags to see if there are any special conditions to handle
|
||||
if (BootFlags & BOOT_EJECT_PENDING) {} // TODO
|
||||
|
@ -1380,7 +1348,7 @@ static void CxbxrKrnlInitHacks()
|
|||
SetupXboxDeviceTypes();
|
||||
}
|
||||
|
||||
InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable
|
||||
InitXboxHardware(hardwareModel);
|
||||
|
||||
// Read Xbox video mode from the SMC, store it in HalBootSMCVideoMode
|
||||
xbox::HalReadSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_AV_PACK, FALSE, (xbox::PULONG)&xbox::HalBootSMCVideoMode);
|
||||
|
@ -1406,6 +1374,29 @@ static void CxbxrKrnlInitHacks()
|
|||
// See: https://multimedia.cx/eggs/xbox-sphinx-protocol/
|
||||
ApplyMediaPatches();
|
||||
|
||||
// Verify that the emulator region matches the game region, if not, show a warning
|
||||
// that it may not work.
|
||||
if (!(g_pCertificate->dwGameRegion & EEPROM->EncryptedSettings.GameRegion))
|
||||
{
|
||||
auto expected = CxbxKrnl_Xbe->GameRegionToString();
|
||||
auto actual = CxbxKrnl_Xbe->GameRegionToString(EEPROM->EncryptedSettings.GameRegion);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "The loaded title is designed for region: " << expected << "\n";
|
||||
ss << "However Cxbx-Reloaded is configured as: " << actual << "\n\n";
|
||||
ss << "This means that you may encounter emulation issues\n\n";
|
||||
ss << "You can fix this by changing your emulated Xbox region in EEPROM Settings\n\n";
|
||||
ss << "Please do not submit bug reports that result from incorrect region flags\n\n";
|
||||
ss << "Would you like to attempt emulation anyway?";
|
||||
|
||||
PopupReturn ret = PopupWarningEx(nullptr, PopupButtons::YesNo, PopupReturn::No, ss.str().c_str());
|
||||
if (ret != PopupReturn::Yes)
|
||||
{
|
||||
CxbxrShutDown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Chihiro games require more patches
|
||||
// The chihiro BIOS does this to bypass XAPI cache init
|
||||
if (g_bIsChihiro) {
|
||||
|
@ -1430,23 +1421,17 @@ static void CxbxrKrnlInitHacks()
|
|||
#endif
|
||||
|
||||
EmuX86_Init();
|
||||
// Create the interrupt processing thread
|
||||
// Start the event thread
|
||||
xbox::HANDLE hThread;
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxKrnlInterruptThread, xbox::zeroptr, FALSE);
|
||||
// Start the kernel clock thread
|
||||
TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", true);
|
||||
Timer_Start(KernelClockThr, SCALE_MS_IN_NS);
|
||||
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, system_events, xbox::zeroptr, FALSE);
|
||||
// Launch the xbe
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE);
|
||||
|
||||
EmuKeFreePcr();
|
||||
__asm add esp, Host2XbStackSizeReserved;
|
||||
|
||||
// This will wait forever
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [] { return false; });
|
||||
xbox::KeRaiseIrqlToDpcLevel();
|
||||
while (true) {
|
||||
xbox::KeWaitForDpc();
|
||||
ExecuteDpcQueue();
|
||||
}
|
||||
}
|
||||
|
||||
// REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes
|
||||
|
@ -1468,7 +1453,7 @@ void CxbxrKrnlSuspendThreads()
|
|||
|
||||
// Don't use EmuKeGetPcr because that asserts kpcr
|
||||
xbox::KPCR* Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot));
|
||||
|
||||
|
||||
// If there's nothing in list entry, skip this step.
|
||||
if (!ThreadListEntry) {
|
||||
return;
|
||||
|
|
|
@ -155,7 +155,9 @@ void CxbxKrnlPanic();
|
|||
/*! empty function */
|
||||
void CxbxKrnlNoFunc();
|
||||
|
||||
void CxbxInitPerformanceCounters(); // Implemented in EmuKrnlKe.cpp
|
||||
void InitDpcData(); // Implemented in EmuKrnlKe.cpp
|
||||
bool IsDpcActive();
|
||||
void ExecuteDpcQueue();
|
||||
|
||||
/*! kernel thunk table */
|
||||
extern uint32_t CxbxKrnl_KernelThunkTable[379];
|
||||
|
|
|
@ -57,15 +57,8 @@ static void ApplyMediaPatches()
|
|||
| XBEIMAGE_MEDIA_TYPE_DVD_5_RO
|
||||
| XBEIMAGE_MEDIA_TYPE_DVD_9_RO
|
||||
| XBEIMAGE_MEDIA_TYPE_DVD_5_RW
|
||||
| XBEIMAGE_MEDIA_TYPE_DVD_9_RW
|
||||
;
|
||||
// Patch the XBE Header to allow running on all regions
|
||||
g_pCertificate->dwGameRegion = 0
|
||||
| XBEIMAGE_GAME_REGION_MANUFACTURING
|
||||
| XBEIMAGE_GAME_REGION_NA
|
||||
| XBEIMAGE_GAME_REGION_JAPAN
|
||||
| XBEIMAGE_GAME_REGION_RESTOFWORLD
|
||||
;
|
||||
| XBEIMAGE_MEDIA_TYPE_DVD_9_RW;
|
||||
|
||||
// Patch the XBE Security Flag
|
||||
// This field is only present if the Xbe Size is >= than our Certificate Structure
|
||||
// This works as our structure is large enough to fit the newer certificate size,
|
||||
|
|
|
@ -49,11 +49,6 @@ bool g_DisablePixelShaders = false;
|
|||
bool g_UseAllCores = false;
|
||||
bool g_SkipRdtscPatching = false;
|
||||
|
||||
// Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime
|
||||
// This shouldn't need to be atomic, but because raising the IRQL to high lv in KeSetSystemTime doesn't really stop KiClockIsr from running,
|
||||
// we need it for now to prevent reading a corrupted value while KeSetSystemTime is in the middle of updating it
|
||||
std::atomic_int64_t HostSystemTimeDelta(0);
|
||||
|
||||
// Static Function(s)
|
||||
static int ExitException(LPEXCEPTION_POINTERS e);
|
||||
|
||||
|
@ -124,7 +119,7 @@ bool EmuExceptionBreakpointAsk(LPEXCEPTION_POINTERS e)
|
|||
// We can skip Xbox as long as they are logged so we know about them
|
||||
// There's no need to prevent emulation, we can just pretend we have a debugger attached and continue
|
||||
// This is because some games (such as Crash Bandicoot) spam exceptions;
|
||||
e->ContextRecord->Eip += EmuX86_OpcodeSize((uint8_t*)e->ContextRecord->Eip); // Skip 1 size bytes
|
||||
e->ContextRecord->Eip += EmuX86_OpcodeSize((uint8_t*)e->ContextRecord->Eip); // Skip 1 instruction
|
||||
return true;
|
||||
|
||||
#if 1
|
||||
|
@ -157,19 +152,26 @@ bool EmuExceptionBreakpointAsk(LPEXCEPTION_POINTERS e)
|
|||
#endif
|
||||
}
|
||||
|
||||
void EmuExceptionNonBreakpointUnhandledShow(LPEXCEPTION_POINTERS e)
|
||||
bool EmuExceptionNonBreakpointUnhandledShow(LPEXCEPTION_POINTERS e)
|
||||
{
|
||||
EmuExceptionPrintDebugInformation(e, /*IsBreakpointException=*/false);
|
||||
|
||||
if (PopupFatalEx(nullptr, PopupButtons::OkCancel, PopupReturn::Ok,
|
||||
auto result = PopupFatalEx(nullptr, PopupButtons::AbortRetryIgnore, PopupReturn::Abort,
|
||||
" The running xbe has encountered an unhandled exception (Code := 0x%.8X) at address 0x%.08X.\n"
|
||||
"\n"
|
||||
" Press \"OK\" to terminate emulation.\n"
|
||||
" Press \"Cancel\" to debug.",
|
||||
e->ExceptionRecord->ExceptionCode, e->ContextRecord->Eip) == PopupReturn::Ok)
|
||||
{
|
||||
" Press \"Abort\" to terminate emulation.\n"
|
||||
" Press \"Retry\" to debug.\n"
|
||||
" Press \"Ignore\" to attempt to continue emulation.",
|
||||
e->ExceptionRecord->ExceptionCode, e->ContextRecord->Eip);
|
||||
|
||||
if (result == PopupReturn::Abort) {
|
||||
EmuExceptionExitProcess();
|
||||
} else if (result == PopupReturn::Ignore) {
|
||||
e->ContextRecord->Eip += EmuX86_OpcodeSize((uint8_t*)e->ContextRecord->Eip); // Skip 1 instruction
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns weither the given address is part of an Xbox managed memory region
|
||||
|
@ -185,7 +187,8 @@ bool IsXboxCodeAddress(xbox::addr_xt addr)
|
|||
#include "distorm.h"
|
||||
bool EmuX86_DecodeOpcode(const uint8_t* Eip, _DInst& info);
|
||||
void EmuX86_DistormLogInstruction(const uint8_t* Eip, _DInst& info, LOG_LEVEL log_level);
|
||||
void genericException(EXCEPTION_POINTERS *e) {
|
||||
bool genericException(EXCEPTION_POINTERS *e)
|
||||
{
|
||||
_DInst info;
|
||||
if (EmuX86_DecodeOpcode((uint8_t*)e->ContextRecord->Eip, info)) {
|
||||
EmuX86_DistormLogInstruction((uint8_t*)e->ContextRecord->Eip, info, LOG_LEVEL::FATAL);
|
||||
|
@ -200,11 +203,14 @@ void genericException(EXCEPTION_POINTERS *e) {
|
|||
}
|
||||
|
||||
// Bypass exception
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// notify user
|
||||
EmuExceptionNonBreakpointUnhandledShow(e);
|
||||
return EmuExceptionNonBreakpointUnhandledShow(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsRdtscInstruction(xbox::addr_xt addr); // Implemented in CxbxKrnl.cpp
|
||||
|
@ -270,8 +276,7 @@ bool EmuTryHandleException(EXCEPTION_POINTERS *e)
|
|||
|
||||
// Check if lle exception is already called first before emu exception.
|
||||
if (bOverrideEmuException) {
|
||||
genericException(e);
|
||||
return false;
|
||||
return genericException(e);
|
||||
}
|
||||
|
||||
if (e->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
|
||||
|
@ -309,10 +314,7 @@ bool EmuTryHandleException(EXCEPTION_POINTERS *e)
|
|||
}
|
||||
}
|
||||
|
||||
genericException(e);
|
||||
|
||||
// Unhandled exception :
|
||||
return false;
|
||||
return genericException(e);
|
||||
}
|
||||
|
||||
long WINAPI EmuException(struct _EXCEPTION_POINTERS* e)
|
||||
|
|
|
@ -68,9 +68,6 @@ extern HWND g_hEmuWindow;
|
|||
|
||||
extern HANDLE g_CurrentProcessHandle; // Set in CxbxKrnlMain
|
||||
|
||||
// Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime
|
||||
extern std::atomic_int64_t HostSystemTimeDelta;
|
||||
|
||||
typedef struct DUMMY_KERNEL
|
||||
{
|
||||
IMAGE_DOS_HEADER DosHeader;
|
||||
|
|
|
@ -770,6 +770,25 @@ void EmuGenerateFS(xbox::PETHREAD Ethread, unsigned Host2XbStackBaseReserved, un
|
|||
}
|
||||
#endif
|
||||
|
||||
// Initialize TlsData in order to avoid xbox's APIs attempt to try access null pointer from CxbxrCreateThread.
|
||||
// As far as I have seen, Xapi's CreateThread's startup function is the only one that does the tls' initialization.
|
||||
// It will repeat same process yet will not cause performance impact.
|
||||
// NOTE: PsCreateSystemThread's startup function does not do tls' initialization.
|
||||
if (Ethread->Tcb.TlsData) {
|
||||
Xbe::TLS* XbeTls = (Xbe::TLS*)CxbxKrnl_Xbe->m_Header.dwTLSAddr;
|
||||
uint32_t RawTlsDataSize = XbeTls->dwDataEndAddr - XbeTls->dwDataStartAddr;
|
||||
// First index is a pointer to the array of tls datas.
|
||||
xbox::addr_xt* TlsData = reinterpret_cast<xbox::addr_xt*>(Ethread->Tcb.TlsData);
|
||||
*TlsData = reinterpret_cast<xbox::addr_xt>(Ethread->Tcb.TlsData) + sizeof(xbox::addr_xt);
|
||||
// Set the actual tls data from xbe.
|
||||
TlsData += 1;
|
||||
std::memcpy(TlsData, reinterpret_cast<xbox::PVOID>(XbeTls->dwDataStartAddr), RawTlsDataSize);
|
||||
|
||||
if (XbeTls->dwSizeofZeroFill) {
|
||||
std::memset(reinterpret_cast<xbox::PBYTE>(TlsData) + RawTlsDataSize, 0, XbeTls->dwSizeofZeroFill);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize a fake PrcbData.CurrentThread
|
||||
{
|
||||
// TODO: Do we need NtTib's overwrite in ENABLE_KTHREAD_SWITCHING usage?
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include <ntstatus.h>
|
||||
#pragma warning(default:4005)
|
||||
#include "Logging.h"
|
||||
#include "common/util/strConverter.hpp" // utf16_to_ascii
|
||||
#include "common/util/cliConfig.hpp"
|
||||
#include "common/CxbxDebugger.h"
|
||||
#include "EmuShared.h"
|
||||
|
@ -790,19 +789,21 @@ std::string CxbxConvertXboxToHostPath(const std::string_view XboxDevicePath)
|
|||
std::wstring wXbePath;
|
||||
// We pretend to come from NtCreateFile to force symbolic link resolution
|
||||
CxbxConvertFilePath(XboxDevicePath.data(), wXbePath, &rootDirectoryHandle, "NtCreateFile");
|
||||
|
||||
// Convert Wide String as returned by above to a string, for XbePath
|
||||
std::string XbePath = utf16_to_ascii(wXbePath.c_str());
|
||||
std::filesystem::path XbePath;
|
||||
|
||||
// If the rootDirectoryHandle is not null, we have a relative path
|
||||
// We need to prepend the path of the root directory to get a full DOS path
|
||||
if (rootDirectoryHandle != nullptr) {
|
||||
char directoryPathBuffer[MAX_PATH];
|
||||
GetFinalPathNameByHandle(rootDirectoryHandle, directoryPathBuffer, MAX_PATH, VOLUME_NAME_DOS);
|
||||
XbePath = directoryPathBuffer + std::string("\\") + XbePath;
|
||||
WCHAR directoryPathBuffer[MAX_PATH];
|
||||
GetFinalPathNameByHandleW(rootDirectoryHandle, directoryPathBuffer, MAX_PATH, VOLUME_NAME_DOS);
|
||||
XbePath = directoryPathBuffer;
|
||||
XbePath /= wXbePath;
|
||||
}
|
||||
else {
|
||||
XbePath = wXbePath;
|
||||
}
|
||||
|
||||
return XbePath;
|
||||
return XbePath.string();
|
||||
}
|
||||
|
||||
int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile, std::size_t size)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue