pioneer/src/main.cpp

242 lines
6.1 KiB
C++

// Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#include "Game.h"
#include "ModelViewer.h"
#include "Pi.h"
#include "buildopts.h"
#include "galaxy/Galaxy.h"
#include "galaxy/GalaxyGenerator.h"
#include "libs.h"
#include "utils.h"
#include "versioningInfo.h"
#include <cstdio>
#include <cstdlib>
enum RunMode {
MODE_GAME,
MODE_MODELVIEWER,
MODE_GALAXYDUMP,
MODE_START_AT,
MODE_VERSION,
MODE_USAGE,
MODE_USAGE_ERROR
};
extern "C" int main(int argc, char **argv)
{
#ifdef PIONEER_PROFILER
Profiler::detect(argc, argv);
#endif
RunMode mode = MODE_GAME;
std::string modeopt;
if (argc > 1) {
const char switchchar = argv[1][0];
if (!(switchchar == '-' || switchchar == '/')) {
mode = MODE_USAGE_ERROR;
goto start;
}
modeopt = std::string(argv[1]).substr(1);
if (modeopt == "game" || modeopt == "g") {
mode = MODE_GAME;
goto start;
}
if (modeopt == "modelviewer" || modeopt == "mv") {
mode = MODE_MODELVIEWER;
goto start;
}
if (modeopt == "galaxydump" || modeopt == "gd") {
mode = MODE_GALAXYDUMP;
goto start;
}
if (modeopt.find("startat", 0, 7) != std::string::npos ||
modeopt.find("sa", 0, 2) != std::string::npos) {
mode = MODE_START_AT;
goto start;
}
if (modeopt == "version" || modeopt == "v") {
mode = MODE_VERSION;
goto start;
}
if (modeopt == "help" || modeopt == "h" || modeopt == "?") {
mode = MODE_USAGE;
goto start;
}
mode = MODE_USAGE_ERROR;
}
start:
int pos = 2;
long int radius = 4;
long int sx = 0, sy = 0, sz = 0;
std::string filename;
SystemPath startPath(0, 0, 0, 0, 0);
switch (mode) {
case MODE_GALAXYDUMP: {
if (argc < 3) {
Output("pioneer: galaxy dump requires a filename\n");
break;
}
filename = argv[pos];
++pos;
if (argc > pos) { // radius (optional)
char *end = nullptr;
radius = std::strtol(argv[pos], &end, 0);
if (end == nullptr || *end != 0 || radius < 0 || radius > 10000) {
Output("pioneer: invalid radius: %s\n", argv[pos]);
break;
}
++pos;
}
if (argc > pos) { // center of dump (three comma separated coordinates, optional)
char *end = nullptr;
sx = std::strtol(argv[pos], &end, 0);
if (end == nullptr || *end != ',' || sx < -10000 || sx > 10000) {
Output("pioneer: invalid center: %s\n", argv[pos]);
break;
}
sy = std::strtol(end + 1, &end, 0);
if (end == nullptr || *end != ',' || sy < -10000 || sy > 10000) {
Output("pioneer: invalid center: %s\n", argv[pos]);
break;
}
sz = std::strtol(end + 1, &end, 0);
if (end == nullptr || *end != 0 || sz < -10000 || sz > 10000) {
Output("pioneer: invalid center: %s\n", argv[pos]);
break;
}
++pos;
}
// fallthrough
}
case MODE_START_AT: {
// fallthrough protect
if (mode == MODE_START_AT) {
// try to get start planet number
std::vector<std::string> keyValue = SplitString(modeopt, "=");
// if found value
if (keyValue.size() == 2) {
if (keyValue[1].empty()) {
startPath = SystemPath(0, 0, 0, 0, 0);
Error("Please provide an actual SystemPath, like 0,0,0,0,18\n");
return -1;
} else {
try {
startPath = SystemPath::Parse(keyValue[1].c_str());
} catch (const SystemPath::ParseFailure &spf) {
startPath = SystemPath(0, 0, 0, 0, 0);
Error("Failed to parse system path %s\n", keyValue[1].c_str());
return -1;
}
}
}
// if value not exists - start on Sol, Mars, Cydonia
else
startPath = SystemPath(0, 0, 0, 0, 18);
// set usual mode
mode = MODE_GAME;
}
// fallthrough
}
case MODE_GAME: {
std::map<std::string, std::string> options;
// if arguments more than parsed already
if (argc > pos) {
static const std::string delim("=");
// for each argument
for (; pos < argc; pos++) {
const std::string arg(argv[pos]);
std::vector<std::string> keyValue = SplitString(arg, "=");
// if there no key and value || key is empty || value is empty
if (keyValue.size() != 2 || keyValue[0].empty() || keyValue[1].empty()) {
Output("malformed option: %s\n", arg.c_str());
return 1;
}
// put key and value to config
options[keyValue[0]] = keyValue[1];
}
}
Pi::Init(options, mode == MODE_GALAXYDUMP);
if (mode == MODE_GAME) {
if (startPath != SystemPath(0, 0, 0, 0, 0))
Pi::GetApp()->SetStartPath(startPath);
Pi::GetApp()->Run();
} else if (mode == MODE_GALAXYDUMP) {
// TODO: don't initialize Pi when dumping the galaxy
// Galaxy generation is (mostly) self-contained, no need to e.g.
// turn on the renderer or load UI for this.
Error("GalaxyDump is not currently implemented!");
FILE *file = filename == "-" ? stdout : fopen(filename.c_str(), "w");
if (file == nullptr) {
Output("pioneer: could not open \"%s\" for writing: %s\n", filename.c_str(), strerror(errno));
break;
}
RefCountedPtr<Galaxy> galaxy = GalaxyGenerator::Create();
galaxy->Dump(file, sx, sy, sz, radius);
if (filename != "-" && fclose(file) != 0) {
Output("pioneer: writing to \"%s\" failed: %s\n", filename.c_str(), strerror(errno));
}
}
break;
}
case MODE_MODELVIEWER: {
std::string modelName;
if (argc > 2)
modelName = argv[2];
auto modelViewer = ModelViewerApp();
modelViewer.SetInitialModel(modelName);
modelViewer.Run();
break;
}
case MODE_VERSION: {
std::string version(PIONEER_VERSION);
if (strlen(PIONEER_EXTRAVERSION)) version += " (" PIONEER_EXTRAVERSION ")";
Output("pioneer %s\n", version.c_str());
OutputVersioningInfo();
break;
}
case MODE_USAGE_ERROR:
Output("pioneer: unknown mode %s\n", argv[1]);
// fall through
case MODE_USAGE:
Output(
"usage: pioneer [mode] [options...]\n"
"available modes:\n"
" -game [-g] game (default)\n"
" -modelviewer [-mv] model viewer\n"
" -galaxydump [-gd] galaxy dumper\n"
" -startat [-sa] skip main menu and start at Mars\n"
" -startat=sp [-sa=sp] skip main menu and start at systempath x,y,z,si,bi\n"
" -version [-v] show version\n"
" -help [-h,-?] this help\n");
break;
}
return 0;
}