forked from Mirrors/openclonk
250 lines
6.5 KiB
C++
250 lines
6.5 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, Matthes Bender
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
|
|
/* Main program entry point */
|
|
|
|
#include "C4Include.h"
|
|
#include "game/C4Application.h"
|
|
|
|
#include "C4Version.h"
|
|
#include "network/C4Network2.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <shellapi.h>
|
|
|
|
void InstallCrashHandler();
|
|
|
|
int WINAPI WinMain (HINSTANCE hInst,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpszCmdParam,
|
|
int nCmdShow)
|
|
{
|
|
#if defined(_DEBUG) && defined(_MSC_VER)
|
|
// enable debugheap!
|
|
_CrtSetDbgFlag( _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ) | _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
#endif
|
|
|
|
// This should be handled in an application manifest, but that is
|
|
// decidedly non-trivial to do portably across compilers and compiler
|
|
// versions, so we do it in code instead.
|
|
// Also we aren't really DPI aware (we'd have to default ingame zoom
|
|
// differently and scale the menus), but this is better than clipping.
|
|
// Fixes #891.
|
|
HMODULE user32 = LoadLibrary(L"user32");
|
|
if (user32)
|
|
{
|
|
typedef BOOL (WINAPI *SETPROCESSDPIAWAREPROC)();
|
|
SETPROCESSDPIAWAREPROC SetProcessDPIAware =
|
|
reinterpret_cast<SETPROCESSDPIAWAREPROC>(GetProcAddress(user32, "SetProcessDPIAware"));
|
|
if (SetProcessDPIAware)
|
|
SetProcessDPIAware();
|
|
FreeLibrary(user32);
|
|
}
|
|
|
|
InstallCrashHandler();
|
|
|
|
// Split wide command line to wide argv array
|
|
std::vector<char*> argv;
|
|
int argc = 0;
|
|
LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
|
if (!wargv)
|
|
{
|
|
const char *error = "Internal error: Unable to split command line! Exiting.";
|
|
Log(error);
|
|
Application.MessageDialog(error);
|
|
return C4XRV_Failure;
|
|
}
|
|
argv.reserve(argc);
|
|
|
|
// Convert args to UTF-8
|
|
LPWSTR *curwarg = wargv;
|
|
while(argc--)
|
|
{
|
|
int arglen = WideCharToMultiByte(CP_UTF8, 0, *curwarg, -1, nullptr, 0, nullptr, nullptr);
|
|
auto *utf8arg = new char[arglen ? arglen : 1];
|
|
WideCharToMultiByte(CP_UTF8, 0, *curwarg, -1, utf8arg, arglen, nullptr, nullptr);
|
|
argv.push_back(utf8arg);
|
|
++curwarg;
|
|
}
|
|
LocalFree(wargv);
|
|
|
|
// Init application
|
|
Application.SetInstance(hInst);
|
|
if (!Application.Init(argv.size(), &argv[0]))
|
|
{
|
|
Application.Clear();
|
|
return C4XRV_Failure;
|
|
}
|
|
|
|
// Run it
|
|
Application.Run();
|
|
Application.Clear();
|
|
|
|
// delete arguments
|
|
for(std::vector<char*>::const_iterator it = argv.begin(); it != argv.end(); ++it)
|
|
delete[] *it;
|
|
argv.clear();
|
|
// Return exit code
|
|
if (!Game.GameOver) return C4XRV_Aborted;
|
|
return C4XRV_Completed;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
return WinMain(GetModuleHandle(nullptr), nullptr, nullptr, 0);
|
|
}
|
|
|
|
#else // _WIN32
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_BACKWARD
|
|
#include <backward-cpp/backward.hpp>
|
|
|
|
#elif defined(HAVE_SIGNAL_H)
|
|
#include <signal.h>
|
|
|
|
#ifdef HAVE_EXECINFO_H
|
|
#include <execinfo.h>
|
|
#endif
|
|
|
|
static void crash_handler(int signo, siginfo_t * si, void *)
|
|
{
|
|
static unsigned signal_count = 0;
|
|
++signal_count;
|
|
switch (signo)
|
|
{
|
|
case SIGINT: case SIGTERM: case SIGHUP:
|
|
if (signal_count < 2) {
|
|
Application.Quit();
|
|
break;
|
|
} // else/fallthrough
|
|
default:
|
|
int logfd = STDERR_FILENO;
|
|
for (;;)
|
|
{
|
|
// Print out the signal
|
|
write(logfd, C4VERSION ": Caught signal ", sizeof (C4VERSION ": Caught signal ") - 1);
|
|
switch (signo)
|
|
{
|
|
case SIGBUS: write(logfd, "SIGBUS", sizeof ("SIGBUS") - 1); break;
|
|
case SIGILL: write(logfd, "SIGILL", sizeof ("SIGILL") - 1); break;
|
|
case SIGSEGV: write(logfd, "SIGSEGV", sizeof ("SIGSEGV") - 1); break;
|
|
case SIGABRT: write(logfd, "SIGABRT", sizeof ("SIGABRT") - 1); break;
|
|
case SIGINT: write(logfd, "SIGINT", sizeof ("SIGINT") - 1); break;
|
|
case SIGHUP: write(logfd, "SIGHUP", sizeof ("SIGHUP") - 1); break;
|
|
case SIGFPE: write(logfd, "SIGFPE", sizeof ("SIGFPE") - 1); break;
|
|
case SIGTERM: write(logfd, "SIGTERM", sizeof ("SIGTERM") - 1); break;
|
|
}
|
|
char hex[sizeof(void *) * 2];
|
|
intptr_t x = reinterpret_cast<intptr_t>(si->si_addr);
|
|
switch (signo)
|
|
{
|
|
case SIGILL: case SIGFPE: case SIGSEGV: case SIGBUS: case SIGTRAP:
|
|
write(logfd, " (0x", sizeof (" (0x") - 1);
|
|
for (int i = sizeof(void *) * 2 - 1; i >= 0; --i)
|
|
{
|
|
if ((x & 0xf) > 9)
|
|
hex[i] = 'a' + (x & 0xf) - 9;
|
|
else
|
|
hex[i] = '0' + (x & 0xf);
|
|
x >>= 4;
|
|
}
|
|
write(logfd, hex, sizeof (hex));
|
|
write(logfd, ")", sizeof (")") - 1);
|
|
break;
|
|
}
|
|
write(logfd, "\n", sizeof ("\n") - 1);
|
|
if (logfd == STDERR_FILENO) logfd = GetLogFD();
|
|
else break;
|
|
if (logfd < 0) break;
|
|
}
|
|
#ifdef HAVE_EXECINFO_H
|
|
// Get the backtrace
|
|
void *stack[100];
|
|
int count = backtrace(stack, 100);
|
|
// Print it out
|
|
backtrace_symbols_fd (stack, count, STDERR_FILENO);
|
|
// Also to the log file
|
|
if (logfd >= 0)
|
|
backtrace_symbols_fd (stack, count, logfd);
|
|
#endif
|
|
// Bye.
|
|
_exit(C4XRV_Failure);
|
|
}
|
|
}
|
|
#endif // HAVE_SIGNAL_H
|
|
|
|
static void restart(char * argv[])
|
|
{
|
|
// Close all file descriptors except stdin, stdout, stderr
|
|
int open_max = sysconf (_SC_OPEN_MAX);
|
|
for (int fd = 4; fd < open_max; fd++)
|
|
fcntl (fd, F_SETFD, FD_CLOEXEC);
|
|
// Execute the new engine
|
|
execlp(argv[0], argv[0], static_cast<char *>(nullptr));
|
|
}
|
|
|
|
int main (int argc, char * argv[])
|
|
{
|
|
if (!geteuid())
|
|
{
|
|
printf("Do not run %s as root!\n", argc ? argv[0] : "this program");
|
|
return C4XRV_Failure;
|
|
}
|
|
#ifdef HAVE_BACKWARD
|
|
backward::SignalHandling sh;
|
|
#elif defined(HAVE_SIGNAL_H)
|
|
struct sigaction sa;
|
|
sa.sa_sigaction = crash_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = SA_SIGINFO;
|
|
// Quit the program when asked
|
|
sigaction(SIGINT, &sa, nullptr);
|
|
sigaction(SIGTERM, &sa, nullptr);
|
|
sigaction(SIGHUP, &sa, nullptr);
|
|
// Set up debugging facilities
|
|
sa.sa_flags |= SA_RESETHAND;
|
|
sigaction(SIGBUS, &sa, nullptr);
|
|
sigaction(SIGILL, &sa, nullptr);
|
|
sigaction(SIGSEGV, &sa, nullptr);
|
|
sigaction(SIGABRT, &sa, nullptr);
|
|
sigaction(SIGFPE, &sa, nullptr);
|
|
#endif
|
|
|
|
// Init application
|
|
if (!Application.Init(argc, argv))
|
|
{
|
|
Application.Clear();
|
|
return C4XRV_Failure;
|
|
}
|
|
// Execute application
|
|
Application.Run();
|
|
// free app stuff
|
|
Application.Clear();
|
|
if (Application.restartAtEnd) restart(argv);
|
|
// Return exit code
|
|
if (!Game.GameOver) return C4XRV_Aborted;
|
|
return C4XRV_Completed;
|
|
}
|
|
#endif
|