2016-07-11 11:14:58 -04:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
|
|
|
// Stuff causes warnings
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable: 4091)
|
|
|
|
#include <dbghelp.h>
|
|
|
|
#pragma comment(lib, "dbghelp.lib")
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
2016-08-27 12:18:03 -04:00
|
|
|
// Fileupload to Webhost
|
2016-08-05 14:30:44 -04:00
|
|
|
bool Exception::UploadMinidump(std::string filename)
|
2016-07-31 14:46:22 -04:00
|
|
|
{
|
2016-08-05 14:30:44 -04:00
|
|
|
Utils::WebIO webio("Firefucks", UPLOAD_URL);
|
|
|
|
|
|
|
|
if (Utils::IO::FileExists(filename))
|
|
|
|
{
|
|
|
|
std::string buffer = Utils::IO::ReadFile(filename);
|
|
|
|
std::string result = webio.PostFile(buffer);
|
|
|
|
|
|
|
|
std::string errors;
|
|
|
|
json11::Json object = json11::Json::parse(result, errors);
|
|
|
|
|
|
|
|
if (!object.is_object()) return false;
|
|
|
|
|
|
|
|
json11::Json success = object["success"];
|
|
|
|
|
|
|
|
if (!success.is_bool() || !success.bool_value()) return false;
|
|
|
|
|
|
|
|
json11::Json files = object["files"];
|
|
|
|
|
|
|
|
if (!files.is_array()) return false;
|
|
|
|
|
|
|
|
for (auto file : files.array_items())
|
|
|
|
{
|
|
|
|
json11::Json url = file["url"];
|
|
|
|
json11::Json hash = file["hash"];
|
|
|
|
|
|
|
|
if (hash.is_string() && url.is_string())
|
|
|
|
{
|
|
|
|
if (Utils::String::ToLower(Utils::Cryptography::SHA1::Compute(buffer, true)) == Utils::String::ToLower(hash.string_value()))
|
|
|
|
{
|
|
|
|
MessageBoxA(0, url.string_value().data(), 0, 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-27 12:18:03 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fileupload to Bitmessage
|
|
|
|
bool Exception::UploadMinidump2BM(std::string filename)
|
|
|
|
{
|
|
|
|
if (Components::BitMessage::Singleton == nullptr)
|
|
|
|
{
|
|
|
|
//throw new std::runtime_error("BitMessage was already stopped.");
|
|
|
|
Logger::Print("Bitmessage was already stopped.\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BitMessage* Singleton;
|
|
|
|
Singleton = Components::BitMessage::Singleton;
|
2016-08-05 14:30:44 -04:00
|
|
|
|
2016-08-27 12:18:03 -04:00
|
|
|
if (Utils::IO::FileExists(filename))
|
|
|
|
{
|
|
|
|
// TODO: Validate filesize of minidump
|
|
|
|
// TODO: Convert to base64
|
|
|
|
// TODO: Split if filesize > xxxkB
|
|
|
|
std::string buffer = Utils::String::encodeBase64(Utils::IO::ReadFile(filename));
|
|
|
|
|
|
|
|
ustring pubAddrString;
|
|
|
|
pubAddrString.fromString(BITMESSAGE_UPLOAD_IDENTITY);
|
|
|
|
PubAddr pubAddr;
|
|
|
|
if (pubAddr.loadAddr(pubAddrString))
|
|
|
|
{
|
|
|
|
int g;
|
|
|
|
ustring msg;
|
|
|
|
|
|
|
|
Logger::Print("Uploading Minidump (this may take a while)...\n");
|
|
|
|
|
|
|
|
for (size_t i = 0; i < buffer.size(); i=i+BITMESSAGE_SIZE_LIMIT)
|
|
|
|
{
|
|
|
|
if (buffer.size() > i + BITMESSAGE_SIZE_LIMIT)
|
|
|
|
g = buffer.size();
|
|
|
|
else
|
|
|
|
g = i + BITMESSAGE_SIZE_LIMIT;
|
|
|
|
std::string substring = buffer.substr(i, g);
|
|
|
|
msg.fromString(substring);
|
|
|
|
Singleton->BMClient->sendMessage(msg, pubAddr, Singleton->BMClient->PrivAddresses[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::Print("Minidump uploaded.\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Print("Address not correct!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-05 14:30:44 -04:00
|
|
|
return false;
|
2016-07-31 14:46:22 -04:00
|
|
|
}
|
|
|
|
|
2016-07-11 11:14:58 -04:00
|
|
|
LONG WINAPI Exception::ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo)
|
|
|
|
{
|
|
|
|
char filename[MAX_PATH];
|
|
|
|
__time64_t time;
|
2016-07-12 12:33:25 -04:00
|
|
|
tm ltime;
|
2016-07-11 11:14:58 -04:00
|
|
|
|
|
|
|
_time64(&time);
|
2016-07-12 12:33:25 -04:00
|
|
|
_localtime64_s(<ime, &time);
|
|
|
|
strftime(filename, sizeof(filename) - 1, "iw4x-" VERSION_STR "-%Y%m%d%H%M%S.dmp", <ime);
|
2016-07-11 11:14:58 -04:00
|
|
|
|
|
|
|
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
|
|
|
|
if (hFile && hFile != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2016-07-31 14:46:22 -04:00
|
|
|
MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE };
|
2016-07-11 11:14:58 -04:00
|
|
|
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ex, NULL, NULL);
|
|
|
|
CloseHandle(hFile);
|
|
|
|
}
|
|
|
|
|
2016-08-07 15:46:30 -04:00
|
|
|
//Exception::UploadMinidump(filename);
|
2016-08-27 12:18:03 -04:00
|
|
|
Exception::UploadMinidump2BM(filename);
|
2016-07-31 14:46:22 -04:00
|
|
|
|
2016-07-11 11:14:58 -04:00
|
|
|
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
|
|
|
|
{
|
|
|
|
Logger::Error("Termination because of a stack overflow.\n");
|
|
|
|
TerminateProcess(GetCurrentProcess(), EXCEPTION_STACK_OVERFLOW);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-07-31 14:46:22 -04:00
|
|
|
Logger::Error("Fatal error (0x%08X) at 0x%08X.", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
2016-07-11 11:14:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
|
|
|
{
|
|
|
|
SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
|
|
|
return lpTopLevelExceptionFilter;
|
|
|
|
}
|
|
|
|
|
|
|
|
Exception::Exception()
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Display DEBUG branding, so we know we're on a debug build
|
|
|
|
Renderer::OnFrame([] ()
|
|
|
|
{
|
|
|
|
Game::Font* font = Game::R_RegisterFont("fonts/normalFont");
|
|
|
|
float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
|
|
|
|
|
|
// Change the color when attaching a debugger
|
|
|
|
if (IsDebuggerPresent())
|
|
|
|
{
|
|
|
|
color[0] = 0.6588f;
|
|
|
|
color[1] = 1.0000f;
|
|
|
|
color[2] = 0.0000f;
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::R_AddCmdDrawText("DEBUG-BUILD", 0x7FFFFFFF, font, 15.0f, 10.0f + Game::R_TextHeight(font), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_SHADOWED);
|
|
|
|
});
|
|
|
|
#else
|
|
|
|
Utils::Hook::Set(0x6D70AC, Exception::SetUnhandledExceptionFilterStub);
|
|
|
|
SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Command::Add("mapTest", [] (Command::Params params)
|
|
|
|
{
|
|
|
|
std::string command;
|
|
|
|
|
|
|
|
int max = (params.Length() >= 2 ? atoi(params[1]) : 16), current = 0;
|
|
|
|
|
|
|
|
for (int i =0;;)
|
|
|
|
{
|
2016-07-31 15:07:08 -04:00
|
|
|
char* mapname = Game::mapnames[i];
|
2016-07-11 11:14:58 -04:00
|
|
|
if (!*mapname)
|
|
|
|
{
|
|
|
|
i = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!(i % 2)) command.append(fmt::sprintf("wait 250;disconnect;wait 750;", mapname)); // Test a disconnect
|
|
|
|
else command.append(fmt::sprintf("wait 500;", mapname)); // Test direct map switch
|
|
|
|
command.append(fmt::sprintf("map %s;", mapname));
|
|
|
|
|
|
|
|
++i, ++current;
|
|
|
|
|
|
|
|
if (current >= max) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Command::Execute(command, false);
|
|
|
|
});
|
2016-08-27 15:18:51 -04:00
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable:4740) // flow in or out of inline asm code suppresses global optimization
|
2016-08-27 13:22:40 -04:00
|
|
|
Command::Add("debug_minidump", [](Command::Params)
|
|
|
|
{
|
2016-08-28 10:07:28 -04:00
|
|
|
// The following code was taken from VC++ 8.0 CRT (invarg.c: line 104)
|
|
|
|
|
|
|
|
EXCEPTION_RECORD ExceptionRecord;
|
|
|
|
CONTEXT ContextRecord;
|
|
|
|
memset(&ContextRecord, 0, sizeof(CONTEXT));
|
|
|
|
|
|
|
|
#ifdef _X86_
|
|
|
|
|
|
|
|
__asm {
|
|
|
|
mov dword ptr[ContextRecord.Eax], eax
|
|
|
|
mov dword ptr[ContextRecord.Ecx], ecx
|
|
|
|
mov dword ptr[ContextRecord.Edx], edx
|
|
|
|
mov dword ptr[ContextRecord.Ebx], ebx
|
|
|
|
mov dword ptr[ContextRecord.Esi], esi
|
|
|
|
mov dword ptr[ContextRecord.Edi], edi
|
|
|
|
mov word ptr[ContextRecord.SegSs], ss
|
|
|
|
mov word ptr[ContextRecord.SegCs], cs
|
|
|
|
mov word ptr[ContextRecord.SegDs], ds
|
|
|
|
mov word ptr[ContextRecord.SegEs], es
|
|
|
|
mov word ptr[ContextRecord.SegFs], fs
|
|
|
|
mov word ptr[ContextRecord.SegGs], gs
|
|
|
|
pushfd
|
|
|
|
pop[ContextRecord.EFlags]
|
2016-08-27 15:18:51 -04:00
|
|
|
}
|
|
|
|
|
2016-08-28 10:07:28 -04:00
|
|
|
ContextRecord.ContextFlags = CONTEXT_CONTROL;
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable:4311)
|
|
|
|
ContextRecord.Eip = (ULONG)_ReturnAddress();
|
|
|
|
ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
|
|
|
|
#pragma warning(pop)
|
|
|
|
ContextRecord.Ebp = *((ULONG *)_AddressOfReturnAddress() - 1);
|
|
|
|
|
|
|
|
|
|
|
|
#elif defined (_IA64_) || defined (_AMD64_)
|
|
|
|
|
|
|
|
/* Need to fill up the Context in IA64 and AMD64. */
|
|
|
|
RtlCaptureContext(&ContextRecord);
|
|
|
|
|
|
|
|
#else /* defined (_IA64_) || defined (_AMD64_) */
|
|
|
|
|
|
|
|
ZeroMemory(&ContextRecord, sizeof(ContextRecord));
|
|
|
|
|
|
|
|
#endif /* defined (_IA64_) || defined (_AMD64_) */
|
|
|
|
|
|
|
|
ZeroMemory(&ExceptionRecord, sizeof(EXCEPTION_RECORD));
|
|
|
|
|
|
|
|
ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT;
|
|
|
|
ExceptionRecord.ExceptionAddress = _ReturnAddress();
|
|
|
|
|
|
|
|
|
|
|
|
EXCEPTION_RECORD* pExceptionRecord = new EXCEPTION_RECORD;
|
|
|
|
memcpy(pExceptionRecord, &ExceptionRecord, sizeof(EXCEPTION_RECORD));
|
|
|
|
CONTEXT* pContextRecord = new CONTEXT;
|
|
|
|
memcpy(pContextRecord, &ContextRecord, sizeof(CONTEXT));
|
2016-08-27 13:22:40 -04:00
|
|
|
|
2016-08-28 10:07:28 -04:00
|
|
|
auto eptr = new EXCEPTION_POINTERS;
|
|
|
|
eptr->ExceptionRecord = pExceptionRecord;
|
|
|
|
eptr->ContextRecord = pContextRecord;
|
2016-08-27 13:22:40 -04:00
|
|
|
|
2016-08-27 15:18:51 -04:00
|
|
|
Exception::ExceptionFilter(eptr);
|
2016-08-27 13:22:40 -04:00
|
|
|
});
|
2016-08-27 15:18:51 -04:00
|
|
|
#pragma warning(pop)
|
2016-07-11 11:14:58 -04:00
|
|
|
}
|
|
|
|
}
|