#include "options.h" #include "external/tinyxml2/tinyxml2.h" #include "util/utils.h" #include "util/fileutils.h" #include #ifdef NO_SCARD #define WITH_SCARD 0 #else #define WITH_SCARD 1 #endif static const std::vector CATEGORY_ORDER_API = { "SpiceCompanion and API", "API (Serial)", "API (Dev)", "BT5 API", }; static const std::vector CATEGORY_ORDER_BASIC = { "Game Options", "Common", "Network", "Graphics (Common)", "Graphics (Full Screen)", "Graphics (Windowed)", "Audio", }; static const std::vector CATEGORY_ORDER_ADVANCED = { "Game Options (Advanced)", "Overlay", "Network (Advanced)", "Performance", "Miscellaneous", "Touch Parameters", "I/O Options", "NFC Card Readers", }; static const std::vector CATEGORY_ORDER_DEV = { "Path Overrides", "Network (Development)", "Audio (Hacks)", "I/O Modules", "Development", "Debug Log", }; static const std::vector CATEGORY_ORDER_NONE = { "" }; bool launcher::USE_CMD_OVERRIDE = false; /* * Option Definitions * Be aware that the order must be the same as in the enum launcher::Options! */ static const std::vector OPTION_DEFINITIONS = { { .title = "Override Game Executable & Disable Hooks", .name = "exec", .desc = "Path to the game DLL file.\n\n" "This option is typically not needed; leave it empty to allow spice to auto-detect your game.\n\n" "WARNING: this option also disables all game-specific hooks and turns off most I/O modules!", .type = OptionType::Text, .setting_name = "*.dll", .category = "Path Overrides", }, { .title = "Open Configurator", .name = "cfg", .desc = "Opens configuration window. This can only be launched via the command line " "(spice -cfg or spice64 -cfg) or just launch spicecfg.exe", .type = OptionType::Bool, .hidden = true, .disabled = true, }, { .title = "(REMOVED) Open KFControl", .name = "kfcontrol", .desc = "This feature has been removed; please use an older version", .type = OptionType::Bool, .hidden = true, .disabled = true, }, { .title = "Basic Local EA Emulation", .name = "ea", .desc = "Enables the integrated local EA server, just enough to boot the game; no card in, no data saving", .type = OptionType::Bool, .category = "Network", }, { // ServiceURL .title = "EA Service URL", .name = "url", .desc = "Sets a custom service URL override", .type = OptionType::Text, .category = "Network", }, { .title = "PCBID", .name = "p", .desc = "Sets a custom PCBID override", .type = OptionType::Text, .setting_name = "04040000000000000000", .category = "Network", .sensitive = true, }, { .title = "Player 1 Card", .name = "card0", .desc = "Set a card number for reader 1. Overrides the selected card file.", .type = OptionType::Text, .setting_name = "E004010000000000", .category = "Network", .sensitive = true, }, { .title = "Player 2 Card", .name = "card1", .desc = "Set a card number for reader 2. Overrides the selected card file.", .type = OptionType::Text, .setting_name = "E004010000000000", .category = "Network", .sensitive = true, }, { // Player1PinMacro .title = "Player 1 PIN Macro", .name = "pinmacro0", .desc = "Set a PIN for Player 1 that will cause the PIN to be automatically typed when Player 1 PIN Macro overlay key is pressed", .type = OptionType::Text, .setting_name = "1234", .category = "Network (Advanced)", .sensitive = true, }, { // Player2PinMacro .title = "Player 2 PIN Macro", .name = "pinmacro1", .desc = "Set a PIN for Player 2 that will cause the PIN to be automatically typed when Player 2 PIN Macro overlay key is pressed", .type = OptionType::Text, .setting_name = "5678", .category = "Network (Advanced)", .sensitive = true, }, { .title = "Windowed Mode", .name = "w", .desc = "Enables windowed mode", .type = OptionType::Bool, .category = "Graphics (Windowed)", }, { // InjectHook .title = "Inject DLL Hooks", .name = "k", .desc = "Multiple files are allowed; use multiple -k flags, or in SpiceCfg, separate by " "space (foo.dll bar.dll baz.dll). Injects a hook by using LoadLibrary on the " "specified file before running the main game code", .type = OptionType::Text, .setting_name = "a.dll b.dll c.dll", .category = "Common", }, { // EarlyInjectHook .title = "Inject Early DLL Hooks", .name = "z", .desc = "Equivalent to 'Inject DLL Hooks' option, but ensures hooks are injected before " "the game module has been loaded into the process", .type = OptionType::Text, .setting_name = "a.dll b.dll c.dll", .category = "Common", }, { .title = "(REMOVED) Execute Lua Script", .name = "script", .desc = "This feature has been removed", .type = OptionType::Text, .hidden = true, .category = "Miscellaneous", .disabled = true, }, { .title = "Lock Cursor to Window", .name = "c", .desc = "Confines the cursor to be within the game window", .type = OptionType::Bool, .category = "Common", }, { .title = "Show Cursor & Touch Emulation Enable", .name = "s", .desc = "Shows the cursor in the game window; also turns on touch emulation. Do not use if you use a real touch screen", .type = OptionType::Bool, .category = "Common", }, { .title = "Monitor", .name = "monitor", .desc = "Sets the display that the game will be opened in, for multiple monitors.\n\n" "0 is the primary monitor, 1 is the second monitor, and so on.\n\n" "Not all games will respect this. Does not work at all with multi-monitor games like TDJ/UFC. " "Disable Full Screen Optimizations for best results", .type = OptionType::Integer, .category = "Graphics (Full Screen)", }, { .title = "Only Use One Monitor", .name = "graphics-force-single-adapter", .desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n" "May cause unstable framerate and desyncs, especially if monitors have different refresh rates!", .type = OptionType::Bool, .category = "Graphics (Full Screen)", }, { .title = "Force Refresh Rate", .name = "graphics-force-refresh", .desc = "Force the refresh rate for the primary display adapter; works in both full screen and windowed modes", .type = OptionType::Integer, .category = "Graphics (Common)", }, { // FullscreenResolution .title = "Force Full Screen Resolution", .name = "forceres", .desc = "For full screen mode, forcibly set a custom resolution.\n\n" "Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n" "If you are using -forceresswap/-sdvxlandscape, put the TARGET monitor resolution in this field.\n\n" "This should only be used as last resort if your GPU/monitor can't display the resolution required by the game", .type = OptionType::Text, .setting_name = "1280,720", .category = "Graphics (Full Screen)" }, { // FullscreenOrientationFlip .title = "Full Screen Orientation Swap", .name = "forceresswap", .desc = "Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n" "Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n" "Strongly consider combining this with -forceres option to render at monitor native resolution\n\n" "WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out!", .type = OptionType::Bool, .category = "Graphics (Full Screen)" }, { // Graphics9On12 .title = "DirectX 9 on 12 (DEPRECATED - use -dx9on12 instead)", .name = "9on12", .desc = "Use D3D9On12 wrapper library, requires Windows 10 Insider Preview 18956 or later. Deprecated - use -dx9on12 instead", .type = OptionType::Bool, .hidden = true, .category = "Graphics (Common)", }, { // spice2x_Dx9On12 .title = "DirectX 9 on 12", .name = "sp2x-dx9on12", .display_name = "dx9on12", .aliases= "dx9on12", .desc = "Use D3D9On12 wrapper library, requires Windows 10 Insider Preview 18956 or later. Has no effect games on that don't use DX9. Can cause some games to crash.\n\n" "Default: auto (use DX9 for most games, but turn on 9on12 for games that require it on non-NVIDIA GPUs)", .type = OptionType::Enum, .category = "Graphics (Common)", .elements = { {"auto", "Automatic"}, {"0", "Use DX9"}, {"1", "Use DX9on12"}, }, }, { .title = "Disable Win/Media/Special Keys", .name = "nolegacy", .desc = "Disables legacy key activation in-game.", .type = OptionType::Bool, .category = "I/O Options", }, { .title = "Discord Rich Presence", .name = "richpresence", .desc = "Enables Discord Rich Presence support", .type = OptionType::Bool, .category = "Miscellaneous", }, { .title = "Smart Local EA", .name = "smartea", .desc = "Automatically enables -ea when server is offline", .type = OptionType::Bool, .category = "Network (Advanced)", }, { // EAmusementMaintenance .title = "EA Maintenance (DEPRECATED - use -forceeamaint instead)", .name = "eamaint", .desc = "Enables EA Maintenance, 1 for on, 0 for off.", .type = OptionType::Enum, .hidden = true, .category = "Network (Advanced)", .elements = {{"0", "Off"}, {"1", "On"}}, }, { // spice2x_EAmusementMaintenance .title = "Local EA Maintenance", .name = "sp2x-eamaint", .display_name = "forceeamaint", .aliases= "forceeamaint", .desc = "Causes local EA to start in maintenance mode. Must be used with -ea or -smartea.", .type = OptionType::Bool, .category = "Network (Advanced)", }, { .title = "Preferred NetAdapter IP", .name = "network", .desc = "This is NOT the EA service URL; use -url for that. " "Force the use of an adapter with the specified network. Must also provide -subnet", .type = OptionType::Text, .category = "Network (Development)", .sensitive = true, }, { .title = "Preferred NetAdapter Subnet", .name = "subnet", .desc = "Force the use of an adapter with the specified subnet. Must also provide -network", .type = OptionType::Text, .category = "Network (Development)", }, { .title = "Disable Network Fixes", .name = "netfixdisable", .desc = "Force disables network fixes", .type = OptionType::Bool, .category = "Network (Development)", }, { .title = "HTTP/1.1", .name = "http11", .desc = "Sets EA3 http11 value", .type = OptionType::Enum, .category = "Network (Development)", .elements = {{"0", "Off"}, {"1", "On"}}, }, { .title = "Disable SSL Protocol", .name = "ssldisable", .desc = "Prevents the SSL protocol from being registered", .type = OptionType::Bool, .category = "Network (Development)", }, { .title = "URL Slash", .name = "urlslash", .desc = "Sets EA3 urlslash value", .type = OptionType::Enum, .category = "Network (Advanced)", .elements = {{"0", "Off"}, {"1", "On"}}, }, { .title = "SOFTID", .name = "r", .desc = "Set custom SOFTID override", .type = OptionType::Text, .category = "Development", .sensitive = true, }, { .title = "(REMOVED) VR controls (experimental)", .name = "vr", .desc = "This feature has been removed", .type = OptionType::Bool, .hidden = true, .game_name = "DANCERUSH", .category = "Game Options", .disabled = true, }, { .title = "Disable Spice Overlay", .name = "overlaydisable", .desc = "Disables the in-game overlay", .type = OptionType::Bool, .category = "Overlay", }, { // spice2x_FpsAutoShow .title = "Auto Show FPS/Clock", .name = "sp2x-autofps", .display_name = "autofps", .aliases= "autofps", .desc = "Automatically show FPS / clock / timer overlay window when the game starts", .type = OptionType::Bool, .category = "Overlay", }, { // spice2x_FpsOpposite .title = "Show FPS/Clock top-left", .name = "fpsflip", .desc = "Show FPS / clock / timer overlay on the top left of the screen instead of the top right", .type = OptionType::Bool, .category = "Overlay", }, { // spice2x_SubScreenAutoShow .title = "Auto Show Subscreen", .name = "sp2x-autosubscreen", .display_name = "autosubscreen", .aliases= "autosubscreen", .desc = "Automatically show the subscreen overlay when the game starts, if the game has one", .type = OptionType::Bool, .category = "Overlay", }, { // spice2x_IOPanelAutoShow .title = "Auto Show IO Panel", .name = "sp2x-autoiopanel", .display_name = "autoiopanel", .aliases= "autoiopanel", .desc = "Automatically show I/O panel window when the game starts", .type = OptionType::Bool, .category = "Overlay", }, { // spice2x_KeypadAutoShow .title = "Auto Show Keypad", .name = "sp2x-autokeypad", .display_name = "autokeypad", .aliases= "autokeypad", .desc = "Automatically show virtual keypad window when the game starts", .type = OptionType::Enum, .category = "Overlay", .elements = { {"0", "Off"}, {"1", "P1"}, {"2", "P2"}, {"3", "P1 and P2"}, }, }, { .title = "Force Load IIDX Module", .name = "iidx", .desc = "Manually enable Beatmania IIDX module", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { .title = "IIDX Camera Order Flip", .name = "iidxflipcams", .desc = "Flip the camera order", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { .title = "IIDX Disable Cameras", .name = "iidxdisablecams", .desc = "Disables cameras", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options", }, { // IIDXCamHook .title = "IIDX Cam Hook", .name = "iidxtdjcamhook", .display_name = "iidxcamhook", .aliases = "iidxcamhook", .desc = "Experimental camera support for IIDX 27+ via texture hooking. Requires camera(s) that support YUY2 or NV12 encoding", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // IIDXCamHookRatio .title = "IIDX Cam Hook Aspect Ratio", .name = "iidxtdjcamhookratio", .display_name = "iidxcamhookratio", .aliases = "iidxcamhookratio", .desc = "Determine what aspect ratio should be preferred for webcam input. Default: 16:9", .type = OptionType::Enum, .hidden = true, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", .elements = { {"43", "4:3"}, {"169", "16:9"}, }, }, { // IIDXCamHookOverride .title = "IIDX Cam Hook Offset Override", .name = "iidxtdjcamhookoffset", .display_name = "iidxcamhookoffset", .aliases = "iidxcamhookoffset", .desc = "Override DLL offsets for camera hook; " "format: 5 hex values separated by commas " "(offset_hook_a, offset_textures, offset_camera_manager, offset_d3d_device, offset_afp_texture)", .type = OptionType::Text, .setting_name = "0x5817a0,0x6fffbd8,0xbbae40,0xe0,0x30", .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // IIDXCamHookTopId .title = "IIDX Cam Hook Top ID", .name = "iidxtdjcamhooktop", .display_name = "iidxcamhooktop", .aliases = "iidxcamhooktop", .desc = "Specify a partial ID of the camera to use as the TOP camera. Useful if you have more than 3 cameras and need to tell spice which ones to use.\n\n" "To get IDs, launch the game and look for camera IDs from MFEnumDeviceSources.\n\n" "For example, `vid_1234&pid_5678` matches on \\\\?\\usb#vid_1234&pid_5678&mi_00#7&234123456&1&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\\global", .type = OptionType::Text, .setting_name = "vid_1234&pid_5678", .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // IIDXCamHookFrontId .title = "IIDX Cam Hook Front ID", .name = "iidxtdjcamhookfront", .display_name = "iidxcamhookfront", .aliases = "iidxcamhookfront", .desc = "Specify a partial ID of the camera to use as the FRONT camera. Useful if you have more than 3 cameras and need to tell spice which ones to use.\n\n" "To get IDs, launch the game and look for camera IDs from MFEnumDeviceSources.\n\n" "For example, `vid_1234&pid_5678` matches on \\\\?\\usb#vid_1234&pid_5678&mi_00#7&234123456&1&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\\global", .type = OptionType::Text, .setting_name = "vid_90ab&pid_cdef", .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { .title = "IIDX Sound Output Device", .name = "iidxsounddevice", .desc = "SOUND_OUTPUT_DEVICE environment variable override. Default: auto. " "Auto will use WASAPI, unless -iidxasio is set or if XONAR SOUND CARD is present, then ASIO is used.", .type = OptionType::Enum, .game_name = "Beatmania IIDX", .category = "Game Options", .elements = { {"auto", "Automatic"}, {"wasapi", "Windows Audio"}, {"asio", "ASIO"}, }, }, { .title = "IIDX ASIO Driver", .name = "iidxasio", .desc = "ASIO driver name to use, replacing XONAR SOUND CARD(64). " "String should match registry key under HKLM\\SOFTWARE\\ASIO\\ \n\n" "IIDX is very picky about ASIO devices it can support, YMMV", .type = OptionType::Text, .game_name = "Beatmania IIDX", .category = "Game Options", }, { .title = "IIDX BIO2 Firmware Update", .name = "iidxbio2fw", .desc = "Enables BIO2 firmware updates. WARNING - can cause semi-permanent change to your I/O board", .type = OptionType::Bool, .hidden = true, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { .title = "IIDX TDJ Mode", .name = "iidxtdj", .desc = "Enables TDJ cabinet mode. Ensure you also set -iidxsounddevice to desired option", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options", }, { // spice2x_IIDXDigitalTTSensitivity .title = "IIDX Digital TT Sensitivity", .name = "sp2x-iidxdigitalttsens", .display_name = "iidxdigitalttsens", .aliases= "iidxdigitalttsens", .desc = "Adjust sensitivity of turntable when digital input (buttons) is used. Does not affect analog input! Default: 4", .type = OptionType::Integer, .setting_name = "(0-255)", .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // spice2x_IIDXLDJForce720p .title = "IIDX LDJ Force 720p (HD)", .name = "sp2x-iidxldjforce720p", .display_name = "iidxldjforce720p", .aliases= "iidxldjforce720p", .desc = "Force Definition Type HD (720p) mode instead of FHD (1080p) for IIDX30+. Only for LDJ! TDJ is always FHD in IIDX30+", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // spice2x_IIDXTDJSubSize .title = "IIDX TDJ Subscreen Size", .name = "sp2x-iidxtdjsubsize", .display_name = "iidxtdjsubsize", .aliases= "iidxtdjsubsize", .desc = "Default size of the subscreen overlay.\n\nNote: in windowed mode, subscreen will always be full size.\n\nDefault: medium", .type = OptionType::Enum, .game_name = "Beatmania IIDX", .category = "Overlay", .elements = { {"small", ""}, {"medium", ""}, {"large", ""}, {"fullscreen", ""}, }, }, { // spice2x_IIDXLEDFontSize .title = "IIDX LED Ticker Font Size", .name = "sp2x-iidxledfontsize", .display_name = "iidxledfontsize", .aliases= "iidxledfontsize", .desc = "Configure the font size of the segment display, in pixels. Default: 64 (px)", .type = OptionType::Integer, .game_name = "Beatmania IIDX", .category = "Overlay", }, { // spice2x_IIDXLEDColor .title = "IIDX LED Ticker Color", .name = "sp2x-iidxledcolor", .display_name = "iidxledcolor", .aliases= "iidxledcolor", .desc = "Configure the font color of the segment display; specify RGB value in hex. " "Default: 0xff0000 (red)", .type = OptionType::Hex, .game_name = "Beatmania IIDX", .category = "Overlay", }, { // spice2x_IIDXLEDPos .title = "IIDX LED Ticker Position", .name = "sp2x-iidxledpos", .display_name = "iidxledpos", .aliases= "iidxledpos", .desc = "Initial position of the segment display. Default: bottom", .type = OptionType::Enum, .game_name = "Beatmania IIDX", .category = "Overlay", .elements = { {"topleft", ""}, {"top", ""}, {"topright", ""}, {"bottomleft", ""}, {"bottom", ""}, {"bottomright", ""}, }, }, { .title = "Force Load Sound Voltex Module", .name = "sdvx", .desc = "Manually enable Sound Voltex Module", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { .title = "SDVX Force 720p", .name = "sdvx720", .desc = "Force Sound Voltex 720p display mode, used by older games. " "For newer games, use window resize function instead", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { .title = "SDVX Printer Emulation", .name = "printer", .desc = "Enable Sound Voltex printer emulation", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { // SDVXPrinterOutputPath .title = "SDVX Printer Output Path", .name = "printerpath", .desc = "Path to folder where images will be stored", .type = OptionType::Text, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { .title = "SDVX Printer Output Clear", .name = "printerclear", .desc = "Clean up saved images in the output directory on startup", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { .title = "SDVX Printer Output Overwrite", .name = "printeroverwrite", .desc = "Always overwrite the same file in output directory", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { // SDVXPrinterOutputFormat .title = "SDVX Printer Output Format", .name = "printerformat", .desc = "File format for printer output", .type = OptionType::Text, .setting_name = "(png/bmp/tga/jpg)", .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { .title = "SDVX Printer JPG Quality", .name = "printerjpgquality", .desc = "Quality setting in percent if JPG format is used", .type = OptionType::Integer, .setting_name = "(0-100)", .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { .title = "SDVX Disable Cameras (DEPRECATED - no longer needed)", .name = "sdvxdisablecams", .desc = "This option does nothing.\n\n" "This option was used in the past to fix cameras in SDVX5 but this is no longer " "needed as camera check will always be skipped with a built-in patch.", .type = OptionType::Bool, .hidden = true, .game_name = "Sound Voltex", .category = "Game Options", }, { .title = "SDVX Native Touch Handling", .name = "sdvxnativetouch", .desc = "Disables touch hooks and lets the game access a touch screen directly. " "Requires a touch screen to be connected as a secondary monitor. " "Touch input must be routed to the primary screen via Windows Tablet PC settings. " "Enable this when you get duplicate touch inputs from an actual touch screen", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { // spice2x_SDVXDigitalKnobSensitivity .title = "SDVX Digital Knob Sensitivity", .name = "sp2x-sdvxdigitalknobsens", .display_name = "sdvxdigitalknobsens", .aliases= "sdvxdigitalknobsens", .desc = "Adjust sensitivity of knobs when digital input (buttons) is used. Does not affect analog input! Default: 16", .type = OptionType::Integer, .setting_name = "(0-255)", .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { // spice2x_SDVXAsioDriver .title = "SDVX ASIO driver", .name = "sp2x-sdvxasio", .display_name = "sdvxasio", .aliases= "sdvxasio", .desc = "ASIO driver name to use, replacing XONAR SOUND CARD(64). " "String should match registry key under HLKM\\SOFTWARE\\ASIO\\ \n\n" "SDVX is EXTREMELY picky about ASIO devices it can support!", .type = OptionType::Text, .game_name = "Sound Voltex", .category = "Game Options", }, { // spice2x_SDVXSubPos .title = "SDVX Subscreen Overlay Position", .name = "sp2x-sdvxsubpos", .display_name = "sdvxsubpos", .aliases= "sdvxsubpos", .desc = "Location for the subscreen overlay. Default: bottom", .type = OptionType::Enum, .game_name = "Sound Voltex", .category = "Overlay", .elements = { {"top", ""}, {"center", ""}, {"bottom", ""}, {"bottomleft", "for landscape"}, {"bottomright", "for landscape"}, }, }, { // spice2x_SDVXSubRedraw .title = "SDVX Subscreen Force Redraw", .name = "sp2x-sdvxsubredraw", .display_name = "sdvxsubredraw", .aliases= "sdvxsubredraw", .desc = "Check if second monitor for subscreen doesn't update every frame; " "forces subscreen to redraw every frame, only needed for newer EG.", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, { .title = "Force Load DDR Module", .name = "ddr", .desc = "Manually enable Dance Dance Revolution module", .type = OptionType::Bool, .game_name = "Dance Dance Revolution", .category = "Game Options (Advanced)", }, { .title = "DDR 4:3 Mode", .name = "ddrsd/o", .desc = "Enable DDR 4:3 (SD) mode", .type = OptionType::Bool, .game_name = "Dance Dance Revolution", .category = "Game Options", }, { // DDRSkipCodecRegisteration .title = "DDR Skip Codec Registration", .name = "ddrnocodec", .desc = "Prevent automatic registration of codecs in the com folder", .type = OptionType::Bool, .game_name = "Dance Dance Revolution", .category = "Game Options (Advanced)", }, { .title = "Force Load Pop'n Music Module", .name = "pnm", .desc = "Manually enable Pop'n Music module", .type = OptionType::Bool, .game_name = "Pop'n Music", .category = "Game Options (Advanced)", }, { .title = "Pop'n Music Force HD Mode", .name = "pnmhd", .desc = "Force enable Pop'n Music HD mode", .type = OptionType::Bool, .game_name = "Pop'n Music", .category = "Game Options", }, { .title = "Pop'n Music Force SD Mode", .name = "pnmsd", .desc = "Force enable Pop'n Music SD mode", .type = OptionType::Bool, .game_name = "Pop'n Music", .category = "Game Options", }, { .title = "Force Load HELLO! Pop'n Music Module", .name = "hpm", .desc = "Manually enable HELLO! Pop'n Music module", .type = OptionType::Bool, .game_name = "HELLO! Pop'n Music", .category = "Game Options (Advanced)", }, { .title = "Force Load GitaDora Module", .name = "gd", .desc = "Manually enable GitaDora module", .type = OptionType::Bool, .game_name = "GitaDora", .category = "Game Options (Advanced)", }, { .title = "GitaDora Two Channel Audio", .name = "2ch", .desc = "Attempt to reduce audio channels down to just two channels.", .type = OptionType::Bool, .game_name = "GitaDora", .category = "Game Options", }, { .title = "GitaDora Cabinet Type", .name = "gdcabtype", .desc = "Select cabinet type. DX has more input and lights. Pick SD2 for single player guitar mode on white cab", .type = OptionType::Enum, .game_name = "GitaDora", .category = "Game Options", .elements = {{"1", "DX"}, {"2", "SD"}, {"3", "SD2 - white cab"}}, }, { .title = "Force Load Jubeat Module", .name = "jb", .desc = "Manually enable Jubeat module", .type = OptionType::Bool, .game_name = "Jubeat", .category = "Game Options (Advanced)", }, { .title = "Force Load Reflec Beat Module", .name = "rb", .desc = "Manually enable Reflec Beat module", .type = OptionType::Bool, .game_name = "Reflec Beat", .category = "Game Options (Advanced)", }, { .title = "Force Load Tenkaichi Shogikai Module", .name = "shogikai", .desc = "Manually enable Tenkaichi Shogikai module", .type = OptionType::Bool, .game_name = "Tenkaichi Shogikai", .category = "Game Options (Advanced)", }, { .title = "Force Load Beatstream Module", .name = "bs", .desc = "Manually enable Beatstream module", .type = OptionType::Bool, .game_name = "Beatstream", .category = "Game Options (Advanced)", }, { .title = "Force Load Nostalgia Module", .name = "nostalgia", .desc = "Manually enable Nostalgia module", .type = OptionType::Bool, .game_name = "Nostalgia", .category = "Game Options (Advanced)", }, { .title = "Force Load Dance Evolution Module", .name = "dea", .desc = "Manually enable Dance Evolution module", .type = OptionType::Bool, .game_name = "Dance Evolution", .category = "Game Options (Advanced)", }, { .title = "Force Load FutureTomTom Module", .name = "ftt", .desc = "Manually enable FutureTomTom module", .type = OptionType::Bool, .game_name = "FutureTomTom", .category = "Game Options (Advanced)", }, { .title = "Force Load BBC Module", .name = "bbc", .desc = "Manually enable Bishi Bashi Channel module", .type = OptionType::Bool, .game_name = "Bishi Bashi Channel", .category = "Game Options (Advanced)", }, { .title = "Force Load Metal Gear Arcade Module", .name = "mga", .desc = "Manually enable Metal Gear Arcade module", .type = OptionType::Bool, .game_name = "Metal Gear", .category = "Game Options (Advanced)", }, { .title = "Force Load Quiz Magic Academy Module", .name = "qma", .desc = "Manually enable Quiz Magic Academy module", .type = OptionType::Bool, .game_name = "Quiz Magic Academy", .category = "Game Options (Advanced)", }, { .title = "Force Load Road Fighters 3D Module", .name = "rf3d", .desc = "Manually enable Road Fighters 3D module", .type = OptionType::Bool, .game_name = "Road Fighters 3D", .category = "Game Options (Advanced)", }, { .title = "Force Load Steel Chronicle Module", .name = "sc", .desc = "Manually enable Steel Chronicle module", .type = OptionType::Bool, .game_name = "Steel Chronicle", .category = "Game Options (Advanced)", }, { .title = "Force Load Mahjong Fight Club Module", .name = "mfc", .desc = "Manually enable Mahjong Fight Club module", .type = OptionType::Bool, .game_name = "Mahjong Fight Club", .category = "Game Options (Advanced)", }, { .title = "Force Load Scotto Module", .name = "scotto", .desc = "Manually enable Scotto module", .type = OptionType::Bool, .game_name = "Scotto", .category = "Game Options (Advanced)", }, { .title = "Force Load Dance Rush Module", .name = "dr", .desc = "Manually enable Dance Rush module", .type = OptionType::Bool, .game_name = "DANCERUSH", .category = "Game Options (Advanced)", }, { .title = "Force Load Winning Eleven Module", .name = "we", .desc = "Manually enable Winning Eleven module", .type = OptionType::Bool, .game_name = "Winning Eleven", .category = "Game Options (Advanced)", }, { .title = "Force Load Otoca D'or Module", .name = "otoca", .desc = "Manually enable Otoca D'or module", .type = OptionType::Bool, .game_name = "Otoca D'or", .category = "Game Options (Advanced)", }, { .title = "Force Load LovePlus Module", .name = "loveplus", .desc = "manually enable LovePlus module", .type = OptionType::Bool, .game_name = "LovePlus", .category = "Game Options (Advanced)", }, { .title = "Force Load Charge Machine Module", .name = "pcm", .desc = "manually enable Charge Machine module", .type = OptionType::Bool, .game_name = "Charge Machine", .category = "Game Options (Advanced)", }, { .title = "Force Load Ongaku Paradise Module", .name = "onpara", .desc = "manually enable Ongaku Paradise module", .type = OptionType::Bool, .game_name = "Ongaku Paradise", .category = "Game Options (Advanced)", }, { .title = "Force Load Busou Shinki Module", .name = "busou", .desc = "manually enable Busou Shinki module", .type = OptionType::Bool, .game_name = "Busou Shinki: Armored Princess Battle Conductor", .category = "Game Options (Advanced)", }, { // LoadCCJModule .title = "Force Load Chase Chase Jokers Module", .name = "ccj", .desc = "manually enable Chase Chase Jokers module", .type = OptionType::Bool, .game_name = "Chase Chase Jokers", .category = "Game Options (Advanced)", }, { // LoadQKSModule .title = "Force Load QuizKnock STADIUM Module", .name = "qks", .desc = "manually enable QuizKnock STADIUM module", .type = OptionType::Bool, .game_name = "QuizKnock STADIUM", .category = "Game Options (Advanced)", }, { // LoadMFGModule .title = "Force Load Mahjong Fight Girl Module", .name = "mfg", .desc = "manually enable Mahjong Fight Girl module", .type = OptionType::Bool, .game_name = "Mahjong Fight Girl", .category = "Game Options (Advanced)", }, { // LoadPCModule .title = "Force Load Polaris Chord Module", .name = "pc", .desc = "manually enable Polaris Chord module", .type = OptionType::Bool, .game_name = "Polaris Chord", .category = "Game Options (Advanced)", }, { // LoadMusecaModule .title = "Force Load Museca Module", .name = "museca", .desc = "manually enable Museca module", .type = OptionType::Bool, .game_name = "Museca", .category = "Game Options (Advanced)", }, { .title = "Modules Folder Override", .name = "modules", .desc = "Sets a custom path to the modules folder", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "Screenshot Folder Override", .name = "screenshotpath", .desc = "Sets a custom path to the screenshots folder", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "Configuration Path Override", .name = "cfgpath", .desc = "Sets a custom file path for config file. Must be passed via the command line. " "If left empty, %appdata%\\spicetools.xml will be used", .type = OptionType::Text, .setting_name = "(default)", .category = "Path Overrides", .disabled = true, }, { // ScreenResizeConfigPath .title = "Screen Resize Config Path Override", .name = "resizecfgpath", .desc = "Sets a custom file path for screen resize config file. " "If left empty, %appdata%\\spice2x\\spicetools_screen_resize.json will be used", .type = OptionType::Text, .category = "Path Overrides", }, { // PatchManagerConfigPath .title = "Patch Manager Config Path Override", .name = "patchcfgpath", .desc = "Sets a custom file path for patch manager config file. Can be used to manage 'profiles' for auto-patches. " "If left empty, %appdata%\\spice2x\\spicetools_patch_manager.json will be used", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "Intel SDE", .name = "sde", .desc = "Path to Intel SDE kit path for automatic attaching", .type = OptionType::Text, .category = "Development", }, { .title = "ea3-config.xml Override", .name = "e", .desc = "Sets a custom path to ea3-config.xml", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "app-config.xml Override", .name = "a", .desc = "Sets a custom path to app-config.xml", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "avs-config.xml Override", .name = "v", .desc = "Sets a custom path to avs-config.xml", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "bootstrap.xml Override", .name = "b", .desc = "Sets a custom path to bootstrap.xml", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "log.txt Override", .name = "y", .desc = "Sets a custom path to log.txt", .type = OptionType::Text, .category = "Path Overrides", }, { .title = "API TCP Port", .name = "api", .desc = "Port the API should be listening on", .type = OptionType::Integer, .category = "SpiceCompanion and API", }, { .title = "API Password", .name = "apipass", .desc = "Set the custom user password needed to use the API", .type = OptionType::Text, .category = "SpiceCompanion and API", .sensitive = true, }, { .title = "API Verbose Logging", .name = "apilogging", .desc = "verbose logging of API activity", .type = OptionType::Bool, .category = "API (Dev)", }, { .title = "API Serial Port", .name = "apiserial", .desc = "Serial port the API should be listening on", .type = OptionType::Text, .category = "API (Serial)", }, { .title = "API Serial Baud", .name = "apiserialbaud", .desc = "Baud rate for the serial port", .type = OptionType::Integer, .category = "API (Serial)", }, { .title = "API Pretty", .name = "apipretty", .desc = "Slower, but pretty API output", .type = OptionType::Bool, .category = "API (Dev)", }, { .title = "API Debug Mode", .name = "apidebug", .desc = "Enables API debugging mode", .type = OptionType::Bool, .category = "API (Dev)", }, { // APIScreenMirrorQuality .title = "API Screen Mirror Quality Override", .name = "apiscreenq", .desc = "JPEG compression level of mirrored images, overriding any client request. " "Value must be between 0 (poor quality) and 100 (best quality), inclusive. Default: 70", .type = OptionType::Integer, .setting_name = "(0-100)", .category = "SpiceCompanion and API", }, { // APIScreenMirrorDivide .title = "API Screen Mirror Divide Override", .name = "apiscreendiv", .desc = "Divide value of mirrored images, overriding any client request. " "Divide of 1 means send every pixel, 2 means every other pixel, 3 means every third pixel, and so on; " "increasing the value drastically reduces transfer size at the cost of image quality. " "Value must be 1 or greater. Default: 1", .type = OptionType::Integer, .setting_name = "1", .category = "SpiceCompanion and API", }, { .title = "Enable All IO Modules", .name = "io", .desc = "Manually enable ALL IO emulation", .type = OptionType::Bool, .category = "I/O Modules", }, { .title = "Enable ACIO Module", .name = "acio", .desc = "Manually enable ACIO emulation", .type = OptionType::Bool, .category = "I/O Modules", }, { .title = "Enable ICCA Module", .name = "icca", .desc = "Manually enable ICCA emulation", .type = OptionType::Bool, .category = "I/O Modules", }, { .title = "Enable DEVICE Module", .name = "device", .desc = "Manually enable DEVICE emulation", .type = OptionType::Bool, .category = "I/O Modules", }, { .title = "Enable EXTDEV Module", .name = "extdev", .desc = "Manually enable EXTDEV emulation", .type = OptionType::Bool, .category = "I/O Modules", }, { .title = "Enable SCIUNIT Module", .name = "sciunit", .desc = "Manually enable SCIUNIT emulation", .type = OptionType::Bool, .category = "I/O Modules", }, { .title = "Enable device passthrough", .name = "devicehookdisable", .desc = "Disable I/O and serial device hooks", .type = OptionType::Bool, .category = "I/O Modules", }, { .title = "Disable Raw Input Touch", .name = "wintouch", .desc = "For touch screen input, disable usage of Raw Input API and instead use " "Win8 Pointer API or Win7 Touch API. Only enable this if you have trouble " "using the default (raw input) touch input, as Raw Input performs better", .type = OptionType::Bool, .category = "Touch Parameters", }, { .title = "Force Touch Emulation", .name = "touchemuforce", .desc = "Force enable hook for GetTouchInputInfo API and inject WM_TOUCH events. " "This is automatically turned on when needed, so it is not typically required to " "turn it on manually. Do not enable this unless you have trouble", .type = OptionType::Bool, .category = "Touch Parameters", }, { .title = "Invert Raw Input Touch", .name = "touchinvert", .desc = "Inverts raw touch coordinates; only works for default raw input handler, " "and not Windows Touch API", .type = OptionType::Bool, .category = "Touch Parameters", }, { // DisableTouchCardInsert .title = "Disable Touch Card Insert (DEPRECATED - use -touchcard instead)", .name = "touchnocard", .desc = "Disables touch overlay card insert button.", .type = OptionType::Bool, .hidden = true, .category = "Touch Parameters", }, { // spice2x_TouchCardInsert .title = "Show Insert Card button", .name = "sp2x-touchcard", .display_name = "touchcard", .aliases= "touchcard", .desc = "Show Insert Card touch button on main display", .type = OptionType::Bool, .category = "Touch Parameters", }, { // ICCAReaderPort .title = "ICCA Reader Port", .name = "reader", .desc = "Connects to and uses a ICCA on a given COM port", .type = OptionType::Text, .category = "NFC Card Readers", }, { .title = "ICCA Reader Port (with toggle)", .name = "togglereader", .desc = "Connects to and uses a ICCA on a given COM port, and enabled NumLock toggling between P1/P2", .type = OptionType::Text, .category = "NFC Card Readers", }, { .title = "CardIO HID Reader Support", .name = "cardio", .desc = "Enables detection and support of cardio HID readers", .type = OptionType::Bool, .category = "NFC Card Readers", }, { .title = "CardIO HID Reader Order Flip", .name = "cardioflip", .desc = "Flips the order of detection for P1/P2", .type = OptionType::Bool, .category = "NFC Card Readers", }, { // CardIOHIDReaderOrderToggle .title = "CardIO HID Reader Order Toggle", .name = "cardiotoggle", .desc = "Toggles reader between P1/P2 using the NumLock key state", .type = OptionType::Bool, .category = "NFC Card Readers", }, { .title = "HID SmartCard (ACR122U)", .name = "scard", .desc = "Detects and uses HID smart card readers for card input. Developed for ACR122U reader", .type = OptionType::Bool, .hidden = !WITH_SCARD, .category = "NFC Card Readers", .disabled = !WITH_SCARD, }, { .title = "HID SmartCard Order Flip", .name = "scardflip", .desc = "Flips the order of detection for P1/P2", .type = OptionType::Bool, .hidden = !WITH_SCARD, .category = "NFC Card Readers", .disabled = !WITH_SCARD, }, { .title = "HID SmartCard Order Toggle", .name = "scardtoggle", .desc = "Toggles reader between P1/P2 using the NumLock key state", .type = OptionType::Bool, .hidden = !WITH_SCARD, .category = "NFC Card Readers", .disabled = !WITH_SCARD, }, { // HIDSmartCardIdConvert .title = "HID SmartCard Fix UID", .name = "scardfix", .desc = "Modify behavior of SmartCard UID logic.\n\n" "legacy: Preserve buggy old behavior (Default)\n\n" "fix: Add E00401 to non-FeliCa cards, scan FeliCa cards as-is (Recommended for most users)\n\n" "all: Add E00401 to all cards, including FeliCa cards (For really old games only)\n\n" "The algorithm is simple; the prefix is applied, the scanned card number is appended upto 8 bytes, and remaining digits are discarded. " "For example, abcd56780123 is converted to e00401abcd567801 on card in. This results in different card numbers so " "be careful when connecting to servers!", .type = OptionType::Enum, .hidden = !WITH_SCARD, .category = "NFC Card Readers", .elements = { {"legacy", "Use cards as-is"}, {"fix", "Fix bad cards only"}, {"all", "Force all cards"}, }, .disabled = !WITH_SCARD, }, { // SextetStreamPort .title = "SextetStream Port", .name = "sextet", .desc = "Use a SextetStream device on a given COM port", .type = OptionType::Text, .category = "I/O Options", }, { .title = "Enable BemaniTools 5 API", .name = "bt5api", .desc = "Enables partial BemaniTools 5 API compatibility layer", .type = OptionType::Bool, .category = "BT5 API", }, { .title = "Realtime Process Priority (DEPRECATED - use -processpriority instead)", .name = "realtime", .desc = "Sets the process priority to realtime, can help with odd lag spikes.", .type = OptionType::Bool, .hidden = true, .category = "Performance", }, { // spice2x_ProcessPriority .title = "Process Priority", .name = "sp2x-processpriority", .display_name = "processpriority", .aliases= "processpriority", .desc = "Sets the process priority, can help with odd lag spikes. Default: high", .type = OptionType::Enum, .category = "Performance", .elements = { {"belownormal", ""}, {"normal", ""}, {"abovenormal", ""}, {"high", ""}, {"realtime", ""} }, }, { // spice2x_ProcessAffinity .title = "Process Affinity", .name = "sp2x-processaffinity", .display_name = "processaffinity", .aliases= "processaffinity", .desc = "Set process affinity by calling SetProcessAffinityMask. " "Must provide a hexadecimal mask (e.g., 0x1ff00)", .type = OptionType::Hex, .category = "Performance", }, { // spice2x_ProcessorEfficiencyClass .title = "Process Efficiency Class", .name = "sp2x-processefficiency", .display_name = "processefficiency", .aliases= "processefficiency", .desc = "Has no effect if -processaffinity is set. Default: Use all cores", .type = OptionType::Enum, .category = "Performance", .elements = { {"all", "Use all cores"}, {"pcores", "Performant cores only"}, {"ecores", "Efficient cores only"}, }, }, { .title = "Heap Size", .name = "h", .desc = "Custom heap size in bytes", .type = OptionType::Integer, .category = "Development", }, // TODO: remove this and create an ignore list { // DisableGSyncDetection .title = "(REMOVED) Disable G-Sync Detection", .name = "keepgsync", .desc = "Broken feature that was not implemented correctly", .type = OptionType::Bool, .hidden = true, .category = "Graphics (Common)", }, { // spice2x_NvapiProfile .title = "NVIDIA profile optimization", .name = "sp2x-nvprofile", .display_name = "nvprofile", .aliases= "nvprofile", .desc = "Creates an NVIDIA application profile for spice(64).exe and applies GPU tweaks.\n\n" "Disables G-SYNC by setting the Monitor Technology to 'Fixed Refresh', " "sets v-sync to 'Application Controlled', " "and the Power Management Mode to 'Prefer maximum performance'.\n\n" "G-SYNC may fail to disable properly; try enabling full-screen optimizations (FSO) for spice(64).exe.\n\n" "May not work as expected for some games; namely G-SYNC and SDVX ", .type = OptionType::Bool, .category = "Graphics (Common)", }, { // DisableAudioHooks .title = "Disable All Spice Audio Hooks", .name = "audiohookdisable", .desc = "Disables all audio hooks, including device initialization and volume hooks.\n\n" "Default: off (allow Spice to hook IMMDeviceEnumerator for and optionally let " "you use a different backend instead of exclusive WASAPI).\n\n" "Check this if you want games to natively access your audio device", .type = OptionType::Bool, .category = "Audio (Hacks)", }, { // spice2x_DisableVolumeHook .title = "Disable Audio Volume Hook", .name = "sp2x-volumehookdisable", .display_name = "volumehookdisable", .aliases= "volumehookdisable", .desc = "Disables audio volume hook.\n\n" "Default: off (prevent games from changing audio volume by hooking IAudioEndpointVolume).\n\n" "Check this if you want games to freely change your volume", .type = OptionType::Bool, .category = "Audio (Hacks)", }, { .title = "Spice Audio Hook Backend", .name = "audiobackend", .desc = "Selects the audio backend to use when spice audio hook is enabled, overriding exclusive WASAPI. " "Does nothing for games that do not output to exclusive WASAPI", .type = OptionType::Enum, .category = "Audio", .elements = { {"asio", "ASIO"}, {"waveout", "broken, do not use"} }, }, { .title = "Spice Audio Hook ASIO Driver ID", .name = "asiodriverid", .desc = "Selects the ASIO driver id to use when Spice Audio Backend is set to ASIO", .type = OptionType::Integer, .category = "Audio", }, { .title = "WASAPI Dummy Context", .name = "audiodummy", .desc = "Uses a dummy `IAudioClient` context to maintain full audio control. " "This is automatically enabled when required and not normally needed", .type = OptionType::Bool, .hidden = true, .category = "Audio (Hacks)", }, { // DelayBy5Seconds .title = "Delay by 5 Seconds (DEPRECATED - use -sleepduration instead)", .name = "sleep", .desc = "Waits five seconds before starting the game", .type = OptionType::Bool, .hidden = true, // superseded by sp2x-sleep_duration .category = "Miscellaneous", }, { // spice2x_DelayByNSeconds .title = "Delay Game Launch", .name = "sp2x-sleepduration", .display_name = "sleepduration", .aliases= "sleepduration", .desc = "Wait for N seconds before starting the game", .type = OptionType::Integer, .category = "Miscellaneous", }, { .title = "Load KBT/KLD Stubs", .name = "stubs", .desc = "Enables loading kbt/kld stub files", .type = OptionType::Bool, .category = "Development", }, { .title = "Adjust Display Orientation (DEPRECATED - use -autoorientation instead)", .name = "adjustorientation", .desc = "Automatically adjust the orientation of your display in portrait games. " "WARNING: game may launch at incorrect refresh rate! Use in combination with " "-graphics-force-refresh flag to fix.", .type = OptionType::Bool, .hidden = true, // superseded by sp2x-autoorientation .category = "Graphics (Common)", }, { // spice2x_AutoOrientation .title = "Auto-rotate Display", .name = "sp2x-autoorientation", .display_name = "autoorientation", .aliases= "autoorientation", .desc = "Automatically adjust the orientation of your display when launched. " "WARNING: game may launch at incorrect refresh rate! Use in combination with " "-graphics-force-refresh and potentially either -9on12 or full-screen optimizations (FSO) to fix.", .type = OptionType::Enum, .category = "Graphics (Common)", // match graphics_orientation .elements = {{"0", "90 (CW)"}, {"1", "270 (CCW)"}}, }, { .title = "AVS Log Level", .name = "loglevel", .desc = "Set the level of detail for AVS log messages written to the log. Does not affect logging from spice", .type = OptionType::Enum, .category = "Debug Log", .elements = {{"fatal", ""}, {"warning", ""}, {"info", ""}, {"misc", ""}, {"all", ""}, {"disable", ""}}, }, { .title = "EA Automap", .name = "automap", .desc = "Enable automap in patch configuration", .type = OptionType::Bool, .category = "Network (Development)", }, { .title = "EA Netdump", .name = "netdump", .desc = "Enable automap in network dumping configuration", .type = OptionType::Bool, .category = "Network (Development)", }, { .title = "Discord RPC AppID Override", .name = "discordappid", .desc = "Set the discord RPC AppID override", .type = OptionType::Text, .category = "Development", }, { .title = "Blocking Logger", .name = "logblock", .desc = "Slower but safer logging used for debugging", .type = OptionType::Bool, .category = "Debug Log", }, { .title = "Debug CreateFile", .name = "createfiledebug", .desc = "Outputs CreateFile debug prints", .type = OptionType::Bool, .category = "Debug Log", }, { .title = "Verbose Graphics Logging", .name = "graphicsverbose", .desc = "Enable the verbose logging of graphics hook code", .type = OptionType::Bool, .category = "Debug Log", }, { .title = "Verbose AVS Logging", .name = "avsverbose", .desc = "Enable the verbose logging of AVS filesystem functions", .type = OptionType::Bool, .category = "Debug Log", }, { // AllowEA3Verbose .title = "Allow EA3 Verbose Debug Logging", .name = "ea3verbose", .desc = "This option is only useful for network developers; leave this OFF.\n\n" "By default, ea3/debug/verbose node is removed by spice to prevent exposing PCBID in the logs. " "When this is enabled, ea3/debug/verbose node in the XML will be kept and exposed to the game", .type = OptionType::Bool, .category = "Debug Log", }, { .title = "Disable Colored Output", .name = "nocolor", .desc = "Disable terminal colors for log outputs to console", .type = OptionType::Bool, .category = "Debug Log", }, { .title = "Disable ACP Hook", .name = "acphookdisable", .desc = "Force disable advanced code pages hooks for encoding", .type = OptionType::Bool, .category = "Development", }, { .title = "Disable Signal Handling", .name = "signaldisable", .desc = "Force disable signal handling", .type = OptionType::Bool, .category = "Development", }, { .title = "Disable Debug Hooks", .name = "dbghookdisable", .desc = "Disable hooks for debug functions (e.g. OutputDebugString)", .type = OptionType::Bool, .category = "Development", }, { .title = "Disable AVS VFS Drive Mount Redirection", .name = "avs-redirect-disable", .desc = "Disable D:/E:/F: AVS VFS mount redirection", .type = OptionType::Bool, .category = "Development", }, { .title = "Output PEB", .name = "pebprint", .desc = "Prints PEB on startup to console", .type = OptionType::Bool, .category = "Debug Log", }, { // DumpSystemInfo .title = "Dump System Information", .name = "sysdump", .desc = "Print system information to the log on startup. Default: basic", .type = OptionType::Enum, .category = "Debug Log", .elements = { {"none", "Nothing"}, {"basic", "OS, CPU, SMBIOS, GPU"}, {"all", "basic + HID devices"}, }, }, { .title = "QKS Arguments Override", .name = "qksargs", .desc = "Command line arguments passed to the game.", .type = OptionType::Text, .setting_name = "", .game_name = "QuizKnock STADIUM", .category = "Game Options (Advanced)", }, { .title = "CCJ Arguments Override", .name = "ccjargs", .desc = "Command line arguments passed to the game. " "If left blank, '-nomatchselect' is applied, which disables matchmaking debug menu", .type = OptionType::Text, .setting_name = "", .game_name = "Chase Chase Jokers", .category = "Game Options (Advanced)", }, { .title = "CCJ Mouse Trackball", .name = "ccjmousetb", .desc = "Use mouse for trackball input; disables any keyboard / joystick control for the trackball. "\ "Hold RMB and move the cursor to control the trackball", .type = OptionType::Bool, .game_name = "Chase Chase Jokers", .category = "Game Options", }, { .title = "CCJ Mouse Trackball Toggle", .name = "ccjmousetbt", .desc = "Instead of holding RMB, click RMB to toggle trackball input", .type = OptionType::Bool, .game_name = "Chase Chase Jokers", .category = "Game Options", }, { .title = "CCJ Trackball Sensitivity", .name = "ccjtrackballsens", .desc = "Adjust sensitivity of trackball. Affects both digital and analog input. Default: 10", .type = OptionType::Integer, .setting_name = "(0-255)", .game_name = "Chase Chase Jokers", .category = "Game Options", }, { .title = "MFG Arguments Override", .name = "mfgargs", .desc = "Command line arguments passed to the game.", .type = OptionType::Text, .setting_name = "", .game_name = "Mahjong Fight Girl", .category = "Game Options (Advanced)", }, { .title = "MFG Cabinet Type", .name = "mfgcabtype", .desc = "MFG Cabinet Type. Default is HG.", .type = OptionType::Enum, .setting_name = "", .game_name = "Mahjong Fight Girl", .category = "Game Options", .elements = { {"HG", "HG"}, {"B", "B"}, {"C", "C"}, {"UKS", "UKS"}, }, }, { .title = "MFG Disable IO Emulation", .name = "mfgnoio", .desc = "Disables BI2X hooks for MFG", .type = OptionType::Bool, .setting_name = "", .game_name = "Mahjong Fight Girl", .category = "Game Options (Advanced)" }, { // PCArgs .title = "PC Arguments Override", .name = "pcargs", .desc = "Command line arguments passed to the game.", .type = OptionType::Text, .setting_name = "", .game_name = "Polaris Chord", .category = "Game Options (Advanced)", }, { // PCNoIO .title = "PC Disable IO Emulation", .name = "pcnoio", .desc = "Disables BI2X hooks for PC", .type = OptionType::Bool, .setting_name = "", .game_name = "Polaris Chord", .category = "Game Options (Advanced)" }, { // PCKnobMode .title = "PC Fader Knobs Mode", .name = "pcknobs", .desc = "Allows SDVX knobs and IIDX turntables to be bound as Polaris Chord faders", .type = OptionType::Bool, .game_name = "Polaris Chord", .category = "Game Options", }, { // spice2x_LightsOverallBrightness .title = "Lights Brightness Adjustment", .name = "sp2x-lights-brightness", .display_name = "lightsbrightness", .aliases= "lightsbrightness", .desc = "Modify HID output values, measured in %. Default is 100. " "Requires compatible hardware, not just any LED", .type = OptionType::Integer, .setting_name = "(0-100)", .category = "I/O Options", }, { // spice2x_WindowBorder .title = "Window Border Style", .name = "sp2x-windowborder", .display_name = "windowborder", .aliases= "windowborder", .desc = "For windowed mode: overrides window border style. " "Does not work for all games; can possibly make the game crash! " "Can also be changed in Screen Resize window (default F11)", .type = OptionType::Enum, .category = "Graphics (Windowed)", // match cfg::WindowDecorationMode .elements = { {"0", "default"}, {"1", "borderless"}, {"2", "resizable window"}, }, }, { // spice2x_WindowSize .title = "Window Size", .name = "sp2x-windowsize", .display_name = "windowsize", .aliases= "windowsize", .desc = "For windowed mode: override for window size, width and height separated by comma. " "Can also be changed in Screen Resize window (default F11)", .type = OptionType::Text, .setting_name = "1280,720", .category = "Graphics (Windowed)", }, { // spice2x_WindowPosition .title = "Window Position", .name = "sp2x-windowpos", .display_name = "windowpos", .aliases= "windowpos", .desc = "For windowed mode: override for window position, x and y separated by comma. " "Can also be changed in Screen Resize window (default F11)", .type = OptionType::Text, .setting_name = "120,240", .category = "Graphics (Windowed)", }, { // spice2x_WindowAlwaysOnTop .title = "Window Always on Top", .name = "sp2x-windowalwaysontop", .display_name = "windowalwaysontop", .aliases= "windowalwaysontop", .desc = "For windowed mode: make window to be always on top of other windows. " "Can also be changed in Screen Resize window (default F11)", .type = OptionType::Bool, .category = "Graphics (Windowed)", }, { // WindowForceScaling .title = "Window Forced Render Scaling", .name = "windowscale", .desc = "For windowed mode: forcibly set DX9 back buffer dimensions to match window size. " "Reduces pixelated scaling artifacts. Works great for some games, but can COMPLETELY BREAK other games - YMMV!", .type = OptionType::Bool, .category = "Graphics (Windowed)", }, { // spice2x_IIDXWindowedSubscreenSize .title = "IIDX Windowed Subscreen Size", .name = "iidxwsubsize", .desc = "Size of the subscreen window. Defaults to (1280,720).", .type = OptionType::Text, .setting_name = "1280,720", .game_name = "Beatmania IIDX", .category = "Graphics (Windowed)", }, { // spice2x_IIDXWindowedSubscreenPosition .title = "IIDX Windowed Subscreen Position", .name = "iidxwsubpos", .desc = "Initial position of the subscreen window. Defaults to (0,0).", .type = OptionType::Text, .setting_name = "0,0", .game_name = "Beatmania IIDX", .category = "Graphics (Windowed)", }, { // IIDXWindowedSubscreenBorderless .title = "IIDX Windowed Subscreen Borderless", .name = "iidxwsubborderless", .desc = "Remove window decoration from windowed subscreen", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Graphics (Windowed)", }, { // IIDXWindowedSubscreenAlwaysOnTop .title = "IIDX Windowed Subscreen Always On Top", .name = "iidxwsubtop", .desc = "Keep windowed subscreen on top", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Graphics (Windowed)", }, { // spice2x_JubeatLegacyTouch .title = "JB Legacy Touch Targets", .name = "sp2x-jubeatlegacytouch", .display_name = "jubeatlegacytouch", .aliases= "jubeatlegacytouch", .desc = "For touch screen players - use the legacy & less accurate grid-based layout for touch recognition, " "instead of the new & more accurate touch targets. Default: off", .type = OptionType::Bool, .game_name = "Jubeat", .category = "Game Options", }, { // spice2x_RBTouchScale .title = "RB Scale Touch Input", .name = "sp2x-rbscaletouch", .display_name = "rbscaletouch", .aliases= "rbscaletouch", .desc = "Apply scaling to make touch area smaller than the screen. " "Specify a number out of 1000 (e.g., 800 means 80%). Default: 1000", .type = OptionType::Integer, .game_name = "Reflec Beat", .category = "Game Options", }, { // spice2x_AsioForceUnload .title = "ASIO Force Unload On Stop", .name = "sp2x-asioforceunload", .display_name = "asioforceunload", .aliases= "asioforceunload", .desc = "For certain buggy ASIO drivers, force unload of ASIO driver when audio stream stops. " "Used for working around ASIO drivers that lock up after force quitting games", .type = OptionType::Bool, .category = "Audio (Hacks)", }, { // spice2x_IIDXNoESpec .title = "IIDX Disable E-spec I/O", .name = "sp2x-iidxnoespec", .display_name = "iidxnoespec", .aliases= "iidxnoespec", .desc = "Disable I/O emulation for E-spec I/O upgrade (GELDJ-JX kit)", .type = OptionType::Bool, .hidden = true, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // spice2x_IIDXWindowedTDJ .title = "IIDX TDJ Windowed Mode (DEPRECATED - just use -iidxtdj and -w together)", .name = "sp2x-iidxtdjw", .display_name = "iidxtdjw", .aliases= "iidxtdjw", .desc = "Enable TDJ, optimized for windowed mode. " "Press Toggle Subscreen button and use your mouse on the main window.", .type = OptionType::Bool, .hidden = true, .game_name = "Beatmania IIDX", .category = "Game Options", }, { // spice2x_DRSDisableTouch .title = "DRS Disable Touch Input", .name = "sp2x-drsdisabletouch", .display_name = "drsdisabletouch", .aliases= "drsdisabletouch", .desc = "Disables multitouch input", .type = OptionType::Bool, .game_name = "DANCERUSH", .category = "Game Options", }, { // spice2x_DRSTransposeTouch .title = "DRS Transpose Touch Input", .name = "sp2x-drstransposetouch", .display_name = "drstransposetouch", .aliases= "drstransposetouch", .desc = "Swap x and y values of touch input", .type = OptionType::Bool, .game_name = "DANCERUSH", .category = "Game Options", }, { // spice2x_IIDXNativeTouch .title = "IIDX Native Touch Handling", .name = "sp2x-iidxnativetouch", .display_name = "iidxnativetouch", .aliases= "iidxnativetouch", .desc = "Disables touch hooks and lets the game access a touch screen directly. " "Requires a touch screen to be connected as a secondary monitor. " "Touch input must be routed to the primary screen via Windows Tablet PC settings. " "Enable this when you get duplicate touch inputs from an actual touch screen", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // spice2x_IIDXNoSub .title = "IIDX TDJ Subscreen Disable", .name = "sp2x-iidxnosub", .display_name = "iidxnosub", .aliases= "iidxnosub", .desc = "Prevents TDJ subscreen from launching a separate window.", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options", }, { // spice2x_IIDXEmulateSubscreenKeypadTouch .title = "IIDX TDJ Subscreen Keypad Touch Emulation", .name = "sp2x-iidxsubpoke", .display_name = "iidxsubpoke", .aliases = "iidxsubpoke", .desc = "Emulates touches on TDJ subscreen when keypad input is received. " "Should be used along with 'Unscramble Touch Screen Keypad in TDJ' patch. " "00 is mapped to erase button, and Decimal shows/hides the keypad", .type = OptionType::Bool, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // spice2x_AutoCard .title = "Auto Card Insert", .name = "sp2x-autocard", .display_name = "autocard", .aliases= "autocard", .desc = "Periodically present virtual card(s) to the reader, removing the need to press Insert Card button. " "Only applies to local virtual cards (-card0, -card1, or card0/1.txt), " "do not use with physical card reader or API.", .type = OptionType::Enum, .category = "Network", .elements = { {"off", ""}, {"p1", ""}, {"p2", ""}, {"both", ""}, }, }, { // spice2x_LowLatencySharedAudio .title = "Low Latency Shared Audio", .name = "sp2x-lowlatencysharedaudio", .display_name = "lowlatencysharedaudio", .aliases= "lowlatencysharedaudio", .desc = "Force the usage of smallest buffer size supported by the device when shared mode audio is used. " "Works for games using DirectSound or shared WASAPI; no effect for exclusive WASAPI and ASIO. " "For best results (under 10ms), use the default Windows inbox audio driver instead of manufacturer supplied driver. " "Requires Windows 10 and above", .type = OptionType::Bool, .category = "Audio", }, { // spice2x_TapeLedAlgorithm .title = "Tape LED Avg Algorithm", .name = "sp2x-tapeledalgo", .display_name = "tapeledalgo", .aliases= "tapeledalgo", .desc = "For games with light arrays, determine the algorithm that is used to translate them into a single light binding in Lights tab. " "Only applies to IIDX, SDVX, and DDR for now. Default: mid", .type = OptionType::Enum, .category = "I/O Options", .elements = { {"off", "Off"}, {"first", "First LED"}, {"mid", "Middle LED"}, {"last", "Last LED"}, {"avg", "Average color"}, }, }, { // spice2x_NoNVAPI .title = "NVAPI Block", .name = "sp2x-nonvapi", .display_name = "nonvapi", .aliases= "nonvapi", .desc = "Completely block the game from accessing NVAPI. Can be used if NVAPI is returning undesirable " "results to the game (e.g., wrong values from NvDisplayConfig for SDVX). You may need to apply additional " "hex edits in order to boot the game correctly", .type = OptionType::Bool, .category = "Graphics (Common)", }, { // spice2x_NoD3D9DeviceHook .title = "Disable D3D9 Device Hook (DEPRECATED - no longer needed for TDJ recording)", .name = "sp2x-nod3d9devhook", .display_name = "nod3d9devhook", .aliases= "nod3d9devhook", .desc = "This was previously required for TDJ play recording, but as of 2024-08-04, it is no longer needed. " "Completely disable all DXD9 device hook, preventing features like Spice overlay, subscreen window, " "screenshots, screen resize, touch emulation, streaming to Companion, etc.", .type = OptionType::Bool, .hidden = true, .category = "Graphics (Common)", }, { // spice2x_SDVXNoSub .title = "SDVX Subscreen Disable", .name = "sp2x-sdvxnosub", .display_name = "sdvxnosub", .aliases= "sdvxnosub", .desc = "Prevents VM subscreen from launching a separate window.", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Game Options", }, { // SDVXFullscreenLandscape .title = "(DISABLED) SDVX Landscape Mode (SDVX5+)", .name = "sdvxlandscape", .desc = "Option HIDDEN and DISABLED due to it affecting in-game camera angle for displaying lanes.\n\n" "Allows you to play in landscape by transposing resolution and applying image scaling.\n\n" "Works only for SDVX5 and above! This is identical to -forceresswap.\n\n" "Will launch at 1080p by default; strongly consider combining this with -forceres option to render at monitor native resolution", .type = OptionType::Bool, .hidden = true, .game_name = "Sound Voltex", .category = "Game Options", }, { // spice2x_EnableSMXStage .title = "StepManiaX Stage Lighting Support", .name = "sp2x-smx-stage", .display_name = "smxstage", .aliases= "smxstage", .desc = "StepmaniaX stage will show up as a device that can recieve lighting output. " "For configurator, restart spicecfg.exe after enabling this to have the device show up for binding", .type = OptionType::Bool, .category = "I/O Options", }, { // spice2x_EnableSMXDedicab .title = "StepManiaX Dedicated Cabinet Lighting Support", .name = "sp2x-smx-dedicab", .display_name = "smxdedicab", .aliases = "smxdedicab", .desc = "StepManiaX Dedicated Cabinet will show up as a device that can recieve lighting output. " "For configurator, restart spicecfg.exe after enabling this to have the device show up for binding", .type = OptionType::Bool, .category = "I/O Options" }, { // IIDXRecQuality .title = "IIDX NVENC Quality", .name = "iidxreccqp", .desc = "WARNING: double check if your network allows this & your hardware supports it.\n\n" "For TDJ Play Record feature, specify ConstQP quality settings for NVENC. Lower is higher quality but bigger file.\n\n" "Two options: specify a single number (20) to set CQ Level, or specify comma-separated numbers (23,25,20) to set P, B, I levels", .type = OptionType::Text, .game_name = "Beatmania IIDX", .category = "Game Options (Advanced)", }, { // MidiAlgoVer .title = "MIDI Note Input Algorithm", .name = "midialgo", .desc = "Remember to restart after changing this value.\n\n" "Switch algorithm used for MIDI note handling.\n\n" "auto (default): use v2 for most games, v2_drum for DrumMania and FutureTomTom\n\n" "v2: new delta-time logic in spice2x, timestamp every on/off events and apply min. sustain period to rapid on/off triggers, supports velocity threshold\n\n" "v2_drum: same as v2, but disable holds; better for some drum kits\n\n" "legacy: legacy poll-based spicetools logic, may behave incorrectly when the same input is mapped to multiple buttons, no support for velocity threshold", .type = OptionType::Enum, .category = "I/O Options", .elements = { {"auto", ""}, {"v2", ""}, {"v2_drum", ""}, {"legacy", ""}, }, }, { // MidiNoteSustain .title = "MIDI Note Min. Sustain", .name = "midisustain", .desc = "Remember to restart after changing this value. Only affects v2 MIDI algorithm. Does not affect spicecfg.\n\n" "Set the minimum duration (in milliseconds) each note is sustained, so that the game's I/O can pick up the input when polled.\n\n" "If this is too short, when game polls for input, it may fail to observe quick input (such as drums). " "If this is too long, rapid hits may be dropped due to coalescing.\n\n" "Recommended to keep at default of 20ms unless you have issues", .type = OptionType::Integer, .setting_name = "20", .category = "I/O Options", }, { // InputRequiresFocus .title = "Input Requires Focus", .name = "inputfocus", .desc = "Determine input behavior when game window is not in focus.\n\n" "never: allow input regardless of focus\n" "naive (default): only naive bindings check for window focus, rawinput binds are always allowed\n" "always: only process input when window is in focus", .type = OptionType::Enum, .category = "I/O Options", .elements = { {"never", ""}, {"naive", ""}, {"always", ""}, }, }, { // NostalgiaPoke .title = "Nost Screen Poke", .name = "nostpoke", .desc = "Enables emulation of touch gestures to make it easier to navigate the game without a touchscreen.\n\n" "This enables two things:\n" " 1. Swipe/Touch bindings in Buttons tab (Swipe Next Page, etc)\n" " 2. PIN pad bindings (make sure Unscramble Keypad patch is applied))", .type = OptionType::Bool, .game_name = "Nostalgia", .category = "Game Options", }, { // ForceBackBufferCount .title = "V-Sync Buffering", .name = "vsyncbuffer", .desc = "Using triple buffering may improve cases of micro-stuttering but increases input latency", .type = OptionType::Enum, .category = "Graphics (Common)", .elements = { {"2", "double buffer"}, {"3", "triple buffer"}, }, }, { // SDVXWindowedSubscreenSize .title = "SDVX Windowed Subscreen Size", .name = "sdvxwsubsize", .desc = "Size of the subscreen window. Defaults to (1920,1080)", .type = OptionType::Text, .setting_name = "1920,1080", .game_name = "Sound Voltex", .category = "Graphics (Windowed)", }, { // SDVXWindowedSubscreenPosition .title = "SDVX Windowed Subscreen Position", .name = "sdvxwsubpos", .desc = "Initial position of the subscreen window. Defaults to (0,0)", .type = OptionType::Text, .setting_name = "0,0", .game_name = "Sound Voltex", .category = "Graphics (Windowed)", }, { // SDVXWindowedSubscreenBorderless .title = "SDVX Windowed Subscreen Borderless", .name = "sdvxwsubborderless", .desc = "Remove window decoration from windowed subscreen", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Graphics (Windowed)", }, { // SDVXWindowedSubscreenAlwaysOnTop .title = "SDVX Windowed Subscreen Always On Top", .name = "sdvxwsubtop", .desc = "Keep windowed subscreen on top", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Graphics (Windowed)", }, { // LovePlusCamEnable .title = "LovePlus Camera Enable", .name = "lovepluscam", .desc = "Allow game to access camera; camera must be compatible with game", .type = OptionType::Bool, .game_name = "LovePlus", .category = "Game Options", }, { // LovePlusPrinterOutputPath .title = "LovePlus Printer Output Path", .name = "lpprinterpath", .desc = "Path to folder where images will be stored", .type = OptionType::Text, .game_name = "LovePlus", .category = "Game Options (Advanced)", }, { .title = "LovePlus Printer Output Clear", .name = "lpprinterclear", .desc = "Clean up saved images in the output directory on startup", .type = OptionType::Bool, .game_name = "LovePlus", .category = "Game Options (Advanced)", }, { .title = "LovePlus Printer Output Overwrite", .name = "lpprinteroverwrite", .desc = "Always overwrite the same file in output directory", .type = OptionType::Bool, .game_name = "LovePlus", .category = "Game Options (Advanced)", }, { // LovePlusPrinterOutputFormat .title = "LovePlus Printer Output Format", .name = "lpprinterformat", .desc = "File format for printer output", .type = OptionType::Text, .setting_name = "(png/bmp/tga/jpg)", .game_name = "LovePlus", .category = "Game Options (Advanced)", }, { .title = "LovePlus Printer JPG Quality", .name = "lpprinterjpgquality", .desc = "Quality setting in percent if JPG format is used", .type = OptionType::Integer, .setting_name = "(0-100)", .game_name = "LovePlus", .category = "Game Options (Advanced)", }, { // OptionConflictResolution .title = "Command Line Args Override", .name = "cmdoverride", .desc = "By default, option values in spicecfg take precedence over command-line args, for legacy compat.\n" "When this is specified in command line, command-line args take precedence instead", .type = OptionType::Bool, .category = "Development", .disabled = true, } }; const std::vector &launcher::get_categories(Options::OptionsCategory category) { switch (category) { case Options::OptionsCategory::API: return CATEGORY_ORDER_API; case Options::OptionsCategory::Basic: return CATEGORY_ORDER_BASIC; case Options::OptionsCategory::Advanced: return CATEGORY_ORDER_ADVANCED; case Options::OptionsCategory::Dev: return CATEGORY_ORDER_DEV; case Options::OptionsCategory::Everything: default: return CATEGORY_ORDER_NONE; } } const std::vector &launcher::get_option_definitions() { return OPTION_DEFINITIONS; } std::unique_ptr> launcher::parse_options(int argc, char *argv[]) { // generate options auto &definitions = get_option_definitions(); auto options = std::make_unique>(); options->reserve(definitions.size()); for (auto &definition : definitions) { // create aliases std::vector aliases; aliases.push_back(definition.name); if (!definition.aliases.empty()) { strsplit(definition.aliases, aliases, '/'); } // create option auto &option = options->emplace_back(definition, ""); // check if enabled for (int i = 1; i < argc; i++) { // ignore leading '-' characters auto argument = argv[i]; while (argument[0] == '-') { argument++; } // check aliases for (const auto &alias : aliases) { if (_stricmp(alias.c_str(), argument) == 0) { switch (definition.type) { case OptionType::Bool: { option.value_add("/ENABLED"); break; } case OptionType::Integer: { if (++i >= argc) { log_fatal("options", "missing parameter for -{}", alias); } else { // validate it is an integer char *p; strtol(argv[i], &p, 10); if (*p) { log_fatal("options", "parameter for -{} is not a number: {}", alias, argv[i]); } else { option.value_add(argv[i]); } } break; } case OptionType::Hex: { if (++i >= argc) { log_fatal("options", "missing parameter for -{}", alias); } else { // validate it is an integer try { auto _ = std::stoull(argv[i], nullptr, 16); } catch (const std::exception &ex) { log_fatal("options", "parameter for -{} is not a hex number: {}", alias, argv[i]); } } break; } case OptionType::Enum: case OptionType::Text: { if (++i >= argc) { log_fatal("options", "missing parameter for -{}", alias); } else { option.value_add(argv[i]); } break; } default: { log_warning("options", "unknown option type: {} (-{})", definition.type, alias); break; } } break; } } } } // positional arguments std::vector positional; for (int i = 1; i < argc; i++) { // check if enabled bool found = false; for (auto &definition : definitions) { // create aliases std::vector aliases; aliases.push_back(definition.name); if (!definition.aliases.empty()) { strsplit(definition.aliases, aliases, '/'); } // ignore leading '-' characters auto argument = argv[i]; while (argument[0] == '-') { argument++; } // check aliases for (const auto &alias : aliases) { if (_stricmp(alias.c_str(), argument) == 0) { found = true; switch (definition.type) { case OptionType::Bool: break; case OptionType::Integer: case OptionType::Hex: case OptionType::Text: case OptionType::Enum: i++; break; } break; } } // early quit if (found) { break; } } // positional argument if (!found && *argv[i] != '-') { positional.emplace_back(argv[i]); } } // game executable if (!positional.empty()) { options->at(launcher::Options::GameExecutable).value = positional[0]; } // return vector return options; } std::vector