Compare commits

...

27 Commits

Author SHA1 Message Date
sp2xdev c96f4330d2 Merge branch 'main' of https://github.com/spice2x/spice2x.github.io 2025-08-21 08:29:33 -07:00
sp2xdev 26a2a0f481 update changelog 2025-08-21 08:29:27 -07:00
bicarus-dev 11b9790e6c cfg: rearrange options (#357)
## Link to GitHub Issue, if one exists
n/a

## Description of change
In an attempt to make the Options tab smaller, move out more options to
advanced / development tabs.

## Testing
n/a
2025-08-19 02:41:56 -07:00
bicarus-dev 1dfcf8c087 ddr: check for common DLL errors (#355)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Check if the path to codecs directory contains non-ASCII characters, and
display a giant warning. This is to help with troubleshooting, since
registration of xactengine silently fails (creates a bad registry entry)
if the path contains non-ASCII chars.

Log the size of codec DLLs, and log any failures from `regsvr32`.

Detect the case where user specified `-exec gamemdx.dll` for DDR which
seems to be a common pitfall. This doesn't work because gamemdx.dll does
not have a DLL entry, but for some reason people are really tempted to
do this.

## Testing
Validated the error cases.
2025-08-18 00:00:38 -07:00
bicarus-dev 5c7ad0d51e ea3: supply default pcbid if not present (#356)
## Link to GitHub Issue, if one exists
#345 

## Description of change
If ea3 xml is missing pcbid node, AND there is no pcbid override, supply
a default value. Useful for quickly getting offline clients up &
running.

## Testing
Tested with/without pcbid override, with/without pcbid node in xml.
2025-08-17 23:52:15 -07:00
sp2xdev 587ae86341 update changelog 2025-08-16 19:22:28 -07:00
bicarus-dev 926f2ef2a0 ddr: enclose file path in quotes & use wchar when registering codecs (#354)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Account for the path to codec DLL having spaces in it and use wide
chars.

## Testing
Testing 32 and 64 bits.
2025-08-16 19:18:15 -07:00
sp2xdev d262e02f8c update changelog 2025-08-11 00:20:04 -07:00
bicarus-dev f6b63473a0 ddr: automatically register codecs on launch (#351)
## Link to GitHub Issue, if one exists
#345 

## Description of change
On launch of spice.exe / spice64.exe, call `regsvr32` on known codecs in
the `com` directory.

Add an option to skip this (`-ddrnocodec`) as a chicken bit.

## Testing
Tested 32 and 64 bit DDR.
2025-08-09 20:53:09 -07:00
bicarus-dev 1ad45edd6e sdvx: remove -sdvxdisablecams, simplify camera hook (#350)
## Link to GitHub Issue, if one exists
#345

## Description of change
Simplify camera hook in SDVX5/6. Camera hook will always be installed
and return 0 cameras.

Deprecate `-sdvxdisablecams` option as it is no longer needed as a
result of this change.

## Testing
Tested SDVX5 final, SDVX6 year 1, and a recent SDVX6 build.
2025-08-09 03:01:10 -07:00
bicarus-dev e79de3b117 Dump button and analog bindings to log on launch (#349)
## Link to GitHub Issue, if one exists
#345 

## Description of change
On launch of spice/spice64, dump bindings in Buttons and Analog tab

## Testing
Tested on 32 bit and 64 bit
2025-08-04 21:40:09 -07:00
sp2xdev f783a5a1cd clarify wording for -loglevel 2025-08-02 14:19:56 -07:00
sp2xdev 7848d5c237 detect custom msvcr100.dll 2025-07-25 18:36:55 -07:00
Emma cec735a81b Check for IFS LayeredFS after loading hooks (#342)
## Link to GitHub Issue, if one exists
N/A

## Description of change
Minor fix for a warning in the log. Moves the check for IFS LayeredFS
_after_ loading hooks. Also checks for the module with `GetModuleHandle`
instead, which should cover all paths as long as `ifs_hook.dll` hasn't
been renamed. 😅

## Testing

Before:
```
[2025/07/19 15:59:56] W:launcher: data_mods directory was found, but ifs_hook.dll is not present; your mods will not load
[2025/07/19 15:59:56] W:launcher: to fix this, download ifs_layeredfs and add it as a DLL hook (-k)
[2025/07/19 15:59:56] W:launcher: https://github.com/mon/ifs_layeredfs
[2025/07/19 15:59:56] I:launcher: msvcrt!__argc value before: 0
[2025/07/19 15:59:56] I:launcher: msvcrt!__argc value after: 57
[2025/07/19 15:59:56] I:launcher: loading hook DLL C:\Users\Emma\AppData\Local\Programs\SpiceTools\hooks\64\ifs_hook.dll
[2025/07/19 15:59:56] I:libraryhook: LibraryHook Attach
```

After changes - no warning observed:
```
[2025/07/19 15:59:08] I:launcher: msvcrt!__argc value before: 0
[2025/07/19 15:59:08] I:launcher: msvcrt!__argc value after: 57
[2025/07/19 15:59:08] I:launcher: loading hook DLL C:\Users\Emma\AppData\Local\Programs\SpiceTools\hooks\64\ifs_hook.dll
[2025/07/19 15:59:08] I:libraryhook: LibraryHook Attach
```
2025-07-19 16:17:07 -07:00
sp2xdev 753702e32e Fix build break due to upstream header file changes 2025-07-19 15:36:21 -07:00
sp2xdev 7bd7503951 Update README.md 2025-06-25 01:03:39 -07:00
sp2xdev d0c38956bf update changelog 2025-06-02 17:51:04 -07:00
bicarus-dev 77bca2baee patcher: add gitadora libshare-pj.dll to extra DLL list (#335)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Allow importing patches for `libshare-pj.dll`

## Testing
Observed patch for the new DLL being attempted to be imported in the
logs.
2025-06-02 02:56:04 -07:00
bicarus-dev 46e8c8a5a5 acio: detect crash during acio init due to RTSS/Afterburner and complain (#334)
## Link to GitHub Issue, if one exists
n/a

## Description of change
When an exception is caught during ACIO hook init, print a special
message to suggest the user to disable RTSS and MSI Afterburner, since
they are known to cause issues.

## Testing
Tried with RTSS enabled with High detection and with it disabled. Also
tried to crash the game a different way and check that the message is
not printed and the callstack is shown propeerly.
2025-06-01 14:52:13 -07:00
sp2xdev 4d58e5d080 Merge branch 'main' of https://github.com/spice2x/spice2x.github.io 2025-05-30 22:21:11 -07:00
sp2xdev 168084d672 update changelog 2025-05-30 22:21:03 -07:00
bicarus-dev 740ed90d95 iidx: fix windowed subscreen mouse clicks being ignored when overlay is active (#333)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Fix an issue when clicking on the TDJ windowed subscreen with overlay
active will occasionally drop all future mouse clicks, until the overlay
is completely hidden.

## Testing
Tested TDJ windowed subscreen, with poke, with auto-pin, and with
spicecompanion.

SDVX: this was never an issue for UFC mode, but this change doesn't make
any functional difference either.
2025-05-30 22:20:03 -07:00
sp2xdev 61c17f15b4 update changelog 2025-05-30 20:19:03 -07:00
bicarus-dev 99035f312f cfg: reorganize menu bar (#330)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add `Help` menu. 

Move Licenses and About dialog under Help.

Add links to the wiki.

Make game selection drop-down slightly wider to make better use of space
that has been freed up.

## Testing
tested in cfg (with/without game), and in-game overlay
2025-05-25 01:32:59 -07:00
bicarus-dev aafc916bb6 launcher: detect custom nvidia DLLs (#329)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Complain if nvcuda.dll, nvcuvid.dll, or nvEncodeAPI64.dll are found in
modules or in root directory. This would complain for spice-provided
stubs as well.

## Testing
2025-05-24 12:49:58 -07:00
bicarus-dev c5eb1e999b launcher: option to make command line args take precedence (#327)
## Link to GitHub Issue, if one exists
Fixes #190 

## Description of change
When `-cmdoverride` is specified, any command line arguments are taken
first before merging with what is in the config file.

## Testing
Still testing...
2025-05-15 23:26:38 -07:00
ASleepyCat 7c15452c1e Add support for outputting IIDX tape LED data via spiceapi (#315)
## Link to GitHub Issue, if one exists
#41 (partially)

## Description of change
This PR adds support for outputting IIDX tape LED data via spiceapi
(`iidx_tapeled_get()`). It returns the data as a `dict`, with the key
being the LED name and the value being the list of RGB values. You can
optionally pass in a variadic list of names to filter the returned data.

The new API function is only implemented in Python since I'm not able to
test the wrappers in other languages.

An example script using the new API is available in this Beef Board PR:
https://github.com/HWXLR8/beef-board/pull/133.

Demo use case: https://streamable.com/pq5e1e

## Testing
Tested in IIDX 32 with custom Beef Board firmware along with a Python
script to read and send the tape LED data over USB.
2025-05-12 21:27:20 -07:00
30 changed files with 576 additions and 314 deletions
+1
View File
@@ -36,6 +36,7 @@ please do not ask for these, as it will never happen here.
Rules for filing a new issue or adding comments to existing issues in the tracker:
* Low effort submissions will be simply deleted, and repeated attempts will get you banned.
* Check the [known issues](https://github.com/spice2x/spice2x.github.io/wiki/Known-issues) page first before reporting a new issue. If you have some new information or workarounds for a known issue, you can file a documentation bug as well.
* Use the search function and see if there is an existing issue.
* This is not the place to obtain a guide or receive basic troubleshooting.
+19
View File
@@ -247,6 +247,25 @@ which also means that your hex edits are applicable directly.
- sets the contents of the 16 segment display and disables writes from game
- ticker_reset()
- re-enables writes from game
- tapeled_get(name: str, ...)
- returns a list containing a dict of the current tape LED states. The dict keys are:
- `Stage Left`
- `Stage Right`
- `Cabinet Left`
- `Cabinet Right`
- `Control Panel Under`
- `Ceiling Left`
- `Title Left`
- `Title Right`
- `Ceiling Right`
- `Touch Panel Left`
- `Touch Panel Right`
- `Side Panel Left Inner`
- `Side Panel Left Outer`
- `Side Panel Left`
- `Side Panel Right Outer`
- `Side Panel Right Inner`
- `Side Panel Right`
#### LCD
- info()
+4
View File
@@ -41,6 +41,7 @@
namespace acio {
HINSTANCE DLL_INSTANCE = nullptr;
std::vector<acio::ACIOModule *> MODULES;
std::atomic<bool> IO_INIT_IN_PROGRESS = false;
}
/*
@@ -58,6 +59,7 @@ static inline acio::HookMode get_hookmode() {
void acio::attach() {
log_info("acio", "SpiceTools ACIO");
IO_INIT_IN_PROGRESS = true;
// load settings and instance
acio::DLL_INSTANCE = LoadLibraryA("libacio.dll");
@@ -111,6 +113,8 @@ void acio::attach() {
for (auto &module : MODULES) {
module->attach();
}
IO_INIT_IN_PROGRESS = false;
}
void acio::attach_icca() {
+2
View File
@@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <vector>
#include <windows.h>
@@ -11,6 +12,7 @@ namespace acio {
// globals
extern HINSTANCE DLL_INSTANCE;
extern std::vector<acio::ACIOModule *> MODULES;
extern std::atomic<bool> IO_INIT_IN_PROGRESS;
void attach();
void attach_icca();
+52 -4
View File
@@ -1,13 +1,9 @@
#include "iidx.h"
#include <functional>
#include <vector>
#include "games/iidx/iidx.h"
#include "external/rapidjson/document.h"
using namespace std::placeholders;
using namespace rapidjson;
namespace api::modules {
// settings
@@ -17,6 +13,11 @@ namespace api::modules {
functions["ticker_get"] = std::bind(&IIDX::ticker_get, this, _1, _2);
functions["ticker_set"] = std::bind(&IIDX::ticker_set, this, _1, _2);
functions["ticker_reset"] = std::bind(&IIDX::ticker_reset, this, _1, _2);
functions["tapeled_get"] = std::bind(&IIDX::tapeled_get, this, _1, _2);
for (auto &light : games::iidx::TAPELED_MAPPING) {
this->lights_by_names.emplace(light.lightName, light);
}
}
/**
@@ -69,4 +70,51 @@ namespace api::modules {
// disable read only
games::iidx::IIDXIO_LED_TICKER_READONLY = false;
}
/**
* tapeled_get()
* tapeled_get(name: str, ...)
*/
void IIDX::tapeled_get(Request &req, Response &res) {
Value response_object(kObjectType);
// all tape leds
if (req.params.Size() == 0) {
// Iterate through each device and dump its lights data into the response
for (const auto &mapping : games::iidx::TAPELED_MAPPING) {
copy_tapeled_data(res, response_object, mapping);
}
} else {
// specified light names
for (Value &param : req.params.GetArray()) {
// check params
if (!param.IsString()) {
error_type(res, "name", "string");
return;
}
const auto name = param.GetString();
if (const auto &it = lights_by_names.find(name); it != lights_by_names.end()) {
const auto mapping = it->second.get();
copy_tapeled_data(res, response_object, mapping);
}
}
}
res.add_data(response_object);
}
void IIDX::copy_tapeled_data(Response &res, Value &response_object, const tapeledutils::tape_led &mapping) {
// Create an array for the light state
Value light_state(kArrayType);
light_state.Reserve(mapping.data.capacity() * 3, res.doc()->GetAllocator());
for (const auto [r, g, b] : mapping.data) {
light_state.PushBack(r, res.doc()->GetAllocator());
light_state.PushBack(g, res.doc()->GetAllocator());
light_state.PushBack(b, res.doc()->GetAllocator());
}
// Can't use StringRef here, turns some strings partially into null bytes for some reason
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
}
}
+7
View File
@@ -2,6 +2,7 @@
#include "api/module.h"
#include "api/request.h"
#include "games/iidx/iidx.h"
namespace api::modules {
@@ -10,10 +11,16 @@ namespace api::modules {
IIDX();
private:
// state
robin_hood::unordered_map<std::string, std::reference_wrapper<tapeledutils::tape_led>> lights_by_names;
// function definitions
void ticker_get(Request &req, Response &res);
void ticker_set(Request &req, Response &res);
void ticker_reset(Request &req, Response &res);
void tapeled_get(Request &req, Response &res);
// helper
void copy_tapeled_data(Response &res, rapidjson::Value &response_object, const tapeledutils::tape_led &mapping);
};
}
@@ -16,3 +16,13 @@ def iidx_ticker_set(con: Connection, text: str):
def iidx_ticker_reset(con: Connection):
req = Request("iidx", "ticker_reset")
con.request(req)
def iidx_tapeled_get(con: Connection, *light_names):
req = Request("iidx", "tapeled_get")
for light_name in light_names:
req.add_param(light_name)
res = con.request(req)
return res.get_data()
+1
View File
@@ -4,6 +4,7 @@
#include <cstddef>
#include <string>
#include <windows.h>
#include <sys/stat.h>
namespace avs {
namespace core {
+7 -1
View File
@@ -356,6 +356,12 @@ namespace avs {
}
}
// fall back to default PCBID if node is not found
if (!EA3_PCBID[0] && PCBID_CUSTOM.empty()) {
log_warning("avs-ea3", "no PCBID set, falling back to default PCBID value (04040000000000000000)");
PCBID_CUSTOM = "04040000000000000000";
}
// custom PCBID
if (!PCBID_CUSTOM.empty()) {
@@ -399,7 +405,7 @@ namespace avs {
avs::core::NODE_TYPE_str, "/ea3/id/accountid", &EA3_ACCOUNTID);
}
// check if PCBID is defined
// check if PCBID is defined - should never hit, left for sanity check only
if (avs::core::property_search(ea3_config, nullptr, "/ea3/id/pcbid") == nullptr) {
log_fatal("avs-ea3", "node not found: /ea3/id/pcbid (try using -p to specify PCBID)");
} else if (strlen(EA3_PCBID) == 0) {
+18 -2
View File
@@ -80,7 +80,7 @@ namespace avs {
if (130 <= dll_path_s.length()) {
log_warning(
"avs-game",
"PATH TOO LONG WARNING\n\n\n"
"PATH TOO LONG WARNING\n\n"
"-------------------------------------------------------------------\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
@@ -95,10 +95,26 @@ namespace avs {
"-------------------------------------------------------------------\n\n",
dll_path_s, dll_path_s.length());
}
// ddr gamemdx.dll user error
if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") {
log_fatal(
"ddr",
"BAD GAME DLL ERROR\n\n"
"!!! !!!\n"
"!!! -exec gamemdx.dll was specified !!!\n"
"!!! this is the wrong DLL; the game will not load !!!\n"
"!!! remove -exec argument and try again. !!!\n"
"!!! !!!\n"
);
}
// file not found on disk
if (!fileutils::file_exists(dll_path)) {
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path.string().c_str());
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
}
if (fileutils::verify_header_pe(dll_path)) {
DLL_INSTANCE = libutils::load_library(dll_path);
}
@@ -106,7 +122,7 @@ namespace avs {
// load entry points
dll_entry_init = (ENTRY_INIT_T) libutils::get_proc(DLL_INSTANCE, ENTRY_INIT_NAME);
dll_entry_main = (ENTRY_MAIN_T) libutils::get_proc(DLL_INSTANCE, ENTRY_MAIN_NAME);
log_info("avs-game", "loaded successfully ({})", fmt::ptr(DLL_INSTANCE));
log_info("avs-game", "loaded {} successfully ({})", DLL_NAME, fmt::ptr(DLL_INSTANCE));
}
bool entry_init(char *sid_code, void *app_param) {
+1
View File
@@ -43,6 +43,7 @@ public:
std::string value;
std::vector<Option> alternatives;
bool disabled = false;
bool conflicting = false;
explicit Option(OptionDefinition definition, std::string value = "") :
definition(std::move(definition)), value(std::move(value)) {
+23
View File
@@ -1,3 +1,26 @@
08/21/2025 [spice2x]
DDR: check for common DLL errors
Supply default PCBID if absent
Rearrange options
08/17/2025 [spice2x]
DDR: fix codec registration failure when path contains spaces
08/11/2025 [spice2x]
SDVX: fix camera hook, remove -sdvxdisablecams
DDR: automatically register codecs in com folder on boot
06/03/2025 [spice2x]
Add libshare-pj.dll to Gitadora patch import
Detect ACIO init failures
05/31/2025 [spice2x]
IIDX: fix windowed subscreen not accepting mouse clicks when
overlay is active
IIDX: tape LED over API
Option to make command line args take precedence
UI tweaks
05/09/2025 [spice2x]
Check for window focus when processing input (-inputfocus)
More options for IIDX/SDVX windowed subscreen
+82
View File
@@ -27,6 +27,7 @@ namespace games::ddr {
// settings
bool SDMODE = false;
bool NO_CODEC_REGISTRATION = false;
uint8_t DDR_TAPELEDS[TAPELED_DEVICE_COUNT][50][3] {};
@@ -49,9 +50,79 @@ namespace games::ddr {
return SendMessage_real(hWnd, Msg, wParam, lParam);
}
bool contains_only_ascii(const std::string& str) {
for (auto c: str) {
if (static_cast<unsigned char>(c) > 127) {
return false;
}
}
return true;
}
DDRGame::DDRGame() : Game("Dance Dance Revolution") {
}
void DDRGame::register_codecs() {
// find where spice.exe / spice64.exe is located
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
// find the com directory
std::filesystem::path dir = "";
if (MODULE_PATH == spice_bin_path) {
// try: \com
dir = spice_bin_path / "com";
} else {
// try: modules\..\com
dir = MODULE_PATH / ".." / "com";
}
if (fileutils::dir_exists(dir)) {
log_info("ddr", "looking for codecs in this directory: {}", dir.string());
} else {
log_info("ddr", "codecs directory not found: {}", dir.string());
return;
}
for (const auto &file : std::filesystem::directory_iterator(dir)) {
const auto &filename = file.path().filename();
const auto extension = strtolower(filename.extension().string());
if (extension != ".dll") {
continue;
}
log_info("ddr", "found DLL: {}, size: {} bytes", filename.string(), file.file_size());
if (filename == "k-clvsd.dll" || filename.string().find("xactengine") == 0) {
const std::wstring wcmd = L"regsvr32.exe /s \"" + file.path().wstring() + L"\"";
const std::string cmd = "regsvr32.exe /s \"" + file.path().string() + "\"";
int result = 0;
std::thread t([wcmd, &result]() {
result = _wsystem(wcmd.c_str());
});
t.join();
if (result == 0) {
log_info("ddr", "`{}` returned {}", cmd, result);
} else {
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
}
if (!contains_only_ascii(file.path().string())) {
log_warning(
"ddr",
"BAD PATH ERROR\n\n\n"
"!!! !!!\n"
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
"!!! this may cause the game to crash! !!!\n"
"!!! !!!\n"
);
}
}
}
}
void DDRGame::pre_attach() {
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) {
log_fatal(
@@ -87,6 +158,17 @@ namespace games::ddr {
"!!! !!!\n\n\n"
);
}
if (!cfg::CONFIGURATOR_STANDALONE) {
if (!NO_CODEC_REGISTRATION) {
this->register_codecs();
} else {
log_warning(
"ddr",
"skipping codec registration (-ddrnocodec), "
"game may crash if you didn't register codecs before launching the game");
}
}
}
void DDRGame::attach() {
+4
View File
@@ -7,6 +7,7 @@ namespace games::ddr {
// settings
extern bool SDMODE;
extern bool NO_CODEC_REGISTRATION;
// Buffers to store RGB data for tape LEDs on gold cabinets
const size_t TAPELED_DEVICE_COUNT = 11;
@@ -18,5 +19,8 @@ namespace games::ddr {
virtual void pre_attach() override;
virtual void attach() override;
virtual void detach() override;
private:
void register_codecs();
};
}
+10 -33
View File
@@ -409,50 +409,27 @@ namespace games::iidx {
* 16 - side panel right - 183 bytes - 61 colors
*
* data is stored in RGB order, 3 bytes per color
*
* TODO: expose this data via API
*/
// data mapping
static struct TapeLedMapping {
size_t data_size;
int index_r, index_g, index_b;
TapeLedMapping(size_t data_size, int index_r, int index_g, int index_b)
: data_size(data_size), index_r(index_r), index_g(index_g), index_b(index_b) {}
} mapping[] = {
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB },
{ 19, Lights::StageRightAvgR, Lights::StageRightAvgG, Lights::StageRightAvgB },
{ 45, Lights::CabinetLeftAvgR, Lights::CabinetLeftAvgG, Lights::CabinetLeftAvgB },
{ 45, Lights::CabinetRightAvgR, Lights::CabinetRightAvgG, Lights::CabinetRightAvgB },
{ 21, Lights::ControlPanelUnderAvgR, Lights::ControlPanelUnderAvgG, Lights::ControlPanelUnderAvgB },
{ 54, Lights::CeilingLeftAvgR, Lights::CeilingLeftAvgG, Lights::CeilingLeftAvgB },
{ 11, Lights::TitleLeftAvgR, Lights::TitleLeftAvgG, Lights::TitleLeftAvgB },
{ 11, Lights::TitleRightAvgR, Lights::TitleRightAvgG, Lights::TitleRightAvgB },
{ 54, Lights::CeilingRightAvgR, Lights::CeilingRightAvgG, Lights::CeilingRightAvgB },
{ 17, Lights::TouchPanelLeftAvgR, Lights::TouchPanelLeftAvgG, Lights::TouchPanelLeftAvgB },
{ 17, Lights::TouchPanelRightAvgR, Lights::TouchPanelRightAvgG, Lights::TouchPanelRightAvgB },
{ 68, Lights::SidePanelLeftInnerAvgR, Lights::SidePanelLeftInnerAvgG, Lights::SidePanelLeftInnerAvgB },
{ 68, Lights::SidePanelLeftOuterAvgR, Lights::SidePanelLeftOuterAvgG, Lights::SidePanelLeftOuterAvgB },
{ 61, Lights::SidePanelLeftAvgR, Lights::SidePanelLeftAvgG, Lights::SidePanelLeftAvgB },
{ 68, Lights::SidePanelRightOuterAvgR, Lights::SidePanelRightOuterAvgG, Lights::SidePanelRightOuterAvgB },
{ 68, Lights::SidePanelRightInnerAvgR, Lights::SidePanelRightInnerAvgG, Lights::SidePanelRightInnerAvgB },
{ 61, Lights::SidePanelRightAvgR, Lights::SidePanelRightAvgG, Lights::SidePanelRightAvgB },
};
// check index bounds
if (tapeledutils::is_enabled() && index < std::size(mapping)) {
auto &map = mapping[index];
if (tapeledutils::is_enabled() && index < std::size(TAPELED_MAPPING)) {
auto &map = TAPELED_MAPPING[index];
const auto data_size = map.data.capacity();
// pick a color to use
const auto rgb = tapeledutils::pick_color_from_led_tape(data, map.data_size);
const auto rgb = tapeledutils::pick_color_from_led_tape(data, data_size);
// program the lights into API
auto &lights = get_lights();
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_r], rgb.r);
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
for (unsigned int i = 0; i < data_size; ++i) {
map.data[i].r = data[i * 3];
map.data[i].g = data[i * 3 + 1];
map.data[i].b = data[i * 3 + 2];
}
}
if (This != custom_node) {
+22
View File
@@ -28,6 +28,8 @@
#include "util/sigscan.h"
#include "util/utils.h"
#include "external/robin_hood.h"
#include "bi2a.h"
#include "bi2x_hook.h"
#include "ezusb.h"
@@ -74,6 +76,26 @@ namespace games::iidx {
bool IIDXIO_LED_TICKER_READONLY = false;
std::mutex IIDX_LED_TICKER_LOCK;
tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL] = {
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" },
{ 19, Lights::StageRightAvgR, Lights::StageRightAvgG, Lights::StageRightAvgB, "Stage Right" },
{ 45, Lights::CabinetLeftAvgR, Lights::CabinetLeftAvgG, Lights::CabinetLeftAvgB, "Cabinet Left" },
{ 45, Lights::CabinetRightAvgR, Lights::CabinetRightAvgG, Lights::CabinetRightAvgB, "Cabinet Right" },
{ 21, Lights::ControlPanelUnderAvgR, Lights::ControlPanelUnderAvgG, Lights::ControlPanelUnderAvgB, "Control Panel Under" },
{ 54, Lights::CeilingLeftAvgR, Lights::CeilingLeftAvgG, Lights::CeilingLeftAvgB, "Ceiling Left" },
{ 11, Lights::TitleLeftAvgR, Lights::TitleLeftAvgG, Lights::TitleLeftAvgB, "Title Left" },
{ 11, Lights::TitleRightAvgR, Lights::TitleRightAvgG, Lights::TitleRightAvgB, "Title Right" },
{ 54, Lights::CeilingRightAvgR, Lights::CeilingRightAvgG, Lights::CeilingRightAvgB, "Ceiling Right" },
{ 17, Lights::TouchPanelLeftAvgR, Lights::TouchPanelLeftAvgG, Lights::TouchPanelLeftAvgB, "Touch Panel Left" },
{ 17, Lights::TouchPanelRightAvgR, Lights::TouchPanelRightAvgG, Lights::TouchPanelRightAvgB, "Touch Panel Right" },
{ 68, Lights::SidePanelLeftInnerAvgR, Lights::SidePanelLeftInnerAvgG, Lights::SidePanelLeftInnerAvgB, "Side Panel Left Inner" },
{ 68, Lights::SidePanelLeftOuterAvgR, Lights::SidePanelLeftOuterAvgG, Lights::SidePanelLeftOuterAvgB, "Side Panel Left Outer" },
{ 61, Lights::SidePanelLeftAvgR, Lights::SidePanelLeftAvgG, Lights::SidePanelLeftAvgB, "Side Panel Left" },
{ 68, Lights::SidePanelRightOuterAvgR, Lights::SidePanelRightOuterAvgG, Lights::SidePanelRightOuterAvgB, "Side Panel Right Outer" },
{ 68, Lights::SidePanelRightInnerAvgR, Lights::SidePanelRightInnerAvgG, Lights::SidePanelRightInnerAvgB, "Side Panel Right Inner" },
{ 61, Lights::SidePanelRightAvgR, Lights::SidePanelRightAvgG, Lights::SidePanelRightAvgB, "Side Panel Right" },
};
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
if (lpSubKey != nullptr && phkResult != nullptr) {
if (hKey == HKEY_LOCAL_MACHINE &&
+8
View File
@@ -2,9 +2,13 @@
#include <mutex>
#include <optional>
#include <vector>
#include "games/game.h"
#include "external/robin_hood.h"
#include "util/tapeled.h"
namespace games::iidx {
// settings
@@ -30,6 +34,10 @@ namespace games::iidx {
extern bool IIDXIO_LED_TICKER_READONLY;
extern std::mutex IIDX_LED_TICKER_LOCK;
constexpr int IIDX_TAPELED_TOTAL = 17;
// data mapping
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
class IIDXGame : public games::Game {
public:
IIDXGame();
+6 -116
View File
@@ -14,134 +14,24 @@
#include <mfidl.h>
#include "avs/game.h"
#include "hooks/cfgmgr32hook.h"
#include "util/detour.h"
#include "util/memutils.h"
#include "util/utils.h"
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
namespace games::sdvx {
static std::wstring CAMERA0_ID;
static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue,
UINT32 *pcchLength) {
// call the original
HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength);
// try first camera
wchar_t *pwc = nullptr;
if (CAMERA0_ID.length() == 23)
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
// check if camera could be identified
if (pwc) {
// fake the USB IDs
pwc[4] = L'2';
pwc[5] = L'8';
pwc[6] = L'8';
pwc[7] = L'c';
pwc[13] = L'0';
pwc[14] = L'0';
pwc[15] = L'0';
pwc[16] = L'2';
pwc[21] = L'0';
pwc[22] = L'0';
}
// return original result
return result;
}
static void hook_camera(IMFActivate* camera, size_t no, std::wstring camera_id, std::string camera_instance) {
// don't hook if camera 0 is already hooked
if (CAMERA0_ID.length() > 0)
return;
// save the camera ID
CAMERA0_ID = camera_id;
// cfgmgr hook
CFGMGR32_HOOK_SETTING camera_setting;
camera_setting.device_instance = 0xDEADBEEF;
camera_setting.parent_instance = ~camera_setting.device_instance;
camera_setting.device_id = "USB\\VEN_1022&DEV_7908";
camera_setting.device_node_id = "USB\\VID_288C&PID_0002&MI_00\\?&????????&?&????";
if (camera_instance.length() == 17) {
for (int i = 0; i < 17; i++) {
camera_setting.device_node_id[28 + i] = camera_instance[i];
}
}
cfgmgr32hook_add(camera_setting);
// save original method for later use
if (GetAllocatedString_orig == nullptr) {
GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString;
}
// hook allocated string method for camera identification
memutils::VProtectGuard camera_guard(camera->lpVtbl);
camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook;
}
static HRESULT WINAPI MFEnumDeviceSources_hook(IMFAttributes *pAttributes, IMFActivate ***pppSourceActivate,
UINT32 *pcSourceActivate) {
// call original function
HRESULT result_orig = MFEnumDeviceSources_orig(pAttributes, pppSourceActivate, pcSourceActivate);
// check for capture devices
if (FAILED(result_orig) || !*pcSourceActivate) {
return result_orig;
}
// iterate cameras
size_t cam_hook_num = 0;
for (size_t cam_num = 0; cam_num < *pcSourceActivate && cam_hook_num < 1; cam_num++) {
// flip
size_t cam_num_flipped = cam_num;
// get camera link
IMFActivate *camera = (*pppSourceActivate)[cam_num_flipped];
LPWSTR camera_link_lpwstr;
UINT32 camera_link_length;
if (SUCCEEDED(camera->lpVtbl->GetAllocatedString(
camera,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
&camera_link_lpwstr,
&camera_link_length))) {
// cut name to make ID
std::wstring camera_link_ws = std::wstring(camera_link_lpwstr);
std::wstring camera_id = camera_link_ws.substr(8, 23);
// get camera instance
std::string camera_link = ws2s(camera_link_ws);
std::string camera_instance = camera_link.substr(32, 17);
// hook the camera
hook_camera(camera, cam_hook_num, camera_id, camera_instance);
// increase camera hook number
cam_hook_num++;
}
}
// return result
return result_orig;
*pppSourceActivate = nullptr;
*pcSourceActivate = 0;
log_misc("sdvx", "MFEnumDeviceSources_hook called, returning 0 cameras");
return S_OK;
}
void camera_init() {
// camera media framework hook
MFEnumDeviceSources_orig = detour::iat_try(
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
log_info("sdvx", "installing camera hooks...");
detour::iat_try("MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
}
}
+6 -7
View File
@@ -39,7 +39,6 @@ namespace games::sdvx {
const char *ORIGINAL_ASIO_DEVICE_NAME = "XONAR SOUND CARD(64)";
// settings
bool DISABLECAMS = false;
bool NATIVETOUCH = false;
uint8_t DIGITAL_KNOB_SENS = 16;
SdvxOverlayPosition OVERLAY_POS = SDVX_OVERLAY_BOTTOM;
@@ -369,17 +368,17 @@ namespace games::sdvx {
winuser_hook_init(avs::game::DLL_INSTANCE);
// hook camera
if (!DISABLECAMS) {
camera_init();
}
camera_init();
// RGB CAMERA error ignore
if (!replace_pattern(
// RGB CAMERA error ignore for SDVX5
// SDVX5: boot sequence triggers camera error if camera is not detected
// SDVX6: boots fine, but game title screen in attract loop will have camera error (cosmetic only)
if (replace_pattern(
avs::game::DLL_INSTANCE,
"418D480484C074218D51FD",
"????????????9090??????",
0, 0)) {
log_info("sdvx", "did not find matching signature for camera error patch");
log_info("sdvx", "applied camera error patch (sdvx5)");
}
// remove log spam
-1
View File
@@ -17,7 +17,6 @@ namespace games::sdvx {
};
// settings
extern bool DISABLECAMS;
extern bool NATIVETOUCH;
extern uint8_t DIGITAL_KNOB_SENS;
extern std::optional<std::string> ASIO_DRIVER;
+130 -12
View File
@@ -150,6 +150,9 @@ static bool check_dll(const std::string &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
@@ -244,6 +247,11 @@ int main_implementation(int argc, char *argv[]) {
// 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()) {
@@ -285,6 +293,7 @@ int main_implementation(int argc, char *argv[]) {
cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config;
cfg_run = true;
}
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
options[launcher::Options::ServiceURL].is_active() &&
!cfg::CONFIGURATOR_STANDALONE) {
@@ -424,9 +433,6 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::LoadSoundVoltexModule].value_bool()) {
attach_sdvx = true;
}
if (options[launcher::Options::SDVXDisableCameras].value_bool()) {
games::sdvx::DISABLECAMS = true;
}
if (options[launcher::Options::SDVXNativeTouch].value_bool()) {
games::sdvx::NATIVETOUCH = true;
}
@@ -623,6 +629,9 @@ int main_implementation(int argc, char *argv[]) {
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;
}
@@ -1220,6 +1229,38 @@ int main_implementation(int argc, char *argv[]) {
}
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;
if (launcher::USE_CMD_OVERRIDE) {
log_warning(
"launcher",
"multiple values for -{}, command line args take precedence: {}",
option.get_definition().name,
option.value);
} else {
log_warning(
"launcher",
"multiple values for -{}, spicecfg values take precedence: {}",
option.get_definition().name,
option.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::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
@@ -1874,6 +1915,10 @@ int main_implementation(int argc, char *argv[]) {
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");
// complain loudly & early about dll load ordering issue
libutils::check_duplicate_dlls();
@@ -1895,6 +1940,21 @@ int main_implementation(int argc, char *argv[]) {
// 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;
@@ -2036,15 +2096,6 @@ int main_implementation(int argc, char *argv[]) {
networkhook_init();
}
// layeredfs
if (fileutils::dir_exists("data_mods") &&
!fileutils::file_exists("ifs_hook.dll") &&
!fileutils::file_exists(MODULE_PATH / "ifs_hook.dll")) {
log_warning("launcher", "data_mods directory was found, but ifs_hook.dll is not present; your mods will not load");
log_warning("launcher", "to fix this, download ifs_layeredfs and add it as a DLL hook (-k)");
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
}
update_msvcrt_args(argc, argv);
// load hooks
@@ -2058,6 +2109,14 @@ int main_implementation(int argc, char *argv[]) {
}
}
// layeredfs
if (fileutils::dir_exists("data_mods") &&
GetModuleHandleA("ifs_hook.dll") == nullptr) {
log_warning("launcher", "data_mods directory was found, but ifs_hook.dll is not present; your mods will not load");
log_warning("launcher", "to fix this, download ifs_layeredfs and add it as a DLL hook (-k)");
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
}
// apply patches
{
overlay::windows::PatchManager patch_manager(nullptr, true);
@@ -2280,6 +2339,65 @@ void update_msvcrt_args(int argc, char *argv[]) {
#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()
);
} else {
log_misc(
"rawinput", " [{}] dev={}, vkey={}, analogtype={}",
button->getName(),
button->getDeviceIdentifier(),
button->getVKey(),
button->getAnalogType()
);
}
for (auto& alt : button->getAlternatives()) {
if (alt.getVKey() == INVALID_VKEY) {
continue;
}
log_misc(
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
button->getName(),
alt.isNaive() ? "Naive" : alt.getDeviceIdentifier(),
alt.getVKey(),
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);
+82 -35
View File
@@ -17,7 +17,6 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
"Game Options",
"Common",
"Network",
"Overlay",
"Graphics (Common)",
"Graphics (Full Screen)",
"Graphics (Windowed)",
@@ -26,23 +25,28 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
static const std::vector<std::string> CATEGORY_ORDER_ADVANCED = {
"Game Options (Advanced)",
"Overlay",
"Network (Advanced)",
"Performance",
"Miscellaneous",
"Paths",
"Touch Parameters",
"I/O Options",
"NFC Card Readers",
};
static const std::vector<std::string> CATEGORY_ORDER_DEV = {
"Network Adapters",
"Paths",
"Network (Development)",
"Audio (Hacks)",
"I/O Modules",
"Development",
"Debug Log",
};
static const std::vector<std::string> 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!
@@ -81,6 +85,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Network",
},
{
// ServiceURL
.title = "EA Service URL",
.name = "url",
.desc = "Sets a custom service URL override",
@@ -142,6 +147,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Graphics (Windowed)",
},
{
// InjectHook
.title = "Inject DLL Hooks",
.name = "k",
.desc = "Multiple files are allowed; use multiple -k flags, or in SpiceCfg, separate by "
@@ -152,6 +158,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Common",
},
{
// EarlyInjectHook
.title = "Inject Early DLL Hooks",
.name = "z",
.desc = "Equivalent to 'Inject DLL Hooks' option, but ensures hooks are injected before "
@@ -263,7 +270,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "nolegacy",
.desc = "Disables legacy key activation in-game.",
.type = OptionType::Bool,
.category = "Miscellaneous",
.category = "I/O Options",
},
{
.title = "Discord Rich Presence",
@@ -305,7 +312,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.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 Adapters",
.category = "Network (Development)",
.sensitive = true,
},
{
@@ -313,21 +320,21 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "subnet",
.desc = "Force the use of an adapter with the specified subnet. Must also provide -network",
.type = OptionType::Text,
.category = "Network Adapters",
.category = "Network (Development)",
},
{
.title = "Disable Network Fixes",
.name = "netfixdisable",
.desc = "Force disables network fixes",
.type = OptionType::Bool,
.category = "Network Adapters",
.category = "Network (Development)",
},
{
.title = "HTTP/1.1",
.name = "http11",
.desc = "Sets EA3 http11 value",
.type = OptionType::Enum,
.category = "Network (Advanced)",
.category = "Network (Development)",
.elements = {{"0", "Off"}, {"1", "On"}},
},
{
@@ -335,7 +342,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "ssldisable",
.desc = "Prevents the SSL protocol from being registered",
.type = OptionType::Bool,
.category = "Network (Advanced)",
.category = "Network (Development)",
},
{
.title = "URL Slash",
@@ -350,7 +357,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "r",
.desc = "Set custom SOFTID override",
.type = OptionType::Text,
.category = "Network (Advanced)",
.category = "Network (Development)",
.sensitive = true,
},
{
@@ -668,6 +675,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
// SDVXPrinterOutputPath
.title = "SDVX Printer Output Path",
.name = "printerpath",
.desc = "Path to folder where images will be stored",
@@ -692,6 +700,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
// SDVXPrinterOutputFormat
.title = "SDVX Printer Output Format",
.name = "printerformat",
.desc = "File format for printer output",
@@ -710,10 +719,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
.title = "SDVX Disable Cameras",
.title = "SDVX Disable Cameras (DEPRECATED - no longer needed)",
.name = "sdvxdisablecams",
.desc = "Disables cameras",
.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",
},
@@ -799,6 +811,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.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",
@@ -1311,6 +1332,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Touch Parameters",
},
{
// ICCAReaderPort
.title = "ICCA Reader Port",
.name = "reader",
.desc = "Connects to and uses a ICCA on a given COM port",
@@ -1387,11 +1409,12 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
},
{
// SextetStreamPort
.title = "SextetStream Port",
.name = "sextet",
.desc = "Use a SextetStream device on a given COM port",
.type = OptionType::Text,
.category = "Miscellaneous",
.category = "I/O Options",
},
{
.title = "Enable BemaniTools 5 API",
@@ -1492,7 +1515,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"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",
.category = "Audio (Hacks)",
},
{
// spice2x_DisableVolumeHook
@@ -1504,7 +1527,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"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",
.category = "Audio (Hacks)",
},
{
.title = "Spice Audio Hook Backend",
@@ -1529,7 +1552,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"This is automatically enabled when required and not normally needed",
.type = OptionType::Bool,
.hidden = true,
.category = "Audio",
.category = "Audio (Hacks)",
},
{
// DelayBy5Seconds
@@ -1582,11 +1605,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.elements = {{"0", "90 (CW)"}, {"1", "270 (CCW)"}},
},
{
.title = "Log Level",
.title = "AVS Log Level",
.name = "loglevel",
.desc = "Set the level of detail that gets written to the log",
.desc = "Set the level of detail for AVS log messages written to the log. Does not affect logging from spice",
.type = OptionType::Enum,
.category = "Performance",
.category = "Debug Log",
.elements = {{"fatal", ""}, {"warning", ""}, {"info", ""}, {"misc", ""}, {"all", ""}, {"disable", ""}},
},
{
@@ -1594,14 +1617,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "automap",
.desc = "Enable automap in patch configuration",
.type = OptionType::Bool,
.category = "Development",
.category = "Network (Development)",
},
{
.title = "EA Netdump",
.name = "netdump",
.desc = "Enable automap in network dumping configuration",
.type = OptionType::Bool,
.category = "Development",
.category = "Network (Development)",
},
{
.title = "Discord RPC AppID Override",
@@ -1615,35 +1638,35 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "logblock",
.desc = "Slower but safer logging used for debugging",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Debug CreateFile",
.name = "createfiledebug",
.desc = "Outputs CreateFile debug prints",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Verbose Graphics Logging",
.name = "graphicsverbose",
.desc = "Enable the verbose logging of graphics hook code",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Verbose AVS Logging",
.name = "avsverbose",
.desc = "Enable the verbose logging of AVS filesystem functions",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Disable Colored Output",
.name = "nocolor",
.desc = "Disable terminal colors for log outputs to console",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Disable ACP Hook",
@@ -1678,7 +1701,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "pebprint",
.desc = "Prints PEB on startup to console",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
// DumpSystemInfo
@@ -1686,7 +1709,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "sysdump",
.desc = "Print system information to the log on startup. Default: basic",
.type = OptionType::Enum,
.category = "Development",
.category = "Debug Log",
.elements = {
{"none", "Nothing"},
{"basic", "OS, CPU, SMBIOS, GPU"},
@@ -1916,7 +1939,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.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",
.category = "Audio (Hacks)",
},
{
// spice2x_IIDXNoESpec
@@ -2260,6 +2283,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options",
},
{
// LovePlusPrinterOutputPath
.title = "LovePlus Printer Output Path",
.name = "lpprinterpath",
.desc = "Path to folder where images will be stored",
@@ -2284,6 +2308,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
// LovePlusPrinterOutputFormat
.title = "LovePlus Printer Output Format",
.name = "lpprinterformat",
.desc = "File format for printer output",
@@ -2301,6 +2326,16 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.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<std::string> &launcher::get_categories(Options::OptionsCategory category) {
@@ -2482,12 +2517,25 @@ std::vector<Option> launcher::merge_options(
auto &new_option = merged.emplace_back(option.get_definition(), "");
new_option.disabled = true;
for (auto &value : option.values()) {
new_option.value_add(value);
}
for (auto &value : override.values()) {
new_option.value_add(value);
if (USE_CMD_OVERRIDE) {
// command-line arguments take precedence (opt-in)
for (auto &value : override.values()) {
new_option.value_add(value);
}
for (auto &value : option.values()) {
new_option.value_add(value);
}
} else {
// spicecfg options take precedence (default)
// this sucks, but it's the default for legacy spicetools compat
for (auto &value : option.values()) {
new_option.value_add(value);
}
for (auto &value : override.values()) {
new_option.value_add(value);
}
}
new_option.conflicting = true;
} else {
auto &new_option = merged.emplace_back(override.get_definition(), "");
new_option.disabled = true;
@@ -2503,7 +2551,6 @@ std::vector<Option> launcher::merge_options(
}
}
}
return merged;
}
+4
View File
@@ -86,6 +86,7 @@ namespace launcher {
spice2x_SDVXSubRedraw,
LoadDDRModule,
DDR43Mode,
DDRSkipCodecRegisteration,
LoadPopnMusicModule,
PopnMusicForceHDMode,
PopnMusicForceSDMode,
@@ -246,6 +247,7 @@ namespace launcher {
LovePlusPrinterOutputOverwrite,
LovePlusPrinterOutputFormat,
LovePlusPrinterJPGQuality,
OptionConflictResolution,
};
enum class OptionsCategory {
@@ -257,6 +259,8 @@ namespace launcher {
};
}
extern bool USE_CMD_OVERRIDE;
const std::vector<std::string> &get_categories(Options::OptionsCategory category);
const std::vector<OptionDefinition> &get_option_definitions();
std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]);
+14
View File
@@ -6,6 +6,7 @@
#include <windows.h>
#include <dbghelp.h>
#include "acio/acio.h"
#include "external/stackwalker/stackwalker.h"
#include "hooks/libraryhook.h"
#include "launcher/shutdown.h"
@@ -97,6 +98,19 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
// print signal
log_warning("signal", "exception raised: {}", exception_code(ExceptionRecord));
// check ACIO init failures
if (acio::IO_INIT_IN_PROGRESS) {
log_warning(
"signal",
"exception raised during ACIO init, this usually happens when a third party application interferes with hooks");
log_warning(
"signal",
" please check for the following, disable them, and try launching the game again:");
log_warning(
"signal",
" RivaTuner Statistics Server (RTSS), MSI Afterburner, kernel mode anti-cheat");
}
// walk the exception chain
struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord;
while (record_cause != nullptr) {
+15 -2
View File
@@ -12,6 +12,7 @@
#include "touch/touch.h"
#include "util/logging.h"
#include "util/utils.h"
#include "hooks/graphics/graphics.h"
#if !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) || \
!defined(IMGUI_DISABLE_DEFAULT_ALLOCATORS) || \
@@ -175,10 +176,22 @@ static void ImGui_ImplSpice_UpdateMousePos() {
static_cast<int>(pos.y / io.DisplaySize.y * window_size.y));
}
const auto active_window = ::GetForegroundWindow();
// if the main focus is a windowed subscreen, put the imgui cursor in a place that won't
// trigger any overlay, don't process anything else
const auto is_windowed_subscreen =
(GRAPHICS_IIDX_WSUB && active_window == TDJ_SUBSCREEN_WINDOW) ||
(active_window == SDVX_SUBSCREEN_WINDOW);
if (is_windowed_subscreen) {
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
return;
}
// set mouse position
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
POINT pos;
if (HWND active_window = ::GetForegroundWindow()) {
if (active_window) {
if (active_window == g_hWnd
|| ::IsChild(active_window, g_hWnd)
|| ::IsChild(g_hWnd, active_window)
@@ -208,7 +221,7 @@ static void ImGui_ImplSpice_UpdateMousePos() {
static size_t delay_touch = 0;
static size_t delay_touch_target = 2;
static DWORD last_touch_id = ~0u;
if (!touch_points.empty()) {
if (!touch_points.empty() && !is_windowed_subscreen) {
// use the first touch point
auto &tp = touch_points[0];
+26 -96
View File
@@ -16,7 +16,6 @@
#include "avs/ea3.h"
#include "avs/game.h"
#include "launcher/launcher.h"
#include "launcher/shutdown.h"
#include "launcher/options.h"
#include "misc/eamuse.h"
#include "overlay/imgui/extensions.h"
@@ -2914,7 +2913,7 @@ namespace overlay::windows {
ImGui::TextUnformatted("");
if (ImGui::Button(PROJECT_URL)) {
launch_shell(PROJECT_URL, nullptr);
launch_shell(PROJECT_URL);
}
ImGui::TextUnformatted("");
@@ -2979,31 +2978,28 @@ namespace overlay::windows {
void Config::build_menu(int *game_selected) {
bool about_popup = false;
bool licenses_popup = false;
bool shutdown_popup = false;
if (ImGui::BeginMenuBar()) {
// [spice2x]
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.f, 0.f, 1.f));
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 0.f, 0.f, 1.f));
ImGui::BeginDisabled(!cfg::CONFIGURATOR_STANDALONE);
if (ImGui::MenuItem("[spice2x]")) {
launch_shell(PROJECT_URL, nullptr);
if (ImGui::BeginMenu("[spice2x]")) {
ImGui::PopStyleColor();
if (ImGui::MenuItem("spice2x.github.io")) {
launch_shell(PROJECT_URL);
}
if (ImGui::MenuItem("Licenses")) {
licenses_popup = true;
}
if (ImGui::MenuItem("About")) {
about_popup = true;
}
ImGui::EndMenu();
} else {
ImGui::PopStyleColor();
}
ImGui::EndDisabled();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::Separator();
// game selector
ImGui::PushItemWidth(MIN(580, MAX(80, ImGui::GetWindowSize().x - 520)));
ImGui::Combo("##game_selector", game_selected, games_names.data(), (int)games_list.size());
ImGui::PopItemWidth();
ImGui::Separator();
// shortcuts
if (ImGui::BeginMenu("Shortcuts", cfg::CONFIGURATOR_STANDALONE)) {
if (ImGui::BeginMenu("Shortcuts")) {
if (ImGui::MenuItem("USB Game Controllers")) {
launch_shell("control.exe", "joy.cpl");
}
@@ -3016,32 +3012,21 @@ namespace overlay::windows {
ImGui::EndMenu();
}
// popup menus
if (ImGui::MenuItem("Licenses")) {
licenses_popup = true;
}
if (ImGui::MenuItem("About")) {
about_popup = true;
}
// power - only active in games
if (ImGui::BeginMenu("Power", !cfg::CONFIGURATOR_STANDALONE)) {
if (ImGui::MenuItem("Restart Game")) {
launcher::restart();
// help
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("FAQ")) {
launch_shell("https://github.com/spice2x/spice2x.github.io/wiki/Known-issues");
}
if (ImGui::MenuItem("Exit Game")) {
launcher::shutdown();
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::MenuItem("Shutdown PC")) {
shutdown_popup = true;
if (ImGui::MenuItem("Wiki")) {
launch_shell("https://github.com/spice2x/spice2x.github.io/wiki");
}
ImGui::EndMenu();
}
ImGui::Separator();
// game selector
ImGui::PushItemWidth(MIN(700, MAX(100, ImGui::GetWindowSize().x - 400)));
ImGui::Combo("##game_selector", game_selected, games_names.data(), (int)games_list.size());
ImGui::PopItemWidth();
ImGui::BeginDisabled();
if (!avs::game::is_model("000")) {
@@ -3059,45 +3044,9 @@ namespace overlay::windows {
if (licenses_popup) {
ImGui::OpenPopup("Licenses##topbarpopup");
}
if (shutdown_popup) {
ImGui::OpenPopup("System##topbarpopup");
}
// draw popups
{
// unused_open is needed for close button to appear on the popup
bool unused_open = true;
if (ImGui::BeginPopupModal(
"System##topbarpopup",
&unused_open,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize)) {
static bool force_shutdown = false;
ImGui::Spacing();
ImGui::Checkbox("Force", &force_shutdown);
ImGui::Spacing();
const ImVec2 button_size(100.f, 0.f);
ImGui::Spacing();
if (ImGui::Button("Shutdown PC", button_size)) {
this->shutdown_system(force_shutdown, false);
}
ImGui::Spacing();
if (ImGui::Button("Reboot PC", button_size)) {
this->shutdown_system(force_shutdown, true);
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button("Cancel", button_size)) {
ImGui::CloseCurrentPopup();
}
ImGui::Spacing();
ImGui::EndPopup();
}
const ImVec2 popup_size(
std::min(ImGui::GetIO().DisplaySize.x * 0.9f, 800.f),
std::min(ImGui::GetIO().DisplaySize.y * 0.9f, 800.f));
@@ -3122,23 +3071,4 @@ namespace overlay::windows {
}
}
}
void Config::shutdown_system(bool force, bool reboot_instead) {
if (!acquire_shutdown_privs()) {
return;
}
UINT flags = 0;
if (force) {
flags |= EWX_FORCE;
}
if (reboot_instead) {
flags |= EWX_REBOOT;
} else {
flags |= EWX_SHUTDOWN | EWX_HYBRID_SHUTDOWN;
}
if (!ExitWindowsEx(flags, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE)) {
return;
}
launcher::shutdown(0);
}
}
+1 -1
View File
@@ -95,7 +95,7 @@ namespace overlay::windows {
void build_about();
void build_licenses();
void build_launcher();
void launch_shell(LPCSTR app, LPCSTR file);
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
static void build_page_selector(int *page);
void build_menu(int *game_selected);
@@ -80,7 +80,7 @@ namespace overlay::windows {
{"arkmmd.dll", {"gamemmd.dll"}},
{"arkklp.dll", {"lpac.dll"}},
{"arknck.dll", {"weac.dll"}},
{"gdxg.dll", {"game.dll"}}
{"gdxg.dll", {"game.dll", "libshare-pj.dll"}}
};
static size_t url_recent_idx = -1;
+9 -3
View File
@@ -240,8 +240,11 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
rawinput::touch::display_update();
}
const auto is_windowed_sub =
(GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) || (hWnd == SDVX_SUBSCREEN_WINDOW);
if (msg == WM_CLOSE) {
if ((GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) || (hWnd == SDVX_SUBSCREEN_WINDOW)) {
if (is_windowed_sub) {
log_misc("touch", "ignore WM_CLOSE for subscreen window");
return false;
}
@@ -461,11 +464,14 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
};
// check if imgui is handling this mouse event
if (overlay::OVERLAY != nullptr && overlay::OVERLAY->get_active() && ImGui::GetIO().WantCaptureMouse) {
if (is_windowed_sub) {
// do nothing, don't let imgui hijack clicks on the sub window
result.action = ACTION_PASS;
} else if (overlay::OVERLAY != nullptr && overlay::OVERLAY->get_active() && ImGui::GetIO().WantCaptureMouse) {
result.action = ACTION_RETURN_DEFAULT;
} else if (TOUCH_HANDLER != nullptr) {
// call touch handler
TOUCH_HANDLER->handle_message(result, hWnd, msg, wParam, lParam);
}
+11
View File
@@ -1,6 +1,8 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace tapeledutils {
@@ -20,6 +22,15 @@ namespace tapeledutils {
float b;
} rgb_float3_t;
struct tape_led {
std::vector<rgb_float3_t> data;
int index_r, index_g, index_b; // Averaged RGB light output indexes
std::string lightName;
tape_led(size_t data_size, int index_r, int index_g, int index_b, std::string lightName)
: data(std::vector<rgb_float3_t>(data_size)), index_r(index_r), index_g(index_g), index_b(index_b), lightName(std::move(lightName)) {}
};
bool is_enabled();
rgb_float3_t pick_color_from_led_tape(uint8_t *data, size_t data_size);
size_t get_led_index_using_avg_algo(size_t data_size);