354d022967
commit f44d146e4ac906ff898e8968c6857fd2280f6d20 Author: project-bo4 <127137346+project-bo4@users.noreply.github.com> Date: Thu May 11 13:39:29 2023 -0700 remove lpc package from repository commit 29fb0f63e50de468ab0b9db35f7e8fdadf58afc3 Author: project-bo4 <themanwithantidote@gmail.com> Date: Thu May 11 13:06:13 2023 -0700 generic improvements + added identity configuration + objectstore improvements + added battlenet 'US' servers to blocklist + some other minor improvements commit 5d5e31099ebcce5a637b9a02d4936a97fb0802a7 Author: project-bo4 <themanwithantidote@gmail.com> Date: Sat Mar 25 16:32:52 2023 -0700 lpc improvements + fix lpc issue with foreign languages(non-english) not being considered in listing + check lpc files pre-start and warn user if missing any of core items commit 2de1a54b6f4cc5c44dc906871021cfbc85057ca9 Author: project-bo4 <themanwithantidote@gmail.com> Date: Thu Mar 23 12:45:56 2023 -0700 demonware improvements + handle object store uploadUserObject + silence bdUNK125 commit 710dcc3ec6178071d67c27e5bf6705f08cabe7e2 Author: project-bo4 <themanwithantidote@gmail.com> Date: Mon Mar 20 13:43:56 2023 -0700 forgot to update names commit 217682dfb07b35f7a09399fb2698924bb7fe8b77 Author: project-bo4 <themanwithantidote@gmail.com> Date: Mon Mar 20 11:09:18 2023 -0700 upload lpc and update readme commit 83f812b33b90791a8d13b9468f27da51e0a4f2b9 Author: project-bo4 <themanwithantidote@gmail.com> Date: Mon Mar 20 10:53:43 2023 -0700 demonware emulator + demonware emulator + platform name and id + xxhash and picoproto - remove g_runframe hook -disable discovery(save time) + improvements to utils + add new resources
213 lines
5.9 KiB
C++
213 lines
5.9 KiB
C++
#include <std_include.hpp>
|
|
#include "loader/component_loader.hpp"
|
|
#include "definitions/t8_engine.hpp"
|
|
|
|
#include <utils/hook.hpp>
|
|
#include <utils/io.hpp>
|
|
#include <utils/string.hpp>
|
|
#include <utils/thread.hpp>
|
|
#include <utils/compression.hpp>
|
|
#include <exception/minidump.hpp>
|
|
|
|
namespace exception
|
|
{
|
|
namespace
|
|
{
|
|
DWORD main_thread_id{};
|
|
|
|
thread_local struct
|
|
{
|
|
DWORD code = 0;
|
|
PVOID address = nullptr;
|
|
} exception_data{};
|
|
|
|
struct
|
|
{
|
|
std::chrono::time_point<std::chrono::high_resolution_clock> last_recovery{};
|
|
std::atomic<int> recovery_counts = {0};
|
|
} recovery_data{};
|
|
|
|
bool is_game_thread()
|
|
{
|
|
return main_thread_id == GetCurrentThreadId();
|
|
}
|
|
|
|
bool is_exception_interval_too_short()
|
|
{
|
|
const auto delta = std::chrono::high_resolution_clock::now() - recovery_data.last_recovery;
|
|
return delta < 1min;
|
|
}
|
|
|
|
bool too_many_exceptions_occured()
|
|
{
|
|
return recovery_data.recovery_counts >= 3;
|
|
}
|
|
|
|
volatile bool& is_initialized()
|
|
{
|
|
static volatile bool initialized = false;
|
|
return initialized;
|
|
}
|
|
|
|
bool is_recoverable()
|
|
{
|
|
return is_initialized()
|
|
&& is_game_thread()
|
|
&& !is_exception_interval_too_short()
|
|
&& !too_many_exceptions_occured();
|
|
}
|
|
|
|
void show_mouse_cursor()
|
|
{
|
|
while (ShowCursor(TRUE) < 0);
|
|
}
|
|
|
|
void display_error_dialog()
|
|
{
|
|
const std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p (0x%p).\n"
|
|
"A minidump has been written.\n",
|
|
exception_data.code, exception_data.address,
|
|
reverse_b(reinterpret_cast<uint64_t>(exception_data.address)));
|
|
|
|
utils::thread::suspend_other_threads();
|
|
show_mouse_cursor();
|
|
|
|
MessageBoxA(nullptr, error_str.data(), "Project-BO4 ERROR", MB_ICONERROR);
|
|
TerminateProcess(GetCurrentProcess(), exception_data.code);
|
|
}
|
|
|
|
void reset_state()
|
|
{
|
|
if (is_recoverable())
|
|
{
|
|
recovery_data.last_recovery = std::chrono::high_resolution_clock::now();
|
|
++recovery_data.recovery_counts;
|
|
|
|
game::Com_Error(game::ERR_DROP, "Fatal error (0x%08X) at 0x%p (0x%p).\nA minidump has been written.\n\n"
|
|
"Project-BO4 has tried to recover your game, but it might not run stable anymore.\n\n"
|
|
"Make sure to update your graphics card drivers and install operating system updates!\n"
|
|
"Closing or restarting Steam might also help.",
|
|
exception_data.code, exception_data.address,
|
|
reverse_b(reinterpret_cast<uint64_t>(exception_data.address)));
|
|
}
|
|
else
|
|
{
|
|
display_error_dialog();
|
|
}
|
|
}
|
|
|
|
size_t get_reset_state_stub()
|
|
{
|
|
static auto* stub = utils::hook::assemble([](utils::hook::assembler& a)
|
|
{
|
|
a.sub(rsp, 0x10);
|
|
a.or_(rsp, 0x8);
|
|
a.jmp(reset_state);
|
|
});
|
|
|
|
return reinterpret_cast<size_t>(stub);
|
|
}
|
|
|
|
std::string get_timestamp()
|
|
{
|
|
tm ltime{};
|
|
char timestamp[MAX_PATH] = {0};
|
|
const auto time = _time64(nullptr);
|
|
|
|
_localtime64_s(<ime, &time);
|
|
strftime(timestamp, sizeof(timestamp) - 1, "%Y-%m-%d-%H-%M-%S", <ime);
|
|
|
|
return timestamp;
|
|
}
|
|
|
|
std::string generate_crash_info(const LPEXCEPTION_POINTERS exceptioninfo)
|
|
{
|
|
std::string info{};
|
|
const auto line = [&info](const std::string& text)
|
|
{
|
|
info.append(text);
|
|
info.append("\r\n");
|
|
};
|
|
|
|
line("Project-BO4 Crash Dump");
|
|
line("");
|
|
line(game::version_string);
|
|
//line("Version: "s + VERSION);
|
|
line("Timestamp: "s + get_timestamp());
|
|
line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
|
|
line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress));
|
|
line(utils::string::va("Base: 0x%llX", get_base()));
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4996)
|
|
OSVERSIONINFOEXA version_info;
|
|
ZeroMemory(&version_info, sizeof(version_info));
|
|
version_info.dwOSVersionInfoSize = sizeof(version_info);
|
|
GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&version_info));
|
|
#pragma warning(pop)
|
|
|
|
line(utils::string::va("OS Version: %u.%u", version_info.dwMajorVersion, version_info.dwMinorVersion));
|
|
|
|
return info;
|
|
}
|
|
|
|
void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
|
|
{
|
|
const std::string crash_name = utils::string::va("minidumps/bo4-crash-%s.zip",
|
|
get_timestamp().data());
|
|
|
|
utils::compression::zip::archive zip_file{};
|
|
zip_file.add("crash.dmp", create_minidump(exceptioninfo));
|
|
zip_file.add("info.txt", generate_crash_info(exceptioninfo));
|
|
zip_file.write(crash_name, "Project-bo4 Crash Dump");
|
|
}
|
|
|
|
bool is_harmless_error(const LPEXCEPTION_POINTERS exceptioninfo)
|
|
{
|
|
const auto code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
|
return code == STATUS_INTEGER_OVERFLOW || code == STATUS_FLOAT_OVERFLOW || code == STATUS_SINGLE_STEP;
|
|
}
|
|
|
|
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS exceptioninfo)
|
|
{
|
|
if (is_harmless_error(exceptioninfo))
|
|
{
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
|
|
write_minidump(exceptioninfo);
|
|
|
|
exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
|
exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress;
|
|
exceptioninfo->ContextRecord->Rip = get_reset_state_stub();
|
|
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
|
|
void WINAPI set_unhandled_exception_filter_stub(LPTOP_LEVEL_EXCEPTION_FILTER)
|
|
{
|
|
// Don't register anything here...
|
|
}
|
|
}
|
|
|
|
class component final : public component_interface
|
|
{
|
|
public:
|
|
component()
|
|
{
|
|
main_thread_id = GetCurrentThreadId();
|
|
SetUnhandledExceptionFilter(exception_filter);
|
|
}
|
|
|
|
void pre_start() override
|
|
{
|
|
const utils::nt::library ntdll("ntdll.dll");
|
|
auto* set_filter = ntdll.get_proc<void(*)(LPTOP_LEVEL_EXCEPTION_FILTER)>("RtlSetUnhandledExceptionFilter");
|
|
|
|
set_filter(exception_filter);
|
|
utils::hook::jump(set_filter, set_unhandled_exception_filter_stub);
|
|
}
|
|
};
|
|
}
|
|
|
|
REGISTER_COMPONENT(exception::component) |