├── .gitignore ├── lib └── avs2-core.lib ├── .gitlab-ci.yml ├── versions ├── 27 │ ├── LDJ-003-2020092900.cmake │ └── LDJ-010-2020092900.cmake ├── 28 │ ├── LDJ-003-2021091500.cmake │ └── LDJ-010-2021091500.cmake ├── 29 │ ├── LDJ-003-2022082400.cmake │ └── LDJ-010-2022082400.cmake ├── 30 │ ├── LDJ-010-2023090500.cmake │ └── LDJ-012-2023090500.cmake ├── 31 │ ├── LDJ-010-2024082600.cmake │ └── LDJ-012-2024082600.cmake ├── 32 │ ├── LDJ-010-2025072900.cmake │ └── LDJ-010-2025082500.cmake └── 33 │ ├── LDJ-010-2025091700.cmake │ └── LDJ-012-2025091700.cmake ├── cmake ├── VersionHeader.in ├── VersionOffsets.in └── CPM.cmake ├── .github └── workflows │ └── build.yml ├── src ├── bm2dx.h ├── avs2-log.h ├── receiver.h ├── receiver.cc └── main.cc ├── LICENSE ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /cmake-build-*/ -------------------------------------------------------------------------------- /lib/avs2-core.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aixxe/2dxcamhook/HEAD/lib/avs2-core.lib -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | 4 | build: 5 | stage: build 6 | script: 7 | - cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -S . -B build/Release 8 | - cmake --build build/Release 9 | artifacts: 10 | paths: 11 | - build/Release/2dxcamhook.*.dll 12 | tags: 13 | - windows -------------------------------------------------------------------------------- /versions/27/LDJ-003-2020092900.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2020092900) 2 | set(DLL_TYPE 003) 3 | 4 | set(DLL_CODE_SIZE 0x007a4e00) 5 | set(DLL_ENTRYPOINT 0x006d4090) 6 | set(DLL_IMAGE_SIZE 0x04ed4000) 7 | 8 | set(OFFSET_HOOK_A 0x005971b0) 9 | set(OFFSET_TEXTURES 0x04e49898) 10 | set(OFFSET_D3D_DEVICE 0x000000d0) 11 | set(OFFSET_CAM_MANAGER 0x009ec6e0) 12 | set(OFFSET_AFP_TEXTURE 0x00000060) -------------------------------------------------------------------------------- /versions/27/LDJ-010-2020092900.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2020092900) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x008d9000) 5 | set(DLL_ENTRYPOINT 0x007f52b0) 6 | set(DLL_IMAGE_SIZE 0x05087000) 7 | 8 | set(OFFSET_HOOK_A 0x006b89e0) 9 | set(OFFSET_TEXTURES 0x04fd08b8) 10 | set(OFFSET_D3D_DEVICE 0x000000d0) 11 | set(OFFSET_CAM_MANAGER 0x00b696e0) 12 | set(OFFSET_AFP_TEXTURE 0x00000060) -------------------------------------------------------------------------------- /versions/28/LDJ-003-2021091500.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2021091500) 2 | set(DLL_TYPE 003) 3 | 4 | set(DLL_CODE_SIZE 0x00a74c00) 5 | set(DLL_ENTRYPOINT 0x0091aa00) 6 | set(DLL_IMAGE_SIZE 0x05c7e000) 7 | 8 | set(OFFSET_HOOK_A 0x007512d0) 9 | set(OFFSET_TEXTURES 0x05bda3c8) 10 | set(OFFSET_D3D_DEVICE 0x000000d8) 11 | set(OFFSET_CAM_MANAGER 0x00d50bc0) 12 | set(OFFSET_AFP_TEXTURE 0x00000060) -------------------------------------------------------------------------------- /versions/28/LDJ-010-2021091500.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2021091500) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x00b41600) 5 | set(DLL_ENTRYPOINT 0x009e58d0) 6 | set(DLL_IMAGE_SIZE 0x05d8e000) 7 | 8 | set(OFFSET_HOOK_A 0x0081c800) 9 | set(OFFSET_TEXTURES 0x05cde4e8) 10 | set(OFFSET_D3D_DEVICE 0x000000d8) 11 | set(OFFSET_CAM_MANAGER 0x00e4cbc0) 12 | set(OFFSET_AFP_TEXTURE 0x00000060) -------------------------------------------------------------------------------- /versions/29/LDJ-003-2022082400.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2022082400) 2 | set(DLL_TYPE 003) 3 | 4 | set(DLL_CODE_SIZE 0x0074f800) 5 | set(DLL_ENTRYPOINT 0x006679ac) 6 | set(DLL_IMAGE_SIZE 0x06e42000) 7 | 8 | set(OFFSET_HOOK_A 0x00458830) 9 | set(OFFSET_TEXTURES 0x06d97498) 10 | set(OFFSET_D3D_DEVICE 0x000000d8) 11 | set(OFFSET_CAM_MANAGER 0x00a0a1e0) 12 | set(OFFSET_AFP_TEXTURE 0x00000060) -------------------------------------------------------------------------------- /versions/29/LDJ-010-2022082400.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2022082400) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x0081c200) 5 | set(DLL_ENTRYPOINT 0x0073273c) 6 | set(DLL_IMAGE_SIZE 0x06f52000) 7 | 8 | set(OFFSET_HOOK_A 0x00523d60) 9 | set(OFFSET_TEXTURES 0x06e9b648) 10 | set(OFFSET_D3D_DEVICE 0x000000d8) 11 | set(OFFSET_CAM_MANAGER 0x00b061e0) 12 | set(OFFSET_AFP_TEXTURE 0x00000060) -------------------------------------------------------------------------------- /versions/30/LDJ-010-2023090500.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2023090500) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x00976200) 5 | set(DLL_ENTRYPOINT 0x00844b10) 6 | set(DLL_IMAGE_SIZE 0x071cf000) 7 | 8 | set(OFFSET_HOOK_A 0x0064e7b0) 9 | set(OFFSET_TEXTURES 0x07106e98) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x00cb9e40) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /versions/30/LDJ-012-2023090500.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2023090500) 2 | set(DLL_TYPE 012) 3 | 4 | set(DLL_CODE_SIZE 0x008a7600) 5 | set(DLL_ENTRYPOINT 0x00777dc0) 6 | set(DLL_IMAGE_SIZE 0x070bb000) 7 | 8 | set(OFFSET_HOOK_A 0x005817a0) 9 | set(OFFSET_TEXTURES 0x06fffbd8) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x00bbae40) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /versions/31/LDJ-010-2024082600.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2024082600) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x00be2400) 5 | set(DLL_ENTRYPOINT 0x00a800bc) 6 | set(DLL_IMAGE_SIZE 0x079c9000) 7 | 8 | set(OFFSET_HOOK_A 0x008d3000) 9 | set(OFFSET_TEXTURES 0x078e0028) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x00f99f20) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /versions/31/LDJ-012-2024082600.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2024082600) 2 | set(DLL_TYPE 012) 3 | 4 | set(DLL_CODE_SIZE 0x00b13600) 5 | set(DLL_ENTRYPOINT 0x009b323c) 6 | set(DLL_IMAGE_SIZE 0x078b4000) 7 | 8 | set(OFFSET_HOOK_A 0x00805f00) 9 | set(OFFSET_TEXTURES 0x077d8328) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x00e99f20) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /versions/32/LDJ-010-2025072900.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2025072900) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x00c80600) 5 | set(DLL_ENTRYPOINT 0x00b107dc) 6 | set(DLL_IMAGE_SIZE 0x0b898000) 7 | 8 | set(OFFSET_HOOK_A 0x0094a310) 9 | set(OFFSET_TEXTURES 0x0b7a60a8) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x0105f800) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /versions/32/LDJ-010-2025082500.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2025082500) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x00c92600) 5 | set(DLL_ENTRYPOINT 0x00b1c7bc) 6 | set(DLL_IMAGE_SIZE 0x0b8b2000) 7 | 8 | set(OFFSET_HOOK_A 0x009562e0) 9 | set(OFFSET_TEXTURES 0x0b7bf0a8) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x01077800) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /versions/33/LDJ-010-2025091700.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2025091700) 2 | set(DLL_TYPE 010) 3 | 4 | set(DLL_CODE_SIZE 0x00c4ea00) 5 | set(DLL_ENTRYPOINT 0x00aeecec) 6 | set(DLL_IMAGE_SIZE 0x0bc0d000) 7 | 8 | set(OFFSET_HOOK_A 0x0092dd00) 9 | set(OFFSET_TEXTURES 0x0bb1b098) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x0102b490) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /versions/33/LDJ-012-2025091700.cmake: -------------------------------------------------------------------------------- 1 | set(DLL_VERSION 2025091700) 2 | set(DLL_TYPE 012) 3 | 4 | set(DLL_CODE_SIZE 0x00b7fa00) 5 | set(DLL_ENTRYPOINT 0x00a21d7c) 6 | set(DLL_IMAGE_SIZE 0x0baf9000) 7 | 8 | set(OFFSET_HOOK_A 0x00860c60) 9 | set(OFFSET_TEXTURES 0x0ba140e8) 10 | set(OFFSET_D3D_DEVICE 0x000000e0) 11 | set(OFFSET_CAM_MANAGER 0x00f2c490) 12 | set(OFFSET_AFP_TEXTURE 0x00000030) -------------------------------------------------------------------------------- /cmake/VersionHeader.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct offsets_t 6 | { 7 | std::size_t dll_code_size {}; 8 | std::size_t dll_entrypoint {}; 9 | std::size_t dll_image_size {}; 10 | 11 | std::uintptr_t hook_a {}; 12 | std::uintptr_t textures {}; 13 | std::uintptr_t device {}; 14 | std::uintptr_t cam_manager {}; 15 | std::uintptr_t afp_texture {}; 16 | }; 17 | 18 | auto static versions = std::vector 19 | {@VERSION_ALL_OFFSETS@}; -------------------------------------------------------------------------------- /cmake/VersionOffsets.in: -------------------------------------------------------------------------------- 1 | 2 | /* LDJ-@DLL_TYPE@-@DLL_VERSION@ */ 3 | { 4 | .dll_code_size = @DLL_CODE_SIZE@, 5 | .dll_entrypoint = @DLL_ENTRYPOINT@, 6 | .dll_image_size = @DLL_IMAGE_SIZE@, 7 | 8 | .hook_a = @OFFSET_HOOK_A@, 9 | .textures = @OFFSET_TEXTURES@, 10 | .device = @OFFSET_D3D_DEVICE@, 11 | .cam_manager = @OFFSET_CAM_MANAGER@, 12 | .afp_texture = @OFFSET_AFP_TEXTURE@, 13 | }, 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: windows-latest 8 | 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v3 12 | 13 | - name: Set up cache 14 | uses: actions/cache@v3 15 | with: 16 | path: cache 17 | key: build 18 | restore-keys: build 19 | 20 | - name: Configure project 21 | run: cmake -DCPM_SOURCE_CACHE=cache -S . -B build 22 | 23 | - name: Build project 24 | run: cmake --build build --config Release 25 | 26 | - name: Upload artifacts 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: release-artifacts 30 | path: build/Release/2dxcamhook.dll 31 | 32 | - name: Create release 33 | uses: softprops/action-gh-release@v1 34 | if: startsWith(github.ref, 'refs/tags/') 35 | with: 36 | files: build/Release/2dxcamhook.dll -------------------------------------------------------------------------------- /src/bm2dx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Camera 4 | { 5 | struct PlayVideoCamera 6 | { 7 | auto texture(const std::uintptr_t offset) -> IDirect3DTexture9** 8 | { 9 | // offset to AfpTextureEx* from PlayVideoCamera varies between games... 10 | auto const afp_texture = *reinterpret_cast 11 | (reinterpret_cast(this) + offset); 12 | 13 | // ...but it's always +0x8 from there to get to its IDirect3DTexture9* 14 | return reinterpret_cast(afp_texture + 0x8); 15 | } 16 | }; 17 | 18 | struct CCameraManager2 19 | { 20 | using camera_pointers = struct 21 | { 22 | /* 0x0000 */ PlayVideoCamera* a; 23 | /* 0x0008 */ PlayVideoCamera* b; 24 | }; 25 | 26 | /* 0x0000 */ char pad_0000[8]; 27 | /* 0x0008 */ camera_pointers* cameras; 28 | }; 29 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 aixxe 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /cmake/CPM.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # 3 | # SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors 4 | 5 | set(CPM_DOWNLOAD_VERSION 0.38.6) 6 | set(CPM_HASH_SUM "11c3fa5f1ba14f15d31c2fb63dbc8628ee133d81c8d764caad9a8db9e0bacb07") 7 | 8 | if(CPM_SOURCE_CACHE) 9 | set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 10 | elseif(DEFINED ENV{CPM_SOURCE_CACHE}) 11 | set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 12 | else() 13 | set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 14 | endif() 15 | 16 | # Expand relative path. This is important if the provided path contains a tilde (~) 17 | get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) 18 | 19 | file(DOWNLOAD 20 | https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake 21 | ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} 22 | ) 23 | 24 | include(${CPM_DOWNLOAD_LOCATION}) 25 | -------------------------------------------------------------------------------- /src/avs2-log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern "C" 6 | { 7 | void log_body_fatal(const char* module, const char* fmt, ...); 8 | void log_body_info(const char* module, const char* fmt, ...); 9 | void log_body_misc(const char* module, const char* fmt, ...); 10 | void log_body_warning(const char* module, const char* fmt, ...); 11 | } 12 | 13 | namespace avs2::log 14 | { 15 | auto constexpr module = "2dxcamhook"; 16 | 17 | template auto constexpr fatal(std::format_string fmt, Args&&... args) 18 | { log_body_fatal(module, "%s", std::format(fmt, std::forward(args)...).c_str()); } 19 | template auto constexpr info(std::format_string fmt, Args&&... args) 20 | { log_body_info(module, "%s", std::format(fmt, std::forward(args)...).c_str()); } 21 | template auto constexpr misc(std::format_string fmt, Args&&... args) 22 | { log_body_misc(module, "%s", std::format(fmt, std::forward(args)...).c_str()); } 23 | template auto constexpr warning(std::format_string fmt, Args&&... args) 24 | { log_body_warning(module, "%s", std::format(fmt, std::forward(args)...).c_str()); } 25 | } -------------------------------------------------------------------------------- /src/receiver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct textures 7 | { 8 | LPDIRECT3DTEXTURE9* camera_texture; 9 | LPDIRECT3DTEXTURE9* preview_texture; 10 | }; 11 | 12 | class receiver 13 | { 14 | public: 15 | receiver() = default; 16 | receiver(std::shared_ptr, LPDIRECT3DDEVICE9EX, std::string, textures&&); 17 | 18 | ~receiver(); 19 | 20 | receiver(const receiver&) = delete; 21 | receiver& operator=(const receiver&) = delete; 22 | receiver(receiver&&) = default; 23 | receiver& operator=(receiver&&) = default; 24 | 25 | [[nodiscard]] auto name() const -> const std::string&; 26 | [[nodiscard]] auto active() const -> bool; 27 | [[nodiscard]] auto texture() const -> LPDIRECT3DTEXTURE9; 28 | 29 | auto connect() -> bool; 30 | auto update() -> bool; 31 | private: 32 | bool _active = false; 33 | std::string _name = {}; 34 | LPDIRECT3DDEVICE9EX _device = nullptr; 35 | LPDIRECT3DTEXTURE9 _texture = nullptr; 36 | LPDIRECT3DTEXTURE9* _camera_target = nullptr; 37 | LPDIRECT3DTEXTURE9 _camera_original = nullptr; 38 | LPDIRECT3DTEXTURE9* _preview_target = nullptr; 39 | LPDIRECT3DTEXTURE9 _preview_original = nullptr; 40 | std::shared_ptr _spout = nullptr; 41 | }; -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.26) 2 | project(2dxcamhook VERSION 0.5.0.0 LANGUAGES CXX) 3 | 4 | include(cmake/CPM.cmake) 5 | 6 | CPMAddPackage( 7 | NAME safetyhook 8 | GIT_TAG v0.6.9 9 | GITHUB_REPOSITORY cursey/safetyhook 10 | OPTIONS "SAFETYHOOK_FETCH_ZYDIS ON" 11 | ) 12 | 13 | CPMAddPackage( 14 | NAME spout2 15 | GIT_TAG 2.007.013 16 | GITHUB_REPOSITORY leadedge/Spout2 17 | OPTIONS "SPOUT_BUILD_CMT OFF" "SPOUT_BUILD_LIBRARY OFF" "SPOUT_BUILD_SPOUTDX ON" 18 | ) 19 | 20 | file(GLOB_RECURSE VERSIONS "versions/*.cmake") 21 | file(READ cmake/VersionOffsets.in VERSION_OFFSET_TEMPLATE) 22 | 23 | foreach (VERSION_PATH ${VERSIONS}) 24 | message(STATUS "Including game support file: ${VERSION_PATH}...") 25 | 26 | include(${VERSION_PATH}) 27 | 28 | string(CONFIGURE ${VERSION_OFFSET_TEMPLATE} VERSION_OFFSET_TEXT @ONLY) 29 | string(APPEND VERSION_ALL_OFFSETS "${VERSION_OFFSET_TEXT}") 30 | endforeach () 31 | 32 | configure_file(cmake/VersionHeader.in macros/versions.h @ONLY) 33 | 34 | add_library(${PROJECT_NAME} SHARED 35 | src/main.cc 36 | src/receiver.cc 37 | ) 38 | 39 | target_link_directories(${PROJECT_NAME} PRIVATE lib) 40 | target_link_libraries(${PROJECT_NAME} safetyhook SpoutDX_static avs2-core) 41 | target_include_directories(${PROJECT_NAME} PUBLIC 42 | ${CMAKE_CURRENT_BINARY_DIR}/macros 43 | ${CPM_PACKAGE_spout2_SOURCE_DIR}/SPOUTSDK/SpoutDirectX 44 | ) -------------------------------------------------------------------------------- /src/receiver.cc: -------------------------------------------------------------------------------- 1 | #include "receiver.h" 2 | #include "avs2-log.h" 3 | 4 | receiver::receiver(std::shared_ptr spout, LPDIRECT3DDEVICE9EX device, std::string name, textures&& sources): 5 | _name(std::move(name)), _device(device), _camera_target(sources.camera_texture), _camera_original(*sources.camera_texture), 6 | _preview_target(sources.preview_texture), _preview_original(*sources.preview_texture), _spout(std::move(spout)) {} 7 | 8 | receiver::~receiver() 9 | { 10 | if (_preview_target) 11 | *_preview_target = _preview_original; 12 | 13 | if (_camera_target) 14 | *_camera_target = _camera_original; 15 | 16 | if (_texture) 17 | _texture->Release(); 18 | } 19 | 20 | auto receiver::name() const -> const std::string& 21 | { return _name; } 22 | 23 | auto receiver::active() const -> bool 24 | { return _active; } 25 | 26 | auto receiver::texture() const -> LPDIRECT3DTEXTURE9 27 | { return _texture; } 28 | 29 | auto receiver::connect() -> bool 30 | { 31 | if (!_spout->sendernames.FindSenderName(_name.c_str())) 32 | return false; 33 | 34 | auto width = 0U; 35 | auto height = 0U; 36 | auto handle = HANDLE {}; 37 | auto format = DWORD {}; 38 | 39 | if (!_spout->GetSenderInfo(_name.c_str(), width, height, handle, format)) 40 | return false; 41 | 42 | avs2::log::misc("creating {}x{} texture for sender '{}' using shared handle {:X}", 43 | width, height, _name, std::uintptr_t(handle)); 44 | 45 | auto const hr = _device->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &_texture, &handle); 46 | 47 | if (FAILED(hr)) 48 | { 49 | avs2::log::warning("failed to create texture (hr={}, sender={})", hr, _name); 50 | return false; 51 | } 52 | 53 | avs2::log::info("receiving from sender '{}'", _name); 54 | 55 | _active = true; 56 | *_camera_target = _texture; 57 | *_preview_target = _texture; 58 | 59 | return true; 60 | } 61 | 62 | auto receiver::update() -> bool 63 | { 64 | if (!active()) 65 | return connect(); 66 | 67 | if (_spout->sendernames.FindSenderName(_name.c_str())) 68 | return true; 69 | 70 | avs2::log::warning("lost sender '{}'", _name); 71 | 72 | _active = false; 73 | *_preview_target = _preview_original; 74 | *_camera_target = _camera_original; 75 | 76 | _texture->Release(); 77 | _texture = nullptr; 78 | 79 | return false; 80 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!TIP] 2 | > **Most functionality provided by this project has been integrated into [spice2x](https://github.com/spice2x/spice2x.github.io/wiki/IIDX-TDJ-Camera-Hook-and-Play-Recording) with the** `-iidxtdjcamhook` **flag** 3 | > 4 | > If you just want to use your cameras in-game, please consider using spice2x instead. Some improvements over 2dxcamhook include persistent camera control for formats and options _(e.g. Exposure, Focus, etc.)_ configurable via the in-game overlay, forwards compatibility with future game versions via pattern scanning, easy one-click horizontal and vertical flipping, and much more. 5 | > 6 | > If you are unable to use spice2x, or need to capture from a virtual camera source, or a device with an esoteric non-UVC driver, or need the additional flexibility provided by OBS, you may need to use 2dxcamhook instead 7 | 8 | ## 2dxcamhook 9 | 10 | Hook library for replacing in-game camera textures with [Spout2](https://github.com/leadedge/Spout2) senders 11 | 12 | ### Compatibility 13 | 14 | - beatmania IIDX 27 HEROIC VERSE 15 | - beatmania IIDX 28 BISTROVER 16 | - beatmania IIDX 29 CastHour 17 | - beatmania IIDX 30 RESIDENT 18 | - beatmania IIDX 31 EPOLIS 19 | - beatmania IIDX 32 Pinky Crush 20 | - beatmania IIDX 33 Sparkle Shower 21 | 22 | ※ In-game cameras are not supported in the LDJ-003 version of IIDX 30 23 | 24 | ### Configuration 25 | 26 | #### Using OBS Studio 27 | 28 | - Install the [Spout2 Plugin for OBS Studio](https://github.com/Off-World-Live/obs-spout2-plugin/releases/) 29 | - Right-click a Source or Scene in OBS Studio and select "Filters" 30 | - Press the add button under Effect Filters and select "Spout Filter" 31 | - Set the name to "Camera A" and press the "Change Spout Filter Name" button 32 | - Repeat the same steps for the second camera, using name "Camera B" instead 33 | 34 | **Note:** The game expects the camera texture to be 1280x720 in size. If you have set OBS to output in a higher resolution, you should also add a "Scaling/Aspect Ratio" filter above the "Spout Filter" and set the resolution to 1280x720. It will appear incorrectly in the camera check menu, but will be normal when in-game. 35 | 36 | ### Installation 37 | 38 | - Compile from source or download a pre-built version from the [releases](https://github.com/aixxe/2dxcamhook/releases/) page 39 | - Copy the appropriate `2dxcamhook.dll` build to your game directory 40 | - Alter your launch command to load the library during startup 41 | 42 | #### [Bemanitools](https://github.com/djhackersdev/bemanitools) 43 | 44 | ``` 45 | launcher.exe [...] -K 2dxcamhook.dll -p io.disable_cams=true 46 | ``` 47 | 48 | #### [spice2x](https://spice2x.github.io) 49 | 50 | ``` 51 | spice64.exe [...] -iidxdisablecams -k 2dxcamhook.dll 52 | ``` -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "receiver.h" 4 | #include "bm2dx.h" 5 | 6 | /* constants */ 7 | auto constexpr name_camera_a = "Camera A"; 8 | auto constexpr name_camera_b = "Camera B"; 9 | auto constexpr game_dll_name = {"bm2dx.dll", "bm2dx_omni.dll"}; 10 | 11 | /* app context */ 12 | auto static recv_a = receiver {}; 13 | auto static recv_b = receiver {}; 14 | auto static init = std::once_flag {}; 15 | auto static spout = std::shared_ptr {}; 16 | 17 | /* game context */ 18 | auto static bm2dx = PBYTE {}; 19 | auto static offsets = offsets_t {}; 20 | 21 | /* game hooks */ 22 | auto static hook = SafetyHookInline {}; 23 | 24 | /* entrypoint */ 25 | auto DllMain(void*, unsigned long reason, void*) 26 | { 27 | if (reason != DLL_PROCESS_ATTACH) 28 | return TRUE; 29 | 30 | /* find game library */ 31 | for (auto it = game_dll_name.begin(); !bm2dx && it != game_dll_name.end(); ++it) 32 | bm2dx = reinterpret_cast(GetModuleHandleA(*it)); 33 | 34 | /* allocate spout instance */ 35 | spout = std::make_shared(); 36 | 37 | if (!bm2dx || !spout) 38 | return FALSE; 39 | 40 | /* determine game version */ 41 | auto const dos = reinterpret_cast(bm2dx); 42 | auto const nt = reinterpret_cast(bm2dx + dos->e_lfanew); 43 | auto const it = std::ranges::find_if(versions, [&] (auto const& info) 44 | { 45 | return info.dll_code_size == nt->OptionalHeader.SizeOfCode 46 | && info.dll_entrypoint == nt->OptionalHeader.AddressOfEntryPoint 47 | && info.dll_image_size == nt->OptionalHeader.SizeOfImage; 48 | }); 49 | 50 | if (it == versions.end()) 51 | return FALSE; 52 | 53 | offsets = *it; 54 | 55 | /* install hooks */ 56 | hook = safetyhook::create_inline(bm2dx + offsets.hook_a, +[] (PBYTE a1) 57 | { 58 | /* call original function */ 59 | auto const result = hook.call(a1); 60 | 61 | std::call_once(init, [&] 62 | { 63 | /* get pointers to the d3d9ex device & camera textures */ 64 | auto const device = *reinterpret_cast(a1 + offsets.device); 65 | auto const textures = *reinterpret_cast(bm2dx + offsets.textures); 66 | auto const cam_manager = reinterpret_cast(bm2dx + offsets.cam_manager); 67 | 68 | recv_a = { spout, device, name_camera_a, { cam_manager->cameras->a->texture(offsets.afp_texture), textures } }; 69 | recv_b = { spout, device, name_camera_b, { cam_manager->cameras->b->texture(offsets.afp_texture), textures + 2 } }; 70 | }); 71 | 72 | /* maintain receiving state */ 73 | recv_a.update(); 74 | recv_b.update(); 75 | 76 | return result; 77 | }); 78 | 79 | return TRUE; 80 | } --------------------------------------------------------------------------------