From 9b885e3439d390a978a742b0dbfdd97b1ba68765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xenxo=20Espasand=C3=ADn?= Date: Sat, 13 Jan 2024 16:54:06 +0100 Subject: [PATCH] feat(tool): impl gnu opts (#181) --- .github/dependabot.yml | 14 +- .gitmodules | 3 + deps/cxxopts | 1 + deps/cxxopts.lua | 11 ++ deps/fmt | 2 +- premake5.lua | 2 + src/tool/main.cpp | 323 +++++++++++++++++++++++++---------------- 7 files changed, 221 insertions(+), 135 deletions(-) create mode 160000 deps/cxxopts create mode 100644 deps/cxxopts.lua diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2a1cfecd..97a61d55 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ -version: 2 -updates: - - package-ecosystem: "gitsubmodule" - directory: "/" - schedule: - interval: "monthly" - open-pull-requests-limit: 10 +# version: 2 +# updates: +# - package-ecosystem: "gitsubmodule" +# directory: "/" +# schedule: +# interval: "monthly" +# open-pull-requests-limit: 10 diff --git a/.gitmodules b/.gitmodules index ebe6e98a..4ba6c4d2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ [submodule "deps/fmt"] path = deps/fmt url = https://github.com/fmtlib/fmt +[submodule "deps/cxxopts"] + path = deps/cxxopts + url = https://github.com/jarro2783/cxxopts.git diff --git a/deps/cxxopts b/deps/cxxopts new file mode 160000 index 00000000..eb787304 --- /dev/null +++ b/deps/cxxopts @@ -0,0 +1 @@ +Subproject commit eb787304d67ec22f7c3a184ee8b4c481d04357fd diff --git a/deps/cxxopts.lua b/deps/cxxopts.lua new file mode 100644 index 00000000..9305b3e3 --- /dev/null +++ b/deps/cxxopts.lua @@ -0,0 +1,11 @@ +cxxopts = { base = path.join(dependencies.base, "cxxopts") } + +function cxxopts:include() + includedirs { path.join(cxxopts.base, "include") } +end + +function cxxopts:link() + self:include() +end + +table.insert(dependencies, cxxopts) diff --git a/deps/fmt b/deps/fmt index b0569451..67c0c0c0 160000 --- a/deps/fmt +++ b/deps/fmt @@ -1 +1 @@ -Subproject commit b0569451a798232efc7a5eaa0c210e6626069e06 +Subproject commit 67c0c0c09cf74d407d71a29c194761981614df3e diff --git a/premake5.lua b/premake5.lua index 9b0d47b3..9855d3bc 100644 --- a/premake5.lua +++ b/premake5.lua @@ -161,6 +161,7 @@ project "xsk-tool" dependson "xsk-gsc" files { + "./include/*.hpp", "./src/tool/**.h", "./src/tool/**.hpp", "./src/tool/**.cpp" @@ -176,6 +177,7 @@ project "xsk-tool" "./include", } + cxxopts:link() fmt:link() zlib:link() diff --git a/src/tool/main.cpp b/src/tool/main.cpp index ff56d050..512e9ff9 100644 --- a/src/tool/main.cpp +++ b/src/tool/main.cpp @@ -31,6 +31,7 @@ #include "xsk/arc/engine/t8.hpp" #include "xsk/arc/engine/t9.hpp" #include "xsk/version.hpp" +#include "cxxopts.hpp" namespace fs = std::filesystem; @@ -295,8 +296,11 @@ auto compile_file(game game, mach mach, fs::path file, fs::path rel) -> result utils::file::save(fs::path{ "compiled" } / rel, result); std::cout << fmt::format("compiled {}\n", rel.generic_string()); - utils::file::save(fs::path{ "compiled" } / fs::path{ "developer_maps" } / rel.replace_extension(".gscmap"), std::get<2>(outbin).data, std::get<2>(outbin).size); - std::cout << fmt::format("saved developer map {}\n", rel.generic_string()); + if (contexts[game][mach]->build() == build::dev) + { + utils::file::save(fs::path{ "compiled" } / fs::path{ "developer_maps" } / rel.replace_extension(".gscmap"), std::get<2>(outbin).data, std::get<2>(outbin).size); + std::cout << fmt::format("saved developer map {}\n", rel.generic_string()); + } } } @@ -487,7 +491,7 @@ auto fs_read(context const* ctx, std::string const& name) -> std::pair void +auto init_iw5(mach mach, bool dev) -> void { if (contexts[game::iw5].contains(mach)) return; @@ -496,19 +500,19 @@ auto init_iw5(mach mach) -> void case mach::pc: { contexts[game::iw5][mach] = std::make_unique(); - contexts[game::iw5][mach]->init(build::prod, fs_read); + contexts[game::iw5][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::ps3: { contexts[game::iw5][mach] = std::make_unique(); - contexts[game::iw5][mach]->init(build::prod, fs_read); + contexts[game::iw5][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::xb2: { contexts[game::iw5][mach] = std::make_unique(); - contexts[game::iw5][mach]->init(build::prod, fs_read); + contexts[game::iw5][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -516,7 +520,7 @@ auto init_iw5(mach mach) -> void } } -auto init_iw6(mach mach) -> void +auto init_iw6(mach mach, bool dev) -> void { if (contexts[game::iw6].contains(mach)) return; @@ -525,19 +529,19 @@ auto init_iw6(mach mach) -> void case mach::pc: { contexts[game::iw6][mach] = std::make_unique(); - contexts[game::iw6][mach]->init(build::prod, fs_read); + contexts[game::iw6][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::ps3: { contexts[game::iw6][mach] = std::make_unique(); - contexts[game::iw6][mach]->init(build::prod, fs_read); + contexts[game::iw6][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::xb2: { contexts[game::iw6][mach] = std::make_unique(); - contexts[game::iw6][mach]->init(build::prod, fs_read); + contexts[game::iw6][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -545,7 +549,7 @@ auto init_iw6(mach mach) -> void } } -auto init_iw7(mach mach) -> void +auto init_iw7(mach mach, bool dev) -> void { if (contexts[game::iw7].contains(mach)) return; @@ -554,7 +558,7 @@ auto init_iw7(mach mach) -> void case mach::pc: { contexts[game::iw7][mach] = std::make_unique(); - contexts[game::iw7][mach]->init(build::prod, fs_read); + contexts[game::iw7][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -562,7 +566,7 @@ auto init_iw7(mach mach) -> void } } -auto init_iw8(mach mach) -> void +auto init_iw8(mach mach, bool dev) -> void { if (contexts[game::iw8].contains(mach)) return; @@ -571,7 +575,7 @@ auto init_iw8(mach mach) -> void case mach::pc: { contexts[game::iw8][mach] = std::make_unique(); - contexts[game::iw8][mach]->init(build::prod, fs_read); + contexts[game::iw8][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -579,7 +583,7 @@ auto init_iw8(mach mach) -> void } } -auto init_iw9(mach mach) -> void +auto init_iw9(mach mach, bool dev) -> void { if (contexts[game::iw9].contains(mach)) return; @@ -588,7 +592,7 @@ auto init_iw9(mach mach) -> void case mach::pc: { contexts[game::iw9][mach] = std::make_unique(); - contexts[game::iw9][mach]->init(build::prod, fs_read); + contexts[game::iw9][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -596,7 +600,7 @@ auto init_iw9(mach mach) -> void } } -auto init_s1(mach mach) -> void +auto init_s1(mach mach, bool dev) -> void { if (contexts[game::s1].contains(mach)) return; @@ -605,19 +609,19 @@ auto init_s1(mach mach) -> void case mach::pc: { contexts[game::s1][mach] = std::make_unique(); - contexts[game::s1][mach]->init(build::prod, fs_read); + contexts[game::s1][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::ps3: { contexts[game::s1][mach] = std::make_unique(); - contexts[game::s1][mach]->init(build::prod, fs_read); + contexts[game::s1][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::xb2: { contexts[game::s1][mach] = std::make_unique(); - contexts[game::s1][mach]->init(build::prod, fs_read); + contexts[game::s1][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -625,7 +629,7 @@ auto init_s1(mach mach) -> void } } -auto init_s2(mach mach) -> void +auto init_s2(mach mach, bool dev) -> void { if (contexts[game::s2].contains(mach)) return; @@ -634,7 +638,7 @@ auto init_s2(mach mach) -> void case mach::pc: { contexts[game::s2][mach] = std::make_unique(); - contexts[game::s2][mach]->init(build::prod, fs_read); + contexts[game::s2][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -642,7 +646,7 @@ auto init_s2(mach mach) -> void } } -auto init_s4(mach mach) -> void +auto init_s4(mach mach, bool dev) -> void { if (contexts[game::s4].contains(mach)) return; @@ -651,7 +655,7 @@ auto init_s4(mach mach) -> void case mach::pc: { contexts[game::s4][mach] = std::make_unique(); - contexts[game::s4][mach]->init(build::prod, fs_read); + contexts[game::s4][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -659,7 +663,7 @@ auto init_s4(mach mach) -> void } } -auto init_h1(mach mach) -> void +auto init_h1(mach mach, bool dev) -> void { if (contexts[game::h1].contains(mach)) return; @@ -668,7 +672,7 @@ auto init_h1(mach mach) -> void case mach::pc: { contexts[game::h1][mach] = std::make_unique(); - contexts[game::h1][mach]->init(build::prod, fs_read); + contexts[game::h1][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -676,7 +680,7 @@ auto init_h1(mach mach) -> void } } -auto init_h2(mach mach) -> void +auto init_h2(mach mach, bool dev) -> void { if (contexts[game::h2].contains(mach)) return; @@ -685,7 +689,7 @@ auto init_h2(mach mach) -> void case mach::pc: { contexts[game::h2][mach] = std::make_unique(); - contexts[game::h2][mach]->init(build::prod, fs_read); + contexts[game::h2][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -693,7 +697,7 @@ auto init_h2(mach mach) -> void } } -auto init(game game, mach mach) -> void +auto init(game game, mach mach, bool dev) -> void { funcs[mode::assemble] = assemble_file; funcs[mode::disassemble] = disassemble_file; @@ -709,16 +713,16 @@ auto init(game game, mach mach) -> void switch (game) { - case game::iw5: init_iw5(mach); break; - case game::iw6: init_iw6(mach); break; - case game::iw7: init_iw7(mach); break; - case game::iw8: init_iw8(mach); break; - case game::iw9: init_iw9(mach); break; - case game::s1: init_s1(mach); break; - case game::s2: init_s2(mach); break; - case game::s4: init_s4(mach); break; - case game::h1: init_h1(mach); break; - case game::h2: init_h2(mach); break; + case game::iw5: init_iw5(mach, dev); break; + case game::iw6: init_iw6(mach, dev); break; + case game::iw7: init_iw7(mach, dev); break; + case game::iw8: init_iw8(mach, dev); break; + case game::iw9: init_iw9(mach, dev); break; + case game::s1: init_s1(mach, dev); break; + case game::s2: init_s2(mach, dev); break; + case game::s4: init_s4(mach, dev); break; + case game::h1: init_h1(mach, dev); break; + case game::h2: init_h2(mach, dev); break; default: break; } } @@ -799,8 +803,11 @@ auto compile_file(game game, mach mach, fs::path const& file, fs::path rel) -> r utils::file::save(fs::path{ "compiled" } / rel, outbin.first.data, outbin.first.size); std::cout << fmt::format("compiled {}\n", rel.generic_string()); - utils::file::save(fs::path{ "compiled" } / fs::path{ "developer_maps" } / rel.replace_extension(".gscmap"), outbin.second.data, outbin.second.size); - std::cout << fmt::format("saved developer map {}\n", rel.generic_string()); + if (contexts[game][mach]->build() == build::dev) + { + utils::file::save(fs::path{ "compiled" } / fs::path{ "developer_maps" } / rel.replace_extension((rel.extension().string().starts_with(".gsc") ? ".gscmap" : ".cscmap")), outbin.second.data, outbin.second.size); + std::cout << fmt::format("saved developer map {}\n", rel.generic_string()); + } return result::success; } @@ -851,7 +858,7 @@ auto fs_read(std::string const& name) -> std::vector return utils::file::read(fs::path{ name }); } -auto init_t6(mach mach) -> void +auto init_t6(mach mach, bool dev) -> void { if (contexts[game::t6].contains(mach)) return; @@ -860,25 +867,25 @@ auto init_t6(mach mach) -> void case mach::pc: { contexts[game::t6][mach] = std::make_unique(); - contexts[game::t6][mach]->init(build::prod, fs_read); + contexts[game::t6][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::ps3: { contexts[game::t6][mach] = std::make_unique(); - contexts[game::t6][mach]->init(build::prod, fs_read); + contexts[game::t6][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::xb2: { contexts[game::t6][mach] = std::make_unique(); - contexts[game::t6][mach]->init(build::prod, fs_read); + contexts[game::t6][mach]->init(dev ? build::dev : build::prod, fs_read); break; } case mach::wiiu: { contexts[game::t6][mach] = std::make_unique(); - contexts[game::t6][mach]->init(build::prod, fs_read); + contexts[game::t6][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -886,7 +893,7 @@ auto init_t6(mach mach) -> void } } -auto init_t7(mach mach) -> void +auto init_t7(mach mach, bool dev) -> void { if (contexts[game::t7].contains(mach)) return; @@ -895,7 +902,7 @@ auto init_t7(mach mach) -> void case mach::pc: { contexts[game::t7][mach] = std::make_unique(); - contexts[game::t7][mach]->init(build::prod, fs_read); + contexts[game::t7][mach]->init(dev ? build::dev : build::prod, fs_read); break; } default: @@ -903,7 +910,7 @@ auto init_t7(mach mach) -> void } } -auto init_t8(mach /*mach*/) -> void +auto init_t8(mach /*mach*/, bool /*dev*/) -> void { throw std::runtime_error("not implemented"); @@ -922,7 +929,7 @@ auto init_t8(mach /*mach*/) -> void // } } -auto init_t9(mach /*mach*/) -> void +auto init_t9(mach /*mach*/, bool /*dev*/) -> void { throw std::runtime_error("not implemented"); @@ -941,7 +948,7 @@ auto init_t9(mach /*mach*/) -> void // } } -auto init(game game, mach mach) -> void +auto init(game game, mach mach, bool dev) -> void { funcs[mode::assemble] = assemble_file; funcs[mode::disassemble] = disassemble_file; @@ -957,10 +964,10 @@ auto init(game game, mach mach) -> void switch (game) { - case game::t6: init_t6(mach); break; - case game::t7: init_t7(mach); break; - case game::t8: init_t8(mach); break; - case game::t9: init_t9(mach); break; + case game::t6: init_t6(mach, dev); break; + case game::t7: init_t7(mach, dev); break; + case game::t8: init_t8(mach, dev); break; + case game::t9: init_t9(mach, dev); break; default: break; } } @@ -988,10 +995,10 @@ auto extension_match(fs::path const& ext, mode mode, game game) -> bool } } -auto execute(mode mode, game game, mach mach, fs::path const& path) -> result +auto execute(mode mode, game game, mach mach, fs::path const& path, bool dev) -> result { - gsc::init(game, mach); - arc::init(game, mach); + gsc::init(game, mach, dev); + arc::init(game, mach, dev); if (fs::is_directory(path)) { @@ -1032,96 +1039,158 @@ auto execute(mode mode, game game, mach mach, fs::path const& path) -> result } } -auto parse_flags(u32 argc, char** argv, mode& mode, game& game, mach& mach, fs::path& path) -> bool +auto parse_mode(std::string const& arg, mode& out) -> bool { - if (argc != 5) return false; + auto mode = utils::string::to_lower(arg); - auto arg = utils::string::to_lower(argv[1]); + auto const it = modes.find(mode); - if (arg.at(0) == 'z') + if (it != modes.end()) { - arg.erase(arg.begin()); - gsc::zonetool = true; + out = it->second; + return true; } - auto const it1 = modes.find(arg); - - if (it1 != modes.end()) - { - mode = it1->second; - } - else - { - std::cout << "[ERROR] unknown mode '" << argv[1] << "'.\n\n"; - return false; - } - - arg = utils::string::to_lower(argv[2]); - - auto const it2 = games.find(arg); - - if (it2 != games.end()) - { - game = it2->second; - } - else - { - std::cout << "[ERROR] unknown game '" << argv[2] << "'.\n\n"; - return false; - } - - arg = utils::string::to_lower(argv[3]); - - auto const it3 = machs.find(arg); - - if (it3 != machs.end()) - { - mach = it3->second; - - if (mach == mach::ps4 || mach == mach::ps5 || mach == mach::xb3 || mach == mach::xb4) - mach = mach::pc; - } - else - { - std::cout << "[ERROR] unknown system '" << argv[3] << "'.\n\n"; - return false; - } - - path = fs::path{ utils::string::fordslash(argv[4]), fs::path::format::generic_format }; - - return true; + return false; } -auto usage() -> void +auto parse_game(std::string const& arg, game& out) -> bool { - std::cout << "usage: gsc-tool \n"; - std::cout << "\t* mode: asm, disasm, comp, decomp, parse, rename\n"; - std::cout << "\t* game: iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6, t7, t8, t9\n"; - std::cout << "\t* system: pc, ps3, ps4, ps5, xb2 (360), xb3 (One), xb4 (Series X|S), wiiu\n"; - std::cout << "\t* path: file or directory (recursive)\n"; + auto game = utils::string::to_lower(arg); + + auto const it = games.find(game); + + if (it != games.end()) + { + out = it->second; + return true; + } + + return false; } -auto branding() -> void +auto parse_system(std::string const& arg, mach& out) -> bool { - std::cout << fmt::format("GSC Tool {} created by xensik\n\n", XSK_VERSION_STR); + auto mach = utils::string::to_lower(arg); + + auto const it = machs.find(mach); + + if (it != machs.end()) + { + out = it->second; + + if (out == mach::ps4 || out == mach::ps5 || out == mach::xb3 || out == mach::xb4) + out = mach::pc; + + return true; + } + + return false; +} + +auto branding() -> std::string +{ + return fmt::format("GSC Tool {} created by xensik\n", XSK_VERSION_STR); } auto main(u32 argc, char** argv) -> result { - auto path = fs::path{}; - auto mode = mode::_; - auto game = game::_; - auto mach = mach::_; + cxxopts::Options options("gsc-tool", branding()); + options.custom_help("[OPTIONS...]"); + options.positional_help(""); + options.set_width(120); - branding(); + options.add_options() + ("m,mode","[REQUIRED] one of: asm, disasm, comp, decomp, parse, rename", cxxopts::value(), "") + ("g,game", "[REQUIRED] one of: iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6, t7, t8, t9", cxxopts::value(), "") + ("s,system", "[REQUIRED] one of: pc, ps3, ps4, ps5, xb2 (360), xb3 (One), xb4 (Series X|S), wiiu", cxxopts::value(), "") + ("p,path", "File or directory to process.", cxxopts::value()) + ("d,dev", "Enable developer mode (generate bytecode map).", cxxopts::value()->implicit_value("true")) + ("z,zonetool", "Enable zonetool mode (use .cgsc files).", cxxopts::value()->implicit_value("true")) + ("h,help", "Display help.") + ("v,version", "Display version."); - if (!parse_flags(argc, argv, mode, game, mach, path)) + options.parse_positional({ "path" }); + + try { - usage(); + auto result = options.parse(argc, argv); + + if (argc == 1 || result.count("help")) + { + std::cout << options.help() << std::endl; + return result::success; + } + + if (result.count("version")) + { + std::cout << branding(); + return result::success; + } + + if(!result.count("mode")) + { + std::cerr << "[ERROR] missing required argument \n"; + return result::failure; + } + + if (!result.count("game")) + { + std::cerr << "[ERROR] missing required argument \n"; + return result::failure; + } + + if (!result.count("system")) + { + std::cerr << "[ERROR] missing required argument \n"; + return result::failure; + } + + if (!result.count("path")) + { + std::cerr << "[ERROR] missing required argument \n"; + return result::failure; + } + + auto mode_arg = utils::string::to_lower(result["mode"].as()); + auto game_arg = utils::string::to_lower(result["game"].as()); + auto mach_arg = utils::string::to_lower(result["system"].as()); + auto path_arg = result["path"].as(); + auto path = fs::path{}; + auto mode = mode::_; + auto game = game::_; + auto mach = mach::_; + auto dev = result["dev"].as(); + gsc::zonetool = result["zonetool"].as(); + + if(!parse_mode(mode_arg, mode)) + { + std::cerr << "[ERROR] unknown mode '" << mode_arg << "'\n"; + return result::failure; + } + + if (!parse_game(game_arg, game)) + { + std::cerr << "[ERROR] unknown game '" << game_arg << "'\n"; + return result::failure; + } + + if (!parse_system(mach_arg, mach)) + { + std::cerr << "[ERROR] unknown system '" << mach_arg << "'\n"; + return result::failure; + } + + path = fs::path{ utils::string::fordslash(path_arg), fs::path::format::generic_format }; + + std::cout << branding(); + return execute(mode, game, mach, path, dev); + } + catch (std::exception const& e) + { + std::cerr << "[ERROR] " << e.what() << std::endl; return result::failure; } - - return execute(mode, game, mach, path); } } // namespace xsk