Beispiel #1
0
void BootUp::update()
{
	bool skipIntro = skipIntroOption.get();
	// The first forms instance causes it to get loaded
	sp<GameState> loadedState;
	std::future<void> loadTask;
	bool loadGame = false;

	if (loadGameOption.get().empty())
	{
		loadTask = fw().threadPoolEnqueue([]() {
			auto &ui_instance = ui();
			std::ignore = ui_instance;
		});
	}
	else
	{
		loadGame = true;
		auto path = loadGameOption.get();
		loadedState = mksp<GameState>();
		loadTask = fw().threadPoolEnqueue([loadedState, path]() {
			auto &ui_instance = ui();
			std::ignore = ui_instance;
			LogWarning("Loading save \"%s\"", path);

			if (!loadedState->loadGame(path))
			{
				LogError("Failed to load supplied game \"%s\"", path);
			}
			loadedState->initState();
		});
	}

	sp<Stage> nextScreen;
	if (loadGame == true)
	{
		nextScreen = mksp<LoadingScreen>(std::move(loadTask), [loadedState]() -> sp<Stage> {
			if (loadedState->current_battle)
			{
				return mksp<BattleView>(loadedState);
			}
			else
			{
				return mksp<CityView>(loadedState);
			}
		});
	}
	else
	{
		nextScreen = mksp<LoadingScreen>(std::move(loadTask),
		                                 []() -> sp<Stage> { return mksp<MainMenu>(); });
	}

	fw().stageQueueCommand(
	    {StageCmd::Command::REPLACE,
	     mksp<VideoScreen>(skipIntro ? "" : "SMK:xcom3/smk/intro1.smk", nextScreen)});
}
Beispiel #2
0
static void initLogger()
{
	outFile = NULL;

	// Handle Log calls befoore the settings are read, just output everything to stdout

	if (!ConfigFile::getInstance().loaded())
	{
		stderrLogLevel = LogLevel::Debug;
		fileLogLevel = LogLevel::Nothing;
		backtraceLogLevel = LogLevel::Nothing;
		showDialogOnError = false;
		// Returning withoput setting loggerInited causes this to be called evey Log call until the
		// config is parsed
		return;
	}

	loggerInited = true;

	stderrLogLevel = (LogLevel)stderrLogLevelOption.get();
	fileLogLevel = (LogLevel)fileLogLevelOption.get();
	backtraceLogLevel = (LogLevel)backtraceLogLevelOption.get();
	showDialogOnError = showDialogOnErrorOption.get();

	auto logFilePath = logFileOption.get();
	if (logFilePath.empty())
	{
		// No log file set, disabling logging to file
		fileLogLevel = LogLevel::Nothing;
		return;
	}
	outFile = fopen(logFilePath.cStr(), "w");
	if (!outFile)
	{
		// Failed to open log file, disabling logging to file
		fileLogLevel = LogLevel::Nothing;
		return;
	}
}
Beispiel #3
0
namespace OpenApoc
{
ConfigOptionInt stderrLogLevelOption(
    "Logger", "StderrLevel",
    "Loglevel to output to stderr (0 = nothing, 1 = error, 2 = warning, 3 = info, 4 = debug) ", 2);
ConfigOptionInt fileLogLevelOption(
    "Logger", "FileLevel",
    "Loglevel to output to file (0 = nothing, 1 = error, 2 = warning, 3 = info, 4 = debug)", 3);
ConfigOptionInt backtraceLogLevelOption(
    "Logger", "BacktraceLevel",
    "Loglevel to print a backtrace on (0 = nothing, 1 = error, 2 = warning, 3 = info, 4 = debug)",
    1);
ConfigOptionString logFileOption("Logger", "File", "File to write log to", LOG_PATH LOGFILE);
ConfigOptionBool showDialogOnErrorOption("Logger", "ShowDialog", "Show dialog on error", true);

#if defined(BACKTRACE_LIBUNWIND)
static void print_backtrace(FILE *f)
{

	unw_cursor_t cursor;
	unw_context_t ctx;
	unw_word_t ip;
	unw_getcontext(&ctx);
	unw_init_local(&cursor, &ctx);

	fprintf(f, "  called by:\n");
	while (unw_step(&cursor) > 0)
	{
		Dl_info info;
		unw_get_reg(&cursor, UNW_REG_IP, &ip);
		if (ip == 0)
			break;
		dladdr(reinterpret_cast<void *>(ip), &info);
		if (info.dli_sname)
		{
			fprintf(f, "  0x%zx %s+0x%zx (%s)\n", static_cast<uintptr_t>(ip), info.dli_sname,
			        static_cast<uintptr_t>(ip) - reinterpret_cast<uintptr_t>(info.dli_saddr),
			        info.dli_fname);
			continue;
		}
		// If dladdr() failed, try libunwind
		unw_word_t offsetInFn;
		char fnName[MAX_SYMBOL_LENGTH];
		if (!unw_get_proc_name(&cursor, fnName, MAX_SYMBOL_LENGTH, &offsetInFn))
		{
			fprintf(f, "  0x%zx %s+0x%zx (%s)\n", static_cast<uintptr_t>(ip), fnName, offsetInFn,
			        info.dli_fname);
			continue;
		}
		else
			fprintf(f, "  0x%zx\n", static_cast<uintptr_t>(ip));
	}
}
#elif defined(BACKTRACE_WINDOWS)
#define MAX_STACK_FRAMES 100
static void print_backtrace(FILE *f)
{
	static bool initialised = false;
	static HANDLE process;

	unsigned int frames;
	void *ip[MAX_STACK_FRAMES];
	SYMBOL_INFO *sym;

	if (!initialised)
	{
		process = GetCurrentProcess();
		SymInitialize(process, NULL, true);
		initialised = true;
	}

	if (!process)
	{
		fprintf(f, "Failed to get process for backtrace\n");
		return;
	}

	sym = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + MAX_SYMBOL_LENGTH * (sizeof(char)), 1);
	if (!sym)
	{
		fprintf(f, "Failed to allocate symbol info for backtrace\n");
		return;
	}
	sym->MaxNameLen = MAX_SYMBOL_LENGTH;
	sym->SizeOfStruct = sizeof(SYMBOL_INFO);

	/* Skip 2 frames (print_backtrace and the LogError function itself) */
	frames = CaptureStackBackTrace(2, MAX_STACK_FRAMES, ip, NULL);

	for (unsigned int frame = 0; frame < frames; frame++)
	{
		SymFromAddr(process, (DWORD64)(ip[frame]), 0, sym);
		fprintf(f, "  0x%p %s+0x%Ix\n", ip[frame], sym->Name,
		        (uintptr_t)ip[frame] - (uintptr_t)sym->Address);
	}

	free(sym);
}
#else
#warning no backtrace enabled for this platform
static void print_backtrace(FILE *f)
{
	// TODO: Other platform backtrace?
}
#endif

static FILE *outFile = nullptr;

// We store options after init as querying every LogInfo() takes a long time

LogLevel stderrLogLevel;
LogLevel fileLogLevel;
LogLevel backtraceLogLevel;
bool showDialogOnError;

bool loggerInited = false;

static std::mutex logMutex;
static std::chrono::time_point<std::chrono::high_resolution_clock> timeInit =
    std::chrono::high_resolution_clock::now();

static void initLogger()
{
	outFile = NULL;

	// Handle Log calls befoore the settings are read, just output everything to stdout

	if (!ConfigFile::getInstance().loaded())
	{
		stderrLogLevel = LogLevel::Debug;
		fileLogLevel = LogLevel::Nothing;
		backtraceLogLevel = LogLevel::Nothing;
		showDialogOnError = false;
		// Returning withoput setting loggerInited causes this to be called evey Log call until the
		// config is parsed
		return;
	}

	loggerInited = true;

	stderrLogLevel = (LogLevel)stderrLogLevelOption.get();
	fileLogLevel = (LogLevel)fileLogLevelOption.get();
	backtraceLogLevel = (LogLevel)backtraceLogLevelOption.get();
	showDialogOnError = showDialogOnErrorOption.get();

	auto logFilePath = logFileOption.get();
	if (logFilePath.empty())
	{
		// No log file set, disabling logging to file
		fileLogLevel = LogLevel::Nothing;
		return;
	}
	outFile = fopen(logFilePath.cStr(), "w");
	if (!outFile)
	{
		// Failed to open log file, disabling logging to file
		fileLogLevel = LogLevel::Nothing;
		return;
	}
}

void _logAssert(UString prefix, UString string, int line, UString file)
{
	Log(LogLevel::Error, prefix,
	    ::OpenApoc::format("Assertion \"%s\" failed at %s:%d", string.cStr(), file.cStr(), line));
	exit(EXIT_FAILURE);
}

void Log(LogLevel level, UString prefix, const UString &text)
{
	bool exit_app = false;
	const char *level_prefix;

	logMutex.lock();
	if (!loggerInited)
	{
		initLogger();
	}

	bool writeToFile = (level <= fileLogLevel);
	bool writeToStderr = (level <= stderrLogLevel);
	if (!writeToFile && !writeToStderr)
	{
		// Nothing to do
		return;
	}

	auto timeNow = std::chrono::high_resolution_clock::now();
	unsigned long long clockns =
	    std::chrono::duration<unsigned long long, std::nano>(timeNow - timeInit).count();

	switch (level)
	{
		case LogLevel::Info:
			level_prefix = "I";
			break;
		case LogLevel::Warning:
			level_prefix = "W";
			break;
		default:
			level_prefix = "E";
			exit_app = true;
			break;
	}

	if (writeToFile)
	{
		fprintf(outFile, "%s %llu %s: %s\n", level_prefix, clockns, prefix.cStr(), text.cStr());
		// On error print a backtrace to the log file
		if (level <= backtraceLogLevel)
			print_backtrace(outFile);
		fflush(outFile);
	}

	if (writeToStderr)
	{
		fprintf(stderr, "%s %llu %s: %s\n", level_prefix, clockns, prefix.cStr(), text.cStr());
		if (level <= backtraceLogLevel)
			print_backtrace(stderr);
		fflush(stderr);
	}
#if defined(ERROR_DIALOG)
	if (showDialogOnError && level == LogLevel::Error)
	{
		if (!Framework::tryGetInstance())
		{
			// No framework to have created a window, so don't try to show a dialog
		}
		else if (!fw().displayHasWindow())
		{
			// Framework created but without window, so don't try to show a dialog
		}
		else
		{
			SDL_MessageBoxData mBoxData;
			mBoxData.flags = SDL_MESSAGEBOX_ERROR;
			mBoxData.window = NULL; // Might happen before we get our window?
			mBoxData.title = "OpenApoc ERROR";
			mBoxData.message = text.cStr();
			mBoxData.numbuttons = 2;
			SDL_MessageBoxButtonData buttons[2];
			buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
			buttons[0].buttonid = 1;
			buttons[0].text = "Exit";
			buttons[1].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
			buttons[1].buttonid = 2;
			buttons[1].text = "try to limp along";
			mBoxData.buttons = buttons;
			mBoxData.colorScheme = NULL; // Use system settings

			int but;
			SDL_ShowMessageBox(&mBoxData, &but);

			/* button 1 = "exit", button 2 = "try to limp along" */
			if (but == 1)
			{
				exit_app = true;
			}
			else
			{
				exit_app = false;
			}
		}
	}
#endif

	logMutex.unlock();

	if (exit_app)
	{
		exit(EXIT_FAILURE);
	}
}

}; // namespace OpenApoc
Beispiel #4
0
namespace OpenApoc
{
ConfigOptionBool skipIntroOption("Game", "SkipIntro", "Skip intro video", false);
ConfigOptionString loadGameOption("Game", "Load", "Path to save game to load at startup", "");

void BootUp::begin() {}

void BootUp::pause() {}

void BootUp::resume() {}

void BootUp::finish() {}

void BootUp::eventOccurred(Event *e) { std::ignore = e; }

void BootUp::update()
{
	bool skipIntro = skipIntroOption.get();
	// The first forms instance causes it to get loaded
	sp<GameState> loadedState;
	std::future<void> loadTask;
	bool loadGame = false;

	if (loadGameOption.get().empty())
	{
		loadTask = fw().threadPoolEnqueue([]() {
			auto &ui_instance = ui();
			std::ignore = ui_instance;
		});
	}
	else
	{
		loadGame = true;
		auto path = loadGameOption.get();
		loadedState = mksp<GameState>();
		loadTask = fw().threadPoolEnqueue([loadedState, path]() {
			auto &ui_instance = ui();
			std::ignore = ui_instance;
			LogWarning("Loading save \"%s\"", path);

			if (!loadedState->loadGame(path))
			{
				LogError("Failed to load supplied game \"%s\"", path);
			}
			loadedState->initState();
		});
	}

	sp<Stage> nextScreen;
	if (loadGame == true)
	{
		nextScreen = mksp<LoadingScreen>(std::move(loadTask), [loadedState]() -> sp<Stage> {
			if (loadedState->current_battle)
			{
				return mksp<BattleView>(loadedState);
			}
			else
			{
				return mksp<CityView>(loadedState);
			}
		});
	}
	else
	{
		nextScreen = mksp<LoadingScreen>(std::move(loadTask),
		                                 []() -> sp<Stage> { return mksp<MainMenu>(); });
	}

	fw().stageQueueCommand(
	    {StageCmd::Command::REPLACE,
	     mksp<VideoScreen>(skipIntro ? "" : "SMK:xcom3/smk/intro1.smk", nextScreen)});
}

void BootUp::render() {}

bool BootUp::isTransition() { return false; }

}; // namespace OpenApoc