Add files via upload

This commit is contained in:
Mercurio 2024-02-03 19:27:53 +01:00 committed by GitHub
commit 69ead06dd7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 350 additions and 0 deletions

33
CMakeLists.txt Normal file
View file

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.26)
project(2dxcamhook VERSION 0.1.0.0 LANGUAGES CXX)
include(cmake/CPM.cmake)
CPMAddPackage(
NAME safetyhook
GIT_TAG v0.1.3
GITHUB_REPOSITORY cursey/safetyhook
OPTIONS "SAFETYHOOK_FETCH_ZYDIS ON"
)
CPMAddPackage(
NAME spout2
GIT_TAG 2.007.012
GITHUB_REPOSITORY leadedge/Spout2
OPTIONS "SPOUT_BUILD_CMT OFF" "SPOUT_BUILD_LIBRARY OFF" "SPOUT_BUILD_SPOUTDX ON"
)
file(GLOB_RECURSE VERSIONS "src/game/*.cc")
foreach (VERSION_PATH ${VERSIONS})
get_filename_component(VERSION_BASENAME ${VERSION_PATH} NAME_WE)
set(TARGET_NAME ${PROJECT_NAME}.${VERSION_BASENAME})
message(STATUS "Adding target for ${VERSION_BASENAME}...")
add_library(${TARGET_NAME} SHARED ${VERSION_PATH} src/main.cc src/receiver.cc)
target_link_directories(${TARGET_NAME} PRIVATE lib)
target_link_libraries(${TARGET_NAME} safetyhook SpoutDX_static avs2-core)
target_include_directories(${TARGET_NAME} PUBLIC ${CPM_PACKAGE_spout2_SOURCE_DIR}/SPOUTSDK/SpoutDirectX)
endforeach ()

7
LICENSE Normal file
View file

@ -0,0 +1,7 @@
Copyright 2023 aixxe
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.

42
README.md Normal file
View file

@ -0,0 +1,42 @@
## 2dxcamhook
Hook library for replacing in-game camera textures with [Spout2](https://github.com/leadedge/Spout2) senders
### Compatibility
- beatmania IIDX 27 HEROIC VERSE
- beatmania IIDX 28 BISTROVER
- beatmania IIDX 29 CastHour
- beatmania IIDX 30 RESIDENT
<sub>※ In-game cameras are not supported in the LDJ-003 version of IIDX 30</sub>
### Configuration
#### Using OBS Studio
- Install the [Spout2 Plugin for OBS Studio](https://github.com/Off-World-Live/obs-spout2-plugin/releases/)
- Right-click a Source or Scene in OBS Studio and select "Filters"
- Press the add button under Effect Filters and select "Spout Filter"
- Set the name to "Camera A" and press the "Change Spout Filter Name" button
- Repeat the same steps for the second camera, using name "Camera B" instead
**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.
### Installation
- Compile from source or download a pre-built version from the [releases](https://github.com/aixxe/2dxcamhook/releases/) page
- Copy the appropriate `2dxcamhook.dll` build to your game directory
- Alter your launch command to load the library during startup
#### [Bemanitools](https://github.com/djhackersdev/bemanitools)
```
launcher.exe [...] -K 2dxcamhook.dll -p io.disable_cams=true
```
#### [spice2x](https://spice2x.github.io)
```
spice64.exe [...] -iidxdisablecams -k 2dxcamhook.dll
```

24
cmake/CPM.cmake Normal file
View file

@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT
#
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
set(CPM_DOWNLOAD_VERSION 0.38.6)
set(CPM_HASH_SUM "11c3fa5f1ba14f15d31c2fb63dbc8628ee133d81c8d764caad9a8db9e0bacb07")
if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()
# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
)
include(${CPM_DOWNLOAD_LOCATION})

BIN
lib/avs2-core.lib Normal file

Binary file not shown.

25
src/avs2-log.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include <format>
extern "C"
{
void log_body_fatal(const char* module, const char* fmt, ...);
void log_body_info(const char* module, const char* fmt, ...);
void log_body_misc(const char* module, const char* fmt, ...);
void log_body_warning(const char* module, const char* fmt, ...);
}
namespace avs2::log
{
auto constexpr module = "2dxcamhook";
template <typename... Args> auto constexpr fatal(std::format_string<Args...> fmt, Args&&... args)
{ log_body_fatal(module, "%s", std::format(fmt, std::forward<Args>(args)...).c_str()); }
template <typename... Args> auto constexpr info(std::format_string<Args...> fmt, Args&&... args)
{ log_body_info(module, "%s", std::format(fmt, std::forward<Args>(args)...).c_str()); }
template <typename... Args> auto constexpr misc(std::format_string<Args...> fmt, Args&&... args)
{ log_body_misc(module, "%s", std::format(fmt, std::forward<Args>(args)...).c_str()); }
template <typename... Args> auto constexpr warning(std::format_string<Args...> fmt, Args&&... args)
{ log_body_warning(module, "%s", std::format(fmt, std::forward<Args>(args)...).c_str()); }
}

View file

@ -0,0 +1,6 @@
#include <cstddef>
std::ptrdiff_t addr_hook_a = 0x05971B0;
std::ptrdiff_t addr_hook_b = 0x05FB2C0;
std::ptrdiff_t addr_textures = 0x4E49898;
std::ptrdiff_t addr_device_offset = 0x00000D0;

View file

@ -0,0 +1,6 @@
#include <cstddef>
std::ptrdiff_t addr_hook_a = 0x07512D0;
std::ptrdiff_t addr_hook_b = 0x07BB510;
std::ptrdiff_t addr_textures = 0x5BDA3C8;
std::ptrdiff_t addr_device_offset = 0x00000D8;

View file

@ -0,0 +1,6 @@
#include <cstddef>
std::ptrdiff_t addr_hook_a = 0x0458830;
std::ptrdiff_t addr_hook_b = 0x04C30A0;
std::ptrdiff_t addr_textures = 0x6D97498;
std::ptrdiff_t addr_device_offset = 0x00000D8;

View file

@ -0,0 +1,6 @@
#include <cstddef>
std::ptrdiff_t addr_hook_a = 0x0872EF0;
std::ptrdiff_t addr_hook_b = 0x08DCBB0;
std::ptrdiff_t addr_textures = 0x7807028;
std::ptrdiff_t addr_device_offset = 0x00000E0;

View file

@ -0,0 +1,6 @@
#include <cstddef>
std::ptrdiff_t addr_hook_a = 0x05817A0;
std::ptrdiff_t addr_hook_b = 0x05EA3B0;
std::ptrdiff_t addr_textures = 0x6FFFBD8;
std::ptrdiff_t addr_device_offset = 0x00000E0;

View file

@ -0,0 +1,6 @@
#include <cstddef>
std::ptrdiff_t addr_hook_a = 0x077D0E0;
std::ptrdiff_t addr_hook_b = 0x07E6DA0;
std::ptrdiff_t addr_textures = 0x76D1328;
std::ptrdiff_t addr_device_offset = 0x00000E0;

76
src/main.cc Normal file
View file

@ -0,0 +1,76 @@
#include <safetyhook.hpp>
#include "receiver.h"
/* constants */
auto constexpr name_camera_a = "Camera A";
auto constexpr name_camera_b = "Camera B";
auto constexpr game_dll_name = {"bm2dx.dll", "bm2dx_omni.dll"};
/* offsets */
extern std::ptrdiff_t addr_hook_a;
extern std::ptrdiff_t addr_hook_b;
extern std::ptrdiff_t addr_textures;
extern std::ptrdiff_t addr_device_offset;
/* app context */
auto static recv_a = receiver();
auto static recv_b = receiver();
auto static init_flag = std::once_flag {};
auto static spout = std::shared_ptr<spoutDX> {};
/* game context */
auto static bm2dx = PBYTE {};
/* game hooks */
auto static hook_a = SafetyHookInline {};
auto static hook_b = SafetyHookInline {};
/* entrypoint */
auto DllMain(HMODULE, unsigned long reason, void*)
{
if (reason != DLL_PROCESS_ATTACH)
return TRUE;
/* find game library */
for (auto it = game_dll_name.begin(); !bm2dx && it != game_dll_name.end(); ++it)
bm2dx = PBYTE(GetModuleHandleA(*it));
/* allocate spout instance */
spout = std::make_shared<spoutDX>();
if (!bm2dx || !spout)
return FALSE;
hook_a = safetyhook::create_inline(bm2dx + addr_hook_a, +[] (PBYTE a1)
{
std::call_once(init_flag, [&] ()
{
/* get pointers to the d3d9ex device & camera textures */
auto device = *reinterpret_cast<LPDIRECT3DDEVICE9EX*>(a1 + addr_device_offset);
auto textures = *reinterpret_cast<LPDIRECT3DTEXTURE9**>(bm2dx + addr_textures);
recv_a = receiver(spout, device, name_camera_a, textures);
recv_b = receiver(spout, device, name_camera_b, textures + 2);
});
/* maintain receiving state */
recv_a.update();
recv_b.update();
/* call original function */
return hook_a.call<void*>(a1);
});
hook_b = safetyhook::create_inline(bm2dx + addr_hook_b, +[] (void* a1, int idx)
{
/* return the appropriate camera texture */
if (idx == 0 && recv_a.active())
return recv_a.texture();
else if (idx == 1 && recv_b.active())
return recv_b.texture();
return hook_b.call<LPDIRECT3DTEXTURE9>(a1, idx);
});
return TRUE;
}

74
src/receiver.cc Normal file
View file

@ -0,0 +1,74 @@
#include "receiver.h"
#include "avs2-log.h"
receiver::receiver(std::shared_ptr<spoutDX> spout, LPDIRECT3DDEVICE9EX device, std::string name, LPDIRECT3DTEXTURE9* target):
_spout(std::move(spout)), _device(device), _name(std::move(name)), _target(target), _original(*target) {}
receiver::~receiver()
{
if (_target)
*_target = _original;
if (_texture)
_texture->Release();
}
auto receiver::name() const -> const std::string&
{ return _name; }
auto receiver::active() const -> bool
{ return _active; }
auto receiver::texture() const -> LPDIRECT3DTEXTURE9
{ return _texture; }
auto receiver::connect() -> bool
{
if (!_spout->sendernames.FindSenderName(_name.c_str()))
return false;
auto width = 0U;
auto height = 0U;
auto handle = HANDLE {};
auto format = DWORD {};
if (!_spout->GetSenderInfo(_name.c_str(), width, height, handle, format))
return false;
avs2::log::misc("creating {}x{} texture for sender '{}' using shared handle {:X}",
width, height, _name, std::uintptr_t(handle));
auto const hr = _device->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &_texture, &handle);
if (FAILED(hr))
{
avs2::log::warning("failed to create texture (hr={}, sender={})", hr, _name);
return false;
}
avs2::log::info("receiving from sender '{}'", _name);
_active = true;
*_target = _texture;
return true;
}
auto receiver::update() -> bool
{
if (!active())
return connect();
if (_spout->sendernames.FindSenderName(_name.c_str()))
return true;
avs2::log::warning("lost sender '{}'", _name);
_active = false;
*_target = _original;
_texture->Release();
_texture = nullptr;
return false;
}

33
src/receiver.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include <d3d9.h>
#include <SpoutDX/SpoutDX.h>
class receiver
{
public:
receiver() = default;
receiver(std::shared_ptr<spoutDX>, LPDIRECT3DDEVICE9EX, std::string, LPDIRECT3DTEXTURE9*);
~receiver();
receiver(const receiver&) = delete;
receiver& operator=(const receiver&) = delete;
receiver(receiver&&) = default;
receiver& operator=(receiver&&) = default;
[[nodiscard]] auto name() const -> const std::string&;
[[nodiscard]] auto active() const -> bool;
[[nodiscard]] auto texture() const -> LPDIRECT3DTEXTURE9;
auto connect() -> bool;
auto update() -> bool;
private:
bool _active = false;
std::string _name = {};
LPDIRECT3DDEVICE9EX _device = nullptr;
LPDIRECT3DTEXTURE9 _texture = nullptr;
LPDIRECT3DTEXTURE9* _target = nullptr;
LPDIRECT3DTEXTURE9 _original = nullptr;
std::shared_ptr<spoutDX> _spout = nullptr;
};