From 6d6e92a8ce8aff34132afc06fb5d67d3cb48cfc5 Mon Sep 17 00:00:00 2001 From: Carsten Teibes Date: Thu, 25 Apr 2024 22:25:26 +0200 Subject: [PATCH] lcfviz: add argument parser --- lcfviz/CMakeLists.txt | 5 +- lcfviz/Makefile.am | 5 +- lcfviz/src/main.cpp | 108 +++++++++++++++++++++++++----------------- 3 files changed, 73 insertions(+), 45 deletions(-) diff --git a/lcfviz/CMakeLists.txt b/lcfviz/CMakeLists.txt index 6716a90..29a0aa5 100644 --- a/lcfviz/CMakeLists.txt +++ b/lcfviz/CMakeLists.txt @@ -7,14 +7,17 @@ include(ConfigureWindows) find_package(liblcf REQUIRED) +set(argparse_dir src/external/argparse) set(dirent_dir src/external/dirent_win) add_executable(lcfviz src/main.cpp src/utils.cpp src/utils.h + ${argparse_dir}/argparse.hpp ${dirent_dir}/dirent_win.h) target_compile_features(lcfviz PRIVATE cxx_std_17) -target_include_directories(lcfviz PRIVATE ${dirent_dir}) +target_include_directories(lcfviz PRIVATE + ${argparse_dir} ${dirent_dir}) target_compile_definitions(lcfviz PRIVATE PACKAGE_VERSION="${PROJECT_VERSION}" PACKAGE_BUGREPORT="https://github.com/EasyRPG/Tools/issues" diff --git a/lcfviz/Makefile.am b/lcfviz/Makefile.am index e94e294..8995dad 100644 --- a/lcfviz/Makefile.am +++ b/lcfviz/Makefile.am @@ -1,19 +1,22 @@ +argparsedir = src/external/argparse direntdir = src/external/dirent_win EXTRA_DIST = README.md \ CMakeLists.txt \ CMakeModules/ConfigureWindows.cmake \ CMakeModules/FindICU.cmake \ - $(direntdir) + $(argparsedir) $(direntdir) bin_PROGRAMS = lcfviz lcfviz_SOURCES = \ src/main.cpp \ src/utils.cpp \ src/utils.h \ + $(argparsedir)/argparse.hpp \ $(direntdir)/dirent_win.h lcfviz_CXXFLAGS = \ -std=c++17 \ + -I$(srcdir)/$(argparsedir) \ -I$(srcdir)/$(direntdir) \ $(LCF_CFLAGS) lcfviz_LDADD = \ diff --git a/lcfviz/src/main.cpp b/lcfviz/src/main.cpp index 1834304..eb1e81c 100644 --- a/lcfviz/src/main.cpp +++ b/lcfviz/src/main.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,12 @@ void ParseLmt(const std::string& filename); +const std::string help_epilog = R"(Example usage: + lcfviz YOURGAME | dot -Goverlap=false -Gsplines=true -Tpng -o graph.png +Creates an overlap-free, directed graph (for huge graphs use sfdp, not dot) + +)" "Homepage " PACKAGE_URL " - Report bugs at: " PACKAGE_BUGREPORT; + static int print_help(char** argv) { std::cerr << "lcfviz - Creates a dot file from a map tree by scanning the map for teleports.\n"; std::cerr << "Usage: " << argv[0] << " [OPTION...] DIRECTORY [ENCODING]\n"; @@ -54,53 +61,68 @@ static int print_help(char** argv) { return 2; } -std::string encoding; -std::string indir; -std::vector> source_files; -std::vector> targets_per_map; -std::vector> maps; - -int depth_limit = -1; -std::string outfile; -bool remove_unreachable = false; -int start_map_id = -1; +namespace { + /* config */ + std::string encoding, indir; + std::vector> source_files; + std::vector> targets_per_map; + std::vector> maps; + + int depth_limit = -1; + std::string outfile; + bool remove_unreachable = false; + int start_map_id = -1; +} int main(int argc, char** argv) { + /*add usage and help messages */ + argparse::ArgumentParser cli("lcfviz", PACKAGE_VERSION); + cli.set_usage_max_line_width(100); + cli.add_description("Creates a dot file from a map tree by scanning the map for teleports."); + cli.add_epilog(help_epilog); + /* parse command line arguments */ - for (int i = 1; i < argc; ++i) { - std::string arg = argv[i]; - - if ((arg == "--help") || (arg == "-h")) { - return print_help(argv); - } else if ((arg == "--depth") || (arg == "-d")) { - if (i + 1 < argc) { - depth_limit = atoi(argv[i + 1]); - ++i; - remove_unreachable = true; - } - } else if ((arg == "--output") || (arg == "-o")) { - if (i+1 < argc) { - outfile = argv[i+1]; - ++i; - } - } else if ((arg == "--remove") || (arg == "-r")) { - remove_unreachable = true; - } else if ((arg == "--start") || (arg == "-s")) { - if (i+1 < argc) { - start_map_id = atoi(argv[i+1]); - ++i; - } - } else { - indir = arg; - if (i+1 < argc) { - encoding = argv[i+1]; - } - break; - } + cli.add_argument("DIRECTORY").default_value(".").store_into(indir) + .help("Game directory").metavar("DIRECTORY"); + cli.add_argument("-d", "--depth").store_into(depth_limit).scan<'i', int>() + .help("Maximal depth from the start node (default: no limit).\n" + "Enables unreachable node detection (-r)").metavar("DEPTH"); + cli.add_argument("-o", "--output").store_into(outfile).metavar("FILE") + .help("Output file (default: stdout)"); + cli.add_argument("-e", "--encoding").store_into(encoding).metavar("ENC") + .help("When not specified, is read from RPG_RT.ini or auto-detected"); + cli.add_argument("-r", "--remove").store_into(remove_unreachable) + .help("Remove nodes that are unreachable from the start node"); + cli.add_argument("-s", "--start").store_into(start_map_id).scan<'i', int>() + .help("Initial node of the graph (default: start party position)") + .metavar("ID"); + // for old encoding argument + cli.add_argument("additional").remaining().hidden(); + + try { + cli.parse_args(argc, argv); + } catch (const std::exception& err) { + std::cerr << err.what() << "\n"; + // print usage message + std::cerr << cli.usage() << "\n"; + std::exit(EXIT_FAILURE); } - if (indir.empty()) { - indir = "."; + // depth arg also sets remove option + if(cli.is_used("--depth")) { + remove_unreachable = true; + } + + // for old encoding argument + if (auto additional_args = cli.present>("additional")) { + if(additional_args.value().size() > 1) { + std::cerr << "Found additional, unrecognized arguments.\n"; + std::cerr << cli.usage() << "\n"; + std::exit(EXIT_FAILURE); + } else { + std::cerr << "Specifying ENCODING as last argument is deprecated, `-e ENC` is the replacement.\n"; + encoding = additional_args.value()[0]; + } } auto full_path = [&](const auto& name) { @@ -171,7 +193,7 @@ int main(int argc, char** argv) { const auto& lname = s.second; if (lname == MAPTREE_FILE) { - std::cerr << "Parsing Maptree " << name << std::endl; + std::cerr << "Parsing Maptree " << name << "\n"; ParseLmt(full_path(name)); parsed_lmt = true; }