diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 411925483fe..ab44d478687 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -580,6 +580,8 @@ if(WZ_INCLUDE_VIDEOS) endif() endif() +add_subdirectory(mods mods_staging) + ADD_CUSTOM_TARGET(data ALL DEPENDS data_base data_mp) if(TARGET data_terrain_overrides_classic) @@ -611,6 +613,23 @@ foreach(font_file ${wz2100_fonts_FILES}) ) endforeach() +# Copy mod files to build directory to support IDE build+run (from the build dir) scenarios +if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/mods") + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mods") +endif() +add_dependencies(data data_mods) +foreach(mod_file ${wz2100_mods_FILES}) + file(RELATIVE_PATH _mods_file_relative_path "${CMAKE_CURRENT_BINARY_DIR}/mods_staging" "${mod_file}") + get_filename_component(_mods_file_subdir_path "${_mods_file_relative_path}" DIRECTORY) + get_filename_component(_mods_file_name "${_mods_file_relative_path}" NAME) + add_custom_command( + TARGET data + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${mod_file}" "${CMAKE_CURRENT_BINARY_DIR}/mods/${_mods_file_subdir_path}/${_mods_file_name}" + VERBATIM + ) +endforeach() + set(DATA_FILES "${CMAKE_CURRENT_BINARY_DIR}/base.wz" "${CMAKE_CURRENT_BINARY_DIR}/mp.wz" @@ -651,6 +670,15 @@ if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") ) endforeach() + foreach(_mod_file ${wz2100_mods_FILES}) + file(RELATIVE_PATH _mods_file_relative_path "${CMAKE_CURRENT_BINARY_DIR}/mods_staging" "${_mod_file}") + get_filename_component(_mods_file_subdir_path "${_mods_file_relative_path}" DIRECTORY) + install(FILES ${_mod_file} + DESTINATION "${WZ_DATADIR}/mods/${_mods_file_subdir_path}" + COMPONENT Data + ) + endforeach() + else() # Emscripten: @@ -703,3 +731,5 @@ set(DATA_TERRAIN_OVERRIDES_FILES ${DATA_TERRAIN_OVERRIDES_FILES} PARENT_SCOPE) set(DATA_FONTS ${wz2100_fonts_FILES} PARENT_SCOPE) set(DATA_MUSIC_FILES ${DATA_MUSIC_FILES} PARENT_SCOPE) set(DATA_MUSIC_BASE_SOURCEDIR "${CMAKE_CURRENT_SOURCE_DIR}/music" PARENT_SCOPE) +set(DATA_MODS_FILES ${wz2100_mods_FILES} PARENT_SCOPE) +set(DATA_MODS_BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}/mods_staging" PARENT_SCOPE) diff --git a/data/mods/CMakeLists.txt b/data/mods/CMakeLists.txt new file mode 100644 index 00000000000..cf878119676 --- /dev/null +++ b/data/mods/CMakeLists.txt @@ -0,0 +1,110 @@ +if(NOT DEFINED WZ_DATADIR) + message(FATAL_ERROR "This file should be included after WZ_DATADIR is defined.") +endif() + +OPTION(WZ_BUILTIN_MODS_CAMCLASSIC "Include the classic campaign balance mod" ON) + +if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") + # "Fake" COMPRESS_ZIP that just stages the needed files in folders + include(EmscriptenCompressZip) +else() + find_package(ZIP REQUIRED) +endif() + +#################################################### + +# IMPORTANT: Must set GENERATED property at this directory level for autorevision.h +set_source_files_properties("${wz2100_autorevision_cache_file}" PROPERTIES GENERATED TRUE) +set_source_files_properties("${wz2100_autorevision_h_file}" PROPERTIES GENERATED TRUE) + +function(_MODINFO_JSON_GET_UNIQUE_TARGET_NAME _name _unique_name) + set(propertyName "_MODINFO_JSON_UNIQUE_COUNTER_${_name}") + get_property(currentCounter GLOBAL PROPERTY "${propertyName}") + if(NOT currentCounter) + set(currentCounter 1) + endif() + set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE) + math(EXPR currentCounter "${currentCounter} + 1") + set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} ) +endfunction() + +function(GENERATE_MOD_INFO_JSON TEMPLATE_FILE OUTPUT_PATH) + + get_filename_component(OUTPUT_PATH_DIR ${OUTPUT_PATH} DIRECTORY) + if(NOT EXISTS "${OUTPUT_PATH_DIR}") + file(MAKE_DIRECTORY "${OUTPUT_PATH_DIR}") + endif() + + if(NOT EXISTS "${TEMPLATE_FILE}") + message(FATAL_ERROR "Missing input template file: ${TEMPLATE_FILE}") + endif() + + # Generate the mod-info.json (with version info) + add_custom_command( + OUTPUT "${OUTPUT_PATH}" + COMMAND ${CMAKE_COMMAND} -DCACHEFILE=${wz2100_autorevision_cache_file} -DPROJECT_ROOT=${PROJECT_SOURCE_DIR} -DTEMPLATE_FILE=${TEMPLATE_FILE} -DOUTPUT_FILE=${OUTPUT_PATH} -P ${CMAKE_CURRENT_SOURCE_DIR}/autorevision_modinfo.cmake + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + DEPENDS "${TEMPLATE_FILE}" "${wz2100_autorevision_cache_file}" "${CMAKE_CURRENT_SOURCE_DIR}/autorevision_modinfo.cmake" + VERBATIM + ) + + if(NOT TARGET mod_info_gen) + add_custom_target(mod_info_gen) + set_property(TARGET mod_info_gen PROPERTY FOLDER "_WZBuildProcessTargets") + endif() + + _MODINFO_JSON_GET_UNIQUE_TARGET_NAME(modinfo uniqueTargetName) + + add_custom_target(${uniqueTargetName} ALL + DEPENDS "${OUTPUT_PATH}" + ) + set_property(TARGET ${uniqueTargetName} PROPERTY FOLDER "_WZBuildProcessTargets") + add_dependencies(${uniqueTargetName} autorevision) # Ensure ordering and non-concurrency + + add_dependencies(mod_info_gen ${uniqueTargetName}) + +endfunction() + +add_custom_target(data_mods) +set_property(TARGET data_mods PROPERTY FOLDER "_WZBuildProcessTargets") + +#################################################### +# Campaign mods + +if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/campaign") + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/campaign") +endif() + +if (WZ_BUILTIN_MODS_CAMCLASSIC) + + # Classic campaign balance mod + + file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/campaign/wz2100_camclassic.wz") + + GENERATE_MOD_INFO_JSON( + "${CMAKE_CURRENT_SOURCE_DIR}/campaign/wz2100_camclassic/mod-info.json.in" + "${CMAKE_CURRENT_BINARY_DIR}/generated_info_json/wz2100_camclassic/mod-info.json" + ) + + COMPRESS_ZIP(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/campaign/wz2100_camclassic.wz" + COMPRESSION_LEVEL 7 + PATHS + "mod-info.json" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated_info_json/wz2100_camclassic" + PATHS + "stats" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/campaign/wz2100_camclassic" + BUILD_ALWAYS_TARGET data_mods_campaign_wz2100_camclassic + IGNORE_GIT + QUIET + ) + add_dependencies(data_mods_campaign_wz2100_camclassic mod_info_gen) + set_property(TARGET data_mods_campaign_wz2100_camclassic PROPERTY FOLDER "data") + list(APPEND wz2100_mods_FILES "${CMAKE_CURRENT_BINARY_DIR}/campaign/wz2100_camclassic.wz") + add_dependencies(data_mods data_mods_campaign_wz2100_camclassic) + +endif(WZ_BUILTIN_MODS_CAMCLASSIC) + +#################################################### + +set(wz2100_mods_FILES ${wz2100_mods_FILES} PARENT_SCOPE) diff --git a/data/mods/autorevision_modinfo.cmake b/data/mods/autorevision_modinfo.cmake new file mode 100644 index 00000000000..06479683300 --- /dev/null +++ b/data/mods/autorevision_modinfo.cmake @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.5...3.27) +if(${CMAKE_VERSION} VERSION_LESS 3.12) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +endif() + +# Automatically update the version compatibility info in a mod-info.json file +# Replaces any instances of @MODINFO_LATEST_VERSION@ with the latest tagged version number +# +# Required input defines: +# - CACHEFILE: the path to the autorevision.cache file generated for the build +# - PROJECT_ROOT: the path the project root (${PROJECT_SOURCE_DIR}) +# - TEMPLATE_FILE: the full filename + path for the input mod-info.json.in template file +# - OUTPUT_FILE: the full filename + path for the output mod-info.json file +# + +if(NOT DEFINED CACHEFILE OR "${CACHEFILE}" STREQUAL "") + message( FATAL_ERROR "Missing required input define: CACHEFILE" ) +endif() +if(NOT DEFINED PROJECT_ROOT OR "${PROJECT_ROOT}" STREQUAL "") + message( FATAL_ERROR "Missing required input define: PROJECT_ROOT" ) +endif() +if(NOT DEFINED TEMPLATE_FILE OR "${TEMPLATE_FILE}" STREQUAL "") + message( FATAL_ERROR "Missing required input define: TEMPLATE_FILE" ) +endif() +if(NOT DEFINED OUTPUT_FILE OR "${OUTPUT_FILE}" STREQUAL "") + message( FATAL_ERROR "Missing required input define: OUTPUT_FILE" ) +endif() + +################################# + +execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++Get build revision info from: ${CACHEFILE}") + +# Attempt to get version information from the current build's autorevision cache. Fail if cache is not present. +execute_process(COMMAND ${CMAKE_COMMAND} -DCACHEFILE=${CACHEFILE} -DSKIPUPDATECACHE="1" -DCACHEFORCE="1" -DVAROUT=1 -P "${PROJECT_ROOT}/build_tools/autorevision.cmake" + WORKING_DIRECTORY "${PROJECT_ROOT}" + OUTPUT_VARIABLE autorevision_info + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +include("${PROJECT_ROOT}/build_tools/autorevision_helpers.cmake") + +# Import the autorevision values into the current scope +cmakeSetAutorevisionValues("${autorevision_info}") + +unset(MODINFO_LATEST_VERSION) + +################################## +# Determine the ProductVersion + +if(DEFINED VCS_TAG AND NOT "${VCS_TAG}" STREQUAL "") + # We're on an exact tag + # See if the tag contains a version number + extractVersionNumberFromGitTag("${VCS_TAG}") + if(DID_EXTRACT_VERSION) + # Able to extract a version number from the tag - use it for the MODINFO_LATEST_TAGGED_VERSION + set(MODINFO_LATEST_VERSION "${EXTRACTED_VERSION_MAJOR}.${EXTRACTED_VERSION_MINOR}.${EXTRACTED_VERSION_PATCH}") + endif() +endif() + +# Extract version info from the most recent tagged version +extractVersionNumberFromGitTag("${VCS_MOST_RECENT_TAGGED_VERSION}") +if(DID_EXTRACT_VERSION) + +else() + message( WARNING "The VCS_MOST_RECENT_TAGGED_VERSION tag does not seem to include a version" ) + execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++Warning: The VCS_MOST_RECENT_TAGGED_VERSION tag does not seem to include a version") +endif() + +if(NOT DEFINED MODINFO_LATEST_VERSION) + # Set the MODINFO_LATEST_VERSION to: + set(MODINFO_LATEST_VERSION "${EXTRACTED_VERSION_MAJOR}.${EXTRACTED_VERSION_MINOR}.${EXTRACTED_VERSION_PATCH}") +endif() + +################################## +# Debug output + +execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++MODINFO_LATEST_VERSION: ${MODINFO_LATEST_VERSION}") + +################################## +# Output configured file based on the template + +if(NOT EXISTS "${TEMPLATE_FILE}") + message( FATAL_ERROR "Input TEMPLATE_FILE does not exist: \"${TEMPLATE_FILE}\"" ) +endif() +configure_file("${TEMPLATE_FILE}" "${OUTPUT_FILE}" @ONLY) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index baddf629cf2..1c89272586b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -299,6 +299,14 @@ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") target_link_options(warzone2100 PRIVATE "SHELL:--preload-file ${_font_file}@/data/fonts/") endforeach() + # Bundle built-in mods + foreach(_mod_file ${DATA_MODS_FILES}) + file(RELATIVE_PATH _mods_file_relative_path "${DATA_MODS_BASE_DIR}" "${_mod_file}") + get_filename_component(_mods_file_subdir_path "${_mods_file_relative_path}" DIRECTORY) + message(STATUS "COPYING MOD FILE: mods/${_mods_file_relative_path}") + target_link_options(warzone2100 PRIVATE "SHELL:--preload-file ${_mod_file}@/data/mods/${_mods_file_subdir_path}") + endforeach() + # Bundle the translations if(ENABLE_NLS AND TARGET translations AND DEFINED wz2100_translations_LOCALE_FOLDER) if (NOT WZ_LOCALEDIR_ISABSOLUTE) @@ -477,12 +485,20 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin") SOURCE ${_music_file} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/data/music/${_music_file_subdir_path}") endforeach() + foreach(_mod_file ${DATA_MODS_FILES}) + file(RELATIVE_PATH _mods_file_relative_path "${DATA_MODS_BASE_DIR}" "${_mod_file}") + get_filename_component(_mods_file_subdir_path "${_mods_file_relative_path}" DIRECTORY) + set_property( + SOURCE ${_mod_file} + PROPERTY MACOSX_PACKAGE_LOCATION "Resources/data/mods/${_mods_file_subdir_path}") + endforeach() + set_source_files_properties( - ${DATA_FILES} ${DATA_TERRAIN_OVERRIDES_FILES} ${DATA_FONTS} ${DATA_MUSIC_FILES} PROPERTIES + ${DATA_FILES} ${DATA_TERRAIN_OVERRIDES_FILES} ${DATA_FONTS} ${DATA_MUSIC_FILES} ${DATA_MODS_FILES} PROPERTIES GENERATED TRUE XCODE_LAST_KNOWN_FILE_TYPE "file" ) - target_sources(warzone2100 PRIVATE ${DATA_FILES} ${DATA_TERRAIN_OVERRIDES_FILES} ${DATA_FONTS} ${DATA_MUSIC_FILES}) + target_sources(warzone2100 PRIVATE ${DATA_FILES} ${DATA_TERRAIN_OVERRIDES_FILES} ${DATA_FONTS} ${DATA_MUSIC_FILES} ${DATA_MODS_FILES}) # Add the icon set(_macos_app_icon "${CMAKE_CURRENT_SOURCE_DIR}/../macosx/Resources/Warzone.icns")