mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-07 09:00:41 -07:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| baa550037a | |||
| b53447bed5 | |||
| 4959a58de3 | |||
| 9141ff453b |
@@ -332,6 +332,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
api/modules/control.cpp
|
||||
api/modules/touch.cpp
|
||||
api/modules/iidx.cpp
|
||||
api/modules/sdvx.cpp
|
||||
api/serial.cpp
|
||||
api/modules/drs.cpp
|
||||
api/modules/lcd.cpp
|
||||
|
||||
@@ -267,6 +267,20 @@ which also means that your hex edits are applicable directly.
|
||||
- `Side Panel Right Inner`
|
||||
- `Side Panel Right`
|
||||
|
||||
#### SDVX
|
||||
- tapeled_get(name: str, ...)
|
||||
- returns a list containing a dict of the current tape LED states. The dict keys are:
|
||||
- `Title`
|
||||
- `Upper Left Speaker`
|
||||
- `Upper Right Speaker`
|
||||
- `Left Wing`
|
||||
- `Right Wing`
|
||||
- `Control Panel`
|
||||
- `Lower Left Speaker`
|
||||
- `Lower Right Speaker`
|
||||
- `Woofer`
|
||||
- `V Unit`
|
||||
|
||||
#### LCD
|
||||
- info()
|
||||
- returns information about the serial LCD controller some games use
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace api {
|
||||
|
||||
extern std::atomic_uint32_t CLIENT_COUNT;
|
||||
|
||||
inline bool has_clients() {
|
||||
return CLIENT_COUNT.load(std::memory_order_relaxed) > 0;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "client.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/crypt.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "modules/lcd.h"
|
||||
#include "modules/lights.h"
|
||||
#include "modules/memory.h"
|
||||
#include "modules/sdvx.h"
|
||||
#include "modules/touch.h"
|
||||
#include "modules/resize.h"
|
||||
#include "request.h"
|
||||
@@ -36,6 +38,8 @@
|
||||
using namespace rapidjson;
|
||||
using namespace api;
|
||||
|
||||
std::atomic_uint32_t api::CLIENT_COUNT = 0;
|
||||
|
||||
Controller::Controller(unsigned short port, std::string password, bool pretty)
|
||||
: port(port), password(std::move(password)), pretty(pretty)
|
||||
{
|
||||
@@ -411,8 +415,11 @@ void Controller::init_state(api::ClientState *state) {
|
||||
state->modules.push_back(new modules::LCD());
|
||||
state->modules.push_back(new modules::Lights());
|
||||
state->modules.push_back(new modules::Memory());
|
||||
state->modules.push_back(new modules::SDVX());
|
||||
state->modules.push_back(new modules::Touch());
|
||||
state->modules.push_back(new modules::Resize());
|
||||
|
||||
CLIENT_COUNT.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Controller::free_state(api::ClientState *state) {
|
||||
@@ -424,6 +431,8 @@ void Controller::free_state(api::ClientState *state) {
|
||||
|
||||
// free cipher
|
||||
delete state->cipher;
|
||||
|
||||
CLIENT_COUNT.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Controller::free_socket() {
|
||||
|
||||
@@ -106,14 +106,16 @@ namespace api::modules {
|
||||
void IIDX::copy_tapeled_data(Response &res, Value &response_object, const tapeledutils::tape_led &mapping) {
|
||||
// Create an array for the light state
|
||||
Value light_state(kArrayType);
|
||||
light_state.Reserve(mapping.data.capacity() * 3, res.doc()->GetAllocator());
|
||||
light_state.Reserve(
|
||||
static_cast<SizeType>(mapping.data.size() * 3),
|
||||
res.doc()->GetAllocator());
|
||||
for (const auto [r, g, b] : mapping.data) {
|
||||
light_state.PushBack(r, res.doc()->GetAllocator());
|
||||
light_state.PushBack(g, res.doc()->GetAllocator());
|
||||
light_state.PushBack(b, res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
// Can't use StringRef here, turns some strings partially into null bytes for some reason
|
||||
// can't use StringRef here, turns some strings partially into null bytes for some reason
|
||||
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
|
||||
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "sdvx.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
SDVX::SDVX() : Module("sdvx") {
|
||||
functions["tapeled_get"] = std::bind(&SDVX::tapeled_get, this, _1, _2);
|
||||
|
||||
for (auto &light : games::sdvx::TAPELED_MAPPING) {
|
||||
lights_by_names.emplace(light.lightName, light);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tapeled_get()
|
||||
* tapeled_get(name: str, ...)
|
||||
*/
|
||||
void SDVX::tapeled_get(Request &req, Response &res) {
|
||||
Value response_object(kObjectType);
|
||||
|
||||
// all tape leds
|
||||
if (req.params.Size() == 0) {
|
||||
// iterate through each device and dump its lights data into the response
|
||||
for (const auto &mapping : games::sdvx::TAPELED_MAPPING) {
|
||||
copy_tapeled_data(res, response_object, mapping);
|
||||
}
|
||||
} else {
|
||||
// specified light names
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
// check params
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto name = param.GetString();
|
||||
if (const auto &it = lights_by_names.find(name); it != lights_by_names.end()) {
|
||||
copy_tapeled_data(res, response_object, it->second.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.add_data(response_object);
|
||||
}
|
||||
|
||||
void SDVX::copy_tapeled_data(Response &res, Value &response_object,
|
||||
const tapeledutils::tape_led &mapping)
|
||||
{
|
||||
Value light_state(kArrayType);
|
||||
light_state.Reserve(
|
||||
static_cast<SizeType>(mapping.data.size() * 3),
|
||||
res.doc()->GetAllocator());
|
||||
for (const auto [r, g, b] : mapping.data) {
|
||||
light_state.PushBack(r, res.doc()->GetAllocator());
|
||||
light_state.PushBack(g, res.doc()->GetAllocator());
|
||||
light_state.PushBack(b, res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
// can't use StringRef here, turns some strings partially into null bytes for some reason
|
||||
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
|
||||
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "external/robin_hood.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class SDVX : public Module {
|
||||
public:
|
||||
SDVX();
|
||||
|
||||
private:
|
||||
robin_hood::unordered_map<std::string, std::reference_wrapper<tapeledutils::tape_led>> lights_by_names;
|
||||
|
||||
void tapeled_get(Request &req, Response &res);
|
||||
void copy_tapeled_data(Response &res, rapidjson::Value &response_object,
|
||||
const tapeledutils::tape_led &mapping);
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,7 @@ from .coin import *
|
||||
from .control import *
|
||||
from .exceptions import *
|
||||
from .iidx import *
|
||||
from .sdvx import *
|
||||
from .info import *
|
||||
from .keypads import *
|
||||
from .lights import *
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def sdvx_tapeled_get(con: Connection, *light_names):
|
||||
req = Request("sdvx", "tapeled_get")
|
||||
|
||||
for light_name in light_names:
|
||||
req.add_param(light_name)
|
||||
|
||||
res = con.request(req)
|
||||
return res.get_data()
|
||||
@@ -6,6 +6,7 @@
|
||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
<dpiAware>true</dpiAware>
|
||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">Legacy</activeCodePage>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
<dependency>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#if SPICE64
|
||||
|
||||
#include <cstdint>
|
||||
#include "api/client.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
@@ -443,10 +444,12 @@ namespace games::iidx {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
|
||||
|
||||
for (unsigned int i = 0; i < data_size; ++i) {
|
||||
map.data[i].r = data[i * 3];
|
||||
map.data[i].g = data[i * 3 + 1];
|
||||
map.data[i].b = data[i * 3 + 2];
|
||||
if (api::has_clients()) {
|
||||
for (size_t i = 0; i < data_size; ++i) {
|
||||
map.data[i].r = data[i * 3];
|
||||
map.data[i].g = data[i * 3 + 1];
|
||||
map.data[i].b = data[i * 3 + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace games::iidx {
|
||||
|
||||
// settings
|
||||
bool FLIP_CAMS = false;
|
||||
std::optional<bool> DISABLE_CAMS;
|
||||
cab_camera_access_mode CAB_CAMERA_ACCESS = cab_camera_access_mode::automatic;
|
||||
bool TDJ_CAMERA = false;
|
||||
bool TDJ_CAMERA_PREFER_16_9 = true;
|
||||
bool TDJ_MODE = false;
|
||||
@@ -424,10 +424,7 @@ namespace games::iidx {
|
||||
"RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE);
|
||||
|
||||
// check if cam hook should be enabled
|
||||
if (!DISABLE_CAMS.has_value()) {
|
||||
log_fatal("iidx", "assertion failure - DISABLE_CAMS not set during attach");
|
||||
}
|
||||
if (!DISABLE_CAMS.value()) {
|
||||
if (CAB_CAMERA_ACCESS == cab_camera_access_mode::legacy) {
|
||||
init_legacy_camera_hook(FLIP_CAMS);
|
||||
}
|
||||
|
||||
@@ -454,19 +451,10 @@ namespace games::iidx {
|
||||
SetEnvironmentVariable("SCREEN_MODE", SCREEN_MODE.value().c_str());
|
||||
}
|
||||
|
||||
// check for cab camera access for the second time (first time was in launcher.cpp)
|
||||
// this time, we are inside -iidx module hook, which means the user is likely NOT on a cab
|
||||
// therefore, start with cams OFF by default, and allow user to forcibly override to ON
|
||||
if (!games::iidx::DISABLE_CAMS.has_value()) {
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
if (options->at(launcher::Options::IIDXCabCamAccess).is_active() &&
|
||||
options->at(launcher::Options::IIDXCabCamAccess).value_text() == "on") {
|
||||
games::iidx::DISABLE_CAMS = false;
|
||||
}
|
||||
if (games::iidx::DISABLE_CAMS.value()) {
|
||||
log_misc("iidx", "CONNECT_CAMERA env var set to 0");
|
||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||
}
|
||||
// auto with iidx module means turn off camera (non-cab use)
|
||||
if (CAB_CAMERA_ACCESS == cab_camera_access_mode::automatic) {
|
||||
log_misc("iidx", "CONNECT_CAMERA env var set to 0");
|
||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||
}
|
||||
|
||||
// windowed subscreen, enabled by default, unless turned off by user
|
||||
|
||||
@@ -11,6 +11,13 @@
|
||||
|
||||
namespace games::iidx {
|
||||
|
||||
enum class cab_camera_access_mode {
|
||||
automatic,
|
||||
off,
|
||||
on,
|
||||
legacy,
|
||||
};
|
||||
|
||||
enum class iidx_aio_emulation_state {
|
||||
unknown,
|
||||
bi2a_com2,
|
||||
@@ -20,7 +27,7 @@ namespace games::iidx {
|
||||
// settings
|
||||
|
||||
extern bool FLIP_CAMS;
|
||||
extern std::optional<bool> DISABLE_CAMS;
|
||||
extern cab_camera_access_mode CAB_CAMERA_ACCESS;
|
||||
extern bool TDJ_CAMERA;
|
||||
extern bool TDJ_CAMERA_PREFER_16_9;
|
||||
extern std::optional<std::string> TDJ_CAMERA_OVERRIDE;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#if SPICE64
|
||||
|
||||
#include <cstdint>
|
||||
#include "api/client.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
@@ -349,43 +350,29 @@ namespace games::sdvx {
|
||||
* 9 - v unit - 258 bytes - 86 colors
|
||||
*
|
||||
* data is stored in RGB order, 3 bytes per color
|
||||
*
|
||||
* TODO: expose this data via API
|
||||
*/
|
||||
|
||||
// data mapping
|
||||
static struct TapeLedMapping {
|
||||
size_t data_size;
|
||||
int index_r, index_g, index_b;
|
||||
|
||||
TapeLedMapping(size_t data_size, int index_r, int index_g, int index_b)
|
||||
: data_size(data_size), index_r(index_r), index_g(index_g), index_b(index_b) {}
|
||||
|
||||
} mapping[] = {
|
||||
{ 74, Lights::TITLE_AVG_R, Lights::TITLE_AVG_G, Lights::TITLE_AVG_B },
|
||||
{ 12, Lights::UPPER_LEFT_SPEAKER_AVG_R, Lights::UPPER_LEFT_SPEAKER_AVG_G, Lights::UPPER_LEFT_SPEAKER_AVG_B },
|
||||
{ 12, Lights::UPPER_RIGHT_SPEAKER_AVG_R, Lights::UPPER_RIGHT_SPEAKER_AVG_G, Lights::UPPER_RIGHT_SPEAKER_AVG_B },
|
||||
{ 56, Lights::LEFT_WING_AVG_R, Lights::LEFT_WING_AVG_G, Lights::LEFT_WING_AVG_B },
|
||||
{ 56, Lights::RIGHT_WING_AVG_R, Lights::RIGHT_WING_AVG_G, Lights::RIGHT_WING_AVG_B },
|
||||
{ 94, Lights::CONTROL_PANEL_AVG_R, Lights::CONTROL_PANEL_AVG_G, Lights::CONTROL_PANEL_AVG_B },
|
||||
{ 12, Lights::LOWER_LEFT_SPEAKER_AVG_R, Lights::LOWER_LEFT_SPEAKER_AVG_G, Lights::LOWER_LEFT_SPEAKER_AVG_B },
|
||||
{ 12, Lights::LOWER_RIGHT_SPEAKER_AVG_R, Lights::LOWER_RIGHT_SPEAKER_AVG_G, Lights::LOWER_RIGHT_SPEAKER_AVG_B },
|
||||
{ 14, Lights::WOOFER_AVG_R, Lights::WOOFER_AVG_G, Lights::WOOFER_AVG_B },
|
||||
{ 86, Lights::V_UNIT_AVG_R, Lights::V_UNIT_AVG_G, Lights::V_UNIT_AVG_B },
|
||||
};
|
||||
|
||||
// check index bounds
|
||||
if (tapeledutils::is_enabled() && index < std::size(mapping)) {
|
||||
auto &map = mapping[index];
|
||||
if (tapeledutils::is_enabled() && index < std::size(TAPELED_MAPPING)) {
|
||||
auto &map = TAPELED_MAPPING[index];
|
||||
const auto data_size = map.data.size();
|
||||
|
||||
// pick a color to use
|
||||
const auto rgb = tapeledutils::pick_color_from_led_tape(data, map.data_size);
|
||||
const auto rgb = tapeledutils::pick_color_from_led_tape(data, data_size);
|
||||
|
||||
// program the lights into API
|
||||
auto &lights = get_lights();
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_r], rgb.r);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
|
||||
|
||||
if (api::has_clients()) {
|
||||
for (size_t i = 0; i < data_size; ++i) {
|
||||
map.data[i].r = data[i * 3];
|
||||
map.data[i].g = data[i * 3 + 1];
|
||||
map.data[i].b = data[i * 3 + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (This != custom_node) {
|
||||
|
||||
@@ -61,6 +61,19 @@ namespace games::sdvx {
|
||||
static HKEY real_asio_reg_handle = nullptr;
|
||||
static HKEY real_asio_device_reg_handle = nullptr;
|
||||
|
||||
tapeledutils::tape_led TAPELED_MAPPING[SDVX_TAPELED_TOTAL] = {
|
||||
{ 74, Lights::TITLE_AVG_R, Lights::TITLE_AVG_G, Lights::TITLE_AVG_B, "Title" },
|
||||
{ 12, Lights::UPPER_LEFT_SPEAKER_AVG_R, Lights::UPPER_LEFT_SPEAKER_AVG_G, Lights::UPPER_LEFT_SPEAKER_AVG_B, "Upper Left Speaker" },
|
||||
{ 12, Lights::UPPER_RIGHT_SPEAKER_AVG_R, Lights::UPPER_RIGHT_SPEAKER_AVG_G, Lights::UPPER_RIGHT_SPEAKER_AVG_B, "Upper Right Speaker" },
|
||||
{ 56, Lights::LEFT_WING_AVG_R, Lights::LEFT_WING_AVG_G, Lights::LEFT_WING_AVG_B, "Left Wing" },
|
||||
{ 56, Lights::RIGHT_WING_AVG_R, Lights::RIGHT_WING_AVG_G, Lights::RIGHT_WING_AVG_B, "Right Wing" },
|
||||
{ 94, Lights::CONTROL_PANEL_AVG_R, Lights::CONTROL_PANEL_AVG_G, Lights::CONTROL_PANEL_AVG_B, "Control Panel" },
|
||||
{ 12, Lights::LOWER_LEFT_SPEAKER_AVG_R, Lights::LOWER_LEFT_SPEAKER_AVG_G, Lights::LOWER_LEFT_SPEAKER_AVG_B, "Lower Left Speaker" },
|
||||
{ 12, Lights::LOWER_RIGHT_SPEAKER_AVG_R, Lights::LOWER_RIGHT_SPEAKER_AVG_G, Lights::LOWER_RIGHT_SPEAKER_AVG_B, "Lower Right Speaker" },
|
||||
{ 14, Lights::WOOFER_AVG_R, Lights::WOOFER_AVG_G, Lights::WOOFER_AVG_B, "Woofer" },
|
||||
{ 86, Lights::V_UNIT_AVG_R, Lights::V_UNIT_AVG_G, Lights::V_UNIT_AVG_B, "V Unit" },
|
||||
};
|
||||
|
||||
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
|
||||
if (lpSubKey != nullptr &&
|
||||
phkResult != nullptr &&
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/game.h"
|
||||
#include "util/tapeled.h"
|
||||
|
||||
namespace games::sdvx {
|
||||
|
||||
@@ -25,6 +26,9 @@ namespace games::sdvx {
|
||||
// states
|
||||
extern bool SHOW_VM_MONITOR_WARNING;
|
||||
|
||||
constexpr int SDVX_TAPELED_TOTAL = 10;
|
||||
extern tapeledutils::tape_led TAPELED_MAPPING[SDVX_TAPELED_TOTAL];
|
||||
|
||||
static inline bool is_valkyrie_model() {
|
||||
return (
|
||||
avs::game::is_model("KFC") &&
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "d3d9_backend.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -113,6 +114,12 @@ static Direct3DCreate9On12Ex_t Direct3DCreate9On12Ex_orig = nullptr;
|
||||
static bool ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = false;
|
||||
static IDirect3DSwapChain9 *SUB_SWAP_CHAIN = nullptr;
|
||||
|
||||
// main and subscreen presents may occur on different threads.
|
||||
static std::atomic_bool SUBSCREEN_PRESENTED_SINCE_LAST_MAIN = false;
|
||||
|
||||
// do not mistake the fallback Present below for a game-originated Present.
|
||||
static thread_local bool SUBSCREEN_FORCE_REDRAW_IN_PROGRESS = false;
|
||||
|
||||
static void graphics_d3d9_ldj_init_sub_screen(
|
||||
IDirect3DDevice9Ex *device,
|
||||
D3DPRESENT_PARAMETERS *present_params,
|
||||
@@ -1445,6 +1452,12 @@ IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen() {
|
||||
return surface;
|
||||
}
|
||||
|
||||
void graphics_d3d9_notify_subscreen_present() {
|
||||
if (SUBSCREEN_FORCE_REDRAW && !SUBSCREEN_FORCE_REDRAW_IN_PROGRESS) {
|
||||
SUBSCREEN_PRESENTED_SINCE_LAST_MAIN.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
||||
// iidx/sdvx
|
||||
int swapchain = 1;
|
||||
@@ -1472,8 +1485,16 @@ static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
||||
//
|
||||
// early versions of popn HC needs this as well, but not on by default as it can cause
|
||||
// graphical glitches on some GPUs
|
||||
if (GRAPHICS_WINDOWED || SUBSCREEN_FORCE_REDRAW) {
|
||||
//
|
||||
// treat forced redraw as a fallback so it does not duplicate a successful game present.
|
||||
|
||||
const bool force_redraw = SUBSCREEN_FORCE_REDRAW &&
|
||||
!SUBSCREEN_PRESENTED_SINCE_LAST_MAIN.exchange(false, std::memory_order_relaxed);
|
||||
|
||||
if (GRAPHICS_WINDOWED || force_redraw) {
|
||||
SUBSCREEN_FORCE_REDRAW_IN_PROGRESS = true;
|
||||
SUB_SWAP_CHAIN->Present(nullptr, nullptr, nullptr, nullptr, 0);
|
||||
SUBSCREEN_FORCE_REDRAW_IN_PROGRESS = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ void graphics_d3d9_on_present(
|
||||
IDirect3DDevice9 *device,
|
||||
IDirect3DDevice9 *wrapped_device);
|
||||
|
||||
void graphics_d3d9_notify_subscreen_present();
|
||||
|
||||
IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen();
|
||||
|
||||
struct WrappedIDirect3D9 : IDirect3D9Ex {
|
||||
|
||||
@@ -134,6 +134,10 @@ ULONG STDMETHODCALLTYPE WrappedIDirect3DDevice9::Release() {
|
||||
this->main_swapchain->Release();
|
||||
this->main_swapchain = nullptr;
|
||||
}
|
||||
if (this->implicit_sub_swapchain) {
|
||||
this->implicit_sub_swapchain->Release();
|
||||
this->implicit_sub_swapchain = nullptr;
|
||||
}
|
||||
for (auto &sc : this->sub_swapchain) {
|
||||
if (sc) {
|
||||
sc->Release();
|
||||
@@ -487,6 +491,24 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
|
||||
fake_sub_swapchain[0]->AddRef();
|
||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[0]);
|
||||
|
||||
graphics_screens_register(iSwapChain);
|
||||
return D3D_OK;
|
||||
} else if (SUBSCREEN_FORCE_REDRAW) {
|
||||
// store implicit sub swap chain
|
||||
if (!implicit_sub_swapchain) {
|
||||
IDirect3DSwapChain9 *real_swapchain = nullptr;
|
||||
HRESULT ret = pReal->GetSwapChain(iSwapChain, &real_swapchain);
|
||||
if (FAILED(ret)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
implicit_sub_swapchain = new WrappedIDirect3DSwapChain9(this, real_swapchain);
|
||||
implicit_sub_swapchain->should_run_hooks = false;
|
||||
}
|
||||
|
||||
implicit_sub_swapchain->AddRef();
|
||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(implicit_sub_swapchain);
|
||||
|
||||
graphics_screens_register(iSwapChain);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
@@ -262,6 +262,7 @@ struct WrappedIDirect3DDevice9 : IDirect3DDevice9Ex {
|
||||
std::atomic_ulong refs = 1;
|
||||
|
||||
WrappedIDirect3DSwapChain9 *main_swapchain = nullptr;
|
||||
WrappedIDirect3DSwapChain9 *implicit_sub_swapchain = nullptr;
|
||||
WrappedIDirect3DSwapChain9 *sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
||||
FakeIDirect3DSwapChain9 *fake_sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
||||
|
||||
|
||||
@@ -96,6 +96,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DSwapChain9::Present(const RECT *pSourc
|
||||
pDirtyRegion,
|
||||
dwFlags);
|
||||
|
||||
// a successful game subscreen present suppresses the next forced fallback present.
|
||||
if (SUCCEEDED(result) &&
|
||||
(this == pDev->implicit_sub_swapchain || this == pDev->sub_swapchain[0])) {
|
||||
graphics_d3d9_notify_subscreen_present();
|
||||
}
|
||||
|
||||
// Some drivers report S_PRESENT_MODE_CHANGED after Windows moves the portrait SMALL
|
||||
// head into the requested exclusive mode, leaving both group heads black. Toggle SMALL
|
||||
// through another supported mode and restore it once, then retry the interrupted MAIN
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
#include "avs/game.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/popn/popn.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
@@ -29,6 +31,7 @@ static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr;
|
||||
#ifdef SPICE64
|
||||
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
|
||||
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
|
||||
static decltype(IsDBCSLeadByteEx) *IsDBCSLeadByteEx_orig = nullptr;
|
||||
static decltype(WideCharToMultiByte) *WideCharToMultiByte_orig = nullptr;
|
||||
static decltype(GetLocaleInfoA) *GetLocaleInfoA_orig = nullptr;
|
||||
static decltype(GetThreadLocale) *GetThreadLocale_orig = nullptr;
|
||||
@@ -182,9 +185,30 @@ static BOOL WINAPI IsDBCSLeadByte_hook (
|
||||
BYTE TestChar
|
||||
)
|
||||
{
|
||||
if (IsDBCSLeadByteEx_orig) {
|
||||
return IsDBCSLeadByteEx_orig(CODEPAGE_SHIFT_JIS, TestChar);
|
||||
}
|
||||
|
||||
return IsDBCSLeadByteEx(CODEPAGE_SHIFT_JIS, TestChar);
|
||||
}
|
||||
|
||||
static BOOL WINAPI IsDBCSLeadByteEx_hook(
|
||||
UINT CodePage,
|
||||
BYTE TestChar)
|
||||
{
|
||||
switch (CodePage) {
|
||||
case CP_ACP:
|
||||
case CP_THREAD_ACP:
|
||||
CodePage = CODEPAGE_SHIFT_JIS;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return IsDBCSLeadByteEx_orig(CodePage, TestChar);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
WINAPI
|
||||
@@ -250,6 +274,18 @@ GetLocaleInfoA_hook(
|
||||
void hooks::lang::early_init() {
|
||||
log_info("hooks::lang", "early initialization");
|
||||
|
||||
const auto native_code_page = GetACP();
|
||||
if (native_code_page == CP_UTF8) {
|
||||
log_warning(
|
||||
"hooks::lang",
|
||||
"Windows is using UTF-8 as the system code page; "
|
||||
"some games may render text incorrectly or behave unexpectedly");
|
||||
|
||||
deferredlogs::defer_error_messages({
|
||||
"Windows is using UTF-8 as the system code page",
|
||||
" some games may render text incorrectly or behave unexpectedly"});
|
||||
}
|
||||
|
||||
// hooking these two functions fixes the jubeat mojibake
|
||||
detour::trampoline_try("kernel32.dll", "GetACP", GetACP_hook, &GetACP_orig);
|
||||
detour::trampoline_try("kernel32.dll", "GetOEMCP", GetOEMCP_hook, &GetOEMCP_orig);
|
||||
@@ -315,6 +351,15 @@ void hooks::lang::early_init() {
|
||||
WideCharToMultiByte_hook,
|
||||
&WideCharToMultiByte_orig);
|
||||
}
|
||||
|
||||
if (games::popn::is_pikapika_model() && native_code_page == CP_UTF8) {
|
||||
detour::trampoline_try(
|
||||
"kernel32.dll",
|
||||
"IsDBCSLeadByteEx",
|
||||
IsDBCSLeadByteEx_hook,
|
||||
&IsDBCSLeadByteEx_orig);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <assert.h>
|
||||
#include <shlwapi.h>
|
||||
#include <windows.h>
|
||||
#include <cfg/configurator.h>
|
||||
@@ -524,22 +523,25 @@ int main_implementation(int argc, char *argv[]) {
|
||||
games::iidx::FLIP_CAMS = true;
|
||||
}
|
||||
|
||||
// IIDX CONNECT_CAMERA logic here for cases where user is running without -iidx module
|
||||
// for now, we assume that user may be running on a cab
|
||||
// (games::iidx::DISABLE_CAMS starts out as false unless user overrides)
|
||||
// we will check again in IIDX module with a different default
|
||||
assert(!games::iidx::DISABLE_CAMS.has_value());
|
||||
if (options[launcher::Options::IIDXDisableCameras].value_bool()) {
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
// Resolve the IIDX camera policy here so it also applies without the -iidx module.
|
||||
const auto &cab_camera_access = options[launcher::Options::IIDXCabCamAccess];
|
||||
if (cab_camera_access.is_active()) {
|
||||
const auto value = cab_camera_access.value_text();
|
||||
if (value == "off") {
|
||||
games::iidx::CAB_CAMERA_ACCESS = games::iidx::cab_camera_access_mode::off;
|
||||
} else if (value == "on") {
|
||||
games::iidx::CAB_CAMERA_ACCESS = games::iidx::cab_camera_access_mode::on;
|
||||
} else if (value == "legacy") {
|
||||
games::iidx::CAB_CAMERA_ACCESS = games::iidx::cab_camera_access_mode::legacy;
|
||||
}
|
||||
}
|
||||
if (options[launcher::Options::IIDXCabCamAccess].is_active() &&
|
||||
options[launcher::Options::IIDXCabCamAccess].value_text() == "off") {
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
|
||||
if (options[launcher::Options::IIDXDisableCameras].value_bool()) {
|
||||
games::iidx::CAB_CAMERA_ACCESS = games::iidx::cab_camera_access_mode::off;
|
||||
}
|
||||
if (options[launcher::Options::IIDXCamHook].value_bool()) {
|
||||
games::iidx::TDJ_CAMERA = true;
|
||||
// Disable legacy behaviour to avoid conflict
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
games::iidx::CAB_CAMERA_ACCESS = games::iidx::cab_camera_access_mode::off;
|
||||
}
|
||||
// CONNECT_CAMERA env var will be set once logging is enabled
|
||||
|
||||
@@ -1717,14 +1719,13 @@ int main_implementation(int argc, char *argv[]) {
|
||||
GRAPHICS_FS_ORIENTATION_SWAP = true;
|
||||
}
|
||||
|
||||
|
||||
// for cab usage - set environment variables (outside of -iidx module)
|
||||
if (games::iidx::DISABLE_CAMS.has_value() &&
|
||||
games::iidx::DISABLE_CAMS.value() &&
|
||||
// apply an explicit off outside of the -iidx module so it also works on cabinets.
|
||||
if (games::iidx::CAB_CAMERA_ACCESS == games::iidx::cab_camera_access_mode::off &&
|
||||
!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_misc("launcher::iidx", "CONNECT_CAMERA env var set to 0");
|
||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||
}
|
||||
|
||||
if (games::iidx::SOUND_OUTPUT_DEVICE.has_value() &&
|
||||
games::iidx::SOUND_OUTPUT_DEVICE.value() != "auto" &&
|
||||
!cfg::CONFIGURATOR_STANDALONE) {
|
||||
|
||||
@@ -640,17 +640,21 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// IIDXCabCamAccess
|
||||
.title = "IIDX Use Official AC Cams",
|
||||
.title = "IIDX Official AC Camera Access",
|
||||
.name = "iidxcabcams",
|
||||
.desc = "For IIDX25+, allow direct access to cameras from real arcade cabinets. "
|
||||
"Only turn this on if you have OFFICIAL arcade cameras connected to the correct USB ports. Default: auto.",
|
||||
.desc = "Controls how the game accesses USB cameras for IIDX 25+.\n\n"
|
||||
"auto (default): use [off] when the IIDX module is enabled; otherwise, use [on].\n\n"
|
||||
"on: game discovers and directly accesses cameras; requires official cameras on correct USB ports.\n\n"
|
||||
"legacy: for IIDX 25/26 only; allow camera access with emulated discovery.\n\n"
|
||||
"off: prevent game from accessing USB cameras.",
|
||||
.type = OptionType::Enum,
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Cab Peripherals",
|
||||
.elements = {
|
||||
{"auto", ""},
|
||||
{"on", ""},
|
||||
{"legacy", ""},
|
||||
{"off", ""},
|
||||
{"on", ""}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -977,7 +981,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.display_name = "sdvxsubredraw",
|
||||
.aliases= "sdvxsubredraw",
|
||||
.desc = "Check if submonitor in fullscreen mode doesn't update every frame; "
|
||||
"this option forces subscreen to redraw every frame.",
|
||||
"this option presents the subscreen when the game does not.",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Advanced Game Options",
|
||||
@@ -1171,7 +1175,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.title = "Pop'n Music PikaPika Subscreen Force Redraw",
|
||||
.name = "popnsubredraw",
|
||||
.desc = "Check if submonitor in fullscreen mode appears stuck; "
|
||||
"this option forces subscreen to redraw every frame.",
|
||||
"this option presents the subscreen when the game does not.",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Pop'n Music",
|
||||
.category = "Advanced Game Options",
|
||||
|
||||
Reference in New Issue
Block a user