chore: CRLF to LF
This commit is contained in:
+27
-27
@@ -1,27 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "cfg/option.h"
|
||||
|
||||
namespace rawinput {
|
||||
class RawInputManager;
|
||||
}
|
||||
namespace api {
|
||||
class Controller;
|
||||
}
|
||||
|
||||
extern std::filesystem::path MODULE_PATH;
|
||||
extern HANDLE LOG_FILE;
|
||||
extern std::string LOG_FILE_PATH;
|
||||
extern int LAUNCHER_ARGC;
|
||||
extern char **LAUNCHER_ARGV;
|
||||
extern std::unique_ptr<std::vector<Option>> LAUNCHER_OPTIONS;
|
||||
|
||||
extern std::unique_ptr<api::Controller> API_CONTROLLER;
|
||||
extern std::unique_ptr<rawinput::RawInputManager> RI_MGR;
|
||||
extern int main_implementation(int argc, char *argv[]);
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "cfg/option.h"
|
||||
|
||||
namespace rawinput {
|
||||
class RawInputManager;
|
||||
}
|
||||
namespace api {
|
||||
class Controller;
|
||||
}
|
||||
|
||||
extern std::filesystem::path MODULE_PATH;
|
||||
extern HANDLE LOG_FILE;
|
||||
extern std::string LOG_FILE_PATH;
|
||||
extern int LAUNCHER_ARGC;
|
||||
extern char **LAUNCHER_ARGV;
|
||||
extern std::unique_ptr<std::vector<Option>> LAUNCHER_OPTIONS;
|
||||
|
||||
extern std::unique_ptr<api::Controller> API_CONTROLLER;
|
||||
extern std::unique_ptr<rawinput::RawInputManager> RI_MGR;
|
||||
extern int main_implementation(int argc, char *argv[]);
|
||||
|
||||
+270
-270
@@ -1,270 +1,270 @@
|
||||
#include "logger.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/ea3.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#define FOREGROUND_GREY (8)
|
||||
#define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN)
|
||||
#define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN)
|
||||
|
||||
namespace logger {
|
||||
|
||||
// settings
|
||||
bool BLOCKING = false;
|
||||
bool COLOR = true;
|
||||
|
||||
// state
|
||||
static bool RUNNING = false;
|
||||
static WORD DEFAULT_ATTRIBUTES = 0;
|
||||
static std::mutex EVENT_MUTEX;
|
||||
static std::condition_variable EVENT_CV;
|
||||
static std::thread *THREAD = nullptr;
|
||||
static std::mutex OUTPUT_MUTEX;
|
||||
static bool OUTPUT_BUFFER_HOT = false;
|
||||
static std::vector<std::pair<std::string, Style>> OUTPUT_BUFFER1;
|
||||
static std::vector<std::pair<std::string, Style>> OUTPUT_BUFFER2;
|
||||
static std::vector<std::pair<std::string, Style>> *OUTPUT_BUFFER = &OUTPUT_BUFFER1;
|
||||
static std::vector<std::pair<std::string, Style>> *OUTPUT_BUFFER_SWAP = &OUTPUT_BUFFER2;
|
||||
static std::vector<std::pair<LogHook_t, void*>> HOOKS;
|
||||
|
||||
static inline std::vector<std::pair<std::string, Style>> *output_buffer_swap() {
|
||||
OUTPUT_MUTEX.lock();
|
||||
auto buffer = OUTPUT_BUFFER;
|
||||
std::swap(OUTPUT_BUFFER, OUTPUT_BUFFER_SWAP);
|
||||
OUTPUT_MUTEX.unlock();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void save_default_console_attributes(HANDLE hTerminal) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
|
||||
if (GetConsoleScreenBufferInfo(hTerminal, &info)) {
|
||||
DEFAULT_ATTRIBUTES = info.wAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_console_color(HANDLE hTerminal, WORD foreground) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(hTerminal, &info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
info.wAttributes &= ~(info.wAttributes & 0x0F);
|
||||
if (foreground == FOREGROUND_YELLOW)
|
||||
info.wAttributes |= foreground;
|
||||
else
|
||||
info.wAttributes |= foreground | FOREGROUND_INTENSITY;
|
||||
|
||||
SetConsoleTextAttribute(hTerminal, info.wAttributes);
|
||||
}
|
||||
|
||||
static void output_buffer_flush() {
|
||||
|
||||
// get buffer and swap
|
||||
auto buffer = output_buffer_swap();
|
||||
|
||||
// return early if no messages to process
|
||||
if (buffer->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get terminal handle
|
||||
HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (logger::COLOR) {
|
||||
|
||||
// save default terminal attributes
|
||||
if (!DEFAULT_ATTRIBUTES) {
|
||||
save_default_console_attributes(hTerminal);
|
||||
}
|
||||
|
||||
// set initial style
|
||||
set_console_color(hTerminal, FOREGROUND_WHITE);
|
||||
}
|
||||
|
||||
// write to console and file
|
||||
DWORD result;
|
||||
Style last_style = DEFAULT;
|
||||
for (auto &content : *buffer) {
|
||||
|
||||
// set style if color mode enabled
|
||||
if (logger::COLOR && last_style != content.second) {
|
||||
last_style = content.second;
|
||||
|
||||
switch (content.second) {
|
||||
case Style::DEFAULT:
|
||||
set_console_color(hTerminal, FOREGROUND_WHITE);
|
||||
break;
|
||||
case Style::GREY:
|
||||
set_console_color(hTerminal, FOREGROUND_GREY);
|
||||
break;
|
||||
case Style::YELLOW:
|
||||
set_console_color(hTerminal, FOREGROUND_YELLOW);
|
||||
break;
|
||||
case Style::RED:
|
||||
set_console_color(hTerminal, FOREGROUND_RED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// write to console
|
||||
WriteFile(hTerminal, content.first.c_str(), content.first.size(), &result, nullptr);
|
||||
|
||||
// write to file
|
||||
if (LOG_FILE && LOG_FILE != INVALID_HANDLE_VALUE) {
|
||||
WriteFile(LOG_FILE, content.first.c_str(), content.first.size(), &result, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
buffer->clear();
|
||||
|
||||
// reset style
|
||||
if (logger::COLOR) {
|
||||
SetConsoleTextAttribute(hTerminal, DEFAULT_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
|
||||
// don't start if blocking
|
||||
if (BLOCKING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start logging thread
|
||||
RUNNING = true;
|
||||
THREAD = new std::thread([] {
|
||||
std::unique_lock<std::mutex> lock(EVENT_MUTEX);
|
||||
|
||||
// main loop
|
||||
while (RUNNING) {
|
||||
|
||||
// wait for hot buffer
|
||||
EVENT_CV.wait(lock, [] { return OUTPUT_BUFFER_HOT; });
|
||||
OUTPUT_BUFFER_HOT = false;
|
||||
|
||||
// flush buffer
|
||||
output_buffer_flush();
|
||||
}
|
||||
|
||||
// make sure all is written
|
||||
output_buffer_flush();
|
||||
|
||||
// flush writes to disk
|
||||
if (LOG_FILE && LOG_FILE != INVALID_HANDLE_VALUE) {
|
||||
FlushFileBuffers(LOG_FILE);
|
||||
}
|
||||
|
||||
// reset terminal
|
||||
if (logger::COLOR) {
|
||||
HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(hTerminal, DEFAULT_ATTRIBUTES);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void stop() {
|
||||
log_info("logger", "stop");
|
||||
|
||||
// clean up thread if required
|
||||
RUNNING = false;
|
||||
if (THREAD) {
|
||||
|
||||
// fake notify to exit wait loop
|
||||
OUTPUT_BUFFER_HOT = true;
|
||||
EVENT_CV.notify_all();
|
||||
|
||||
// join and clean up
|
||||
THREAD->join();
|
||||
delete THREAD;
|
||||
THREAD = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void push(std::string data, Style color, bool terminate) {
|
||||
|
||||
// log hooks
|
||||
for (auto &hook : HOOKS) {
|
||||
std::string out;
|
||||
|
||||
if (hook.first(hook.second, data, color, out)) {
|
||||
data = std::move(out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if empty
|
||||
if (data.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add to output
|
||||
OUTPUT_MUTEX.lock();
|
||||
OUTPUT_BUFFER->emplace_back(std::move(data), color);
|
||||
if (terminate) {
|
||||
OUTPUT_BUFFER->emplace_back("\r\n", color);
|
||||
}
|
||||
OUTPUT_MUTEX.unlock();
|
||||
|
||||
// check if blocking or the logging thread is not running
|
||||
if (BLOCKING || !RUNNING) {
|
||||
|
||||
// blocking guard
|
||||
static std::mutex blocking_lock;
|
||||
std::lock_guard<std::mutex> blocking_guard(blocking_lock);
|
||||
|
||||
// immediately process logs
|
||||
output_buffer_flush();
|
||||
|
||||
} else {
|
||||
|
||||
// mark buffer as hot
|
||||
std::unique_lock<std::mutex> lock(EVENT_MUTEX);
|
||||
OUTPUT_BUFFER_HOT = true;
|
||||
EVENT_CV.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void hook_add(LogHook_t hook, void *user) {
|
||||
HOOKS.emplace_back(hook, user);
|
||||
}
|
||||
|
||||
void hook_remove(LogHook_t hook, void *user) {
|
||||
HOOKS.erase(std::remove(HOOKS.begin(), HOOKS.end(), std::pair(hook, user)), HOOKS.end());
|
||||
}
|
||||
|
||||
PCBIDFilter::PCBIDFilter() {
|
||||
hook_add(logger::PCBIDFilter::filter, this);
|
||||
}
|
||||
|
||||
PCBIDFilter::~PCBIDFilter() {
|
||||
hook_remove(logger::PCBIDFilter::filter, this);
|
||||
}
|
||||
|
||||
bool PCBIDFilter::filter(void *user, const std::string &data, Style style, std::string &out) {
|
||||
|
||||
// check if PCBID in data
|
||||
if (data.find(avs::ea3::EA3_BOOT_PCBID) != std::string::npos) {
|
||||
|
||||
// replace pcbid
|
||||
out = data;
|
||||
strreplace(out, avs::ea3::EA3_BOOT_PCBID, "[hidden]");
|
||||
return true;
|
||||
}
|
||||
|
||||
// no replacement
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#include "logger.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/ea3.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#define FOREGROUND_GREY (8)
|
||||
#define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN)
|
||||
#define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN)
|
||||
|
||||
namespace logger {
|
||||
|
||||
// settings
|
||||
bool BLOCKING = false;
|
||||
bool COLOR = true;
|
||||
|
||||
// state
|
||||
static bool RUNNING = false;
|
||||
static WORD DEFAULT_ATTRIBUTES = 0;
|
||||
static std::mutex EVENT_MUTEX;
|
||||
static std::condition_variable EVENT_CV;
|
||||
static std::thread *THREAD = nullptr;
|
||||
static std::mutex OUTPUT_MUTEX;
|
||||
static bool OUTPUT_BUFFER_HOT = false;
|
||||
static std::vector<std::pair<std::string, Style>> OUTPUT_BUFFER1;
|
||||
static std::vector<std::pair<std::string, Style>> OUTPUT_BUFFER2;
|
||||
static std::vector<std::pair<std::string, Style>> *OUTPUT_BUFFER = &OUTPUT_BUFFER1;
|
||||
static std::vector<std::pair<std::string, Style>> *OUTPUT_BUFFER_SWAP = &OUTPUT_BUFFER2;
|
||||
static std::vector<std::pair<LogHook_t, void*>> HOOKS;
|
||||
|
||||
static inline std::vector<std::pair<std::string, Style>> *output_buffer_swap() {
|
||||
OUTPUT_MUTEX.lock();
|
||||
auto buffer = OUTPUT_BUFFER;
|
||||
std::swap(OUTPUT_BUFFER, OUTPUT_BUFFER_SWAP);
|
||||
OUTPUT_MUTEX.unlock();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void save_default_console_attributes(HANDLE hTerminal) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
|
||||
if (GetConsoleScreenBufferInfo(hTerminal, &info)) {
|
||||
DEFAULT_ATTRIBUTES = info.wAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_console_color(HANDLE hTerminal, WORD foreground) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(hTerminal, &info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
info.wAttributes &= ~(info.wAttributes & 0x0F);
|
||||
if (foreground == FOREGROUND_YELLOW)
|
||||
info.wAttributes |= foreground;
|
||||
else
|
||||
info.wAttributes |= foreground | FOREGROUND_INTENSITY;
|
||||
|
||||
SetConsoleTextAttribute(hTerminal, info.wAttributes);
|
||||
}
|
||||
|
||||
static void output_buffer_flush() {
|
||||
|
||||
// get buffer and swap
|
||||
auto buffer = output_buffer_swap();
|
||||
|
||||
// return early if no messages to process
|
||||
if (buffer->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get terminal handle
|
||||
HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (logger::COLOR) {
|
||||
|
||||
// save default terminal attributes
|
||||
if (!DEFAULT_ATTRIBUTES) {
|
||||
save_default_console_attributes(hTerminal);
|
||||
}
|
||||
|
||||
// set initial style
|
||||
set_console_color(hTerminal, FOREGROUND_WHITE);
|
||||
}
|
||||
|
||||
// write to console and file
|
||||
DWORD result;
|
||||
Style last_style = DEFAULT;
|
||||
for (auto &content : *buffer) {
|
||||
|
||||
// set style if color mode enabled
|
||||
if (logger::COLOR && last_style != content.second) {
|
||||
last_style = content.second;
|
||||
|
||||
switch (content.second) {
|
||||
case Style::DEFAULT:
|
||||
set_console_color(hTerminal, FOREGROUND_WHITE);
|
||||
break;
|
||||
case Style::GREY:
|
||||
set_console_color(hTerminal, FOREGROUND_GREY);
|
||||
break;
|
||||
case Style::YELLOW:
|
||||
set_console_color(hTerminal, FOREGROUND_YELLOW);
|
||||
break;
|
||||
case Style::RED:
|
||||
set_console_color(hTerminal, FOREGROUND_RED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// write to console
|
||||
WriteFile(hTerminal, content.first.c_str(), content.first.size(), &result, nullptr);
|
||||
|
||||
// write to file
|
||||
if (LOG_FILE && LOG_FILE != INVALID_HANDLE_VALUE) {
|
||||
WriteFile(LOG_FILE, content.first.c_str(), content.first.size(), &result, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
buffer->clear();
|
||||
|
||||
// reset style
|
||||
if (logger::COLOR) {
|
||||
SetConsoleTextAttribute(hTerminal, DEFAULT_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
|
||||
// don't start if blocking
|
||||
if (BLOCKING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start logging thread
|
||||
RUNNING = true;
|
||||
THREAD = new std::thread([] {
|
||||
std::unique_lock<std::mutex> lock(EVENT_MUTEX);
|
||||
|
||||
// main loop
|
||||
while (RUNNING) {
|
||||
|
||||
// wait for hot buffer
|
||||
EVENT_CV.wait(lock, [] { return OUTPUT_BUFFER_HOT; });
|
||||
OUTPUT_BUFFER_HOT = false;
|
||||
|
||||
// flush buffer
|
||||
output_buffer_flush();
|
||||
}
|
||||
|
||||
// make sure all is written
|
||||
output_buffer_flush();
|
||||
|
||||
// flush writes to disk
|
||||
if (LOG_FILE && LOG_FILE != INVALID_HANDLE_VALUE) {
|
||||
FlushFileBuffers(LOG_FILE);
|
||||
}
|
||||
|
||||
// reset terminal
|
||||
if (logger::COLOR) {
|
||||
HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(hTerminal, DEFAULT_ATTRIBUTES);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void stop() {
|
||||
log_info("logger", "stop");
|
||||
|
||||
// clean up thread if required
|
||||
RUNNING = false;
|
||||
if (THREAD) {
|
||||
|
||||
// fake notify to exit wait loop
|
||||
OUTPUT_BUFFER_HOT = true;
|
||||
EVENT_CV.notify_all();
|
||||
|
||||
// join and clean up
|
||||
THREAD->join();
|
||||
delete THREAD;
|
||||
THREAD = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void push(std::string data, Style color, bool terminate) {
|
||||
|
||||
// log hooks
|
||||
for (auto &hook : HOOKS) {
|
||||
std::string out;
|
||||
|
||||
if (hook.first(hook.second, data, color, out)) {
|
||||
data = std::move(out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if empty
|
||||
if (data.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add to output
|
||||
OUTPUT_MUTEX.lock();
|
||||
OUTPUT_BUFFER->emplace_back(std::move(data), color);
|
||||
if (terminate) {
|
||||
OUTPUT_BUFFER->emplace_back("\r\n", color);
|
||||
}
|
||||
OUTPUT_MUTEX.unlock();
|
||||
|
||||
// check if blocking or the logging thread is not running
|
||||
if (BLOCKING || !RUNNING) {
|
||||
|
||||
// blocking guard
|
||||
static std::mutex blocking_lock;
|
||||
std::lock_guard<std::mutex> blocking_guard(blocking_lock);
|
||||
|
||||
// immediately process logs
|
||||
output_buffer_flush();
|
||||
|
||||
} else {
|
||||
|
||||
// mark buffer as hot
|
||||
std::unique_lock<std::mutex> lock(EVENT_MUTEX);
|
||||
OUTPUT_BUFFER_HOT = true;
|
||||
EVENT_CV.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void hook_add(LogHook_t hook, void *user) {
|
||||
HOOKS.emplace_back(hook, user);
|
||||
}
|
||||
|
||||
void hook_remove(LogHook_t hook, void *user) {
|
||||
HOOKS.erase(std::remove(HOOKS.begin(), HOOKS.end(), std::pair(hook, user)), HOOKS.end());
|
||||
}
|
||||
|
||||
PCBIDFilter::PCBIDFilter() {
|
||||
hook_add(logger::PCBIDFilter::filter, this);
|
||||
}
|
||||
|
||||
PCBIDFilter::~PCBIDFilter() {
|
||||
hook_remove(logger::PCBIDFilter::filter, this);
|
||||
}
|
||||
|
||||
bool PCBIDFilter::filter(void *user, const std::string &data, Style style, std::string &out) {
|
||||
|
||||
// check if PCBID in data
|
||||
if (data.find(avs::ea3::EA3_BOOT_PCBID) != std::string::npos) {
|
||||
|
||||
// replace pcbid
|
||||
out = data;
|
||||
strreplace(out, avs::ea3::EA3_BOOT_PCBID, "[hidden]");
|
||||
return true;
|
||||
}
|
||||
|
||||
// no replacement
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+34
-34
@@ -1,34 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace logger {
|
||||
|
||||
// settings
|
||||
extern bool BLOCKING;
|
||||
extern bool COLOR;
|
||||
|
||||
enum Style {
|
||||
DEFAULT = 0,
|
||||
GREY = 1,
|
||||
YELLOW = 2,
|
||||
RED = 3
|
||||
};
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void push(std::string data, Style color, bool terminate = false);
|
||||
|
||||
// log hooks
|
||||
typedef bool (*LogHook_t)(void *user, const std::string &data, Style style, std::string &out);
|
||||
void hook_add(LogHook_t hook, void *user);
|
||||
void hook_remove(LogHook_t hook, void *user);
|
||||
|
||||
class PCBIDFilter {
|
||||
public:
|
||||
PCBIDFilter();
|
||||
~PCBIDFilter();
|
||||
private:
|
||||
static bool filter(void *user, const std::string &data, Style style, std::string &out);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace logger {
|
||||
|
||||
// settings
|
||||
extern bool BLOCKING;
|
||||
extern bool COLOR;
|
||||
|
||||
enum Style {
|
||||
DEFAULT = 0,
|
||||
GREY = 1,
|
||||
YELLOW = 2,
|
||||
RED = 3
|
||||
};
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void push(std::string data, Style color, bool terminate = false);
|
||||
|
||||
// log hooks
|
||||
typedef bool (*LogHook_t)(void *user, const std::string &data, Style style, std::string &out);
|
||||
void hook_add(LogHook_t hook, void *user);
|
||||
void hook_remove(LogHook_t hook, void *user);
|
||||
|
||||
class PCBIDFilter {
|
||||
public:
|
||||
PCBIDFilter();
|
||||
~PCBIDFilter();
|
||||
private:
|
||||
static bool filter(void *user, const std::string &data, Style style, std::string &out);
|
||||
};
|
||||
}
|
||||
|
||||
+151
-151
@@ -1,151 +1,151 @@
|
||||
#include "richpresence.h"
|
||||
#include <external/robin_hood.h>
|
||||
#include "external/discord-rpc/include/discord_rpc.h"
|
||||
#include "util/logging.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
namespace richpresence {
|
||||
|
||||
namespace discord {
|
||||
|
||||
// application IDs
|
||||
static robin_hood::unordered_map<std::string, std::string> APP_IDS = {
|
||||
{"Sound Voltex", "609803246617231377"},
|
||||
{"Beatmania IIDX", "609870727654539266"},
|
||||
{"Jubeat", "609870833032364035"},
|
||||
{"Dance Evolution", "609870916259938314"},
|
||||
{"Beatstream", "609871030609248256"},
|
||||
{"Metal Gear", "609871105213071449"},
|
||||
{"Reflec Beat", "609871162570178560"},
|
||||
{"Pop'n Music", "609871244782862387"},
|
||||
{"Steel Chronicle", "609871515974107136"},
|
||||
{"Road Fighters 3D", "609871634295291914"},
|
||||
{"Museca", "609871785323790336"},
|
||||
{"Bishi Bashi Channel", "609874994989760522"},
|
||||
{"GitaDora", "609875077416091734"},
|
||||
{"Dance Dance Revolution", "609875139890380800"},
|
||||
{"Nostalgia", "609875303711375475"},
|
||||
{"Quiz Magic Academy", "609875394610462720"},
|
||||
{"FutureTomTom", "609875462436421652"},
|
||||
{"Mahjong Fight Club", "609875523665002497"},
|
||||
{"HELLO! Pop'n Music", "609875610474381322"},
|
||||
{"LovePlus", "609875666279858199"},
|
||||
};
|
||||
|
||||
// state
|
||||
std::string APPID_OVERRIDE = "";
|
||||
bool INITIALIZED = false;
|
||||
|
||||
void ready(const DiscordUser *request) {
|
||||
log_warning("richpresence:discord", "ready");
|
||||
}
|
||||
|
||||
void disconnected(int errorCode, const char *message) {
|
||||
log_warning("richpresence:discord", "disconnected");
|
||||
}
|
||||
|
||||
void errored(int errorCode, const char *message) {
|
||||
log_warning("richpresence:discord", "error {}: {}", errorCode, message);
|
||||
}
|
||||
|
||||
void joinGame(const char *joinSecret) {
|
||||
log_warning("richpresence:discord", "joinGame");
|
||||
}
|
||||
|
||||
void spectateGame(const char *spectateSecret) {
|
||||
log_warning("richpresence:discord", "spectateGame");
|
||||
}
|
||||
|
||||
void joinRequest(const DiscordUser *request) {
|
||||
log_warning("richpresence:discord", "joinRequest");
|
||||
}
|
||||
|
||||
// handler object
|
||||
static DiscordEventHandlers handlers {
|
||||
.ready = discord::ready,
|
||||
.disconnected = discord::disconnected,
|
||||
.errored = discord::errored,
|
||||
.joinGame = discord::joinGame,
|
||||
.spectateGame = discord::spectateGame,
|
||||
.joinRequest = discord::joinRequest
|
||||
};
|
||||
|
||||
void update() {
|
||||
|
||||
// check state
|
||||
if (!INITIALIZED)
|
||||
return;
|
||||
|
||||
// update presence
|
||||
DiscordRichPresence presence {};
|
||||
Discord_UpdatePresence(&presence);
|
||||
}
|
||||
|
||||
void init() {
|
||||
|
||||
// check state
|
||||
if (INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get id
|
||||
std::string id = "";
|
||||
if (!APPID_OVERRIDE.empty()) {
|
||||
log_info("richpresence:discord", "using custom APPID: {}", APPID_OVERRIDE);
|
||||
id = APPID_OVERRIDE;
|
||||
} else {
|
||||
auto game_model = eamuse_get_game();
|
||||
if (game_model.empty()) {
|
||||
log_warning("richpresence:discord", "could not get game model");
|
||||
return;
|
||||
}
|
||||
|
||||
id = APP_IDS[game_model];
|
||||
if (id.empty()) {
|
||||
log_warning("richpresence:discord", "did not find app ID for {}", game_model);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize discord
|
||||
Discord_Initialize(id.c_str(), &discord::handlers, 0, nullptr);
|
||||
|
||||
// mark as initialized
|
||||
INITIALIZED = true;
|
||||
log_info("richpresence:discord", "initialized");
|
||||
|
||||
// update once so the presence is displayed
|
||||
update();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
Discord_ClearPresence();
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// state
|
||||
static bool INITIALIZED = false;
|
||||
|
||||
void init() {
|
||||
if (INITIALIZED)
|
||||
return;
|
||||
log_info("richpresence", "initializing");
|
||||
INITIALIZED = true;
|
||||
discord::init();
|
||||
}
|
||||
|
||||
void update(const char *state) {
|
||||
if (!INITIALIZED)
|
||||
return;
|
||||
discord::update();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (!INITIALIZED)
|
||||
return;
|
||||
log_info("richpresence", "shutdown");
|
||||
discord::shutdown();
|
||||
INITIALIZED = false;
|
||||
}
|
||||
}
|
||||
#include "richpresence.h"
|
||||
#include <external/robin_hood.h>
|
||||
#include "external/discord-rpc/include/discord_rpc.h"
|
||||
#include "util/logging.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
namespace richpresence {
|
||||
|
||||
namespace discord {
|
||||
|
||||
// application IDs
|
||||
static robin_hood::unordered_map<std::string, std::string> APP_IDS = {
|
||||
{"Sound Voltex", "609803246617231377"},
|
||||
{"Beatmania IIDX", "609870727654539266"},
|
||||
{"Jubeat", "609870833032364035"},
|
||||
{"Dance Evolution", "609870916259938314"},
|
||||
{"Beatstream", "609871030609248256"},
|
||||
{"Metal Gear", "609871105213071449"},
|
||||
{"Reflec Beat", "609871162570178560"},
|
||||
{"Pop'n Music", "609871244782862387"},
|
||||
{"Steel Chronicle", "609871515974107136"},
|
||||
{"Road Fighters 3D", "609871634295291914"},
|
||||
{"Museca", "609871785323790336"},
|
||||
{"Bishi Bashi Channel", "609874994989760522"},
|
||||
{"GitaDora", "609875077416091734"},
|
||||
{"Dance Dance Revolution", "609875139890380800"},
|
||||
{"Nostalgia", "609875303711375475"},
|
||||
{"Quiz Magic Academy", "609875394610462720"},
|
||||
{"FutureTomTom", "609875462436421652"},
|
||||
{"Mahjong Fight Club", "609875523665002497"},
|
||||
{"HELLO! Pop'n Music", "609875610474381322"},
|
||||
{"LovePlus", "609875666279858199"},
|
||||
};
|
||||
|
||||
// state
|
||||
std::string APPID_OVERRIDE = "";
|
||||
bool INITIALIZED = false;
|
||||
|
||||
void ready(const DiscordUser *request) {
|
||||
log_warning("richpresence:discord", "ready");
|
||||
}
|
||||
|
||||
void disconnected(int errorCode, const char *message) {
|
||||
log_warning("richpresence:discord", "disconnected");
|
||||
}
|
||||
|
||||
void errored(int errorCode, const char *message) {
|
||||
log_warning("richpresence:discord", "error {}: {}", errorCode, message);
|
||||
}
|
||||
|
||||
void joinGame(const char *joinSecret) {
|
||||
log_warning("richpresence:discord", "joinGame");
|
||||
}
|
||||
|
||||
void spectateGame(const char *spectateSecret) {
|
||||
log_warning("richpresence:discord", "spectateGame");
|
||||
}
|
||||
|
||||
void joinRequest(const DiscordUser *request) {
|
||||
log_warning("richpresence:discord", "joinRequest");
|
||||
}
|
||||
|
||||
// handler object
|
||||
static DiscordEventHandlers handlers {
|
||||
.ready = discord::ready,
|
||||
.disconnected = discord::disconnected,
|
||||
.errored = discord::errored,
|
||||
.joinGame = discord::joinGame,
|
||||
.spectateGame = discord::spectateGame,
|
||||
.joinRequest = discord::joinRequest
|
||||
};
|
||||
|
||||
void update() {
|
||||
|
||||
// check state
|
||||
if (!INITIALIZED)
|
||||
return;
|
||||
|
||||
// update presence
|
||||
DiscordRichPresence presence {};
|
||||
Discord_UpdatePresence(&presence);
|
||||
}
|
||||
|
||||
void init() {
|
||||
|
||||
// check state
|
||||
if (INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get id
|
||||
std::string id = "";
|
||||
if (!APPID_OVERRIDE.empty()) {
|
||||
log_info("richpresence:discord", "using custom APPID: {}", APPID_OVERRIDE);
|
||||
id = APPID_OVERRIDE;
|
||||
} else {
|
||||
auto game_model = eamuse_get_game();
|
||||
if (game_model.empty()) {
|
||||
log_warning("richpresence:discord", "could not get game model");
|
||||
return;
|
||||
}
|
||||
|
||||
id = APP_IDS[game_model];
|
||||
if (id.empty()) {
|
||||
log_warning("richpresence:discord", "did not find app ID for {}", game_model);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize discord
|
||||
Discord_Initialize(id.c_str(), &discord::handlers, 0, nullptr);
|
||||
|
||||
// mark as initialized
|
||||
INITIALIZED = true;
|
||||
log_info("richpresence:discord", "initialized");
|
||||
|
||||
// update once so the presence is displayed
|
||||
update();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
Discord_ClearPresence();
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// state
|
||||
static bool INITIALIZED = false;
|
||||
|
||||
void init() {
|
||||
if (INITIALIZED)
|
||||
return;
|
||||
log_info("richpresence", "initializing");
|
||||
INITIALIZED = true;
|
||||
discord::init();
|
||||
}
|
||||
|
||||
void update(const char *state) {
|
||||
if (!INITIALIZED)
|
||||
return;
|
||||
discord::update();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (!INITIALIZED)
|
||||
return;
|
||||
log_info("richpresence", "shutdown");
|
||||
discord::shutdown();
|
||||
INITIALIZED = false;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-15
@@ -1,15 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace richpresence {
|
||||
|
||||
// settings
|
||||
namespace discord {
|
||||
extern std::string APPID_OVERRIDE;
|
||||
}
|
||||
|
||||
void init();
|
||||
void update(const char *state);
|
||||
void shutdown();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace richpresence {
|
||||
|
||||
// settings
|
||||
namespace discord {
|
||||
extern std::string APPID_OVERRIDE;
|
||||
}
|
||||
|
||||
void init();
|
||||
void update(const char *state);
|
||||
void shutdown();
|
||||
}
|
||||
|
||||
+111
-111
@@ -1,111 +1,111 @@
|
||||
// included early to avoid warning
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "shutdown.h"
|
||||
|
||||
#include "api/controller.h"
|
||||
#include "easrv/easrv.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/vrutil.h"
|
||||
#include "hooks/audio/audio.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "launcher.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace launcher {
|
||||
|
||||
void stop_subsystems() {
|
||||
log_info("launcher", "stopping subsystems");
|
||||
|
||||
// flush/stop logger
|
||||
logger::stop();
|
||||
|
||||
// VR
|
||||
vrutil::shutdown();
|
||||
|
||||
// stop ea server
|
||||
easrv_shutdown();
|
||||
|
||||
// free api sockets
|
||||
if (API_CONTROLLER) {
|
||||
API_CONTROLLER->free_socket();
|
||||
}
|
||||
|
||||
// notify audio hook
|
||||
hooks::audio::stop();
|
||||
|
||||
// stop raw input
|
||||
if (RI_MGR) {
|
||||
RI_MGR->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void kill(UINT exit_code) {
|
||||
|
||||
// terminate
|
||||
TerminateProcess(GetCurrentProcess(), exit_code);
|
||||
}
|
||||
|
||||
void shutdown(UINT exit_code) {
|
||||
|
||||
// force exit after 1s
|
||||
std::thread force_thread([exit_code] {
|
||||
Sleep(1000);
|
||||
log_info("launcher", "force shutdown");
|
||||
kill(exit_code);
|
||||
return nullptr;
|
||||
});
|
||||
force_thread.detach();
|
||||
|
||||
// stop all subsystems
|
||||
stop_subsystems();
|
||||
|
||||
// terminate
|
||||
kill(exit_code);
|
||||
}
|
||||
|
||||
static void restart_spawn() {
|
||||
|
||||
// never do this twice
|
||||
static bool already_done = false;
|
||||
if (already_done) {
|
||||
return;
|
||||
} else {
|
||||
already_done = true;
|
||||
}
|
||||
|
||||
// start the process using the same args
|
||||
if (LAUNCHER_ARGC > 0) {
|
||||
|
||||
// build cmd line
|
||||
std::stringstream cmd_line;
|
||||
cmd_line << "START \"\" ";
|
||||
for (int i = 0; i < LAUNCHER_ARGC; i++)
|
||||
cmd_line << " \"" << LAUNCHER_ARGV[i] << "\"";
|
||||
|
||||
// run command
|
||||
system(cmd_line.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void restart() {
|
||||
|
||||
// force restart after 1s
|
||||
std::thread force_thread([] {
|
||||
Sleep(1000);
|
||||
log_info("launcher", "force restart");
|
||||
restart_spawn();
|
||||
launcher::kill(0);
|
||||
return nullptr;
|
||||
});
|
||||
force_thread.detach();
|
||||
|
||||
// clean up before restart so resources can be reclaimed
|
||||
stop_subsystems();
|
||||
|
||||
// spawn new and terminate this process
|
||||
restart_spawn();
|
||||
launcher::kill(0);
|
||||
}
|
||||
}
|
||||
// included early to avoid warning
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "shutdown.h"
|
||||
|
||||
#include "api/controller.h"
|
||||
#include "easrv/easrv.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/vrutil.h"
|
||||
#include "hooks/audio/audio.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "launcher.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace launcher {
|
||||
|
||||
void stop_subsystems() {
|
||||
log_info("launcher", "stopping subsystems");
|
||||
|
||||
// flush/stop logger
|
||||
logger::stop();
|
||||
|
||||
// VR
|
||||
vrutil::shutdown();
|
||||
|
||||
// stop ea server
|
||||
easrv_shutdown();
|
||||
|
||||
// free api sockets
|
||||
if (API_CONTROLLER) {
|
||||
API_CONTROLLER->free_socket();
|
||||
}
|
||||
|
||||
// notify audio hook
|
||||
hooks::audio::stop();
|
||||
|
||||
// stop raw input
|
||||
if (RI_MGR) {
|
||||
RI_MGR->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void kill(UINT exit_code) {
|
||||
|
||||
// terminate
|
||||
TerminateProcess(GetCurrentProcess(), exit_code);
|
||||
}
|
||||
|
||||
void shutdown(UINT exit_code) {
|
||||
|
||||
// force exit after 1s
|
||||
std::thread force_thread([exit_code] {
|
||||
Sleep(1000);
|
||||
log_info("launcher", "force shutdown");
|
||||
kill(exit_code);
|
||||
return nullptr;
|
||||
});
|
||||
force_thread.detach();
|
||||
|
||||
// stop all subsystems
|
||||
stop_subsystems();
|
||||
|
||||
// terminate
|
||||
kill(exit_code);
|
||||
}
|
||||
|
||||
static void restart_spawn() {
|
||||
|
||||
// never do this twice
|
||||
static bool already_done = false;
|
||||
if (already_done) {
|
||||
return;
|
||||
} else {
|
||||
already_done = true;
|
||||
}
|
||||
|
||||
// start the process using the same args
|
||||
if (LAUNCHER_ARGC > 0) {
|
||||
|
||||
// build cmd line
|
||||
std::stringstream cmd_line;
|
||||
cmd_line << "START \"\" ";
|
||||
for (int i = 0; i < LAUNCHER_ARGC; i++)
|
||||
cmd_line << " \"" << LAUNCHER_ARGV[i] << "\"";
|
||||
|
||||
// run command
|
||||
system(cmd_line.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void restart() {
|
||||
|
||||
// force restart after 1s
|
||||
std::thread force_thread([] {
|
||||
Sleep(1000);
|
||||
log_info("launcher", "force restart");
|
||||
restart_spawn();
|
||||
launcher::kill(0);
|
||||
return nullptr;
|
||||
});
|
||||
force_thread.detach();
|
||||
|
||||
// clean up before restart so resources can be reclaimed
|
||||
stop_subsystems();
|
||||
|
||||
// spawn new and terminate this process
|
||||
restart_spawn();
|
||||
launcher::kill(0);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace launcher {
|
||||
void stop_subsystems();
|
||||
void kill(UINT exit_code = EXIT_FAILURE);
|
||||
void shutdown(UINT exit_code = EXIT_SUCCESS);
|
||||
void restart();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace launcher {
|
||||
void stop_subsystems();
|
||||
void kill(UINT exit_code = EXIT_FAILURE);
|
||||
void shutdown(UINT exit_code = EXIT_SUCCESS);
|
||||
void restart();
|
||||
}
|
||||
|
||||
+222
-222
@@ -1,222 +1,222 @@
|
||||
#include "signal.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "external/stackwalker/stackwalker.h"
|
||||
#include "hooks/libraryhook.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "cfg/configurator.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
// MSVC compatibility
|
||||
#ifdef exception_code
|
||||
#undef exception_code
|
||||
#endif
|
||||
|
||||
static decltype(MiniDumpWriteDump) *MiniDumpWriteDump_local = nullptr;
|
||||
|
||||
namespace launcher::signal {
|
||||
|
||||
// settings
|
||||
bool DISABLE = false;
|
||||
}
|
||||
|
||||
#define V(variant) case variant: return #variant
|
||||
|
||||
static std::string control_code(DWORD dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
V(CTRL_C_EVENT);
|
||||
V(CTRL_BREAK_EVENT);
|
||||
V(CTRL_CLOSE_EVENT);
|
||||
V(CTRL_LOGOFF_EVENT);
|
||||
V(CTRL_SHUTDOWN_EVENT);
|
||||
default:
|
||||
return "Unknown(0x" + to_hex(dwCtrlType) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string exception_code(struct _EXCEPTION_RECORD *ExceptionRecord) {
|
||||
switch (ExceptionRecord->ExceptionCode) {
|
||||
V(EXCEPTION_ACCESS_VIOLATION);
|
||||
V(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
|
||||
V(EXCEPTION_BREAKPOINT);
|
||||
V(EXCEPTION_DATATYPE_MISALIGNMENT);
|
||||
V(EXCEPTION_FLT_DENORMAL_OPERAND);
|
||||
V(EXCEPTION_FLT_DIVIDE_BY_ZERO);
|
||||
V(EXCEPTION_FLT_INEXACT_RESULT);
|
||||
V(EXCEPTION_FLT_INVALID_OPERATION);
|
||||
V(EXCEPTION_FLT_OVERFLOW);
|
||||
V(EXCEPTION_FLT_STACK_CHECK);
|
||||
V(EXCEPTION_FLT_UNDERFLOW);
|
||||
V(EXCEPTION_ILLEGAL_INSTRUCTION);
|
||||
V(EXCEPTION_IN_PAGE_ERROR);
|
||||
V(EXCEPTION_INT_DIVIDE_BY_ZERO);
|
||||
V(EXCEPTION_INT_OVERFLOW);
|
||||
V(EXCEPTION_INVALID_DISPOSITION);
|
||||
V(EXCEPTION_NONCONTINUABLE_EXCEPTION);
|
||||
V(EXCEPTION_PRIV_INSTRUCTION);
|
||||
V(EXCEPTION_SINGLE_STEP);
|
||||
V(EXCEPTION_STACK_OVERFLOW);
|
||||
V(DBG_CONTROL_C);
|
||||
default:
|
||||
return "Unknown(0x" + to_hex(ExceptionRecord->ExceptionCode) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
#undef V
|
||||
|
||||
static BOOL WINAPI HandlerRoutine(DWORD dwCtrlType) {
|
||||
log_info("signal", "console ctrl handler called: {}", control_code(dwCtrlType));
|
||||
|
||||
if (dwCtrlType == CTRL_C_EVENT) {
|
||||
launcher::shutdown();
|
||||
} else if (dwCtrlType == CTRL_CLOSE_EVENT) {
|
||||
launcher::shutdown();
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {
|
||||
|
||||
// ignore signal if disabled or no exception info provided
|
||||
if (!launcher::signal::DISABLE && ExceptionInfo != nullptr) {
|
||||
|
||||
// get exception record
|
||||
struct _EXCEPTION_RECORD *ExceptionRecord = ExceptionInfo->ExceptionRecord;
|
||||
|
||||
// print signal
|
||||
log_warning("signal", "exception raised: {}", exception_code(ExceptionRecord));
|
||||
|
||||
// walk the exception chain
|
||||
struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord;
|
||||
while (record_cause != nullptr) {
|
||||
log_warning("signal", "caused by: {}", exception_code(record_cause));
|
||||
record_cause = record_cause->ExceptionRecord;
|
||||
}
|
||||
|
||||
// print stacktrace
|
||||
StackWalker sw;
|
||||
log_info("signal", "printing callstack");
|
||||
if (!sw.ShowCallstack(GetCurrentThread(), ExceptionInfo->ContextRecord)) {
|
||||
log_warning("signal", "failed to print callstack");
|
||||
}
|
||||
|
||||
if (MiniDumpWriteDump_local != nullptr) {
|
||||
HANDLE minidump_file = CreateFileA(
|
||||
"minidump.dmp",
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
nullptr,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
|
||||
if (minidump_file != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION ExceptionParam {};
|
||||
ExceptionParam.ThreadId = GetCurrentThreadId();
|
||||
ExceptionParam.ExceptionPointers = ExceptionInfo;
|
||||
ExceptionParam.ClientPointers = FALSE;
|
||||
|
||||
MiniDumpWriteDump_local(
|
||||
GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
minidump_file,
|
||||
MiniDumpNormal,
|
||||
&ExceptionParam,
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
CloseHandle(minidump_file);
|
||||
} else {
|
||||
log_warning("signal", "failed to create 'minidump.dmp' for minidump: 0x{:08x}",
|
||||
GetLastError());
|
||||
}
|
||||
} else {
|
||||
log_warning("signal", "minidump creation function not available, skipping");
|
||||
}
|
||||
|
||||
log_fatal("signal", "end");
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
static BOOL WINAPI SetConsoleCtrlHandler_hook(PHANDLER_ROUTINE pHandlerRoutine, BOOL Add) {
|
||||
log_misc("signal", "SetConsoleCtrlHandler hook hit");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
||||
{
|
||||
log_info("signal", "SetUnhandledExceptionFilter hook hit");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||||
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void launcher::signal::attach() {
|
||||
|
||||
if (launcher::signal::DISABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("signal", "attaching...");
|
||||
|
||||
// set a `std::terminate` handler so `std::abort()` is not called by default
|
||||
std::set_terminate([]() {
|
||||
log_warning("signal", "std::terminate called");
|
||||
|
||||
launcher::kill();
|
||||
});
|
||||
|
||||
// NOTE: inline hooks are not used here as they have caused EXCEPTION_ACCESS_VIOLATION in the past
|
||||
// when hooking these methods
|
||||
|
||||
// hook relevant functions
|
||||
libraryhook_hook_proc("SetConsoleCtrlHandler", SetConsoleCtrlHandler_hook);
|
||||
libraryhook_hook_proc("SetUnhandledExceptionFilter", SetUnhandledExceptionFilter_hook);
|
||||
libraryhook_hook_proc("AddVectoredExceptionHandler", AddVectoredExceptionHandler_hook);
|
||||
libraryhook_enable();
|
||||
|
||||
// hook in all loaded modules
|
||||
detour::iat_try("SetConsoleCtrlHandler", SetConsoleCtrlHandler_hook);
|
||||
detour::iat_try("SetUnhandledExceptionFilter", SetUnhandledExceptionFilter_hook);
|
||||
detour::iat_try("AddVectoredExceptionHandler", AddVectoredExceptionHandler_hook);
|
||||
|
||||
log_info("signal", "attached");
|
||||
}
|
||||
|
||||
void launcher::signal::init() {
|
||||
|
||||
// load debug help library
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
auto dbghelp = libutils::try_library("dbghelp.dll");
|
||||
|
||||
if (dbghelp != nullptr) {
|
||||
MiniDumpWriteDump_local = libutils::try_proc<decltype(MiniDumpWriteDump) *>(
|
||||
dbghelp, "MiniDumpWriteDump");
|
||||
}
|
||||
}
|
||||
|
||||
// register our console ctrl handler
|
||||
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
|
||||
|
||||
// register our exception handler
|
||||
SetUnhandledExceptionFilter(TopLevelExceptionFilter);
|
||||
}
|
||||
#include "signal.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "external/stackwalker/stackwalker.h"
|
||||
#include "hooks/libraryhook.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "cfg/configurator.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
// MSVC compatibility
|
||||
#ifdef exception_code
|
||||
#undef exception_code
|
||||
#endif
|
||||
|
||||
static decltype(MiniDumpWriteDump) *MiniDumpWriteDump_local = nullptr;
|
||||
|
||||
namespace launcher::signal {
|
||||
|
||||
// settings
|
||||
bool DISABLE = false;
|
||||
}
|
||||
|
||||
#define V(variant) case variant: return #variant
|
||||
|
||||
static std::string control_code(DWORD dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
V(CTRL_C_EVENT);
|
||||
V(CTRL_BREAK_EVENT);
|
||||
V(CTRL_CLOSE_EVENT);
|
||||
V(CTRL_LOGOFF_EVENT);
|
||||
V(CTRL_SHUTDOWN_EVENT);
|
||||
default:
|
||||
return "Unknown(0x" + to_hex(dwCtrlType) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string exception_code(struct _EXCEPTION_RECORD *ExceptionRecord) {
|
||||
switch (ExceptionRecord->ExceptionCode) {
|
||||
V(EXCEPTION_ACCESS_VIOLATION);
|
||||
V(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
|
||||
V(EXCEPTION_BREAKPOINT);
|
||||
V(EXCEPTION_DATATYPE_MISALIGNMENT);
|
||||
V(EXCEPTION_FLT_DENORMAL_OPERAND);
|
||||
V(EXCEPTION_FLT_DIVIDE_BY_ZERO);
|
||||
V(EXCEPTION_FLT_INEXACT_RESULT);
|
||||
V(EXCEPTION_FLT_INVALID_OPERATION);
|
||||
V(EXCEPTION_FLT_OVERFLOW);
|
||||
V(EXCEPTION_FLT_STACK_CHECK);
|
||||
V(EXCEPTION_FLT_UNDERFLOW);
|
||||
V(EXCEPTION_ILLEGAL_INSTRUCTION);
|
||||
V(EXCEPTION_IN_PAGE_ERROR);
|
||||
V(EXCEPTION_INT_DIVIDE_BY_ZERO);
|
||||
V(EXCEPTION_INT_OVERFLOW);
|
||||
V(EXCEPTION_INVALID_DISPOSITION);
|
||||
V(EXCEPTION_NONCONTINUABLE_EXCEPTION);
|
||||
V(EXCEPTION_PRIV_INSTRUCTION);
|
||||
V(EXCEPTION_SINGLE_STEP);
|
||||
V(EXCEPTION_STACK_OVERFLOW);
|
||||
V(DBG_CONTROL_C);
|
||||
default:
|
||||
return "Unknown(0x" + to_hex(ExceptionRecord->ExceptionCode) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
#undef V
|
||||
|
||||
static BOOL WINAPI HandlerRoutine(DWORD dwCtrlType) {
|
||||
log_info("signal", "console ctrl handler called: {}", control_code(dwCtrlType));
|
||||
|
||||
if (dwCtrlType == CTRL_C_EVENT) {
|
||||
launcher::shutdown();
|
||||
} else if (dwCtrlType == CTRL_CLOSE_EVENT) {
|
||||
launcher::shutdown();
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {
|
||||
|
||||
// ignore signal if disabled or no exception info provided
|
||||
if (!launcher::signal::DISABLE && ExceptionInfo != nullptr) {
|
||||
|
||||
// get exception record
|
||||
struct _EXCEPTION_RECORD *ExceptionRecord = ExceptionInfo->ExceptionRecord;
|
||||
|
||||
// print signal
|
||||
log_warning("signal", "exception raised: {}", exception_code(ExceptionRecord));
|
||||
|
||||
// walk the exception chain
|
||||
struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord;
|
||||
while (record_cause != nullptr) {
|
||||
log_warning("signal", "caused by: {}", exception_code(record_cause));
|
||||
record_cause = record_cause->ExceptionRecord;
|
||||
}
|
||||
|
||||
// print stacktrace
|
||||
StackWalker sw;
|
||||
log_info("signal", "printing callstack");
|
||||
if (!sw.ShowCallstack(GetCurrentThread(), ExceptionInfo->ContextRecord)) {
|
||||
log_warning("signal", "failed to print callstack");
|
||||
}
|
||||
|
||||
if (MiniDumpWriteDump_local != nullptr) {
|
||||
HANDLE minidump_file = CreateFileA(
|
||||
"minidump.dmp",
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
nullptr,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
|
||||
if (minidump_file != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION ExceptionParam {};
|
||||
ExceptionParam.ThreadId = GetCurrentThreadId();
|
||||
ExceptionParam.ExceptionPointers = ExceptionInfo;
|
||||
ExceptionParam.ClientPointers = FALSE;
|
||||
|
||||
MiniDumpWriteDump_local(
|
||||
GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
minidump_file,
|
||||
MiniDumpNormal,
|
||||
&ExceptionParam,
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
CloseHandle(minidump_file);
|
||||
} else {
|
||||
log_warning("signal", "failed to create 'minidump.dmp' for minidump: 0x{:08x}",
|
||||
GetLastError());
|
||||
}
|
||||
} else {
|
||||
log_warning("signal", "minidump creation function not available, skipping");
|
||||
}
|
||||
|
||||
log_fatal("signal", "end");
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
static BOOL WINAPI SetConsoleCtrlHandler_hook(PHANDLER_ROUTINE pHandlerRoutine, BOOL Add) {
|
||||
log_misc("signal", "SetConsoleCtrlHandler hook hit");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
||||
{
|
||||
log_info("signal", "SetUnhandledExceptionFilter hook hit");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||||
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void launcher::signal::attach() {
|
||||
|
||||
if (launcher::signal::DISABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("signal", "attaching...");
|
||||
|
||||
// set a `std::terminate` handler so `std::abort()` is not called by default
|
||||
std::set_terminate([]() {
|
||||
log_warning("signal", "std::terminate called");
|
||||
|
||||
launcher::kill();
|
||||
});
|
||||
|
||||
// NOTE: inline hooks are not used here as they have caused EXCEPTION_ACCESS_VIOLATION in the past
|
||||
// when hooking these methods
|
||||
|
||||
// hook relevant functions
|
||||
libraryhook_hook_proc("SetConsoleCtrlHandler", SetConsoleCtrlHandler_hook);
|
||||
libraryhook_hook_proc("SetUnhandledExceptionFilter", SetUnhandledExceptionFilter_hook);
|
||||
libraryhook_hook_proc("AddVectoredExceptionHandler", AddVectoredExceptionHandler_hook);
|
||||
libraryhook_enable();
|
||||
|
||||
// hook in all loaded modules
|
||||
detour::iat_try("SetConsoleCtrlHandler", SetConsoleCtrlHandler_hook);
|
||||
detour::iat_try("SetUnhandledExceptionFilter", SetUnhandledExceptionFilter_hook);
|
||||
detour::iat_try("AddVectoredExceptionHandler", AddVectoredExceptionHandler_hook);
|
||||
|
||||
log_info("signal", "attached");
|
||||
}
|
||||
|
||||
void launcher::signal::init() {
|
||||
|
||||
// load debug help library
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
auto dbghelp = libutils::try_library("dbghelp.dll");
|
||||
|
||||
if (dbghelp != nullptr) {
|
||||
MiniDumpWriteDump_local = libutils::try_proc<decltype(MiniDumpWriteDump) *>(
|
||||
dbghelp, "MiniDumpWriteDump");
|
||||
}
|
||||
}
|
||||
|
||||
// register our console ctrl handler
|
||||
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
|
||||
|
||||
// register our exception handler
|
||||
SetUnhandledExceptionFilter(TopLevelExceptionFilter);
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace launcher::signal {
|
||||
|
||||
// settings
|
||||
extern bool DISABLE;
|
||||
|
||||
//void print_stacktrace();
|
||||
void attach();
|
||||
void init();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
namespace launcher::signal {
|
||||
|
||||
// settings
|
||||
extern bool DISABLE;
|
||||
|
||||
//void print_stacktrace();
|
||||
void attach();
|
||||
void init();
|
||||
}
|
||||
|
||||
+99
-99
@@ -1,99 +1,99 @@
|
||||
#include "superexit.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "windows.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "touch/touch.h"
|
||||
|
||||
|
||||
namespace superexit {
|
||||
|
||||
static std::thread *THREAD = nullptr;
|
||||
static bool THREAD_RUNNING = false;
|
||||
|
||||
bool has_focus() {
|
||||
HWND fg_wnd = GetForegroundWindow();
|
||||
if (fg_wnd == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (fg_wnd == SPICETOUCH_TOUCH_HWND) {
|
||||
return true;
|
||||
}
|
||||
DWORD fg_pid;
|
||||
GetWindowThreadProcessId(fg_wnd, &fg_pid);
|
||||
return fg_pid == GetCurrentProcessId();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
|
||||
// check if already running
|
||||
if (THREAD)
|
||||
return;
|
||||
|
||||
// create new thread
|
||||
THREAD_RUNNING = true;
|
||||
THREAD = new std::thread([] {
|
||||
|
||||
// log
|
||||
log_info("superexit", "enabled");
|
||||
|
||||
// set variable to false to stop
|
||||
while (THREAD_RUNNING) {
|
||||
|
||||
// check rawinput for ALT+F4
|
||||
bool rawinput_exit = false;
|
||||
if (RI_MGR != nullptr) {
|
||||
auto devices = RI_MGR->devices_get();
|
||||
for (auto &device : devices) {
|
||||
switch (device.type) {
|
||||
case rawinput::KEYBOARD: {
|
||||
auto &key_states = device.keyboardInfo->key_states;
|
||||
for (int page_index = 0; page_index < 1024; page_index += 256) {
|
||||
if (key_states[page_index + VK_MENU]
|
||||
&& key_states[page_index + VK_F4]) {
|
||||
rawinput_exit = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for exit
|
||||
if (rawinput_exit || (GetAsyncKeyState(VK_MENU) && GetAsyncKeyState(VK_F4))) {
|
||||
|
||||
// check if in focus
|
||||
if (has_focus()) {
|
||||
log_info("superexit", "detected ALT+F4, exiting...");
|
||||
launcher::shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// slow down
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void disable() {
|
||||
|
||||
// stop old thread
|
||||
THREAD_RUNNING = false;
|
||||
THREAD->join();
|
||||
|
||||
// delete thread
|
||||
delete THREAD;
|
||||
THREAD = nullptr;
|
||||
|
||||
// log
|
||||
log_info("superexit", "disabled");
|
||||
}
|
||||
}
|
||||
#include "superexit.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "windows.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "touch/touch.h"
|
||||
|
||||
|
||||
namespace superexit {
|
||||
|
||||
static std::thread *THREAD = nullptr;
|
||||
static bool THREAD_RUNNING = false;
|
||||
|
||||
bool has_focus() {
|
||||
HWND fg_wnd = GetForegroundWindow();
|
||||
if (fg_wnd == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (fg_wnd == SPICETOUCH_TOUCH_HWND) {
|
||||
return true;
|
||||
}
|
||||
DWORD fg_pid;
|
||||
GetWindowThreadProcessId(fg_wnd, &fg_pid);
|
||||
return fg_pid == GetCurrentProcessId();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
|
||||
// check if already running
|
||||
if (THREAD)
|
||||
return;
|
||||
|
||||
// create new thread
|
||||
THREAD_RUNNING = true;
|
||||
THREAD = new std::thread([] {
|
||||
|
||||
// log
|
||||
log_info("superexit", "enabled");
|
||||
|
||||
// set variable to false to stop
|
||||
while (THREAD_RUNNING) {
|
||||
|
||||
// check rawinput for ALT+F4
|
||||
bool rawinput_exit = false;
|
||||
if (RI_MGR != nullptr) {
|
||||
auto devices = RI_MGR->devices_get();
|
||||
for (auto &device : devices) {
|
||||
switch (device.type) {
|
||||
case rawinput::KEYBOARD: {
|
||||
auto &key_states = device.keyboardInfo->key_states;
|
||||
for (int page_index = 0; page_index < 1024; page_index += 256) {
|
||||
if (key_states[page_index + VK_MENU]
|
||||
&& key_states[page_index + VK_F4]) {
|
||||
rawinput_exit = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for exit
|
||||
if (rawinput_exit || (GetAsyncKeyState(VK_MENU) && GetAsyncKeyState(VK_F4))) {
|
||||
|
||||
// check if in focus
|
||||
if (has_focus()) {
|
||||
log_info("superexit", "detected ALT+F4, exiting...");
|
||||
launcher::shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// slow down
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void disable() {
|
||||
|
||||
// stop old thread
|
||||
THREAD_RUNNING = false;
|
||||
THREAD->join();
|
||||
|
||||
// delete thread
|
||||
delete THREAD;
|
||||
THREAD = nullptr;
|
||||
|
||||
// log
|
||||
log_info("superexit", "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace superexit {
|
||||
bool has_focus();
|
||||
void enable();
|
||||
void disable();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
namespace superexit {
|
||||
bool has_focus();
|
||||
void enable();
|
||||
void disable();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user