mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
e452734bc1
This was a lot more background work than expected, but that is the nature of unnecessary hobbies I guess. Most things in here are obvious, so here's the non-obvious thoughts and notes: 1. All the XP stuff is optional, if the toolchain isn't available it won't even try 2. The docker build is the "known good" build and now has all the required tools for XP and checking binaries a. If you're OK with it, can add the XP_MUST_BUILD flag to build_docker.sh 4. New `SPICE_XP` flag to comment out blocks 5. We could technically build using the Docker image I've made for my XP compatible LLVM toolchain, but combining the binaries into a single zip at the end of it is somewhat tedious, so I've chosen to grab the binaries from my release 6. I also wrote a DLL checker to make sure the final binaries are _actually_ XP compatible, which found some issues on x86_64 (I don't know a single game that runs on 64 bit XP, but it wasn't too much effort so whatever). This also won't run if missing, just to save the trouble. 7. The build script is still pretty shit, I've made some minor improvements a. `/usr/bin/env bash` instead of `/bin/bash` as it's more correct b. Adding `-it` to the docker build, so Ctrl+C actually halts it c. Main issue is that failures don't halt, because of the custom error catching function. This has caused a lot of frustration, maybe we can change that ## Testing It runs on 32 and 64 bit installs of XP. My jubeat cab is on Win10 these days and I haven't got around to testing an actual game, but I've distributed test builds of this to people who *have* confirmed it works as expected.
671 lines
26 KiB
C++
671 lines
26 KiB
C++
#include "camera.h"
|
|
|
|
#if SPICE64 && !SPICE_XP
|
|
|
|
#include <d3d9.h>
|
|
#include "mf_wrappers.h"
|
|
#include "avs/game.h"
|
|
#include "external/rapidjson/document.h"
|
|
#include "external/rapidjson/pointer.h"
|
|
#include "external/rapidjson/prettywriter.h"
|
|
#include "games/iidx/iidx.h"
|
|
#include "games/io.h"
|
|
#include "launcher/options.h"
|
|
#include "misc/eamuse.h"
|
|
#include "util/detour.h"
|
|
#include "util/fileutils.h"
|
|
#include "util/libutils.h"
|
|
#include "util/logging.h"
|
|
#include "util/sigscan.h"
|
|
#include "util/utils.h"
|
|
|
|
static std::filesystem::path dll_path;
|
|
static HMODULE iidx_module;
|
|
static uintptr_t addr_hook_a = 0;
|
|
static uintptr_t addr_textures = 0;
|
|
static uintptr_t addr_camera_manager = 0;
|
|
static uintptr_t addr_device_offset = 0;
|
|
static uintptr_t addr_afp_texture_offset = 0;
|
|
|
|
typedef void **(__fastcall *camera_hook_a_t)(PBYTE);
|
|
static camera_hook_a_t camera_hook_a_orig = nullptr;
|
|
auto static hook_a_init = std::once_flag {};
|
|
|
|
static LPDIRECT3DDEVICE9EX device;
|
|
static LPDIRECT3DTEXTURE9 *camera_texture_a = nullptr;
|
|
static LPDIRECT3DTEXTURE9 *camera_texture_b = nullptr;
|
|
static LPDIRECT3DTEXTURE9 *preview_texture_a = nullptr;
|
|
static LPDIRECT3DTEXTURE9 *preview_texture_b = nullptr;
|
|
|
|
struct PredefinedHook {
|
|
std::string pe_identifier;
|
|
uintptr_t hook_a;
|
|
uintptr_t hook_textures;
|
|
uintptr_t hook_camera_manager;
|
|
uintptr_t hook_device_offset;
|
|
uintptr_t hook_afp_texture_offset;
|
|
};
|
|
|
|
PredefinedHook g_predefinedHooks[] = {};
|
|
|
|
const DWORD g_predefinedHooksLength = ARRAYSIZE(g_predefinedHooks);
|
|
|
|
namespace games::iidx {
|
|
|
|
static IIDXLocalCamera *top_camera = nullptr; // camera id #0
|
|
static IIDXLocalCamera *front_camera = nullptr; // camera id #1
|
|
std::vector<IIDXLocalCamera*> LOCAL_CAMERA_LIST = {};
|
|
static IDirect3DDeviceManager9 *s_pD3DManager = nullptr;
|
|
std::filesystem::path CAMERA_CONFIG_PATH;
|
|
bool CAMERA_READY = false;
|
|
|
|
bool parse_cmd_params() {
|
|
const auto remove_spaces = [](const char& c) {
|
|
return c == ' ';
|
|
};
|
|
|
|
log_misc("iidx:camhook", "Using user-supplied addresses (-iidxcamhookoffset)");
|
|
|
|
auto s = TDJ_CAMERA_OVERRIDE.value();
|
|
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
|
|
|
if (sscanf(
|
|
s.c_str(),
|
|
"%i,%i,%i,%i,%i",
|
|
(int*)&addr_hook_a, (int*)&addr_textures, (int*)&addr_camera_manager, (int*)&addr_device_offset, (int*)&addr_afp_texture_offset) == 5) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
log_fatal("iidx:camhook", "failed to parse -iidxcamhookoffset");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool find_camera_hooks() {
|
|
std::string dll_name = avs::game::DLL_NAME;
|
|
dll_path = MODULE_PATH / dll_name;
|
|
|
|
iidx_module = libutils::try_module(dll_path);
|
|
if (!iidx_module) {
|
|
log_warning("iidx:camhook", "failed to hook game DLL");
|
|
return FALSE;
|
|
}
|
|
|
|
uint32_t time_date_stamp = 0;
|
|
uint32_t address_of_entry_point = 0;
|
|
|
|
// there are three different ways we try to hook the camera entry points
|
|
// 1) user-provided override via cmd line
|
|
// 2) predefined offsets using PE header matching
|
|
// 3) signature match (should work for most iidx27-33)
|
|
|
|
// method 1: user provided
|
|
if (TDJ_CAMERA_OVERRIDE.has_value() && parse_cmd_params()) {
|
|
return TRUE;
|
|
}
|
|
|
|
// method 2: predefined offsets
|
|
get_pe_identifier(dll_path, &time_date_stamp, &address_of_entry_point);
|
|
auto pe = fmt::format("{:x}_{:x}", time_date_stamp, address_of_entry_point);
|
|
log_info("iidx:camhook", "Locating predefined hook addresses for LDJ-{}", pe);
|
|
|
|
for (DWORD i = 0; i < g_predefinedHooksLength; i++) {
|
|
if (pe.compare(g_predefinedHooks[i].pe_identifier) == 0) {
|
|
log_misc("iidx:camhook", "Found predefined addresses");
|
|
addr_hook_a = g_predefinedHooks[i].hook_a;
|
|
addr_textures = g_predefinedHooks[i].hook_textures;
|
|
addr_camera_manager = g_predefinedHooks[i].hook_camera_manager;
|
|
addr_device_offset = g_predefinedHooks[i].hook_device_offset;
|
|
addr_afp_texture_offset = g_predefinedHooks[i].hook_afp_texture_offset;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// method 3: signature match
|
|
log_info("iidx:camhook", "Did not find predefined hook address, try signature match");
|
|
|
|
// --- addr_hook_a ---
|
|
uint8_t *addr_hook_a_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
|
iidx_module,
|
|
"E800000000488B8F0000000048894D",
|
|
"X????XXX????XXX",
|
|
0, 0));
|
|
|
|
if (addr_hook_a_ptr == nullptr) {
|
|
log_warning("iidx:camhook", "failed to find hook: addr_hook_a");
|
|
log_warning("iidx:camhook", "hint: this feature REQUIRES a compatible DLL!");
|
|
return FALSE;
|
|
}
|
|
|
|
// displace with the content of wildcard bytes
|
|
int32_t disp_a = *((int32_t *) (addr_hook_a_ptr + 1));
|
|
|
|
addr_hook_a = (addr_hook_a_ptr - (uint8_t*) iidx_module) + disp_a + 5;
|
|
|
|
// --- addr_textures ---
|
|
uint8_t* addr_textures_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
|
iidx_module,
|
|
"E800000000488BC8488BD34883C420",
|
|
"X????XXXXXXXXXX",
|
|
0, 0));
|
|
|
|
if (addr_textures_ptr == nullptr) {
|
|
log_warning("iidx:camhook", "failed to find hook: addr_textures (part 1)");
|
|
return FALSE;
|
|
}
|
|
|
|
// displace with the content of wildcard bytes
|
|
int32_t disp_textures = *((int32_t *) (addr_textures_ptr + 1));
|
|
addr_textures_ptr += disp_textures + 5;
|
|
|
|
uint32_t search_from = (addr_textures_ptr - (uint8_t*) iidx_module);
|
|
|
|
addr_textures_ptr = reinterpret_cast<uint8_t *>(find_pattern_from(
|
|
iidx_module,
|
|
"488D0D",
|
|
"XXX",
|
|
0, 0, search_from));
|
|
|
|
if (addr_textures_ptr == nullptr) {
|
|
log_warning("iidx:camhook", "failed to find hook: addr_textures (part 2)");
|
|
return FALSE;
|
|
}
|
|
|
|
// displace again with the next integer
|
|
disp_textures = *((int32_t *) (addr_textures_ptr + 3));
|
|
|
|
addr_textures = (addr_textures_ptr - (uint8_t*) iidx_module) + disp_textures + 7;
|
|
|
|
// --- addr_camera_manager ---
|
|
uint8_t *addr_camera_manager_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
|
iidx_module,
|
|
"E80000000033FF488B58",
|
|
"X????XXXXX",
|
|
0, 0));
|
|
|
|
if (addr_camera_manager_ptr == nullptr) {
|
|
log_warning("iidx:camhook", "failed to find hook: addr_get_camera_manager");
|
|
return FALSE;
|
|
}
|
|
|
|
// displace with the content of wildcard bytes
|
|
int32_t disp_camera_manager = *((int32_t *) (addr_camera_manager_ptr + 1));
|
|
|
|
addr_camera_manager_ptr = addr_camera_manager_ptr + disp_camera_manager + 5;
|
|
|
|
// once more to get the final address
|
|
disp_camera_manager = *((int32_t *) (addr_camera_manager_ptr + 3));
|
|
|
|
addr_camera_manager = (addr_camera_manager_ptr - (uint8_t*) iidx_module) + disp_camera_manager + 7;
|
|
|
|
// addr_device_offset
|
|
search_from = (addr_hook_a_ptr - (uint8_t*) iidx_module);
|
|
uint8_t *addr_device_ptr = reinterpret_cast<uint8_t *>(find_pattern_from(
|
|
iidx_module,
|
|
"488B89",
|
|
"XXX",
|
|
3, 0, search_from));
|
|
|
|
if (addr_device_ptr == nullptr) {
|
|
log_warning("iidx:camhook", "failed to find hook: addr_device_ptr");
|
|
return FALSE;
|
|
}
|
|
|
|
addr_device_offset = *addr_device_ptr;
|
|
|
|
// --- addr_afp_texture_offset ---
|
|
uint8_t *addr_afp_texture_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
|
iidx_module,
|
|
"488B41004885C07400488D5424",
|
|
"XXX?XXXX?XXXX",
|
|
0, 0));
|
|
|
|
if (addr_afp_texture_ptr == nullptr) {
|
|
log_warning("iidx:camhook", "failed to find hook: addr_afp_texture_offset");
|
|
return FALSE;
|
|
}
|
|
|
|
addr_afp_texture_offset = *(addr_afp_texture_ptr + 3);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void **__fastcall camera_hook_a(PBYTE a1) {
|
|
std::call_once(hook_a_init, [&]{
|
|
device = *reinterpret_cast<LPDIRECT3DDEVICE9EX*>(a1 + addr_device_offset);
|
|
auto const preview = *reinterpret_cast<LPDIRECT3DTEXTURE9**>((uint8_t*)iidx_module + addr_textures);
|
|
auto const manager = reinterpret_cast<Camera::CCameraManager2*>((uint8_t*)iidx_module + addr_camera_manager);
|
|
|
|
camera_texture_a = manager->cameras->a->d3d9_texture(addr_afp_texture_offset);
|
|
camera_texture_b = manager->cameras->b->d3d9_texture(addr_afp_texture_offset);
|
|
preview_texture_a = preview;
|
|
preview_texture_b = preview + 2;
|
|
|
|
init_local_camera();
|
|
});
|
|
return camera_hook_a_orig(a1);
|
|
}
|
|
|
|
bool create_hooks() {
|
|
auto *hook_a_ptr = reinterpret_cast<uint16_t *>(
|
|
reinterpret_cast<intptr_t>(iidx_module) + addr_hook_a);
|
|
|
|
if (!detour::trampoline(
|
|
reinterpret_cast<camera_hook_a_t>(hook_a_ptr),
|
|
camera_hook_a,
|
|
&camera_hook_a_orig)) {
|
|
log_warning("iidx:camhook", "failed to trampoline hook_a");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT create_d3d_device_manager() {
|
|
UINT resetToken = 0;
|
|
IDirect3DDeviceManager9 *pD3DManager = nullptr;
|
|
|
|
HRESULT hr = DXVA2CreateDirect3DDeviceManager9(&resetToken, &pD3DManager);
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
hr = pD3DManager->ResetDevice(device, resetToken);
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
s_pD3DManager = pD3DManager;
|
|
(s_pD3DManager)->AddRef();
|
|
|
|
done:
|
|
if (SUCCEEDED(hr)) {
|
|
log_misc("iidx:camhook", "Created DeviceManager for DXVA");
|
|
} else {
|
|
log_warning("iidx:camhook", "Cannot create DXVA DeviceManager: {}", hr);
|
|
}
|
|
|
|
SafeRelease(&pD3DManager);
|
|
return hr;
|
|
}
|
|
|
|
bool init_local_camera() {
|
|
HRESULT hr = S_OK;
|
|
IMFAttributes *pAttributes = nullptr;
|
|
IMFActivate **ppDevices = nullptr;
|
|
uint32_t numDevices;
|
|
|
|
// Initialize an attribute store to specify enumeration parameters.
|
|
hr = WrappedMFCreateAttributes(&pAttributes, 1);
|
|
|
|
// Ask for source type = video capture devices.
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pAttributes->SetGUID(
|
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
|
|
);
|
|
}
|
|
|
|
// Enumerate devices.
|
|
if (SUCCEEDED(hr)) {
|
|
hr = WrappedMFEnumDeviceSources(pAttributes, &ppDevices, &numDevices);
|
|
}
|
|
|
|
log_info("iidx:camhook", "MFEnumDeviceSources returned {} device(s)", numDevices);
|
|
if (numDevices > 0) {
|
|
hr = create_d3d_device_manager();
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
// get options
|
|
std::string top_camera_id("");
|
|
std::string front_camera_id("");
|
|
auto options = games::get_options(eamuse_get_game());
|
|
if (options->at(launcher::Options::IIDXCamHookTopId).is_active()) {
|
|
top_camera_id = options->at(launcher::Options::IIDXCamHookTopId).value_text();
|
|
}
|
|
if (options->at(launcher::Options::IIDXCamHookFrontId).is_active()) {
|
|
front_camera_id = options->at(launcher::Options::IIDXCamHookFrontId).value_text();
|
|
}
|
|
int preferred_top_index = -1;
|
|
int preferred_front_index = -1;
|
|
|
|
log_info("iidx:camhook", "-------- Listing all cameras from MFEnumDeviceSources --------");
|
|
|
|
// print camera names first for user
|
|
for (size_t i = 0; i < numDevices; i++) {
|
|
// get camera activation
|
|
auto pActivate = ppDevices[i];
|
|
|
|
// print friendly name
|
|
PWCHAR friendly = nullptr;
|
|
UINT32 friendly_len = 0;
|
|
hr = pActivate->GetAllocatedString(
|
|
MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
|
&friendly,
|
|
&friendly_len);
|
|
if (SUCCEEDED(hr) && friendly != nullptr) {
|
|
log_info("iidx:camhook", "Camera {} Name: {}", i, ws2s(friendly));
|
|
CoTaskMemFree(friendly);
|
|
friendly = nullptr;
|
|
}
|
|
|
|
// get symlink name
|
|
PWCHAR symlink = nullptr;
|
|
UINT32 symlink_len = 0;
|
|
hr = pActivate->GetAllocatedString(
|
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
|
&symlink,
|
|
&symlink_len);
|
|
if (FAILED(hr) || symlink == nullptr) {
|
|
log_info("iidx:camhook", "Camera {}: failed to get vidcap symlink name", i);
|
|
continue;
|
|
}
|
|
|
|
const auto symlink_s = ws2s(symlink);
|
|
log_info("iidx:camhook", "Camera {} ID: {}", i, symlink_s);
|
|
|
|
if (!top_camera_id.empty() && (symlink_s.find(top_camera_id) != std::string::npos)) {
|
|
preferred_top_index = i;
|
|
} else if (!front_camera_id.empty() && (symlink_s.find(front_camera_id) != std::string::npos)) {
|
|
preferred_front_index = i;
|
|
}
|
|
|
|
if (symlink != nullptr) {
|
|
CoTaskMemFree(symlink);
|
|
symlink = nullptr;
|
|
}
|
|
}
|
|
|
|
log_info("iidx:camhook", "-------- End of cameras from MFEnumDeviceSources --------");
|
|
|
|
// pick out preferred top/front cameras
|
|
|
|
log_misc("iidx:camhook", "Adding user-preferred cameras");
|
|
for (size_t i = 0; i < numDevices && LOCAL_CAMERA_LIST.size() < 2; i++) {
|
|
auto pActivate = ppDevices[i];
|
|
if ((int)i == preferred_top_index && top_camera == nullptr) {
|
|
log_misc("iidx:camhook", "Adding user-preferred top camera {} / '{}'", i, top_camera_id);
|
|
top_camera = add_top_camera(pActivate);
|
|
} else if ((int)i == preferred_front_index && front_camera == nullptr) {
|
|
log_misc("iidx:camhook", "Adding user-preferred front camera {} / '{}'", i, front_camera_id);
|
|
front_camera = add_front_camera(pActivate);
|
|
}
|
|
}
|
|
|
|
// fill out the rest of cameras
|
|
log_misc("iidx:camhook", "Adding other cameras");
|
|
for (size_t i = 0; i < numDevices && LOCAL_CAMERA_LIST.size() < 2; i++) {
|
|
auto pActivate = ppDevices[i];
|
|
|
|
if ((int)i == preferred_top_index || (int)i == preferred_front_index) {
|
|
// we already tried these above; don't use them here
|
|
continue;
|
|
}
|
|
|
|
if (!FLIP_CAMS) {
|
|
// top camera first, then front
|
|
if (top_camera == nullptr) {
|
|
top_camera = add_top_camera(pActivate);
|
|
} else if (front_camera == nullptr) {
|
|
front_camera = add_front_camera(pActivate);
|
|
}
|
|
} else {
|
|
// front first, then top
|
|
if (front_camera == nullptr) {
|
|
front_camera = add_front_camera(pActivate);
|
|
} else if (top_camera == nullptr) {
|
|
top_camera = add_top_camera(pActivate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LOCAL_CAMERA_LIST.size() == 0) {
|
|
goto done;
|
|
}
|
|
|
|
camera_config_load();
|
|
if (top_camera) {
|
|
top_camera->StartCapture();
|
|
}
|
|
if (front_camera) {
|
|
front_camera->StartCapture();
|
|
}
|
|
|
|
CAMERA_READY = true;
|
|
|
|
done:
|
|
SafeRelease(&pAttributes);
|
|
|
|
for (DWORD i = 0; i < numDevices; i++) {
|
|
SafeRelease(&ppDevices[i]);
|
|
}
|
|
CoTaskMemFree(ppDevices);
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
IIDXLocalCamera* add_top_camera(IMFActivate* pActivate) {
|
|
auto top_camera = new IIDXLocalCamera("top", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, camera_texture_a, preview_texture_a);
|
|
if (top_camera->m_initialized) {
|
|
LOCAL_CAMERA_LIST.push_back(top_camera);
|
|
} else {
|
|
top_camera->Release();
|
|
top_camera = nullptr;
|
|
}
|
|
return top_camera;
|
|
}
|
|
|
|
IIDXLocalCamera* add_front_camera(IMFActivate* pActivate) {
|
|
auto front_camera = new IIDXLocalCamera("front", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, camera_texture_b, preview_texture_b);
|
|
if (front_camera->m_initialized) {
|
|
LOCAL_CAMERA_LIST.push_back(front_camera);
|
|
} else {
|
|
front_camera->Release();
|
|
front_camera = nullptr;
|
|
}
|
|
return front_camera;
|
|
}
|
|
|
|
bool init_camera_hooks() {
|
|
bool result = find_camera_hooks();
|
|
if (result) {
|
|
log_misc(
|
|
"iidx:camhook",
|
|
"found hooks:\n hook_a 0x{:x}\n textures 0x{:x}\n camera_manager 0x{:x}\n device_offset 0x{:x}\n afp_texture_offset 0x{:x}",
|
|
addr_hook_a, addr_textures, addr_camera_manager, addr_device_offset, addr_afp_texture_offset);
|
|
result = create_hooks();
|
|
if (result) {
|
|
init_mf_library();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void camera_release() {
|
|
if (top_camera) {
|
|
top_camera->Release();
|
|
top_camera = nullptr;
|
|
}
|
|
if (front_camera) {
|
|
front_camera->Release();
|
|
front_camera = nullptr;
|
|
}
|
|
SafeRelease(&s_pD3DManager);
|
|
}
|
|
|
|
bool camera_config_load() {
|
|
CAMERA_CONFIG_PATH =
|
|
fileutils::get_config_file_path("iidx::camhook", "spicetools_camera_control.json");
|
|
|
|
try {
|
|
// read config file
|
|
std::string config = fileutils::text_read(CAMERA_CONFIG_PATH);
|
|
if (!config.empty()) {
|
|
// parse document
|
|
rapidjson::Document doc;
|
|
doc.Parse(config.c_str());
|
|
|
|
// check parse error
|
|
auto error = doc.GetParseError();
|
|
if (error) {
|
|
log_warning("iidx:camhook", "config parse error: {}", static_cast<uint32_t>(error));
|
|
return false;
|
|
}
|
|
|
|
// verify root is a dict
|
|
if (!doc.IsObject()) {
|
|
log_warning("iidx:camhook", "config not found");
|
|
return false;
|
|
}
|
|
|
|
const std::string root("/sp2x_cameras");
|
|
auto numCameras = LOCAL_CAMERA_LIST.size();
|
|
for (size_t i = 0; i < numCameras; i++) {
|
|
auto *camera = LOCAL_CAMERA_LIST.at(i);
|
|
auto symLink = camera->GetSymLink();
|
|
|
|
const auto cameraNode = rapidjson::Pointer(root + "/" + symLink).Get(doc);
|
|
if (cameraNode && cameraNode->IsObject()) {
|
|
log_info("iidx:camhook", "Parsing config for: {}", symLink);
|
|
|
|
// Media type
|
|
auto mediaTypePointer = rapidjson::Pointer(root + "/" + symLink + "/MediaType").Get(doc);
|
|
if (mediaTypePointer && mediaTypePointer->IsString()) {
|
|
std::string mediaType = mediaTypePointer->GetString();
|
|
if (mediaType.length() > 0) {
|
|
camera->m_selectedMediaTypeDescription = mediaType;
|
|
camera->m_useAutoMediaType = false;
|
|
} else {
|
|
camera->m_useAutoMediaType = true;
|
|
}
|
|
}
|
|
|
|
// Draw mode
|
|
auto drawModePointer = rapidjson::Pointer(root + "/" + symLink + "/DrawMode").Get(doc);
|
|
if (drawModePointer && drawModePointer->IsString()) {
|
|
std::string drawModeString = drawModePointer->GetString();
|
|
for (int j = 0; j < DRAW_MODE_SIZE; j++) {
|
|
if (DRAW_MODE_LABELS[j].compare(drawModeString) == 0) {
|
|
camera->m_drawMode = (LocalCameraDrawMode) j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flip
|
|
auto flipHorizontalPointer = rapidjson::Pointer(root + "/" + symLink + "/FlipHorizontal").Get(doc);
|
|
if (flipHorizontalPointer && flipHorizontalPointer->IsBool()) {
|
|
camera->m_flipHorizontal = flipHorizontalPointer->GetBool();
|
|
}
|
|
|
|
auto flipVerticalPointer = rapidjson::Pointer(root + "/" + symLink + "/FlipVertical").Get(doc);
|
|
if (flipVerticalPointer && flipVerticalPointer->IsBool()) {
|
|
camera->m_flipVertical = flipVerticalPointer->GetBool();
|
|
}
|
|
|
|
// Allow manual control
|
|
auto manualControlPointer = rapidjson::Pointer(root + "/" + symLink + "/AllowManualControl").Get(doc);
|
|
if (manualControlPointer && manualControlPointer->IsBool() && manualControlPointer->GetBool()) {
|
|
camera->m_allowManualControl = true;
|
|
}
|
|
|
|
// Camera control
|
|
// if m_allowManualControl is false, SetCameraControlProp will fail, but run through this code
|
|
// anyway to exercise parsing code
|
|
for (int propIndex = 0; propIndex < CAMERA_CONTROL_PROP_SIZE; propIndex++) {
|
|
std::string label = CAMERA_CONTROL_LABELS[propIndex];
|
|
CameraControlProp prop = {};
|
|
camera->GetCameraControlProp(propIndex, &prop);
|
|
auto valuePointer = rapidjson::Pointer(root + "/" + symLink + "/" + label + "/value").Get(doc);
|
|
auto flagsPointer = rapidjson::Pointer(root + "/" + symLink + "/" + label + "/flags").Get(doc);
|
|
if (valuePointer && valuePointer->IsInt() && flagsPointer && flagsPointer->IsInt()) {
|
|
prop.value = (long)valuePointer->GetInt();
|
|
prop.valueFlags = (long)flagsPointer->GetInt();
|
|
camera->SetCameraControlProp(propIndex, prop.value, prop.valueFlags);
|
|
}
|
|
|
|
}
|
|
log_misc("iidx:camhook", " >> done");
|
|
} else {
|
|
log_misc("iidx:camhook", "No previous config for: {}", symLink);
|
|
}
|
|
}
|
|
}
|
|
} catch (const std::exception& e) {
|
|
log_warning("iidx:camhook", "exception occurred while config: {}", e.what());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool camera_config_save() {
|
|
log_info("iidx:camhook", "saving config");
|
|
|
|
// create document
|
|
rapidjson::Document doc;
|
|
std::string config = fileutils::text_read(CAMERA_CONFIG_PATH);
|
|
if (!config.empty()) {
|
|
doc.Parse(config.c_str());
|
|
log_misc("iidx:camhook", "existing config file found");
|
|
}
|
|
if (!doc.IsObject()) {
|
|
log_misc("iidx:camhook", "clearing out config file");
|
|
doc.SetObject();
|
|
}
|
|
|
|
auto numCameras = LOCAL_CAMERA_LIST.size();
|
|
for (size_t i = 0; i < numCameras; i++) {
|
|
auto *camera = LOCAL_CAMERA_LIST.at(i);
|
|
auto symLink = camera->GetSymLink();
|
|
std::string root("/sp2x_cameras/" + symLink + "/");
|
|
|
|
// Media type
|
|
if (camera->m_useAutoMediaType) {
|
|
rapidjson::Pointer(root + "MediaType").Set(doc, "");
|
|
} else {
|
|
rapidjson::Pointer(root + "MediaType").Set(doc, camera->m_selectedMediaTypeDescription);
|
|
}
|
|
|
|
// Draw Mode
|
|
rapidjson::Pointer(root + "DrawMode").Set(doc, DRAW_MODE_LABELS[camera->m_drawMode]);
|
|
|
|
// Flip
|
|
rapidjson::Pointer(root + "FlipHorizontal").Set(doc, camera->m_flipHorizontal);
|
|
rapidjson::Pointer(root + "FlipVertical").Set(doc, camera->m_flipVertical);
|
|
|
|
// Manual control
|
|
rapidjson::Pointer(root + "AllowManualControl").Set(doc, camera->m_allowManualControl);
|
|
|
|
// Camera control
|
|
for (int propIndex = 0; propIndex < CAMERA_CONTROL_PROP_SIZE; propIndex++) {
|
|
std::string label = CAMERA_CONTROL_LABELS[propIndex];
|
|
CameraControlProp prop = {};
|
|
camera->GetCameraControlProp(propIndex, &prop);
|
|
rapidjson::Pointer(root + label + "/value").Set(doc, (int)prop.value);
|
|
rapidjson::Pointer(root + label + "/flags").Set(doc, (int)prop.valueFlags);
|
|
}
|
|
}
|
|
|
|
// build JSON
|
|
rapidjson::StringBuffer buffer;
|
|
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
|
doc.Accept(writer);
|
|
|
|
// save to file
|
|
if (fileutils::write_config_file("iidx::camhook", CAMERA_CONFIG_PATH, buffer.GetString())) {
|
|
} else {
|
|
log_warning("iidx:camhook", "unable to save config file");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool camera_config_reset() {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endif
|