Files
spice2x.github.io/src/spice2x/launcher/launcher.cpp
T
Selundine ef384e85dd Add GITADORA Arena windowed monitor layout support, allows the SMALL touch window to be resized (#716)
## Link to GitHub Issue or related Pull Request, if one exists
N/A

## Description of change
Adds GITADORA Arena windowed monitor layout options for the GITADORA,
LEFT, RIGHT, and SMALL windows.

This also applies the selected window border style to all four GITADORA
Arena windows, and allows the SMALL touch window to be resized in
windowed mode while keeping touch coordinates updated.

## Testing
- Manually tested GITADORA Arena windowed mode:
  - default, resizable, and borderless window styles
  - per-window monitor placement
  - SMALL resize
  - SMALL touch coordinate mapping after resize
2026-05-30 23:09:06 -07:00

2814 lines
100 KiB
C++

#include <condition_variable>
#include <iostream>
#include <memory>
#include <vector>
#include <assert.h>
#include <shlwapi.h>
#include <windows.h>
#include <cfg/configurator.h>
#include "api/modules/capture.h"
#include "acio/acio.h"
#include "acio/icca/icca.h"
#include "acio/mdxf/mdxf.h"
#include "api/controller.h"
#include "avs/automap.h"
#include "avs/core.h"
#include "avs/ea3.h"
#include "avs/game.h"
#include "build/defs.h"
#include "cfg/spicecfg.h"
#include "cfg/config.h"
#include "cfg/screen_resize.h"
#include "easrv/easrv.h"
#include "external/cardio/cardio_runner.h"
#ifndef NO_SCARD
#include "external/scard/scard.h"
#endif
#include "games/game.h"
#include "games/io.h"
#include "games/bbc/bbc.h"
#include "games/bs/bs.h"
#include "games/ddr/ddr.h"
#include "games/dea/dea.h"
#include "games/drs/drs.h"
#include "games/gitadora/gitadora.h"
#include "games/hpm/hpm.h"
#include "games/iidx/iidx.h"
#ifdef SPICE64
#include "games/iidx/camera.h"
#endif
#include "games/iidx/poke.h"
#include "games/jb/jb.h"
#include "games/mga/mga.h"
#include "games/nost/nost.h"
#include "games/nost/poke.h"
#include "games/popn/popn.h"
#include "games/qma/qma.h"
#include "games/rb/rb.h"
#include "games/rf3d/rf3d.h"
#include "games/sc/sc.h"
#include "games/scotto/scotto.h"
#include "games/sdvx/sdvx.h"
#include "games/shared/printer.h"
#include "games/silentscope/silentscope.h"
#include "games/mfc/mfc.h"
#include "games/ftt/ftt.h"
#include "games/loveplus/loveplus.h"
#include "games/we/we.h"
#include "games/otoca/otoca.h"
#include "games/shogikai/shogikai.h"
#include "games/pcm/pcm.h"
#include "games/onpara/onpara.h"
#include "games/bc/bc.h"
#include "games/ccj/ccj.h"
#include "games/ccj/trackball.h"
#include "games/qks/qks.h"
#include "games/mfg/mfg.h"
#include "games/pc/pc.h"
#include "games/museca/museca.h"
#include "hooks/avshook.h"
#include "hooks/audio/audio.h"
#include "hooks/debughook.h"
#include "hooks/devicehook.h"
#include "hooks/graphics/nvenc_hook.h"
#include "hooks/input/dinput8/hook.h"
#include "hooks/graphics/graphics.h"
#include "hooks/lang.h"
#include "hooks/networkhook.h"
#include "hooks/icmphook_net.h"
#include "hooks/unisintrhook.h"
#include "launcher/launcher.h"
#include "launcher/logger.h"
#include "launcher/signal.h"
#include "launcher/superexit.h"
#include "launcher/richpresence.h"
#include "launcher/shutdown.h"
#include "launcher/options.h"
#include "misc/bt5api.h"
#include "misc/device.h"
#include "misc/eamuse.h"
#include "misc/extdev.h"
#include "misc/ami2000.h"
#include "misc/sciunit.h"
#include "misc/sde.h"
#include "misc/wintouchemu.h"
#include "overlay/overlay.h"
#include "overlay/notifications.h"
#include "overlay/windows/patch_manager.h"
#include "overlay/windows/iidx_seg.h"
#include "rawinput/rawinput.h"
#include "rawinput/touch.h"
#include "reader/reader.h"
#include "sdk/sdk.h"
#include "stubs/stubs.h"
#include "touch/touch.h"
#include "util/cpuutils.h"
#include "util/crypt.h"
#include "util/deferlog.h"
#include "util/fileutils.h"
#include "util/libutils.h"
#include "util/logging.h"
#include "util/peb.h"
#include "util/precise_timer.h"
#include "util/socd_cleaner.h"
#include "util/sysutils.h"
#include "util/tapeled.h"
#include "util/time.h"
#include "util/utils.h"
#include "avs/ssl.h"
#include "nvapi/nvapi.h"
#include "hooks/graphics/nvapi_hook.h"
// std::max
#ifdef max
#undef max
#endif
// constants
static const char *STUBS[] = {"kbt.dll", "kld.dll"};
// general settings
static std::vector<std::string> game_hooks;
static std::vector<std::string> early_hooks;
std::filesystem::path MODULE_PATH;
HANDLE LOG_FILE = INVALID_HANDLE_VALUE;
std::string LOG_FILE_PATH = "";
int LAUNCHER_ARGC = 0;
char **LAUNCHER_ARGV = nullptr;
std::unique_ptr<std::vector<Option>> LAUNCHER_OPTIONS;
std::mutex CARD_OVERRIDES_LOCK;
std::string CARD_OVERRIDES[2];
// sub-systems
std::unique_ptr<api::Controller> API_CONTROLLER;
std::unique_ptr<rawinput::RawInputManager> RI_MGR;
// trigger NVIDIA Optimus & AMD Enduro High Performance Graphics
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
static bool CHECK_DLL_IGNORE_ARCH = false;
static bool check_dll(const std::string &model) {
if (cfg::CONFIGURATOR_STANDALONE || CHECK_DLL_IGNORE_ARCH) {
return fileutils::file_exists(MODULE_PATH / model);
} else {
return fileutils::verify_header_pe(MODULE_PATH / model);
}
}
void update_msvcrt_args(int argc, char *argv[]);
void dump_button_bindings(std::vector<Button> *buttons);
void dump_analog_bindings();
int main_implementation(int argc, char *argv[]) {
// remember argv, argv
LAUNCHER_ARGC = argc;
LAUNCHER_ARGV = argv;
// register exception handler and control handler
launcher::signal::init();
// start logger
logger::start();
// get module path
MODULE_PATH = libutils::module_file_name(nullptr).parent_path();
// initialize crypt
crypt::init();
// initialize timer
init_performance_counter();
// api settings
bool api_enable = false;
bool api_pretty = false;
bool api_debug = false;
unsigned short api_port = 1337;
std::string api_pass = "";
std::vector<std::string> api_serial_port;
std::vector<DWORD> api_serial_baud;
// attach settings
bool attach_io = false;
bool attach_acio = false;
bool attach_icca = false;
bool attach_device = false;
bool attach_extdev = false;
bool attach_ami2000 = false;
bool attach_sciunit = false;
bool attach_cpusbxpkm_printer = false;
bool attach_iidx = false;
bool attach_sdvx = false;
bool attach_jb = false;
bool attach_rb = false;
bool attach_shogikai = false;
bool attach_mga = false;
bool attach_sc = false;
bool attach_popn = false;
bool attach_ddr = false;
bool attach_gitadora = false;
bool attach_nostalgia = false;
bool attach_bbc = false;
bool attach_hpm = false;
bool attach_qma = false;
bool attach_dea = false;
bool attach_mfc = false;
bool attach_ftt = false;
bool attach_bs = false;
bool attach_loveplus = false;
bool attach_scotto = false;
bool attach_rf3d = false;
bool attach_drs = false;
bool attach_we = false;
bool attach_otoca = false;
bool attach_silentscope = false;
bool attach_pcm = false;
bool attach_onpara = false;
bool attach_bc = false;
bool attach_ccj = false;
bool attach_qks = false;
bool attach_mfg = false;
bool attach_pc = false;
bool attach_museca = false;
bool show_cursor_if_no_touch = false;
// misc settings
size_t user_heap_size = 0;
unsigned short easrv_port = 0;
bool easrv_maint = true;
bool easrv_smart = false;
bool load_stubs = false;
bool netfix_disable = false;
bool icmphook_enable = false;
bool lang_disable = false;
std::string process_priority_str = "high";
bool cardio_enabled = false;
bool peb_print = false;
bool cfg_run = false;
bool rich_presence = false;
bool automap = false;
bool ssl_disable = false;
bool dump_sysinfo = true;
std::vector<std::string> sextet_devices;
std::optional<rawinput::MidiNoteAlgorithm> midi_algo;
// parse arguments
LAUNCHER_OPTIONS = launcher::parse_options(argc, argv);
// command line override (must be done before merging options with cfg)
if (LAUNCHER_OPTIONS->at(launcher::Options::OptionConflictResolution).value_bool()) {
launcher::USE_CMD_OVERRIDE = true;
}
// determine config file path - must be done before anything else
const auto &cfg_path = LAUNCHER_OPTIONS->at(launcher::Options::ConfigurationPath);
if (cfg_path.is_active()) {
CONFIG_PATH_OVERRIDE = cfg_path.value_text();
}
// detect model used to load option overrides
auto options_version = launcher::detect_gameversion(
LAUNCHER_OPTIONS->at(launcher::Options::PathToEa3Config).value,
LAUNCHER_OPTIONS->at(launcher::Options::PathToBootstrap).value
);
if (!options_version.model.empty() && options_version.model.size() < 4) {
if (options_version.dest.size() == 1) {
avs::game::DEST[0] = options_version.dest[0];
}
if (options_version.spec.size() == 1) {
avs::game::SPEC[0] = options_version.spec[0];
}
if (options_version.rev.size() == 1) {
avs::game::REV[0] = options_version.rev[0];
}
if (options_version.ext.size() == 10) {
strcpy(avs::game::EXT, options_version.ext.c_str());
}
strcpy(avs::game::MODEL, options_version.model.c_str());
eamuse_autodetect_game();
}
// grab merged game options
auto options_ptr = games::get_options(eamuse_get_game());
if (!options_ptr) {
options_ptr = LAUNCHER_OPTIONS.get();
}
auto &options = *options_ptr;
#if !SPICE_XP
{
// skip elevation only if AutoElevate is explicitly set to "user"
const bool skip_elevation = options[launcher::Options::AutoElevate].is_active() &&
options[launcher::Options::AutoElevate].value_text() == "user";
if (!skip_elevation && !sysutils::is_running_as_admin()) {
log_info("launcher", "relaunching with administrator privileges");
if (sysutils::relaunch_as_admin()) {
exit(0);
} else {
// elevation failed or was denied by the user
log_fatal("launcher", "failed to launch with administrator privileges");
exit(1);
}
}
}
#endif // !SPICE_XP
// check options
// TODO: get rid of some booleans here and make use of the options directly
if (options[launcher::Options::OpenConfigurator].value_bool()) {
CHECK_DLL_IGNORE_ARCH = true;
cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config;
cfg_run = true;
}
// -ea, but ignored if -url is set
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
!options[launcher::Options::ServiceURL].is_active()) {
avs::ea3::URL_SLASH = 1;
easrv_port = 8080;
}
if (options[launcher::Options::SmartEAmusement].value_bool()) {
easrv_smart = true;
}
if (options[launcher::Options::EAmusementMaintenance].is_active()) {
easrv_maint = options[launcher::Options::EAmusementMaintenance].value_uint32() > 0;
}
if (options[launcher::Options::spice2x_EAmusementMaintenance].value_bool()) {
easrv_maint = true;
}
if (options[launcher::Options::WindowedMode].value_bool()) {
GRAPHICS_WINDOWED = true;
}
if (options[launcher::Options::GraphicsForceRefresh].is_active()) {
GRAPHICS_FORCE_REFRESH = options[launcher::Options::GraphicsForceRefresh].value_uint32();
}
if (options[launcher::Options::FullscreenSubRefreshRate].is_active()) {
GRAPHICS_FORCE_REFRESH_SUB =
options[launcher::Options::FullscreenSubRefreshRate].value_uint32();
}
if (options[launcher::Options::GraphicsForceSingleAdapter].value_bool()) {
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
}
if (options[launcher::Options::ForceBackBufferCount].is_active()) {
const auto n = options[launcher::Options::ForceBackBufferCount].value_uint32();
if (n == 2 || n == 3) {
// user provided 2 => double buffering => 1 back buffer
// user provided 3 => triple buffering => 2 back buffers
GRAPHICS_FORCE_VSYNC_BUFFER = n - 1;
} else {
log_warning("graphics", "invalid parameter specified for -vsyncbuffer: {}", n);
}
}
if (options[launcher::Options::spice2x_IIDXNoSub].value_bool()) {
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
}
if (options[launcher::Options::spice2x_SDVXNoSub].value_bool()) {
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
GRAPHICS_PREVENT_SECONDARY_WINDOWS = true;
}
if (options[launcher::Options::DXDisplayAdapter].is_active() &&
options[launcher::Options::DXDisplayAdapter].value_uint32() != D3DADAPTER_DEFAULT) {
D3D9_ADAPTER = options[launcher::Options::DXDisplayAdapter].value_uint32();
// when we fix up adapter numbers, we only fix the first adapter, and not any subsequent
// adapters, so we can't deal with multi-monitor games
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
}
if (options[launcher::Options::CaptureCursor].value_bool()) {
GRAPHICS_CAPTURE_CURSOR = true;
}
if (options[launcher::Options::ShowCursor].value_bool()) {
GRAPHICS_SHOW_CURSOR = true;
}
if (options[launcher::Options::VerboseGraphicsLogging].value_bool()) {
GRAPHICS_LOG_HRESULT = true;
}
if (options[launcher::Options::VerboseAVSLogging].value_bool()) {
hooks::avs::config::LOG = true;
}
if (options[launcher::Options::AllowEA3Verbose].value_bool()) {
avs::ea3::EA3_DEBUG_VERBOSE = true;
}
std::optional<graphics_orientation> monitor_orientation;
if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
monitor_orientation =
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
} else if (options[launcher::Options::AdjustOrientation].value_bool()) {
monitor_orientation = ORIENTATION_CW;
}
std::optional<std::pair<uint32_t, uint32_t>> monitor_resolution;
if (options[launcher::Options::ChangeResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::ChangeResolution].value_text(), result)) {
monitor_resolution = result;
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "failed to parse -changeres");
}
}
if (options[launcher::Options::spice2x_NoD3D9DeviceHook].value_bool()) {
D3D9_DEVICE_HOOK_DISABLE = true;
// touch emulation gets disabled, might as well turn these on
games::iidx::NATIVE_TOUCH = true;
games::sdvx::NATIVETOUCH = true;
// not strictly necessary as it will fail to init anyway, but cleaner to just disable it now
overlay::ENABLED = false;
// leaving these on without dx9hooks result in torn state and therefore failure to draw
GRAPHICS_FORCE_SINGLE_ADAPTER = false;
}
if (options[launcher::Options::spice2x_NvapiProfile].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
nvapi::ADD_PROFILE = true;
}
if (options[launcher::Options::spice2x_NoNVAPI].value_bool()) {
nvapi_hook::BYPASS_NVAPI = true;
}
if (options[launcher::Options::LogLevel].is_active()) {
avs::core::LOG_LEVEL_CUSTOM = options[launcher::Options::LogLevel].value_text();
}
for (auto &hook : options[launcher::Options::InjectHook].values_text()) {
std::string buffer;
std::stringstream stream(hook);
std::vector<std::string> tokens;
while (stream >> buffer) {
game_hooks.push_back(buffer);
}
}
for (auto &hook : options[launcher::Options::EarlyInjectHook].values_text()) {
std::string buffer;
std::stringstream stream(hook);
std::vector<std::string> tokens;
while (stream >> buffer) {
early_hooks.push_back(buffer);
}
}
if (options[launcher::Options::LoadStubs].value_bool()) {
load_stubs = true;
}
if (options[launcher::Options::EnableAllIOModules].value_bool()) {
attach_io = true;
}
if (options[launcher::Options::EnableACIOModule].value_bool()) {
attach_acio = true;
}
if (options[launcher::Options::EnableICCAModule].value_bool()) {
attach_icca = true;
}
if (options[launcher::Options::EnableDEVICEModule].value_bool()) {
attach_device = true;
}
if (options[launcher::Options::EnableEXTDEVModule].value_bool()) {
attach_extdev = true;
}
if (options[launcher::Options::EnableAMI2000Module].value_bool()) {
attach_ami2000 = true;
}
if (options[launcher::Options::EnableDevicePassthrough].value_bool()) {
hooks::device::ENABLE = false;
}
if (options[launcher::Options::LoadSoundVoltexModule].value_bool()) {
attach_sdvx = true;
}
if (options[launcher::Options::SDVXNativeTouch].value_bool()) {
games::sdvx::NATIVETOUCH = true;
}
if (options[launcher::Options::spice2x_SDVXDigitalKnobSensitivity].is_active()) {
games::sdvx::DIGITAL_KNOB_SENS = (uint8_t)
options[launcher::Options::spice2x_SDVXDigitalKnobSensitivity].value_uint32();
}
if (options[launcher::Options::spice2x_SDVXAsioDriver].is_active()) {
games::sdvx::ASIO_DRIVER = options[launcher::Options::spice2x_SDVXAsioDriver].value_text();
}
if (options[launcher::Options::spice2x_SDVXSubPos].is_active()) {
auto txt = options[launcher::Options::spice2x_SDVXSubPos].value_text();
if (txt == "top") {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_TOP;
} else if (txt == "center") {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_MIDDLE;
} else if (txt == "bottomleft") {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT;
} else if (txt == "bottomright") {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT;
}
// else - bottom
}
if (options[launcher::Options::spice2x_SDVXSubRedraw].value_bool()) {
SUBSCREEN_FORCE_REDRAW = true;
}
if (options[launcher::Options::SDVXSubMonitorOverride].is_active()) {
sysutils::SECOND_MONITOR_OVERRIDE = options[launcher::Options::SDVXSubMonitorOverride].value_text();
}
if (options[launcher::Options::LoadIIDXModule].value_bool()) {
attach_iidx = true;
}
if (options[launcher::Options::IIDXCameraOrderFlip].value_bool()) {
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;
}
if (options[launcher::Options::IIDXCabCamAccess].is_active() &&
options[launcher::Options::IIDXCabCamAccess].value_text() == "off") {
games::iidx::DISABLE_CAMS = true;
}
if (options[launcher::Options::IIDXCamHook].value_bool()) {
games::iidx::TDJ_CAMERA = true;
// Disable legacy behaviour to avoid conflict
games::iidx::DISABLE_CAMS = true;
}
// CONNECT_CAMERA env var will be set once logging is enabled
if (options[launcher::Options::IIDXCamHookOverride].is_active()) {
games::iidx::TDJ_CAMERA_OVERRIDE = options[launcher::Options::IIDXCamHookOverride].value_text();
}
if (options[launcher::Options::IIDXCamHookRatio].is_active() &&
options[launcher::Options::IIDXCamHookRatio].value_text() == "169") {
games::iidx::TDJ_CAMERA_PREFER_16_9 = true;
}
if (options[launcher::Options::IIDXSoundOutputDevice].is_active()) {
games::iidx::SOUND_OUTPUT_DEVICE = options[launcher::Options::IIDXSoundOutputDevice].value_text();
}
if (options[launcher::Options::IIDXAsioDriver].is_active()) {
games::iidx::ASIO_DRIVER = options[launcher::Options::IIDXAsioDriver].value_text();
}
if (options[launcher::Options::IIDXTDJMode].value_bool()) {
games::iidx::TDJ_MODE = true;
}
if (options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].is_active()) {
games::iidx::DIGITAL_TT_SENS = (uint8_t)
options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].value_uint32();
}
if (options[launcher::Options::spice2x_IIDXLDJForce720p].value_bool()) {
games::iidx::FORCE_720P = true;
}
if (options[launcher::Options::spice2x_IIDXTDJSubSize].is_active()) {
games::iidx::SUBSCREEN_OVERLAY_SIZE =
options[launcher::Options::spice2x_IIDXTDJSubSize].value_text();
}
if (options[launcher::Options::spice2x_IIDXLEDFontSize].is_active()) {
overlay::windows::IIDX_SEGMENT_FONT_SIZE =
options[launcher::Options::spice2x_IIDXLEDFontSize].value_uint32();
}
if (options[launcher::Options::spice2x_IIDXLEDColor].is_active()) {
overlay::windows::IIDX_SEGMENT_FONT_COLOR =
options[launcher::Options::spice2x_IIDXLEDColor].value_hex64();
}
if (options[launcher::Options::spice2x_IIDXLEDPos].is_active()) {
overlay::windows::IIDX_SEGMENT_LOCATION =
options[launcher::Options::spice2x_IIDXLEDPos].value_text();
}
if (options[launcher::Options::IIDXLEDBorderless].value_bool()) {
overlay::windows::IIDX_SEGMENT_BORDERLESS = true;
}
if (options[launcher::Options::spice2x_IIDXNoESpec].value_bool()) {
games::iidx::DISABLE_ESPEC_IO = true;
}
if (options[launcher::Options::spice2x_IIDXNativeTouch].value_bool()) {
games::iidx::NATIVE_TOUCH = true;
}
// should come later since this will override a few settings
if (options[launcher::Options::spice2x_IIDXWindowedTDJ].value_bool() ||
(options[launcher::Options::IIDXTDJMode].value_bool() && GRAPHICS_WINDOWED)) {
games::iidx::TDJ_MODE = true;
GRAPHICS_WINDOWED = true;
games::iidx::SCREEN_MODE = "2";
}
#ifdef SPICE64
if (options[launcher::Options::IIDXRecQuality].is_active()) {
nvenc_hook::VIDEO_CQP_STRING_OVERRIDE = options[launcher::Options::IIDXRecQuality].value_text();
}
if (options[launcher::Options::IIDXRecDisable].value_bool()) {
nvenc_hook::FORCE_DISABLE = true;
}
#endif
if (options[launcher::Options::OtocaCamHook].value_bool()) {
games::otoca::BYPASS_CAMERA = true;
}
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
attach_jb = true;
}
if (options[launcher::Options::LoadBeatstreamModule].value_bool()) {
attach_bs = true;
}
if (options[launcher::Options::LoadReflecBeatModule].value_bool()) {
attach_rb = true;
}
if (options[launcher::Options::LoadShogikaiModule].value_bool()) {
attach_shogikai = true;
}
if (options[launcher::Options::LoadPopnMusicModule].value_bool()) {
attach_popn = true;
}
if (options[launcher::Options::PopnMusicForceHDMode].value_bool()) {
avs::ea3::PCB_TYPE = 1;
}
if (options[launcher::Options::PopnMusicForceSDMode].value_bool()) {
avs::ea3::PCB_TYPE = 0;
}
if (options[launcher::Options::PopnNoSub].value_bool()) {
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
GRAPHICS_PREVENT_SECONDARY_WINDOWS = true;
}
if (options[launcher::Options::PopnSubMonitorOverride].is_active()) {
sysutils::SECOND_MONITOR_OVERRIDE = options[launcher::Options::PopnSubMonitorOverride].value_text();
}
if (options[launcher::Options::PopnNativeTouch].value_bool()) {
games::popn::NATIVE_TOUCH = true;
}
if (options[launcher::Options::LoadMetalGearArcadeModule].value_bool()) {
attach_mga = true;
}
if (options[launcher::Options::LoadGitaDoraModule].value_bool()) {
attach_gitadora = true;
}
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32();
}
// gitadora arena layout
if (options[launcher::Options::GitaDoraArenaSingleWindow].value_bool()) {
games::gitadora::ARENA_WINDOW_COUNT = 1;
}
if (options[launcher::Options::GitaDoraArenaWindowLayout].is_active()) {
const auto window_count = options[launcher::Options::GitaDoraArenaWindowLayout].value_text();
if (window_count == "1") {
games::gitadora::ARENA_WINDOW_COUNT = 1;
} else if (window_count == "2") {
games::gitadora::ARENA_WINDOW_COUNT = 2;
} else if (window_count == "4") {
games::gitadora::ARENA_WINDOW_COUNT = 4;
}
}
if (options[launcher::Options::GitaDoraWailHold].is_active()) {
socd::TILT_HOLD_MS = options[launcher::Options::GitaDoraWailHold].value_uint32();
}
if (options[launcher::Options::GitaDoraPickAlgo].is_active()) {
const auto text = options[launcher::Options::GitaDoraPickAlgo].value_text();
if (text == "legacy") {
games::gitadora::PICK_ALGO.reset();
} else if (text == "neutral") {
games::gitadora::PICK_ALGO = socd::SocdAlgorithm::Neutral;
} else if (text == "raw") {
games::gitadora::PICK_ALGO = socd::SocdAlgorithm::None;
}
}
if (options[launcher::Options::GitaDoraSubOverlaySize].is_active()) {
games::gitadora::SUBSCREEN_OVERLAY_SIZE = options[launcher::Options::GitaDoraSubOverlaySize].value_text();
}
if (options[launcher::Options::GitaDoraArenaAsioDriver].is_active()) {
games::gitadora::ASIO_DRIVER = options[launcher::Options::GitaDoraArenaAsioDriver].value_text();
}
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
attach_nostalgia = true;
}
if (options[launcher::Options::LoadBBCModule].value_bool()) {
attach_bbc = true;
}
if (options[launcher::Options::LoadHelloPopnMusicModule].value_bool()) {
attach_hpm = true;
}
if (options[launcher::Options::LoadQuizMagicAcademyModule].value_bool()) {
attach_qma = true;
}
if (options[launcher::Options::LoadDanceEvolutionModule].value_bool()) {
attach_dea = true;
}
if (options[launcher::Options::LoadLovePlusModule].value_bool()) {
attach_loveplus = true;
}
if (options[launcher::Options::GitaDoraTwoChannelAudio].value_bool()) {
games::gitadora::TWOCHANNEL = true;
}
if (options[launcher::Options::GitaDoraLefty].is_active()) {
const auto text = options[launcher::Options::GitaDoraLefty].value_text();
if (text == "p1") {
games::gitadora::P1_LEFTY = true;
} else if (text == "p2") {
games::gitadora::P2_LEFTY = true;
} else if (text == "both") {
games::gitadora::P1_LEFTY = true;
games::gitadora::P2_LEFTY = true;
}
}
if (options[launcher::Options::LoadDDRModule].value_bool()) {
attach_ddr = true;
}
if (options[launcher::Options::LoadMahjongFightClubModule].value_bool()) {
attach_mfc = true;
}
if (options[launcher::Options::LoadFutureTomTomModule].value_bool()) {
attach_ftt = true;
}
if (options[launcher::Options::LoadScottoModule].value_bool()) {
attach_scotto = true;
}
if (options[launcher::Options::LoadRoadFighters3DModule].value_bool()) {
attach_rf3d = true;
}
if (options[launcher::Options::LoadDanceRushModule].value_bool()) {
attach_drs = true;
}
if (options[launcher::Options::LoadWinningElevenModule].value_bool()) {
attach_we = true;
}
if (options[launcher::Options::LoadOtocaModule].value_bool()) {
attach_otoca = true;
}
if (options[launcher::Options::LoadChargeMachineModule].value_bool()) {
attach_pcm = true;
}
if (options[launcher::Options::LoadOngakuParadiseModule].value_bool()) {
attach_onpara = true;
}
if (options[launcher::Options::LoadBusouShinkiModule].value_bool()) {
attach_bc = true;
}
if (options[launcher::Options::LoadCCJModule].value_bool()) {
attach_ccj = true;
}
if (options[launcher::Options::LoadQKSModule].value_bool()) {
attach_qks = true;
}
if (options[launcher::Options::LoadMFGModule].value_bool()) {
attach_mfg = true;
}
if (options[launcher::Options::LoadPCModule].value_bool()) {
attach_pc = true;
}
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
attach_museca = true;
}
if (options[launcher::Options::DDR43Mode].value_bool()) {
games::ddr::SDMODE = true;
}
if (options[launcher::Options::DDRSkipCodecRegisteration].value_bool()) {
games::ddr::NO_CODEC_REGISTRATION = true;
}
if (options[launcher::Options::LoadSteelChronicleModule].value_bool()) {
attach_sc = true;
}
if (options[launcher::Options::DisableNetworkFixes].value_bool()) {
netfix_disable = true;
}
if (options[launcher::Options::EnableICMPHook].value_bool()) {
icmphook_enable = true;
}
if (options[launcher::Options::DisableACPHook].value_bool()) {
lang_disable = true;
}
if (options[launcher::Options::DisableSignalHandling].value_bool()) {
launcher::signal::DISABLE = true;
}
if (options[launcher::Options::DebugCreateFile].value_bool()) {
DEVICE_CREATEFILE_DEBUG = true;
}
if (options[launcher::Options::BlockingLogger].value_bool()) {
logger::BLOCKING = true;
}
if (options[launcher::Options::OutputPEB].value_bool()) {
peb_print = true;
}
if (options[launcher::Options::DumpSystemInfo].is_active()) {
if (options[launcher::Options::DumpSystemInfo].value_text() == "all") {
rawinput::DUMP_HID_DEVICES_TO_LOG = true;
} else if (options[launcher::Options::DumpSystemInfo].value_text() == "basic") {
dump_sysinfo = true;
} else {
dump_sysinfo = false;
}
}
if (options[launcher::Options::EnableBemaniTools5API].value_bool()) {
BT5API_ENABLED = true;
}
if (options[launcher::Options::CardIOHIDReaderSupport].value_bool()) {
cardio_enabled = true;
}
if (options[launcher::Options::SDVXPrinterEmulation].value_bool()) {
attach_cpusbxpkm_printer = true;
}
// printer overwrite
if (options[launcher::Options::SDVXPrinterOutputOverwrite].value_bool() ||
options[launcher::Options::LovePlusPrinterOutputOverwrite].value_bool()) {
games::shared::PRINTER_OVERWRITE_FILE = true;
}
// printer output path
for (auto &path : options[launcher::Options::SDVXPrinterOutputPath].values_text()) {
games::shared::PRINTER_PATH.push_back(path);
}
for (auto &path : options[launcher::Options::LovePlusPrinterOutputPath].values_text()) {
games::shared::PRINTER_PATH.push_back(path);
}
// printer file format
for (auto &path : options[launcher::Options::SDVXPrinterOutputFormat].values_text()) {
games::shared::PRINTER_FORMAT.push_back(path);
}
for (auto &path : options[launcher::Options::LovePlusPrinterOutputFormat].values_text()) {
games::shared::PRINTER_FORMAT.push_back(path);
}
// printer JPG quality
if (options[launcher::Options::SDVXPrinterJPGQuality].is_active()) {
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::SDVXPrinterJPGQuality].value_uint32();
} else if (options[launcher::Options::LovePlusPrinterJPGQuality].is_active()) {
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::LovePlusPrinterJPGQuality].value_uint32();
}
// printer output clear
if (options[launcher::Options::SDVXPrinterOutputClear].value_bool() ||
options[launcher::Options::LovePlusPrinterOutputClear].value_bool()) {
games::shared::PRINTER_CLEAR = true;
}
if (options[launcher::Options::HTTP11].is_active()) {
avs::ea3::HTTP11 = options[launcher::Options::HTTP11].value_uint32();
}
if (options[launcher::Options::DisableSSL].value_bool()) {
ssl_disable = true;
}
if (options[launcher::Options::URLSlash].is_active()) {
avs::ea3::URL_SLASH = options[launcher::Options::URLSlash].value_uint32();
}
if (options[launcher::Options::spice2x_ProcessPriority].is_active()) {
process_priority_str = options[launcher::Options::spice2x_ProcessPriority].value_text();
} else if (options[launcher::Options::RealtimeProcessPriority].value_bool()) {
process_priority_str = "realtime";
}
if (options[launcher::Options::HeapSize].is_active()) {
user_heap_size = options[launcher::Options::HeapSize].value_uint32();
}
if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) {
hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true;
}
if (options[launcher::Options::DisableAvsCache].is_active()) {
hooks::avs::config::DISABLE_CACHE = true;
}
if (options[launcher::Options::ScreenResizeConfigPath].is_active()) {
cfg::SCREEN_RESIZE_CFG_PATH_OVERRIDE =
options[launcher::Options::ScreenResizeConfigPath].value_text();
}
if (options[launcher::Options::PatchManagerConfigPath].is_active()) {
overlay::windows::PATCH_MANAGER_CFG_PATH_OVERRIDE =
options[launcher::Options::PatchManagerConfigPath].value_text();
}
if (options[launcher::Options::PathToAppConfig].is_active()) {
avs::ea3::APP_PATH = options[launcher::Options::PathToAppConfig].value_text();
}
if (options[launcher::Options::PathToAvsConfig].is_active()) {
avs::core::CFG_PATH = options[launcher::Options::PathToAvsConfig].value_text();
}
if (options[launcher::Options::PathToEa3Config].is_active()) {
avs::ea3::CFG_PATH = options[launcher::Options::PathToEa3Config].value_text();
}
if (options[launcher::Options::PathToBootstrap].is_active()) {
avs::ea3::BOOTSTRAP_PATH = options[launcher::Options::PathToBootstrap].value_text();
}
if (options[launcher::Options::PathToLog].is_active()) {
avs::core::LOG_PATH = options[launcher::Options::PathToLog].value_text();
}
if (options[launcher::Options::PCBID].is_active()) {
avs::ea3::PCBID_CUSTOM = options[launcher::Options::PCBID].value_text();
}
if (options[launcher::Options::SOFTID].is_active()) {
avs::ea3::SOFTID_CUSTOM = options[launcher::Options::SOFTID].value_text();
}
if (options[launcher::Options::ServiceURL].is_active()) {
avs::ea3::URL_CUSTOM = options[launcher::Options::ServiceURL].value_text();
}
if (options[launcher::Options::PathToModules].is_active()) {
std::error_code err;
auto &path = options[launcher::Options::PathToModules].value_text();
auto module_path = std::filesystem::absolute(path, err);
if (err) {
if (cfg::CONFIGURATOR_STANDALONE) {
log_warning("launcher", "failed to resolve module path '{}': {}", path, err.message());
} else {
log_fatal("launcher", "failed to resolve module path '{}': {}", path, err.message());
}
}
MODULE_PATH = std::move(module_path);
SetDllDirectoryW(MODULE_PATH.c_str());
}
if (options[launcher::Options::Player1Card].is_active()) {
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
CARD_OVERRIDES[0] = options[launcher::Options::Player1Card].value_text();
}
if (options[launcher::Options::Player2Card].is_active()) {
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
CARD_OVERRIDES[1] = options[launcher::Options::Player2Card].value_text();
}
if (options[launcher::Options::Player1PinMacro].is_active()) {
PIN_MACRO_ENABLED = true;
PIN_MACRO_VALUES[0] = options[launcher::Options::Player1PinMacro].value_text();
}
if (options[launcher::Options::Player2PinMacro].is_active()) {
PIN_MACRO_ENABLED = true;
PIN_MACRO_VALUES[1] = options[launcher::Options::Player2PinMacro].value_text();
}
if (options[launcher::Options::AutoPinMacroTrigger0].is_active()) {
AUTO_PIN_MACRO_TRIGGER[0] = options[launcher::Options::AutoPinMacroTrigger0].value_text();
}
if (options[launcher::Options::AutoPinMacroTrigger1].is_active()) {
AUTO_PIN_MACRO_TRIGGER[1] = options[launcher::Options::AutoPinMacroTrigger1].value_text();
}
for (auto &reader : options[launcher::Options::ICCAReaderPort].values_text()) {
static int reader_id = 0;
if (reader_id < 2) {
start_reader_thread(reader, reader_id++);
} else {
if (!cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "too many readers specified (maximum is 2)");
}
break;
}
}
for (auto &sextet : options[launcher::Options::SextetStreamPort].values_text()) {
sextet_devices.emplace_back(sextet);
}
#ifndef NO_SCARD
if (options[launcher::Options::HIDSmartCard].value_bool()) {
WINSCARD_CONFIG.cardinfo_callback = eamuse_scard_callback;
scard_threadstart();
}
if (options[launcher::Options::HIDSmartCardOrderFlip].value_bool()) {
WINSCARD_CONFIG.flip_order = true;
}
if (options[launcher::Options::HIDSmartCardOrderToggle].value_bool()) {
WINSCARD_CONFIG.toggle_order = true;
}
if (options[launcher::Options::HIDSmartCardIdConvert].is_active()) {
const auto text = options[launcher::Options::HIDSmartCardIdConvert].value_text();
if (text == "fix") {
WINSCARD_CONFIG.add_padding_to_old_cards = true;
} else if (text == "all") {
WINSCARD_CONFIG.add_padding_to_old_cards = true;
WINSCARD_CONFIG.add_padding_to_felica = true;
}
}
#endif
if (options[launcher::Options::CardIOHIDReaderOrderFlip].value_bool()) {
CARDIO_RUNNER_FLIP = true;
}
if (options[launcher::Options::CardIOHIDReaderOrderToggle].value_bool()) {
CARDIO_RUNNER_TOGGLE = true;
}
if (options[launcher::Options::ICCAReaderPortToggle].is_active()) {
start_reader_thread(options[launcher::Options::ICCAReaderPortToggle].value_text(), -1);
}
if (options[launcher::Options::IntelSDEFolder].is_active()) {
sde_init(options[launcher::Options::IntelSDEFolder].value_text());
}
if (options[launcher::Options::AdapterNetwork].is_active()) {
NETWORK_ADDRESS = options[launcher::Options::AdapterNetwork].value_text();
}
if (options[launcher::Options::AdapterSubnet].is_active()) {
NETWORK_SUBNET = options[launcher::Options::AdapterSubnet].value_text();
}
if (options[launcher::Options::APITCPPort].is_active()) {
api_enable = true;
api_port = options[launcher::Options::APITCPPort].value_uint32();
}
if (options[launcher::Options::APIPassword].is_active()) {
api_pass = options[launcher::Options::APIPassword].value_text();
}
if (options[launcher::Options::APISerialPort].is_active()) {
api_serial_port.push_back(options[launcher::Options::APISerialPort].value_text());
}
if (options[launcher::Options::APISerialBaud].is_active()) {
api_serial_baud.push_back(options[launcher::Options::APISerialBaud].value_uint32());
}
if (options[launcher::Options::APIPretty].value_bool()) {
api_pretty = true;
}
if (options[launcher::Options::APIVerboseLogging].value_bool()) {
api::LOGGING = true;
}
if (options[launcher::Options::APIDebugMode].value_bool()) {
api_debug = true;
}
if (options[launcher::Options::APIScreenMirrorQuality].is_active()) {
api::modules::CAPTURE_QUALITY = options[launcher::Options::APIScreenMirrorQuality].value_uint32();
}
if (options[launcher::Options::APIScreenMirrorDivide].is_active()) {
api::modules::CAPTURE_DIVIDE = options[launcher::Options::APIScreenMirrorDivide].value_uint32();
}
if (options[launcher::Options::DisableDebugHooks].value_bool()) {
debughook::DEBUGHOOK_LOGGING = false;
}
if (options[launcher::Options::ForceWinTouch].value_bool()) {
rawinput::touch::DISABLED = true;
}
if (options[launcher::Options::SDVXForce720p].value_bool()) {
GRAPHICS_SDVX_FORCE_720 = true;
}
if (options[launcher::Options::InvertTouchCoordinates].value_bool()) {
rawinput::touch::INVERTED = true;
}
// DisableTouchCardInsert is no longer honored in spice2x
// if (options[launcher::Options::DisableTouchCardInsert].value_bool()) {
// SPICETOUCH_CARD_DISABLE = true;
// }
if (options[launcher::Options::spice2x_TouchCardInsert].value_bool()) {
SPICETOUCH_CARD_DISABLE = false;
}
if (options[launcher::Options::ForceTouchEmulation].value_bool()) {
wintouchemu::FORCE = true;
}
if (options[launcher::Options::Graphics9On12].value_bool()) {
GRAPHICS_9_ON_12_STATE = DX9ON12_FORCE_ON;
}
if (options[launcher::Options::spice2x_Dx9On12].is_active()) {
auto &name = options[launcher::Options::spice2x_Dx9On12].value_text();
if (name == "0") {
GRAPHICS_9_ON_12_STATE = DX9ON12_FORCE_OFF;
} else if (name == "1") {
GRAPHICS_9_ON_12_STATE = DX9ON12_FORCE_ON;
}
// do not explicitly set GRAPHICS_9_ON_12_STATE to default here; must respect
// legacy Graphics9On12 option from above if set
}
if (options[launcher::Options::NoLegacy].value_bool() && !cfg_run) {
rawinput::NOLEGACY = true;
}
if (options[launcher::Options::RichPresence].value_bool()) {
rich_presence = true;
}
if (options[launcher::Options::DiscordAppID].is_active()) {
richpresence::discord::APPID_OVERRIDE = options[launcher::Options::DiscordAppID].value_text();
}
if (options[launcher::Options::ScreenshotFolder].is_active()) {
GRAPHICS_SCREENSHOT_DIR = options[launcher::Options::ScreenshotFolder].value_text();
}
if (options[launcher::Options::DisableColoredOutput].value_bool()) {
logger::COLOR = false;
}
if (options[launcher::Options::DisableOverlay].value_bool()) {
overlay::ENABLED = false;
}
if (options[launcher::Options::OverlayScaling].is_active() && !cfg::CONFIGURATOR_STANDALONE && !cfg_run) {
const auto val = options[launcher::Options::OverlayScaling].value_uint32();
if (10 <= val && val <= 400 && val != 100) {
overlay::UI_SCALE_PERCENT = options[launcher::Options::OverlayScaling].value_uint32();
}
}
if (options[launcher::Options::DisableAudioHooks].value_bool()) {
hooks::audio::ENABLED = false;
deferredlogs::defer_error_messages({
"audio hooks are forcibly disabled by user (-audiohookdisable)",
" having hooks disabled means some required audio fixes are not being applied",
" if you have audio-related crashes, you should try again after clearing this option"
});
}
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
hooks::audio::VOLUME_HOOK_ENABLED = false;
}
if (options[launcher::Options::AudioBackend].is_active()) {
auto &name = options[launcher::Options::AudioBackend].value_text();
auto backend = hooks::audio::name_to_backend(name.c_str());
if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "invalid audio backend: {}", name);
}
hooks::audio::BACKEND = backend;
}
if (options[launcher::Options::AsioDriverId].is_active()) {
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32();
}
if (options[launcher::Options::AsioDriverName].is_active()) {
hooks::audio::BACKEND = hooks::audio::name_to_backend("asio");
hooks::audio::ASIO_DRIVER_NAME = options[launcher::Options::AsioDriverName].value_text();
// this new option overrides the old one
hooks::audio::ASIO_DRIVER_ID.reset();
}
if (options[launcher::Options::AudioDummy].value_bool()) {
hooks::audio::USE_DUMMY = true;
}
if (options[launcher::Options::EAAutomap].value_bool()) {
easrv_maint = false;
automap = true;
avs::automap::ENABLED = true;
avs::automap::PATCH = true;
avs::automap::RESTRICT_NETWORK = true;
avs::automap::DUMP = true;
}
if (options[launcher::Options::EANetdump].value_bool()) {
easrv_maint = false;
automap = true;
avs::automap::ENABLED = true;
avs::automap::PATCH = false;
avs::automap::RESTRICT_NETWORK = true;
avs::automap::DUMP = true;
}
if (options[launcher::Options::GameExecutable].is_active()) {
avs::game::DLL_NAME = options[launcher::Options::GameExecutable].value_text();
}
if (options[launcher::Options::spice2x_LightsOverallBrightness].is_active()) {
rawinput::HID_LIGHT_BRIGHTNESS =
(uint8_t)options[launcher::Options::spice2x_LightsOverallBrightness].value_uint32();
}
if (options[launcher::Options::spice2x_FpsAutoShow].value_bool()) {
overlay::AUTO_SHOW_FPS = true;
}
if (options[launcher::Options::spice2x_FpsOpposite].value_bool()) {
overlay::FPS_SHOULD_FLIP = true;
}
if (options[launcher::Options::spice2x_SubScreenAutoShow].value_bool()) {
overlay::AUTO_SHOW_SUBSCREEN = true;
}
if (options[launcher::Options::spice2x_IOPanelAutoShow].value_bool()) {
overlay::AUTO_SHOW_IOPANEL = true;
}
if (options[launcher::Options::spice2x_KeypadAutoShow].is_active()) {
auto s = options[launcher::Options::spice2x_KeypadAutoShow].value_uint32();
switch (s) {
case 1:
overlay::AUTO_SHOW_KEYPAD_P1 = true;
break;
case 2:
overlay::AUTO_SHOW_KEYPAD_P2 = true;
break;
case 3:
overlay::AUTO_SHOW_KEYPAD_P1 = true;
overlay::AUTO_SHOW_KEYPAD_P2 = true;
break;
}
}
if (options[launcher::Options::spice2x_WindowBorder].is_active()) {
GRAPHICS_WINDOW_STYLE = options[launcher::Options::spice2x_WindowBorder].value_uint32();
}
if (options[launcher::Options::spice2x_WindowSize].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
GRAPHICS_WINDOW_SIZE = result;
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "failed to parse -windowsize");
}
}
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
GRAPHICS_WINDOW_POS = options[launcher::Options::spice2x_WindowPosition].value_text();
}
GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool();
GRAPHICS_WINDOW_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].value_bool();
GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS = options[launcher::Options::WindowDisableRoundedCorners].value_bool();
if (options[launcher::Options::GitaDoraWindowedMainMonitor].is_active()) {
GRAPHICS_GITADORA_MAIN_MONITOR =
options[launcher::Options::GitaDoraWindowedMainMonitor].value_text();
}
if (options[launcher::Options::GitaDoraWindowedLeftMonitor].is_active()) {
GRAPHICS_GITADORA_LEFT_MONITOR =
options[launcher::Options::GitaDoraWindowedLeftMonitor].value_text();
}
if (options[launcher::Options::GitaDoraWindowedRightMonitor].is_active()) {
GRAPHICS_GITADORA_RIGHT_MONITOR =
options[launcher::Options::GitaDoraWindowedRightMonitor].value_text();
}
if (options[launcher::Options::GitaDoraWindowedSmallMonitor].is_active()) {
GRAPHICS_GITADORA_SMALL_MONITOR =
options[launcher::Options::GitaDoraWindowedSmallMonitor].value_text();
}
// IIDX/SDVX Windowed Subscreen
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) {
GRAPHICS_WSUB_SIZE = options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].value_text();
} else if (options[launcher::Options::SDVXWindowedSubscreenSize].is_active()) {
GRAPHICS_WSUB_SIZE = options[launcher::Options::SDVXWindowedSubscreenSize].value_text();
}
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].is_active()) {
GRAPHICS_WSUB_POS = options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].value_text();
} else if (options[launcher::Options::SDVXWindowedSubscreenPosition].is_active()) {
GRAPHICS_WSUB_POS = options[launcher::Options::SDVXWindowedSubscreenPosition].value_text();
}
if (options[launcher::Options::IIDXWindowedSubscreenBorderless].value_bool() ||
options[launcher::Options::SDVXWindowedSubscreenBorderless].value_bool()) {
GRAPHICS_WSUB_BORDERLESS = true;
}
if (options[launcher::Options::IIDXWindowedSubscreenAlwaysOnTop].value_bool() ||
options[launcher::Options::SDVXWindowedSubscreenAlwaysOnTop].value_bool()) {
GRAPHICS_WSUB_ALWAYS_ON_TOP = true;
}
if (options[launcher::Options::IIDXSubMonitorOverride].is_active()) {
sysutils::SECOND_MONITOR_OVERRIDE = options[launcher::Options::IIDXSubMonitorOverride].value_text();
}
if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) {
games::jb::TOUCH_ALGORITHM = games::jb::JubeatTouchAlgorithm::Legacy;
}
if (options[launcher::Options::JubeatTouchAlgo].is_active()) {
if (options[launcher::Options::JubeatTouchAlgo].value_text() == "accurate") {
games::jb::TOUCH_ALGORITHM = games::jb::JubeatTouchAlgorithm::AcAccurate;
} else if (options[launcher::Options::JubeatTouchAlgo].value_text() == "legacy") {
games::jb::TOUCH_ALGORITHM = games::jb::JubeatTouchAlgorithm::Legacy;
} else {
games::jb::TOUCH_ALGORITHM = games::jb::JubeatTouchAlgorithm::Improved;
}
}
// reflec beat touch emulation
if (options[launcher::Options::spice2x_RBTouchScale].is_active()) {
games::rb::TOUCH_SCALING = options[launcher::Options::spice2x_RBTouchScale].value_uint32();
}
if (options[launcher::Options::RBTouchSize].is_active()) {
const auto text = options[launcher::Options::RBTouchSize].value_text();
if (text == "3") {
games::rb::TOUCH_SIZE = 3;
}
}
if (options[launcher::Options::RBTouchPollRate].is_active()) {
games::rb::TOUCH_POLL_RATE = options[launcher::Options::RBTouchPollRate].value_uint32();
}
if (options[launcher::Options::spice2x_AsioForceUnload].value_bool()) {
hooks::audio::ASIO_FORCE_UNLOAD_ON_STOP = true;
}
if (options[launcher::Options::spice2x_DRSDisableTouch].value_bool()) {
games::drs::DISABLE_TOUCH = true;
}
if (options[launcher::Options::spice2x_DRSTransposeTouch].value_bool()) {
games::drs::TRANSPOSE_TOUCH = true;
}
if (options[launcher::Options::DRSRGBCameraHook].value_bool()) {
games::drs::RGB_CAMERA_HOOK = true;
}
if (options[launcher::Options::spice2x_AutoCard].is_active()) {
const auto text = options[launcher::Options::spice2x_AutoCard].value_text();
if (text == "p1") {
AUTO_INSERT_CARD[0] = true;
} else if (text == "p2") {
AUTO_INSERT_CARD[1] = true;
} else if (text == "both") {
AUTO_INSERT_CARD[0] = true;
AUTO_INSERT_CARD[1] = true;
}
}
if (options[launcher::Options::spice2x_LowLatencySharedAudio].value_bool()) {
hooks::audio::LOW_LATENCY_SHARED_WASAPI = true;
}
if (options[launcher::Options::spice2x_TapeLedAlgorithm].is_active()) {
const auto text = options[launcher::Options::spice2x_TapeLedAlgorithm].value_text();
if (text == "off") {
tapeledutils::TAPE_LED_ALGORITHM = tapeledutils::TAPE_LED_USE_NONE;
} else if (text == "avg") {
tapeledutils::TAPE_LED_ALGORITHM = tapeledutils::TAPE_LED_USE_AVERAGE;
} else if (text == "first") {
tapeledutils::TAPE_LED_ALGORITHM = tapeledutils::TAPE_LED_USE_FIRST;
} else if (text == "last") {
tapeledutils::TAPE_LED_ALGORITHM = tapeledutils::TAPE_LED_USE_LAST;
}
}
if (options[launcher::Options::CCJTrackballSensitivity].is_active()) {
games::ccj::TRACKBALL_SENSITIVITY = (uint8_t)
options[launcher::Options::CCJTrackballSensitivity].value_uint32();
}
if (options[launcher::Options::CCJMouseTrackball].value_bool()) {
games::ccj::MOUSE_TRACKBALL = true;
}
if (options[launcher::Options::CCJMouseTrackballWithToggle].value_bool()) {
games::ccj::MOUSE_TRACKBALL_USE_TOGGLE = true;
}
if (options[launcher::Options::CCJArgs].is_active()) {
games::ccj::CCJ_INJECT_ARGS = options[launcher::Options::CCJArgs].value_text();
}
if (options[launcher::Options::QKSArgs].is_active()) {
games::qks::QKS_INJECT_ARGS = options[launcher::Options::QKSArgs].value_text();
}
if (options[launcher::Options::MFGArgs].is_active()) {
games::mfg::MFG_INJECT_ARGS = options[launcher::Options::MFGArgs].value_text();
}
if (options[launcher::Options::MFGCabType].is_active()) {
games::mfg::MFG_CABINET_TYPE = options[launcher::Options::MFGCabType].value_text();
}
if (options[launcher::Options::MFGNoIO].is_active()) {
games::mfg::MFG_NO_IO = options[launcher::Options::MFGNoIO].value_bool();
}
if (options[launcher::Options::PCArgs].is_active()) {
games::pc::PC_INJECT_ARGS = options[launcher::Options::PCArgs].value_text();
}
if (options[launcher::Options::PCNoIO].is_active()) {
games::pc::PC_NO_IO = options[launcher::Options::PCNoIO].value_bool();
}
if (options[launcher::Options::PCKnobMode].value_bool()) {
games::pc::PC_KNOB_MODE = true;
}
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
rawinput::ENABLE_SMX_STAGE = true;
}
if (options[launcher::Options::spice2x_EnableSMXDedicab].value_bool()) {
rawinput::ENABLE_SMX_DEDICAB = true;
}
if (options[launcher::Options::InputRequiresFocus].is_active()) {
const auto &text = options[launcher::Options::InputRequiresFocus].value_text();
if (text == "always") {
rawinput::NAIVE_REQUIRE_FOCUS = true;
rawinput::RAWINPUT_REQUIRE_FOCUS = true;
} else if (text == "never") {
rawinput::NAIVE_REQUIRE_FOCUS = false;
rawinput::RAWINPUT_REQUIRE_FOCUS = false;
}
}
if (options[launcher::Options::DDRP4IOBufferMode].is_active()) {
if (options[launcher::Options::DDRP4IOBufferMode].value_text() == "thread") {
acio::MDXF_BUFFER_FILL_MODE = acio::MDXFBufferFillMode::THREAD_MODE;
} else if (options[launcher::Options::DDRP4IOBufferMode].value_text() == "backfill") {
acio::MDXF_BUFFER_FILL_MODE = acio::MDXFBufferFillMode::BACKFILL_MODE;
}
}
if (options[launcher::Options::MidiAlgoVer].is_active()) {
if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") {
midi_algo = rawinput::MidiNoteAlgorithm::LEGACY;
} else if (options[launcher::Options::MidiAlgoVer].value_text() == "v2") {
midi_algo = rawinput::MidiNoteAlgorithm::V2;
} else if (options[launcher::Options::MidiAlgoVer].value_text() == "v2_drum") {
midi_algo = rawinput::MidiNoteAlgorithm::V2_DRUM;
}
// else - automatic (no value)
}
if (cfg::CONFIGURATOR_STANDALONE) {
// three reasons for this hardcoded value
// 1) poll rate is tied to framerate in spicecfg, and the software ImGui renderer has
// inconsistent framerate, often failing to meet 60fps
// 2) the default is 20ms which is 1 frame, which is too quick for the user to see
// 3) to help with binding process
rawinput::MIDI_NOTE_SUSTAIN = 100;
} else if (options[launcher::Options::MidiNoteSustain].is_active()) {
rawinput::MIDI_NOTE_SUSTAIN = options[launcher::Options::MidiNoteSustain].value_uint32();
}
if (options[launcher::Options::LovePlusCamEnable].value_bool()) {
games::loveplus::CAMERA_ENABLE = true;
}
if (options[launcher::Options::DisableHighResTimer].value_bool()) {
timeutils::TIMER_HACKS_DISABLE = true;
}
if (options[launcher::Options::CfgForceSoftwareRender].value_bool()) {
cfg::CONFIGURATOR_FORCE_SOFTWARE_RENDER = true;
}
// API debugging
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
for (size_t i = 0; i < std::min(api_serial_port.size(), api_serial_baud.size()); i++) {
API_CONTROLLER->listen_serial(api_serial_port[i], api_serial_baud[i]);
}
if (cfg_run) {
exit(spicecfg_run(sextet_devices));
} else {
while (API_CONTROLLER->server_running) {
Sleep(100);
}
}
log_fatal("launcher", "API server stopped");
}
// delay
if (!cfg::CONFIGURATOR_STANDALONE) {
DWORD delayInSeconds = 0;
if (options[launcher::Options::spice2x_DelayByNSeconds].is_active()) {
delayInSeconds = (DWORD)options[launcher::Options::spice2x_DelayByNSeconds].value_uint32();
} else if (options[launcher::Options::DelayBy5Seconds].value_bool()) {
delayInSeconds = 5;
}
if (0 < delayInSeconds) {
log_info("launcher", "delay by {}ms...", delayInSeconds * 1000);
Sleep(delayInSeconds * 1000);
}
}
// create log file
// configurator does not write a log file because it tends to cause the
// config file to be corrupt...
if (!cfg::CONFIGURATOR_STANDALONE) {
avs::core::create_log();
}
// log
#if SPICE_LINUX
#ifdef SPICE64
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x) for Linux");
#else
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x) for Linux");
#endif
#elif SPICE_XP
#ifdef SPICE64
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x) for WinXP");
#else
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x) for WinXP");
#endif
#else
#ifdef SPICE64
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
#elif SPICE32_LARGE_ADDRESS_AWARE
log_info("launcher", "SpiceTools Bootstrap (x32 - Large Address Aware) (spice2x)");
#else
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
#endif
#endif
log_info("launcher", "{}", VERSION_STRING);
// note: distribution of modified version of this software without providing source is GPLv3 license violation.
log_info("launcher", "spice2x is free & open source; if you paid money for it, you got scammed");
log_info("launcher", "visit https://spice2x.github.io to download the latest version for free");
// log command line arguments
std::ostringstream arguments;
for (auto &root_option : options) {
std::vector<Option> options_all { root_option };
options_all.insert(options_all.end(),
root_option.alternatives.begin(),
root_option.alternatives.end());
for (const auto &option : options_all) {
if (option.is_active()) {
auto &definition = option.get_definition();
arguments << " ";
arguments << "-";
if (definition.display_name.empty()) {
arguments << definition.name;
} else {
arguments << definition.display_name;
}
if (definition.type != OptionType::Bool) {
arguments << " ";
if (definition.sensitive) {
arguments << std::string(5, '*');
} else {
arguments << option.value;
}
}
arguments << "\n";
}
}
}
log_info("launcher", "arguments:\n{}", arguments.str());
// print out conflicts
size_t conflicts = 0;
for (const auto &option : options) {
if (option.conflicting && option.get_definition().type != OptionType::Bool) {
conflicts += 1;
const auto& value = option.get_definition().sensitive ? "*****" : option.value;
if (launcher::USE_CMD_OVERRIDE) {
log_warning(
"launcher",
"multiple values for -{}, command line args take precedence: {}",
option.get_definition().name,
value);
} else {
log_warning(
"launcher",
"multiple values for -{}, spicecfg values take precedence: {}",
option.get_definition().name,
value);
}
}
}
if (conflicts) {
if (launcher::USE_CMD_OVERRIDE) {
log_info(
"launcher",
"user specified -cmdoverride, therefore command line args took precedence over spicecfg");
} else {
log_warning(
"launcher",
"spicecfg values take precedence over command line args; to change this behavior, use -cmdoverride");
}
}
if (options[launcher::Options::GameExecutable].is_active() && !cfg::CONFIGURATOR_STANDALONE) {
log_warning(
"launcher",
"WARNING - user specified -exec option\n\n\n"
"!!! !!!\n"
"!!! Using -exec option disables all game-specific hooks! !!!\n"
"!!! Unless you know exactly what you are doing, clear -exec !!!\n"
"!!! and try again. Using -exec by itself will result in weird !!!\n"
"!!! errors and loss of functionality. !!!\n"
"!!! !!!\n"
);
}
if (launcher::signal::DISABLE && !cfg::CONFIGURATOR_STANDALONE) {
log_warning(
"launcher",
"WARNING - user specified -signaldisable option\n\n\n"
"!!! !!!\n"
"!!! Using -signaldisable option disables all exception handling, !!!\n"
"!!! which means when the game crashes it will likely just vanish !!!\n"
"!!! without troubleshooting information in the logs. !!!\n"
"!!! !!!\n"
);
}
if (options[launcher::Options::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "failed to parse -forceres");
}
}
if (options[launcher::Options::FullscreenSubResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenSubResolution].value_text(), result)) {
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB = result;
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "failed to parse -forceressub");
}
}
// SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
// FullscreenOrientationFlip continues to live on, however.
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!");
}
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
// #if SPICE64
// GRAPHICS_FS_ORIENTATION_SWAP = true;
// #else
// log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
// #endif
// }
if (options[launcher::Options::FullscreenOrientationFlip].value_bool() && !GRAPHICS_WINDOWED) {
GRAPHICS_FS_ORIENTATION_SWAP = true;
}
if (games::iidx::DISABLE_CAMS.has_value() &&
games::iidx::DISABLE_CAMS.value() &&
!cfg::CONFIGURATOR_STANDALONE) {
log_misc("launcher", "CONNECT_CAMERA env var set to 0");
SetEnvironmentVariable("CONNECT_CAMERA", "0");
}
// deleted options
if (options[launcher::Options::OpenKFControl].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version.");
}
if (options[launcher::Options::VREnable].value_bool()) {
log_warning("launcher", "-vr has been removed from spice2x; ignoring");
}
if (options[launcher::Options::ExecuteScript].is_active()) {
log_warning("launcher", "-script has been removed from spice2x; ignoring");
}
// disable automatic system/monitor sleep
if (!SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)) {
log_warning("launcher", "could not set thread execution state: {}", GetLastError());
}
if (!cfg::CONFIGURATOR_STANDALONE) {
// set process priority
cpuutils::set_processor_priority(process_priority_str);
// set process affinity
if (options[launcher::Options::spice2x_ProcessAffinity].is_active()) {
uint64_t affinity = options[launcher::Options::spice2x_ProcessAffinity].value_hex64();
cpuutils::set_processor_affinity(affinity, true);
// efficiency class (but only if no affinity is set)
} else if (options[launcher::Options::spice2x_ProcessorEfficiencyClass].is_active()) {
const auto eff_class_str =
options[launcher::Options::spice2x_ProcessorEfficiencyClass].value_text();
if (eff_class_str == "ecores") {
cpuutils::set_processor_affinity(cpuutils::CpuEfficiencyClass::PreferECores);
} else if (eff_class_str == "pcores") {
cpuutils::set_processor_affinity(cpuutils::CpuEfficiencyClass::PreferPCores);
}
}
// initialize nvapi if available
nvapi::initialize();
// add application profile to nvcp
nvapi::set_profile_settings();
// enable super exit
superexit::enable();
// enable subscreen touch emulation
if (options[launcher::Options::spice2x_IIDXEmulateSubscreenKeypadTouch].is_active()) {
games::iidx::poke::enable();
}
if (options[launcher::Options::NostalgiaPoke].is_active()) {
games::nost::ENABLE_POKE = TRUE;
}
}
// early hooks
if (!cfg_run) {
for (auto &hook : early_hooks) {
log_info("launcher", "loading early hook DLL {}", hook);
libutils::print_dll_info(hook);
HMODULE module;
if (!(module = libutils::try_library(hook))) {
log_warning("launcher", "failed to load early hook {}: {}", hook, get_last_error_string());
deferredlogs::defer_error_messages({
fmt::format("failed to load early hook {}:", hook),
fmt::format(" {}", get_last_error_string())
});
}
}
}
// auto detect game if not specified
if (avs::game::DLL_NAME.empty()) {
bool module_path_tried = false;
do {
// IIDX
if (check_dll("bm2dx.dll")) {
avs::game::DLL_NAME = "bm2dx.dll";
attach_io = true;
attach_iidx = true;
// automatically show cursor in windowed mode to interact with second window (sub)
if (GRAPHICS_WINDOWED) {
GRAPHICS_SHOW_CURSOR = true;
}
break;
}
// SDVX
if (check_dll("soundvoltex.dll")) {
avs::game::DLL_NAME = "soundvoltex.dll";
attach_io = true;
attach_sdvx = true;
#ifdef SPICE64
debughook::DEBUGHOOK_LOGGING = false;
#endif
// automatically show cursor in windowed mode to interact with second window (sub)
if (GRAPHICS_WINDOWED) {
GRAPHICS_SHOW_CURSOR = true;
}
break;
}
// JB
if (check_dll("jubeat.dll")) {
avs::game::DLL_NAME = "jubeat.dll";
attach_io = true;
attach_jb = true;
break;
}
// RB
if (check_dll("reflecbeat.dll")) {
avs::game::DLL_NAME = "reflecbeat.dll";
attach_io = true;
attach_rb = true;
break;
}
// Shogikai
if (check_dll("shogi_engine.dll")) {
avs::game::DLL_NAME = "system.dll";
attach_io = true;
attach_shogikai = true;
show_cursor_if_no_touch = true;
break;
}
// PCM & MGA
if (check_dll("launch.dll")) {
avs::game::DLL_NAME = "launch.dll";
attach_io = true;
// MGA uses ESS while PCM does not
if (check_dll("ess.dll")) {
attach_mga = true;
} else {
attach_pcm = true;
}
break;
}
// DEA
if (check_dll("arkkdm.dll")) {
avs::game::DLL_NAME = "arkkdm.dll";
attach_io = true;
attach_dea = true;
// the game is windowed by default unless we set the env
if (GRAPHICS_WINDOWED) {
GRAPHICS_WINDOWED = false;
} else {
SetEnvironmentVariable("DAMAC_VIEWER_FULLSCREEN", "0");
}
break;
}
// BS
if (check_dll("beatstream.dll")) {
avs::game::DLL_NAME = "beatstream.dll";
attach_io = true;
attach_bs = true;
// game crash fix
easrv_maint = false;
show_cursor_if_no_touch = true;
break;
}
// RF3D
if (check_dll("jgt.dll")) {
avs::game::DLL_NAME = "jgt.dll";
attach_io = true;
attach_rf3d = true;
break;
}
// MUSECA
if (check_dll("museca.dll")) {
avs::game::DLL_NAME = "museca.dll";
attach_io = true;
attach_museca = true;
break;
}
// pop'n Lapistoria/eclale/UsaNeko/peace
if (check_dll("popn22.dll")) {
avs::game::DLL_NAME = "popn22.dll";
attach_io = true;
attach_popn = true;
break;
}
// pop'n Sunny Park
if (check_dll("popn21.dll")) {
avs::game::DLL_NAME = "popn21.dll";
attach_io = true;
attach_popn = true;
break;
}
// pop'n Fantasia
if (check_dll("popn20.dll")) {
avs::game::DLL_NAME = "popn20.dll";
attach_io = true;
attach_popn = true;
break;
}
// pop'n Tune Street
if (check_dll("popn19.dll")) {
avs::game::DLL_NAME = "popn19.dll";
attach_io = true;
attach_popn = true;
break;
}
// DDR Ace/A20 (bio2)
if (check_dll("arkmdxbio2.dll")) {
avs::game::DLL_NAME = "arkmdxbio2.dll";
attach_io = true;
attach_ddr = true;
break;
}
// DDR Ace/A20 (p3io)
if (check_dll("arkmdxp3.dll")) {
avs::game::DLL_NAME = "arkmdxp3.dll";
attach_io = true;
attach_ddr = true;
break;
}
// DDR Ace/A20 (p4io)
if (check_dll("arkmdxp4.dll")) {
avs::game::DLL_NAME = "arkmdxp4.dll";
attach_io = true;
attach_ddr = true;
break;
}
// DDR 2013/2014 (old cabinets)
if (check_dll("mdxja_945.dll")) {
avs::game::DLL_NAME = "mdxja_945.dll";
attach_io = true;
attach_ddr = true;
break;
}
// DDR 2013/2014 (white cabinet)
if (check_dll("mdxja_hm65.dll")) {
avs::game::DLL_NAME = "mdxja_hm65.dll";
attach_io = true;
attach_ddr = true;
break;
}
// DDR X2/X3
if (check_dll("ddr.dll")) {
avs::game::DLL_NAME = "ddr.dll";
attach_io = true;
attach_ddr = true;
break;
}
// GitaDora
if (check_dll("gdxg.dll")) {
avs::game::DLL_NAME = "gdxg.dll";
attach_io = true;
attach_device = true;
attach_extdev = true;
attach_ami2000 = true;
attach_gitadora = true;
break;
}
// Nostalgia
if (check_dll("nostalgia.dll")) {
avs::game::DLL_NAME = "nostalgia.dll";
attach_io = true;
attach_nostalgia = true;
show_cursor_if_no_touch = true;
break;
}
// Bishi Bashi Channel
if (check_dll("bsch.dll")) {
avs::game::DLL_NAME = "bsch.dll";
attach_io = true;
attach_bbc = true;
break;
}
if (check_dll("popn.dll")) {
avs::game::DLL_NAME = "popn.dll";
attach_io = true;
if (check_dll("libaio-iob2_video.dll")) {
// pop'n music (pika cabinet)
attach_popn = true;
// automatically show cursor in windowed mode to interact with second window (sub)
if (GRAPHICS_WINDOWED) {
GRAPHICS_SHOW_CURSOR = true;
}
} else {
// HELLO! Pop'n Music
attach_hpm = true;
}
break;
}
// Quiz Magic Academy
if (check_dll("client.dll")) {
avs::game::DLL_NAME = "client.dll";
attach_io = true;
attach_qma = true;
break;
}
// LovePlus
if (check_dll("arkklp.dll")) {
avs::game::DLL_NAME = "arkklp.dll";
attach_io = true;
attach_loveplus = true;
attach_cpusbxpkm_printer = true;
break;
}
// Steel Chronicle
if (check_dll("arkkgg.dll")) {
avs::game::DLL_NAME = "arkkgg.dll";
attach_io = true;
attach_sc = true;
easrv_maint = false;
break;
}
// Mahjong Fight Club
if (check_dll("allinone.dll")) {
avs::game::DLL_NAME = "system.dll";
attach_io = true;
attach_mfc = true;
break;
// Mahjong Fight Club - 2025?
// they removed allinone.dll and moved i/o into system.dll
} else if (check_dll("system.dll") && fileutils::file_exists("data/mfc.ini")) {
avs::game::DLL_NAME = "system.dll";
attach_io = true;
attach_mfc = true;
break;
}
// FutureTomTom
if (check_dll("arkmmd.dll")) {
avs::game::DLL_NAME = "arkmmd.dll";
attach_io = true;
attach_ftt = true;
// the game is windowed by default unless we set the env
if (!GRAPHICS_WINDOWED) {
SetEnvironmentVariable("DAMAC_VIEWER_FULLSCREEN", "0");
}
break;
}
// Scotto
if (check_dll("scotto.dll")) {
avs::game::DLL_NAME = "scotto.dll";
attach_io = true;
attach_scotto = true;
break;
}
// TsumTsum
if (check_dll("arko26.dll")) {
avs::game::DLL_NAME = "arko26.dll";
attach_io = true;
break;
}
// DANCERUSH
if (check_dll("superstep.dll")) {
avs::game::DLL_NAME = "superstep.dll";
attach_io = true;
attach_drs = true;
break;
}
// Winning Eleven 2012
if (check_dll("weac12_bootstrap_release.dll")) {
avs::game::DLL_NAME = "weac12_bootstrap_release.dll";
attach_io = true;
attach_we = true;
break;
}
// Winning Eleven 2014
if (check_dll("arknck.dll")) {
avs::game::DLL_NAME = "arknck.dll";
attach_io = true;
attach_we = true;
show_cursor_if_no_touch = true;
break;
}
// Otoca D'or
if (check_dll("arkkep.dll")) {
avs::game::DLL_NAME = "arkkep.dll";
attach_io = true;
attach_otoca = true;
break;
}
// Silent Scope: Bone Eater
if (check_dll("arkndd.dll")) {
avs::game::DLL_NAME = "arkndd.dll";
attach_io = true;
attach_silentscope = true;
break;
}
// Ongaku Paradise
if (check_dll("arkjc9.dll")) {
avs::game::DLL_NAME = "arkjc9.dll";
attach_io = true;
attach_onpara = true;
break;
}
// Chase Chase Jokers
if (check_dll("kamunity.dll") && fileutils::file_exists("game/chaseproject.exe")) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_ccj = true;
show_cursor_if_no_touch = true;
break;
}
// QuizKnock STADIUM
if (check_dll("kamunity.dll") && fileutils::file_exists("game/uks.exe")) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_qks = true;
show_cursor_if_no_touch = true;
break;
}
// Mahjong Fight Girl
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/MFGClient_Data")) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_mfg = true;
launcher::signal::USE_VEH_WORKAROUND = true;
show_cursor_if_no_touch = true;
break;
}
// Polaris Chord
if (check_dll("kamunity.dll") && fileutils::file_exists("game/svm.exe")) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_pc = true;
launcher::signal::USE_VEH_WORKAROUND = true;
show_cursor_if_no_touch = true;
break;
}
// Busou Shinki: Armored Princess Battle Conductor
if (check_dll("kamunity.dll") && fileutils::file_exists("game/bsac_app.exe")) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_bc = true;
show_cursor_if_no_touch = true;
break;
}
// try module path
if (!module_path_tried) {
module_path_tried = true;
MODULE_PATH /= "modules";
SetDllDirectoryW(MODULE_PATH.c_str());
MODULE_PATH /= "";
continue;
}
// usage error
if (!cfg::CONFIGURATOR_STANDALONE
&& (!CHECK_DLL_IGNORE_ARCH)) {
log_warning(
"launcher",
"module auto detection failed!\n\n"
"This usually means one of the following:\n\n"
" 1. You put spice executables in the wrong place, or\n"
" 2. XML files in prop directory are malformed or missing, or\n"
" 3. You passed in invalid options (do not specify -exec or -modules if you don't know what you're doing)\n\n"
);
log_fatal("launcher", "module auto detection failed, see log.txt for troubleshooting");
}
break;
} while (true);
}
// set error mode to show all errors
SetErrorMode(0);
// set the AVS heap size to a default value varying by game
avs::core::set_default_heap_size(avs::game::DLL_NAME);
// load the games
std::vector<games::Game *> games;
if (attach_popn) {
games.push_back(new games::popn::POPNGame());
}
if (attach_bbc) {
avs::core::set_default_heap_size("bsch.dll");
games.push_back(new games::bbc::BBCGame());
}
if (attach_ddr) {
avs::core::set_default_heap_size("arkmdxp3.dll");
games.push_back(new games::ddr::DDRGame());
}
if (attach_iidx) {
avs::core::set_default_heap_size("bm2dx.dll");
games.push_back(new games::iidx::IIDXGame());
}
if (attach_sdvx) {
avs::core::set_default_heap_size("soundvoltex.dll");
games.push_back(new games::sdvx::SDVXGame());
}
if (attach_jb) {
avs::core::set_default_heap_size("jubeat.dll");
games.push_back(new games::jb::JBGame());
}
if (attach_nostalgia) {
avs::core::set_default_heap_size("nostalgia.dll");
games.push_back(new games::nost::NostGame());
}
if (attach_gitadora) {
games.push_back(new games::gitadora::GitaDoraGame());
}
if (attach_hpm) {
avs::core::set_default_heap_size("popn.dll");
games.push_back(new games::hpm::HPMGame());
}
if (attach_mga) {
games.push_back(new games::mga::MGAGame());
}
if (attach_sc) {
games.push_back(new games::sc::SCGame());
}
if (attach_rb) {
games.push_back(new games::rb::RBGame());
}
if (attach_shogikai) {
games.push_back(new games::shogikai::ShogikaiGame());
}
if (attach_qma) {
avs::core::set_default_heap_size("client.dll");
games.push_back(new games::qma::QMAGame());
}
if (attach_dea) {
games.push_back(new games::dea::DEAGame());
}
if (attach_mfc) {
avs::core::set_default_heap_size("system.dll");
games.push_back(new games::mfc::MFCGame());
}
if (attach_ftt) {
avs::core::set_default_heap_size("arkmmd.dll");
games.push_back(new games::ftt::FTTGame());
}
if (attach_bs) {
games.push_back(new games::bs::BSGame());
}
if (attach_loveplus) {
games.push_back(new games::loveplus::LovePlusGame());
}
if (attach_scotto) {
avs::core::set_default_heap_size("scotto.dll");
games.push_back(new games::scotto::ScottoGame());
}
if (attach_rf3d) {
games.push_back(new games::rf3d::RF3DGame());
}
if (attach_museca) {
games.push_back(new games::museca::MusecaGame());
}
if (attach_drs) {
avs::core::set_default_heap_size("superstep.dll");
games.push_back(new games::drs::DRSGame());
}
if (attach_we) {
games.push_back(new games::we::WEGame());
}
if (attach_otoca) {
games.push_back(new games::otoca::OtocaGame());
}
if (attach_silentscope) {
games.push_back(new games::silentscope::SilentScopeGame());
}
if (attach_pcm) {
games.push_back(new games::pcm::PCMGame());
}
if (attach_onpara) {
avs::core::set_default_heap_size("arkjc9.dll");
games.push_back(new games::onpara::OnparaGame());
}
if (attach_bc) {
avs::core::set_default_heap_size("kamunity.dll");
games.push_back(new games::bc::BCGame());
}
if (attach_ccj) {
avs::core::set_default_heap_size("kamunity.dll");
games.push_back(new games::ccj::CCJGame());
}
if (attach_qks) {
avs::core::set_default_heap_size("kamunity.dll");
games.push_back(new games::qks::QKSGame());
}
if (attach_mfg) {
avs::core::set_default_heap_size("kamunity.dll");
games.push_back(new games::mfg::MFGGame());
}
if (attach_pc) {
avs::core::HEAP_SIZE = 536870912; // 512MB
games.push_back(new games::pc::PCGame());
}
// apply user heap size, if defined
if (user_heap_size > 0) {
avs::core::HEAP_SIZE = user_heap_size;
}
// call pre-attach
for (auto game : games) {
game->pre_attach();
}
// run configuration utility
if (cfg_run || cfg::CONFIGURATOR_STANDALONE) {
// cardio thread for cfg
if (cardio_enabled) {
cardio_runner_start(true);
}
if (!midi_algo.has_value()) {
midi_algo = rawinput::MidiNoteAlgorithm::V2;
}
rawinput::set_midi_algorithm(midi_algo.value());
if (api_enable || api_debug) {
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
for (size_t i = 0; i < std::min(api_serial_port.size(), api_serial_baud.size()); i++) {
API_CONTROLLER->listen_serial(api_serial_port[i], api_serial_baud[i]);
}
}
exit(spicecfg_run(sextet_devices));
}
// log some DLLs found in path (purely for troubleshooting purposes to detect
// dxvk, ForceD3D9On12, ifs_layeredfs, etc)
libutils::warn_if_dll_exists("d3d8.dll");
libutils::warn_if_dll_exists("d3d9.dll");
libutils::warn_if_dll_exists("d3d10core.dll");
libutils::warn_if_dll_exists("d3d11.dll");
libutils::warn_if_dll_exists("d3d12.dll");
libutils::warn_if_dll_exists("dxgi.dll");
libutils::warn_if_dll_exists("opengl32.dll");
libutils::warn_if_dll_exists("nvcuda.dll");
libutils::warn_if_dll_exists("nvcuvid.dll");
libutils::warn_if_dll_exists("nvEncodeAPI64.dll");
libutils::warn_if_dll_exists("msvcr100.dll");
libutils::warn_if_dll_exists("dsound.dll");
// complain loudly & early about dll load ordering issue
libutils::check_duplicate_dlls();
// print cpu features
if (!cfg::CONFIGURATOR_STANDALONE && dump_sysinfo) {
cpuutils::print_cpu_features();
sysutils::print_os();
sysutils::print_smbios();
sysutils::print_gpus();
}
// fix up monitor
if (options[launcher::Options::PrimaryMonitor].is_active()) {
change_primary_monitor(options[launcher::Options::PrimaryMonitor].value_text());
}
update_monitor_on_boot(monitor_orientation, GRAPHICS_FORCE_REFRESH, monitor_resolution);
// initialize raw input
RI_MGR = std::make_unique<rawinput::RawInputManager>();
for (const auto &device : sextet_devices) {
RI_MGR->sextet_register(device);
}
// print devices
RI_MGR->devices_print();
auto buttons = games::get_buttons(eamuse_get_game());
log_misc("rawinput", "Button mappings:");
dump_button_bindings(buttons);
log_misc("rawinput", "Keypad button mappings:");
auto keypads = games::get_buttons_keypads(eamuse_get_game());
dump_button_bindings(keypads);
log_misc("rawinput", "Overlay button mappings:");
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
dump_button_bindings(overlay_buttons);
log_misc("rawinput", "Analog mappings:");
dump_analog_bindings();
// for certain games, show cursor if no touch is available (must be called after RI_MGR is available)
if (show_cursor_if_no_touch && !is_touch_available("launcher::main_implementation")) {
GRAPHICS_SHOW_CURSOR = true;
}
// cardio
if (cardio_enabled) {
cardio_runner_start(true);
}
// load stubs
if (load_stubs) {
for (const auto &stub : STUBS) {
if (fileutils::verify_header_pe(MODULE_PATH / stub)) {
libutils::load_library(MODULE_PATH / stub);
} else {
log_warning("launcher", "failed to load stubs!");
load_stubs = false;
}
}
}
// locale hook
if (!lang_disable) {
hooks::lang::early_init();
}
// use high resolution timer for this process
timeutils::set_timer_resolution();
// load DLLs
avs::core::load_dll();
avs::ea3::load_dll();
// ICMP emulation (opt-in; before games open raw ICMP sockets)
if (icmphook_enable) {
icmphook_net_init();
}
// net fix
if (!netfix_disable) {
networkhook_init();
}
// boot AVS
avs::core::boot();
// initialize SSL
if (!ssl_disable) {
avs::ssl::init();
}
// copy defaults to nvram
avs::core::copy_defaults();
// prepare patches
{
overlay::windows::PatchManager patch_manager(nullptr, false);
}
// load game
avs::game::load_dll();
// attach games
for (auto game : games) {
game->attach();
}
#if SPICE64 && !SPICE_XP
if (!cfg::CONFIGURATOR_STANDALONE) {
if (games::iidx::TDJ_CAMERA) {
games::iidx::init_camera_hooks();
}
if (!D3D9_DEVICE_HOOK_DISABLE) {
nvenc_hook::initialize();
}
}
#endif
// attach stub functions
if (!load_stubs) {
stubs::attach();
}
// locale hook
if (!lang_disable) {
hooks::lang::init();
}
// exception/signal hook
launcher::signal::attach();
// audio hook
hooks::audio::init();
// DirectInput 8 hook
hooks::input::dinput8::init();
// D3D9 hook
graphics_init();
// debug hook
debughook::attach();
// device debug
if (DEVICE_CREATEFILE_DEBUG) {
devicehook_init();
}
// server
if (easrv_port != 0u && !easrv_smart) {
easrv_start(easrv_port, easrv_maint, 4, 8);
}
// acio attach
if (attach_io || attach_acio) {
acio::attach();
}
// acio icca attach
if (attach_icca) {
acio::attach_icca();
}
// device attach
if (attach_io || attach_device) {
spicedevice_attach();
}
// ext dev attach
if (attach_io || attach_extdev) {
extdev_attach();
}
// ami 2000 card reader attach
if (attach_io || attach_ami2000) {
ami2000_attach();
}
// sci unit attach
if (attach_io || attach_sciunit) {
sciunit_attach();
}
// SDVX printer attach
if (attach_cpusbxpkm_printer) {
games::shared::printer_attach();
}
// net fix
if (!netfix_disable) {
networkhook_init();
}
update_msvcrt_args(argc, argv);
// load hooks
for (auto &hook : game_hooks) {
log_info("launcher", "loading hook DLL {}", hook);
libutils::print_dll_info(hook);
HMODULE module;
if (!(module = libutils::try_library(hook))) {
log_warning("launcher", "failed to load hook {}: {}", hook, get_last_error_string());
deferredlogs::defer_error_messages({
fmt::format("failed to load hook {}:", hook),
fmt::format(" {}", get_last_error_string())
});
} else {
bt5api_hook(module);
sdk::register_sdk_hooks(hook, module);
}
}
// layeredfs
if (fileutils::dir_exists("data_mods") &&
GetModuleHandleA("ifs_hook.dll") == nullptr) {
log_warning("launcher", "data_mods directory found, but ifs_hook.dll is not loaded; mods will not load");
deferredlogs::defer_error_messages({
"data_mods directory was found, but ifs_hook.dll is not loaded",
" your mods will not load",
" to fix this, download ifs_layeredfs and add it as a DLL hook (-k)",
" https://github.com/mon/ifs_layeredfs"
});
}
// apply patches
{
overlay::windows::PatchManager patch_manager(nullptr, true);
}
// load AVS-EA3
avs::ea3::boot(easrv_port, easrv_maint, easrv_smart);
// eamuse init
eamuse_autodetect_game();
// notification position: apply per-game default first, then the explicit
// user option (if set) wins over the default.
overlay::notifications::apply_game_default_position(eamuse_get_game());
if (options[launcher::Options::NotificationPosition].is_active()) {
const auto txt = options[launcher::Options::NotificationPosition].value_text();
if (txt == "topleft") {
overlay::notifications::POSITION = overlay::notifications::Position::TopLeft;
} else if (txt == "topright") {
overlay::notifications::POSITION = overlay::notifications::Position::TopRight;
} else if (txt == "bottomleft") {
overlay::notifications::POSITION = overlay::notifications::Position::BottomLeft;
} else if (txt == "bottomright") {
overlay::notifications::POSITION = overlay::notifications::Position::BottomRight;
} else if (txt == "off") {
overlay::notifications::ENABLED = false;
}
}
// unis device hook
unisintrhook_init();
// BT5API
if (BT5API_ENABLED) {
bt5api_init();
}
// init SDK
sdk::init_sdk_modules();
// API
if (api_enable || std::min(api_serial_port.size(), api_serial_baud.size()) > 0) {
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
}
for (size_t i = 0; i < std::min(api_serial_port.size(), api_serial_baud.size()); i++) {
API_CONTROLLER->listen_serial(api_serial_port[i], api_serial_baud[i]);
}
// start coin input thread
eamuse_coin_start_thread();
// pin macro
if (!cfg::CONFIGURATOR_STANDALONE && PIN_MACRO_ENABLED) {
eamuse_pin_macro_start_thread();
}
// print PEB
if (peb_print) {
peb::peb_print();
}
// enable automap
if (automap) {
avs::automap::enable();
}
// initialize rich presence
if (rich_presence) {
richpresence::init();
}
// turn off controlled game lights
auto lights = games::get_lights(eamuse_get_game());
if (lights) {
for (auto &light : *lights) {
GameAPI::Lights::writeLight(RI_MGR, light, 0.f);
}
RI_MGR->devices_flush_output();
}
// update rawinput midi algorithm, which is game-dependent
if (!midi_algo.has_value()) {
if (games::gitadora::is_drum() || avs::game::is_model("MMD")) {
// GITADORA DrumMania, FutureTomTom
midi_algo = rawinput::MidiNoteAlgorithm::V2_DRUM;
} else {
midi_algo = rawinput::MidiNoteAlgorithm::V2;
}
}
rawinput::set_midi_algorithm(midi_algo.value());
// attach games
for (auto game : games) {
game->post_attach();
}
// game start
log_info("launcher", "calling game entry");
avs::game::entry_main();
// clear presence
richpresence::shutdown();
// detach games
for (auto game : games) {
game->detach();
}
// sci unit detach
if (attach_io || attach_sciunit) {
sciunit_detach();
}
// ext dev detach
if (attach_io || attach_extdev) {
extdev_detach();
}
// device detach
if (attach_io || attach_device) {
spicedevice_detach();
}
// acio detach
if (attach_io || attach_acio) {
acio::detach();
}
// delete games
while (!games.empty()) {
delete games.back();
games.pop_back();
}
// free api controller
API_CONTROLLER.reset();
// stop coin input thread
eamuse_coin_stop_thread();
eamuse_pin_macro_stop_thread();
// BT5API
if (BT5API_ENABLED) {
bt5api_dispose();
}
sdk::fini_sdk_modules();
// stop raw input
RI_MGR.reset();
// debug hook
debughook::detach();
#ifndef NO_SCARD
// scard
scard_fini();
#endif
// stop reader thread in case it was running
stop_reader_thread();
// cardio
if (cardio_enabled) {
cardio_runner_stop();
}
// server
if (easrv_port != 0u) {
easrv_shutdown();
}
// AVS EA3 shutdown
avs::ea3::shutdown();
// AVS cleanup
avs::core::shutdown();
// dispose crypt
crypt::dispose();
// disable super exit
superexit::disable();
// disable poke
games::iidx::poke::disable();
#if SPICE64 && !SPICE_XP
games::iidx::camera_release();
#endif
// shutdown
log_warning("launcher", "end");
launcher::stop_subsystems();
return 0;
}
// https://github.com/spice2x/spice2x.github.io/issues/264
// huge ugly hack to work around things that broke when MinGW switched from msvcrt to ucrt
// this is done to ensure that any DLL hooks that rely on msvcrt continue to work
void update_msvcrt_args(int argc, char *argv[]) {
#if defined(_UCRT)
auto msvc = LoadLibraryA("msvcrt.dll");
if (!msvc) {
log_warning("launcher", "failed to load msvcrt.dll");
return;
}
// get __argc
PINT32 argc_addr = (PINT32)GetProcAddress(msvc, "__argc");
if (!argc_addr) {
log_warning("launcher", "failed to find msvcrt!__argc");
return;
}
try {
if (*argc_addr == argc) {
log_warning("launcher", "msvcrt!__argc is already set");
return;
}
} catch (const std::exception &e) {
log_warning("launcher", "exception while reading msvcrt!_argc: {}", e.what());
}
// get __argv
PCHAR **argv_addr = (PCHAR **)GetProcAddress(msvc, "__argv");
if (!argv_addr) {
log_warning("launcher", "failed to find msvcrt!__argv");
return;
}
// update them
try {
log_info("launcher", "msvcrt!__argc value before: {}", *argc_addr);
*argc_addr = argc;
log_info("launcher", "msvcrt!__argc value after: {}", *argc_addr);
*argv_addr = argv;
} catch (const std::exception &e) {
log_warning("launcher", "exception while messing with msvcrt!_argc and _argv: {}", e.what());
}
#else
log_misc("launcher", "not UCRT, skipping msvcrt!_argc / _argv hacks");
#endif
}
void dump_button_bindings(std::vector<Button> *buttons) {
if (!buttons) {
return;
}
for (auto button = buttons->begin(); button != buttons->end(); ++button) {
if (!button->isSet()) {
continue;
}
if (button->isNaive()) {
log_misc(
"rawinput", " [{}] dev=Naive, vkey={} ({})",
button->getName(),
button->getVKey(),
button->getVKeyString()
);
} else {
log_misc(
"rawinput", " [{}] dev={}, vkey={}, analogtype={}",
button->getName(),
button->getDeviceIdentifier(),
button->getVKey(),
static_cast<uint32_t>(button->getAnalogType())
);
}
for (auto& alt : button->getAlternatives()) {
if (!alt.isValid()) {
continue;
}
if (alt.isNaive()) {
log_misc(
"rawinput", " [{}] (alt) dev=Naive, vkey={} ({})",
alt.getName(),
alt.getVKey(),
alt.getVKeyString()
);
} else {
log_misc(
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
alt.getName(),
alt.getDeviceIdentifier(),
alt.getVKey(),
static_cast<uint32_t>(alt.getAnalogType())
);
}
}
}
}
void dump_analog_bindings() {
auto analogs = games::get_analogs(eamuse_get_game());
if (!analogs) {
return;
}
for (auto& analog : *analogs) {
if (!analog.isSet()) {
continue;
}
log_misc(
"rawinput", " [{}] dev={}, index={}",
analog.getName(),
analog.getDeviceIdentifier(),
analog.getIndex()
);
}
}
#ifndef SPICETOOLS_SPICECFG_STANDALONE
int main(int argc, char *argv[]) {
return main_implementation(argc, argv);
}
#endif