Update to spice2x-26-02-17 (pre-apply)
> broken commit
This commit is contained in:
+39
-7
@@ -12,17 +12,36 @@ namespace deferredlogs {
|
||||
" this crash is most likely related to audio init failure",
|
||||
" * check if the default audio device has changed",
|
||||
" * fix your audio device settings (e.g., sample rate)",
|
||||
" * double check your spice audio options and patches"
|
||||
" * double check your spice audio options and patches",
|
||||
" * ensure no other application is using the device in exclusive mode"
|
||||
};
|
||||
|
||||
std::mutex deferred_errors_mutex;
|
||||
std::vector<std::vector<std::string>> deferred_errors;
|
||||
|
||||
std::mutex softid_mutex;
|
||||
std::string softid;
|
||||
|
||||
void set_softid(const std::string& softid_new) {
|
||||
std::lock_guard<std::mutex> lock(softid_mutex);
|
||||
softid = softid_new;
|
||||
}
|
||||
|
||||
void defer_error_messages(std::initializer_list<std::string> messages) {
|
||||
std::lock_guard<std::mutex> lock(deferred_errors_mutex);
|
||||
deferred_errors.emplace_back(messages);
|
||||
}
|
||||
|
||||
void report_fatal_message() {
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, []() {
|
||||
deferredlogs::defer_error_messages({
|
||||
"game reported FATAL error(s)",
|
||||
" look for messages containing F: in the log",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void dump_to_logger(bool is_crash) {
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, [is_crash]() {
|
||||
@@ -45,17 +64,30 @@ namespace deferredlogs {
|
||||
msg += "/-------------------------- spice2x auto-troubleshooter -----------------------\\\n";
|
||||
msg += "\n";
|
||||
msg += " spice2x version: " + to_string(VERSION_STRING_CFG) + "\n";
|
||||
|
||||
// soft ID
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(softid_mutex);
|
||||
if (!softid.empty()) {
|
||||
msg += " game version: " + softid + "\n";
|
||||
} else {
|
||||
msg += " game version: unknown\n";
|
||||
}
|
||||
}
|
||||
|
||||
msg += "\n";
|
||||
|
||||
if (is_crash) {
|
||||
msg += " the game has crashed\n";
|
||||
msg += " share this entire log file with someone for troubleshooting (log.txt)\n";
|
||||
msg += " spice will also attempt to create a minidump (minidump.dmp)\n";
|
||||
msg += " * share this entire log file with someone for troubleshooting (log.txt)\n";
|
||||
msg += " * spice will also attempt to create a minidump (minidump.dmp)\n";
|
||||
msg += " minidump should only be shared with people you trust as it may contain\n";
|
||||
msg += " sensitive data (PCBID, card ID, etc)\n";
|
||||
msg += "\n";
|
||||
}
|
||||
|
||||
for (auto messages : errors) {
|
||||
for (auto message : messages) {
|
||||
for (const auto &messages : errors) {
|
||||
for (const auto &message : messages) {
|
||||
msg += " " + message + "\n";
|
||||
}
|
||||
msg += "\n";
|
||||
@@ -63,13 +95,13 @@ namespace deferredlogs {
|
||||
|
||||
msg += " unsure what to do next?\n";
|
||||
msg += " * update to the latest version:\n";
|
||||
msg += " https://github.com/spice2x/spice2x.github.io/releases/latest\n";
|
||||
msg += " https://github.com/spice2x/spice2x.github.io/releases\n";
|
||||
msg += " * check the FAQ:\n";
|
||||
msg += " https://github.com/spice2x/spice2x.github.io/wiki/Known-issues\n";
|
||||
msg += "\n";
|
||||
msg += "\\------------------------- spice2x auto-troubleshooter ------------------------/\n";
|
||||
|
||||
log_warning("troubleshooter", "{}", msg);
|
||||
log_special("troubleshooter", "{}", msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace deferredlogs {
|
||||
// some shared error messages
|
||||
extern const std::initializer_list<std::string> SUPERSTEP_SOUND_ERROR_MESSAGE;
|
||||
|
||||
void set_softid(const std::string& softid);
|
||||
void report_fatal_message();
|
||||
void defer_error_messages(std::initializer_list<std::string> messages);
|
||||
void dump_to_logger(bool is_crash=false);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace {
|
||||
{
|
||||
"msvcr100.dll",
|
||||
"Visual Studio 2010 (VC++ 10.0) SP1 Redistributable",
|
||||
"Download and install VisualCppRedist_AIO_x86_x64.exe from https://github.com/abbodi1406/vcredist/releases/latest",
|
||||
"Download and install from https://github.com/spice2x/spice2x.github.io/wiki/Visual-Cpp-Redistributable-Runtimes",
|
||||
},
|
||||
{
|
||||
"d3dx9_43.dll",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "fileutils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
+2
-1
@@ -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"
|
||||
, file_name) };
|
||||
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 {
|
||||
log_warning("libutils", "{}", info_str);
|
||||
}
|
||||
|
||||
+72
-2
@@ -1,4 +1,10 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
|
||||
#include "logging.h"
|
||||
#include "build/defs.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
std::string_view log_get_datetime() {
|
||||
return log_get_datetime(time(nullptr));
|
||||
@@ -7,8 +13,72 @@ std::string_view log_get_datetime() {
|
||||
std::string_view log_get_datetime(std::time_t now) {
|
||||
static thread_local char buf[64];
|
||||
|
||||
// `localtime` on Windows is thread-safe
|
||||
strftime(buf, sizeof(buf), "[%Y/%m/%d %X]", localtime(&now));
|
||||
std::tm local_tm{};
|
||||
localtime_s(&local_tm, &now);
|
||||
strftime(buf, sizeof(buf), "[%Y/%m/%d %X]", &local_tm);
|
||||
|
||||
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);
|
||||
}
|
||||
+17
-8
@@ -42,13 +42,12 @@ struct fmt_log {
|
||||
|
||||
template<>
|
||||
struct fmt::formatter<fmt_log> {
|
||||
template<typename ParseContext>
|
||||
constexpr auto parse(ParseContext &ctx) {
|
||||
constexpr auto parse(format_parse_context &ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const fmt_log &v, FormatContext &ctx) {
|
||||
auto format(const fmt_log &v, FormatContext &ctx) const {
|
||||
return format_to(ctx.out(), FMT_COMPILE("{} {}:{}: "), log_get_datetime(v.ts), v.level, v.module);
|
||||
}
|
||||
};
|
||||
@@ -60,31 +59,41 @@ struct fmt_hresult {
|
||||
|
||||
template<>
|
||||
struct fmt::formatter<fmt_hresult> {
|
||||
template<typename ParseContext>
|
||||
constexpr auto parse(ParseContext &ctx) {
|
||||
constexpr auto parse(format_parse_context &ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const fmt_hresult &v, FormatContext &ctx) {
|
||||
auto format(const fmt_hresult &v, FormatContext &ctx) const {
|
||||
return format_to(ctx.out(), FMT_COMPILE("0x{:08x}"), static_cast<unsigned>(v.result));
|
||||
}
|
||||
};
|
||||
|
||||
void show_popup_for_crash();
|
||||
void show_popup_for_fatal_error(std::string message);
|
||||
|
||||
// misc log
|
||||
#define LOG_FORMAT(level, module, fmt_str, ...) fmt::format(FMT_COMPILE("{}" fmt_str "\n"), \
|
||||
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( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
|
||||
#define log_info(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("I", module, format_str, ## __VA_ARGS__), logger::Style::DEFAULT)
|
||||
|
||||
#define log_warning(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::YELLOW)
|
||||
|
||||
#define log_special(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::SPECIAL)
|
||||
|
||||
#define log_fatal(module, format_str, ...) { \
|
||||
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(); \
|
||||
Sleep(10000); \
|
||||
launcher::kill(); \
|
||||
std::terminate(); \
|
||||
} ((void) 0 )
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "socd_cleaner.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
namespace socd {
|
||||
|
||||
SocdAlgorithm ALGORITHM = SocdAlgorithm::Neutral;
|
||||
|
||||
static double last_rising_edge[4][2] = {};
|
||||
static bool last_button_state[4][2] = {};
|
||||
|
||||
// this has no locking, use only if you know there is only one i/o thread that will call this
|
||||
SocdResult socd_clean(
|
||||
uint8_t device, bool ccw, bool cw, double time_now,
|
||||
std::optional<SocdAlgorithm> algorithm_override) {
|
||||
|
||||
if (device >= 4) {
|
||||
log_fatal("socd", "invalid device index in socd_clean: {}", device);
|
||||
}
|
||||
|
||||
SocdAlgorithm algo =
|
||||
algorithm_override.has_value() ? algorithm_override.value() : ALGORITHM;
|
||||
|
||||
// keep track of rising edge times
|
||||
if (algo == SocdAlgorithm::PreferRecent || algo == SocdAlgorithm::PreferFirst) {
|
||||
if (!last_button_state[device][SocdCCW] && ccw) {
|
||||
last_rising_edge[device][SocdCCW] = time_now;
|
||||
}
|
||||
if (!last_button_state[device][SocdCW] && cw) {
|
||||
last_rising_edge[device][SocdCW] = time_now;
|
||||
}
|
||||
|
||||
// update button state for next time
|
||||
last_button_state[device][SocdCCW] = ccw;
|
||||
last_button_state[device][SocdCW] = cw;
|
||||
}
|
||||
|
||||
// determine direction: easy cases
|
||||
if (!ccw && !cw) {
|
||||
return SocdNone;
|
||||
}
|
||||
if (ccw && !cw) {
|
||||
return SocdCCW;
|
||||
}
|
||||
if (!ccw && cw) {
|
||||
return SocdCW;
|
||||
}
|
||||
|
||||
// SOCD logic; depends on algorithm in use
|
||||
const auto ccw_time = last_rising_edge[device][SocdCCW];
|
||||
const auto cw_time = last_rising_edge[device][SocdCW];
|
||||
log_debug("socd", "ccw={}, cw ={}", ccw_time, cw_time);
|
||||
|
||||
if (algo == SocdAlgorithm::PreferRecent) {
|
||||
// SOCD: prefer last input
|
||||
if (ccw_time < cw_time) {
|
||||
// while CCW is being held, CW got pressed
|
||||
return SocdCW;
|
||||
} else if (ccw_time > cw_time) {
|
||||
// while CW is being held, CCW got pressed
|
||||
return SocdCCW;
|
||||
} else {
|
||||
// it's a tie; instead of none, we'll pick a direction
|
||||
return SocdCW;
|
||||
}
|
||||
} else if (algo == SocdAlgorithm::PreferFirst) {
|
||||
// SOCD: keep first input
|
||||
if (ccw_time < cw_time) {
|
||||
// while CCW is being held, CW got pressed
|
||||
return SocdCCW;
|
||||
} else if (ccw_time > cw_time) {
|
||||
// while CW is being held, CCW got pressed
|
||||
return SocdCW;
|
||||
} else {
|
||||
// it's a tie; instead of none, we'll pick a direction
|
||||
return SocdCW;
|
||||
}
|
||||
} else if (algo == SocdAlgorithm::Neutral) {
|
||||
// SOCD: neutral when both are pressed
|
||||
return SocdNone;
|
||||
} else if (algo == SocdAlgorithm::None) {
|
||||
// SOCD: both are pressed
|
||||
return SocdBoth;
|
||||
} else {
|
||||
log_fatal("socd", "invalid SOCD algorithm");
|
||||
return SocdNone;
|
||||
}
|
||||
}
|
||||
|
||||
// first dimension: p1/p2
|
||||
// second dimension: TiltUp / TiltDown
|
||||
// value: last timestamp when it was on
|
||||
static double most_recent_active[2][2] = {};
|
||||
|
||||
uint32_t TILT_HOLD_MS = 100;
|
||||
|
||||
TiltResult get_guitar_wail(uint8_t device, bool up, bool down, double time_now) {
|
||||
if (device >= 2) {
|
||||
log_fatal("socd", "invalid device index in socd_clean: {}", device);
|
||||
}
|
||||
|
||||
const auto socd_result = socd_clean(
|
||||
device + 2, up, down, time_now, SocdAlgorithm::PreferRecent);
|
||||
|
||||
if (up) {
|
||||
most_recent_active[device][TiltUp] = time_now;
|
||||
}
|
||||
if (down) {
|
||||
most_recent_active[device][TiltDown] = time_now;
|
||||
}
|
||||
|
||||
log_debug(
|
||||
"socd",
|
||||
"p{} wail up={}, down={}",
|
||||
device + 1,
|
||||
most_recent_active[device][TiltUp],
|
||||
most_recent_active[device][TiltDown]);
|
||||
|
||||
const auto delta_up = time_now - most_recent_active[device][TiltUp];
|
||||
const auto delta_down = time_now - most_recent_active[device][TiltDown];
|
||||
const bool is_up = delta_up <= TILT_HOLD_MS;
|
||||
const bool is_down = delta_down <= TILT_HOLD_MS;
|
||||
|
||||
auto result = TiltNone;
|
||||
if (is_up && is_down) {
|
||||
// both held recently: prefer more recently held using SOCD logic
|
||||
if (socd_result == SocdCCW) {
|
||||
result = TiltUp;
|
||||
} else if (socd_result == SocdCW) {
|
||||
result = TiltDown;
|
||||
} else if (socd_result == SocdBoth) {
|
||||
result = TiltUp;
|
||||
}
|
||||
} else if (is_up) {
|
||||
result = TiltUp;
|
||||
} else if (is_down) {
|
||||
result = TiltDown;
|
||||
}
|
||||
|
||||
// clear opposite direction being held
|
||||
if (result == TiltUp) {
|
||||
most_recent_active[device][TiltDown] = 0.f;
|
||||
} else if (result == TiltDown) {
|
||||
most_recent_active[device][TiltUp] = 0.f;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
|
||||
namespace socd {
|
||||
|
||||
// SOCD for knobs / turntables
|
||||
|
||||
enum class SocdAlgorithm {
|
||||
Neutral,
|
||||
PreferRecent,
|
||||
PreferFirst,
|
||||
None,
|
||||
};
|
||||
|
||||
extern SocdAlgorithm ALGORITHM;
|
||||
|
||||
typedef enum _SocdResult {
|
||||
SocdCCW = 0,
|
||||
SocdCW = 1,
|
||||
SocdNone = 2,
|
||||
SocdBoth = 3
|
||||
} SocdResult;
|
||||
|
||||
SocdResult socd_clean(
|
||||
uint8_t device,
|
||||
bool ccw,
|
||||
bool cw,
|
||||
double time_now,
|
||||
std::optional<SocdAlgorithm> algorithm = std::nullopt);
|
||||
|
||||
// for guitar wail (up/down only)
|
||||
|
||||
extern uint32_t TILT_HOLD_MS;
|
||||
|
||||
typedef enum _TiltResult {
|
||||
TiltUp = 0,
|
||||
TiltDown = 1,
|
||||
TiltNone = 2
|
||||
} TiltResult;
|
||||
|
||||
TiltResult get_guitar_wail(uint8_t device, bool up, bool down, double time_now);
|
||||
}
|
||||
@@ -235,6 +235,12 @@ namespace sysutils {
|
||||
} else {
|
||||
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
|
||||
}
|
||||
|
||||
log_misc(
|
||||
"gpuinfo", "{} {} is primary : {}",
|
||||
prefix.c_str(),
|
||||
index,
|
||||
(adapter->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-1
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -339,4 +340,33 @@ static inline bool parse_width_height(const std::string wh, std::pair<uint32_t,
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline std::string wchar_to_u8(const wchar_t* wstr) {
|
||||
int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
|
||||
if (size <= 1) {
|
||||
return "";
|
||||
}
|
||||
std::string out(size - 1, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, out.data(), size - 1, nullptr, nullptr);
|
||||
return out;
|
||||
}
|
||||
|
||||
// convert litte vs big endian
|
||||
inline uint64_t bswap64(uint64_t x) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(x);
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
return _byteswap_uint64(x);
|
||||
#endif
|
||||
|
||||
return (x >> 56) |
|
||||
((x >> 40) & 0x000000000000FF00ULL) |
|
||||
((x >> 24) & 0x0000000000FF0000ULL) |
|
||||
((x >> 8) & 0x00000000FF000000ULL) |
|
||||
((x << 8) & 0x000000FF00000000ULL) |
|
||||
((x << 24) & 0x0000FF0000000000ULL) |
|
||||
((x << 40) & 0x00FF000000000000ULL) |
|
||||
(x << 56);
|
||||
}
|
||||
Reference in New Issue
Block a user