mirror of
https://github.com/XLabsProject/s1x-client.git
synced 2023-08-02 15:02:12 +02:00
Merge remote-tracking branch 'origin/develop' into feature/demonware
This commit is contained in:
commit
ebd614aaf7
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
||||
Subproject commit 84aeb59f269ee9747e85d780c8aa180bca3a9a60
|
||||
Subproject commit ef0ffefe525a6219ff245d19a832ce06f3fd3504
|
@ -44,7 +44,7 @@ namespace bots
|
||||
game::SV_SpawnTestClient(&game::mp::g_entities[entity_num]);
|
||||
if (game::Com_GetCurrentCoDPlayMode() == game::CODPLAYMODE_CORE)
|
||||
{
|
||||
bot_team_join(entity_num);
|
||||
//bot_team_join(entity_num); // super bugger rn
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
//#include "system_check.hpp"
|
||||
//#include "scheduler.hpp"
|
||||
#include "system_check.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#include <utils/thread.hpp>
|
||||
#include <utils/compression.hpp>
|
||||
|
||||
//#include <exception/minidump.hpp>
|
||||
#include <exception/minidump.hpp>
|
||||
|
||||
#include <version.hpp>
|
||||
|
||||
@ -31,6 +31,25 @@ namespace exception
|
||||
std::atomic<int> recovery_counts = {0};
|
||||
} recovery_data;
|
||||
|
||||
bool is_game_thread()
|
||||
{
|
||||
static std::vector<int> allowed_threads =
|
||||
{
|
||||
game::THREAD_CONTEXT_MAIN,
|
||||
};
|
||||
|
||||
const auto self_id = GetCurrentThreadId();
|
||||
for (const auto& index : allowed_threads)
|
||||
{
|
||||
if (game::threadIds[index] == self_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_exception_interval_too_short()
|
||||
{
|
||||
const auto delta = std::chrono::high_resolution_clock::now() - recovery_data.last_recovery;
|
||||
@ -48,6 +67,14 @@ namespace exception
|
||||
return initialized;
|
||||
}
|
||||
|
||||
bool is_recoverable()
|
||||
{
|
||||
return is_game_thread()
|
||||
&& !is_exception_interval_too_short()
|
||||
&& !too_many_exceptions_occured()
|
||||
&& is_initialized();
|
||||
}
|
||||
|
||||
void show_mouse_cursor()
|
||||
{
|
||||
while (ShowCursor(TRUE) < 0);
|
||||
@ -55,18 +82,62 @@ namespace exception
|
||||
|
||||
void display_error_dialog()
|
||||
{
|
||||
std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n\n",
|
||||
std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n"
|
||||
"A minidump has been written.\n\n",
|
||||
exception_data.code, exception_data.address);
|
||||
|
||||
error_str += "Make sure to update your graphics card drivers and install operating system updates!";
|
||||
if (!system_check::is_valid())
|
||||
{
|
||||
error_str += "Make sure to get supported game files to avoid such crashes!";
|
||||
}
|
||||
else
|
||||
{
|
||||
error_str += "Make sure to update your graphics card drivers and install operating system updates!";
|
||||
}
|
||||
|
||||
utils::thread::suspend_other_threads();
|
||||
show_mouse_cursor();
|
||||
|
||||
MessageBoxA(nullptr, error_str.data(), "ERROR", MB_ICONERROR);
|
||||
MessageBoxA(nullptr, error_str.data(), "S1x ERROR", MB_ICONERROR);
|
||||
TerminateProcess(GetCurrentProcess(), exception_data.code);
|
||||
}
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
// TODO: Add a limit for dedi restarts
|
||||
if (game::environment::is_dedi())
|
||||
{
|
||||
utils::nt::relaunch_self();
|
||||
utils::nt::terminate(exception_data.code);
|
||||
}
|
||||
|
||||
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.\nA minidump has been written.\n\n"
|
||||
"S1x 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!",
|
||||
exception_data.code, 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{};
|
||||
@ -79,6 +150,48 @@ namespace exception
|
||||
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("S1x Crash Dump");
|
||||
line("");
|
||||
line("Version: "s + VERSION);
|
||||
line("Environment: "s + game::environment::get_string());
|
||||
line("Timestamp: "s + get_timestamp());
|
||||
line("Clean game: "s + (system_check::is_valid() ? "Yes" : "No"));
|
||||
line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
|
||||
line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress));
|
||||
|
||||
#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/s1x-crash-%d-%s.zip",
|
||||
game::environment::get_real_mode(), 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, "S1x Crash Dump");
|
||||
}
|
||||
|
||||
bool is_harmless_error(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
const auto code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
||||
@ -87,13 +200,17 @@ namespace exception
|
||||
|
||||
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
if (!is_harmless_error(exceptioninfo))
|
||||
if (is_harmless_error(exceptioninfo))
|
||||
{
|
||||
exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
||||
exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress;
|
||||
display_error_dialog();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -111,6 +228,11 @@ namespace exception
|
||||
{
|
||||
SetUnhandledExceptionFilter(exception_filter);
|
||||
utils::hook::jump(SetUnhandledExceptionFilter, set_unhandled_exception_filter_stub, true);
|
||||
|
||||
scheduler::on_game_initialized([]()
|
||||
{
|
||||
is_initialized() = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -82,6 +82,17 @@ namespace fastfiles
|
||||
}, 0, false);
|
||||
});
|
||||
|
||||
command::add("rawfilelist", [](const command::params& params)
|
||||
{
|
||||
game::DB_EnumXAssets_FastFile(game::ASSET_TYPE_RAWFILE, [](const game::XAssetHeader header, void*)
|
||||
{
|
||||
if (header.rawfile && header.rawfile->name)
|
||||
{
|
||||
printf("%s\n", header.rawfile->name);
|
||||
}
|
||||
}, 0, false);
|
||||
});
|
||||
|
||||
command::add("g_poolSizes", []()
|
||||
{
|
||||
for (auto i = 0; i < game::ASSET_TYPE_COUNT; i++)
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "version.hpp"
|
||||
|
||||
namespace patches
|
||||
{
|
||||
namespace
|
||||
@ -265,6 +267,12 @@ namespace patches
|
||||
|
||||
// disable codPointStore
|
||||
dvars::override::Dvar_RegisterInt("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE);
|
||||
|
||||
// don't register every replicated dvar as a network dvar
|
||||
utils::hook::nop(0x1403534BE, 5); // dvar_foreach
|
||||
|
||||
// patch "Server is different version" to show the server client version
|
||||
utils::hook::inject(0x1404398B2, VERSION);
|
||||
}
|
||||
|
||||
static void patch_sp()
|
||||
|
@ -66,6 +66,15 @@ namespace renderer
|
||||
|
||||
r_init_draw_method_hook.create(SELECT_VALUE(0x14046C150, 0x140588B00), &r_init_draw_method_stub);
|
||||
r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404A5330, 0x1405C3AE0), &r_update_front_end_dvar_options_stub);
|
||||
|
||||
// use "saved" flags for "r_normalMap"
|
||||
utils::hook::set<uint8_t>(SELECT_VALUE(0x14047E0B8, 0x14059AD71), game::DVAR_FLAG_SAVED);
|
||||
|
||||
// use "saved" flags for "r_specularMap"
|
||||
utils::hook::set<uint8_t>(SELECT_VALUE(0x14047E0DA, 0x14059AD99), game::DVAR_FLAG_SAVED);
|
||||
|
||||
// use "saved" flags for "r_specOccMap"
|
||||
utils::hook::set<uint8_t>(SELECT_VALUE(0x14047E0FC, 0x14059ADC1), game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ namespace scheduler
|
||||
{
|
||||
schedule([=]()
|
||||
{
|
||||
const auto dw_init = game::Live_SyncOnlineDataFlags(0) == 0;
|
||||
const auto dw_init = game::environment::is_sp() ? true : game::Live_SyncOnlineDataFlags(0) == 0;
|
||||
if (dw_init && game::Sys_IsDatabaseReady2())
|
||||
{
|
||||
once(callback, type, delay);
|
||||
|
@ -100,8 +100,8 @@ namespace server_list
|
||||
return count > 15 ? 15 : count;
|
||||
}
|
||||
|
||||
const char* ui_feeder_item_text(int /*localClientNum*/, void* /*a2*/, void* /*a3*/, const size_t index,
|
||||
const size_t column)
|
||||
const char* ui_feeder_item_text(int /*localClientNum*/, void* /*a2*/, void* /*a3*/, const int index,
|
||||
const int column)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(mutex);
|
||||
|
||||
|
@ -42,7 +42,7 @@ namespace steam_proxy
|
||||
#ifndef DEV_BUILD
|
||||
try
|
||||
{
|
||||
this->start_mod("\xF0\x9F\x90\xA4" " S1x: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 209650 : 209660);
|
||||
this->start_mod("\xF0\x9F\x90\xA4" " S1x: "s + game::environment::get_string(), game::environment::is_sp() ? 209650 : 209660);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
88
src/client/component/system_check.cpp
Normal file
88
src/client/component/system_check.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "system_check.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
namespace system_check
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string read_zone(const std::string& name)
|
||||
{
|
||||
std::string data{};
|
||||
if (utils::io::read_file(name, &data))
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
if (utils::io::read_file("zone/" + name, &data))
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string hash_zone(const std::string& name)
|
||||
{
|
||||
const auto data = read_zone(name);
|
||||
return utils::cryptography::sha256::compute(data, true);
|
||||
}
|
||||
|
||||
bool verify_hashes(const std::unordered_map<std::string, std::string>& zone_hashes)
|
||||
{
|
||||
for (const auto& zone_hash : zone_hashes)
|
||||
{
|
||||
const auto hash = hash_zone(zone_hash.first);
|
||||
if (hash != zone_hash.second)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_system_valid()
|
||||
{
|
||||
static std::unordered_map<std::string, std::string> mp_zone_hashes =
|
||||
{
|
||||
{"patch_common_mp.ff", "23B15B4EF0AC9B52B3C6F9F681290B25B6B24B49F17238076A3D7F3CCEF9A0E1"},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, std::string> sp_zone_hashes =
|
||||
{
|
||||
{"patch_common.ff", "4624A974C6C7F8BECD9C343E7951722D8378889AC08ED4F2B22459B171EC553C"},
|
||||
{"patch_common_zm_mp.ff", "DA16B546B7233BBC4F48E1E9084B49218CB9271904EA7120A0EB4CB8723C19CF"},
|
||||
};
|
||||
|
||||
return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes));
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid()
|
||||
{
|
||||
static auto valid = is_system_valid();
|
||||
return valid;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_load() override
|
||||
{
|
||||
if (!is_valid())
|
||||
{
|
||||
MessageBoxA(nullptr, "Your game files are outdated or unsupported.\n"
|
||||
"Please get the latest officially supported Call of Duty: Advanced Warfare files, or you will get random crashes and issues.",
|
||||
"Invalid game files!", MB_ICONINFORMATION);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(system_check::component)
|
6
src/client/component/system_check.hpp
Normal file
6
src/client/component/system_check.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace system_check
|
||||
{
|
||||
bool is_valid();
|
||||
}
|
60
src/client/component/thread_names.cpp
Normal file
60
src/client/component/thread_names.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/thread.hpp>
|
||||
|
||||
namespace thread_names
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void set_thread_names()
|
||||
{
|
||||
static std::unordered_map<int, std::string> thread_names =
|
||||
{
|
||||
{game::THREAD_CONTEXT_MAIN, "Main"},
|
||||
{game::THREAD_CONTEXT_BACKEND, "Backend"}, // Renderer
|
||||
{game::THREAD_CONTEXT_WORKER0, "Worker0"},
|
||||
{game::THREAD_CONTEXT_WORKER1, "Worker1"},
|
||||
{game::THREAD_CONTEXT_WORKER2, "Worker2"},
|
||||
{game::THREAD_CONTEXT_WORKER3, "Worker3"},
|
||||
{game::THREAD_CONTEXT_WORKER4, "Worker4"},
|
||||
{game::THREAD_CONTEXT_WORKER5, "Worker5"},
|
||||
{game::THREAD_CONTEXT_WORKER6, "Worker6"},
|
||||
{game::THREAD_CONTEXT_WORKER7, "Worker7"},
|
||||
{game::THREAD_CONTEXT_SERVER, "Server"},
|
||||
{game::THREAD_CONTEXT_CINEMATIC, "Cinematic"},
|
||||
{game::THREAD_CONTEXT_DATABASE, "Database"},
|
||||
{game::THREAD_CONTEXT_STREAM, "Stream"},
|
||||
{game::THREAD_CONTEXT_SNDSTREAMPACKETCALLBACK, "Snd stream packet callback"},
|
||||
{game::THREAD_CONTEXT_STATS_WRITE, "Stats write"},
|
||||
};
|
||||
|
||||
for (const auto& thread_name : thread_names)
|
||||
{
|
||||
const auto id = game::threadIds[thread_name.first];
|
||||
if (id)
|
||||
{
|
||||
utils::thread::set_name(id, thread_name.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
set_thread_names();
|
||||
scheduler::once(set_thread_names, scheduler::pipeline::main);
|
||||
scheduler::once(set_thread_names, scheduler::pipeline::renderer);
|
||||
scheduler::once(set_thread_names, scheduler::pipeline::server);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(thread_names::component)
|
@ -76,12 +76,18 @@ namespace game
|
||||
|
||||
std::string get_string()
|
||||
{
|
||||
const auto current_mode = get_mode();
|
||||
const auto current_mode = get_real_mode();
|
||||
switch (current_mode)
|
||||
{
|
||||
case launcher::mode::server:
|
||||
return "Dedicated Server";
|
||||
|
||||
case launcher::mode::zombies:
|
||||
return "Zombies";
|
||||
|
||||
case launcher::mode::survival:
|
||||
return "Survival";
|
||||
|
||||
case launcher::mode::multiplayer:
|
||||
return "Multiplayer";
|
||||
|
||||
@ -95,24 +101,5 @@ namespace game
|
||||
return "Unknown (" + std::to_string(static_cast<int>(mode)) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
std::string playmode_to_string(game::CodPlayMode playmode)
|
||||
{
|
||||
switch (playmode)
|
||||
{
|
||||
case CODPLAYMODE_CORE:
|
||||
return "Core";
|
||||
case CODPLAYMODE_ZOMBIES:
|
||||
return "Zombies";
|
||||
case CODPLAYMODE_SURVIVAL:
|
||||
return "Survival";
|
||||
case CODPLAYMODE_SP:
|
||||
return "Singleplayer";
|
||||
case CODPLAYMODE_NONE:
|
||||
return "None";
|
||||
default:
|
||||
return "Unknown (" + std::to_string(static_cast<int>(playmode)) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -779,6 +779,29 @@ namespace game
|
||||
SV_CMD_RELIABLE = 0x1,
|
||||
};
|
||||
|
||||
enum threadType
|
||||
{
|
||||
THREAD_CONTEXT_MAIN = 0x0,
|
||||
THREAD_CONTEXT_BACKEND = 0x1,
|
||||
THREAD_CONTEXT_WORKER0 = 0x2,
|
||||
THREAD_CONTEXT_WORKER1 = 0x3,
|
||||
THREAD_CONTEXT_WORKER2 = 0x4,
|
||||
THREAD_CONTEXT_WORKER3 = 0x5,
|
||||
THREAD_CONTEXT_WORKER4 = 0x6,
|
||||
THREAD_CONTEXT_WORKER5 = 0x7,
|
||||
THREAD_CONTEXT_WORKER6 = 0x8,
|
||||
THREAD_CONTEXT_WORKER7 = 0x9,
|
||||
THREAD_CONTEXT_SERVER = 0xA,
|
||||
THREAD_CONTEXT_TRACE_COUNT = 0xB,
|
||||
THREAD_CONTEXT_TRACE_LAST = 0xA,
|
||||
THREAD_CONTEXT_CINEMATIC = 0xB,
|
||||
THREAD_CONTEXT_DATABASE = 0xC,
|
||||
THREAD_CONTEXT_STREAM = 0xD,
|
||||
THREAD_CONTEXT_SNDSTREAMPACKETCALLBACK = 0xE,
|
||||
THREAD_CONTEXT_STATS_WRITE = 0xF,
|
||||
THREAD_CONTEXT_COUNT = 0x10,
|
||||
};
|
||||
|
||||
enum DBSyncMode
|
||||
{
|
||||
DB_LOAD_ASYNC = 0x0,
|
||||
@ -981,12 +1004,27 @@ namespace game
|
||||
const char* buffer;
|
||||
};
|
||||
|
||||
struct StringTableCell
|
||||
{
|
||||
const char* string;
|
||||
int hash;
|
||||
};
|
||||
|
||||
struct StringTable
|
||||
{
|
||||
const char* name;
|
||||
int columnCount;
|
||||
int rowCount;
|
||||
StringTableCell* values;
|
||||
};
|
||||
|
||||
union XAssetHeader
|
||||
{
|
||||
void* data;
|
||||
Material* material;
|
||||
Font_s* font;
|
||||
RawFile* rawfile;
|
||||
StringTable* stringTable;
|
||||
};
|
||||
|
||||
enum TestClientType
|
||||
|
@ -174,6 +174,8 @@ namespace game
|
||||
WEAK symbol<int> g_poolSize{ 0x140804140, 0x1409B4B90 };
|
||||
WEAK symbol<const char*> g_assetNames{ 0x140803C90 , 0x1409B3180 };
|
||||
|
||||
WEAK symbol<DWORD> threadIds{ 0x149632EC0, 0x147DCEA30 };
|
||||
|
||||
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{ 0x14CDFAFE8, 0x14D80FD98 };
|
||||
|
||||
namespace mp
|
||||
|
93
src/common/exception/minidump.cpp
Normal file
93
src/common/exception/minidump.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include "minidump.hpp"
|
||||
|
||||
#include <DbgHelp.h>
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
namespace exception
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr MINIDUMP_TYPE get_minidump_type()
|
||||
{
|
||||
const auto type = MiniDumpIgnoreInaccessibleMemory //
|
||||
| MiniDumpWithHandleData //
|
||||
| MiniDumpScanMemory //
|
||||
| MiniDumpWithProcessThreadData //
|
||||
| MiniDumpWithFullMemoryInfo //
|
||||
| MiniDumpWithThreadInfo //
|
||||
| MiniDumpWithUnloadedModules;
|
||||
|
||||
return static_cast<MINIDUMP_TYPE>(type);
|
||||
}
|
||||
|
||||
std::string get_temp_filename()
|
||||
{
|
||||
char filename[MAX_PATH] = {0};
|
||||
char pathname[MAX_PATH] = {0};
|
||||
|
||||
GetTempPathA(sizeof(pathname), pathname);
|
||||
GetTempFileNameA(pathname, "S1x-", 0, filename);
|
||||
return filename;
|
||||
}
|
||||
|
||||
HANDLE write_dump_to_temp_file(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
MINIDUMP_EXCEPTION_INFORMATION minidump_exception_info = {GetCurrentThreadId(), exceptioninfo, FALSE};
|
||||
|
||||
auto* const file_handle = CreateFileA(get_temp_filename().data(), GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
|
||||
nullptr);
|
||||
|
||||
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file_handle, get_minidump_type(),
|
||||
&minidump_exception_info,
|
||||
nullptr,
|
||||
nullptr))
|
||||
{
|
||||
MessageBoxA(nullptr, "There was an error creating the minidump! Hit OK to close the program.",
|
||||
"Minidump Error", MB_OK | MB_ICONERROR);
|
||||
TerminateProcess(GetCurrentProcess(), 123);
|
||||
}
|
||||
|
||||
return file_handle;
|
||||
}
|
||||
|
||||
std::string read_file(HANDLE file_handle)
|
||||
{
|
||||
FlushFileBuffers(file_handle);
|
||||
SetFilePointer(file_handle, 0, nullptr, FILE_BEGIN);
|
||||
|
||||
std::string buffer{};
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
char temp_bytes[0x2000];
|
||||
|
||||
do
|
||||
{
|
||||
if (!ReadFile(file_handle, temp_bytes, sizeof(temp_bytes), &bytes_read, nullptr))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
buffer.append(temp_bytes, bytes_read);
|
||||
}
|
||||
while (bytes_read == sizeof(temp_bytes));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
std::string create_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
auto* const file_handle = write_dump_to_temp_file(exceptioninfo);
|
||||
|
||||
const auto _ = gsl::finally([file_handle]()
|
||||
{
|
||||
CloseHandle(file_handle);
|
||||
});
|
||||
|
||||
return read_file(file_handle);
|
||||
}
|
||||
}
|
8
src/common/exception/minidump.hpp
Normal file
8
src/common/exception/minidump.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../utils/nt.hpp"
|
||||
|
||||
namespace exception
|
||||
{
|
||||
std::string create_minidump(LPEXCEPTION_POINTERS exceptioninfo);
|
||||
}
|
Loading…
Reference in New Issue
Block a user