Compare commits

..

13 Commits

Author SHA1 Message Date
bicarus-dev 88ec6dbcee launcher: hide pcbid when -cmdoverride specified (#420)
## Link to GitHub Issue, if one exists
Fixes #419 

## Description of change
-cmdoverride caused pcbid to be logged in plain text

## Testing
2025-11-07 14:20:37 -08:00
bicarus-dev 1b614c94a7 mfc: remove log spam (#418)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Remove mutex lock/unlock log spam

Are they important? Idk

## Testing
2025-11-06 17:51:39 -08:00
bicarus-dev 37c5913f69 mfc: detect 2025 version (#416)
## Link to GitHub Issue, if one exists
n/a

## Description of change
They moved some DLLs around so MFC wasn't being auto-detected, and I/O
hooks were not being performed. Fix that.

## Testing
The game boots into test menu, some i/o works, touch doesn't, not
playable yet.
2025-11-05 19:23:10 -08:00
wrigglebug 2eca668593 update readme.txt (#415)
please merge this important code
2025-11-02 23:29:48 -08:00
bicarus-dev aec1da9d16 troubleshooter: detect ea3_report_posev error (#413)
## Link to GitHub Issue, if one exists
#345 

## Description of change
When `ea3-pos: ea3_report_posev: no such node:` is detected, add a
deferred error message and guide user to the FAQ.

## Testing
Tested SDVX with the missing XML node.
2025-11-02 14:13:04 -08:00
sp2xdev 548b441f1f update build script 2025-11-01 17:33:40 -07:00
bicarus-dev 98bc285d98 logger: show message popup for log_fatal (#412)
## Link to GitHub Issue, if one exists
#410 

## Description of change
When raising fatal errors via `log_fatal`, also show a modal popup.

Update instances of `log_fatal` where the message was too long to fit.

## Testing
Tested various failure conditions.
2025-11-01 13:41:58 -07:00
bicarus-dev 553c180bee signal: force minimize widnows before showing crash handler dialog (#411)
## Link to GitHub Issue, if one exists
#410 

## Description of change
Forcibly minimize all windows before showing the crash dialog. Without
this, the dialog shows "behind" the game when launching multi-monitor
fullscreen games (TDJ, VM)

## Testing
Tested TDJ and VM in fullscreen.
2025-11-01 02:08:14 -07:00
bicarus-dev 537252440e signal: show Windows popup window on crash (#410)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Show a MessageBox when signal detects a crash.

## Testing
Tested full screen and windowed games.
2025-10-31 20:54:56 -07:00
sp2xdev 13a0877d38 fix up deferred messages formatting 2025-10-28 16:49:14 -07:00
bicarus-dev 50691b79f1 troubleshooter: add cases for audio hook and dx9on12 (#408)
## Link to GitHub Issue, if one exists
#345

## Description of change
Add auto-troubleshooter messages for when audio hooks are disabled, and
when 9on12 is turned on by user.

## Testing
Manual verification
2025-10-28 16:29:49 -07:00
bicarus-dev ad229dabdb update build script (#407)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Create a "full" version of the archive with api and extra binaries. The
normal archive remains in place with just src and stubs.
2025-10-27 23:00:32 -07:00
bicarus-dev bdff33a420 overlay: fix font loading (#406)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Fix font loading - when the code was refactored in #286 I used the wrong
file extension for couple of them.
2025-10-20 00:53:28 -07:00
19 changed files with 230 additions and 55 deletions
+2 -1
View File
@@ -1503,7 +1503,8 @@ namespace avs {
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n" " * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
"\n" "\n"
, DLL_NAME, MODULE_PATH.string()) }; , DLL_NAME, MODULE_PATH.string()) };
log_fatal("avs-ea3", "{}", info_str); log_warning("avs-ea3", "{}", info_str);
log_fatal("avs-ea3", "Failed to find critical avs DLL on disk (avs2-core.dll OR {})", DLL_NAME);
} }
} }
+3 -1
View File
@@ -150,7 +150,9 @@ namespace avs {
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n" " * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
"\n" "\n"
, DLL_NAME, MODULE_PATH.string()) }; , DLL_NAME, MODULE_PATH.string()) };
log_fatal("avs-ea3", "{}", info_str);
log_warning("avs-ea3", "{}", info_str);
log_fatal("avs-ea3", "Failed to find critical ea3 DLL on disk (avs2-ea3.dll OR {})", DLL_NAME);
} }
} }
+2 -1
View File
@@ -98,7 +98,7 @@ namespace avs {
// ddr gamemdx.dll user error // ddr gamemdx.dll user error
if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") { if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") {
log_fatal( log_warning(
"ddr", "ddr",
"BAD GAME DLL ERROR\n\n" "BAD GAME DLL ERROR\n\n"
"!!! !!!\n" "!!! !!!\n"
@@ -107,6 +107,7 @@ namespace avs {
"!!! remove -exec argument and try again. !!!\n" "!!! remove -exec argument and try again. !!!\n"
"!!! !!!\n" "!!! !!!\n"
); );
log_fatal("ddr", "BAD GAME DLL ERROR (don't use -exec gamemdx.dll, that's the wrong DLL)");
} }
// file not found on disk // file not found on disk
+4 -9
View File
@@ -49,7 +49,7 @@ BUILDDIR_64_RELEASE="./cmake-build-release-64"
BUILDDIR_64_DEBUG="./cmake-build-debug-64" BUILDDIR_64_DEBUG="./cmake-build-debug-64"
DEBUG=0 DEBUG=0
OUTDIR="./bin/spice2x" OUTDIR="./bin/spice2x"
OUTDIR_EXTRAS="./bin/spice2x_extras" OUTDIR_EXTRAS="./bin/spice2x/extras"
# disabled UPX since it tends to falsely trigger malware detection # disabled UPX since it tends to falsely trigger malware detection
UPX_ENABLE=0 UPX_ENABLE=0
@@ -60,8 +60,8 @@ INCLUDE_SRC=1
DIST_ENABLE=1 DIST_ENABLE=1
DIST_FOLDER="./dist" DIST_FOLDER="./dist"
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip" DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
DIST_NAME_EXTRAS="spice2x-$(date +%y)-$(date +%m)-$(date +%d)-full.zip"
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.' DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
DIST_NAME_EXTRAS="spice2x-$(date +%y)-$(date +%m)-$(date +%d)-extras.zip"
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_cfg_linux spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_cpusbxpkm" TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_cfg_linux spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_cpusbxpkm"
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux" TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux"
@@ -231,14 +231,11 @@ fi
# pack source files to output directory # pack source files to output directory
rm -rf ${OUTDIR}/src rm -rf ${OUTDIR}/src
mkdir -p ${OUTDIR}/src mkdir -p ${OUTDIR}/src
rm -rf ${OUTDIR_EXTRAS}/src
mkdir -p ${OUTDIR_EXTRAS}/src
if ((INCLUDE_SRC > 0)) if ((INCLUDE_SRC > 0))
then then
echo "Generating source file archive..." echo "Generating source file archive..."
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \ git archive --format tar.gz --prefix=spice2x/ HEAD ./ > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?" echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
cp -r ${OUTDIR}/src/* ${OUTDIR_EXTRAS}/src
fi fi
# copy resources # copy resources
@@ -254,11 +251,9 @@ then
mkdir -p ${DIST_FOLDER} mkdir -p ${DIST_FOLDER}
rm -rf ${DIST_FOLDER}/${DIST_NAME} rm -rf ${DIST_FOLDER}/${DIST_NAME}
pushd ${OUTDIR}/.. > /dev/null pushd ${OUTDIR}/.. > /dev/null
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -z <<< "$DIST_COMMENT" zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
popd > /dev/null
echo "Building extras..." echo "Building extras..."
pushd ${OUTDIR_EXTRAS}/.. > /dev/null zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x -z <<< "$DIST_COMMENT"
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x_extras -z <<< "$DIST_COMMENT"
popd > /dev/null popd > /dev/null
fi fi
+1
View File
@@ -1,2 +1,3 @@
del /s /q .ccache del /s /q .ccache
del /s /q dist
call build_docker.bat call build_docker.bat
+3 -1
View File
@@ -125,7 +125,7 @@ namespace games::ddr {
void DDRGame::pre_attach() { void DDRGame::pre_attach() {
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) {
log_fatal( log_warning(
"ddr", "ddr",
"BAD MODEL NAME ERROR\n\n\n" "BAD MODEL NAME ERROR\n\n\n"
"!!! model name set to TDX, this is WRONG and will break your game !!!\n" "!!! model name set to TDX, this is WRONG and will break your game !!!\n"
@@ -136,6 +136,8 @@ namespace games::ddr {
"!!! !!!\n" "!!! !!!\n"
"!!! model name set to TDX, this is WRONG and will break your game !!!\n\n\n" "!!! model name set to TDX, this is WRONG and will break your game !!!\n\n\n"
); );
log_fatal("ddr", "BAD MODEL NAME ERROR - TDX specified, should be MDX instead");
} }
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::DEST[0] == 'U') { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::DEST[0] == 'U') {
+3 -9
View File
@@ -405,14 +405,6 @@ namespace games::iidx {
// init cfgmgr32 hooks // init cfgmgr32 hooks
cfgmgr32hook_init(avs::game::DLL_INSTANCE); cfgmgr32hook_init(avs::game::DLL_INSTANCE);
// report common errors on iidx31 and above
if (avs::game::is_ext(2023091500, MAXINT) && GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
deferredlogs::defer_error_messages({
"common incompatibility with DX 9on12 + IIDX 31 and above",
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes"
});
}
} }
void IIDXGame::pre_attach() { void IIDXGame::pre_attach() {
@@ -436,7 +428,7 @@ namespace games::iidx {
// check bad model name // check bad model name
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDJ")) { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDJ")) {
log_fatal( log_warning(
"iidx", "iidx",
"BAD MODEL NAME ERROR\n\n\n" "BAD MODEL NAME ERROR\n\n\n"
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n" "!!! model name set to TDJ, this is WRONG and will break your game !!!\n"
@@ -454,6 +446,8 @@ namespace games::iidx {
"!!! !!!\n" "!!! !!!\n"
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n\n\n" "!!! model name set to TDJ, this is WRONG and will break your game !!!\n\n\n"
); );
log_fatal("iidx", "BAD MODEL NAME ERROR - TDJ specified, must be LDJ instead");
} }
} }
+5 -1
View File
@@ -169,7 +169,7 @@ namespace games::jb {
const auto current_path = std::filesystem::current_path(); const auto current_path = std::filesystem::current_path();
log_misc("jubeat", "current working directory: {}", current_path.string()); log_misc("jubeat", "current working directory: {}", current_path.string());
if (current_path.parent_path() == current_path.root_path()) { if (current_path.parent_path() == current_path.root_path()) {
log_fatal( log_warning(
"jubeat", "jubeat",
"\n\nInvalid path error; jubeat cannot run from a directory in the drive root\n" "\n\nInvalid path error; jubeat cannot run from a directory in the drive root\n"
"The game will overflow the stack and silently fail to boot\n\n" "The game will overflow the stack and silently fail to boot\n\n"
@@ -179,6 +179,10 @@ namespace games::jb {
"To fix this, create a new directory and move ALL game files there.\n\n" "To fix this, create a new directory and move ALL game files there.\n\n"
"Your current working directory: {}\n", "Your current working directory: {}\n",
current_path.string()); current_path.string());
log_fatal(
"jubeat",
"Invalid path error; jubeat cannot run from a directory in the drive root");
} }
} }
} }
+24
View File
@@ -288,6 +288,21 @@ namespace games::mfc {
MFCGame::MFCGame() : Game("Mahjong Fight Club") { MFCGame::MFCGame() : Game("Mahjong Fight Club") {
} }
static bool spam_remover(void *user, const std::string &data, logger::Style style, std::string &out) {
if (data.empty() || data[0] != '[') {
return false;
}
if (data.find("W:mutex: lock: mid 00000000 != AVS_DTYPE_MUTEX") != std::string::npos) {
out = "";
return true;
}
if (data.find("W:mutex: unlock: mid 00000000 != AVS_DTYPE_MUTEX") != std::string::npos) {
out = "";
return true;
}
return false;
}
void MFCGame::attach() { void MFCGame::attach() {
Game::attach(); Game::attach();
@@ -308,6 +323,13 @@ namespace games::mfc {
auto allinone_module = libutils::try_module("allinone.dll"); auto allinone_module = libutils::try_module("allinone.dll");
auto system_module = libutils::try_module("system.dll"); auto system_module = libutils::try_module("system.dll");
// Mahjong Fight Club - 2025?
// they removed allinone.dll and moved i/o into system.dll
if (allinone_module == nullptr) {
log_misc("mfc", "using system.dll instead of allinone.dll for i/o hooks");
allinone_module = system_module;
}
// network fix // network fix
detour::iat_try("?nic_dhcp_maybe_exist@@YAEXZ", nic_dhcp_maybe_exist, system_module); detour::iat_try("?nic_dhcp_maybe_exist@@YAEXZ", nic_dhcp_maybe_exist, system_module);
@@ -378,5 +400,7 @@ namespace games::mfc {
detour::inline_hook((void *) mouse_utl_step, libutils::try_proc( detour::inline_hook((void *) mouse_utl_step, libutils::try_proc(
allinone_module, "?mouse_utl_step@@YAXXZ")); allinone_module, "?mouse_utl_step@@YAXXZ"));
} }
logger::hook_add(spam_remover, nullptr);
} }
} }
+13 -1
View File
@@ -215,6 +215,15 @@ namespace games::sdvx {
deferredlogs::defer_error_messages(deferredlogs::SUPERSTEP_SOUND_ERROR_MESSAGE); deferredlogs::defer_error_messages(deferredlogs::SUPERSTEP_SOUND_ERROR_MESSAGE);
return false; return false;
} }
if (data.find("W:ea3-pos: ea3_report_posev: no such node:") != std::string::npos) {
deferredlogs::defer_error_messages({
"bad prop\\ea3-config.xml detected by game",
std::string(" ") + data,
" this will most likely cause Server Busy errors in certain conditions",
" follow the FAQ to fix your ea3-config.xml and try again"
});
return false;
}
return false; return false;
} }
@@ -268,7 +277,7 @@ namespace games::sdvx {
AUTO_INSERT_CARD_COOLDOWN = 15.f; AUTO_INSERT_CARD_COOLDOWN = 15.f;
// check bad model name // check bad model name
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("UFC")) { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("UFC")) {
log_fatal( log_warning(
"sdvx", "sdvx",
"BAD MODEL NAME ERROR\n\n\n" "BAD MODEL NAME ERROR\n\n\n"
"!!! model name set to UFC, this is WRONG and will break your game !!!\n" "!!! model name set to UFC, this is WRONG and will break your game !!!\n"
@@ -278,6 +287,9 @@ namespace games::sdvx {
"!!! !!!\n" "!!! !!!\n"
"!!! model name set to UFC, this is WRONG and will break your game !!!\n\n\n" "!!! model name set to UFC, this is WRONG and will break your game !!!\n\n\n"
); );
log_fatal(
"sdvx",
"BAD MODEL NAME ERROR - model name set to UFC, must be KFC instead");
} }
} }
@@ -238,14 +238,17 @@ static void log_create_device_failure(HRESULT hresult) {
} }
static bool is_dx9_on_12_enabled() { static bool is_dx9_on_12_enabled() {
bool result = false;
switch (GRAPHICS_9_ON_12_STATE) { switch (GRAPHICS_9_ON_12_STATE) {
case DX9ON12_FORCE_OFF: case DX9ON12_FORCE_OFF:
log_info("graphics::d3d9", "DirectX 9on12: forced OFF by user (-dx9on12)"); log_info("graphics::d3d9", "DirectX 9on12: forced OFF by user (-dx9on12)");
return false; result = false;
break;
case DX9ON12_FORCE_ON: case DX9ON12_FORCE_ON:
log_info("graphics::d3d9", "DirectX 9on12: forced ON by user (-9on12 or -dx9on12)"); log_info("graphics::d3d9", "DirectX 9on12: forced ON by user (-9on12 or -dx9on12)");
return true; result = true;
break;
case DX9ON12_AUTO: case DX9ON12_AUTO:
default: default:
@@ -253,14 +256,33 @@ static bool is_dx9_on_12_enabled() {
log_info( log_info(
"graphics::d3d9", "graphics::d3d9",
"DirectX 9on12: enabled automatically for current game (tip: use -dx9on12 to force on or off)"); "DirectX 9on12: enabled automatically for current game (tip: use -dx9on12 to force on or off)");
return true; result = true;
} else { } else {
log_info( log_info(
"graphics::d3d9", "graphics::d3d9",
"DirectX 9on12: disabled by default, using DX9 (tip: use -dx9on12 to force on or off)"); "DirectX 9on12: disabled by default, using DX9 (tip: use -dx9on12 to force on or off)");
return false; result = false;
} }
break;
} }
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, MAXINT)) {
deferredlogs::defer_error_messages({
"dx9on12 was force enabled by user (-dx9on12)",
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
" try again with -dx9on12 option set to default value"
});
} else {
deferredlogs::defer_error_messages({
"dx9on12 was force enabled by user (-dx9on12)",
" not very game is compatible with this, and can lead to crashes",
" try without force enabling this if you are seeing issues"
});
}
}
return result;
} }
static IDirect3D9 *WINAPI Direct3DCreate9_hook(UINT SDKVersion) { static IDirect3D9 *WINAPI Direct3DCreate9_hook(UINT SDKVersion) {
+42 -19
View File
@@ -299,22 +299,6 @@ int main_implementation(int argc, char *argv[]) {
cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config; cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config;
cfg_run = true; cfg_run = true;
} }
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
options[launcher::Options::ServiceURL].is_active() &&
!cfg::CONFIGURATOR_STANDALONE) {
log_fatal(
"launcher",
"BAD EAMUSE SETTINGS ERROR\n\n\n"
"-------------------------------------------------------------------\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"A service URL is set **AND** E-Amusement emulation is enabled.\n"
"Either remove the service URL, or disable E-Amusement emulation.\n"
"Otherwise you may experience problems logging in.\n"
"-------------------------------------------------------------------\n\n\n"
);
}
if (options[launcher::Options::EAmusementEmulation].value_bool()) { if (options[launcher::Options::EAmusementEmulation].value_bool()) {
avs::ea3::URL_SLASH = 1; avs::ea3::URL_SLASH = 1;
easrv_port = 8080; easrv_port = 8080;
@@ -946,6 +930,11 @@ int main_implementation(int argc, char *argv[]) {
} }
if (options[launcher::Options::DisableAudioHooks].value_bool()) { if (options[launcher::Options::DisableAudioHooks].value_bool()) {
hooks::audio::ENABLED = false; hooks::audio::ENABLED = false;
deferredlogs::defer_error_messages({
"audio hooks are forcibly disabled by user (-audiohookdisable)",
" having hooks disabled means some required audio fixes are not being applied",
" if you have audio-related crashes, you should try again after clearing this option"
});
} }
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) { if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
hooks::audio::VOLUME_HOOK_ENABLED = false; hooks::audio::VOLUME_HOOK_ENABLED = false;
@@ -1226,6 +1215,10 @@ int main_implementation(int argc, char *argv[]) {
log_info("launcher", "{}", VERSION_STRING); log_info("launcher", "{}", VERSION_STRING);
// note: distribution of modified version of this software without providing source is GPLv3 license violation.
log_info("launcher", "spice2x is free & open source; if you paid money for it, you got scammed");
log_info("launcher", "visit https://spice2x.github.io to download the latest version for free");
// log command line arguments // log command line arguments
std::ostringstream arguments; std::ostringstream arguments;
for (auto &root_option : options) { for (auto &root_option : options) {
@@ -1269,18 +1262,19 @@ int main_implementation(int argc, char *argv[]) {
for (const auto &option : options) { for (const auto &option : options) {
if (option.conflicting && option.get_definition().type != OptionType::Bool) { if (option.conflicting && option.get_definition().type != OptionType::Bool) {
conflicts += 1; conflicts += 1;
const auto& value = option.get_definition().sensitive ? "*****" : option.value;
if (launcher::USE_CMD_OVERRIDE) { if (launcher::USE_CMD_OVERRIDE) {
log_warning( log_warning(
"launcher", "launcher",
"multiple values for -{}, command line args take precedence: {}", "multiple values for -{}, command line args take precedence: {}",
option.get_definition().name, option.get_definition().name,
option.value); value);
} else { } else {
log_warning( log_warning(
"launcher", "launcher",
"multiple values for -{}, spicecfg values take precedence: {}", "multiple values for -{}, spicecfg values take precedence: {}",
option.get_definition().name, option.get_definition().name,
option.value); value);
} }
} }
} }
@@ -1309,6 +1303,25 @@ int main_implementation(int argc, char *argv[]) {
); );
} }
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
options[launcher::Options::ServiceURL].is_active() &&
!cfg::CONFIGURATOR_STANDALONE) {
log_warning(
"launcher",
"BAD EAMUSE SETTINGS ERROR\n\n\n"
"-------------------------------------------------------------------\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"A service URL is set **AND** E-Amusement emulation is enabled.\n"
"Either remove the service URL, or disable E-Amusement emulation.\n"
"Otherwise you may experience problems logging in.\n"
"-------------------------------------------------------------------\n\n\n"
);
log_fatal(
"launcher",
"BAD EAMUSE SETTINGS ERROR: both -ea and -url are set");
}
if (options[launcher::Options::FullscreenResolution].is_active()) { if (options[launcher::Options::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result; std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) { if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
@@ -1664,6 +1677,14 @@ int main_implementation(int argc, char *argv[]) {
attach_io = true; attach_io = true;
attach_mfc = true; attach_mfc = true;
break; break;
// Mahjong Fight Club - 2025?
// they removed allinone.dll and moved i/o into system.dll
} else if (check_dll("system.dll") && fileutils::file_exists("data/mfc.ini")) {
avs::game::DLL_NAME = "system.dll";
attach_io = true;
attach_mfc = true;
break;
} }
// FutureTomTom // FutureTomTom
@@ -1808,7 +1829,7 @@ int main_implementation(int argc, char *argv[]) {
// usage error // usage error
if (!cfg::CONFIGURATOR_STANDALONE if (!cfg::CONFIGURATOR_STANDALONE
&& (!CHECK_DLL_IGNORE_ARCH)) { && (!CHECK_DLL_IGNORE_ARCH)) {
log_fatal( log_warning(
"launcher", "launcher",
"module auto detection failed!\n\n" "module auto detection failed!\n\n"
"This usually means one of the following:\n\n" "This usually means one of the following:\n\n"
@@ -1816,6 +1837,8 @@ int main_implementation(int argc, char *argv[]) {
" 2. XML files in prop directory are malformed or missing, or\n" " 2. XML files in prop directory are malformed or missing, or\n"
" 3. You passed in invalid options (usually, path overrides like -exec)\n\n" " 3. You passed in invalid options (usually, path overrides like -exec)\n\n"
); );
log_fatal("launcher", "module auto detection failed, see log.txt for troubleshooting");
} }
break; break;
+4
View File
@@ -1,5 +1,6 @@
#include "signal.h" #include "signal.h"
#include <future>
#include <exception> #include <exception>
#include <windows.h> #include <windows.h>
@@ -174,6 +175,9 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
log_warning("signal", "minidump creation function not available, skipping"); log_warning("signal", "minidump creation function not available, skipping");
} }
// this will stall all UI threads for this process
show_popup_for_crash();
log_fatal("signal", "end"); log_fatal("signal", "end");
} }
+4 -3
View File
@@ -270,13 +270,14 @@ void overlay::SpiceOverlay::init() {
ImFontConfig config {}; ImFontConfig config {};
config.MergeMode = true; config.MergeMode = true;
log_misc("overlay", "loading fonts, failures are not fatal");
add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesCyrillic()); add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesCyrillic());
add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese()); add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese()); add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean()); add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean());
add_font("cordia.ttc", &config, io.Fonts->GetGlyphRangesThai()); add_font("cordia.ttf", &config, io.Fonts->GetGlyphRangesThai());
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesVietnamese()); add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesVietnamese());
// add special font // add special font
if (avs::game::is_model("LDJ")) { if (avs::game::is_model("LDJ")) {
+12 -2
View File
@@ -556,9 +556,16 @@ namespace overlay::windows {
} }
// disclaimer // disclaimer
// note: distribution of modified version of this software without providing source is GPLv3 license violation.
ImGui::TextColored( ImGui::TextColored(
ImVec4(1, 0.5f, 0.5f, 1.f), ImVec4(1, 0.5f, 0.5f, 1.f),
"Do NOT stream or upload game data anywhere public! Support arcades when you can. Thanks."); "spice2x is free & open source; if you paid money for it, you got scammed.");
if (cfg::CONFIGURATOR_STANDALONE) {
ImGui::SameLine();
if (ImGui::TextLink(PROJECT_URL)) {
launch_shell(PROJECT_URL);
}
}
} }
void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) { void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) {
@@ -2917,7 +2924,7 @@ namespace overlay::windows {
#endif #endif
ImGui::TextUnformatted(""); ImGui::TextUnformatted("");
if (ImGui::Button(PROJECT_URL)) { if (ImGui::TextLink(PROJECT_URL)) {
launch_shell(PROJECT_URL); launch_shell(PROJECT_URL);
} }
@@ -2978,6 +2985,7 @@ namespace overlay::windows {
void Config::build_menu(int *game_selected) { void Config::build_menu(int *game_selected) {
bool about_popup = false; bool about_popup = false;
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 1.0f));
if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenuBar()) {
// [spice2x] // [spice2x]
@@ -3038,6 +3046,8 @@ namespace overlay::windows {
ImGui::EndMenuBar(); ImGui::EndMenuBar();
} }
ImGui::PopStyleColor(); // ImGuiCol_PopupBg
// workaround for popups triggered by menu, see https://github.com/ocornut/imgui/issues/331 // workaround for popups triggered by menu, see https://github.com/ocornut/imgui/issues/331
if (about_popup) { if (about_popup) {
ImGui::OpenPopup("About##topbarpopup"); ImGui::OpenPopup("About##topbarpopup");
+1
View File
@@ -3,6 +3,7 @@ developers when you can. Sources should be included, have fun!
spice2x fork maintainers: spice2x fork maintainers:
* sp2xdev team * sp2xdev team
* the lab
SpiceTools original contributors: SpiceTools original contributors:
* cube, felix, dinsfire, nolm, s2, crazyredmachine (jotaro44), mon, nibs * cube, felix, dinsfire, nolm, s2, crazyredmachine (jotaro44), mon, nibs
+2 -1
View File
@@ -43,7 +43,8 @@ static inline void load_library_fail(const std::string &file_name, bool fatal) {
" b. http://www.dependencywalker.com/ (for old OS) \n" " b. http://www.dependencywalker.com/ (for old OS) \n"
, file_name) }; , file_name) };
if (fatal) { if (fatal) {
log_fatal("libutils", "{}", info_str); log_warning("libutils", "{}", info_str);
log_fatal("libutils", "DLL failed to load: {}, see log.txt for troubleshooting", file_name);
} else { } else {
log_warning("libutils", "{}", info_str); log_warning("libutils", "{}", info_str);
} }
+69
View File
@@ -1,4 +1,10 @@
#include <atomic>
#include <chrono>
#include <future>
#include "logging.h" #include "logging.h"
#include "build/defs.h"
#include "hooks/graphics/graphics.h"
std::string_view log_get_datetime() { std::string_view log_get_datetime() {
return log_get_datetime(time(nullptr)); return log_get_datetime(time(nullptr));
@@ -12,3 +18,66 @@ std::string_view log_get_datetime(std::time_t now) {
return buf; return buf;
} }
static void show_popup(const std::string text) {
static std::once_flag shown;
std::call_once(shown, [text]() {
// minimize all windows
// only needed because in multi-monitor full screen games, MessageBox fails to show above the game
for (auto &hwnd : GRAPHICS_WINDOWS) {
ShowWindow(hwnd, SW_FORCEMINIMIZE);
}
// schedule a worker to terminate the game in 30 seconds in case user can't dismiss UI
std::atomic_bool cancel_popup = false;
auto worker = std::async(std::launch::async, [&cancel_popup]{
for (int ms = 300; ms > 0; ms--) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (cancel_popup) {
return;
}
}
launcher::stop_subsystems();
launcher::kill();
std::terminate();
});
// MessageBox will block until user presses OK
const std::string title = "spice2x (" + to_string(VERSION_STRING_CFG) + ")";
MessageBox(
nullptr,
text.c_str(),
title.c_str(),
MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);
// cancel the worker since the user acknowledged the popup
cancel_popup = true;
});
}
void show_popup_for_crash() {
std::string text;
text += "Game has crashed.\n\n";
text += "Check log.txt and look for error messages near the end of file.\n\n";
text += "Unsure what to do next?\n";
text += " * update spice2x to the latest version\n";
text += " * check the FAQ on spice2x github wiki\n";
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
show_popup(text);
}
void show_popup_for_fatal_error(std::string message) {
std::string text;
text += "A fatal error was encountered. For details, check log.txt.\n\n";
text += "----------------------------------------------------------\n";
text += message;
text += "----------------------------------------------------------\n\n";
text += "Unsure what to do next?\n";
text += " * update spice2x to the latest version\n";
text += " * check the FAQ on spice2x github wiki\n";
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
show_popup(text);
}
+10 -2
View File
@@ -71,20 +71,28 @@ struct fmt::formatter<fmt_hresult> {
} }
}; };
void show_popup_for_crash();
void show_popup_for_fatal_error(std::string message);
// misc log // misc log
#define LOG_FORMAT(level, module, fmt_str, ...) fmt::format(FMT_COMPILE("{}" fmt_str "\n"), \ #define LOG_FORMAT(level, module, fmt_str, ...) fmt::format(FMT_COMPILE("{}" fmt_str "\n"), \
fmt_log { std::time(nullptr), level, module }, ## __VA_ARGS__) fmt_log { std::time(nullptr), level, module }, ## __VA_ARGS__)
#define LOG_FORMAT_POPUP(module, fmt_str, ...) fmt::format(FMT_COMPILE("{}: " fmt_str "\n"), module, ## __VA_ARGS__)
#define log_misc(module, format_str, ...) logger::push( \ #define log_misc(module, format_str, ...) logger::push( \
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY) LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
#define log_info(module, format_str, ...) logger::push( \ #define log_info(module, format_str, ...) logger::push( \
LOG_FORMAT("I", module, format_str, ## __VA_ARGS__), logger::Style::DEFAULT) LOG_FORMAT("I", module, format_str, ## __VA_ARGS__), logger::Style::DEFAULT)
#define log_warning(module, format_str, ...) logger::push( \ #define log_warning(module, format_str, ...) logger::push( \
LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::YELLOW) LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::YELLOW)
#define log_fatal(module, format_str, ...) { \ #define log_fatal(module, format_str, ...) { \
logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \ logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \
logger::push(LOG_FORMAT("F", "spice", "encountered a fatal error, you can close the window or press ctrl + c"), logger::Style::RED); \ show_popup_for_fatal_error(LOG_FORMAT_POPUP(module, format_str, ## __VA_ARGS__)); \
launcher::stop_subsystems(); \ launcher::stop_subsystems(); \
Sleep(10000); \
launcher::kill(); \ launcher::kill(); \
std::terminate(); \ std::terminate(); \
} ((void) 0 ) } ((void) 0 )