358 lines
9.7 KiB
C++
358 lines
9.7 KiB
C++
/*
|
|
_________ __ __
|
|
/ _____// |_____________ _/ |______ ____ __ __ ______
|
|
\_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
|
|
/ \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
|
|
/_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
|
|
\/ \/ \//_____/ \/
|
|
______________________ ______________________
|
|
T H E W A R B E G I N S
|
|
Stratagus - A free fantasy real time strategy game engine
|
|
|
|
stratagus-game-launcher.h - Stratagus Game Launcher
|
|
Copyright (C) 2015-2016 The Stratagus Developers
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef STRATAGUS_GAMEUTILS_H
|
|
#define STRATAGUS_GAMEUTILS_H
|
|
|
|
void error(const char *title, const char *text);
|
|
void mkdir_p(const char *path);
|
|
void copy_dir(const char *source_folder, const char *target_folder);
|
|
|
|
#if __APPLE__
|
|
#define USE_MAC
|
|
#endif
|
|
|
|
#if ( defined (_MSC_VER) || defined (_WIN32) || defined (_WIN64) ) && ! defined (WIN32)
|
|
#define WIN32 1
|
|
#endif
|
|
|
|
#ifndef _CRT_SECURE_NO_DEPRECATE
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
#endif
|
|
#ifndef _CRT_SECURE_NO_WARNINGS
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
|
|
// set everything to winxp sp2 compatiblity
|
|
#define NTDDI_VERSION 0x05010200
|
|
#define _WIN32_WINNT 0x0501
|
|
#define WINVER 0x0501
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX MAX_PATH
|
|
#endif
|
|
#include <Shlwapi.h>
|
|
#include <Shlobj.h>
|
|
#pragma comment(lib, "comdlg32.lib")
|
|
#pragma comment(lib, "ole32.lib")
|
|
#pragma comment(lib, "Shlwapi.lib")
|
|
#include <direct.h>
|
|
#define mkdir(f, m) _mkdir(f)
|
|
// PathRemoveFileSpec on a drive (e.g. when extracting from CD) will leave the trailing \... remove that
|
|
#define parentdir(x) PathRemoveFileSpecA(x); if (x[strlen(x) - 1] == '\\') x[strlen(x) - 1] = '\0'
|
|
#else
|
|
#if defined(USE_MAC)
|
|
#define parentdir(x) strcpy(x, dirname(x))
|
|
#else
|
|
#define parentdir(x) dirname(x)
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#include <wincon.h>
|
|
#include <process.h>
|
|
#define QUOTE "\""
|
|
#else
|
|
#include <ftw.h>
|
|
#include <unistd.h>
|
|
#include <libgen.h>
|
|
#include <sys/wait.h>
|
|
#define QUOTE "'"
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <istream>
|
|
#if __has_include(<filesystem>)
|
|
#include <filesystem>
|
|
namespace fs = std::filesystem;
|
|
#elif __has_include(<experimental/filesystem>)
|
|
#include <experimental/filesystem>
|
|
namespace fs = std::experimental::filesystem;
|
|
#else
|
|
error "Missing the <filesystem> header."
|
|
#endif
|
|
|
|
#include "stratagus-tinyfiledialogs.h"
|
|
|
|
#ifdef WIN32
|
|
#define BUFF_SIZE MAX_PATH
|
|
#else
|
|
#define BUFF_SIZE 4096
|
|
#endif
|
|
|
|
void error(const char *title, const char *text)
|
|
{
|
|
tinyfd_messageBox(title, text, "ok", "error", 1);
|
|
exit(-1);
|
|
}
|
|
|
|
void mkdir_p(const char *path)
|
|
{
|
|
fs::create_directories(path);
|
|
}
|
|
void mkdir_p(const wchar_t *path)
|
|
{
|
|
fs::create_directories(path);
|
|
}
|
|
|
|
void copy_dir(fs::path source_folder, fs::path target_folder)
|
|
{
|
|
if (fs::exists(target_folder)) {
|
|
if (fs::equivalent(source_folder, target_folder)) {
|
|
return;
|
|
}
|
|
// first delete the target_folder, if it exists, to ensure clean slate
|
|
fs::remove_all(target_folder);
|
|
} else {
|
|
// make the parentdir of the target folder
|
|
fs::create_directories(target_folder.parent_path());
|
|
}
|
|
// now copy the new folder in its place
|
|
fs::copy(source_folder, target_folder, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
char *GetExtractionLogPath(const char *game_name, char *data_path)
|
|
{
|
|
static char *marker = (char *)calloc(MAX_PATH, sizeof(char));
|
|
if (marker[0] != '\0') {
|
|
return marker;
|
|
}
|
|
char logname[MAX_PATH];
|
|
strcpy(logname, game_name);
|
|
strcat(logname, "-extraction.log");
|
|
if (PathCombineA(marker, data_path, "portable-install")) {
|
|
if (PathFileExistsA(marker)) {
|
|
PathCombineA(marker, data_path, logname);
|
|
return marker;
|
|
}
|
|
}
|
|
SHGetFolderPathA(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, marker);
|
|
PathAppendA(marker, "Stratagus");
|
|
mkdir_p(marker);
|
|
PathAppendA(marker, logname);
|
|
return marker;
|
|
}
|
|
|
|
wchar_t *GetExtractionLogPath(const wchar_t *game_name, const wchar_t *data_path)
|
|
{
|
|
static wchar_t *marker = (wchar_t *)calloc(MAX_PATH, sizeof(wchar_t));
|
|
if (marker[0] != '\0') {
|
|
return marker;
|
|
}
|
|
wchar_t logname[MAX_PATH];
|
|
wcscpy(logname, game_name);
|
|
wcscat(logname, L"-extraction.log");
|
|
if (PathCombineW(marker, data_path, L"portable-install")) {
|
|
if (PathFileExistsW(marker)) {
|
|
PathCombineW(marker, data_path, logname);
|
|
return marker;
|
|
}
|
|
}
|
|
SHGetFolderPathW(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, marker);
|
|
PathAppendW(marker, L"Stratagus");
|
|
mkdir_p(marker);
|
|
PathAppendW(marker, logname);
|
|
return marker;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
// quoting logic taken from "Everyone quotes command line arguments the wrong way" by Daniel Colascione
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine appends the given argument to a command line such
|
|
that CommandLineToArgvW will return the argument string unchanged.
|
|
Arguments in a command line should be separated by spaces; this
|
|
function does not add these spaces.
|
|
|
|
Arguments:
|
|
|
|
Argument - Supplies the argument to encode.
|
|
|
|
CommandLine - Supplies the command line to which we append the encoded argument string.
|
|
|
|
Force - Supplies an indication of whether we should quote
|
|
the argument even if it does not contain any characters that would
|
|
ordinarily require quoting.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Arbitrary.
|
|
|
|
--*/
|
|
void ArgvQuote(const std::wstring &Argument, std::wstring &CommandLine, bool Force)
|
|
{
|
|
//
|
|
// Unless we're told otherwise, don't quote unless we actually
|
|
// need to do so --- hopefully avoid problems if programs won't
|
|
// parse quotes properly
|
|
//
|
|
if (Force == false && Argument.empty() == false && Argument.find_first_of(L" \t\n\v\"") == Argument.npos) {
|
|
CommandLine.append(Argument);
|
|
} else {
|
|
CommandLine.push_back(L'"');
|
|
|
|
for (auto It = Argument.begin(); ; ++It) {
|
|
unsigned NumberBackslashes = 0;
|
|
|
|
while (It != Argument.end() && *It == L'\\') {
|
|
++It;
|
|
++NumberBackslashes;
|
|
}
|
|
|
|
if (It == Argument.end()) {
|
|
//
|
|
// Escape all backslashes, but let the terminating
|
|
// double quotation mark we add below be interpreted
|
|
// as a metacharacter.
|
|
//
|
|
CommandLine.append(NumberBackslashes * 2, L'\\');
|
|
break;
|
|
} else if (*It == L'"') {
|
|
//
|
|
// Escape all backslashes and the following
|
|
// double quotation mark.
|
|
//
|
|
CommandLine.append(NumberBackslashes * 2 + 1, L'\\');
|
|
CommandLine.push_back(*It);
|
|
} else {
|
|
//
|
|
// Backslashes aren't special here.
|
|
//
|
|
CommandLine.append(NumberBackslashes, L'\\');
|
|
CommandLine.push_back(*It);
|
|
}
|
|
}
|
|
CommandLine.push_back(L'"');
|
|
}
|
|
}
|
|
|
|
int runCommand(std::wstring &file, std::vector<std::wstring> argv, bool echo = false, std::wstring *outputCommandline = nullptr)
|
|
{
|
|
std::wstring cmdline;
|
|
std::wstring executable;
|
|
|
|
ArgvQuote(file, executable, false);
|
|
|
|
for (size_t i = 0; i < argv.size(); i++) {
|
|
std::wstring arg = argv[i];
|
|
ArgvQuote(arg, cmdline, false);
|
|
if (i + 1 < argv.size()) {
|
|
cmdline.push_back(L' ');
|
|
}
|
|
}
|
|
std::wstring cmdcmdline;
|
|
for (auto c : cmdline) {
|
|
if (c == L'(' || c == L')' || c == L'%' || c == L'!' || c == L'^' || c == L'"' || c == L'<' || c == L'>' || c == L'&' || c == L'|') {
|
|
cmdcmdline.push_back(L'^');
|
|
}
|
|
cmdcmdline.push_back(c);
|
|
}
|
|
|
|
if (argv.size() > 0) {
|
|
cmdcmdline = std::wstring(L"@") + executable + std::wstring(L" ") + cmdcmdline;
|
|
} else {
|
|
cmdcmdline = std::wstring(L"@") + executable;
|
|
}
|
|
|
|
if (outputCommandline != nullptr) {
|
|
outputCommandline->append(cmdcmdline);
|
|
}
|
|
if (echo) {
|
|
std::wcout << executable << L' ' << cmdline << L'\n';
|
|
std::wcout << cmdcmdline << L'\n';
|
|
}
|
|
_flushall();
|
|
int code = _wsystem(cmdcmdline.c_str());
|
|
if (code == -1) {
|
|
std::wcout << _wcserror(errno) << L'\n';
|
|
}
|
|
return code;
|
|
}
|
|
#else
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
int runCommand(const char *file, char *const argv[], bool echo = false, std::string *outputCommandline = nullptr)
|
|
{
|
|
pid_t pid = fork();
|
|
|
|
if (echo || outputCommandline) {
|
|
std::string commandline = file;
|
|
for (int i = 0; ; i++) {
|
|
if (argv[i] == nullptr) {
|
|
break;
|
|
}
|
|
commandline += " ";
|
|
commandline += argv[i];
|
|
}
|
|
if (echo) {
|
|
std::cout << commandline << std::endl;
|
|
}
|
|
if (outputCommandline) {
|
|
outputCommandline->append(commandline);
|
|
}
|
|
}
|
|
|
|
if (pid == 0) {
|
|
// child
|
|
exit(execvp(file, argv));
|
|
} else {
|
|
int status;
|
|
waitpid(pid, &status, 0);
|
|
if (WIFEXITED(status)) {
|
|
return WEXITSTATUS(status);
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif
|