diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a259eb..f522b4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,13 +3,21 @@ project( nsui_banner_fixer VERSION 2.0.0 DESCRIPTION "Fixes banners for GBA forwarders generated with NSUIv28 on non-US consoles." - LANGUAGES CXX) + LANGUAGES C CXX) + +option(BUILD_TESTS "Build tests." ON) +option(DYNAMIC_LINKING "Link CURL and OPENSSL dynamic instead of static. (Linux only)" OFF) +option(STATIC_STD_LIBS "Link libstdc++ and libgcc static instead of dynamic. (Linux only)" OFF) include(dependencies.cmake) add_subdirectory(src) -enable_testing() -add_subdirectory(test) +if(BUILD_TESTS) + enable_testing() + add_subdirectory(test) +endif() + + #---------------------------------------------------------------------------------------- diff --git a/dependencies.cmake b/dependencies.cmake index 51ad4bd..4bdd90f 100644 --- a/dependencies.cmake +++ b/dependencies.cmake @@ -20,39 +20,185 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(tclap) +#---------------------------------------------------------------------------------------- +# Windows #---------------------------------------------------------------------------------------- -FetchContent_Declare( - 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 -) -set(BOOST_INCLUDE_LIBRARIES process) -FetchContent_MakeAvailable(Boost) +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + FetchContent_Declare( + 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 + ) + set(BOOST_INCLUDE_LIBRARIES process) + FetchContent_MakeAvailable(Boost) -#---------------------------------------------------------------------------------------- + #---------------------------------------------------------------------------------------- -FetchContent_Declare( - 3dstool - URL https://github.com/dnasdw/3dstool/releases/download/v1.0.9/3dstool.zip -) + FetchContent_Declare( + 3dstool + URL https://github.com/dnasdw/3dstool/releases/download/v1.0.9/3dstool.zip + ) -FetchContent_Declare( - ctrtool - URL https://github.com/3DSGuy/Project_CTR/releases/download/ctrtool-v0.5/ctrtool-win_x86_64-v0.5.zip -) + FetchContent_Declare( + ctrtool + URL https://github.com/3DSGuy/Project_CTR/releases/download/ctrtool-v0.5/ctrtool-win_x86_64-v0.5.zip + ) -FetchContent_Declare( - makerom - URL https://github.com/3DSGuy/Project_CTR/releases/download/makerom-v0.15/makerom-win_x86_64-v0.15.zip -) + FetchContent_Declare( + makerom + URL https://github.com/3DSGuy/Project_CTR/releases/download/makerom-v0.15/makerom-win_x86_64-v0.15.zip + ) + + FetchContent_MakeAvailable(3dstool ctrtool makerom) + + #---------------------------------------------------------------------------------------- -FetchContent_MakeAvailable(3dstool ctrtool makerom) + install(FILES ${3dstool_SOURCE_DIR}/3dstool.exe DESTINATION tools) + install(FILES ${ctrtool_SOURCE_DIR}/ctrtool.exe DESTINATION tools) + install(FILES ${makerom_SOURCE_DIR}/makerom.exe DESTINATION tools) +endif() #---------------------------------------------------------------------------------------- +# Linux +#---------------------------------------------------------------------------------------- + +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + FetchContent_Declare( + stdcapture + GIT_REPOSITORY https://github.com/dmikushin/stdcapture.git + GIT_TAG 25ea65ba7933c4ce7baa48dcc90063476d539586 # + ) + FetchContent_MakeAvailable(stdcapture) + + #---------------------------------------------------------------------------------------- + + FetchContent_Declare( + 3dstool_download + GIT_REPOSITORY https://github.com/dnasdw/3dstool.git + GIT_TAG 9c4336bca8898f3860b41241b8a7d9d4a6772e79 + GIT_PROGRESS TRUE + PATCH_COMMAND git apply "${CMAKE_CURRENT_SOURCE_DIR}/patches/3dstool.patch" + UPDATE_DISCONNECTED 1 + ) + FetchContent_Populate(3dstool_download) + file(GLOB 3dstool_sources ${3dstool_download_SOURCE_DIR}/src/*.cpp) + + file(GLOB capstone_sources + ${3dstool_download_SOURCE_DIR}/dep/src/capstone-3.0.5/include/*.h + ${3dstool_download_SOURCE_DIR}/dep/src/capstone-3.0.5/*.h + ${3dstool_download_SOURCE_DIR}/dep/src/capstone-3.0.5/*.c + ${3dstool_download_SOURCE_DIR}/dep/src/capstone-3.0.5/arch/ARM/*.c + ${3dstool_download_SOURCE_DIR}/dep/src/capstone-3.0.5/arch/ARM/*.h + ${3dstool_download_SOURCE_DIR}/dep/src/capstone-3.0.5/arch/ARM/*.inc) + + if(DYNAMIC_LINKING) + find_package(CURL REQUIRED) + find_package(OpenSSL REQUIRED) + else() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(BUILD64 1) + endif() + include(${3dstool_download_SOURCE_DIR}/cmake/AddDep.cmake) + ADD_DEP_INCLUDE_DIR("${3dstool_download_SOURCE_DIR}/dep") + ADD_DEP_LIBRARY_DIR("${3dstool_download_SOURCE_DIR}/dep") + set(3dstool_deps libcurl libssl libcrypto) + foreach(LIB IN LISTS 3dstool_deps) + add_library(${LIB} STATIC IMPORTED) + add_dependencies(${LIB} 3dstool_check) + target_include_directories(${LIB} INTERFACE ${DEP_INCLUDE_DIR}) + set_target_properties(${LIB} PROPERTIES IMPORTED_LOCATION ${DEP_LIBRARY_DIR}/${LIB}.a) + endforeach() + target_link_libraries(libcrypto INTERFACE libssl libcurl) + target_link_libraries(libcurl INTERFACE libssl libcrypto) + target_link_libraries(libssl INTERFACE libcrypto libcurl) + target_link_libraries(libcrypto INTERFACE dl) + endif() + + #---------------------------------------------------------------------------------------- + + FetchContent_Declare( + project_ctr_download + GIT_REPOSITORY https://github.com/3DSGuy/Project_CTR.git + GIT_TAG master + GIT_PROGRESS TRUE + PATCH_COMMAND git apply "${CMAKE_CURRENT_SOURCE_DIR}/patches/ctr.patch" + UPDATE_DISCONNECTED 1 + ) + FetchContent_MakeAvailable(project_ctr_download) + + #---------------------------------------------------------------------------------------- + + FILE( + COPY ${project_ctr_download_SOURCE_DIR}/ctrtool/src/ + DESTINATION ${project_ctr_download_SOURCE_DIR}/include/ctrtool + FILES_MATCHING PATTERN "*.h") + + add_custom_command( + OUTPUT ${project_ctr_download_SOURCE_DIR}/ctrtool/bin/ctrtool.a + ${project_ctr_download_SOURCE_DIR}/ctrtool/deps/libbroadon-es/bin/libbroadon-es.a + ${project_ctr_download_SOURCE_DIR}/ctrtool/deps/libfmt/bin/libfmt.a + ${project_ctr_download_SOURCE_DIR}/ctrtool/deps/libmbedtls/bin/libmbedtls.a + ${project_ctr_download_SOURCE_DIR}/ctrtool/deps/libnintendo-n3ds/bin/libnintendo-n3ds.a + ${project_ctr_download_SOURCE_DIR}/ctrtool/deps/libtoolchain/bin/libtoolchain.a + COMMAND make -C ${project_ctr_download_SOURCE_DIR}/ctrtool deps static_lib + COMMENT "=================== running make on project_ctr/ctrtool ..." + ) + add_custom_target(ctrtool_make ALL DEPENDS ${project_ctr_download_SOURCE_DIR}/ctrtool/bin/ctrtool.a) + + add_library(ctrtool_lib STATIC IMPORTED) + add_dependencies(ctrtool_lib ctrtool_make) + target_include_directories(ctrtool_lib INTERFACE ${project_ctr_download_SOURCE_DIR}/include) + set_target_properties(ctrtool_lib PROPERTIES IMPORTED_LOCATION ${project_ctr_download_SOURCE_DIR}/ctrtool/bin/ctrtool.a) + + set(ctrtool_deps libbroadon-es libfmt libmbedtls libnintendo-n3ds libtoolchain) + foreach(LIB IN LISTS ctrtool_deps) + add_library(${LIB} STATIC IMPORTED) + add_dependencies(${LIB} ctrtool_make) + target_include_directories(${LIB} INTERFACE ${project_ctr_download_SOURCE_DIR}/ctrtool/deps/${LIB}/include) + set_target_properties(${LIB} PROPERTIES IMPORTED_LOCATION ${project_ctr_download_SOURCE_DIR}/ctrtool/deps/${LIB}/bin/${LIB}.a) + target_link_libraries(ctrtool_lib INTERFACE ${LIB}) + endforeach() + target_link_libraries(libtoolchain INTERFACE libmbedtls) + + #---------------------------------------------------------------------------------------- + + FILE( + COPY ${project_ctr_download_SOURCE_DIR}/makerom/src/ + DESTINATION ${project_ctr_download_SOURCE_DIR}/include/makerom + FILES_MATCHING PATTERN "*.h") + + add_custom_command( + OUTPUT ${project_ctr_download_SOURCE_DIR}/makerom/bin/makerom.a + ${project_ctr_download_SOURCE_DIR}/makerom/deps/libblz/bin/libblz.a + ${project_ctr_download_SOURCE_DIR}/makerom/deps/libyaml/bin/libyaml.a + ${project_ctr_download_SOURCE_DIR}/makerom/deps/libmbedtls/bin/libmbedtls.a + COMMAND make -C ${project_ctr_download_SOURCE_DIR}/makerom deps static_lib + COMMENT "=================== running make on project_ctr/makerom ..." + ) + add_custom_target(makerom_make ALL DEPENDS ${project_ctr_download_SOURCE_DIR}/makerom/bin/makerom.a) + + add_library(makerom_lib STATIC IMPORTED) + add_dependencies(makerom_lib makerom_make) + target_include_directories(makerom_lib INTERFACE ${project_ctr_download_SOURCE_DIR}/include) + set_target_properties(makerom_lib PROPERTIES IMPORTED_LOCATION ${project_ctr_download_SOURCE_DIR}/makerom/bin/makerom.a) + + set(makerom_deps libblz libyaml) + foreach(LIB IN LISTS makerom_deps) + add_library(${LIB} STATIC IMPORTED) + add_dependencies(${LIB} makerom_make) + target_include_directories(${LIB} INTERFACE ${project_ctr_download_SOURCE_DIR}/makerom/deps/${LIB}/include) + set_target_properties(${LIB} PROPERTIES IMPORTED_LOCATION ${project_ctr_download_SOURCE_DIR}/makerom/deps/${LIB}/bin/${LIB}.a) + target_link_libraries(makerom_lib INTERFACE ${LIB}) + endforeach() + + add_library(libmbedtls2 STATIC IMPORTED) + add_dependencies(libmbedtls2 makerom_make) + target_include_directories(libmbedtls2 INTERFACE ${project_ctr_download_SOURCE_DIR}/makerom/deps/libmbedtls/include) + set_target_properties(libmbedtls2 PROPERTIES IMPORTED_LOCATION ${project_ctr_download_SOURCE_DIR}/makerom/deps/libmbedtls/bin/libmbedtls.a) + target_link_libraries(makerom_lib INTERFACE libmbedtls2) +endif() + -install(FILES ${3dstool_SOURCE_DIR}/3dstool.exe DESTINATION tools) -install(FILES ${ctrtool_SOURCE_DIR}/ctrtool.exe DESTINATION tools) -install(FILES ${makerom_SOURCE_DIR}/makerom.exe DESTINATION tools) \ No newline at end of file diff --git a/patches/3dstool.patch b/patches/3dstool.patch new file mode 100644 index 0000000..38d7ab7 --- /dev/null +++ b/patches/3dstool.patch @@ -0,0 +1,150 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +deleted file mode 100644 +index 963d2714..00000000 +--- a/CMakeLists.txt ++++ /dev/null +@@ -1,79 +0,0 @@ +-cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +-project(3dstool) +-if(MSVC_VERSION EQUAL 1700 AND MSVC_IDE) +- set(CMAKE_GENERATOR_TOOLSET "v110_xp" CACHE STRING "Name of generator toolset." FORCE) +-endif() +-if(MSVC_VERSION EQUAL 1800 AND MSVC_IDE) +- set(CMAKE_GENERATOR_TOOLSET "v120_xp" CACHE STRING "Name of generator toolset." FORCE) +-endif() +-if(MSVC_VERSION EQUAL 1900 AND MSVC_IDE) +- set(CMAKE_GENERATOR_TOOLSET "v140_xp" CACHE STRING "Name of generator toolset." FORCE) +-endif() +-if(MSVC_VERSION GREATER 1909 AND MSVC_VERSION LESS 1920 AND MSVC_IDE) +- set(CMAKE_GENERATOR_TOOLSET "v141_xp" CACHE STRING "Name of generator toolset." FORCE) +-endif() +-if(MSVC_VERSION GREATER 1600 AND NOT MSVC_IDE) +- if(CMAKE_SIZEOF_VOID_P EQUAL 8) +- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:CONSOLE,5.02") +- else() +- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:CONSOLE,5.01") +- endif() +-endif() +-if(APPLE) +- set(CMAKE_MACOSX_RPATH 1) +-endif() +-set(_3DSTOOL_MAJOR 1) +-set(_3DSTOOL_MINOR 2) +-set(_3DSTOOL_PATCHLEVEL 6) +-if(NOT MSVC_IDE AND NOT XCODE_VERSION AND NOT CMAKE_BUILD_TYPE) +- set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) +-endif() +-option(BUILD64 "Build x86_64(unix only)" ON) +-if(MSVC OR APPLE OR (NOT CYGWIN AND NOT MINGW)) +- option(USE_DEP "Use prebuilt dep" ON) +-endif() +-set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}") +-set(ROOT_SOURCE_DIR "${PROJECT_SOURCE_DIR}") +-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${ROOT_SOURCE_DIR}/cmake") +-include(AddCompilationFlags) +-include(AddDep) +-include(AddTarget) +-include(AutoFiles) +-ADD_COMPILATION_FLAGS() +-if(USE_DEP) +- ADD_DEP_INCLUDE_DIR("${ROOT_SOURCE_DIR}/dep") +- ADD_DEP_LIBRARY_DIR("${ROOT_SOURCE_DIR}/dep") +-endif() +-if(UNIX OR MINGW) +- if(CYGWIN) +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") +- else() +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +- if(NOT APPLE) +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") +- endif() +- endif() +- if((UNIX AND BUILD64) OR (MINGW AND CMAKE_SIZEOF_VOID_P EQUAL 8)) +- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64") +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64") +- else() +- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") +- endif() +-endif() +-if(MSVC_IDE OR XCODE_VERSION) +- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin") +-else() +- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}") +-endif() +-add_definitions(-D_3DSTOOL_VERSION="${_3DSTOOL_MAJOR}.${_3DSTOOL_MINOR}.${_3DSTOOL_PATCHLEVEL}") +-if(WIN32) +- add_definitions(-D_CRT_SECURE_NO_WARNINGS) +-endif() +-if(UNIX OR MINGW) +- add_definitions(-D_FILE_OFFSET_BITS=64) +- add_definitions(-Wno-multichar -Wno-shift-overflow -Wno-unused-result) +- set(CMAKE_INSTALL_RPATH .) +- set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +-endif() +-add_subdirectory(src) +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +deleted file mode 100644 +index 23a33253..00000000 +--- a/src/CMakeLists.txt ++++ /dev/null +@@ -1,46 +0,0 @@ +-AUTO_FILES("." "src" "\\.(cpp|h)$") +-if(MSVC_VERSION LESS 1600) +- AUTO_FILES("${ROOT_SOURCE_DIR}/dep/src/capstone/msvc" "src" "\\.h$") +-endif() +-AUTO_FILES("${ROOT_SOURCE_DIR}/dep/src/capstone-3.0.5/include" "src" "\\.h$") +-AUTO_FILES("${ROOT_SOURCE_DIR}/dep/src/capstone-3.0.5" "src" "capstone-3.0.5/[^/]+\\.(c|h)$") +-AUTO_FILES("${ROOT_SOURCE_DIR}/dep/src/capstone-3.0.5/arch/ARM" "src" "\\.(c|h|inc)$") +-include_directories(${DEP_INCLUDE_DIR} "${ROOT_SOURCE_DIR}/dep/src/capstone-3.0.5/include") +-if(MSVC_VERSION LESS 1600) +- include_directories("${ROOT_SOURCE_DIR}/dep/src/capstone/msvc") +-endif() +-link_directories(${DEP_LIBRARY_DIR}) +-add_definitions(-DSDW_MAIN -DCURL_STATICLIB -DCAPSTONE_USE_SYS_DYN_MEM -DCAPSTONE_HAS_ARM) +-if(APPLE) +- add_definitions(-DSDW_XCONVERT) +-endif() +-ADD_EXE(3dstool "${src}") +-if(WIN32) +- if(MSVC) +- if(MSVC_VERSION LESS 1800) +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4005") +- endif() +- target_link_libraries(3dstool libcurl libcrypto WS2_32 Wldap32 Crypt32) +- set(3dstool_LINK_FLAGS_DEBUG "${3dstool_LINK_FLAGS_DEBUG} /NODEFAULTLIB:LIBCMT") +- if(MSVC_VERSION GREATER 1700) +- set(3dstool_LINK_FLAGS_DEBUG "${3dstool_LINK_FLAGS_DEBUG} /IGNORE:4099") +- set(3dstool_LINK_FLAGS_RELWITHDEBINFO "${3dstool_LINK_FLAGS_RELWITHDEBINFO} /IGNORE:4099") +- endif() +- set_target_properties(3dstool PROPERTIES +- LINK_FLAGS_DEBUG "${3dstool_LINK_FLAGS_DEBUG}" +- LINK_FLAGS_RELWITHDEBINFO "${3dstool_LINK_FLAGS_RELWITHDEBINFO}") +- else() +- target_link_libraries(3dstool curl crypto) +- endif() +-else() +- target_link_libraries(3dstool curl ssl crypto) +- if(APPLE) +- target_link_libraries(3dstool ldap) +- else() +- target_link_libraries(3dstool pthread dl) +- endif() +- if(APPLE OR CYGWIN) +- target_link_libraries(3dstool iconv) +- endif() +-endif() +-install(TARGETS 3dstool DESTINATION bin) +diff --git a/src/utility.cpp b/src/utility.cpp +index 7e341c06..ec84ca10 100644 +--- a/src/utility.cpp ++++ b/src/utility.cpp +@@ -113,7 +113,7 @@ void PadFile(FILE* a_fpFile, n64 a_nPadSize, u8 a_uPadData) + #if defined(SDW_MAIN) + extern int UMain(int argc, UChar* argv[]); + +-int main(int argc, char* argv[]) ++int main_3dstool(int argc, char* argv[]) + { + SetLocale(); + int nArgc = 0; diff --git a/patches/ctr.patch b/patches/ctr.patch new file mode 100644 index 0000000..8ac0f41 --- /dev/null +++ b/patches/ctr.patch @@ -0,0 +1,15 @@ +diff --git a/makerom/src/user_settings.h b/makerom/src/user_settings.h +index e09ec50..a44a714 100644 +--- a/makerom/src/user_settings.h ++++ b/makerom/src/user_settings.h +@@ -305,4 +305,7 @@ typedef struct + + void init_UserSettings(user_settings *set); + void free_UserSettings(user_settings *set); +-int ParseArgs(int argc, char *argv[], user_settings *usr_settings); +\ No newline at end of file ++int ParseArgs(int argc, char *argv[], user_settings *usr_settings); ++void SetDefaults(user_settings* set); ++int CheckArgumentCombination(user_settings* set); ++int SetKeys(keys_struct* keys); +\ No newline at end of file diff --git a/patches/subprocess.patch b/patches/subprocess.patch index 21568dc..2185ddf 100644 --- a/patches/subprocess.patch +++ b/patches/subprocess.patch @@ -1,3 +1,25 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 5acf5f8..bd8c649 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,14 +1,14 @@ +-cmake_minimum_required(VERSION 3.1) ++cmake_minimum_required(VERSION 3.5) + project(subprocess VERSION 0.0.1 LANGUAGES CXX) + +-set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to use") + option(EXPORT_COMPILE_COMMANDS "create clang compile database" ON) + option(SUBPROCESS_TESTS "enable subprocess tests" OFF) + option(SUBPROCESS_INSTALL "enable subprocess install" OFF) + + find_package(Threads REQUIRED) + +-add_library(subprocess INTERFACE) ++add_library(subprocess INTERFACE subprocess.hpp) ++target_compile_features(subprocess INTERFACE cxx_std_11) + target_link_libraries(subprocess INTERFACE Threads::Threads) + target_include_directories(subprocess INTERFACE . ) + diff --git a/subprocess.hpp b/subprocess.hpp index 62cfa92..7e34440 100755 --- a/subprocess.hpp diff --git a/patches/tclap.patch b/patches/tclap.patch index bbc45fb..2dd8d49 100644 --- a/patches/tclap.patch +++ b/patches/tclap.patch @@ -1,5 +1,5 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index c2c9a26..fd8f952 100644 +index c2c9a26..e40e55a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,12 @@ project(TCLAP VERSION 1.4.0 LANGUAGES CXX) @@ -18,9 +18,13 @@ index c2c9a26..fd8f952 100644 include(CheckCXXSourceCompiles) check_cxx_source_compiles("#include -@@ -20,12 +22,20 @@ configure_file(config.h.in tclap/TCLAPConfig.h) +@@ -18,14 +20,23 @@ int main() { std::istringstream iss; }" TCLAP_HAVE_SSTREAM) - add_library(TCLAP INTERFACE) + configure_file(config.h.in tclap/TCLAPConfig.h) + +-add_library(TCLAP INTERFACE) ++file(GLOB tclap_headers include/tclap/*.h) ++add_library(TCLAP INTERFACE ${tclap_headers}) target_include_directories(TCLAP INTERFACE include/) -install(TARGETS TCLAP DESTINATION include) -install(FILES "${PROJECT_BINARY_DIR}/tclap/TCLAPConfig.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..7f0db74 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,93 @@ +add_executable(nsui_banner_fixer nsui_banner_fixer.cpp) +target_compile_features(nsui_banner_fixer PRIVATE cxx_std_20) + +target_compile_definitions(nsui_banner_fixer PRIVATE VERSION=\"${CMAKE_PROJECT_VERSION}\") +string(TIMESTAMP year %Y UTC) +target_compile_definitions(nsui_banner_fixer PRIVATE YEAR="${year}") +string(TIMESTAMP compile_time "%Y-%m-%d %H:%M:%S UTC" UTC) +target_compile_definitions(nsui_banner_fixer PRIVATE COMPILE_TIME="${compile_time}") + +add_library(nsui_banner_fixer_game + Game.hpp + Game.cpp + 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 PRIVATE nsui_banner_fixer_game TCLAP) + +#---------------------------------------------------------------------------------------- +# Windows +#---------------------------------------------------------------------------------------- + +if (CMAKE_SYSTEM_NAME MATCHES "Windows") + #target_sources(nsui_banner_fixer_game PRIVATE GameWin.cpp) + target_link_libraries(nsui_banner_fixer_game PRIVATE Boost::process) + add_custom_command( + TARGET nsui_banner_fixer POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + $/tools + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${3dstool_SOURCE_DIR}/3dstool.exe + ${ctrtool_SOURCE_DIR}/ctrtool.exe + ${makerom_SOURCE_DIR}/makerom.exe + $/tools) +endif() + +#---------------------------------------------------------------------------------------- +# Linux +#---------------------------------------------------------------------------------------- + +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + target_sources(nsui_banner_fixer_game PRIVATE Tool.hpp Tool.cpp) + target_link_libraries(nsui_banner_fixer_game PRIVATE stdcapture) + + target_include_directories(nsui_banner_fixer_game PRIVATE ${3dstool_download_SOURCE_DIR}/src) + target_sources(nsui_banner_fixer_game PRIVATE ${3dstool_sources}) + + target_include_directories(nsui_banner_fixer_game PRIVATE ${3dstool_download_SOURCE_DIR}/dep/src/capstone-3.0.5/include) + target_sources(nsui_banner_fixer_game PRIVATE ${capstone_sources}) + target_compile_definitions(nsui_banner_fixer_game PRIVATE SDW_MAIN CAPSTONE_USE_SYS_DYN_MEM CAPSTONE_HAS_ARM _3DSTOOL_VERSION="1.2.6") + + if(DYNAMIC_LINKING) + target_link_libraries(nsui_banner_fixer_game PRIVATE CURL::libcurl) + target_link_libraries(nsui_banner_fixer_game PRIVATE OpenSSL::SSL OpenSSL::Crypto) + else() + target_compile_definitions(nsui_banner_fixer_game PRIVATE CURL_STATICLIB OPENSSL_USE_STATIC_LIBS) + target_include_directories(nsui_banner_fixer_game PRIVATE ${DEP_INCLUDE_DIR}) + target_link_libraries(nsui_banner_fixer_game PRIVATE libcurl libssl libcrypto) + endif() + + if(STATIC_STD_LIBS) + target_compile_options(nsui_banner_fixer_game PRIVATE -static-libgcc -static-libstdc++) + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + target_compile_options(nsui_banner_fixer_game PRIVATE -m64) + else() + target_compile_options(nsui_banner_fixer_game PRIVATE -m32) + endif() + target_compile_definitions(nsui_banner_fixer_game PUBLIC _FILE_OFFSET_BITS=64) + target_compile_options(nsui_banner_fixer_game PRIVATE -Wno-multichar -Wno-shift-overflow -Wno-unused-result) + target_link_libraries(nsui_banner_fixer_game PRIVATE pthread dl) + + target_link_libraries(nsui_banner_fixer_game PUBLIC ctrtool_lib) + target_link_libraries(nsui_banner_fixer_game PRIVATE makerom_lib) +endif() + + + +include(generate_product_version) +generate_product_version(ProductVersionFiles + NAME ${CMAKE_PROJECT_NAME} + ICON ${CMAKE_SOURCE_DIR}/resources/icon.ico + VERSION_MAJOR ${CMAKE_PROJECT_VERSION_MAJOR} + VERSION_MINOR ${CMAKE_PROJECT_VERSION_MINOR} + VERSION_PATCH ${CMAKE_PROJECT_VERSION_PATCH} + COMPANY_NAME pivotiii + FILE_DESCRIPTION ${CMAKE_PROJECT_DESCRIPTION}) +target_sources(nsui_banner_fixer PRIVATE ${ProductVersionFiles}) + + + +install(TARGETS nsui_banner_fixer DESTINATION .) \ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index a4d0bb8..edf0f9c 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,13 +1,19 @@ #include #include #include -#include #include #include #include "Game.hpp" +#if defined(_WIN32) + +#include +#include + +namespace bp = boost::process; + #define run_process(exec, arg_list, verbose_offset, ret_code, err_msg) \ { \ if (set.verbose) { \ @@ -33,9 +39,13 @@ } \ } \ } +#elif defined(__linux__) + +#include "Tool.hpp" + +#endif namespace fs = std::filesystem; -namespace bp = boost::process; const std::vector locale_offsets = {0x14BC, 0x14CB}; @@ -62,7 +72,7 @@ Game::Game(const fs::path &cia, const Settings &set) banner_ext("bin") { this->version = get_version(); - fs::create_directories(this->cwd); + fs::create_directories(cwd); } Game::~Game() @@ -70,40 +80,10 @@ Game::~Game() fs::remove_all(this->cwd.parent_path()); } -bool Game::fix_banner() -{ - if (!set.quiet) { - std::cout << "--- " << this->cia_path.string() << "\n--- extracting cia\n"; - } - if (!this->extract_cia()) { - std::cerr << "ERROR: Failed to extract CIA\n"; - return false; - } - - if (!set.quiet) { - std::cout << "--- editing banner\n"; - } - if (!this->edit_bcmdl()) { - std::cerr << "ERROR: Failed to edit banner files\n"; - return false; - } - - if (!set.quiet) { - std::cout << "--- repacking cia\n"; - } - if (!this->repack_cia()) { - std::cerr << "ERROR: Failed to repack CIA\n"; - return false; - } - - if (!set.quiet) { - std::cout << "--- done\n"; - } - return true; -} - versionS Game::get_version() { +#if defined(_WIN32) + versionS version; bp::ipstream output_stream; std::string line; @@ -127,6 +107,13 @@ versionS Game::get_version() break; } } + +#elif defined(__linux__) + + Tool::CTR ctr(cia_path, cwd, set); + versionS version = ctr.get_cia_version(); + +#endif if (set.verbose) { std::cout << name << "\n"; std::cout << "Detected version: " << version.major << "." << version.minor << "." << version.micro << "\n"; @@ -136,6 +123,8 @@ versionS Game::get_version() bool Game::extract_cia() { +#if defined(_WIN32) + std::vector extract_contents = {std::string("--contents=") + (this->cwd / "contents").string(), this->cia_path.string()}; run_process(set.ctrtool.string(), extract_contents, 1, 0, "Failed to extract contents from .CIA"); @@ -165,18 +154,77 @@ bool Game::extract_cia() run_process(set.dstool.string(), extract_banner, 1, 0, "Failed to extract banner from exefs"); return true; + +#elif defined(__linux__) + + Tool::CTR ctr(cia_path, cwd, set); + if (!ctr.extract_cia_contents()) { + std::cerr << "ERROR: Failed to extract contents from .CIA\n"; + return false; + } + + Tool::DS ds(name, cwd, set); + if (!ds.split_contents()) { + std::cerr << "ERROR: Failed to split contents\n"; + return false; + } + + if (!ds.extract_exefs()) { + std::cerr << "ERROR: Failed to extract exefs from contents\n"; + return false; + } + + if (!ds.extract_banner()) { + std::cerr << "ERROR: Failed to extract banner from exefs\n"; + return false; + } + + return true; + +#endif +} + +bool Game::fix_banner() +{ + if (!set.quiet) { + std::cout << "--- " << this->cia_path.string() << "\n--- extracting cia\n"; + } + if (!this->extract_cia()) { + std::cerr << "ERROR: Failed to extract CIA\n"; + return false; + } + + if (!set.quiet) { + std::cout << "--- editing banner\n"; + } + if (!this->edit_bcmdl()) { + std::cerr << "ERROR: Failed to edit banner files\n"; + return false; + } + + if (!set.quiet) { + std::cout << "--- repacking cia\n"; + } + if (!this->repack_cia()) { + std::cerr << "ERROR: Failed to repack CIA\n"; + return false; + } + + if (!set.quiet) { + std::cout << "--- done\n"; + } + return true; } bool Game::edit_bcmdl() { for (int i = 1; i < 14; i++) { std::fstream file; - file.open((this->cwd / "banner" / (std::string("banner") + std::to_string(i) + ".bcmdl")).string(), + file.open((cwd / "banner" / (std::string("banner") + std::to_string(i) + ".bcmdl")).string(), std::fstream::in | std::fstream::out | std::fstream::binary); if (file.is_open()) { - if (set.verbose) { + if (set.verbose) std::cout << "Editing banner" << i << ".bcmdl\n"; - } char rbuf[10] = {0}; for (const auto &offset : locale_offsets) { file.seekg(offset, std::fstream::beg); @@ -189,6 +237,7 @@ bool Game::edit_bcmdl() << ", data is \"" << std::string_view {rbuf, 6} << "\"\n"; return false; } + file.seekp(offset, std::fstream::beg); file.write(locale_codes[i - 1].c_str(), 6); if (set.verbose) { @@ -199,7 +248,6 @@ bool Game::edit_bcmdl() } } else { std::cerr << "ERROR: Failed to open banner" << i << ".bcmdl\n"; - std::cerr << "Was " << this->name << ".cia created with NSUI v28 using the 3D GBA banner?\n"; return false; } file.close(); @@ -209,6 +257,7 @@ bool Game::edit_bcmdl() bool Game::repack_cia() { +#if defined(_WIN32) fs::remove(this->cwd / "exefs" / (std::string("banner.") + this->banner_ext)); std::vector rebuild_banner = {"-c", "-t", "banner", "-f", (this->cwd / "exefs" / (std::string("banner.") + this->banner_ext)).string(), @@ -248,4 +297,43 @@ bool Game::repack_cia() run_process(set.makerom.string(), rebuild_cia, 0, 0, "Failed to rebuild CIA"); return true; + +#elif defined(__linux__) + + Tool::DS ds(name, cwd, set); + + if (!ds.rebuild_banner()) { + std::cerr << "ERROR: Failed to rebuild banner\n"; + return false; + } + + if (!ds.rebuild_exefs()) { + std::cerr << "ERROR: Failed to rebuild exefs\n"; + return false; + } + + if (!ds.rebuild_cxi()) { + std::cerr << "ERROR: Failed to rebuild cxi\n"; + return false; + } + + fs::path out_cia; + if (set.replace) { + out_cia = cia_path; + } else { + fs::path out_dir = cwd.parent_path().parent_path() / "out"; + fs::create_directories(out_dir); + out_cia = out_dir / (name + ".cia"); + } + + Tool::MakeRom mr(name, out_cia, version, set); + + if (!mr.rebuild_cia()) { + std::cerr << "ERROR: Failed to rebuild CIA\n"; + return false; + } + + return true; + +#endif } diff --git a/src/Game.hpp b/src/Game.hpp index a4edfbb..dcf6f39 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -16,7 +16,7 @@ class Game { ~Game(); bool fix_banner(); - protected: + private: Settings set; std::filesystem::path cia_path; std::filesystem::path cwd; diff --git a/src/Tool.cpp b/src/Tool.cpp new file mode 100644 index 0000000..d8b4b86 --- /dev/null +++ b/src/Tool.cpp @@ -0,0 +1,457 @@ +#include "Tool.hpp" + +#include +#include + +#include +// ctrtool +#include +#include +// 3dstool +#include +#include +#include +// makerom +extern "C" { +#include +// makerom/lib.h has to be imported first, all the other makerom headers depend on it but don't always import +#include +#include +#include +} + +#define capture_output(x, y) \ + { \ + std::stringstream captured; \ + { \ + std::capture::CaptureStdout cap([&](const char* buf, size_t szbuf) { \ + captured << std::string(buf, szbuf); \ + }); \ + x; \ + } \ + y.clear(); \ + std::string line; \ + while (std::getline(captured, line)) { \ + y.push_back(line); \ + } \ + } + +#define capture_output_and_errors(x, y) \ + { \ + std::stringstream captured2; \ + { \ + std::capture::CaptureStderr cap([&](const char* buf, size_t szbuf) { \ + captured2 << std::string(buf, szbuf); \ + }); \ + capture_output(x, y); \ + } \ + std::string line; \ + while (std::getline(captured2, line)) { \ + y.push_back(line); \ + } \ + } + +namespace fs = std::filesystem; + +namespace Tool { + +BasicTool::BasicTool(const Settings &set, const std::filesystem::path &cwd) + : set(set), + cwd(cwd) +{ +} + +std::vector BasicTool::get_output_lines() +{ + return output_lines; +} + +CTR::CTR(const fs::path &cia, const fs::path &cwd, const Settings &set) + : BasicTool(set, cwd), + cia(cia) +{ +} + +ctrtool::CiaProcess CTR::setup_cia_process(const std::vector &args) +{ + std::vector call_args = {set.bin.string()}; + call_args.insert(std::end(call_args), std::begin(args), std::end(args)); + ctrtool::CiaProcess proc; + ctrtool::Settings ctr_set = ctrtool::SettingsInitializer(call_args); + std::shared_ptr infile_stream = + std::make_shared(tc::io::FileStream(ctr_set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read)); + + proc.setInputStream(infile_stream); + proc.setKeyBag(ctr_set.opt.keybag); + proc.setCliOutputMode(ctr_set.opt.info); + proc.setVerboseMode(ctr_set.opt.verbose); + proc.setVerifyMode(ctr_set.opt.verify); + if (ctr_set.rom.content_extract_path.isSet()) + proc.setContentExtractPath(ctr_set.rom.content_extract_path.get()); + proc.setContentIndex(ctr_set.rom.content_process_index); + if (ctr_set.cia.certs_path.isSet()) + proc.setCertExtractPath(ctr_set.cia.certs_path.get()); + if (ctr_set.cia.tik_path.isSet()) + proc.setTikExtractPath(ctr_set.cia.tik_path.get()); + if (ctr_set.cia.tmd_path.isSet()) + proc.setTmdExtractPath(ctr_set.cia.tmd_path.get()); + if (ctr_set.cia.meta_path.isSet()) + proc.setFooterExtractPath(ctr_set.cia.meta_path.get()); + proc.setRawMode(ctr_set.opt.raw); + proc.setPlainMode(ctr_set.opt.plain); + proc.setShowSyscallName(ctr_set.exheader.show_syscalls_as_names); + proc.setNcchRegionProcessOutputMode( + ctrtool::NcchProcess::NcchRegion_Header, ctr_set.opt.info, false, tc::Optional(), tc::Optional()); + proc.setNcchRegionProcessOutputMode( + ctrtool::NcchProcess::NcchRegion_ExHeader, ctr_set.opt.info, false, ctr_set.ncch.exheader_path, tc::Optional()); + proc.setNcchRegionProcessOutputMode( + ctrtool::NcchProcess::NcchRegion_PlainRegion, false, false, ctr_set.ncch.plainregion_path, tc::Optional()); + proc.setNcchRegionProcessOutputMode( + ctrtool::NcchProcess::NcchRegion_Logo, false, false, ctr_set.ncch.logo_path, tc::Optional()); + proc.setNcchRegionProcessOutputMode( + ctrtool::NcchProcess::NcchRegion_ExeFs, ctr_set.opt.info, ctr_set.exefs.list_fs, ctr_set.ncch.exefs_path, ctr_set.exefs.extract_path); + proc.setNcchRegionProcessOutputMode( + ctrtool::NcchProcess::NcchRegion_RomFs, ctr_set.opt.info, ctr_set.romfs.list_fs, ctr_set.ncch.romfs_path, ctr_set.romfs.extract_path); + + return proc; +} + +versionS CTR::get_cia_version() +{ + std::vector call_args = {"-i", cia.string()}; + ctrtool::CiaProcess proc = setup_cia_process(call_args); + + capture_output(proc.process(), output_lines); + + versionS version; + for (auto line : output_lines) { + if (line.starts_with("|- TitleVersion:")) { + std::regex version_regex("(\\d+).(\\d+).(\\d+)"); + std::smatch matches; + if (std::regex_search(line, matches, version_regex)) { + if (matches.size() == 4) { +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + 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); +#else + 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); +#endif + } + } + break; + } + } + return version; +} + +bool CTR::extract_cia_contents() +{ + if (set.verbose) { + std::cout << "----- extracting cia contents\n"; + } + const std::vector call_args = {std::string("--contents=") + (cwd / "contents").string(), cia.string()}; + ctrtool::CiaProcess proc = setup_cia_process(call_args); + capture_output(proc.process(), output_lines); + if (set.verbose) { + for (const auto &line : output_lines) { + std::cout << line << "\n"; + } + } + if (set.verbose) { + std::cout << "----- extracting cia contents done\n"; + } + return true; +} + +DS::DS(const std::string &name, const fs::path &cwd, const Settings &set) + : BasicTool(set, cwd), + name(name) +{ +} + +void DS::convert_args(const std::vector &args, int &argc, std::vector &argv) +{ + std::vector call_args = {set.bin.string()}; + call_args.insert(std::end(call_args), std::begin(args), std::end(args)); + + auto convert = [](const std::string &s) -> char* { + char* pc = new char[s.size() + 1]; + std::strcpy(pc, s.c_str()); + return pc; + }; + + std::transform(call_args.begin(), call_args.end(), std::back_inserter(argv), convert); + + // argv.reserve(args.size() + 1); + // argv.push_back(set.bin.string().c_str()); + // for (int i = 0; i < args.size(); ++i) { + // argv.push_back(args[i].c_str()); + // } + argc = args.size() + 1; +} + +int DS::run_3dstool(const std::vector &args) +{ + int argc_like = 0; + std::vector argv_like = {}; + convert_args(args, argc_like, argv_like); + int a = main_3dstool(argc_like, &argv_like[0]); + for (size_t i = 0; i < argv_like.size(); i++) + delete[] argv_like[i]; + return a; +} + +bool DS::split_contents() +{ + if (set.verbose) { + std::cout << "----- splitting contents\n"; + } + bool result; + std::filesystem::path contents = cwd / "contents.0000.00000000"; + if (std::filesystem::exists(contents)) { + CNcch ncch; + ncch.SetFileName(contents.string()); + ncch.SetVerbose(set.verbose); + ncch.SetHeaderFileName((cwd / "ncch.header").string()); + ncch.SetEncryptMode(3); + ncch.SetDev(false); + ncch.SetExtendedHeaderFileName((cwd / "exheader.bin").string()); + ncch.SetLogoRegionFileName(""); + ncch.SetPlainRegionFileName(""); + ncch.SetExeFsFileName((cwd / "exefs.bin").string()); + ncch.SetRomFsFileName((cwd / "romfs.bin").string()); + result = ncch.ExtractFile(); + } else { + std::cerr << "ERROR: can't find " << contents.string() << "\n"; + return false; + } + if (result) { + if (set.verbose) { + std::cout << "----- splitting contents done\n"; + } + return true; + } else { + std::cerr << "ERROR: There was an error splitting the contents\n"; + return false; + } +} + +bool DS::extract_exefs() +{ + if (set.verbose) { + std::cout << "----- extracting exefs\n"; + } + bool result; + std::filesystem::path exefs = cwd / "exefs.bin"; + if (std::filesystem::exists(exefs)) { + CExeFs exeFs; + exeFs.SetFileName((cwd / "exefs.bin").string()); + exeFs.SetVerbose(set.verbose); + exeFs.SetHeaderFileName((cwd / "exefs.header").string()); + exeFs.SetExeFsDirName((cwd / "exefs").string()); + exeFs.SetUncompress(false); + result = exeFs.ExtractFile(); + } else { + std::cerr << "ERROR: can't find " << exefs.string() << "\n"; + return false; + } + if (result) { + if (set.verbose) { + std::cout << "----- extracting exefs done\n"; + } + return true; + } else { + std::cerr << "ERROR: There was an error extracting exefs\n"; + return false; + } +} + +bool DS::extract_banner() +{ + if (set.verbose) { + std::cout << "----- extracting banner\n"; + } + bool result; + fs::create_directory(cwd / "banner"); + std::string banner_ext = "bin"; + if (fs::exists(cwd / "exefs" / "banner.bnr")) + banner_ext = "bnr"; + + CBanner banner; + banner.SetFileName((cwd / "exefs" / (std::string("banner.") + banner_ext)).string()); + banner.SetVerbose(set.verbose); + banner.SetBannerDirName((cwd / "banner").string()); + result = banner.ExtractFile(); + + if (result) { + if (set.verbose) { + std::cout << "----- extracting banner done\n"; + } + return true; + } else { + std::cerr << "ERROR: There was an error extracting the banner from exefs\n"; + return false; + } +} + +bool DS::rebuild_banner() +{ + if (set.verbose) { + std::cout << "----- rebuilding banner\n"; + } + bool result; + std::string banner_ext = "bin"; + if (fs::exists(cwd / "exefs" / "banner.bnr")) + banner_ext = "bnr"; + fs::remove(cwd / "exefs" / (std::string("banner.") + banner_ext)); + + CBanner banner; + banner.SetFileName((cwd / "exefs" / (std::string("banner.") + banner_ext)).string()); + banner.SetVerbose(set.verbose); + banner.SetBannerDirName((cwd / "banner").string()); + result = banner.CreateFile(); + + if (result) { + if (set.verbose) { + std::cout << "----- rebuilding banner done\n"; + } + return true; + } else { + std::cerr << "ERROR: There was an error rebuilding the banner\n"; + return false; + } +} + +bool DS::rebuild_exefs() +{ + if (set.verbose) { + std::cout << "----- rebuilding exefs\n"; + } + bool result; + CExeFs exeFs; + exeFs.SetFileName((cwd / "exefs.bin").string()); + exeFs.SetVerbose(set.verbose); + exeFs.SetHeaderFileName((cwd / "exefs.header").string()); + exeFs.SetExeFsDirName((cwd / "exefs").string()); + exeFs.SetCompress(false); + result = exeFs.CreateFile(); + + if (result) { + if (set.verbose) { + std::cout << "----- rebuilding exefs done\n"; + } + return true; + } else { + std::cerr << "ERROR: There was an error rebuilding exefs\n"; + return false; + } +} + +bool DS::rebuild_cxi() +{ + if (set.verbose) { + std::cout << "----- rebuilding cxi\n"; + } + bool result; + CNcch ncch; + ncch.SetFileName((cwd / (name + ".cxi")).string()); + ncch.SetVerbose(set.verbose); + ncch.SetHeaderFileName((cwd / "ncch.header").string()); + ncch.SetEncryptMode(3); + ncch.SetRemoveExtKey(true); + ncch.SetDev(false); + ncch.SetExtendedHeaderFileName((cwd / "exheader.bin").string()); + ncch.SetLogoRegionFileName(""); + ncch.SetPlainRegionFileName(""); + ncch.SetExeFsFileName((cwd / "exefs.bin").string()); + ncch.SetRomFsFileName((cwd / "romfs.bin").string()); + result = ncch.CreateFile(); + + if (result) { + if (set.verbose) { + std::cout << "----- rebuilding cxi done\n"; + } + return true; + } else { + std::cerr << "ERROR: There was an error rebuilding cxi\n"; + return false; + } +} + +MakeRom::MakeRom(const std::string &name, const std::filesystem::path &out_cia, const versionS version, const Settings &set) + : BasicTool(set, set.cwd), + name(name), + out_cia(out_cia), + version(version) +{ +} + +bool MakeRom::rebuild_cia() +{ + if (set.verbose) { + std::cout << "----- rebuilding CIA\n"; + } + int result; + std::string cxi = (cwd / "temp" / name / (name + ".cxi")).string(); + user_settings mr_set; + + init_UserSettings(&mr_set); + initRand(); + InitKeys(&mr_set.common.keys); + SetDefaults(&mr_set); + mr_set.common.outFormat = CIA; + mr_set.common.outFileName = const_cast(out_cia.c_str()); + mr_set.common.outFileName_mallocd = false; + mr_set.common.verbose = set.verbose; + mr_set.cia.useNormTitleVer = true; + mr_set.cia.titleVersion[VER_MAJOR] = version.major; + mr_set.cia.titleVersion[VER_MINOR] = version.minor; + mr_set.cia.titleVersion[VER_MICRO] = version.micro; + mr_set.common.contentPath = (char**) calloc(CIA_MAX_CONTENT, sizeof(char*)); + mr_set.common.contentPath[0] = cxi.data(); + mr_set.common.contentSize[0] = GetFileSize64(cxi.data()); + mr_set.cia.contentId[0] = 0; + SetKeys(&mr_set.common.keys); + + FILE* ncch0 = fopen(mr_set.common.contentPath[0], "rb"); + ncch_hdr hdr; + + ReadNcchHdr(&hdr, ncch0); + u64 fileSize = GetFileSize64(mr_set.common.contentPath[0]); + mr_set.common.workingFile.size = fileSize; + mr_set.common.workingFile.buffer = (u8*) malloc(fileSize); + ReadFile64(mr_set.common.workingFile.buffer, mr_set.common.workingFile.size, 0, ncch0); + fclose(ncch0); + + std::vector temp_output; + capture_output_and_errors(result = build_CIA(&mr_set), temp_output); + + for (size_t i = 0; i < temp_output.size(); i++) { + if (!temp_output[i].starts_with("[NCCH ERROR] Failed to load ncch aes key") && + !temp_output[i].starts_with("[CIA WARNING] CXI AES Key could not be loaded") && + !temp_output[i].starts_with(" Meta Region, SaveDataSize, Remaster Version cannot be obtained")) { + output_lines.push_back(temp_output[i]); + } + } + if (set.verbose) { + for (const auto &line : output_lines) { + std::cout << line << "\n"; + } + } + + free(mr_set.common.contentPath); + + if (result == 0) { + if (set.verbose) { + std::cout << "----- rebuilding CIA done\n"; + } + return true; + } else { + std::cerr << "ERROR: There was an error rebuilding CIA\n"; + return false; + } +} +} // namespace Tool diff --git a/src/Tool.hpp b/src/Tool.hpp new file mode 100644 index 0000000..fd98299 --- /dev/null +++ b/src/Tool.hpp @@ -0,0 +1,66 @@ +#pragma once +#include +#include +#include + +#include "Game.hpp" +#include "Settings.hpp" + +#include + +int main_3dstool(int argc, char* argv[]); +namespace Tool { + +class BasicTool { + protected: + std::vector output_lines; + Settings set; + std::filesystem::path cwd; + + public: + BasicTool(const Settings &set, const std::filesystem::path &cwd); + std::vector get_output_lines(); +}; + +class CTR : public BasicTool { + protected: + std::filesystem::path cia; + ctrtool::CiaProcess setup_cia_process(const std::vector &args); + + public: + CTR(const std::filesystem::path &cia, const std::filesystem::path &cwd, const Settings &set); + versionS get_cia_version(); + bool extract_cia_contents(); +}; + +class DS : public BasicTool { + protected: + std::string name; + void convert_args(const std::vector &args, int &argc, std::vector &argv); + int run_3dstool(const std::vector &args); + + public: + DS(const std::string &name, const std::filesystem::path &cwd, const Settings &set); + bool split_contents(); + bool extract_exefs(); + bool extract_banner(); + bool rebuild_banner(); + bool rebuild_exefs(); + bool rebuild_cxi(); +}; + +class MakeRom : public BasicTool { + protected: + std::string name; + versionS version; + std::filesystem::path out_cia; + + public: + MakeRom(const std::string &name, const std::filesystem::path &out_cia, const versionS version, const Settings &set); + bool rebuild_cia(); +}; +} // namespace Tool + +class CIABuilder { + protected: +}; diff --git a/src/nsui_banner_fixer.cpp b/src/nsui_banner_fixer.cpp index e7b7f87..fff34c1 100644 --- a/src/nsui_banner_fixer.cpp +++ b/src/nsui_banner_fixer.cpp @@ -1,8 +1,5 @@ -#include -#include #include #include -#include #include #include @@ -10,6 +7,12 @@ #include "Game.hpp" #include "Settings.hpp" +#ifdef _WIN32 +#include +#include +#include +#endif + #ifndef VERSION #define VERSION "0.0.0" #endif @@ -22,6 +25,21 @@ namespace fs = std::filesystem; +void pause_if_double_clicked(bool require_key_press = true, int sleep = 0) +{ +#ifdef _WIN32 + DWORD procIDs[2]; + DWORD maxCount = 2; + DWORD result = GetConsoleProcessList((LPDWORD) procIDs, maxCount); + if (result == 1) { + std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + if (require_key_press) { + system("pause"); + } + } +#endif +} + bool check_requirements(std::vector reqs) { uint8_t err_count = 0; @@ -37,19 +55,6 @@ bool check_requirements(std::vector reqs) return true; } -void pause_if_double_clicked(bool require_key_press = true, int sleep = 0) -{ - DWORD procIDs[2]; - DWORD maxCount = 2; - DWORD result = GetConsoleProcessList((LPDWORD) procIDs, maxCount); - if (result == 1) { - std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); - if (require_key_press) { - system("pause"); - } - } -} - int parse_args(int argc, char** argv, std::vector &cias, Settings &set) { try { @@ -126,6 +131,7 @@ int main(int argc, char* argv[]) set.bin = argv[0]; set.cwd = fs::current_path(); +#ifdef _WIN32 set.dstool = set.bin.parent_path() / "tools" / "3dstool.exe"; set.ctrtool = set.bin.parent_path() / "tools" / "ctrtool.exe"; set.makerom = set.bin.parent_path() / "tools" / "makerom.exe"; @@ -133,6 +139,7 @@ int main(int argc, char* argv[]) std::cerr << "ERROR: requirements are missing!\n"; return 1; } +#endif std::vector cia_paths; @@ -160,7 +167,6 @@ int main(int argc, char* argv[]) for (const auto &res : results) { if (res.result == false) { std::cerr << "ERROR: There was a problem processing " << res.cia << "\n"; - return 1; } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..adfe227 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,38 @@ +project(nsui_banner_fixer_tests LANGUAGES CXX) + +set(test_names + test_fix_banner + test_get_version) +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) + if(CMAKE_SYSTEM_NAME MATCHES Windows) + target_link_libraries(${test_name} PRIVATE Boost::process) + endif() + add_test(NAME ${test_name} COMMAND $ WORKING_DIRECTORY $) +endforeach() + +list(GET test_names 0 DUMMY_TARGET) + +if(CMAKE_SYSTEM_NAME MATCHES Windows) + add_custom_command( + TARGET ${DUMMY_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + $/tools + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${3dstool_SOURCE_DIR}/3dstool.exe + ${ctrtool_SOURCE_DIR}/ctrtool.exe + ${makerom_SOURCE_DIR}/makerom.exe + $/tools) +endif() + +foreach(test_file IN LISTS test_files) + add_custom_command( + TARGET ${DUMMY_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/${test_file} + $) +endforeach() \ No newline at end of file diff --git a/test/test_get_version.cpp b/test/test_get_version.cpp index afde794..eafd725 100644 --- a/test/test_get_version.cpp +++ b/test/test_get_version.cpp @@ -1,16 +1,21 @@ #include -#include - -#include #include #include -namespace fs = std::filesystem; +#if defined(_WIN32) +#include +#include namespace bp = boost::process; +#elif defined(__linux) +#include +#endif + +namespace fs = std::filesystem; versionS get_version(const fs::path &cia, const Settings set) { +#if defined(_WIN32) versionS version; bp::ipstream output_stream; std::string line; @@ -35,6 +40,11 @@ versionS get_version(const fs::path &cia, const Settings set) } } return version; +#elif defined(__linux__) + Tool::CTR ctr(cia, set.cwd, set); + versionS version = ctr.get_cia_version(); + return version; +#endif } bool test_get_version(const fs::path &cia, const Settings &set)