Compare commits

..

No commits in common. "master" and "CI-6788bf1" have entirely different histories.

164 changed files with 4151 additions and 7731 deletions

111
.github/labeler.yml vendored
View file

@ -1,111 +1,74 @@
# Labels are in alphabetical order.
cmake:
- changed-files:
- any-glob-to-any-file:
- 'CMake*'
- '**/CMakeLists.txt'
- '**/*.cmake'
- 'CMake*'
- '**/CMakeLists.txt'
- '**/*.cmake'
cpu-emulation:
- changed-files:
- any-glob-to-any-file:
- 'src/devices/x86/**'
- 'src/devices/x86/**'
deployment:
- changed-files:
- any-glob-to-any-file:
- '*.yml'
- '.github/workflows/CI.yml'
- '*.yml'
- '.github/workflows/CI.yml'
file-system:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/support/EmuFile*'
- 'src/core/kernel/support/EmuFile*'
graphics:
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/D3D8/**'
- 'src/core/hle/XGRAPHIC/**'
- 'src/devices/video/**'
- 'src/gui/*Video*'
- 'src/core/hle/D3D8/**'
- 'src/core/hle/XGRAPHIC/**'
- 'src/devices/video/**'
- 'src/gui/*Video*'
HLE:
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/**'
- 'src/core/kernel/**'
- 'src/core/hle/**'
- 'src/core/kernel/**'
informational:
- changed-files:
- any-glob-to-any-file:
- '**/*Logging*'
- '**/*Logging*/**'
- '**/README.md'
- '**/*Logging*'
- '**/*Logging*/**'
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*'
- 'src/common/input/**'
- 'src/core/hle/XAPI/input/**'
- 'src/devices/usb/**'
- 'src/gui/controllers/**'
- 'src/gui/*Input*'
kernel:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/**'
- 'src/core/kernel/**'
LLE:
- changed-files:
- any-glob-to-any-file:
- 'src/devices/**'
- 'src/devices/**'
memory:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/memory-manager/**'
- 'src/core/kernel/memory-manager/**'
networking:
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/XONLINE/**'
- 'src/devices/network/**'
- 'src/gui/*Network*'
- 'src/core/hle/XONLINE/**'
- 'src/devices/network/**'
- 'src/gui/*Network*'
sound:
- changed-files:
- any-glob-to-any-file:
- 'src/common/audio/**'
- 'src/core/hle/DSOUND/**'
- 'src/core/hle/XACTENG/**'
- 'src/devices/audio/**'
- 'src/gui/*Audio*'
- 'src/common/audio/**'
- 'src/core/hle/DSOUND/**'
- 'src/core/hle/XACTENG/**'
- 'src/devices/audio/**'
- 'src/gui/*Audio*'
modules:
- changed-files:
- any-glob-to-any-file:
- 'import/**'
- 'import/**'
threading:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/support/EmuFS*'
- 'src/core/kernel/support/EmuFS*'
timing:
- changed-files:
- any-glob-to-any-file:
- 'src/common/Timer*'
- 'src/common/Timer*'
user interface:
- changed-files:
- any-glob-to-any-file:
- 'src/core/common/imgui/*'
- 'src/gui/**'
- 'src/core/common/imgui/*'
- 'src/gui/**'
xbdm:
- changed-files:
- any-glob-to-any-file:
- 'src/common/xbdm/**'
- 'src/common/xbdm/**'

View file

@ -30,21 +30,19 @@ jobs:
fail-fast: false
matrix:
configuration: [Release, Debug]
vsver: [2019]
winver: [7]
sdkver: [10.0.22621.0]
vsver: [2022, 2019]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Generate CMake files
run: cmake -B build -A Win32,version=${{ matrix.sdkver }} -DCMAKE_SYSTEM_VERSION=${{ matrix.winver }}
run: cmake -B build -A Win32
- 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@v4
- uses: actions/upload-artifact@v3
if: matrix.configuration == 'Release'
with:
name: CxbxReloaded-${{ matrix.configuration }}-VS${{ matrix.vsver }}
@ -59,9 +57,9 @@ jobs:
needs: build-windows
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
path: artifacts
- name: Re-zip artifacts
@ -74,7 +72,7 @@ jobs:
exit 1
fi
done
echo "tag_name=CI-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "::set-output name=tag_name::CI-${GITHUB_SHA::7}"
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -7,9 +7,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Automatically close issues that don't follow the issue template
uses: ergo720/auto-close-issues@v1
uses: ergo720/auto-close-issues@v1.0.4
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

View file

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Labeler
uses: actions/labeler@v5
uses: actions/labeler@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
sync-labels: true

12
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "import/subhook"]
path = import/subhook
url = https://github.com/Cxbx-Reloaded/subhook.git
url = https://github.com/Zeex/subhook.git
shallow = true
[submodule "import/cs_x86"]
path = import/cs_x86
@ -37,13 +37,5 @@
shallow = true
[submodule "import/libusb"]
path = import/libusb
url = https://github.com/Cxbx-Reloaded/libusb
branch = deadlock_fix
url = https://github.com/libusb/libusb
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

View file

@ -22,8 +22,6 @@ 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})
@ -41,9 +39,6 @@ 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
@ -54,16 +49,13 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/nv2a_vsh_cpu" EXCLUDE_FROM_AL
# Common (GUI and Emulator)
file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuRsa.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuSha.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/LibRc4.h"
"${CXBXR_ROOT_DIR}/src/common/CxbxDebugger.h"
"${CXBXR_ROOT_DIR}/src/common/cxbxr.hpp"
"${CXBXR_ROOT_DIR}/src/common/EmuEEPROM.h"
"${CXBXR_ROOT_DIR}/src/common/Error.h"
"${CXBXR_ROOT_DIR}/src/common/FilePaths.hpp"
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardCodes.h"
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.h"
"${CXBXR_ROOT_DIR}/src/common/input/layout_xbox_device.h"
@ -75,10 +67,13 @@ file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.h"
"${CXBXR_ROOT_DIR}/src/common/IPCHybrid.hpp"
"${CXBXR_ROOT_DIR}/src/common/Logging.h"
"${CXBXR_ROOT_DIR}/src/common/ReservedMemory.h"
"${CXBXR_ROOT_DIR}/src/common/Settings.hpp"
"${CXBXR_ROOT_DIR}/src/common/Timer.h"
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/CPUID.h"
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.h"
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.h"
"${CXBXR_ROOT_DIR}/src/common/util/std_extend.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/strConverter.hpp"
@ -86,20 +81,15 @@ file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/win32/AlignPrefix1.h"
"${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.h"
"${CXBXR_ROOT_DIR}/src/common/win32/Mutex.h"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.h"
"${CXBXR_ROOT_DIR}/src/common/win32/Util.h"
"${CXBXR_ROOT_DIR}/src/common/win32/WineEnv.h"
"${CXBXR_ROOT_DIR}/src/common/xbdm/CxbxXbdm.h"
"${CXBXR_ROOT_DIR}/src/common/xbe/Xbe.h"
"${CXBXR_ROOT_DIR}/src/common/xbe/XbePrinter.h"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Types.hpp"
"${CXBXR_ROOT_DIR}/src/common/xbox_types.h"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/buffered_io.h"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/xdvdfs.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Types.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.h"
"${CXBXR_ROOT_DIR}/src/Cxbx.h"
"${CXBXR_ROOT_DIR}/src/CxbxVersion.h"
"${CXBXR_ROOT_DIR}/src/version.h"
@ -132,12 +122,13 @@ file (GLOB CXBXR_HEADER_EMU_IMPORT
"${CXBXR_ROOT_DIR}/import/imgui/backends/imgui_impl_win32.h"
)
file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.h"
"${CXBXR_ROOT_DIR}/src/common/FilePaths.hpp"
"${CXBXR_ROOT_DIR}/src/common/audio/converter.hpp"
"${CXBXR_ROOT_DIR}/src/common/audio/XADPCM.h"
"${CXBXR_ROOT_DIR}/src/common/Timer.h"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/glextensions.h"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen.h"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.h"
"${CXBXR_ROOT_DIR}/src/common/audio/XADPCM.h"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/audio.hpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/ui.hpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/settings.h"
@ -145,7 +136,6 @@ 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"
@ -154,11 +144,13 @@ 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/VertexShaderCache.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Types.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPixelShader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPushBuffer.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbState.h"
@ -177,13 +169,14 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.hpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.hpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XONLINE/XOnline.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/common/strings.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlAvModes.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKe.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKi.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPs.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/init/CxbxKrnl.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/init/KrnlPatches.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PhysicalMemory.h"
@ -233,16 +226,13 @@ list(FILTER CXBXR_HEADER_HLSL INCLUDE REGEX ".*\\.hlsl$")
# Common (GUI and Emulator)
file (GLOB CXBXR_SOURCE_COMMON
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuRsa.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuSha.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/LibRc4.cpp"
"${CXBXR_ROOT_DIR}/src/common/CxbxDebugger.cpp"
"${CXBXR_ROOT_DIR}/src/common/cxbxr.cpp"
"${CXBXR_ROOT_DIR}/src/common/EmuEEPROM.cpp"
"${CXBXR_ROOT_DIR}/src/common/Error.cpp"
"${CXBXR_ROOT_DIR}/src/common/FilePaths.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.cpp"
@ -252,28 +242,25 @@ file (GLOB CXBXR_SOURCE_COMMON
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/Logging.cpp"
"${CXBXR_ROOT_DIR}/src/common/Settings.cpp"
"${CXBXR_ROOT_DIR}/src/common/Timer.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/hasher.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/InlineFunc.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/IPCWindows.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Mutex.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Util.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/WineEnv.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbdm/CxbxXbdm.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbe/Xbe.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbe/XbePrinter.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Types.cpp"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/buffered_io.cpp"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/xdvdfs.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXbox.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXc.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXe.cpp"
"${CXBXR_ROOT_DIR}/src/CxbxVersion.cpp"
"${CXBXR_ROOT_DIR}/src/HighPerformanceGraphicsEnabler.c"
)
@ -311,12 +298,12 @@ file (GLOB CXBXR_SOURCE_EMU_IMPORT
)
file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_KRNL_CPP}"
"${CXBXR_ROOT_DIR}/src/common/Timer.cpp"
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/VerifyAddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/glextensions.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen_common.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen_wgl.cpp"
"${CXBXR_ROOT_DIR}/src/common/VerifyAddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.cpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/audio.cpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/ui.cpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/video.cpp"
@ -327,10 +314,11 @@ 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/VertexShaderCache.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPixelShader.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPushBuffer.cpp"
@ -348,9 +336,9 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalDSVoice.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalStruct.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/JVS/JVS.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnl.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlAv.cpp"
@ -362,12 +350,16 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKd.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKe.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKi.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlMm.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlNt.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlOb.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPhy.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPs.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlRtl.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXbox.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXc.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXe.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/KernelThunk.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PhysicalMemory.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PoolManager.cpp"
@ -379,8 +371,6 @@ 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"
@ -443,10 +433,6 @@ 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)
@ -474,19 +460,14 @@ install(FILES ${cxbxr_INSTALL_files}
DESTINATION bin
)
# 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(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
)
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")

View file

@ -8,21 +8,18 @@ Cxbx-Reloaded is an emulator for running Microsoft Xbox (and eventually, Chihiro
## System Requirements
### Minimum
* OS: Windows 7+ x64, or x86-64 Linux with Wine. 32-bit is not supported.
* MacOS with Wine is known not to work, and BSD-based systems are untested.
* Also note that Wine is relatively unstable, and it might break compatibility with Cxbx-Reloaded at any time without warning.
* GPU: Direct3D 9.0c with Pixel Shader Model 2.x, and Vertex Shader Model 3.0.
## Prerequisites
### Windows
* [32-bit (x86) Visual C++ 2022 Redistributable](https://aka.ms/vs/17/release/vc_redist.x86.exe)
* [32-bit (x86) Visual C++ 2019 Redistributable](https://aka.ms/vs/16/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](https://github.com/libusb/libusb/wiki/Windows#Driver_Installation)
* WinUSB compliant driver
* *Optional, only needed for USB pass-through of original Xbox and Steel Battalion controllers.*
### Wine
**Please use the latest stable release version of Wine. If it does not work for you, then roll back to Wine 7.0 which is the last known working version.**<br/>
**There also exists this known [issue](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/2314) which currently prevents savings in some games with the most recent Wine 6.8 and later versions.**
**Please use the latest stable release version of Wine. If it does not work for you, then roll back to Wine 5.0.3 which is the last known working version.**
* Winetricks
* `vcrun2019`
* Requires the latest winetricks script.
@ -31,9 +28,9 @@ Cxbx-Reloaded is an emulator for running Microsoft Xbox (and eventually, Chihiro
* Winpcap is built-in, no installation is required.
## Automated Builds
Cxbx-Reloaded doesn't currently have stable builds, but you can obtain pre-release builds from our official website's download page, or the links below:
Cxbx-Reloaded doesn't currently have stable builds, but you can obtain pre-release builds from the Releases tab, or the links below:
* **[Release Builds](https://cxbx-reloaded.co.uk/download)**
* **[Release Builds](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/releases)**
* *[Full build history](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/actions?query=workflow%3A%22GitHub+CI%22)*
## Compatibility
@ -86,7 +83,7 @@ Please contact us before you start working on something, so we can make sure you
Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in the `build` directory.
##### Prerequisites
1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2022
1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2017 or later
* C++ and C# desktop development
* Windows Universal CRT SDK
* C++ CMake tools for Windows
@ -98,8 +95,12 @@ Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in th
2. `cd` to the Cxbx-Reloaded directory.
3. Run these commands.
1. `mkdir build & cd build`
2. `cmake .. -G "Visual Studio 17 2022" -A Win32`
* VS2022 17.0 or later is required.
2. `cmake .. -G "generator" -A Win32`
* Generators:
* `Visual Studio 17 2022`
* `Visual Studio 16 2019`
* VS2019 16.1 or later is required and has CMake 3.14 bundled.
* `Visual Studio 15 2017`
4. Open `Cxbx-Reloaded.sln` from the `build` directory.
5. Select the Release configuration, then click Build.
* Debug builds are **significantly slower, and only for developers**.
@ -114,5 +115,3 @@ You can support [Luke Usher](https://github.com/LukeUsher), initiator of Cxbx-Re
* All contributors to the original Cxbx and [Dxbx](https://github.com/PatrickvL/Dxbx) projects. Without them Cxbx-Reloaded would not exist at all.
* [XQEMU](https://github.com/xqemu/xqemu) - While the majority of Cxbx-R is our own work (Kernel, HLE, etc), the NV2A LLE and NVNet implementation are primarily the work of the XQEMU developers.
* [XboxDev](https://github.com/xboxdev) - Providing Xbox hardware research & useful tooling.
* [XbSymbolDatabase](https://github.com/Cxbx-Reloaded/XbSymbolDatabase) - Providing support to detect symbols across XDK builds from reverse engineered retail titles.
* [Xbox Kernel Test Suite](https://github.com/Cxbx-Reloaded/xbox_kernel_test_suite) - Making accurate tests on hardware to compare against cxbxr's kernel implementation.

2
import/SDL2 vendored

@ -1 +1 @@
Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd
Subproject commit 0e9560aea22818884921e5e5064953257bfe7fa7

@ -1 +1 @@
Subproject commit 774111351210e6f340246d6fb32741b09708f381
Subproject commit 75ce58fa8d135ef0a75bee729cde9542eda393b6

2
import/libusb vendored

@ -1 +1 @@
Subproject commit cf178f1fac38426990425cc034f7d4b8c9e1e388
Subproject commit c6a35c56016ea2ab2f19115d2ea1e85e0edae155

1
import/mio vendored

@ -1 +0,0 @@
Subproject commit 3f86a95c0784d73ce6815237ec33ed25f233b643

1
import/nv2a_vsh_cpu vendored

@ -1 +0,0 @@
Subproject commit ead0af5dee49e408c005151393f8a7dbcd27026c

2
import/subhook vendored

@ -1 +1 @@
Subproject commit 83d4e1ebef3588fae48b69a7352cc21801cb70bc
Subproject commit a86a253e6e764275a21ab7c9348f84d1f95e409f

View file

@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
project(cxbx)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
# Suppress extra stuff from generated solution
set(CMAKE_SUPPRESS_REGENERATION true)
@ -42,11 +42,8 @@ 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
@ -68,7 +65,7 @@ XXH_INLINE_ALL
)
file (GLOB RESOURCES
"${CXBXR_ROOT_DIR}/CONTRIBUTORS"
"${CXBXR_ROOT_DIR}/COPYING"
"${CXBXR_ROOT_DIR}/README.md"
@ -81,18 +78,24 @@ file (GLOB RESOURCES
"${CXBXR_ROOT_DIR}/src/.editorconfig"
)
source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
${CXBXR_HEADER_EMU_IMPORT}
)
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX header FILES
${CXBXR_HEADER_GUIv1}
${CXBXR_HEADER_COMMON}
${CXBXR_HEADER_EMU}
)
source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
${CXBXR_SOURCE_EMU_IMPORT}
)
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX source FILES
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX source FILES
${CXBXR_SOURCE_GUIv1}
${CXBXR_SOURCE_COMMON}
${CXBXR_SOURCE_EMU}
)
source_group(TREE ${CXBXR_ROOT_DIR} FILES ${RESOURCES})
@ -100,8 +103,12 @@ source_group(TREE ${CXBXR_ROOT_DIR} FILES ${RESOURCES})
add_executable(cxbx WIN32 ${RESOURCES}
${CXBXR_HEADER_GUIv1}
${CXBXR_HEADER_COMMON}
${CXBXR_HEADER_EMU_IMPORT}
${CXBXR_HEADER_EMU}
${CXBXR_SOURCE_GUIv1}
${CXBXR_SOURCE_COMMON}
${CXBXR_SOURCE_EMU_IMPORT}
${CXBXR_SOURCE_EMU}
${CXBXR_GIT_VERSION_H}
)
@ -111,7 +118,15 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/linker-options
set_target_properties(cxbx PROPERTIES
LINK_FLAGS "
/INCREMENTAL:NO \
/LARGEADDRESSAWARE \
/FIXED \
/SAFESEH:NO \
/DYNAMICBASE:NO \
/BASE:0x10000 \
/STACK:65536,65536 \
/NODEFAULTLIB:libcmt \
/DELAYLOAD:wpcap.dll \
"
LINK_FLAGS_RELEASE "
/LTCG \
@ -121,7 +136,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
# /Ob3 = Controls inline expansion of functions.
# /Ob2 = Controls inline expansion of functions.
# /Oi = Generate intrinsic functions
# /Ot = In favor of using fast code than small code
# /GL = Whole program optimization
@ -132,7 +147,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Set optimization options for release build
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
/Zi \
/Ob3 \
/Ob2 \
/Oi \
/Ot \
/GL \
@ -142,7 +157,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-"
@ -150,7 +165,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
endif()
# Windows libraries
set(WINS_LIB
set(WINS_LIB
legacy_stdio_definitions
d3d9
d3dcompiler
@ -170,7 +185,7 @@ set(WINS_LIB
comctl32
XINPUT9_1_0
Iphlpapi
Dwmapi
wpcap
)
target_link_libraries(cxbx
@ -180,7 +195,6 @@ target_link_libraries(cxbx
SDL2
imgui
libusb
mio::mio_min_winapi
${WINS_LIB}
)

View file

@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
project(cxbxr-emu)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
# Suppress extra stuff from generated solution
set(CMAKE_SUPPRESS_REGENERATION true)
@ -48,9 +48,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Use inline XXHash version
XXH_INLINE_ALL
# Enable Chihiro work
CHIHIRO_WORK
)
add_compile_options(
/EHs
@ -131,7 +128,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Set optimization options for release build
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
/Zi \
/Ob3 \
/Ob2 \
/Oi \
/Ot \
/GL \
@ -144,7 +141,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
endif()
# Windows libraries
set(WINS_LIB
set(WINS_LIB
legacy_stdio_definitions
d3d9
d3dcompiler
@ -174,8 +171,6 @@ target_link_libraries(cxbxr-emu
SDL2
imgui
libusb
nv2a_vsh_emulator
mio::mio_min_winapi
${WINS_LIB}
)

View file

@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
project(cxbxr-ldr)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
# Suppress extra stuff from generated solution
set(CMAKE_SUPPRESS_REGENERATION true)

View file

@ -27,3 +27,14 @@ 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})

View file

@ -3,5 +3,5 @@ root = true
[*]
indent_size = 4
trim_trailing_whitespace = true
end_of_line = lf
end_of_line = crlf
insert_final_newline = true

View file

@ -84,4 +84,6 @@ extern volatile bool g_bPrintfOn;
#define CxbxSetThreadName(Name)
#endif
#include <filesystem>
#endif

View file

@ -163,14 +163,6 @@ inline constexpr uint32_t FLASH_DEVICE4_END = (FLASH_DEVICE4_BASE - 1 + FLA
#define XBOX_MEMORY_SIZE (MiB(64))
#define CHIHIRO_MEMORY_SIZE (MiB(128))
// Common page calculations
#define ROUND_UP_4K(size) (((size) + PAGE_MASK) & (~PAGE_MASK))
#define ROUND_UP(size, alignment) (((size) + (alignment - 1)) & (~(alignment - 1)))
#define ROUND_DOWN_4K(size) ((size) & (~PAGE_MASK))
#define ROUND_DOWN(size, alignment) ((size) & (~(alignment - 1)))
#define CHECK_ALIGNMENT(size, alignment) (((size) % (alignment)) == 0)
#define PAGE_ALIGN(address) ROUND_DOWN_4K(address)
// Windows' address space allocation granularity;
// See https://blogs.msdn.microsoft.com/oldnewthing/20031008-00/?p=42223
const int BLOCK_SIZE = KiB(64);

View file

@ -27,8 +27,8 @@
#define LOG_PREFIX CXBXR_MODULE::EEPR
#define LOG_PREFIX_INIT CXBXR_MODULE::INIT
#include <core\kernel\exports\xboxkrnl.h> // For XC_VALUE_INDEX and XBOX_EEPROM
#include "cxbxr.hpp" // For CxbxrAbort
#include <stdio.h> // For printf
#include <shlobj.h> // For HANDLE, CreateFile, CreateFileMapping, MapViewOfFile
#include <random>
@ -36,6 +36,7 @@
#include "EmuEEPROM.h" // For EEPROMInfo, EEPROMInfos
#include "core\kernel\support\Emu.h" // For EmuWarning
#include "..\..\src\devices\LED.h" // For SetLEDSequence
#include "..\core\kernel\init\CxbxKrnl.h"
xbox::XBOX_EEPROM *EEPROM = nullptr; // Set using CxbxRestoreEEPROM()
@ -89,7 +90,6 @@ void gen_section_CRCs(xbox::XBOX_EEPROM* eeprom) {
);
}
#ifdef CXBXR_EMU
xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
{
xbox::XBOX_EEPROM *pEEPROM;
@ -141,8 +141,10 @@ xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
LARGE_INTEGER len_li;
GetFileSizeEx(hFileEEPROM, &len_li);
unsigned int FileSize = len_li.u.LowPart;
if (FileSize != 256) {
CxbxrAbort("%s : EEPROM.bin file is not 256 bytes large!\n", __func__);
if (FileSize != 256)
{
CxbxrKrnlAbort("%s : EEPROM.bin file is not 256 bytes large!\n", __func__);
return nullptr;
}
// Map EEPROM.bin contents into memory :
@ -192,7 +194,6 @@ xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
return pEEPROM;
}
#endif
void EmuEEPROMReset(xbox::XBOX_EEPROM* eeprom)
{

View file

@ -1,187 +0,0 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx-Reloaded project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::FILE
#define LOG_PREFIX_INIT CXBXR_MODULE::INIT
#include <filesystem>
#include "common/cxbxr.hpp"
#include "Settings.hpp"
#include "EmuShared.h"
#include "xxhash.h" // for XXH3_64bits
#include "core/kernel/common/xbox.h"
#include "Logging.h"
char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 };
char szFilePath_EEPROM_bin[MAX_PATH] = { 0 };
std::string g_DataFilePath;
std::string g_DiskBasePath;
std::string g_MuBasePath;
//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere.
// Let filesystem library clean it up for us, including resolve host's symbolic link path.
// Since internal kernel do translate to full path than preserved host symoblic link path.
void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence) {
std::error_code error;
std::filesystem::path sanityPath = std::filesystem::canonical(file_path, error);
if (error) {
// The MS implementation of std::filesystem::canonical internally calls GetFinalPathNameByHandleW, which fails with ERROR_FILE_NOT_FOUND when called
// on a file inside a mounted xiso under Windows with the xbox-iso-vfs tool because of this known dokany bug https://github.com/dokan-dev/dokany/issues/343
EmuLogInit(LOG_LEVEL::WARNING, "Could not resolve to %s: %s, dokany in use? The error was: %s",
finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
sanityPath = std::filesystem::absolute(std::filesystem::weakly_canonical(file_path, error), error);
if (error) {
CxbxrAbortEx(LOG_PREFIX_INIT, "Could not resolve to %s: %s. The error was: %s", finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
}
}
file_path = sanityPath;
}
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence) {
std::filesystem::path sanityPath(file_path);
CxbxResolveHostToFullPath(sanityPath, finish_error_sentence);
file_path = sanityPath.string();
}
// NOTE: Do NOT modify g_<custom>BasePath variables after this call!
void CxbxrInitFilePaths()
{
if (g_Settings) {
g_DataFilePath = g_Settings->GetDataLocation();
}
else {
char dataLoc[MAX_PATH];
g_EmuShared->GetDataLocation(dataLoc);
g_DataFilePath = dataLoc;
}
// Make sure our data folder exists :
bool result = std::filesystem::exists(g_DataFilePath);
if (!result && !std::filesystem::create_directory(g_DataFilePath)) {
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded's data folder!", __func__);
}
// Make sure the EmuDisk folder exists
g_DiskBasePath = g_DataFilePath + "\\EmuDisk";
result = std::filesystem::exists(g_DiskBasePath);
if (!result && !std::filesystem::create_directory(g_DiskBasePath)) {
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__);
}
CxbxResolveHostToFullPath(g_DiskBasePath, "Cxbx-Reloaded's EmuDisk directory");
g_DiskBasePath = std::filesystem::path(g_DiskBasePath).append("").string();
// Make sure the EmuDMu folder exists
g_MuBasePath = g_DataFilePath + "\\EmuMu";
result = std::filesystem::exists(g_MuBasePath);
if (!result && !std::filesystem::create_directory(g_MuBasePath)) {
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded EmuMu folder!", __func__);
}
CxbxResolveHostToFullPath(g_MuBasePath, "Cxbx-Reloaded's EmuMu directory");
g_MuBasePath = std::filesystem::path(g_MuBasePath).append("").string();
snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", g_DataFilePath.c_str());
GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH);
}
// Loads a keys.bin file as generated by dump-xbox
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
void LoadXboxKeys()
{
std::string keys_path = g_DataFilePath + "\\keys.bin";
// Attempt to open Keys.bin
FILE* fp = fopen(keys_path.c_str(), "rb");
if (fp != nullptr) {
// Determine size of Keys.bin
xbox::XBOX_KEY_DATA keys[2];
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
// If the size of Keys.bin is correct (two keys), read it
if (size == xbox::XBOX_KEY_LENGTH * 2) {
fread(keys, xbox::XBOX_KEY_LENGTH, 2, fp);
memcpy(xbox::XboxEEPROMKey, &keys[0], xbox::XBOX_KEY_LENGTH);
memcpy(xbox::XboxCertificateKey, &keys[1], xbox::XBOX_KEY_LENGTH);
}
else {
EmuLog(LOG_LEVEL::WARNING, "Keys.bin has an incorrect filesize. Should be %d bytes", xbox::XBOX_KEY_LENGTH * 2);
}
fclose(fp);
return;
}
// If we didn't already exit the function, keys.bin could not be loaded
EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox");
}
static HANDLE hMapDataHash = nullptr;
bool CxbxrLockFilePath()
{
std::stringstream filePathHash("Local\\");
uint64_t hashValue = XXH3_64bits(g_DataFilePath.c_str(), g_DataFilePath.length() + 1);
if (!hashValue) {
CxbxrAbort("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__);
}
filePathHash << std::hex << hashValue;
hMapDataHash = CreateFileMapping(
INVALID_HANDLE_VALUE, // Paging file
nullptr, // default security attributes
PAGE_READONLY, // readonly access
0, // size: high 32 bits
/*Dummy size*/4, // size: low 32 bits
filePathHash.str().c_str() // name of map object
);
if (hMapDataHash == nullptr) {
return false;
}
if (GetLastError() == ERROR_ALREADY_EXISTS) {
PopupError(nullptr, "Data path directory is currently in use.\nUse a different data path directory or stop emulation from another process.");
CloseHandle(hMapDataHash);
return false;
}
return true;
}
void CxbxrUnlockFilePath()
{
// Close opened file path lockdown shared memory.
if (hMapDataHash) {
CloseHandle(hMapDataHash);
hMapDataHash = nullptr;
}
}

View file

@ -24,30 +24,148 @@
// ******************************************************************
#pragma once
extern char szFilePath_CxbxReloaded_Exe[MAX_PATH];
extern char szFilePath_EEPROM_bin[MAX_PATH];
extern std::string g_DataFilePath;
extern std::string g_DiskBasePath;
extern std::string g_MuBasePath;
#include <filesystem>
//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere.
// Let filesystem library clean it up for us, including resolve host's symbolic link path.
// Since internal kernel do translate to full path than preserved host symoblic link path.
void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence);
static inline void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence) {
std::error_code error;
std::filesystem::path sanityPath = std::filesystem::canonical(file_path, error);
if (error) {
// The MS implementation of std::filesystem::canonical internally calls GetFinalPathNameByHandleW, which fails with ERROR_FILE_NOT_FOUND when called
// on a file inside a mounted xiso under Windows with the xbox-iso-vfs tool because of this known dokany bug https://github.com/dokan-dev/dokany/issues/343
EmuLogInit(LOG_LEVEL::WARNING, "Could not resolve to %s: %s, dokany in use? The error was: %s",
finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
sanityPath = std::filesystem::absolute(std::filesystem::weakly_canonical(file_path, error), error);
if (error) {
CxbxrKrnlAbortEx(LOG_PREFIX_INIT, "Could not resolve to %s: %s. The error was: %s", finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
}
}
file_path = sanityPath;
}
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence);
static inline void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence) {
std::filesystem::path sanityPath(file_path);
CxbxResolveHostToFullPath(sanityPath, finish_error_sentence);
file_path = sanityPath.string();
}
// NOTE: Do NOT modify g_<custom>BasePath variables after this call!
void CxbxrInitFilePaths();
void CxbxrInitFilePaths()
{
if (g_Settings) {
g_DataFilePath = g_Settings->GetDataLocation();
}
else {
char dataLoc[MAX_PATH];
g_EmuShared->GetDataLocation(dataLoc);
g_DataFilePath = dataLoc;
}
// Make sure our data folder exists :
bool result = std::filesystem::exists(g_DataFilePath);
if (!result && !std::filesystem::create_directory(g_DataFilePath)) {
CxbxrKrnlAbort("%s : Couldn't create Cxbx-Reloaded's data folder!", __func__);
}
// Make sure the EmuDisk folder exists
g_DiskBasePath = g_DataFilePath + "\\EmuDisk";
result = std::filesystem::exists(g_DiskBasePath);
if (!result && !std::filesystem::create_directory(g_DiskBasePath)) {
CxbxrKrnlAbort("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__);
}
CxbxResolveHostToFullPath(g_DiskBasePath, "Cxbx-Reloaded's EmuDisk directory");
g_DiskBasePath = std::filesystem::path(g_DiskBasePath).append("").string();
// Make sure the EmuDMu folder exists
g_MuBasePath = g_DataFilePath + "\\EmuMu";
result = std::filesystem::exists(g_MuBasePath);
if (!result && !std::filesystem::create_directory(g_MuBasePath)) {
CxbxrKrnlAbort("%s : Couldn't create Cxbx-Reloaded EmuMu folder!", __func__);
}
CxbxResolveHostToFullPath(g_MuBasePath, "Cxbx-Reloaded's EmuMu directory");
g_MuBasePath = std::filesystem::path(g_MuBasePath).append("").string();
snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", g_DataFilePath.c_str());
GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH);
}
// Loads a keys.bin file as generated by dump-xbox
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
void LoadXboxKeys();
static void LoadXboxKeys()
{
std::string keys_path = g_DataFilePath + "\\keys.bin";
bool CxbxrLockFilePath();
// Attempt to open Keys.bin
FILE* fp = fopen(keys_path.c_str(), "rb");
void CxbxrUnlockFilePath();
if (fp != nullptr) {
// Determine size of Keys.bin
xbox::XBOX_KEY_DATA keys[2];
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
// If the size of Keys.bin is correct (two keys), read it
if (size == xbox::XBOX_KEY_LENGTH * 2) {
fread(keys, xbox::XBOX_KEY_LENGTH, 2, fp);
memcpy(xbox::XboxEEPROMKey, &keys[0], xbox::XBOX_KEY_LENGTH);
memcpy(xbox::XboxCertificateKey, &keys[1], xbox::XBOX_KEY_LENGTH);
}
else {
EmuLog(LOG_LEVEL::WARNING, "Keys.bin has an incorrect filesize. Should be %d bytes", xbox::XBOX_KEY_LENGTH * 2);
}
fclose(fp);
return;
}
// If we didn't already exit the function, keys.bin could not be loaded
EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox");
}
static HANDLE hMapDataHash = nullptr;
static bool CxbxLockFilePath()
{
std::stringstream filePathHash("Local\\");
uint64_t hashValue = XXH3_64bits(g_DataFilePath.c_str(), g_DataFilePath.length() + 1);
if (!hashValue) {
CxbxrKrnlAbort("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__);
}
filePathHash << std::hex << hashValue;
hMapDataHash = CreateFileMapping(
INVALID_HANDLE_VALUE, // Paging file
nullptr, // default security attributes
PAGE_READONLY, // readonly access
0, // size: high 32 bits
/*Dummy size*/4, // size: low 32 bits
filePathHash.str().c_str() // name of map object
);
if (hMapDataHash == nullptr) {
return false;
}
if (GetLastError() == ERROR_ALREADY_EXISTS) {
PopupError(nullptr, "Data path directory is currently in use.\nUse a different data path directory or stop emulation from another process.");
CloseHandle(hMapDataHash);
return false;
}
return true;
}
static void CxbxUnlockFilePath()
{
// Close opened file path lockdown shared memory.
if (hMapDataHash) {
CloseHandle(hMapDataHash);
hMapDataHash = nullptr;
}
}

View file

@ -49,9 +49,8 @@ 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_CHANGE_TIME
CONFIG_LOGGING_SYNC = 0
, CONFIG_INPUT_SYNC
} IPC_UPDATE_KERNEL;
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd);

View file

@ -313,7 +313,7 @@ PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, con
uType |= MB_OKCANCEL;
break;
case PopupButtons::AbortRetryIgnore:
uType |= MB_ABORTRETRYIGNORE;
uType |= MB_RETRYCANCEL;
break;
case PopupButtons::YesNoCancel:
uType |= MB_YESNOCANCEL;
@ -396,13 +396,8 @@ inline void output_wchar(std::ostream& os, wchar_t c)
default: os << "\\x" << std::setfill('0') << std::setw(4) << std::right << std::hex << std::uppercase << (wint_t)c;
}
}
else {
const wchar_t *wc = reinterpret_cast<const wchar_t *>(&c);
std::string dst(2, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wc, 1, &ps);
os << dst;
}
else
os << c;
}
LOG_SANITIZE_HEADER(hex1, uint8_t)

View file

@ -225,30 +225,9 @@ extern inline void output_wchar(std::ostream& os, wchar_t c);
// By default, sanitization functions simply return the given argument
// (type and value) which results in calls to standard output writers.
template<class T>
inline auto _log_sanitize(T value, int ignored_length = 0)
inline T _log_sanitize(T value, int ignored_length = 0)
{
// This is necessary because C++20 has deleted the overloaded operator<< of ostream for wchar_t, char8_t, char16_t and char32_t.
// The proper solution is to use wide strings in all our logging macros/functions, see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/743
if constexpr (std::is_same_v<xbox::wchar_xt, std::remove_cvref_t<std::remove_pointer_t<T>>>) {
if constexpr (std::is_pointer_v<T>) {
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(value);
std::size_t len = std::wcslen(wstr);
std::string dst(len + 1, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wstr, len, &ps);
return dst;
}
else {
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(&value);
std::string dst(2, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wstr, 1, &ps);
return dst;
}
}
else {
return value;
}
return value;
}
#if 0 // TODO FIXME : Disabled for now, as this is incorrectly called for INT types too

View file

@ -319,6 +319,12 @@ bool isSystemFlagSupport(unsigned int reserved_systems, unsigned int assign_syst
if (reserved_systems & assign_system) {
return true;
}
// TODO: Once host's standalone emulation is remove from GUI, remove below as well.
#ifndef CXBXR_EMU
if (reserved_systems == 0) {
return true;
}
#endif
return false;
}

View file

@ -0,0 +1,55 @@
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx is free software; you can redistribute it
// * and/or modify it under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2016 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef RESERVEDMEMORY_H
#define RESERVEDMEMORY_H
#ifndef CXBXR_EMU
#if defined(__cplusplus)
extern "C"
{
#endif
#include "EmuShared.h" // For XBE_MAX_VA, XBE_IMAGE_BASE and CXBX_BASE_OF_CODE
// The following code reserves virtual addresses from 0x00011000 upwards;
#define VM_PLACEHOLDER_SIZE (XBE_MAX_VA - XBE_IMAGE_BASE - CXBX_BASE_OF_CODE)
// First, declare the '.text' section again :
#pragma section(".text") // Note : 'read,write,execute' would cause a warning
// Then place the following variable into the '.text' section :
__declspec(allocate(".text"))
// This variable *MUST* be this large, for it to take up address space
// so that all other code and data in this module are placed outside of the
// maximum virtual memory range.
unsigned char virtual_memory_placeholder[VM_PLACEHOLDER_SIZE]; // = { OPCODE_NOP_90 };
// TODO : Try to get the same result without enlarging our executable by 128 MB!
#if defined(__cplusplus)
}
#endif
#endif
#endif // RESERVEDMEMORY_H

View file

@ -52,12 +52,7 @@ static_assert(false, "Please implement support for cross-platform's user profile
uint16_t g_LibVersion_D3D8 = 0;
uint16_t g_LibVersion_DSOUND = 0;
// NOTE: Update settings_version when conversion to setting's structure is required.
// UPDATE: When settings are removed, use "if (use false && settings_version < {next_version}) {" statement
// until existing settings require replacement or conversion. next_version input is a hardcode number.
// Settings version 10 and later should consider as not backward compatible.
// TODO: Add read-only state when using an older build and add a notification for will not be able save to file.
// The sooner we do this, the better before version upgrade.
// NOTE: Update settings_version when add/edit/delete setting's structure.
///////////////////////////
// * History:
// * 2: (RadWolfie), initial version
@ -101,6 +96,7 @@ static struct {
const char* AllowAdminPrivilege = "AllowAdminPrivilege";
const char* LoggedModules = "LoggedModules";
const char* LogLevel = "LogLevel";
const char* LoaderExecutable = "LoaderExecutable";
const char* LogPopupTestCase = "LogPopupTestCase";
} sect_core_keys;
@ -117,11 +113,8 @@ 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";
@ -385,6 +378,8 @@ bool Settings::LoadConfig()
}
m_core.bLogPopupTestCase = m_si.GetBoolValue(section_core, sect_core_keys.LogPopupTestCase, /*Default=*/true);
m_core.bUseLoaderExec = m_si.GetBoolValue(section_core, sect_core_keys.LoaderExecutable, /*Default=*/true);
// ==== Core End ============
// Delete/update legacy configs from previous revisions
@ -550,11 +545,8 @@ 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 ===========
@ -609,6 +601,8 @@ bool Settings::Save(std::string file_path)
}
m_si.SetBoolValue(section_core, sect_core_keys.LogPopupTestCase, m_core.bLogPopupTestCase, nullptr, true);
m_si.SetBoolValue(section_core, sect_core_keys.LoaderExecutable, m_core.bUseLoaderExec, nullptr, true);
// ==== Core End ============
// ==== Video Begin =========
@ -741,11 +735,8 @@ 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 =========
@ -1009,7 +1000,7 @@ void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
std::string device_name = m_si.GetValue(current_section.c_str(), sect_input_port.device, "");
if (device_name.ends_with(kb_str)) {
if (StrEndsWith(device_name, kb_str)) {
device_name += "Mouse";
m_si.SetValue(current_section.c_str(), sect_input_port.device, device_name.c_str(), nullptr, true);
}
@ -1036,9 +1027,4 @@ void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
if(CurrentRevision < 9) {
m_si.Delete(section_video, "HardwareYUV", true);
}
// see settings_version for details.
if(false && CurrentRevision < 10) {
m_si.Delete(section_core, "LoaderExecutable", true);
}
}

View file

@ -26,6 +26,7 @@
// ******************************************************************
#ifndef SETTINGS_HPP
#define SETTINGS_HPP
#include "Cxbx.h"
#include "SimpleIni.h"
#include "common\input\InputManager.h"
@ -102,7 +103,7 @@ public:
char szStorageLocation[xbox::max_path] = "";
unsigned int LoggedModules[NUM_INTEGERS_LOG];
int LogLevel = 1;
bool bUnused_WasUseLoaderExec;
bool bUseLoaderExec;
bool allowAdminPrivilege;
bool bLogPopupTestCase;
bool Reserved4 = 0;

View file

@ -27,43 +27,19 @@
#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\init\CxbxKrnl.h"
#include "core\kernel\support\EmuFS.h"
#include "core\kernel\exports\EmuKrnlPs.hpp"
#include "core\kernel\exports\EmuKrnl.h"
#include "devices\Xbox.h"
#include "devices\usb\OHCI.h"
#include "core\hle\DSOUND\DirectSound\DirectSoundGlobal.hpp"
static std::atomic_uint64_t last_qpc; // last time when QPC was called
static std::atomic_uint64_t exec_time; // total execution time in us since the emulation started
static uint64_t pit_last; // last time when the pit time was updated
static uint64_t pit_last_qpc; // last QPC time of the pit
// The frequency of the high resolution clock of the host, and the start time
int64_t HostQPCFrequency, HostQPCStartTime;
void timer_init()
{
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *>(&HostQPCFrequency));
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&HostQPCStartTime));
pit_last_qpc = last_qpc = HostQPCStartTime;
pit_last = get_now();
// Synchronize xbox system time with host time
LARGE_INTEGER HostSystemTime;
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
xbox::KeSystemTime.High2Time = HostSystemTime.u.HighPart;
xbox::KeSystemTime.LowPart = HostSystemTime.u.LowPart;
xbox::KeSystemTime.High1Time = HostSystemTime.u.HighPart;
}
#ifdef __linux__
#include <time.h>
#endif
// More precise sleep, but with increased CPU usage
void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
@ -95,83 +71,174 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
}
}
// NOTE: the pit device is not implemented right now, so we put this here
static uint64_t pit_next(uint64_t now)
// 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)
{
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
#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;
}
static void update_non_periodic_events()
// Calculates the next expire time of the timer
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
{
// update dsound
dsound_worker();
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load();
}
// 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)
{
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);
}
uint64_t get_now()
void Timer_Shutdown()
{
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;
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();
}
static uint64_t get_next(uint64_t now)
// Thread that runs the timer
void NTAPI ClockThread(void *TimerArg)
{
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());
}
TimerObject *Timer = static_cast<TimerObject *>(TimerArg);
if (!Timer->Name.empty()) {
CxbxSetThreadName(Timer->Name.c_str());
}
if (!Timer->IsXboxTimer) {
g_AffinityPolicy->SetAffinityOther();
}
xbox::void_xt NTAPI system_events(xbox::PVOID arg)
{
// Testing shows that, if this thread has the same priority of the other xbox threads, it can take tens, even hundreds of ms to complete a single loop.
// So we increase its priority to above normal, so that it scheduled more often
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
// Always run this thread at dpc level to prevent it from ever executing APCs/DPCs
xbox::KeRaiseIrqlToDpcLevel();
uint64_t NewExpireTime = GetNextExpireTime(Timer);
while (true) {
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;
if (GetTime_NS(Timer) > NewExpireTime) {
if (Timer->Exit.load()) {
Timer_Destroy(Timer);
return;
}
std::this_thread::yield();
Timer->Callback(Timer->Opaque);
NewExpireTime = GetNextExpireTime(Timer);
}
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;

View file

@ -40,12 +40,33 @@
#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;
void timer_init();
uint64_t get_now();
/* 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();
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
#endif

View file

@ -1,169 +0,0 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx-Reloaded project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::FILE
#ifdef CXBXR_EMU
#include "core/kernel/support/EmuFile.h" // For g_io_mu_metadata
#include "common/Timer.h" // For Timer_Shutdown
#include "core/common/video/RenderBase.hpp" // For g_renderbase
#include "core/kernel/memory-manager/VMManager.h"
extern void CxbxrKrnlSuspendThreads();
#endif
#include "cxbxr.hpp"
#include "EmuShared.h"
#include "Settings.hpp"
#include "Logging.h"
#include "win32/WineEnv.h"
#include "Settings.hpp"
volatile bool g_bPrintfOn = true;
bool CreateSettings()
{
g_Settings = new Settings();
if (g_Settings == nullptr) {
PopupError(nullptr, szSettings_alloc_error);
return false;
}
if (!g_Settings->Init()) {
return false;
}
log_get_settings();
return true;
}
bool HandleFirstLaunch()
{
bool bFirstLaunch;
g_EmuShared->GetIsFirstLaunch(&bFirstLaunch);
/* check if process is launch with elevated access then prompt for continue on or not. */
if (!bFirstLaunch) {
if (!CreateSettings()) {
return false;
}
// Wine will always run programs as administrator by default, it can be safely disregard.
// Since Wine doesn't use root permission. Unless user is running Wine as root.
bool bElevated = CxbxrIsElevated();
if (bElevated && !isWineEnv() && !g_Settings->m_core.allowAdminPrivilege) {
PopupReturn ret = PopupWarningEx(nullptr, PopupButtons::YesNo, PopupReturn::No,
"Cxbx-Reloaded has detected that it has been launched with Administrator rights.\n"
"\nThis is dangerous, as a maliciously modified Xbox titles could take control of your system.\n"
"\nAre you sure you want to continue?");
if (ret != PopupReturn::Yes) {
return false;
}
}
g_EmuShared->SetIsFirstLaunch(true);
}
return true;
}
[[noreturn]] void CxbxrShutDown(bool is_reboot)
{
if (!is_reboot) {
// Clear all kernel boot flags. These (together with the shared memory) persist until Cxbx-Reloaded is closed otherwise.
int BootFlags = 0;
g_EmuShared->SetBootFlags(&BootFlags);
}
// NOTE: This causes a hang when exiting while NV2A is processing
// This is okay for now: It won't leak memory or resources since TerminateProcess will free everything
// delete g_NV2A; // TODO : g_pXbox
// Shutdown the input device manager
g_InputDeviceManager.Shutdown();
#ifdef CXBXR_EMU
// NOTE: this code causes freezes/crashes at shutdown, so avoid for now
// This is very important process to prevent false positive report and allow IDEs to continue debug multiple reboots.
//CxbxrKrnlSuspendThreads();
if (g_io_mu_metadata) {
delete g_io_mu_metadata;
g_io_mu_metadata = nullptr;
}
// Shutdown the render manager
if (g_renderbase != nullptr) {
g_renderbase->Shutdown();
g_renderbase = nullptr;
}
// NOTE: Require to be after g_renderbase's shutdown process.
// NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call!
// Shutdown the memory manager
g_VMManager.Shutdown();
CxbxrUnlockFilePath();
if (CxbxKrnl_hEmuParent != NULL && !is_reboot) {
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0);
}
#endif
EmuShared::Cleanup();
TerminateProcess(GetCurrentProcess(), 0);
}
[[noreturn]] void CxbxrAbortEx(CXBXR_MODULE cxbxr_module, const char* szErrorMessage, ...)
{
// print out error message (if exists)
if (szErrorMessage != NULL)
{
char szBuffer2[1024];
va_list argp;
va_start(argp, szErrorMessage);
vsprintf(szBuffer2, szErrorMessage, argp);
va_end(argp);
(void)PopupCustomEx(nullptr, cxbxr_module, LOG_LEVEL::FATAL, PopupIcon::Error, PopupButtons::Ok, PopupReturn::Ok, "Received Fatal Message:\n\n* %s\n", szBuffer2); // Will also EmuLogEx
}
EmuLogInit(LOG_LEVEL::INFO, "MAIN: Terminating Process");
fflush(stdout);
// cleanup debug output
{
FreeConsole();
char buffer[16];
if (GetConsoleTitle(buffer, 16) != NULL)
freopen("nul", "w", stdout);
}
CxbxrShutDown();
}

View file

@ -1,58 +0,0 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx-Reloaded project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include <string>
#include <optional>
#include "Logging.h"
bool CreateSettings();
bool HandleFirstLaunch();
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence);
// Loads a keys.bin file as generated by dump-xbox
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
void LoadXboxKeys();
bool CxbxrLockFilePath();
void CxbxrUnlockFilePath();
// Hybrid functions depending on specific platforms
bool CxbxrIsElevated();
std::optional<std::string> CxbxrExec(bool useDebugger, void** hProcess, bool requestHandleProcess);
/*! cleanup emulation */
[[noreturn]] void CxbxrAbortEx(CXBXR_MODULE cxbxr_module, const char* szErrorMessage, ...);
#define CxbxrAbort(fmt, ...) CxbxrAbortEx(LOG_PREFIX, fmt, ##__VA_ARGS__)
/*! terminate gracefully the emulation */
[[noreturn]] void CxbxrShutDown(bool is_reboot = false);

View file

@ -51,17 +51,16 @@ void Button::GetText(char* const text, size_t size) const
SendMessage(m_button_hwnd, WM_GETTEXT, size, reinterpret_cast<LPARAM>(text));
}
void Button::AddTooltip(HWND hwnd, HWND tooltip_hwnd, std::string_view text) const
void Button::AddTooltip(HWND hwnd, HWND tooltip_hwnd, char *text) const
{
assert((hwnd != NULL) && (tooltip_hwnd != NULL));
std::string tooltip_text(text);
TOOLINFO tool = { 0 };
tool.cbSize = sizeof(tool);
tool.hwnd = hwnd;
tool.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
tool.uId = reinterpret_cast<UINT_PTR>(m_button_hwnd);
tool.lpszText = tooltip_text.data();
tool.lpszText = text;
SendMessage(tooltip_hwnd, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&tool));
}
@ -107,8 +106,13 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
case WM_RBUTTONDOWN: {
Button *button = reinterpret_cast<Button *>(dwRefData);
button->ClearText();
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
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);
}
}
break;
@ -123,7 +127,7 @@ LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam,
{
// Remove the window subclass when this window is destroyed
case WM_NCDESTROY: {
RemoveWindowSubclass(hWnd, ButtonLightgunSubclassProc, uIdSubclass);
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
}
break;

View file

@ -51,7 +51,7 @@ public:
int GetId() const { return m_id; }
int GetIndex() const { return m_index; }
void *GetWnd() const { return m_wnd; }
void AddTooltip(HWND hwnd, HWND tooltip_hwnd, std::string_view text) const;
void AddTooltip(HWND hwnd, HWND tooltip_hwnd, char *text) const;
private:

View file

@ -37,7 +37,6 @@
#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.

View file

@ -25,15 +25,14 @@
// *
// ******************************************************************
#include <algorithm>
#include "Button.h"
#include "InputManager.h"
#include "layout_xbox_device.h"
#include "gui/resource/ResCxbx.h"
static const char *tooltip_text_toggle = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
static const char *tooltip_text_no_toggle = "Left-click: start input detection\nRight-click: clear binding";
static char *tooltip_text_toggle = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
static char *tooltip_text_no_toggle = "Left-click: start input detection\nRight-click: clear binding";
EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
{
@ -58,7 +57,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_no_toggle);
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
// Install the subclass for the button control
SetWindowSubclass(GetDlgItem(hwnd, button_sbc_id[i]), ButtonSbcSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));

View file

@ -35,9 +35,8 @@
#define _XBOXKRNL_DEFEXTRN_
#define LOG_PREFIX CXBXR_MODULE::INPSYS
#include <core\kernel\exports\xboxkrnl.h> // For PKINTERRUPT, etc.
#include "common/cxbxr.hpp"
#include <core\kernel\exports\xboxkrnl.h> // For PKINTERRUPT, etc.
#include "SdlJoystick.h"
#include "XInputPad.h"
#include "RawDevice.h"
@ -48,17 +47,12 @@
#include "core\kernel\exports\EmuKrnl.h" // For EmuLog
#include "EmuShared.h"
#include "devices\usb\OHCI.h"
#ifdef CXBXR_EMU
#include "core/common/video/RenderBase.hpp"
#endif
#include <charconv>
// hle input specific
#include "core\hle\XAPI\Xapi.h"
// Allocate enough memory for the max number of devices we can support simultaneously
// 4 duke / S / sbc / arcade joystick / lightgun (mutually exclusive) + 8 memory units
DeviceState g_devs[MAX_DEVS];
int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = {
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_DUKE
@ -84,13 +78,11 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
m_hwnd = hwnd;
m_PollingThread = std::thread([this, is_gui]() {
#ifdef CXBXR_EMU
// This code can run in both cxbx.exe and cxbxr-ldr.exe, but will not have
// the affinity policy when running in the former.
if (g_AffinityPolicy) {
g_AffinityPolicy->SetAffinityOther();
}
#endif
XInput::Init(m_Mtx);
RawInput::Init(m_Mtx, is_gui, m_hwnd);
@ -107,7 +99,7 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
lck.unlock();
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0 || Libusb::InitStatus < 0) {
CxbxrAbort("Failed to initialize input subsystem! Consult debug log for more information");
CxbxrKrnlAbort("Failed to initialize input subsystem! Consult debug log for more information");
}
UpdateOpt(is_gui);
@ -375,11 +367,7 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
// First check if ImGui is focus, then ignore any input update occur.
// If somebody else is currently holding the lock, we won't wait and instead report no input changes
if (
#ifdef CXBXR_EMU
!g_renderbase->IsImGuiFocus() &&
#endif
m_Mtx.try_lock()) {
if (!g_renderbase->IsImGuiFocus() && m_Mtx.try_lock()) {
for (auto &dev : m_Devices) {
std::string port_str = std::to_string(port);
if (dev->GetPort(port_str)) {
@ -788,7 +776,7 @@ void InputDeviceManager::RefreshDevices()
return Sdl::PopulateOK;
});
for (auto &dev : m_Devices) {
if (dev->GetDeviceName().starts_with("KeyboardMouse")) {
if (StrStartsWith(dev->GetDeviceName(), "KeyboardMouse")) {
static_cast<DInput::KeyboardMouse *>(dev.get())->SetHwnd(m_hwnd);
break;
}
@ -888,7 +876,7 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
std::unique_lock<std::mutex> lck(m_Mtx);
auto it = std::remove_if(m_Devices.begin(), m_Devices.end(), [](const auto &Device) {
if (Device->IsLibusb() || Device->GetAPI().starts_with("XInput")) {
if (Device->IsLibusb() || StrStartsWith(Device->GetAPI(), "XInput")) {
return true;
}
return false;

View file

@ -352,11 +352,11 @@ void InputWindow::SwapMoCursorAxis(Button *button)
// Axis X- <-> Cursor X-
// Axis Y+ <-> Cursor Y-
// Axis Y- <-> Cursor Y+
if (m_host_dev.ends_with("KeyboardMouse")) {
if (StrEndsWith(m_host_dev, "KeyboardMouse")) {
assert(button != nullptr);
char control_name[HOST_BUTTON_NAME_LENGTH];
button->GetText(control_name, sizeof(control_name));
if (std::string_view(control_name).starts_with("Axis")) {
if (StrStartsWith(control_name, "Axis")) {
switch (control_name[5])
{
case 'X':
@ -384,7 +384,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
return;
}
if (std::string_view(control_name).starts_with("Cursor")) {
if (StrStartsWith(control_name, "Cursor")) {
switch (control_name[7])
{
case 'X':

View file

@ -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,18 +185,9 @@ namespace Libusb
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
// check if we are able to open device through libusb
if (int err = libusb_open(Dev, &m_hDev)) {
// Couldn't open device, create an error log report then don't use it.
EmuLog(LOG_LEVEL::ERROR2, "Unable to open original xbox device \"%s\" (%hX:%hX), libusb's error was: %s",
m_Name.c_str(), Desc->idVendor, Desc->idProduct, libusb_strerror(err));
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
return;
}
// If we are able to open device, continue with query process.
else {
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
// The code below assumes that third-party controllers follow suit.
// 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) {
libusb_config_descriptor *ConfDesc;
if (libusb_get_active_config_descriptor(Dev, &ConfDesc) == 0) {
if (ConfDesc->bNumInterfaces == 1) {
@ -220,7 +211,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)) {
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum) != 0) {
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;

View file

@ -35,9 +35,9 @@
#define LOG_PREFIX CXBXR_MODULE::SDL
#include <assert.h>
#include <algorithm>
#include <thread>
#include "core\kernel\support\Emu.h"
#include "core\kernel\init\CxbxKrnl.h"
#include "SdlJoystick.h"
#include "XInputPad.h"
#include "DInputKeyboardMouse.h"

View file

@ -52,6 +52,7 @@
#include <cstring> // For memcpy
#include "common\util\CxbxUtil.h"
#include "core\kernel\init\CxbxKrnl.h"
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
@ -279,3 +280,27 @@ std::string StripQuotes(const std::string& str)
{
return StripChars(str, "\"");
}
// NOTE: with C++20, this can be replaced by simply calling full_str.ends_with()
bool StrEndsWith(const std::string &full_str, const std::string &substr)
{
if (full_str.length() >= substr.length()) {
if (full_str.compare(full_str.length() - substr.length(), substr.length(), substr) == 0) {
return true;
}
}
return false;
}
// NOTE: with C++20, this can be replaced by simply calling full_str.starts_with()
bool StrStartsWith(const std::string &full_str, const std::string &substr)
{
if (!full_str.empty()) {
if (full_str.rfind(substr, 0) == 0) {
return true;
}
}
return false;
}

View file

@ -26,15 +26,12 @@
#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() */
@ -70,6 +67,8 @@ bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite);
void unix2dos(std::string& string);
std::string StripSpaces(const std::string& str);
std::string StripQuotes(const std::string& str);
bool StrEndsWith(const std::string &full_str, const std::string &substr);
bool StrStartsWith(const std::string &full_str, const std::string &substr);
// Retrieves the underlying integer value of a scoped enumerator. It allows to avoid using static_cast every time
template <typename E>
@ -93,17 +92,4 @@ static uint32_t RoundUp(uint32_t dwValue, uint32_t dwMult)
return dwValue + dwMult - remainder;
}
constexpr std::size_t longest_str(const std::vector<std::string_view> &vec)
{
if (!vec.empty()) {
return std::max_element(vec.begin(), vec.end(),
[](const auto &a, const auto &b) {
return a.length() < b.length();
})->length();
}
else {
throw std::logic_error("No strings to compare!");
}
}
#endif

335
src/common/util/crc32c.cpp Normal file
View file

@ -0,0 +1,335 @@
/*
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);
}

35
src/common/util/crc32c.h Normal file
View file

@ -0,0 +1,35 @@
#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

View file

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

View file

@ -27,7 +27,8 @@
#ifndef _HASHER_H
#define _HASHER_H
#include "xxhash.h"
#define ComputeHash XXH3_64bits
#include <stdint.h>
uint64_t ComputeHash(const void* data, size_t len);
#endif

View file

@ -25,8 +25,7 @@
// *
// ******************************************************************
#include "cxbxr.hpp"
#include "core\kernel\init\CxbxKrnl.h"
#include "core\kernel\support\Emu.h"
#include "EmuShared.h"
@ -84,9 +83,8 @@ bool EmuShared::Init(long long sessionID)
emuSharedStr.c_str() // name of map object
);
if (hMapObject == NULL) {
CxbxrAbortEx(CXBXR_MODULE::INIT, "Could not map shared memory!");
}
if(hMapObject == NULL)
return false; // CxbxrKrnlAbortEx(CXBXR_MODULE::INIT, "Could not map shared memory!");
if(GetLastError() == ERROR_ALREADY_EXISTS)
bRequireConstruction = false;
@ -107,7 +105,7 @@ bool EmuShared::Init(long long sessionID)
if (g_EmuShared == nullptr) {
CloseHandle(hMapObject);
CxbxrAbortEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
return false; // CxbxrKrnlAbortEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
}
}

View file

@ -272,7 +272,7 @@ class EmuShared : public Mutex
void GetImGuiIniSettings(char value[IMGUI_INI_SIZE_MAX]) {
Lock();
if (m_imgui_general.ini_size < IMGUI_INI_SIZE_MAX) {
value[0] = '\0';
value = '\0';
return;
}
strcpy_s(value, IMGUI_INI_SIZE_MAX, m_imgui_general.ini_settings);

View file

@ -30,6 +30,7 @@
#include <windows.h>
#include "Cxbx.h"
#include "core\kernel\init\CxbxKrnl.h"
#include "gui/resource/ResCxbx.h"
#include "common\IPCHybrid.hpp"
@ -37,9 +38,6 @@
#include "common\Settings.hpp"
#include "Logging.h"
#ifdef CXBXR_EMU
/*! parent window handle */
extern "C" HWND CxbxKrnl_hEmuParent = NULL;
void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value)
{
@ -81,7 +79,6 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value)
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, MAKEWPARAM(WM_COMMAND, cmdParam), value);
}
}
#endif
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd)
{
@ -101,10 +98,6 @@ 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;

View file

@ -27,13 +27,12 @@
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
#include <optional>
#include "core\kernel\init\CxbxKrnl.h"
#include "common/util/cliConfig.hpp"
#include "Util.h"
// Source: https://stackoverflow.com/questions/8046097/how-to-check-if-a-process-has-the-administrative-rights
bool CxbxrIsElevated() {
bool CxbxIsElevated() {
bool fRet = false;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
@ -49,7 +48,7 @@ bool CxbxrIsElevated() {
return fRet;
}
std::optional<std::string> CxbxrExec(bool useDebugger, void** hProcess, bool requestHandleProcess) {
std::optional<std::string> CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) {
STARTUPINFO startupInfo = { 0 };
PROCESS_INFORMATION processInfo = { 0 };

View file

@ -200,7 +200,7 @@ class Win7Policy final : public AffinityPolicy
public:
bool Initialize() {
if (!GetProcessAffinityMask(g_CurrentProcessHandle, &CPUXbox, &CPUOthers))
CxbxrAbortEx(CXBXR_MODULE::INIT, "GetProcessAffinityMask failed.");
CxbxrKrnlAbortEx(CXBXR_MODULE::INIT, "GetProcessAffinityMask failed.");
// For the other threads, remove one bit from the processor mask:
CPUOthers = ((CPUXbox - 1) & CPUXbox);

View file

@ -34,6 +34,7 @@
#include <locale> // For ctime
#include <array>
#include "devices\LED.h" // For LED::Sequence
#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlPrintUEM
#include "common\crypto\EmuSha.h" // For the SHA functions
#include "common\crypto\EmuRsa.h" // For the RSA functions
#include "core\hle\XAPI\Xapi.h" // For LDT_FROM_DASHBOARD
@ -41,12 +42,12 @@
#include "common/AddressRanges.h"
#include "common/xbox/Types.hpp"
#ifdef CXBXR_EMU
extern "C" void CxbxKrnlPrintUEM(ULONG);
#endif
namespace fs = std::filesystem;
// construct via Xbe file
Xbe::Xbe(const char *x_szFilename)
Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
{
char szBuffer[MAX_PATH];
@ -56,23 +57,22 @@ Xbe::Xbe(const char *x_szFilename)
FILE *XbeFile = fopen(x_szFilename, "rb");
std::string XbeName = std::filesystem::path(x_szFilename).filename().string(); // recover the xbe name
// verify Xbe file was opened successfully
if(XbeFile == 0)
{
using namespace fs; // limit its scope inside here
// 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
#ifdef CXBXR_EMU
if (XbeName.compare(std::string("xboxdash.xbe")) == 0) {
// Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons
if (XbeName.compare(std::string("xboxdash.xbe")) == 0 && !bFromGUI)
{
// 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
// 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();
CxbxInitWindow(false);
ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC;
@ -86,15 +86,13 @@ Xbe::Xbe(const char *x_szFilename)
// TODO: FATAL_ERROR_XBE_DASH_X2_PASS (requires DVD drive authentication emulation...)
}
else {
#endif
else
{
// 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");
@ -109,14 +107,6 @@ Xbe::Xbe(const char *x_szFilename)
*(++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
@ -732,29 +722,25 @@ void Xbe::PurgeBadChar(std::string& s, const std::string& illegalChars)
}
}
const char *Xbe::GameRegionToString(uint32_t dwRegionFlags)
const char *Xbe::GameRegionToString()
{
if (!dwRegionFlags) {
dwRegionFlags = m_Certificate.dwGameRegion;
}
const char *Region_text[] = {
"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)"
"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)"
};
const uint32_t all_regions = XBEIMAGE_GAME_REGION_NA |
XBEIMAGE_GAME_REGION_JAPAN |
XBEIMAGE_GAME_REGION_RESTOFWORLD |
XBEIMAGE_GAME_REGION_MANUFACTURING;
if(dwRegionFlags & ~all_regions) {
if(m_Certificate.dwGameRegion & ~all_regions) {
return "REGION ERROR";
}
uint8_t index = (dwRegionFlags & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
index |= (dwRegionFlags & 0x7);
uint8_t index = (m_Certificate.dwGameRegion & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
index |= (m_Certificate.dwGameRegion & 0x7);
return Region_text[index];
}
@ -829,15 +815,5 @@ 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);

View file

@ -50,7 +50,7 @@ class Xbe : public Error
{
public:
// construct via Xbe file
Xbe(const char *x_szFilename);
Xbe(const char *x_szFilename, bool bFromGUI);
// deconstructor
~Xbe();
@ -81,13 +81,10 @@ class Xbe : public Error
void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|");
// Convert game region field to string
const char *GameRegionToString(uint32_t dwRegionFlags = 0);
const char *GameRegionToString();
XbeType GetXbeType();
uint32_t GetDiscVersion();
uint32_t GetPatchVersion();
// Xbe header
#include "AlignPrefix1.h"
struct Header
@ -160,7 +157,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 dwDiscNumber; // 0x00A8 - disc number
uint32_t dwDiskNumber; // 0x00A8 - disk number
uint32_t dwVersion; // 0x00AC - version
uint8_t bzLanKey[16]; // 0x00B0 - lan key
uint8_t bzSignatureKey[16]; // 0x00C0 - signature key
@ -261,9 +258,6 @@ 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];

View file

@ -33,6 +33,8 @@
#include <iomanip> // For std::setfill, std::uppercase, std::hex
#include "common/util/strConverter.hpp" // for utf16le_to_ascii
extern std::string FormatTitleId(uint32_t title_id); // Exposed in Emu.cpp
// better time
static char *BetterTime(uint32_t x_timeDate)
{
@ -58,34 +60,6 @@ std::string DumpInformation(Xbe* Xbe_object)
}
#define SSTREAM_SET_HEX(stream_name) stream_name << std::setfill('0') << std::uppercase << std::hex;
#define SSTREAM_SET_DEC(stream_name) stream_name << std::setfill('0') << std::uppercase << std::dec;
std::string FormatTitleId(uint32_t title_id)
{
std::stringstream ss;
// If the Title ID prefix is a printable character, parse it
// This shows the correct game serial number for retail titles!
// EG: MS-001 for 1st tile published by MS, EA-002 for 2nd title by EA, etc
// Some special Xbes (Dashboard, XDK Samples) use non-alphanumeric serials
// We fall back to Hex for those
// ergo720: we cannot use isalnum() here because it will treat chars in the range -1 - 255 as valid ascii chars which can
// lead to unicode characters being printed in the title (e.g.: dashboard uses 0xFE and 0xFF)
uint8_t pTitleId1 = (title_id >> 24) & 0xFF;
uint8_t pTitleId2 = (title_id >> 16) & 0xFF;
if ((pTitleId1 < 65 || pTitleId1 > 90) || (pTitleId2 < 65 || pTitleId2 > 90)) {
// Prefix was non-printable, so we need to print a hex reprentation of the entire title_id
ss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << title_id;
return ss.str();
}
ss << pTitleId1 << pTitleId2;
ss << "-";
ss << std::setfill('0') << std::setw(3) << std::dec << (title_id & 0x0000FFFF);
return ss.str();
}
XbePrinter::XbePrinter(Xbe* Xbe_object)
{
@ -360,12 +334,8 @@ 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 << "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";
text << "Disk Number : 0x" << std::setw(8) << Xbe_certificate->dwDiskNumber << "\n";
text << "Version : 1." << std::dec << std::setw(2) << Xbe_certificate->dwVersion << "\n";
return text.str();
}

View file

@ -30,8 +30,6 @@
#include <cstdio>
extern std::string FormatTitleId(uint32_t title_id);
extern std::string DumpInformation(Xbe* Xbe_object);
class XbePrinter

View file

@ -25,7 +25,6 @@ void ImGuiAudio::DrawMenu()
{
if (ImGui::BeginMenu("Audio")) {
ImGui::MenuItem("Debug General Cache Stats", NULL, &m_windows.cache_stats_general);
ImGui::MenuItem("SoundBuffer Visualization", NULL, &m_windows.cache_visualization);
ImGui::EndMenu();
}
}
@ -33,9 +32,6 @@ void ImGuiAudio::DrawMenu()
void ImGuiAudio::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
{
//TODO: In need of make interface class to return generic info in some way.
extern void DSound_PrintStats(bool, ImGuiWindowFlags, bool m_show_audio_stats);
extern void DSound_PrintStats(bool, ImGuiWindowFlags, bool m_show_audio_stats);
DSound_PrintStats(is_focus, input_handler, m_windows.cache_stats_general);
extern void DSound_DrawBufferVisualization(bool, bool *p_show, ImGuiWindowFlags);
DSound_DrawBufferVisualization(is_focus, &m_windows.cache_visualization, input_handler);
}

View file

@ -27,11 +27,8 @@
// 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
@ -47,7 +44,6 @@ typedef struct {
typedef struct {
bool cache_stats_general;
bool cache_visualization;
bool Reserved[3];
} imgui_audio_windows;

View file

@ -20,7 +20,7 @@ bool ImGuiUI::Initialize()
IMGUI_CHECKVERSION();
m_imgui_context = ImGui::CreateContext();
if (!m_imgui_context) {
CxbxrAbort("Unable to create ImGui context!");
CxbxrKrnlAbort("Unable to create ImGui context!");
return false;
}
@ -61,7 +61,7 @@ void ImGuiUI::Shutdown()
size_t ini_size = IMGUI_INI_SIZE_MAX;
const char* temp_ini_settings = ImGui::SaveIniSettingsToMemory(&ini_size);
if (ini_size > IMGUI_INI_SIZE_MAX) {
CxbxrAbort("ImGui ini settings is too large: %d > %d (IMGUI_INI_SIZE_MAX)", ini_size, IMGUI_INI_SIZE_MAX);
CxbxrKrnlAbort("ImGui ini settings is too large: %d > %d (IMGUI_INI_SIZE_MAX)", ini_size, IMGUI_INI_SIZE_MAX);
}
g_EmuShared->SetImGuiIniSettings(temp_ini_settings);
g_EmuShared->SetOverlaySettings(&m_settings);
@ -120,11 +120,8 @@ 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);
@ -142,19 +139,17 @@ void ImGuiUI::DrawMenu()
void ImGuiUI::DrawWidgets()
{
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) {
if (m_settings.fps || m_settings.hle_lle_stats) {
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_topright", nullptr, overlay_window_flags)) {
if (ImGui::Begin("overlay_stats", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) {
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));
}
@ -185,28 +180,8 @@ void ImGuiUI::DrawWidgets()
- ImGui::GetScrollX() - 2 * ImGui::GetStyle().ItemSpacing.x);
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), flagString.c_str());
}
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();
}
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;

View file

@ -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

View file

@ -1,3 +1,6 @@
// 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
@ -51,8 +54,7 @@ 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
@ -90,9 +92,10 @@ uniform const float FOGENABLE : register(c29);
#define PS_FINALCOMBINERSETTING_CLAMP_SUM
#endif
// Hardcoded state will be inserted here
// <HARDCODED STATE GOES HERE>
// End hardcoded state
)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(
// 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
@ -114,7 +117,7 @@ uniform const float FOGENABLE : register(c29);
#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) % 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) 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
#endif
// PS_FINALCOMBINERSETTING_COMPLEMENT_V1, when defined, applies a modifier to the v1 input when calculating the sum register
@ -138,17 +141,15 @@ uniform const float FOGENABLE : register(c29);
#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(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,
#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,
// 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:
@ -170,6 +171,10 @@ uniform const float FOGENABLE : register(c29);
// 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)
{
@ -198,39 +203,20 @@ 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 * 256) + b) * 255)
#define TwoIntoOne(a,b) (((a * 255) * 256) + (b * 255)) / 255 // TODO : Verify whether this works at all !
#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(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)
#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)
// Declare one sampler per each {Sampler Type, Texture Stage} combination
// TODO : Generate sampler status?
@ -324,7 +310,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 = Normal2(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*--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
/*---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
@ -337,34 +323,6 @@ 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)
@ -398,11 +356,12 @@ 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, saturate(fogFactor)); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
fog = float4(c_fog.rgb, xIn.iFog); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
// Xbox shader program will be inserted here
// <XBOX SHADER PROGRAM GOES HERE>
// End Xbox shader program
// 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(
// Copy r0.rgba to output
PS_OUTPUT xOut;
@ -411,3 +370,5 @@ PS_OUTPUT main(const PS_INPUT xIn)
return xOut;
}
// End of pixel shader footer)DELIMITER" /* This terminates the footer raw string" // */

View file

@ -1,108 +0,0 @@
// 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;
}

View file

@ -1,3 +1,6 @@
// 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;
@ -320,20 +323,40 @@ 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);
// Temp variable for paired VS instruction
float4 temp;
// Xbox shader program)DELIMITER", /* This terminates the header raw string" // */
// Xbox shader program will be inserted here
// <XBOX SHADER PROGRAM GOES HERE>
// End Xbox shader program
R"DELIMITER(
// 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 = oFog.x; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
@ -345,3 +368,5 @@ VS_OUTPUT main(const VS_INPUT xIn)
return xOut;
}
// End of vertex shader footer)DELIMITER" /* This terminates the footer raw string" // */

File diff suppressed because it is too large Load diff

View file

@ -4256,27 +4256,3 @@ 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;
}

View file

@ -40,7 +40,26 @@
void LookupTrampolinesD3D();
// initialize render window
extern void CxbxInitWindow();
extern void CxbxInitWindow(bool bFullInit);
extern void CxbxSetPixelContainerHeader
(
xbox::X_D3DPixelContainer* pPixelContainer,
DWORD Common,
UINT Width,
UINT Height,
UINT Levels,
xbox::X_D3DFORMAT Format,
UINT Dimensions,
UINT Pitch
);
extern uint8_t *ConvertD3DTextureToARGB(
xbox::X_D3DPixelContainer *pXboxPixelContainer,
uint8_t *pSrc,
int *pWidth, int *pHeight,
int TextureStage = 0
);
void CxbxUpdateNativeD3DResources();
@ -144,14 +163,14 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetDisplayFieldStatus)
);
// ******************************************************************
// * patch: D3DDevice_BeginPush_4
// * patch: D3DDevice_BeginPush
// ******************************************************************
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count);
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush)(dword_xt Count);
// ******************************************************************
// * patch: D3DDevice_BeginPush_8
// * patch: D3DDevice_BeginPush2 //two arg version for xdk before 4531
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush_8)(dword_xt Count, dword_xt **ppPush);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush2)(dword_xt Count, dword_xt **ppPush);
// ******************************************************************
// * patch: D3DDevice_EndPush
@ -213,8 +232,8 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader)
dword_xt Address
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4)
(
dword_xt Address
);
@ -359,10 +378,6 @@ 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
// ******************************************************************
@ -509,7 +524,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader)
dword_xt Handle
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0)();
// ******************************************************************
// * patch: D3DDevice_CreateTexture2
@ -615,7 +630,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture)
dword_xt Stage
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4)
(
X_D3DBaseTexture *pTexture
);
@ -1394,27 +1409,18 @@ 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__LTCG_ecx2_eax3
// * patch: D3DDevice_DrawVertices_4
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4)
(
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
// ******************************************************************
@ -1426,11 +1432,11 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP)
uint_xt VertexStreamZeroStride
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt VertexCount,
uint_xt VertexStreamZeroStride
uint_xt VertexCount,
uint_xt VertexStreamZeroStride
);
@ -1910,15 +1916,12 @@ 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_8)(xbox::addr_xt _this, xbox::ulong_xt Unknown1);
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)( 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

View file

@ -237,24 +237,6 @@ 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
@ -302,7 +284,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(fogFactor));
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(input.iFog));
}
// Add specular if enabled

View file

@ -63,16 +63,10 @@ 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;
// 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;
const int SAMPLE_NONE = 0;
const int SAMPLE_2D = 1;
const int SAMPLE_3D = 2;
const int SAMPLE_CUBE = 3;
// This state is passed to the shader
struct PsTextureStageState {
@ -131,11 +125,7 @@ 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

View file

@ -288,8 +288,20 @@ float DoFog(const VS_INPUT xIn)
fogDepth = abs(Projection.Position.z);
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W)
fogDepth = Projection.Position.w;
return fogDepth;
// 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;
}
float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
@ -339,9 +351,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(0, 0, 1);
texCoord.yzw = float3(1, 0, 0);
if (componentCount == 2)
texCoord.zw = float2(0, 1);
texCoord.zw = float2(1, 0);
if (componentCount == 3)
texCoord.w = 1;
} // Generate texture coordinates
@ -378,15 +390,14 @@ 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.yz = texCoord.x;
//if (countFlag == 2)
//texCoord.z = texCoord.y;
//texCoord.xyzw = texCoord.xyzw / texCoord.w;
if (countFlag == 1)
texCoord.yzw = texCoord.x;
if (countFlag == 2)
texCoord.zw = texCoord.y;
if (countFlag == 3)
texCoord.w = texCoord.z;
}
return texCoord;

View file

@ -289,7 +289,12 @@ bool IsTextureSampled(DecodedRegisterCombiner* pShader, int reg)
void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
{
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[0]; // Start with the HLSL template header
// 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 << "\n#define ALPHAKILL {"
<< (pShader->AlphaKill[0] ? "true, " : "false, ")
@ -336,9 +341,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 << g_ShaderSources.pixelShaderTemplateHlsl[1];
hlsl << hlsl_template[1];
hlsl << hlsl_template[2];
// Generate all four texture stages
for (unsigned i = 0; i < PSH_XBOX_MAX_T_REGISTER_COUNT; i++) {
@ -385,7 +390,7 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
FinalCombinerStageHlsl(hlsl, pShader->FinalCombiner, pShader->hasFinalCombiner);
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[2]; // Finish with the HLSL template footer
hlsl << hlsl_template[3]; // Finish with the HLSL template footer
}
// recompile xbox pixel shader function

View file

@ -36,14 +36,20 @@ void SetXboxMultiSampleType(xbox::X_D3DMULTISAMPLE_TYPE value);
bool XboxRenderStateConverter::Init()
{
// Get render state
if (g_SymbolAddresses.find("D3D_g_RenderState") != g_SymbolAddresses.end()) {
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3D_g_RenderState"];
if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) {
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3DDeferredRenderState"];
} 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();
@ -64,6 +70,54 @@ 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");

View file

@ -29,18 +29,10 @@
#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();
@ -148,173 +140,3 @@ 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;
});
}

View file

@ -1,6 +1,5 @@
#pragma once
#include <atomic>
#include <string> // std::string
#include <d3dcompiler.h> // ID3DBlob (via d3d9.h > d3d11shader.h > d3dcommon.h)
@ -11,38 +10,3 @@ 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;

View file

@ -38,7 +38,7 @@
#include <optional>
typedef struct {
const char* S; // String representation.
char* S; // String representation.
bool IsSamplerState; // True if the state maps to a Sampler State instead of Texture Stage
DWORD PC; // PC Index
} TextureStateInfo;
@ -79,12 +79,11 @@ TextureStateInfo CxbxTextureStateInfo[] = {
bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState)
{
// Deferred states start at 0, this means that D3D_g_DeferredTextureState IS D3D__TextureState
// Deferred states start at 0, this means that D3DDeferredTextureState IS D3D__TextureState
// No further works is required to derive the offset
if (g_SymbolAddresses.find("D3D_g_DeferredTextureState") != g_SymbolAddresses.end()) {
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3D_g_DeferredTextureState"];
if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) {
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"];
} else {
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_DeferredTextureState was not found!");
return false;
}
@ -362,9 +361,9 @@ DWORD NormalizeValue(DWORD xboxState, DWORD value) {
uint32_t XboxTextureStateConverter::Get(int textureStage, DWORD xboxState) {
if (textureStage < 0 || textureStage > 3)
CxbxrAbort("Requested texture stage was out of range: %d", textureStage);
CxbxrKrnlAbort("Requested texture stage was out of range: %d", textureStage);
if (xboxState < xbox::X_D3DTSS_FIRST || xboxState > xbox::X_D3DTSS_LAST)
CxbxrAbort("Requested texture state was out of range: %d", xboxState);
CxbxrKrnlAbort("Requested texture state was out of range: %d", xboxState);
// Read the value of the current stage/state from the Xbox data structure
DWORD rawValue = D3D__TextureState[(textureStage * xbox::X_D3DTS_STAGESIZE) + XboxTextureStateOffsets[xboxState]];

View file

@ -5,11 +5,14 @@
#include "core\kernel\init\CxbxKrnl.h" // implicit CxbxKrnl_Xbe used in LOG_TEST_CASE
#include "core\kernel\support\Emu.h" // LOG_TEST_CASE (via Logging.h)
#include <fstream>
#include <sstream> // std::stringstream
extern const char* g_vs_model = vs_model_3_0;
void DestRegisterHlsl(std::stringstream& hlsl, VSH_IMD_DEST& dest) {
// HLSL generation
void OutputHlsl(std::stringstream& hlsl, VSH_IMD_OUTPUT& dest)
{
static const char* OReg_Name[/*VSH_OREG_NAME*/] = {
"oPos",
"???",
@ -30,37 +33,34 @@ void DestRegisterHlsl(std::stringstream& hlsl, VSH_IMD_DEST& dest) {
};
switch (dest.Type) {
case IMD_DEST_C:
case IMD_OUTPUT_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_DEST_R:
case IMD_OUTPUT_R:
hlsl << "r" << dest.Address;
break;
case IMD_DEST_O:
case IMD_OUTPUT_O:
assert(dest.Address < OREG_A0X);
hlsl << OReg_Name[dest.Address];
break;
case IMD_DEST_A0X:
case IMD_OUTPUT_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_DEST_O &&
if (dest.Type == IMD_OUTPUT_O &&
dest.Address == OREG_OFOG &&
dest.Mask != MASK_X)
{
@ -77,9 +77,9 @@ void DestMaskHlsl(std::stringstream& hlsl, VSH_IMD_DEST& dest)
if (dest.Mask & MASK_W) hlsl << "w";
}
void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool IndexesWithA0_X, bool useTemp)
void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool IndexesWithA0_X)
{
static const char* RegisterName[/*VSH_PARAMETER_TYPE*/] = {
static char* RegisterName[/*VSH_PARAMETER_TYPE*/] = {
"?", // PARAM_UNKNOWN = 0,
"r", // PARAM_R, // Temporary (scRatch) registers
"v", // PARAM_V, // Vertex registers
@ -91,10 +91,7 @@ void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool Index
hlsl << "-";
}
if (useTemp) {
hlsl << "temp";
}
else if (param.Type == PARAM_C) {
if (param.ParameterType == 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:
@ -114,7 +111,7 @@ void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool Index
}
}
else {
hlsl << RegisterName[param.Type] << param.Address;
hlsl << RegisterName[param.ParameterType] << param.Address;
}
// Write the swizzle if we need to
@ -177,107 +174,25 @@ void BuildShader(IntermediateVertexShader* pShader, std::stringstream& hlsl)
/*ILU_LIT:*/"x_lit" // = 7 - all values of the 3 bits are used
};
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 << "(";
for (size_t i = 0; i < pShader->Instructions.size(); i++) {
VSH_INTERMEDIATE_FORMAT& IntermediateInstruction = pShader->Instructions[i];
DestRegisterHlsl(hlsl, dest);
DestMaskHlsl(hlsl, dest);
std::string str;
if (IntermediateInstruction.MAC > MAC_NOP) {
str = VSH_MAC_HLSL[IntermediateInstruction.MAC];
}
else {
str = VSH_ILU_HLSL[IntermediateInstruction.ILU];
}
for (int i = 0; i < paramCount; i++) {
hlsl << "\n " << str << "("; // opcode
OutputHlsl(hlsl, IntermediateInstruction.Output);
for (unsigned i = 0; i < IntermediateInstruction.ParamCount; i++) {
hlsl << ", ";
ParameterHlsl(hlsl, params[i], indexesWithA0_X, iluUseTempParam);
ParameterHlsl(hlsl, IntermediateInstruction.Parameters[i], IntermediateInstruction.IndexesWithA0_X);
}
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
}
}
@ -288,23 +203,26 @@ extern HRESULT EmuCompileVertexShader
ID3DBlob** ppHostShader
)
{
assert(pIntermediateShader->Instructions.size() > 0);
// Include HLSL header and footer as raw strings :
static std::string hlsl_template[2] = {
#include "core\hle\D3D8\Direct3D9\CxbxVertexShaderTemplate.hlsl"
};
// 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
hlsl_stream << hlsl_template[0]; // Start with the HLSL template header
assert(pIntermediateShader->Instructions.size() > 0);
BuildShader(pIntermediateShader, hlsl_stream);
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[1]; // Finish with the HLSL template footer
hlsl_stream << hlsl_template[1]; // Finish with the HLSL template footer
std::string hlsl_str = hlsl_stream.str();
const char* notionalSourceName = "CxbxVertexShaderTemplate.hlsl";
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, notionalSourceName);
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
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, notionalSourceName);
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
}
return hRet;
@ -312,10 +230,174 @@ extern HRESULT EmuCompileVertexShader
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader)
{
EmuCompileShader(g_ShaderSources.fixedFunctionVertexShaderHlsl, g_vs_model, ppHostShader, g_ShaderSources.fixedFunctionVertexShaderPath.c_str());
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;
};
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
static ID3DBlob* pPassthroughShader = nullptr;
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
{
EmuCompileShader(g_ShaderSources.vertexShaderPassthroughHlsl, g_vs_model, ppHostShader, g_ShaderSources.vertexShaderPassthroughPath.c_str());
// 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;
}

View file

@ -21,5 +21,5 @@ extern HRESULT EmuCompileVertexShader
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader);
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);

View file

@ -1,16 +1,15 @@
#define LOG_PREFIX CXBXR_MODULE::VSHCACHE
#include "VertexShaderCache.h"
#include "VertexShaderSource.h"
#include "core/kernel/init/CxbxKrnl.h"
#include "util/hasher.h"
#include "core/kernel/support/Emu.h"
VertexShaderCache g_VertexShaderCache = VertexShaderCache();
VertexShaderSource g_VertexShaderSource = VertexShaderSource();
// 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;
@ -26,7 +25,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
// Find a shader
// Return true if the shader was found
bool VertexShaderCache::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
auto it = cache.find(key);
if (it == cache.end()) {
// We didn't find anything! Was CreateShader called?
@ -40,7 +39,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
// Create a new shader
// If the shader was already created, just increase its reference count
ShaderKey VertexShaderCache::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
IntermediateVertexShader intermediateShader;
*pXboxFunctionSize = GetVshFunctionSize(pXboxFunction);
@ -87,7 +86,7 @@ ShaderKey VertexShaderCache::CreateShader(const xbox::dword_xt* pXboxFunction, D
}
// Get a shader using the given key
IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
{
LazyVertexShader* pLazyShader = nullptr;
@ -114,12 +113,6 @@ IDirect3DVertexShader* VertexShaderCache::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
(
@ -152,7 +145,7 @@ IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
}
// Release a shader. Doesn't actually release any resources for now
void VertexShaderCache::ReleaseShader(ShaderKey key)
void VertexShaderSource::ReleaseShader(ShaderKey key)
{
// For now, don't bother releasing any shaders
LazyVertexShader* pLazyShader;
@ -172,25 +165,8 @@ void VertexShaderCache::ReleaseShader(ShaderKey key)
}
}
void VertexShaderCache::ResetD3DDevice(IDirect3DDevice9* newDevice)
void VertexShaderSource::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();
}

View file

@ -8,7 +8,7 @@
typedef uint64_t ShaderKey;
// Manages creation and caching of vertex shaders
class VertexShaderCache {
class VertexShaderSource {
public:
ShaderKey CreateShader(const xbox::dword_xt* pXboxFunction, DWORD* pXboxFunctionSize);
@ -16,7 +16,6 @@ public:
void ReleaseShader(ShaderKey key);
void ResetD3DDevice(IDirect3DDevice9* pD3DDevice);
void Clear();
// TODO
// WriteCacheToDisk
@ -40,9 +39,9 @@ private:
std::mutex cacheMutex;
std::map<ShaderKey, LazyVertexShader> cache;
bool _FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
};
extern VertexShaderCache g_VertexShaderCache;
extern VertexShaderSource g_VertexShaderSource;
#endif

View file

@ -144,22 +144,6 @@ 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);

View file

@ -28,7 +28,6 @@ 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

View file

@ -861,7 +861,7 @@ typedef struct _FormatInfo {
_ComponentEncoding components;
D3DFORMAT pc;
_FormatUsage usage;
const char *warning;
char *warning;
} FormatInfo;
static const FormatInfo FormatInfos[] = {
@ -1047,7 +1047,7 @@ D3DFORMAT EmuXB2PC_D3DFormat(xbox::X_D3DFORMAT Format)
case ((xbox::X_D3DFORMAT)0xffffffff):
return D3DFMT_UNKNOWN; // TODO -oCXBX: Not sure if this counts as swizzled or not...
default:
CxbxrAbort("EmuXB2PC_D3DFormat: Unknown Format (0x%.08X)", Format);
CxbxrKrnlAbort("EmuXB2PC_D3DFormat: Unknown Format (0x%.08X)", Format);
}
return D3DFMT_UNKNOWN;
@ -1056,8 +1056,8 @@ D3DFORMAT EmuXB2PC_D3DFormat(xbox::X_D3DFORMAT Format)
xbox::X_D3DFORMAT EmuPC2XB_D3DFormat(D3DFORMAT Format, bool bPreferLinear)
{
xbox::X_D3DFORMAT result;
switch(Format)
{
switch(Format)
{
case D3DFMT_YUY2:
result = xbox::X_D3DFMT_YUY2;
break;
@ -1129,10 +1129,10 @@ xbox::X_D3DFORMAT EmuPC2XB_D3DFormat(D3DFORMAT Format, bool bPreferLinear)
result = xbox::X_D3DFMT_VERTEXDATA;
break;
default:
CxbxrAbort("EmuPC2XB_D3DFormat: Unknown Format (%d)", Format);
}
CxbxrKrnlAbort("EmuPC2XB_D3DFormat: Unknown Format (%d)", Format);
}
return result;
return result;
}
DWORD EmuXB2PC_D3DLock(DWORD Flags)
@ -1591,220 +1591,6 @@ Direct3D9 states unused :
D3DRS_BLENDOPALPHA = 209 // Blending operation for the alpha channel when D3DRS_SEPARATEDESTALPHAENABLE is TRUE
*/
xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::dword_xt XboxPixelContainer_Format)
{
xbox::X_D3DFORMAT d3d_format = (xbox::X_D3DFORMAT)((XboxPixelContainer_Format & X_D3DFORMAT_FORMAT_MASK) >> X_D3DFORMAT_FORMAT_SHIFT);
return d3d_format;
}
xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::X_D3DPixelContainer* pXboxPixelContainer)
{
// Don't pass in unassigned Xbox pixel container
assert(pXboxPixelContainer != xbox::zeroptr);
return GetXboxPixelContainerFormat(pXboxPixelContainer->Format);
}
void CxbxGetPixelContainerMeasures
(
xbox::X_D3DPixelContainer* pPixelContainer,
// TODO : Add X_D3DCUBEMAP_FACES argument
DWORD dwMipMapLevel, // unused - TODO : Use
UINT* pWidth,
UINT* pHeight,
UINT* pDepth,
UINT* pRowPitch,
// Slice pitch (does not include mipmaps!)
UINT* pSlicePitch
)
{
DWORD Size = pPixelContainer->Size;
xbox::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer);
if (Size != 0)
{
*pDepth = 1;
*pWidth = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1;
*pHeight = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1;
*pRowPitch = (((Size & X_D3DSIZE_PITCH_MASK) >> X_D3DSIZE_PITCH_SHIFT) + 1) * X_D3DTEXTURE_PITCH_ALIGNMENT;
}
else
{
DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT;
DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT;
DWORD l2d = (pPixelContainer->Format & X_D3DFORMAT_PSIZE_MASK) >> X_D3DFORMAT_PSIZE_SHIFT;
DWORD dwBPP = EmuXBFormatBitsPerPixel(X_Format);
*pDepth = 1 << l2d;
*pHeight = 1 << l2h;
*pWidth = 1 << l2w;
*pRowPitch = (*pWidth) * dwBPP / 8;
}
*pSlicePitch = (*pRowPitch) * (*pHeight);
if (EmuXBFormatIsCompressed(X_Format)) {
*pRowPitch *= 4;
}
}
bool ConvertD3DTextureToARGBBuffer(
xbox::X_D3DFORMAT X_Format,
uint8_t* pSrc,
int SrcWidth, int SrcHeight, int SrcRowPitch, int SrcSlicePitch,
uint8_t* pDst, int DstRowPitch, int DstSlicePitch,
unsigned int uiDepth ,
xbox::PVOID pPalleteData
)
{
const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format);
if (ConvertRowToARGB == nullptr)
return false; // Unhandled conversion
uint8_t* unswizleBuffer = nullptr;
if (EmuXBFormatIsSwizzled(X_Format)) {
unswizleBuffer = (uint8_t*)malloc(SrcSlicePitch * uiDepth); // TODO : Reuse buffer when performance is important
// First we need to unswizzle the texture data
EmuUnswizzleBox(
pSrc, SrcWidth, SrcHeight, uiDepth,
EmuXBFormatBytesPerPixel(X_Format),
// Note : use src pitch on dest, because this is an intermediate step :
unswizleBuffer, SrcRowPitch, SrcSlicePitch
);
// Convert colors from the unswizzled buffer
pSrc = unswizleBuffer;
}
int AdditionalArgument;
if (X_Format == xbox::X_D3DFMT_P8)
AdditionalArgument = (int)pPalleteData;
else
AdditionalArgument = DstRowPitch;
if (EmuXBFormatIsCompressed(X_Format)) {
if (SrcWidth < 4 || SrcHeight < 4) {
// HACK: The compressed DXT conversion code currently writes more pixels than it should, which can cause a crash.
// This code will get hit when converting compressed texture mipmaps on hardware that somehow doesn't support DXT natively
// (or lied when Cxbx asked it if it does!)
EmuLog(LOG_LEVEL::WARNING, "Converting DXT textures smaller than a block is not currently implemented. Ignoring conversion!");
return true;
}
// All compressed formats (DXT1, DXT3 and DXT5) encode blocks of 4 pixels on 4 lines
SrcHeight = (SrcHeight + 3) / 4;
DstRowPitch *= 4;
}
uint8_t* pSrcSlice = pSrc;
uint8_t* pDstSlice = pDst;
for (unsigned int z = 0; z < uiDepth; z++) {
uint8_t* pSrcRow = pSrcSlice;
uint8_t* pDstRow = pDstSlice;
for (int y = 0; y < SrcHeight; y++) {
*(int*)pDstRow = AdditionalArgument; // Dirty hack, to avoid an extra parameter to all conversion callbacks
ConvertRowToARGB(pSrcRow, pDstRow, SrcWidth);
pSrcRow += SrcRowPitch;
pDstRow += DstRowPitch;
}
pSrcSlice += SrcSlicePitch;
pDstSlice += DstSlicePitch;
}
if (unswizleBuffer)
free(unswizleBuffer);
return true;
}
// Called by WndMain::LoadGameLogo() to load game logo bitmap
uint8_t* ConvertD3DTextureToARGB(
xbox::X_D3DPixelContainer* pXboxPixelContainer,
uint8_t* pSrc,
int* pWidth, int* pHeight,
xbox::PVOID pPalleteData // default = zeroptr
)
{
// Avoid allocating pDest when ConvertD3DTextureToARGBBuffer will fail anyway
xbox::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pXboxPixelContainer);
const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format);
if (ConvertRowToARGB == nullptr)
return nullptr; // Unhandled conversion
unsigned int SrcDepth, SrcRowPitch, SrcSlicePitch;
CxbxGetPixelContainerMeasures(
pXboxPixelContainer,
0, // dwMipMapLevel
(UINT*)pWidth,
(UINT*)pHeight,
&SrcDepth,
&SrcRowPitch,
&SrcSlicePitch
);
// Now we know ConvertD3DTextureToARGBBuffer will do it's thing, allocate the resulting buffer
int DstDepth = 1; // for now TODO : Use SrcDepth when supporting volume textures
int DstRowPitch = (*pWidth) * sizeof(DWORD); // = sizeof ARGB pixel. TODO : Is this correct?
int DstSlicePitch = DstRowPitch * (*pHeight); // TODO : Is this correct?
int DstSize = DstSlicePitch * DstDepth;
uint8_t* pDst = (uint8_t*)malloc(DstSize);
// And convert the source towards that buffer
/*ignore result*/ConvertD3DTextureToARGBBuffer(
X_Format,
pSrc, *pWidth, *pHeight, SrcRowPitch, SrcSlicePitch,
pDst, DstRowPitch, DstSlicePitch,
DstDepth,
pPalleteData);
// NOTE : Caller must take ownership!
return pDst;
}
void CxbxSetPixelContainerHeader
(
xbox::X_D3DPixelContainer* pPixelContainer,
DWORD Common,
UINT Width,
UINT Height,
UINT Levels,
xbox::X_D3DFORMAT Format,
UINT Dimensions,
UINT Pitch
)
{
// Set X_D3DResource field(s) :
pPixelContainer->Common = Common;
// DON'T SET pPixelContainer->Data
// DON'T SET pPixelContainer->Lock
// Are Width and Height both a power of two?
DWORD l2w; _BitScanReverse(&l2w, Width); // MSVC intrinsic; GCC has __builtin_clz
DWORD l2h; _BitScanReverse(&l2h, Height);
DWORD l2d = 0; // TODO : Set this via input argument
if (((1 << l2w) == Width) && ((1 << l2h) == Height)) {
Width = Height = Pitch = 1; // When setting Format, clear Size field
}
else {
l2w = l2h = l2d = 0; // When setting Size, clear D3DFORMAT_USIZE, VSIZE and PSIZE
}
// Set X_D3DPixelContainer field(s) :
pPixelContainer->Format = 0
| ((Dimensions << X_D3DFORMAT_DIMENSION_SHIFT) & X_D3DFORMAT_DIMENSION_MASK)
| (((DWORD)Format << X_D3DFORMAT_FORMAT_SHIFT) & X_D3DFORMAT_FORMAT_MASK)
| ((Levels << X_D3DFORMAT_MIPMAP_SHIFT) & X_D3DFORMAT_MIPMAP_MASK)
| ((l2w << X_D3DFORMAT_USIZE_SHIFT) & X_D3DFORMAT_USIZE_MASK)
| ((l2h << X_D3DFORMAT_VSIZE_SHIFT) & X_D3DFORMAT_VSIZE_MASK)
| ((l2d << X_D3DFORMAT_PSIZE_SHIFT) & X_D3DFORMAT_PSIZE_MASK)
;
pPixelContainer->Size = 0
| (((Width - 1) /*X_D3DSIZE_WIDTH_SHIFT*/) & X_D3DSIZE_WIDTH_MASK)
| (((Height - 1) << X_D3DSIZE_HEIGHT_SHIFT) & X_D3DSIZE_HEIGHT_MASK)
| (((Pitch - 1) << X_D3DSIZE_PITCH_SHIFT) & X_D3DSIZE_PITCH_MASK)
;
}
#if 0
/* Generic swizzle function, usable for both x and y dimensions.
When passing x, Max should be 2*height, and Shift should be 0

View file

@ -25,7 +25,6 @@
#ifndef XBCONVERT_H
#define XBCONVERT_H
#include "common/cxbxr.hpp"
#include "core\kernel\init\CxbxKrnl.h"
#include "core\hle\D3D8\XbD3D8Types.h"
@ -91,7 +90,7 @@ else if((uint32)State < 20)
else if((uint32)State > 255)
State = (D3DTRANSFORMSTATETYPE)(State - 250);
else
CxbxrAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
CxbxrKrnlAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
//*/
// convert from xbox to pc texture transform state types
@ -116,7 +115,7 @@ inline D3DTRANSFORMSTATETYPE EmuXB2PC_D3DTS(xbox::X_D3DTRANSFORMSTATETYPE State)
return D3DTS_WORLDMATRIX(State - 256);
}
CxbxrAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
CxbxrKrnlAbortEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
return (D3DTRANSFORMSTATETYPE)0;
}
@ -1806,12 +1805,12 @@ typedef enum _TXBType {
} TXBType;
typedef struct _RenderStateInfo {
const char *S; // String representation.
char *S; // String representation.
WORD V; // The XDK version since which a render state was introduced (using the 5911 declarations as a base).
TXBType T = xt_Unknown; // The Xbox data type. Defaults to xt_Unknown.
xbox::NV2AMETHOD M; // The related push buffer method. Not always a 1-to-1 mapping. Needs push-buffer interpretation & conversion code.
D3DRENDERSTATETYPE PC = (D3DRENDERSTATETYPE)0; // Map XBox to PC render state
const char *N; // XDK notes. Defaults to ''.
char *N; // XDK notes. Defaults to ''.
WORD R; // The XDK version since which a render state was removed
}
RenderStateInfo;
@ -1820,36 +1819,5 @@ RenderStateInfo;
extern const RenderStateInfo& GetDxbxRenderStateInfo(int State);
extern xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::dword_xt XboxPixelContainer_Format);
extern xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::X_D3DPixelContainer* pXboxPixelContainer);
extern bool ConvertD3DTextureToARGBBuffer(
xbox::X_D3DFORMAT X_Format,
uint8_t* pSrc,
int SrcWidth, int SrcHeight, int SrcRowPitch, int SrcSlicePitch,
uint8_t* pDst, int DstRowPitch, int DstSlicePitch,
unsigned int uiDepth = 1,
xbox::PVOID pPalleteData = xbox::zeroptr
);
extern void CxbxSetPixelContainerHeader
(
xbox::X_D3DPixelContainer* pPixelContainer,
DWORD Common,
UINT Width,
UINT Height,
UINT Levels,
xbox::X_D3DFORMAT Format,
UINT Dimensions,
UINT Pitch
);
extern uint8_t* ConvertD3DTextureToARGB(
xbox::X_D3DPixelContainer* pXboxPixelContainer,
uint8_t* pSrc,
int* pWidth, int* pHeight,
xbox::PVOID pPalleteData = xbox::zeroptr
);
#endif

View file

@ -39,20 +39,17 @@
#include "core\kernel\support\Emu.h"
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice, g_pXbox_PixelShader
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
#include "core\hle\D3D8\XbPixelShader.h"
#include "core\hle\D3D8\Direct3D9\PixelShader.h" // EmuCompilePixelShader
#include "core\hle\D3D8\XbD3D8Logging.h" // For D3DErrorString()
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrAbort()
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort()
#include "util\hasher.h"
#include "core\hle\D3D8\Direct3D9\FixedFunctionPixelShader.hlsli"
#include "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
#include <assert.h> // assert()
#include <process.h>
#include <locale.h>
#include <filesystem>
#include <fstream>
#include <sstream>
@ -558,12 +555,6 @@ 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;
}
}
}
}
@ -661,12 +652,35 @@ 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_FOGENABLE + 1; // = 30
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;
}
std::string_view GetD3DTOPString(int d3dtop) {
static constexpr std::string_view opToString[] = {
@ -768,21 +782,6 @@ 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,74 +864,68 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
}
// Build and compile a new shader
std::string hlslTemplate = g_ShaderSources.fixedFunctionPixelShaderHlsl;
auto hlslTemplate = GetFixedFunctionShaderTemplate();
// 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;
if (sampleTypeReplace != std::string::npos) {
static constexpr std::string_view typeToString[] = {
"SAMPLE_NONE",
"SAMPLE_2D",
"SAMPLE_3D",
"SAMPLE_CUBE"
};
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]] << "};";
finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
}
auto 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);
if (stageDefInsert != std::string::npos) {
stageDefInsert += stageDef.size();
auto stageDefInsert = finalShader.find(stageDef) + 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';
}
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
stageSetup << '\n';
}
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
// Compile the shader
ID3DBlob* pShaderBlob;
@ -944,15 +937,12 @@ 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;
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();
}
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
if (hRet != S_OK)
CxbxrKrnlAbort("Failed to compile fixed function pixel shader");
pShaderBlob->Release();
// Insert the shader into the cache
ffPsCache[key] = pShader;
@ -975,10 +965,7 @@ 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++) {
@ -1035,21 +1022,6 @@ 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;
@ -1171,18 +1143,7 @@ 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:

View file

@ -469,15 +469,12 @@ 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);
// 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!");
return; // For now, don't even attempt to run through
}
else {
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL");

View file

@ -84,7 +84,7 @@ void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT HostStreamN
uiCachedHostVertexStride);
//DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetStreamSource");
if (FAILED(hRet)) {
CxbxrAbort("Failed to set the type patched buffer as the new stream source!\n");
CxbxrKrnlAbort("Failed to set the type patched buffer as the new stream source!\n");
// TODO : test-case : XDK Cartoon hits the above case when the vertex cache size is 0.
}
}
@ -244,7 +244,7 @@ void CxbxVertexBufferConverter::ConvertStream
if (pDrawContext->pXboxVertexStreamZeroData != xbox::zeroptr) {
// There should only be one stream (stream zero) in this case
if (XboxStreamNumber != 0) {
CxbxrAbort("Trying to patch a Draw..UP with more than stream zero!");
CxbxrKrnlAbort("Trying to patch a Draw..UP with more than stream zero!");
}
pXboxVertexData = (uint8_t *)pDrawContext->pXboxVertexStreamZeroData;
@ -350,7 +350,7 @@ void CxbxVertexBufferConverter::ConvertStream
pHostVertexData = (uint8_t*)malloc(dwHostVertexDataSize);
if (pHostVertexData == nullptr) {
CxbxrAbort("Couldn't allocate the new stream zero buffer");
CxbxrKrnlAbort("Couldn't allocate the new stream zero buffer");
}
} else {
HRESULT hRet = g_pD3DDevice->CreateVertexBuffer(
@ -363,14 +363,14 @@ void CxbxVertexBufferConverter::ConvertStream
);
if (FAILED(hRet)) {
CxbxrAbort("Failed to create vertex buffer");
CxbxrKrnlAbort("Failed to create vertex buffer");
}
}
// If we need to lock a host vertex buffer, do so now
if (pHostVertexData == nullptr && pNewHostVertexBuffer != nullptr) {
if (FAILED(pNewHostVertexBuffer->Lock(0, 0, (D3DLockData **)&pHostVertexData, D3DLOCK_DISCARD))) {
CxbxrAbort("Couldn't lock vertex buffer");
CxbxrKrnlAbort("Couldn't lock vertex buffer");
}
}
@ -609,7 +609,7 @@ void CxbxVertexBufferConverter::ConvertStream
void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
{
if ((pDrawContext->XboxPrimitiveType < xbox::X_D3DPT_POINTLIST) || (pDrawContext->XboxPrimitiveType > xbox::X_D3DPT_POLYGON))
CxbxrAbort("Unknown primitive type: 0x%.02X\n", pDrawContext->XboxPrimitiveType);
CxbxrKrnlAbort("Unknown primitive type: 0x%.02X\n", pDrawContext->XboxPrimitiveType);
CxbxVertexDeclaration* pCxbxVertexDeclaration = CxbxGetVertexDeclaration();

View file

@ -34,8 +34,7 @@
#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\VertexShaderCache.h" // For g_VertexShaderCache
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
#include "core\hle\D3D8\Direct3D9\VertexShaderSource.h" // For g_VertexShaderSource
#include "core\hle\D3D8\XbVertexBuffer.h" // For CxbxImpl_SetVertexData4f
#include "core\hle\D3D8\XbVertexShader.h"
#include "core\hle\D3D8\XbD3D8Logging.h" // For DEBUG_D3DRESULT
@ -50,7 +49,6 @@
#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
@ -530,7 +528,7 @@ namespace XboxVertexShaderDecoder
return ((((CReg >> 5) & 7) - 3) * 32) + (CReg & 31);
}
static VSH_IMD_PARAMETER VshGetIntermediateParam(
static void VshConvertIntermediateParam(VSH_IMD_PARAMETER& Param,
uint32_t* pShaderToken,
VSH_FIELD_NAME FLD_MUX,
VSH_FIELD_NAME FLD_NEG,
@ -538,30 +536,80 @@ namespace XboxVertexShaderDecoder
uint16_t V,
uint16_t C)
{
VSH_IMD_PARAMETER param{};
param.Type = (VSH_IMD_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX);
switch (param.Type) {
Param.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX);
switch (Param.ParameterType) {
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));
}
return param;
static void VshAddIntermediateInstruction(
uint32_t* pShaderToken,
IntermediateVertexShader* pShader,
VSH_MAC MAC,
VSH_ILU ILU,
VSH_IMD_OUTPUT_TYPE output_type,
int16_t output_address,
int8_t output_mask)
{
// Is the output mask set?
if (output_mask == 0) {
return;
}
if (pShader->Instructions.size() >= VSH_MAX_INTERMEDIATE_COUNT) {
CxbxrKrnlAbort("Shader exceeds conversion buffer!");
}
VSH_INTERMEDIATE_FORMAT intermediate;
intermediate.MAC = MAC;
intermediate.ILU = ILU;
intermediate.Output.Type = output_type;
intermediate.Output.Address = output_address;
intermediate.Output.Mask = output_mask;
// Get a0.x indirect constant addressing
intermediate.IndexesWithA0_X = VshGetField(pShaderToken, FLD_A0X) > 0; // Applies to PARAM_C parameter reads
int16_t R;
int16_t V = VshGetField(pShaderToken, FLD_V);
int16_t C = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST));
intermediate.ParamCount = 0;
if (MAC >= MAC_MOV) {
// Get parameter A
R = VshGetField(pShaderToken, FLD_A_R);
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_A_MUX, FLD_A_NEG, R, V, C);
}
if ((MAC == MAC_MUL) || ((MAC >= MAC_MAD) && (MAC <= MAC_SGE))) {
// Get parameter B
R = VshGetField(pShaderToken, FLD_B_R);
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_B_MUX, FLD_B_NEG, R, V, C);
}
if ((ILU >= ILU_MOV) || (MAC == MAC_ADD) || (MAC == MAC_MAD)) {
// Get parameter C
R = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | VshGetField(pShaderToken, FLD_C_R_LOW);
VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_C_MUX, FLD_C_NEG, R, V, C);
}
// Add the instruction to the shader
pShader->Instructions.push_back(intermediate);
}
static bool VshConvertToIntermediate(uint32_t* pShaderToken, IntermediateVertexShader* pShader)
@ -571,86 +619,52 @@ 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) {
MacOp.Opcode = MAC;
if (MAC > MAC_NOP && MAC <= MAC_ARL) {
if (bIsPaired && RAddress == 1) {
// Ignore paired MAC opcodes that write to R1
}
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);
} 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));
}
}
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 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));
}
}
// Check if there's an ILU opcode
if (ILU != ILU_NOP) {
// Paired ILU opcodes will only write to R1
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);
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));
}
}
// 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;
}
};
@ -891,7 +905,7 @@ private:
return false;
}
assert(HostVertexElementDataType < D3DDECLTYPE_UNUSED);
assert(HostVertexElementDataType > 0);
assert(HostVertexElementByteSize > 0);
// Select new stream, if needed
@ -1126,49 +1140,10 @@ IDirect3DVertexDeclaration* CxbxCreateHostVertexDeclaration(D3DVERTEXELEMENT *pD
return pHostVertexDeclaration;
}
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;
}
static IDirect3DVertexShader* passthroughshader;
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
@ -1176,20 +1151,43 @@ void CxbxUpdateHostVertexShader()
LOG_INIT; // Allows use of DEBUG_D3DRESULT
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
HRESULT hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
if (FAILED(hRet)) CxbxrAbort("Failed to set fixed-function shader");
IDirect3DVertexShader* fixedFunctionShader = nullptr;
HRESULT hRet;
if (g_UseFixedFunctionVertexShader) {
static IDirect3DVertexShader* ffHlsl = nullptr;
if (ffHlsl == nullptr) {
ID3DBlob* pBlob = nullptr;
EmuCompileFixedFunction(&pBlob);
if (pBlob) {
hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &ffHlsl);
if (FAILED(hRet)) CxbxrKrnlAbort("Failed to create fixed-function shader");
}
}
fixedFunctionShader = ffHlsl;
}
hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
if (FAILED(hRet)) CxbxrKrnlAbort("Failed to set fixed-function shader");
}
else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) {
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughShader);
if (FAILED(hRet)) CxbxrAbort("Failed to set passthrough shader");
if (passthroughshader == nullptr) {
ID3DBlob* pBlob = nullptr;
EmuCompileXboxPassthrough(&pBlob);
if (pBlob) {
g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &passthroughshader);
}
}
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughshader);
}
else {
auto pTokens = GetCxbxVertexShaderSlotPtr(g_Xbox_VertexShader_FunctionSlots_StartAddress);
assert(pTokens);
// Create a vertex shader from the tokens
DWORD shaderSize;
auto VertexShaderKey = g_VertexShaderCache.CreateShader(pTokens, &shaderSize);
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderCache.GetShader(VertexShaderKey);
auto VertexShaderKey = g_VertexShaderSource.CreateShader(pTokens, &shaderSize);
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderSource.GetShader(VertexShaderKey);
HRESULT hRet = g_pD3DDevice->SetVertexShader(pHostVertexShader);
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
}
@ -1577,7 +1575,7 @@ void CxbxImpl_DeleteVertexShader(DWORD Handle)
RegisterCxbxVertexDeclaration(pCxbxVertexDeclaration->Key, nullptr); // Remove from cache (which will free present pCxbxVertexDeclaration)
// Release the host vertex shader
g_VertexShaderCache.ReleaseShader(pCxbxVertexShader->Key);
g_VertexShaderSource.ReleaseShader(pCxbxVertexShader->Key);
#endif
}

View file

@ -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_DEST_TYPE {
IMD_DEST_C,
IMD_DEST_R,
IMD_DEST_O,
IMD_DEST_A0X
enum VSH_IMD_OUTPUT_TYPE {
IMD_OUTPUT_C,
IMD_OUTPUT_R,
IMD_OUTPUT_O,
IMD_OUTPUT_A0X
};
typedef struct {
VSH_IMD_DEST_TYPE Type;
int16_t Address;
int8_t Mask; // If 0 skip writing to this output
} VSH_IMD_DEST;
typedef struct _VSH_IMD_OUTPUT {
VSH_IMD_OUTPUT_TYPE Type;
int16_t Address;
int8_t Mask;
} VSH_IMD_OUTPUT;
enum VSH_SWIZZLE {
SWIZZLE_X = 0,
@ -167,7 +167,7 @@ enum VSH_SWIZZLE {
SWIZZLE_W
};
enum VSH_IMD_PARAMETER_TYPE {
enum VSH_PARAMETER_TYPE {
PARAM_UNKNOWN = 0,
PARAM_R, // Temporary (scRatch) registers
PARAM_V, // Vertex registers
@ -175,49 +175,28 @@ enum VSH_IMD_PARAMETER_TYPE {
PARAM_O // = 0??
};
typedef struct {
VSH_IMD_PARAMETER_TYPE Type; // Parameter type, R, V or C
typedef struct _VSH_IMD_PARAMETER {
VSH_PARAMETER_TYPE ParameterType; // 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_ILU Opcode;
VSH_IMD_DEST Dest;
VSH_IMD_PARAMETER Parameter;
} VSH_IMD_ILU_OP;
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_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;
typedef struct _IntermediateVertexShader {
std::vector<VSH_INTERMEDIATE_FORMAT> Instructions;
} IntermediateVertexShader;
// parse xbox vertex shader function into an intermediate format

View file

@ -53,7 +53,6 @@
// 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()
{
@ -86,6 +85,10 @@ 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
@ -95,7 +98,6 @@ extern "C" {
void CxbxInitAudio()
{
g_EmuShared->GetAudioSettings(&g_XBAudio);
dsound_last = get_now();
}
#ifdef __cplusplus
@ -123,6 +125,10 @@ 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;
@ -160,13 +166,13 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
}
if (dsErrorMsg != nullptr) {
CxbxrAbort(dsErrorMsg, hRet);
CxbxrKrnlAbort(dsErrorMsg, hRet);
}
hRet = g_pDSound8->SetCooperativeLevel(GET_FRONT_WINDOW_HANDLE, DSSCL_PRIORITY);
if (hRet != DS_OK) {
CxbxrAbort("g_pDSound8->SetCooperativeLevel Failed!");
CxbxrKrnlAbort("g_pDSound8->SetCooperativeLevel Failed!");
}
// clear sound buffer cache
@ -194,7 +200,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
hRet = g_pDSound8->CreateSoundBuffer(&bufferDesc, &g_pDSoundPrimaryBuffer, nullptr);
if (hRet != DS_OK) {
CxbxrAbort("Creating primary buffer for DirectSound Failed!");
CxbxrKrnlAbort("Creating primary buffer for DirectSound Failed!");
}
/* Quote from MDSN "For the primary buffer, you must use the
@ -207,7 +213,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
hRet = g_pDSoundPrimaryBuffer->QueryInterface(IID_IDirectSound3DListener8, (LPVOID*)&g_pDSoundPrimary3DListener8);
if (hRet != DS_OK) {
CxbxrAbort("Creating primary 3D Listener for DirectSound Failed!");
CxbxrKrnlAbort("Creating primary 3D Listener for DirectSound Failed!");
}
initialized = true;
@ -359,140 +365,24 @@ xbox::void_xt WINAPI xbox::EMUPATCH(DirectSoundDoWork)()
return;
}
void StreamBufferAudio(xbox::XbHybridDSBuffer* pHybridBuffer, float msToCopy) {
auto pThis = pHybridBuffer->emuDSBuffer;
auto dsb = pThis->EmuDirectSoundBuffer8;
bool isAdpcm = pThis->EmuFlags & DSE_FLAG_XADPCM;
DWORD xBufferRangeStart;
DWORD xBufferRangeSize;
DSoundBufferRegionCurrentLocation(pHybridBuffer, pThis->EmuPlayFlags, xBufferRangeStart, xBufferRangeSize);
DWORD hostBufferSize = pThis->EmuBufferDesc.dwBufferBytes;
DWORD playCursor;
DWORD writeCursor;
dsb->GetCurrentPosition(&playCursor, &writeCursor);
DWORD cursorGap = writeCursor >= playCursor
? (writeCursor - playCursor)
: hostBufferSize - playCursor + writeCursor;
// Determine where to copy data from.
// Note: The DirectSound write cursor can sit quite far ahead of the play cursor,
// but copying closer to the play cursor can introduce weird looping or
// latency issues
// Test case: NBA Live 2005 (writes to a very small buffer expecting low latency, can crackle at > 1ms stream interval)
// Test case: Halo (intro video delay when writing from play cursor)
DWORD writeOffset = writeCursor + cursorGap * g_dsBufferStreaming.tweakCopyOffset;
DWORD writeSize = std::min(
(DWORD)(pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec * msToCopy / 1000),
hostBufferSize
);
DWORD blockSize = isAdpcm
? XBOX_ADPCM_DSTSIZE * pThis->EmuBufferDesc.lpwfxFormat->nChannels
: pThis->EmuBufferDesc.lpwfxFormat->nBlockAlign;
// ADPCM block alignment
writeOffset = ((writeOffset + blockSize / 2) / blockSize) * blockSize;
writeSize = ((writeSize + blockSize / 2) / blockSize) * blockSize;
writeOffset %= hostBufferSize;
DWORD xWriteOffset = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, writeOffset);
assert(xBufferRangeStart + xBufferRangeSize > xWriteOffset);
if (isAdpcm) {
assert(writeOffset % XBOX_ADPCM_DSTSIZE == 0);
assert(xWriteOffset % XBOX_ADPCM_SRCSIZE == 0);
}
LPVOID lplpvAudioPtr1, lplpvAudioPtr2;
DWORD lplpvAudioBytes1, lplpvAudioBytes2;
HRESULT hRet = pThis->EmuDirectSoundBuffer8->Lock(writeOffset, writeSize,
&lplpvAudioPtr1, &lplpvAudioBytes1,
&lplpvAudioPtr2, &lplpvAudioBytes2,
0);
if (hRet != 0) {
CxbxrAbort("DirectSoundBuffer Lock Failed!");
}
if (lplpvAudioPtr1 && pThis->X_BufferCache != nullptr) {
DSoundBufferOutputXBtoHost(
pThis->EmuFlags,
pThis->EmuBufferDesc,
((PBYTE)pThis->X_BufferCache + xBufferRangeStart + xWriteOffset),
DSoundBufferGetXboxBufferSize(pThis->EmuFlags, lplpvAudioBytes1),
lplpvAudioPtr1,
lplpvAudioBytes1
);
if (lplpvAudioPtr2) {
DSoundBufferOutputXBtoHost(
pThis->EmuFlags,
pThis->EmuBufferDesc,
((PBYTE)pThis->X_BufferCache + xBufferRangeStart + 0),
DSoundBufferGetXboxBufferSize(pThis->EmuFlags, lplpvAudioBytes2),
lplpvAudioPtr2,
lplpvAudioBytes2
);
}
HRESULT hRet = dsb->Unlock(lplpvAudioPtr1, lplpvAudioBytes1, lplpvAudioPtr2, lplpvAudioBytes2);
if (hRet != DS_OK) {
CxbxrAbort("DirectSoundBuffer Unlock Failed!");
}
}
}
void dsound_async_worker()
// For Async process purpose only
static void dsound_thread_worker(LPVOID nullPtr)
{
DSoundMutexGuardLock;
g_AffinityPolicy->SetAffinityOther();
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
DirectSoundDoWork_Stream(getTime);
}
while (true) {
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
Sleep(300);
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
{
DSoundMutexGuardLock;
void dsound_worker()
{
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
DSoundMutexGuardLock;
// Stream sound buffer audio
// because the title may change the content of sound buffers at any time
for (auto& pBuffer : g_pDSoundBufferCache) {
// Avoid expensive calls to DirectSound on buffers unless they've been played at least once
// Since some titles create a large amount of buffers, but only use a few
if (pBuffer->emuDSBuffer->EmuStreamingInfo.playRequested) {
DWORD status;
HRESULT hRet = pBuffer->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&status);
if (hRet == 0 && status & DSBSTATUS_PLAYING) {
auto streamMs = g_dsBufferStreaming.streamInterval + g_dsBufferStreaming.streamAhead;
StreamBufferAudio(pBuffer, streamMs);
}
}
}
}
uint64_t dsound_next(uint64_t now)
{
constexpr uint64_t dsound_period = 300 * 1000;
uint64_t next = dsound_last + dsound_period;
if (now >= next) {
dsound_async_worker();
dsound_last = get_now();
return dsound_period;
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
DirectSoundDoWork_Stream(getTime);
}
}
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.
@ -1055,7 +945,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(CDirectSound_SynchPlayback)
DSoundBufferSynchPlaybackFlagRemove(pDSBuffer->EmuFlags);
EmuLog(LOG_LEVEL::DEBUG, "SynchPlayback - pDSBuffer: %08X; EmuPlayFlags: %08X", *ppDSBuffer, pDSBuffer->EmuPlayFlags);
pDSBuffer->EmuDirectSoundBuffer8->Play(0, 0, pDSBuffer->EmuPlayFlags);
pDSBuffer->EmuStreamingInfo.playRequested = true;
}
}

View file

@ -110,10 +110,6 @@ struct EmuDirectSoundBuffer
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
X_DSVOICEPROPS Xb_VoiceProperties;
DWORD Xb_Flags;
struct {
// True if the buffer has been played, and should be considered for streaming
bool playRequested = false;
} EmuStreamingInfo;
};
struct XbHybridDSBuffer : DSBUFFER_S::DSBUFFER_I {

View file

@ -80,7 +80,6 @@ void DirectSoundDoWork_Buffer(xbox::LARGE_INTEGER &time)
pThis->Xb_rtPauseEx = 0LL;
pThis->EmuFlags &= ~DSE_FLAG_PAUSE;
pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
pThis->EmuStreamingInfo.playRequested = true;
}
if (pThis->Xb_rtStopEx != 0LL && pThis->Xb_rtStopEx <= time.QuadPart) {
@ -174,7 +173,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateBuffer)
hRet = xbox::EMUPATCH(DirectSoundCreate)(nullptr, &g_pDSound8, nullptr);
if (hRet != DS_OK) {
CxbxrAbort("Unable to initialize DirectSound!");
CxbxrKrnlAbort("Unable to initialize DirectSound!");
}
}
@ -239,7 +238,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateBuffer)
EmuLog(LOG_LEVEL::WARNING, output.str().c_str());
output.str("");
output << static_cast<DS_RESULT>(hRet);
CxbxrAbort("DSB: DSoundBufferCreate error: %s", output.str().c_str());
CxbxrKrnlAbort("DSB: DSoundBufferCreate error: %s", output.str().c_str());
}
else {
if (pdsbd->dwFlags & DSBCAPS_CTRL3D) {
@ -438,26 +437,55 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Lock)
LOG_FUNC_END;
EmuDirectSoundBuffer* pThis = pHybridThis->emuDSBuffer;
HRESULT hRet = D3D_OK;
DWORD pcmSize = DSoundBufferGetPCMBufferSize(pThis->EmuFlags, dwBytes);
DWORD pcmOffset = DSoundBufferGetPCMBufferSize(pThis->EmuFlags, dwOffset);
// Xbox directsound doesn't require locking buffers
// This Xbox api only exists to match PC
DSoundGenericUnlock(pThis->EmuFlags,
pThis->EmuDirectSoundBuffer8,
pThis->EmuBufferDesc,
pThis->Host_lock,
pThis->X_BufferCache,
pThis->X_lock.dwLockOffset,
pThis->X_lock.dwLockBytes1,
pThis->X_lock.dwLockBytes2);
if (dwOffset + dwBytes <= pThis->X_BufferCacheSize) {
*pdwAudioBytes1 = dwBytes;
*ppvAudioPtr1 = (PBYTE)pThis->X_BufferCache + dwOffset;
if (ppvAudioPtr2 != nullptr) {
*ppvAudioPtr2 = nullptr;
*pdwAudioBytes2 = 0;
}
}
else {
*pdwAudioBytes1 = pThis->X_BufferCacheSize - dwOffset;
*ppvAudioPtr1 = (PBYTE)pThis->X_BufferCache + dwOffset;
if (ppvAudioPtr2 != nullptr) {
*pdwAudioBytes2 = dwBytes - *pdwAudioBytes1;
*ppvAudioPtr2 = (PBYTE)pThis->X_BufferCache;
}
}
if (ppvAudioPtr2 == xbox::zeroptr) {
hRet = pThis->EmuDirectSoundBuffer8->Lock(pcmOffset, pcmSize, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
nullptr, 0, dwFlags);
pThis->Host_lock.pLockPtr2 = nullptr;
} else {
hRet = pThis->EmuDirectSoundBuffer8->Lock(pcmOffset, pcmSize, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
&pThis->Host_lock.pLockPtr2, &pThis->Host_lock.dwLockBytes2, dwFlags);
}
if (hRet != DS_OK) {
CxbxrKrnlAbort("IDirectSoundBuffer_Lock Failed!");
}
// Host lock position
pThis->Host_lock.dwLockOffset = pcmOffset;
pThis->Host_lock.dwLockFlags = dwFlags;
pThis->X_lock.dwLockFlags = dwFlags;
// Emulate to xbox's lock position
pThis->X_lock.dwLockOffset = dwOffset;
*ppvAudioPtr1 = pThis->X_lock.pLockPtr1 = ((LPBYTE)pThis->X_BufferCache + dwOffset);
*pdwAudioBytes1 = pThis->X_lock.dwLockBytes1 = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, pThis->Host_lock.dwLockBytes1);
if (pThis->Host_lock.pLockPtr2 != nullptr) {
*ppvAudioPtr2 = pThis->X_lock.pLockPtr2 = pThis->X_BufferCache;
*pdwAudioBytes2 = pThis->X_lock.dwLockBytes2 = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, pThis->Host_lock.dwLockBytes2);
} else {
// If secondary pointers are not used, then set them as zero.
// There are applications bug didn't check for audio pointer that is null pointer which should not use invalid audio bytes.
// Since internal functions do set them zero. We'll set them here as well.
if (ppvAudioPtr2 != xbox::zeroptr) {
*ppvAudioPtr2 = xbox::zeroptr;
}
if (pdwAudioBytes2 != xbox::zeroptr) {
*pdwAudioBytes2 = 0;
}
}
LOG_FUNC_BEGIN_ARG_RESULT
LOG_FUNC_ARG_RESULT(ppvAudioPtr1)
@ -466,7 +494,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Lock)
LOG_FUNC_ARG_RESULT(pdwAudioBytes2)
LOG_FUNC_END_ARG_RESULT;
RETURN(DS_OK);
RETURN_RESULT_CHECK(hRet);
}
// ******************************************************************
@ -481,7 +509,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Unlock)
dword_xt pdwAudioBytes2
)
{
// DSoundMutexGuardLock;
DSoundMutexGuardLock;
LOG_FUNC_BEGIN
LOG_FUNC_ARG(pHybridThis)
@ -491,8 +519,28 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Unlock)
LOG_FUNC_ARG(pdwAudioBytes2)
LOG_FUNC_END;
// Xbox directsound doesn't require locking buffers
// This Xbox api only exists to match PC
EmuDirectSoundBuffer* pThis = pHybridThis->emuDSBuffer;
// TODO: Find out why pThis->EmuLockPtr1 is nullptr... (workaround atm is to check if it is not a nullptr.)
if (pThis->X_BufferCache != xbox::zeroptr && pThis->Host_lock.pLockPtr1 != nullptr) {
memcpy_s((PBYTE)pThis->X_BufferCache + pThis->X_lock.dwLockOffset,
pThis->X_BufferCacheSize - pThis->X_lock.dwLockOffset,
pThis->X_lock.pLockPtr1,
pThis->X_lock.dwLockBytes1);
if (pThis->Host_lock.pLockPtr2 != nullptr) {
memcpy_s(pThis->X_BufferCache, pThis->X_BufferCacheSize, pThis->X_lock.pLockPtr2, pThis->X_lock.dwLockBytes2);
}
}
DSoundGenericUnlock(pThis->EmuFlags,
pThis->EmuDirectSoundBuffer8,
pThis->EmuBufferDesc,
pThis->Host_lock,
pThis->X_BufferCache,
pThis->X_lock.dwLockOffset,
pThis->X_lock.dwLockBytes1,
pThis->X_lock.dwLockBytes2);
return DS_OK;
}
@ -584,7 +632,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Play)
pThis->X_lock.dwLockBytes2);
if (dwFlags & ~(X_DSBPLAY_LOOPING | X_DSBPLAY_FROMSTART | X_DSBPLAY_SYNCHPLAYBACK)) {
CxbxrAbort("Unsupported Playing Flags");
CxbxrKrnlAbort("Unsupported Playing Flags");
}
pThis->EmuPlayFlags = dwFlags;
@ -612,7 +660,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Play)
}
if ((pThis->EmuFlags & DSE_FLAG_SYNCHPLAYBACK_CONTROL) == 0) {
hRet = pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
pThis->EmuStreamingInfo.playRequested = true;
}
}
@ -1547,54 +1594,52 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_StopEx)
hRet = pThis->EmuDirectSoundBuffer8->Stop();
pThis->Xb_rtStopEx = 0LL;
}
else if(dwFlags & X_DSBSTOPEX_ENVELOPE) {
bool isLooping = pThis->EmuPlayFlags & X_DSBPLAY_LOOPING;
else {
bool isLooping;
if ((pThis->EmuPlayFlags & X_DSBPLAY_LOOPING) > 0) {
isLooping = true;
}
else {
isLooping = false;
}
double releaseSamples = pThis->Xb_EnvolopeDesc.dwRelease * 512.0;
if (rtTimeStamp == 0LL) {
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
pThis->Xb_rtStopEx = getTime.QuadPart;
if ((dwFlags & X_DSBSTOPEX_ENVELOPE) > 0) {
if (rtTimeStamp == 0LL) {
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
pThis->Xb_rtStopEx = getTime.QuadPart;
}
else {
pThis->Xb_rtStopEx = rtTimeStamp;
}
pThis->Xb_rtStopEx += (pThis->Xb_EnvolopeDesc.dwRelease * 512) / 48000;
}
else {
pThis->Xb_rtStopEx = rtTimeStamp;
}
const double samplesToTicks = 10000000 / 48000.0;
xbox::REFERENCE_TIME releaseTicks = static_cast<xbox::REFERENCE_TIME>(releaseSamples * samplesToTicks);
pThis->Xb_rtStopEx += releaseTicks;
DWORD currentPos, dwStatus;
if ((dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) > 0) {
// Release from loop region.
pThis->EmuPlayFlags &= ~X_DSBPLAY_LOOPING;
}
DWORD dwValue, dwStatus;
pThis->EmuDirectSoundBuffer8->GetStatus(&dwStatus);
if (pThis->EmuBufferToggle != X_DSB_TOGGLE_DEFAULT) {
pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &currentPos);
pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &dwValue);
hRet = pThis->EmuDirectSoundBuffer8->Stop();
// Determine the range of bytes we need to play
// Test case: Outrun 2006 - converting large buffers tanks the FPS
// Is set within DSoundBufferRegionCurrentLocation function
DWORD bufferRangeStart;
DWORD bufferRangeSize;
DSoundBufferRegionCurrentLocation(pHybridThis, pThis->EmuPlayFlags, bufferRangeStart, bufferRangeSize);
DSoundBufferResizeUpdate(pHybridThis, pThis->EmuPlayFlags, hRet, 0, pThis->X_BufferCacheSize);
if (pThis->EmuBufferToggle == X_DSB_TOGGLE_LOOP) {
// if we are to release from loop region, then we need change the size to end of actual buffer cache.
if (dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) {
bufferRangeSize = pThis->X_BufferCacheSize - bufferRangeStart;
}
dwValue += pThis->EmuRegionPlayStartOffset;
if (isLooping) {
dwValue += pThis->EmuRegionLoopStartOffset;
}
DSoundBufferResizeUpdate(pHybridThis, pThis->EmuPlayFlags, hRet, bufferRangeStart, bufferRangeSize);
pThis->EmuBufferToggle = X_DSB_TOGGLE_DEFAULT;
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(currentPos);
}
if (dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) {
// Release from loop region.
pThis->EmuPlayFlags &= ~X_DSBPLAY_LOOPING;
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(dwValue);
}
if (dwStatus & DSBSTATUS_PLAYING && rtTimeStamp != 0LL) {
@ -1605,9 +1650,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_StopEx)
pThis->Xb_rtStopEx = 0LL;
}
}
else {
LOG_TEST_CASE("Expected X_DSBSTOPEX_ENVELOPE");
}
}
return hRet;

View file

@ -27,13 +27,11 @@
#define LOG_PREFIX CXBXR_MODULE::DSOUND
#include <imgui.h>
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h" // For ImVec
#include "core/common/imgui/ui.hpp"
#include <core\kernel\exports\xboxkrnl.h>
#include <dsound.h>
#include "DirectSoundGlobal.hpp"
#include "DirectSoundInline.hpp" // For GetCurrentPosition, RegionCurrentLocation
Settings::s_audio g_XBAudio = { 0 };
std::recursive_mutex g_DSoundMutex;
@ -56,105 +54,6 @@ DWORD g_dwXbMemAllocated = 0;
DWORD g_dwFree2DBuffers = 0;
DWORD g_dwFree3DBuffers = 0;
DsBufferStreaming g_dsBufferStreaming;
void DrawAudioProgress(xbox::XbHybridDSBuffer* pHybrid, float scaleWidth, ImDrawList* drawList) {
const auto& pBuffer = pHybrid->emuDSBuffer;
auto cursor = ImGui::GetCursorScreenPos();
auto width = ImGui::GetContentRegionAvail().x;
DWORD rawCursor;
HybridDirectSoundBuffer_GetCurrentPosition(pBuffer->EmuDirectSoundBuffer8, &rawCursor, nullptr, pBuffer->EmuFlags);
float scale = (width / pBuffer->X_BufferCacheSize) / scaleWidth;
float playCursor = rawCursor * scale;
bool isLooping = pBuffer->EmuPlayFlags & X_DSBPLAY_LOOPING;
auto colSpan = ImColor(0.8f, 0.1f, 0.1f, 0.3f);
auto colRegion = ImColor(0.1f, 0.8f, 0.1, 0.3f);
auto colRegionLoop = ImColor(0.1f, 0.2f, 0.8, 0.3f);
auto colPlay = ImColor(0.8f, 0.8f, 0.1f, 0.6);
float height = 8;
float sBuf = height * 0.4;
float sReg = height * 1;
float sPlay = height * 0.4;
// Buffer
auto start = cursor + ImVec2(0, (height - sBuf) / 2);
drawList->AddRectFilled(start, start + ImVec2(pBuffer->X_BufferCacheSize * scale, sBuf), colSpan, 0);
DWORD bufferRangeStart;
DWORD bufferRangeSize;
DSoundBufferRegionCurrentLocation(pHybrid, pBuffer->EmuPlayFlags, bufferRangeStart, bufferRangeSize);
bufferRangeStart *= scale;
bufferRangeSize *= scale;
// Region
start = cursor + ImVec2(bufferRangeStart, (height - sReg) / 2);
drawList->AddRectFilled(start, start + ImVec2(bufferRangeSize, sReg), isLooping ? colRegionLoop : colRegion);
// Play area
start = cursor + ImVec2(bufferRangeStart, (height - sPlay) / 2);
drawList->AddRectFilled(start, start + ImVec2(playCursor, sPlay), colPlay);
// Play cursor
start = cursor + ImVec2(bufferRangeStart + playCursor, 0);
drawList->AddLine(start, start + ImVec2(0, height), colPlay);
ImGui::Dummy(ImVec2(pBuffer->X_BufferCacheSize * scale, height));
}
void DSound_DrawBufferVisualization(bool is_focus, bool* p_show, ImGuiWindowFlags input_handler) {
if (!*p_show) return;
DSoundMutexGuardLock;
ImGui::SetNextWindowPos(ImVec2(IMGUI_MIN_DIST_SIDE, IMGUI_MIN_DIST_TOP), ImGuiCond_FirstUseEver, ImVec2(0.0f, 0.0f));
ImGui::SetNextWindowSize(ImVec2(200, 275), ImGuiCond_FirstUseEver);
if (ImGui::Begin("DSBuffer Visualization", p_show, input_handler)) {
static bool showPlayingOnly = true;
ImGui::Checkbox("Show playing only", &showPlayingOnly);
static float bufferScale = 1;
ImGui::PushItemWidth(100);
ImGui::DragFloat("Audio Scale", &bufferScale, 1 / 1000.f, 1 / 24000.f, 1.f, "%.7f", ImGuiSliderFlags_Logarithmic);
if (ImGui::CollapsingHeader("Buffering Controls")) {
ImGui::SliderInt("Stream interval (ms)", (int*)&g_dsBufferStreaming.streamInterval, 0, 50);
ImGui::SliderInt("Stream ahead (ms)", (int*)&g_dsBufferStreaming.streamAhead, 0, 1000);
ImGui::SliderFloat("Tweak copy offset", &g_dsBufferStreaming.tweakCopyOffset, -1, 1);
}
if (ImGui::BeginChild("DSBuffer Graph", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) {
auto drawList = ImGui::GetWindowDrawList();
int index = 0;
for (const auto& i : g_pDSoundBufferCache) {
if (showPlayingOnly) {
DWORD dwStatus;
auto hRet = i->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&dwStatus);
if (hRet != DS_OK || !(dwStatus & DSBSTATUS_PLAYING)) {
continue;
}
}
// Required to add controls inside the loop
ImGui::PushID(index++);
DrawAudioProgress(i, bufferScale, drawList);
ImGui::PopID();
}
}
ImGui::EndChild();
}
ImGui::End();
}
void DSound_PrintStats(bool is_focus, ImGuiWindowFlags input_handler, bool m_show_audio_stats)
{
DSoundMutexGuardLock;
@ -250,7 +149,7 @@ void DSound_PrintStats(bool is_focus, ImGuiWindowFlags input_handler, bool m_sho
ImGui::Text("Total active DSStream = %u", isActive);
}
}
ImGui::End();
}
ImGui::End();
}
}

View file

@ -61,13 +61,6 @@ extern DWORD g_dwXbMemAllocated;
extern DWORD g_dwFree2DBuffers;
extern DWORD g_dwFree3DBuffers;
struct DsBufferStreaming {
DWORD streamInterval = 1;
DWORD streamAhead = 50;
float tweakCopyOffset = 0;
};
extern DsBufferStreaming g_dsBufferStreaming;
// size of DirectSound cache max size
#define X_DIRECTSOUND_CACHE_MAX 0x800
@ -81,6 +74,3 @@ 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);

View file

@ -274,7 +274,7 @@ static inline void GeneratePCMFormat(
}
if (DSBufferDesc.lpwfxFormat == nullptr) {
CxbxrAbort("Unable to allocate DSBufferDesc.Xb_lpwfxFormat");
CxbxrKrnlAbort("Unable to allocate DSBufferDesc.Xb_lpwfxFormat");
}
if (Xb_lpwfxFormat != xbox::zeroptr) {
@ -358,7 +358,7 @@ static inline void DSoundGenericUnlock(
HRESULT hRet = pDSBuffer->Unlock(Host_lock.pLockPtr1, Host_lock.dwLockBytes1, Host_lock.pLockPtr2, Host_lock.dwLockBytes2);
if (hRet != DS_OK) {
CxbxrAbort("DirectSoundBuffer Unlock Failed!");
CxbxrKrnlAbort("DirectSoundBuffer Unlock Failed!");
}
Host_lock.pLockPtr1 = nullptr;
@ -485,7 +485,7 @@ static inline void DSoundBufferRelease(
if (pDS3DBuffer != nullptr) {
refCount = pDS3DBuffer->Release();
if (refCount > 0) {
CxbxrAbort("Nope, wasn't fully cleaned up.");
CxbxrKrnlAbort("Nope, wasn't fully cleaned up.");
}
}
@ -554,7 +554,7 @@ static inline void DSoundBufferResizeUpdate(
hRet = pThis->EmuDirectSoundBuffer8->Lock(0, 0, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
nullptr, nullptr, DSBLOCK_ENTIREBUFFER);
if (hRet != DS_OK) {
CxbxrAbort("Unable to lock region buffer!");
CxbxrKrnlAbort("Unable to lock region buffer!");
}
DSoundGenericUnlock(pThis->EmuFlags,
pThis->EmuDirectSoundBuffer8,
@ -569,6 +569,7 @@ static inline void DSoundBufferResizeUpdate(
static inline void DSoundBufferRegionCurrentLocation(
xbox::XbHybridDSBuffer* pHybridThis,
DWORD dwPlayFlags,
HRESULT &hRet,
DWORD &Xb_dwStartOffset,
DWORD &Xb_dwByteLength)
{
@ -607,7 +608,7 @@ static inline void DSoundBufferUpdate(
DWORD Xb_dwByteLength;
DWORD Xb_dwStartOffset;
DSoundBufferRegionCurrentLocation(pHybridThis, dwPlayFlags, Xb_dwStartOffset, Xb_dwByteLength);
DSoundBufferRegionCurrentLocation(pHybridThis, dwPlayFlags, hRet, Xb_dwStartOffset, Xb_dwByteLength);
DSoundBufferResizeUpdate(pHybridThis, dwPlayFlags, hRet, Xb_dwStartOffset, Xb_dwByteLength);
}
@ -629,7 +630,7 @@ static inline void DSoundBufferRegenWithNewFormat(
HRESULT hRet = pDSBuffer->GetStatus(&dwStatus);
if (hRet != DS_OK) {
CxbxrAbort("Unable to retrieve current status for replace DS buffer!");
CxbxrKrnlAbort("Unable to retrieve current status for replace DS buffer!");
}
pDSBuffer->Stop();
@ -637,7 +638,7 @@ static inline void DSoundBufferRegenWithNewFormat(
hRet = pDSBuffer->GetCurrentPosition(&dwPlayCursor, nullptr);
if (hRet != DS_OK) {
CxbxrAbort("Unable to retrieve current position for replace DS buffer!");
CxbxrKrnlAbort("Unable to retrieve current position for replace DS buffer!");
}
// TODO: Untested if transfer buffer to new audio buffer is necessary.
@ -921,7 +922,7 @@ static inline HRESULT HybridDirectSoundBuffer_Play(
{
if (dwFlags & ~(X_DSBPLAY_LOOPING | X_DSBPLAY_FROMSTART | X_DSBPLAY_SYNCHPLAYBACK)) {
CxbxrAbort("Unsupported Playing Flags");
CxbxrKrnlAbort("Unsupported Playing Flags");
}
// rewind buffer
if ((dwFlags & X_DSBPLAY_FROMSTART)) {

View file

@ -222,7 +222,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateStream)
hRet = xbox::EMUPATCH(DirectSoundCreate)(nullptr, &g_pDSound8, nullptr);
if (hRet != DS_OK) {
CxbxrAbort("Unable to initialize DirectSound!");
CxbxrKrnlAbort("Unable to initialize DirectSound!");
}
}
@ -299,7 +299,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreateStream)
EmuLog(LOG_LEVEL::WARNING, output.str().c_str());
output.str("");
output << static_cast<DS_RESULT>(hRet);
CxbxrAbort("DSS: DSoundBufferCreate error: %s", output.str().c_str());
CxbxrKrnlAbort("DSS: DSoundBufferCreate error: %s", output.str().c_str());
}
else {
if (DSBufferDesc.dwFlags & DSBCAPS_CTRL3D) {

View file

@ -34,7 +34,6 @@
#include <cmath>
#include <iomanip> // For std::setfill and std::setw
#include <filesystem>
#include "core\kernel\init\CxbxKrnl.h"
#include "core\kernel\support\Emu.h"
@ -45,7 +44,6 @@
#include "Intercept.hpp"
#include "Patches.hpp"
#include "common\util\hasher.h"
#include "common/FilePaths.hpp"
#include <Shlwapi.h>
#include <shlobj.h>
@ -193,7 +191,7 @@ void CDECL EmuOutputMessage(xb_output_message mFlag,
break;
}
case XB_OUTPUT_MESSAGE_ERROR: {
CxbxrAbort("%s", message);
CxbxrKrnlAbort("%s", message);
break;
}
case XB_OUTPUT_MESSAGE_DEBUG:
@ -206,7 +204,6 @@ 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)
@ -386,7 +383,7 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
// Make sure the Symbol Cache directory exists
std::string cachePath = g_DataFilePath + "\\SymbolCache\\";
if (!std::filesystem::exists(cachePath) && !std::filesystem::create_directory(cachePath)) {
CxbxrAbort("Couldn't create Cxbx-Reloaded SymbolCache folder!");
CxbxrKrnlAbort("Couldn't create Cxbx-Reloaded SymbolCache folder!");
}
// Hash the loaded XBE's header, use it as a filename
@ -440,6 +437,22 @@ 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!");
}
}
}

View file

@ -1,670 +0,0 @@
// 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 &region = (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);
}

View file

@ -1,182 +0,0 @@
// ******************************************************************
// * 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

View file

@ -29,7 +29,6 @@
#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"
@ -58,16 +57,13 @@ 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_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_BeginPush", xbox::EMUPATCH(D3DDevice_BeginPush), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginPush2", xbox::EMUPATCH(D3DDevice_BeginPush2), 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),
@ -78,10 +74,9 @@ 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__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_DrawVertices_4", xbox::EMUPATCH(D3DDevice_DrawVertices_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVerticesUP", xbox::EMUPATCH(D3DDevice_DrawVerticesUP), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12__LTCG_ebx3", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12), 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),
@ -123,14 +118,12 @@ 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__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_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_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),
@ -145,7 +138,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__LTCG_eax_handle", xbox::EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPixelShader_0", xbox::EMUPATCH(D3DDevice_SetPixelShader_0), 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),
@ -163,7 +156,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__LTCG_eax_Stage", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTexture_4", xbox::EMUPATCH(D3DDevice_SetTexture_4), 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),
@ -184,7 +177,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),
@ -376,54 +369,6 @@ 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;

View file

@ -27,14 +27,14 @@
#define LOG_PREFIX CXBXR_MODULE::XAPI
#include <core\kernel\exports\xboxkrnl.h>
#include "core\hle\XAPI\Xapi.h"
#include "common/cxbxr.hpp"
#include <core\kernel\exports\xboxkrnl.h>
#include "common\input\SdlJoystick.h"
#include "common\input\InputManager.h"
#include <Shlwapi.h>
#include "common\input\LibusbDevice.h" // include this after Shlwapi.h or else it causes an error
#include "core\kernel\init\CxbxKrnl.h"
#include "Logging.h"
#include "core\kernel\support\Emu.h"
#include "core\kernel\exports\EmuKrnl.h" // For DefaultLaunchDataPage
@ -45,6 +45,7 @@
#include "core\hle\Intercept.hpp"
#include "Windef.h"
#include <vector>
#include "core\hle\XAPI\Xapi.h"
#include <charconv>
@ -59,14 +60,16 @@ std::atomic<bool> g_bIsDevicesEmulating = false;
// Protects access to xpp types
std::atomic<bool> g_bXppGuard = false;
// Allocate enough memory for the max number of devices we can support simultaneously
// 4 duke / S / sbc / arcade joystick / lightgun (mutually exclusive) + 8 memory units
DeviceState g_devs[MAX_DEVS];
xbox::ulong_xt g_Mounted_MUs = 0;
xbox::char_xt g_AltLett_MU = 0;
xbox::ulong_xt *g_XapiMountedMUs = &g_Mounted_MUs;
xbox::char_xt *g_XapiAltLett_MU = &g_AltLett_MU;
std::recursive_mutex g_MuLock;
#ifdef CXBXR_EMU
// Declare trampolines
#define XB_TRAMPOLINES(XB_MACRO) \
XB_MACRO(xbox::dword_xt, WINAPI, XUnmountAlternateTitleA, (xbox::char_xt) ); \
@ -81,7 +84,6 @@ void LookupTrampolinesXAPI()
#undef XB_TRAMPOLINES
#endif
static inline xbox::char_xt MuPort2Lett(xbox::dword_xt port, xbox::dword_xt slot)
{
@ -209,13 +211,11 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
{
g_bIsDevicesEmulating = true;
#ifdef CXBXR_EMU
if (g_bIsChihiro) {
// Don't emulate XID devices during Chihiro Emulation
g_bIsDevicesEmulating = false;
return;
}
#endif
// Set up common device state
int port_num, slot;
@ -394,39 +394,93 @@ void DestructHleInputDevice(DeviceState *dev)
EmuLogEx(CXBXR_MODULE::INPSYS, LOG_LEVEL::INFO, "Detached device %s from port %s", GetInputDeviceName(to_underlying(type)).c_str(), PortUserFormat(port).c_str());
}
// Patches/Functions below are mainly for emulation, we don't need them for GUI purpose.
#ifdef CXBXR_EMU
void SetupXboxDeviceTypes()
{
// 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);
// 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)) {
CxbxrKrnlAbort("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 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);
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));
}
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);
else {
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT was not found by XbSymbolDatabase");
}
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));
}
else {
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs was not found by XbSymbolDatabase");
}
}
@ -946,55 +1000,23 @@ 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) {
Timeout = nullptr;
}
else if (dwMilliseconds == 0) {
Timeout = &NewTime;
NewTime.QuadPart = 0;
NewTime.QuadPart = ~0ull;
}
else {
Timeout = &NewTime;
NewTime.QuadPart = xbox::KeQueryInterruptTime();
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT);
}
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> {
xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
if (dwRet == WAIT_TIMEOUT) {
return std::nullopt;
}
// 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 std::make_optional<DWORD>(dwRet);
}, &NewTime, bAlertable, UserMode);
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);
RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
}
// ******************************************************************
@ -1263,5 +1285,3 @@ xbox::void_xt WINAPI xbox::EMUPATCH(OutputDebugStringA)
LOG_FUNC_ONE_ARG(lpOutputString);
printf("OutputDebugStringA: %s\n", lpOutputString);
}
#endif

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