From cbe8d228000d48e490bd838690132f457c100878 Mon Sep 17 00:00:00 2001 From: pivotiiii <17112987+pivotiiii@users.noreply.github.com> Date: Fri, 26 Apr 2024 03:38:35 +0200 Subject: [PATCH] Switch from cpp-subprocess to boost::process subprocess had issues with special characters in paths and file names. --- LICENSE | 3 - dependencies.cmake | 18 +++--- src/CMakelists.txt | 2 +- src/Game.cpp | 120 +++++++++++++++++++------------------- src/nsui_banner_fixer.cpp | 1 - test/CMakelists.txt | 2 +- test/test_get_version.cpp | 36 +++++++----- 7 files changed, 93 insertions(+), 89 deletions(-) diff --git a/LICENSE b/LICENSE index b0bc3ea..4009bf6 100644 --- a/LICENSE +++ b/LICENSE @@ -19,9 +19,6 @@ Copyright (c) 2003 Michael E. Smoot Copyright (c) 2004 Daniel Aarno Copyright (c) 2017 Google Inc. ---- cpp-subprocess --- -Copyright (c) 2016-2018 Arun Muralidharan - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to diff --git a/dependencies.cmake b/dependencies.cmake index c20c321..51ad4bd 100644 --- a/dependencies.cmake +++ b/dependencies.cmake @@ -18,15 +18,19 @@ FetchContent_Declare( PATCH_COMMAND git apply "${CMAKE_SOURCE_DIR}/patches/tclap.patch" UPDATE_DISCONNECTED 1 ) +FetchContent_MakeAvailable(tclap) + +#---------------------------------------------------------------------------------------- + FetchContent_Declare( - subprocess - GIT_REPOSITORY https://github.com/arun11299/cpp-subprocess.git - GIT_TAG 40cd59c0970960a0ef41365ae9d96c6a72ee6922 # - PATCH_COMMAND git apply "${CMAKE_SOURCE_DIR}/patches/subprocess.patch" - UPDATE_DISCONNECTED 1 + Boost + URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz + URL_MD5 893b5203b862eb9bbd08553e24ff146a + DOWNLOAD_EXTRACT_TIMESTAMP ON + EXCLUDE_FROM_ALL ) - -FetchContent_MakeAvailable(tclap subprocess) +set(BOOST_INCLUDE_LIBRARIES process) +FetchContent_MakeAvailable(Boost) #---------------------------------------------------------------------------------------- diff --git a/src/CMakelists.txt b/src/CMakelists.txt index 64c7f45..79bf3da 100644 --- a/src/CMakelists.txt +++ b/src/CMakelists.txt @@ -13,7 +13,7 @@ add_library(nsui_banner_fixer_game Settings.hpp) target_compile_features(nsui_banner_fixer_game PRIVATE cxx_std_20) target_include_directories(nsui_banner_fixer_game INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(nsui_banner_fixer_game PRIVATE subprocess) +target_link_libraries(nsui_banner_fixer_game PRIVATE Boost::process) target_link_libraries(nsui_banner_fixer PRIVATE nsui_banner_fixer_game TCLAP) diff --git a/src/Game.cpp b/src/Game.cpp index d835a40..a4d0bb8 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -4,32 +4,38 @@ #include #include -#include +#include #include "Game.hpp" -#define run_process(name, verbose_offset, ret_code, err_msg) \ - { \ - if (set.verbose) { \ - name.insert(name.begin() + verbose_offset, "-v"); \ - } \ - \ - sp::Popen process = sp::Popen(name, sp::output {sp::PIPE}); \ - auto obuf = process.communicate().first; \ - \ - if (process.retcode() != ret_code) { \ - std::cerr << obuf.buf.data() << "\n"; \ - std::cerr << "ERROR: " << err_msg << " (" << process.retcode() << ")\n"; \ - return false; \ - } \ - \ - if (set.verbose) { \ - std::cerr << obuf.buf.data() << "\n"; \ - } \ +#define run_process(exec, arg_list, verbose_offset, ret_code, err_msg) \ + { \ + if (set.verbose) { \ + arg_list.insert(arg_list.begin() + verbose_offset, "-v"); \ + } \ + bp::ipstream output_stream; \ + std::string line; \ + int retval = bp::system(bp::exe = exec, \ + bp::args = arg_list, \ + bp::std_out > output_stream); \ + \ + if (retval != ret_code) { \ + while (std::getline(output_stream, line)) { \ + std::cerr << line << "\n"; \ + } \ + std::cerr << "ERROR: " << err_msg << " (" << retval << ")\n"; \ + return false; \ + } \ + \ + if (set.verbose) { \ + while (std::getline(output_stream, line)) { \ + std::cout << line << "\n"; \ + } \ + } \ } namespace fs = std::filesystem; -namespace sp = subprocess; +namespace bp = boost::process; const std::vector locale_offsets = {0x14BC, 0x14CB}; @@ -99,24 +105,26 @@ bool Game::fix_banner() versionS Game::get_version() { versionS version; - - auto output_buffer = sp::check_output({set.ctrtool.string(), "-i", this->cia_path.string()}); - if (output_buffer.buf.data() != NULL) { - std::stringstream ss(output_buffer.buf.data()); - std::string line; - while (std::getline(ss, line, '\n')) { - if (line.starts_with("Title version:")) { - std::regex version_regex("(\\d+).(\\d+).(\\d+)"); - std::smatch matches; - if (std::regex_search(line, matches, version_regex)) { - if (matches.size() == 4) { - version.major = max(min(63, stoi(matches[1].str())), 0); - version.minor = max(min(63, stoi(matches[2].str())), 0); - version.micro = max(min(15, stoi(matches[3].str())), 0); - } + bp::ipstream output_stream; + std::string line; + + bp::system(bp::exe = set.ctrtool.string(), + bp::args = {"-i", + this->cia_path.string()}, + bp::std_out > output_stream); + + while (std::getline(output_stream, line)) { + if (line.starts_with("Title version:")) { + std::regex version_regex("(\\d+).(\\d+).(\\d+)"); + std::smatch matches; + if (std::regex_search(line, matches, version_regex)) { + if (matches.size() == 4) { + version.major = std::max(std::min(63, stoi(matches[1].str())), 0); + version.minor = std::max(std::min(63, stoi(matches[2].str())), 0); + version.micro = std::max(std::min(15, stoi(matches[3].str())), 0); } - break; } + break; } } if (set.verbose) { @@ -128,37 +136,33 @@ versionS Game::get_version() bool Game::extract_cia() { - std::vector extract_contents = {set.ctrtool.string(), - std::string("--contents=") + (this->cwd / "contents").string(), + std::vector extract_contents = {std::string("--contents=") + (this->cwd / "contents").string(), this->cia_path.string()}; - run_process(extract_contents, 1, 0, "Failed to extract contents from .CIA"); + run_process(set.ctrtool.string(), extract_contents, 1, 0, "Failed to extract contents from .CIA"); - std::vector split_contents = {set.dstool.string(), - "-x", "-t", + std::vector split_contents = {"-x", "-t", "cxi", "-f", (this->cwd / "contents.0000.00000000").string(), "--header", (this->cwd / "ncch.header").string(), "--exh", (this->cwd / "exheader.bin").string(), "--exefs", (this->cwd / "exefs.bin").string(), "--romfs", (this->cwd / "romfs.bin").string()}; - run_process(split_contents, 2, 0, "Failed to split contents"); + run_process(set.dstool.string(), split_contents, 1, 0, "Failed to split contents"); - std::vector extract_exefs = {set.dstool.string(), - "-x", "-t", + std::vector extract_exefs = {"-x", "-t", "exefs", "-f", (this->cwd / "exefs.bin").string(), "--header", (this->cwd / "exefs.header").string(), "--exefs-dir", (this->cwd / "exefs").string()}; - run_process(extract_exefs, 2, 0, "Failed to extract exefs from contents"); + run_process(set.dstool.string(), extract_exefs, 1, 0, "Failed to extract exefs from contents"); fs::create_directory(this->cwd / "banner"); if (fs::exists(this->cwd / "exefs" / "banner.bnr")) { this->banner_ext = "bnr"; } - std::vector extract_banner = {set.dstool.string(), - "-x", "-t", + std::vector extract_banner = {"-x", "-t", "banner", "-f", (this->cwd / "exefs" / (std::string("banner.") + this->banner_ext)).string(), "--banner-dir", (this->cwd / "banner").string()}; - run_process(extract_banner, 2, 0, "Failed to extract banner from exefs"); + run_process(set.dstool.string(), extract_banner, 1, 0, "Failed to extract banner from exefs"); return true; } @@ -206,27 +210,24 @@ bool Game::edit_bcmdl() bool Game::repack_cia() { fs::remove(this->cwd / "exefs" / (std::string("banner.") + this->banner_ext)); - std::vector rebuild_banner = {set.dstool.string(), - "-c", "-t", + std::vector rebuild_banner = {"-c", "-t", "banner", "-f", (this->cwd / "exefs" / (std::string("banner.") + this->banner_ext)).string(), "--banner-dir", (this->cwd / "banner").string()}; - run_process(rebuild_banner, 2, 0, "Failed to rebuild banner"); + run_process(set.dstool.string(), rebuild_banner, 1, 0, "Failed to rebuild banner"); - std::vector rebuild_exefs = {set.dstool.string(), - "-c", "-t", + std::vector rebuild_exefs = {"-c", "-t", "exefs", "-f", (this->cwd / "exefs.bin").string(), "--header", (this->cwd / "exefs.header").string(), "--exefs-dir", (this->cwd / "exefs").string()}; - run_process(rebuild_exefs, 2, 0, "Failed to rebuild exefs"); + run_process(set.dstool.string(), rebuild_exefs, 1, 0, "Failed to rebuild exefs"); - std::vector rebuild_cxi = {set.dstool.string(), - "-c", "-t", + std::vector rebuild_cxi = {"-c", "-t", "cxi", "-f", (this->cwd / (this->name + ".cxi")).string(), "--header", (this->cwd / "ncch.header").string(), "--exh", (this->cwd / "exheader.bin").string(), "--exefs", (this->cwd / "exefs.bin").string(), "--romfs", (this->cwd / "romfs.bin").string()}; - run_process(rebuild_cxi, 2, 0, "Failed to rebuild cxi"); + run_process(set.dstool.string(), rebuild_cxi, 1, 0, "Failed to rebuild cxi"); fs::path out_cia; if (set.replace) { @@ -238,14 +239,13 @@ bool Game::repack_cia() } fs::path content_path_rel = fs::relative(this->cwd / (this->name + ".cxi"), fs::current_path()); // may need checking if file on different drive - std::vector rebuild_cia = {set.makerom.string(), - "-f", "cia", + std::vector rebuild_cia = {"-f", "cia", "-o", out_cia.string(), "-content", content_path_rel.string() + ":0:0x00", "-major", std::to_string(this->version.major), "-minor", std::to_string(this->version.minor), "-micro", std::to_string(this->version.micro)}; - run_process(rebuild_cia, 1, 0, "Failed to rebuild CIA"); + run_process(set.makerom.string(), rebuild_cia, 0, 0, "Failed to rebuild CIA"); return true; } diff --git a/src/nsui_banner_fixer.cpp b/src/nsui_banner_fixer.cpp index 1681721..08e7fc0 100644 --- a/src/nsui_banner_fixer.cpp +++ b/src/nsui_banner_fixer.cpp @@ -77,7 +77,6 @@ int parse_args(int argc, char** argv, std::vector &cias, Settings &set "nsui_banner_fixer uses the following tools and libraries licensed under the MIT license:\n\n" "3dstool\nCopyright (c) 2014-2020 Daowen Sun\n\n" "TCLAP\nCopyright (c) 2003 Michael E. Smoot\nCopyright (c) 2004 Daniel Aarno\nCopyright (c) 2017 Google Inc.\n\n" - "cpp-subprocess\nCopyright (c) 2016-2018 Arun Muralidharan\n\n" "The full MIT license text is available at https://github.com/pivotiiii/nsui_banner_fixer/blob/master/LICENSE\n"; return 2; } diff --git a/test/CMakelists.txt b/test/CMakelists.txt index 9939c6e..12febc5 100644 --- a/test/CMakelists.txt +++ b/test/CMakelists.txt @@ -8,7 +8,7 @@ set(test_files test_v28.cia test_v27.cia) foreach(test_name IN LISTS test_names) add_executable(${test_name} ${test_name}.cpp) target_compile_features(${test_name} PRIVATE cxx_std_20) - target_link_libraries(${test_name} PRIVATE nsui_banner_fixer_game subprocess) + target_link_libraries(${test_name} PRIVATE nsui_banner_fixer_game Boost::process) add_test(NAME ${test_name} COMMAND $ WORKING_DIRECTORY $) endforeach() diff --git a/test/test_get_version.cpp b/test/test_get_version.cpp index 304dca1..694e50b 100644 --- a/test/test_get_version.cpp +++ b/test/test_get_version.cpp @@ -1,33 +1,37 @@ #include #include -#include +#include #include #include namespace fs = std::filesystem; +namespace bp = boost::process; versionS get_version(const fs::path &cia, const Settings set) { versionS version; - auto output_buffer = subprocess::check_output({set.ctrtool.string(), "-i", cia.string()}); - if (output_buffer.buf.data() != NULL) { - std::stringstream ss(output_buffer.buf.data()); - std::string line; - while (std::getline(ss, line, '\n')) { - if (line.starts_with("Title version:")) { - std::regex version_regex("(\\d+).(\\d+).(\\d+)"); - std::smatch matches; - if (std::regex_search(line, matches, version_regex)) { - if (matches.size() == 4) { - version.major = max(min(63, stoi(matches[1].str())), 0); - version.minor = max(min(63, stoi(matches[2].str())), 0); - version.micro = max(min(15, stoi(matches[3].str())), 0); - } + bp::ipstream output_stream; + std::string line; + + bp::system(bp::exe = set.ctrtool.string(), + bp::args = {"-i", + cia.string()}, + bp::std_out > output_stream); + + while (std::getline(output_stream, line)) { + if (line.starts_with("Title version:")) { + std::regex version_regex("(\\d+).(\\d+).(\\d+)"); + std::smatch matches; + if (std::regex_search(line, matches, version_regex)) { + if (matches.size() == 4) { + version.major = std::max(std::min(63, stoi(matches[1].str())), 0); + version.minor = std::max(std::min(63, stoi(matches[2].str())), 0); + version.micro = std::max(std::min(15, stoi(matches[3].str())), 0); } - break; } + break; } } return version;