2020-03-28 14:02:27 +01:00
|
|
|
#include <cstdio>
|
2012-08-22 15:49:45 +02:00
|
|
|
#include <cstdlib>
|
2016-11-18 22:01:15 +01:00
|
|
|
#include <cstring>
|
2012-08-23 12:30:09 +02:00
|
|
|
#include <getopt.h>
|
2016-08-09 17:31:56 +02:00
|
|
|
#include <fstream>
|
2012-08-22 15:49:45 +02:00
|
|
|
#include <iostream>
|
2020-03-28 14:02:27 +01:00
|
|
|
#include <utility>
|
2012-08-22 16:04:43 +02:00
|
|
|
#include <string>
|
2012-11-24 19:25:13 +01:00
|
|
|
#include <sstream>
|
2014-03-09 12:32:13 +01:00
|
|
|
#include <stdexcept>
|
2020-05-08 22:10:49 +02:00
|
|
|
#include "config.h"
|
2012-08-23 12:46:22 +02:00
|
|
|
#include "TileGenerator.h"
|
2012-08-22 15:49:45 +02:00
|
|
|
|
2020-03-26 23:07:27 +01:00
|
|
|
static void usage()
|
2012-08-22 15:49:45 +02:00
|
|
|
{
|
2020-03-28 14:02:27 +01:00
|
|
|
const std::pair<const char*, const char*> options[] = {
|
|
|
|
{"-i/--input", "<world_path>"},
|
|
|
|
{"-o/--output", "<output_image.png>"},
|
|
|
|
{"--bgcolor", "<color>"},
|
|
|
|
{"--scalecolor", "<color>"},
|
|
|
|
{"--playercolor", "<color>"},
|
|
|
|
{"--origincolor", "<color>"},
|
|
|
|
{"--drawscale", ""},
|
|
|
|
{"--drawplayers", ""},
|
|
|
|
{"--draworigin", ""},
|
|
|
|
{"--drawalpha", ""},
|
|
|
|
{"--noshading", ""},
|
|
|
|
{"--noemptyimage", ""},
|
|
|
|
{"--min-y", "<y>"},
|
|
|
|
{"--max-y", "<y>"},
|
|
|
|
{"--backend", "<backend>"},
|
|
|
|
{"--geometry", "x:y+w+h"},
|
|
|
|
{"--extent", ""},
|
|
|
|
{"--zoom", "<zoomlevel>"},
|
|
|
|
{"--colors", "<colors.txt>"},
|
|
|
|
{"--scales", "[t][b][l][r]"},
|
|
|
|
{"--exhaustive", "never|y|full|auto"},
|
2022-02-09 21:52:28 +01:00
|
|
|
{"--dumpblock", "x,y,z"},
|
2020-03-28 14:02:27 +01:00
|
|
|
};
|
|
|
|
const char *top_text =
|
|
|
|
"minetestmapper -i <world_path> -o <output_image.png> [options]\n"
|
2024-11-11 09:50:26 +01:00
|
|
|
"Generate an overview image of a Luanti map.\n"
|
2020-03-28 14:02:27 +01:00
|
|
|
"\n"
|
|
|
|
"Options:\n";
|
|
|
|
const char *bottom_text =
|
|
|
|
"\n"
|
|
|
|
"Color format: hexadecimal '#RRGGBB', e.g. '#FF0000' = red\n";
|
|
|
|
|
|
|
|
printf("%s", top_text);
|
|
|
|
for (const auto &p : options)
|
|
|
|
printf(" %-18s%s\n", p.first, p.second);
|
|
|
|
printf("%s", bottom_text);
|
|
|
|
auto backends = TileGenerator::getSupportedBackends();
|
|
|
|
printf("Supported backends: ");
|
|
|
|
for (auto s : backends)
|
|
|
|
printf("%s ", s.c_str());
|
|
|
|
printf("\n");
|
2012-08-22 15:49:45 +02:00
|
|
|
}
|
|
|
|
|
2022-02-09 22:46:07 +01:00
|
|
|
static inline bool file_exists(const std::string &path)
|
2016-08-09 16:45:39 +02:00
|
|
|
{
|
2020-05-08 22:10:49 +02:00
|
|
|
std::ifstream ifs(path);
|
2016-08-09 17:31:56 +02:00
|
|
|
return ifs.is_open();
|
|
|
|
}
|
|
|
|
|
2022-02-09 22:46:07 +01:00
|
|
|
static inline int stoi(const char *s)
|
|
|
|
{
|
|
|
|
std::istringstream iss(s);
|
|
|
|
int ret;
|
|
|
|
iss >> ret;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-03-26 23:07:27 +01:00
|
|
|
static std::string search_colors(const std::string &worldpath)
|
2016-08-09 17:31:56 +02:00
|
|
|
{
|
2022-02-09 22:46:07 +01:00
|
|
|
if (file_exists(worldpath + "/colors.txt"))
|
2016-08-09 17:31:56 +02:00
|
|
|
return worldpath + "/colors.txt";
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
char *home = std::getenv("HOME");
|
2022-02-09 22:46:07 +01:00
|
|
|
if (home) {
|
|
|
|
std::string check = std::string(home) + "/.minetest/colors.txt";
|
|
|
|
if (file_exists(check))
|
2016-08-09 17:31:56 +02:00
|
|
|
return check;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-26 23:07:27 +01:00
|
|
|
constexpr bool sharedir_valid = !(SHAREDIR[0] == '.' || SHAREDIR[0] == '\0');
|
2022-02-09 22:46:07 +01:00
|
|
|
if (sharedir_valid && file_exists(SHAREDIR "/colors.txt"))
|
2016-08-09 17:31:56 +02:00
|
|
|
return SHAREDIR "/colors.txt";
|
|
|
|
|
|
|
|
std::cerr << "Warning: Falling back to using colors.txt from current directory." << std::endl;
|
2016-08-09 16:45:39 +02:00
|
|
|
return "colors.txt";
|
|
|
|
}
|
|
|
|
|
2012-08-22 15:22:01 +02:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2020-03-26 23:07:27 +01:00
|
|
|
const static struct option long_options[] =
|
2012-08-22 15:49:45 +02:00
|
|
|
{
|
|
|
|
{"help", no_argument, 0, 'h'},
|
|
|
|
{"input", required_argument, 0, 'i'},
|
|
|
|
{"output", required_argument, 0, 'o'},
|
|
|
|
{"bgcolor", required_argument, 0, 'b'},
|
|
|
|
{"scalecolor", required_argument, 0, 's'},
|
|
|
|
{"origincolor", required_argument, 0, 'r'},
|
|
|
|
{"playercolor", required_argument, 0, 'p'},
|
2012-08-22 16:04:43 +02:00
|
|
|
{"draworigin", no_argument, 0, 'R'},
|
|
|
|
{"drawplayers", no_argument, 0, 'P'},
|
|
|
|
{"drawscale", no_argument, 0, 'S'},
|
2014-04-03 20:32:48 +02:00
|
|
|
{"drawalpha", no_argument, 0, 'e'},
|
2014-03-05 17:19:37 +01:00
|
|
|
{"noshading", no_argument, 0, 'H'},
|
2014-07-11 10:54:50 +02:00
|
|
|
{"backend", required_argument, 0, 'd'},
|
2012-11-24 19:25:13 +01:00
|
|
|
{"geometry", required_argument, 0, 'g'},
|
2018-10-20 17:40:01 +02:00
|
|
|
{"extent", no_argument, 0, 'E'},
|
2014-03-05 18:06:05 +01:00
|
|
|
{"min-y", required_argument, 0, 'a'},
|
2016-07-06 21:45:38 +02:00
|
|
|
{"max-y", required_argument, 0, 'c'},
|
2016-08-09 16:45:39 +02:00
|
|
|
{"zoom", required_argument, 0, 'z'},
|
|
|
|
{"colors", required_argument, 0, 'C'},
|
2016-11-18 22:01:15 +01:00
|
|
|
{"scales", required_argument, 0, 'f'},
|
2018-10-31 16:18:29 +01:00
|
|
|
{"noemptyimage", no_argument, 0, 'n'},
|
2020-03-27 21:10:00 +01:00
|
|
|
{"exhaustive", required_argument, 0, 'j'},
|
2022-02-09 21:52:28 +01:00
|
|
|
{"dumpblock", required_argument, 0, 'k'},
|
2016-09-29 21:11:58 +02:00
|
|
|
{0, 0, 0, 0}
|
2012-08-22 15:49:45 +02:00
|
|
|
};
|
2012-08-23 12:46:22 +02:00
|
|
|
|
2016-08-09 16:45:39 +02:00
|
|
|
std::string input;
|
|
|
|
std::string output;
|
2022-02-09 21:52:28 +01:00
|
|
|
std::string colors;
|
|
|
|
bool onlyPrintExtent = false;
|
|
|
|
BlockPos dumpblock(INT16_MIN);
|
2012-08-22 16:04:43 +02:00
|
|
|
|
2012-08-23 12:46:22 +02:00
|
|
|
TileGenerator generator;
|
2012-08-22 15:49:45 +02:00
|
|
|
while (1) {
|
2018-10-20 22:36:27 +02:00
|
|
|
int option_index;
|
|
|
|
int c = getopt_long(argc, argv, "hi:o:", long_options, &option_index);
|
|
|
|
if (c == -1)
|
|
|
|
break; // done
|
|
|
|
|
2012-08-22 16:04:43 +02:00
|
|
|
switch (c) {
|
|
|
|
case 'h':
|
|
|
|
usage();
|
2014-03-05 17:19:37 +01:00
|
|
|
return 0;
|
2012-08-22 16:04:43 +02:00
|
|
|
case 'i':
|
|
|
|
input = optarg;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
output = optarg;
|
|
|
|
break;
|
|
|
|
case 'b':
|
2012-08-23 12:46:22 +02:00
|
|
|
generator.setBgColor(optarg);
|
2012-08-22 16:04:43 +02:00
|
|
|
break;
|
|
|
|
case 's':
|
2012-08-23 12:46:22 +02:00
|
|
|
generator.setScaleColor(optarg);
|
2012-08-22 16:04:43 +02:00
|
|
|
break;
|
|
|
|
case 'r':
|
2012-08-23 12:46:22 +02:00
|
|
|
generator.setOriginColor(optarg);
|
2012-08-22 16:04:43 +02:00
|
|
|
break;
|
|
|
|
case 'p':
|
2012-08-23 12:46:22 +02:00
|
|
|
generator.setPlayerColor(optarg);
|
2012-08-22 16:04:43 +02:00
|
|
|
break;
|
|
|
|
case 'R':
|
2012-08-23 12:46:22 +02:00
|
|
|
generator.setDrawOrigin(true);
|
2012-08-22 16:04:43 +02:00
|
|
|
break;
|
|
|
|
case 'P':
|
2012-08-23 12:46:22 +02:00
|
|
|
generator.setDrawPlayers(true);
|
2012-08-22 16:04:43 +02:00
|
|
|
break;
|
|
|
|
case 'S':
|
2012-08-23 12:46:22 +02:00
|
|
|
generator.setDrawScale(true);
|
2012-08-22 16:04:43 +02:00
|
|
|
break;
|
2014-04-03 20:32:48 +02:00
|
|
|
case 'e':
|
2014-07-11 10:54:50 +02:00
|
|
|
generator.setDrawAlpha(true);
|
|
|
|
break;
|
2018-10-20 17:40:01 +02:00
|
|
|
case 'E':
|
|
|
|
onlyPrintExtent = true;
|
|
|
|
break;
|
2014-03-05 17:19:37 +01:00
|
|
|
case 'H':
|
|
|
|
generator.setShading(false);
|
|
|
|
break;
|
2014-07-11 10:54:50 +02:00
|
|
|
case 'd':
|
|
|
|
generator.setBackend(optarg);
|
|
|
|
break;
|
2022-02-09 22:46:07 +01:00
|
|
|
case 'a':
|
|
|
|
generator.setMinY(stoi(optarg));
|
2014-03-05 18:06:05 +01:00
|
|
|
break;
|
2022-02-09 22:46:07 +01:00
|
|
|
case 'c':
|
|
|
|
generator.setMaxY(stoi(optarg));
|
2014-03-05 18:06:05 +01:00
|
|
|
break;
|
2012-11-24 19:25:13 +01:00
|
|
|
case 'g': {
|
2018-10-20 23:01:03 +02:00
|
|
|
std::istringstream geometry(optarg);
|
2012-11-24 19:25:13 +01:00
|
|
|
int x, y, w, h;
|
|
|
|
char c;
|
|
|
|
geometry >> x >> c >> y >> w >> h;
|
|
|
|
if (geometry.fail() || c != ':' || w < 1 || h < 1) {
|
|
|
|
usage();
|
2014-03-05 18:06:05 +01:00
|
|
|
exit(1);
|
2012-11-24 19:25:13 +01:00
|
|
|
}
|
|
|
|
generator.setGeometry(x, y, w, h);
|
|
|
|
}
|
|
|
|
break;
|
2016-11-18 22:01:15 +01:00
|
|
|
case 'f': {
|
|
|
|
uint flags = 0;
|
2022-02-09 22:46:07 +01:00
|
|
|
if (strchr(optarg, 't'))
|
2016-11-18 22:01:15 +01:00
|
|
|
flags |= SCALE_TOP;
|
2022-02-09 22:46:07 +01:00
|
|
|
if (strchr(optarg, 'b'))
|
2016-11-18 22:01:15 +01:00
|
|
|
flags |= SCALE_BOTTOM;
|
2022-02-09 22:46:07 +01:00
|
|
|
if (strchr(optarg, 'l'))
|
2016-11-18 22:01:15 +01:00
|
|
|
flags |= SCALE_LEFT;
|
2022-02-09 22:46:07 +01:00
|
|
|
if (strchr(optarg, 'r'))
|
2016-11-18 22:01:15 +01:00
|
|
|
flags |= SCALE_RIGHT;
|
|
|
|
generator.setScales(flags);
|
|
|
|
}
|
|
|
|
break;
|
2022-02-09 22:46:07 +01:00
|
|
|
case 'z':
|
|
|
|
generator.setZoom(stoi(optarg));
|
2016-07-06 21:45:38 +02:00
|
|
|
break;
|
2016-08-09 16:45:39 +02:00
|
|
|
case 'C':
|
|
|
|
colors = optarg;
|
|
|
|
break;
|
2018-10-31 16:18:29 +01:00
|
|
|
case 'n':
|
|
|
|
generator.setDontWriteEmpty(true);
|
|
|
|
break;
|
2020-03-27 21:10:00 +01:00
|
|
|
case 'j': {
|
2022-02-09 22:46:07 +01:00
|
|
|
int mode = EXH_AUTO;;
|
2020-03-27 21:10:00 +01:00
|
|
|
if (!strcmp(optarg, "never"))
|
|
|
|
mode = EXH_NEVER;
|
|
|
|
else if (!strcmp(optarg, "y"))
|
|
|
|
mode = EXH_Y;
|
|
|
|
else if (!strcmp(optarg, "full"))
|
|
|
|
mode = EXH_FULL;
|
|
|
|
generator.setExhaustiveSearch(mode);
|
|
|
|
}
|
|
|
|
break;
|
2022-02-09 21:52:28 +01:00
|
|
|
case 'k': {
|
|
|
|
std::istringstream iss(optarg);
|
|
|
|
char c, c2;
|
|
|
|
iss >> dumpblock.x >> c >> dumpblock.y >> c2 >> dumpblock.z;
|
|
|
|
if (iss.fail() || c != ',' || c2 != ',') {
|
|
|
|
usage();
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2012-08-22 16:04:43 +02:00
|
|
|
default:
|
2014-03-05 18:06:05 +01:00
|
|
|
exit(1);
|
2012-08-22 16:04:43 +02:00
|
|
|
}
|
2012-08-22 15:49:45 +02:00
|
|
|
}
|
2018-10-20 22:36:27 +02:00
|
|
|
|
2022-02-09 21:52:28 +01:00
|
|
|
const bool need_output = !onlyPrintExtent && dumpblock.x == INT16_MIN;
|
|
|
|
if (input.empty() || (need_output && output.empty())) {
|
2018-10-20 22:36:27 +02:00
|
|
|
usage();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-05 17:19:37 +01:00
|
|
|
try {
|
2018-10-20 22:36:27 +02:00
|
|
|
|
|
|
|
if (onlyPrintExtent) {
|
2018-10-20 17:40:01 +02:00
|
|
|
generator.printGeometry(input);
|
2018-10-20 22:36:27 +02:00
|
|
|
return 0;
|
2022-02-09 21:52:28 +01:00
|
|
|
} else if (dumpblock.x != INT16_MIN) {
|
|
|
|
generator.dumpBlock(input, dumpblock);
|
|
|
|
return 0;
|
2018-10-20 22:36:27 +02:00
|
|
|
}
|
|
|
|
|
2022-02-09 22:46:07 +01:00
|
|
|
if(colors.empty())
|
2018-10-20 22:36:27 +02:00
|
|
|
colors = search_colors(input);
|
|
|
|
generator.parseColorsFile(colors);
|
|
|
|
generator.generate(input, output);
|
|
|
|
|
2022-02-09 21:52:28 +01:00
|
|
|
} catch (const std::exception &e) {
|
2016-08-09 16:45:39 +02:00
|
|
|
std::cerr << "Exception: " << e.what() << std::endl;
|
2014-03-05 17:19:37 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
2012-08-22 15:22:01 +02:00
|
|
|
}
|