mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-19 13:03:33 -04:00
Merge remote-tracking branch 'origin/main' into tphd
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||
|
||||
# Android mod linking: symgen scans and filters the game's exports, generating a version script used for the executable
|
||||
# link step, and generates a shared object stub that mods can link against without having to build the whole game.
|
||||
function(setup_android_exports target)
|
||||
include("${_dir}/SymbolManifest.cmake")
|
||||
ensure_symgen(TRUE)
|
||||
add_dependencies(${target} symgen)
|
||||
|
||||
set(_rsp_lines "$<TARGET_OBJECTS:${target}>")
|
||||
foreach (_lib IN LISTS JSYSTEM_LIBRARIES)
|
||||
list(APPEND _rsp_lines "$<TARGET_FILE:${_lib}>")
|
||||
endforeach ()
|
||||
list(JOIN _rsp_lines "\n" _rsp_content)
|
||||
set(_rsp "${CMAKE_BINARY_DIR}/dusklight_exports_input.rsp")
|
||||
file(GENERATE OUTPUT "${_rsp}" CONTENT "${_rsp_content}")
|
||||
|
||||
set(_sdk_args)
|
||||
foreach (_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
|
||||
aurora_os aurora_pad aurora_si aurora_vi)
|
||||
if (TARGET ${_lib})
|
||||
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:${_lib}>")
|
||||
endif ()
|
||||
endforeach ()
|
||||
if (TARGET dawn::webgpu_dawn)
|
||||
get_target_property(_dawn_type dawn::webgpu_dawn TYPE)
|
||||
if (_dawn_type STREQUAL "STATIC_LIBRARY")
|
||||
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:dawn::webgpu_dawn>")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(_vscript "${CMAKE_BINARY_DIR}/dusklight_exports.ver")
|
||||
add_custom_command(TARGET ${target} PRE_LINK
|
||||
COMMAND "${SYMGEN_EXE}" exports
|
||||
"@${_rsp}"
|
||||
--out "${_vscript}"
|
||||
--format version-script
|
||||
--exclude cmake_pch
|
||||
--exclude miniz
|
||||
--exclude asan_options
|
||||
--exclude src/dusk
|
||||
# Resolved from the Java side; the SDL ones live in the statically-linked
|
||||
# SDL archive, outside the provenance scan.
|
||||
--extra-sym JNI_OnLoad
|
||||
--extra-sym SDL_main
|
||||
--extra-sym "Java_*"
|
||||
${_sdk_args}
|
||||
COMMENT "Generating dusklight exports"
|
||||
VERBATIM)
|
||||
target_link_options(${target} PRIVATE "-Wl,--version-script=${_vscript}")
|
||||
|
||||
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _arch)
|
||||
set(_stub "${CMAKE_BINARY_DIR}/stub-android-${_arch}.so")
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND "${SYMGEN_EXE}" stub -f elf "$<TARGET_FILE:${target}>" -o "${_stub}"
|
||||
--soname "$<TARGET_FILE_NAME:${target}>" --arch "${_arch}"
|
||||
BYPRODUCTS "${_stub}"
|
||||
COMMENT "Generating dusklight link stub"
|
||||
VERBATIM)
|
||||
install(FILES "${_stub}" DESTINATION sdk)
|
||||
endfunction()
|
||||
@@ -0,0 +1,90 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||
|
||||
# Apple mod linking: symgen scans and filters the game's exports, generating an exports list used for the executable
|
||||
# link step, and generates a MH_EXECUTE Mach-O stub that mods can link against without having to build the whole game.
|
||||
function(setup_apple_exports target)
|
||||
include("${_dir}/SymbolManifest.cmake")
|
||||
ensure_symgen(TRUE)
|
||||
set(_symgen "${SYMGEN_EXE}")
|
||||
add_dependencies(${target} symgen)
|
||||
|
||||
set(_config_subdir "")
|
||||
if (CMAKE_CONFIGURATION_TYPES)
|
||||
set(_config_subdir "$<CONFIG>/")
|
||||
endif ()
|
||||
|
||||
set(_rsp_lines "$<TARGET_OBJECTS:${target}>")
|
||||
foreach (_lib IN LISTS JSYSTEM_LIBRARIES)
|
||||
list(APPEND _rsp_lines "$<TARGET_FILE:${_lib}>")
|
||||
endforeach ()
|
||||
list(JOIN _rsp_lines "\n" _rsp_content)
|
||||
set(_rsp "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports_input.rsp")
|
||||
file(GENERATE OUTPUT "${_rsp}" CONTENT "${_rsp_content}")
|
||||
|
||||
set(_sdk_args)
|
||||
foreach (_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
|
||||
aurora_os aurora_pad aurora_si aurora_vi)
|
||||
if (TARGET ${_lib})
|
||||
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:${_lib}>")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# Dawn is linked statically on Apple; mods reach wgpu* through the executable.
|
||||
if (TARGET dawn::webgpu_dawn)
|
||||
get_target_property(_dawn_type dawn::webgpu_dawn TYPE)
|
||||
if (_dawn_type STREQUAL "STATIC_LIBRARY")
|
||||
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:dawn::webgpu_dawn>")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(_exp "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports.exp")
|
||||
add_custom_command(TARGET ${target} PRE_LINK
|
||||
COMMAND "${_symgen}" exports
|
||||
"@${_rsp}"
|
||||
--out "${_exp}"
|
||||
--exclude cmake_pch
|
||||
--exclude miniz
|
||||
--exclude asan_options
|
||||
--exclude src/dusk
|
||||
${_sdk_args}
|
||||
COMMENT "Generating dusklight exports"
|
||||
VERBATIM)
|
||||
target_link_options(${target} PRIVATE -Xlinker -exported_symbols_list -Xlinker "${_exp}")
|
||||
|
||||
# Generate the stub executable mods link against via -bundle_loader.
|
||||
set(_stub_args)
|
||||
if (IOS)
|
||||
set(_stub_platform "ios")
|
||||
list(APPEND _stub_args --platform ios)
|
||||
elseif (TVOS)
|
||||
set(_stub_platform "tvos")
|
||||
list(APPEND _stub_args --platform tvos)
|
||||
else ()
|
||||
set(_stub_platform "macos")
|
||||
endif ()
|
||||
if (CMAKE_OSX_DEPLOYMENT_TARGET)
|
||||
list(APPEND _stub_args --min-os "${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||
endif ()
|
||||
if (CMAKE_OSX_ARCHITECTURES)
|
||||
set(_archs "${CMAKE_OSX_ARCHITECTURES}")
|
||||
else ()
|
||||
set(_archs "${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif ()
|
||||
set(_arch_names "")
|
||||
foreach (_arch IN LISTS _archs)
|
||||
string(TOLOWER "${_arch}" _arch)
|
||||
list(APPEND _stub_args --arch "${_arch}")
|
||||
list(APPEND _arch_names "${_arch}")
|
||||
endforeach ()
|
||||
list(JOIN _arch_names "_" _arch_name)
|
||||
|
||||
set(_stub "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight-stub")
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND "${_symgen}" stub -f macho "${_exp}" -o "${_stub}" ${_stub_args}
|
||||
BYPRODUCTS "${_stub}"
|
||||
COMMENT "Generating dusklight link stub"
|
||||
VERBATIM)
|
||||
install(FILES "${_stub}" DESTINATION sdk RENAME "stub-${_stub_platform}-${_arch_name}")
|
||||
endfunction()
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copies a mod asset directory for packaging, skipping dotfiles and dot-directories
|
||||
# (.gitkeep, .DS_Store, ...). Usage: cmake -DSRC=<dir> -DDST=<dir> -P CopyModAssets.cmake
|
||||
file(MAKE_DIRECTORY "${DST}")
|
||||
file(GLOB_RECURSE _files RELATIVE "${SRC}" "${SRC}/*")
|
||||
foreach (_file IN LISTS _files)
|
||||
if (NOT _file MATCHES "(^|/)\\.")
|
||||
get_filename_component(_dir "${_file}" DIRECTORY)
|
||||
file(COPY "${SRC}/${_file}" DESTINATION "${DST}/${_dir}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
@@ -0,0 +1,68 @@
|
||||
# The game ABI surface shared by the main build and the mod SDK (sdk/CMakeLists.txt)
|
||||
include_guard(GLOBAL)
|
||||
|
||||
get_filename_component(_game_root "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
|
||||
|
||||
# PARTIAL_DEBUG makes debug and release share one struct/vtable ABI so a mod binary loads into either
|
||||
set(_game_compile_defs TARGET_PC=1 WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1 PARTIAL_DEBUG=1 DUSK_TPHD=1)
|
||||
if (ANDROID)
|
||||
list(APPEND _game_compile_defs TARGET_ANDROID=1)
|
||||
endif ()
|
||||
|
||||
# Public game headers
|
||||
set(_game_abi_include_dirs
|
||||
${_game_root}/include
|
||||
${_game_root}/assets/GZ2E01
|
||||
${_game_root}/libs/JSystem/include
|
||||
${_game_root}/extern/aurora/include/dolphin
|
||||
${_game_root}/extern/aurora/include
|
||||
${_game_root}/sdk/include
|
||||
)
|
||||
|
||||
# Internal game headers
|
||||
set(_game_include_dirs
|
||||
${_game_abi_include_dirs}
|
||||
${_game_root}/src
|
||||
${_game_root}/extern
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Mod API, including services
|
||||
add_library(dusklight_mod_api INTERFACE)
|
||||
target_include_directories(dusklight_mod_api INTERFACE ${_game_root}/sdk/include)
|
||||
|
||||
# Full internal headers used to build the game
|
||||
add_library(dusklight_game_headers INTERFACE)
|
||||
target_include_directories(dusklight_game_headers INTERFACE ${_game_include_dirs})
|
||||
target_compile_definitions(dusklight_game_headers INTERFACE ${_game_compile_defs})
|
||||
|
||||
# Public game ABI for mods
|
||||
add_library(dusklight_game_abi_headers INTERFACE)
|
||||
target_include_directories(dusklight_game_abi_headers INTERFACE ${_game_abi_include_dirs})
|
||||
target_compile_definitions(dusklight_game_abi_headers INTERFACE ${_game_compile_defs})
|
||||
|
||||
# Mod feature targets
|
||||
add_library(dusklight_mod_feature_game INTERFACE)
|
||||
target_link_libraries(dusklight_mod_feature_game INTERFACE
|
||||
dusklight_mod_api
|
||||
dusklight_game_abi_headers)
|
||||
target_compile_definitions(dusklight_mod_feature_game INTERFACE DUSK_MOD_FEATURE_GAME=1)
|
||||
# Game headers assume global.h comes first in the translation unit (it defines DUSK_GAME_DATA
|
||||
# and friends); force-include it so mods don't depend on include order.
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
target_compile_options(dusklight_mod_feature_game INTERFACE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:/FIglobal.h>")
|
||||
else ()
|
||||
target_compile_options(dusklight_mod_feature_game INTERFACE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include global.h>")
|
||||
endif ()
|
||||
target_sources(dusklight_mod_feature_game INTERFACE
|
||||
${_game_root}/sdk/src/game_feature.cpp)
|
||||
|
||||
add_library(dusklight_mod_feature_webgpu INTERFACE)
|
||||
target_link_libraries(dusklight_mod_feature_webgpu INTERFACE dusklight_mod_api)
|
||||
target_compile_definitions(dusklight_mod_feature_webgpu INTERFACE DUSK_MOD_FEATURE_WEBGPU=1)
|
||||
|
||||
add_library(dusklight_mod_feature_fmt INTERFACE)
|
||||
target_link_libraries(dusklight_mod_feature_fmt INTERFACE dusklight_mod_api)
|
||||
target_compile_definitions(dusklight_mod_feature_fmt INTERFACE DUSK_MOD_FEATURE_FMT=1)
|
||||
@@ -0,0 +1,518 @@
|
||||
# add_mod(<target> [FEATURES <feature>...] SOURCES <file>... MOD_JSON <mod.json>
|
||||
# [RUNTIME_LIBRARIES <file>...] [RES_DIR <res>] [OVERLAY_DIR <overlay>]
|
||||
# [TEXTURES_DIR <textures>] [OUTPUT_DIR <dir>] [BUNDLE])
|
||||
set(DUSK_MODS_OUTPUT_DIR "${CMAKE_BINARY_DIR}/mods" CACHE PATH "Directory to write mod packages into")
|
||||
set(DUSKLIGHT_SDK_STUB_URL "https://github.com/encounter/dusklight/releases/download/sdk"
|
||||
CACHE STRING "Base URL for game link stubs downloaded by out-of-tree mod builds")
|
||||
|
||||
function(_mod_lib_info out_platform_var out_name_var)
|
||||
set(_arch "${CMAKE_SYSTEM_PROCESSOR}")
|
||||
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
list(LENGTH CMAKE_OSX_ARCHITECTURES _count)
|
||||
if (_count GREATER 1)
|
||||
message(FATAL_ERROR "add_mod: universal binaries are not supported")
|
||||
endif ()
|
||||
set(_arch "${CMAKE_OSX_ARCHITECTURES}")
|
||||
elseif (WIN32)
|
||||
set(_arch_id "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}")
|
||||
if (NOT _arch_id)
|
||||
set(_arch_id "${CMAKE_C_COMPILER_ARCHITECTURE_ID}")
|
||||
endif ()
|
||||
if (_arch_id MATCHES "^ARM64(EC)?$")
|
||||
set(_arch "arm64")
|
||||
elseif (_arch_id STREQUAL "x64")
|
||||
set(_arch "amd64")
|
||||
elseif (_arch_id STREQUAL "X86")
|
||||
set(_arch "x86")
|
||||
endif ()
|
||||
endif ()
|
||||
string(TOLOWER "${CMAKE_SYSTEM_NAME}" _platform)
|
||||
if (_platform STREQUAL "darwin")
|
||||
set(_platform "macos")
|
||||
endif ()
|
||||
string(TOLOWER "${_arch}" _arch)
|
||||
if (_arch MATCHES "^(i[3-6]86|x86)$")
|
||||
set(_arch "x86")
|
||||
endif ()
|
||||
if (WIN32)
|
||||
set(_ext ".dll")
|
||||
else ()
|
||||
set(_ext ".so")
|
||||
endif ()
|
||||
set(${out_platform_var} "${_platform}-${_arch}" PARENT_SCOPE)
|
||||
set(${out_name_var} "mod${_ext}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# For out-of-tree builds without a game binary: download the version-independent link stub
|
||||
# (generated by symgen) for the target platform.
|
||||
function(_mod_download_link_stub out_var)
|
||||
_mod_lib_info(_platform _lib_name)
|
||||
if (WIN32)
|
||||
set(_asset "${_platform}.lib")
|
||||
elseif (ANDROID)
|
||||
set(_asset "stub-${_platform}.so")
|
||||
else ()
|
||||
set(_asset "stub-${_platform}")
|
||||
endif ()
|
||||
set(_stub "${CMAKE_BINARY_DIR}/dusklight-sdk-stubs/${_asset}")
|
||||
if (NOT EXISTS "${_stub}")
|
||||
message(STATUS "Mod SDK: downloading link stub ${_asset}")
|
||||
file(DOWNLOAD "${DUSKLIGHT_SDK_STUB_URL}/${_asset}" "${_stub}.tmp" STATUS _status)
|
||||
list(GET _status 0 _code)
|
||||
if (NOT _code EQUAL 0)
|
||||
list(GET _status 1 _error)
|
||||
file(REMOVE "${_stub}.tmp")
|
||||
message(FATAL_ERROR
|
||||
"Mod SDK: failed to download ${DUSKLIGHT_SDK_STUB_URL}/${_asset}: ${_error}\n"
|
||||
"Set DUSK_GAME_EXE to a game binary or link stub to skip the download.")
|
||||
endif ()
|
||||
file(RENAME "${_stub}.tmp" "${_stub}")
|
||||
endif ()
|
||||
set(${out_var} "${_stub}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_mod_resolve_source_path out_var path)
|
||||
if (IS_ABSOLUTE "${path}")
|
||||
set(_path "${path}")
|
||||
else ()
|
||||
set(_path "${CMAKE_CURRENT_SOURCE_DIR}/${path}")
|
||||
endif ()
|
||||
set(${out_var} "${_path}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_mod_collect_assets out_var dir)
|
||||
if (NOT IS_DIRECTORY "${dir}")
|
||||
message(FATAL_ERROR "add_mod: asset directory does not exist: ${dir}")
|
||||
endif ()
|
||||
|
||||
file(GLOB_RECURSE _files CONFIGURE_DEPENDS LIST_DIRECTORIES false "${dir}/*")
|
||||
# Dotfiles (.gitkeep, .DS_Store, ...) are not packaged; see CopyModAssets.cmake.
|
||||
set(_assets "")
|
||||
foreach (_file IN LISTS _files)
|
||||
file(RELATIVE_PATH _rel "${dir}" "${_file}")
|
||||
if (NOT _rel MATCHES "(^|/)\\.")
|
||||
list(APPEND _assets "${_file}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
set(${out_var} ${_assets} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_mod_add_webgpu_headers target_name)
|
||||
if (NOT TARGET dawn::dawncpp_headers AND NOT TARGET dawn::webgpu_dawn)
|
||||
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../extern/aurora/cmake/AuroraDependencyVersions.cmake")
|
||||
set(AURORA_DAWN_PROVIDER "package" CACHE STRING "How to provide Dawn for the mod SDK")
|
||||
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../extern/aurora/cmake/AuroraDawnProvider.cmake")
|
||||
endif ()
|
||||
|
||||
if (TARGET dawn::dawncpp_headers)
|
||||
target_link_libraries(${target_name} PRIVATE dawn::dawncpp_headers)
|
||||
elseif (TARGET dawn::webgpu_dawn)
|
||||
target_link_libraries(${target_name} PRIVATE dawn::webgpu_dawn)
|
||||
else ()
|
||||
message(FATAL_ERROR "add_mod: FEATURES webgpu could not provide WebGPU headers")
|
||||
endif ()
|
||||
endfunction()
|
||||
|
||||
function(_mod_add_fmt target_name)
|
||||
if (NOT TARGET fmt::fmt-header-only)
|
||||
find_package(fmt 11 CONFIG QUIET GLOBAL)
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET fmt::fmt-header-only)
|
||||
include(FetchContent)
|
||||
message(STATUS "Mod SDK: fetching fmt")
|
||||
# Keep the fallback version in sync with extern/aurora/extern/CMakeLists.txt.
|
||||
FetchContent_Declare(fmt
|
||||
URL https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz
|
||||
URL_HASH SHA256=ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP FALSE
|
||||
EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(fmt)
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET fmt::fmt-header-only)
|
||||
message(FATAL_ERROR "add_mod: FEATURES fmt could not provide fmt::fmt-header-only")
|
||||
endif ()
|
||||
target_link_libraries(${target_name} PRIVATE fmt::fmt-header-only)
|
||||
endfunction()
|
||||
|
||||
function(add_mod target_name)
|
||||
cmake_parse_arguments(ARG "BUNDLE" "MOD_JSON;RES_DIR;OVERLAY_DIR;TEXTURES_DIR;OUTPUT_DIR"
|
||||
"SOURCES;RUNTIME_LIBRARIES;FEATURES" ${ARGN})
|
||||
if (ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "add_mod: unknown arguments: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif ()
|
||||
if (NOT ARG_MOD_JSON)
|
||||
message(FATAL_ERROR "add_mod: MOD_JSON is required")
|
||||
endif ()
|
||||
_mod_resolve_source_path(_mod_json "${ARG_MOD_JSON}")
|
||||
if (NOT EXISTS "${_mod_json}")
|
||||
message(FATAL_ERROR "add_mod: MOD_JSON does not exist: ${_mod_json}")
|
||||
endif ()
|
||||
|
||||
set(_supported_features fmt game webgpu)
|
||||
set(_features "")
|
||||
foreach (_feature IN LISTS ARG_FEATURES)
|
||||
list(FIND _supported_features "${_feature}" _feature_index)
|
||||
if (_feature_index EQUAL -1)
|
||||
list(JOIN _supported_features ", " _supported_features_text)
|
||||
message(FATAL_ERROR
|
||||
"add_mod: unknown feature '${_feature}' (supported: ${_supported_features_text})")
|
||||
endif ()
|
||||
list(FIND _features "${_feature}" _duplicate_index)
|
||||
if (NOT _duplicate_index EQUAL -1)
|
||||
message(FATAL_ERROR "add_mod: duplicate feature '${_feature}'")
|
||||
endif ()
|
||||
list(APPEND _features "${_feature}")
|
||||
endforeach ()
|
||||
if (_features AND NOT ARG_SOURCES)
|
||||
message(FATAL_ERROR "add_mod: FEATURES requires SOURCES")
|
||||
endif ()
|
||||
|
||||
set(_has_lib FALSE)
|
||||
set(_needs_host_link FALSE)
|
||||
set(_lib_platform "")
|
||||
set(_lib_name "")
|
||||
if (ARG_SOURCES)
|
||||
set(_has_lib TRUE)
|
||||
add_library(${target_name} MODULE ${ARG_SOURCES})
|
||||
_mod_lib_info(_lib_platform _lib_name)
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
PREFIX ""
|
||||
C_VISIBILITY_PRESET hidden
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON
|
||||
WINDOWS_EXPORT_ALL_SYMBOLS OFF)
|
||||
target_compile_features(${target_name} PRIVATE cxx_std_20)
|
||||
target_link_libraries(${target_name} PRIVATE dusklight_mod_api)
|
||||
foreach (_feature IN LISTS _features)
|
||||
target_link_libraries(${target_name} PRIVATE dusklight_mod_feature_${_feature})
|
||||
if (_feature STREQUAL "webgpu")
|
||||
_mod_add_webgpu_headers(${target_name})
|
||||
endif ()
|
||||
if (_feature STREQUAL "fmt")
|
||||
_mod_add_fmt(${target_name})
|
||||
endif ()
|
||||
if (_feature STREQUAL "game" OR _feature STREQUAL "webgpu")
|
||||
set(_needs_host_link TRUE)
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
if (NOT TARGET dusklight)
|
||||
# Apply global compile options for out-of-tree mod builds
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
-Wno-multichar -Wno-trigraphs -Wno-deprecated-declarations)
|
||||
elseif (APPLE)
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
-Wno-declaration-after-statement -Wno-non-pod-varargs)
|
||||
elseif (MSVC)
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:C,CXX>:/bigobj>"
|
||||
"$<$<COMPILE_LANGUAGE:C,CXX>:/utf-8>")
|
||||
endif ()
|
||||
# Use signed char on ARM to match the original game (and x86)
|
||||
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _mod_arch)
|
||||
if (_mod_arch MATCHES "^(arm|aarch64)" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
|
||||
target_compile_options(${target_name} PRIVATE -fsigned-char)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
if (_needs_host_link)
|
||||
if (TARGET dusklight)
|
||||
set(_game_exe "$<TARGET_FILE:dusklight>")
|
||||
add_dependencies(${target_name} dusklight)
|
||||
elseif (DUSK_GAME_EXE)
|
||||
_mod_resolve_source_path(_game_exe "${DUSK_GAME_EXE}")
|
||||
else ()
|
||||
_mod_download_link_stub(_game_exe)
|
||||
endif ()
|
||||
target_link_options(${target_name} PRIVATE
|
||||
-Xlinker -bundle_loader -Xlinker "${_game_exe}")
|
||||
set_property(TARGET ${target_name} APPEND PROPERTY LINK_DEPENDS "${_game_exe}")
|
||||
endif ()
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
BUILD_RPATH "@loader_path"
|
||||
INSTALL_RPATH "@loader_path")
|
||||
elseif (ANDROID)
|
||||
if (_needs_host_link)
|
||||
if (TARGET dusklight)
|
||||
target_link_libraries(${target_name} PRIVATE dusklight)
|
||||
else ()
|
||||
if (DUSK_GAME_EXE)
|
||||
_mod_resolve_source_path(_game_lib "${DUSK_GAME_EXE}")
|
||||
else ()
|
||||
_mod_download_link_stub(_game_lib)
|
||||
endif ()
|
||||
target_link_libraries(${target_name} PRIVATE "${_game_lib}")
|
||||
endif ()
|
||||
endif ()
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
BUILD_RPATH "$ORIGIN"
|
||||
INSTALL_RPATH "$ORIGIN")
|
||||
elseif (UNIX)
|
||||
if (_needs_host_link)
|
||||
target_link_options(${target_name} PRIVATE -Wl,--allow-shlib-undefined)
|
||||
endif ()
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
BUILD_RPATH "$ORIGIN"
|
||||
INSTALL_RPATH "$ORIGIN")
|
||||
elseif (WIN32)
|
||||
# Mods link against the game's import library (sdk/windows-<arch>.lib, generated by
|
||||
# setup_windows_exports); cl and clang-cl both consume it in MSVC mode. Function
|
||||
# calls resolve through import thunks; game data is reachable only through
|
||||
# __declspec(dllimport), i.e. DUSK_GAME_DATA-annotated declarations.
|
||||
if (_needs_host_link)
|
||||
if (TARGET dusklight)
|
||||
if (NOT DUSK_GAME_IMPLIB)
|
||||
message(FATAL_ERROR
|
||||
"add_mod: DUSK_GAME_IMPLIB is not set (see setup_windows_exports)")
|
||||
endif ()
|
||||
set(_game_lib "${DUSK_GAME_IMPLIB}")
|
||||
elseif (DUSK_GAME_EXE)
|
||||
_mod_resolve_source_path(_game_lib "${DUSK_GAME_EXE}")
|
||||
if (NOT _game_lib MATCHES "\\.lib$")
|
||||
message(FATAL_ERROR
|
||||
"add_mod: DUSK_GAME_EXE must be an import library on Windows "
|
||||
"(sdk/windows-<arch>.lib)")
|
||||
endif ()
|
||||
else ()
|
||||
_mod_download_link_stub(_game_lib)
|
||||
endif ()
|
||||
target_link_libraries(${target_name} PRIVATE "${_game_lib}")
|
||||
endif ()
|
||||
set_target_properties(${target_name} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
|
||||
target_compile_definitions(${target_name} PRIVATE _ITERATOR_DEBUG_LEVEL=0)
|
||||
endif ()
|
||||
endif ()
|
||||
if (ARG_RUNTIME_LIBRARIES AND NOT _has_lib)
|
||||
message(FATAL_ERROR "add_mod: RUNTIME_LIBRARIES requires SOURCES")
|
||||
endif ()
|
||||
|
||||
set(_output_dir "${DUSK_MODS_OUTPUT_DIR}")
|
||||
if (ARG_OUTPUT_DIR)
|
||||
set(_output_dir "${ARG_OUTPUT_DIR}")
|
||||
endif ()
|
||||
set(_stage "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_stage")
|
||||
set(_out "${_output_dir}/${target_name}.dusk")
|
||||
|
||||
set(_zip_args mod.json)
|
||||
set(_package_deps "${_mod_json}")
|
||||
set(_package_inputs "${_mod_json}")
|
||||
set(_extra_cmds "")
|
||||
set(_lib_copy_cmd "")
|
||||
set(_target_depend "")
|
||||
if (_has_lib)
|
||||
list(APPEND _zip_args lib)
|
||||
set(_lib_copy_cmd
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${_stage}/lib/${_lib_platform}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$<TARGET_FILE:${target_name}>" "${_stage}/lib/${_lib_platform}/${_lib_name}")
|
||||
set(_target_depend ${target_name})
|
||||
endif ()
|
||||
string(TOLOWER "${_lib_name}" _lib_name_key)
|
||||
set(_runtime_lib_name_keys "${_lib_name_key}")
|
||||
foreach (_runtime_lib IN LISTS ARG_RUNTIME_LIBRARIES)
|
||||
_mod_resolve_source_path(_runtime_lib_path "${_runtime_lib}")
|
||||
if (NOT EXISTS "${_runtime_lib_path}" OR IS_DIRECTORY "${_runtime_lib_path}")
|
||||
message(FATAL_ERROR "add_mod: runtime library does not exist or is not a file: ${_runtime_lib_path}")
|
||||
endif ()
|
||||
get_filename_component(_runtime_lib_name "${_runtime_lib_path}" NAME)
|
||||
string(TOLOWER "${_runtime_lib_name}" _runtime_lib_name_key)
|
||||
list(FIND _runtime_lib_name_keys "${_runtime_lib_name_key}" _runtime_lib_name_index)
|
||||
if (NOT _runtime_lib_name_index EQUAL -1)
|
||||
message(FATAL_ERROR "add_mod: duplicate runtime library filename: ${_runtime_lib_name}")
|
||||
endif ()
|
||||
list(APPEND _runtime_lib_name_keys "${_runtime_lib_name_key}")
|
||||
list(APPEND _package_deps "${_runtime_lib_path}")
|
||||
list(APPEND _package_inputs "${_runtime_lib_path}")
|
||||
list(APPEND _lib_copy_cmd COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${_runtime_lib_path}" "${_stage}/lib/${_lib_platform}/${_runtime_lib_name}")
|
||||
endforeach ()
|
||||
if (ARG_RES_DIR)
|
||||
_mod_resolve_source_path(_res_dir "${ARG_RES_DIR}")
|
||||
_mod_collect_assets(_res_deps "${_res_dir}")
|
||||
list(APPEND _package_deps ${_res_deps})
|
||||
list(APPEND _package_inputs "${_res_dir}" ${_res_deps})
|
||||
list(APPEND _zip_args res)
|
||||
list(APPEND _extra_cmds COMMAND ${CMAKE_COMMAND} "-DSRC=${_res_dir}"
|
||||
"-DDST=${_stage}/res" -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CopyModAssets.cmake")
|
||||
endif ()
|
||||
if (ARG_OVERLAY_DIR)
|
||||
_mod_resolve_source_path(_overlay_dir "${ARG_OVERLAY_DIR}")
|
||||
_mod_collect_assets(_overlay_deps "${_overlay_dir}")
|
||||
list(APPEND _package_deps ${_overlay_deps})
|
||||
list(APPEND _package_inputs "${_overlay_dir}" ${_overlay_deps})
|
||||
list(APPEND _zip_args overlay)
|
||||
list(APPEND _extra_cmds COMMAND ${CMAKE_COMMAND} "-DSRC=${_overlay_dir}"
|
||||
"-DDST=${_stage}/overlay" -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CopyModAssets.cmake")
|
||||
endif ()
|
||||
if (ARG_TEXTURES_DIR)
|
||||
_mod_resolve_source_path(_textures_dir "${ARG_TEXTURES_DIR}")
|
||||
_mod_collect_assets(_textures_deps "${_textures_dir}")
|
||||
list(APPEND _package_deps ${_textures_deps})
|
||||
list(APPEND _package_inputs "${_textures_dir}" ${_textures_deps})
|
||||
list(APPEND _zip_args textures)
|
||||
list(APPEND _extra_cmds COMMAND ${CMAKE_COMMAND} "-DSRC=${_textures_dir}"
|
||||
"-DDST=${_stage}/textures" -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CopyModAssets.cmake")
|
||||
endif ()
|
||||
|
||||
set(_bundle_cmds "")
|
||||
if (ARG_BUNDLE AND TARGET dusklight)
|
||||
file(READ "${_mod_json}" _mod_json_text)
|
||||
string(JSON _mod_id GET "${_mod_json_text}" id)
|
||||
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_TARGETS "${target_name}")
|
||||
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_IDS "${_mod_id}")
|
||||
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_STAGES "${_stage}")
|
||||
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_PACKAGES "${_out}")
|
||||
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_LIB_PLATFORMS "${_lib_platform}")
|
||||
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_LIB_NAMES "${_lib_name}")
|
||||
set(_bundle_cmds
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundled_mods"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_out}" "${CMAKE_BINARY_DIR}/bundled_mods/${target_name}.dusk")
|
||||
endif ()
|
||||
|
||||
set(_package_target "${target_name}_package")
|
||||
set(_package_inputs_file "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_package_inputs.txt")
|
||||
list(SORT _package_inputs)
|
||||
set(_package_inputs_text "")
|
||||
foreach (_package_input IN LISTS _package_inputs)
|
||||
string(APPEND _package_inputs_text "${_package_input}\n")
|
||||
endforeach ()
|
||||
file(GENERATE OUTPUT "${_package_inputs_file}" CONTENT "${_package_inputs_text}")
|
||||
add_custom_command(OUTPUT "${_out}"
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -rf "${_stage}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${_stage}" "${_output_dir}"
|
||||
${_lib_copy_cmd}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_mod_json}" "${_stage}/mod.json"
|
||||
${_extra_cmds}
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir "${_stage}" ${CMAKE_COMMAND} -E tar cvf "${_out}" --format=zip ${_zip_args}
|
||||
${_bundle_cmds}
|
||||
DEPENDS ${_target_depend} ${_package_deps} "${_package_inputs_file}"
|
||||
COMMENT "Packaging ${target_name} -> ${_out}"
|
||||
COMMAND_EXPAND_LISTS
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(${_package_target} ALL DEPENDS "${_out}")
|
||||
if (TARGET dusklight_mods)
|
||||
add_dependencies(dusklight_mods ${_package_target})
|
||||
endif ()
|
||||
endfunction()
|
||||
|
||||
# Install rules for BUNDLE mods.
|
||||
# - Windows: the .dusk archives into <install>/mods (the loader extracts native libs to the
|
||||
# user cache).
|
||||
# - Linux: pre-extracted stage dirs into <install>/mods so native libs dlopen in place from
|
||||
# read-only installs.
|
||||
# - macOS: pre-extracted stage dirs into the installed app's Contents/Resources/mods, native
|
||||
# libs ad-hoc signed in place, then the whole bundle re-signed.
|
||||
# - iOS/tvOS: assets into <app>/mods/<id>, the mod library into Frameworks/<id>.so,
|
||||
# and runtime libraries alongside it.
|
||||
# - Android: nothing here; gradle packs ${CMAKE_BINARY_DIR}/bundled_mods into APK assets.
|
||||
function(install_bundled_mods)
|
||||
get_property(_targets GLOBAL PROPERTY DUSK_BUNDLED_MOD_TARGETS)
|
||||
if (NOT _targets OR ANDROID)
|
||||
return ()
|
||||
endif ()
|
||||
get_property(_ids GLOBAL PROPERTY DUSK_BUNDLED_MOD_IDS)
|
||||
get_property(_stages GLOBAL PROPERTY DUSK_BUNDLED_MOD_STAGES)
|
||||
get_property(_lib_platforms GLOBAL PROPERTY DUSK_BUNDLED_MOD_LIB_PLATFORMS)
|
||||
get_property(_lib_names GLOBAL PROPERTY DUSK_BUNDLED_MOD_LIB_NAMES)
|
||||
list(LENGTH _targets _count)
|
||||
math(EXPR _last "${_count} - 1")
|
||||
|
||||
if (APPLE)
|
||||
get_target_property(_app_name dusklight OUTPUT_NAME)
|
||||
if (NOT _app_name)
|
||||
set(_app_name dusklight)
|
||||
endif ()
|
||||
set(_bundle_dir "${CMAKE_INSTALL_PREFIX}/${_app_name}.app")
|
||||
if (IOS OR TVOS)
|
||||
foreach (_i RANGE ${_last})
|
||||
list(GET _targets ${_i} _target)
|
||||
list(GET _ids ${_i} _id)
|
||||
list(GET _stages ${_i} _stage)
|
||||
list(GET _lib_platforms ${_i} _lib_platform)
|
||||
list(GET _lib_names ${_i} _lib_name)
|
||||
install(DIRECTORY "${_stage}/" DESTINATION "${_bundle_dir}/mods/${_id}"
|
||||
PATTERN "lib" EXCLUDE)
|
||||
install(PROGRAMS "$<TARGET_FILE:${_target}>"
|
||||
DESTINATION "${_bundle_dir}/Frameworks" RENAME "${_id}.so")
|
||||
install(DIRECTORY "${_stage}/lib/${_lib_platform}/"
|
||||
DESTINATION "${_bundle_dir}/Frameworks"
|
||||
PATTERN "${_lib_name}" EXCLUDE)
|
||||
endforeach ()
|
||||
if (DUSK_HAS_PREPATCH)
|
||||
set(_prepatch_executable "${_bundle_dir}/Dusklight")
|
||||
set(_prepatch_mod_args "")
|
||||
foreach (_id IN LISTS _ids)
|
||||
string(APPEND _prepatch_mod_args
|
||||
" \"${_bundle_dir}/Frameworks/${_id}.so\"")
|
||||
endforeach ()
|
||||
install(CODE "
|
||||
execute_process(
|
||||
COMMAND \"${SYMGEN_EXE}\" prepatch
|
||||
--binary \"${_prepatch_executable}\"
|
||||
--report \"${CMAKE_BINARY_DIR}/prepatch-report-$<CONFIG>.json\"
|
||||
${_prepatch_mod_args}
|
||||
COMMAND_ERROR_IS_FATAL ANY)")
|
||||
endif ()
|
||||
else ()
|
||||
foreach (_i RANGE ${_last})
|
||||
list(GET _ids ${_i} _id)
|
||||
list(GET _stages ${_i} _stage)
|
||||
list(GET _lib_platforms ${_i} _lib_platform)
|
||||
list(GET _lib_names ${_i} _lib_name)
|
||||
install(DIRECTORY "${_stage}/" DESTINATION "${_bundle_dir}/Contents/Resources/mods/${_id}")
|
||||
install(CODE "
|
||||
file(GLOB _mod_libs \"${_bundle_dir}/Contents/Resources/mods/${_id}/lib/${_lib_platform}/*\")
|
||||
foreach (_mod_lib IN LISTS _mod_libs)
|
||||
if (NOT IS_DIRECTORY \"\${_mod_lib}\")
|
||||
execute_process(COMMAND /usr/bin/codesign --force --sign - \"\${_mod_lib}\" COMMAND_ERROR_IS_FATAL ANY)
|
||||
endif ()
|
||||
endforeach ()")
|
||||
endforeach ()
|
||||
if (DUSK_HAS_PREPATCH)
|
||||
set(_prepatch_executable "${_bundle_dir}/Contents/MacOS/Dusklight")
|
||||
set(_prepatch_mod_args "")
|
||||
foreach (_i RANGE ${_last})
|
||||
list(GET _ids ${_i} _id)
|
||||
list(GET _lib_platforms ${_i} _lib_platform)
|
||||
list(GET _lib_names ${_i} _lib_name)
|
||||
string(APPEND _prepatch_mod_args
|
||||
" \"${_bundle_dir}/Contents/Resources/mods/${_id}/lib/${_lib_platform}/${_lib_name}\"")
|
||||
endforeach ()
|
||||
install(CODE "
|
||||
execute_process(
|
||||
COMMAND \"${SYMGEN_EXE}\" prepatch
|
||||
--binary \"${_prepatch_executable}\"
|
||||
--report \"${CMAKE_BINARY_DIR}/prepatch-report-$<CONFIG>.json\"
|
||||
${_prepatch_mod_args}
|
||||
COMMAND_ERROR_IS_FATAL ANY)")
|
||||
endif ()
|
||||
if (TARGET crashpad_handler)
|
||||
install(CODE "execute_process(COMMAND /usr/bin/codesign --force --sign - \"${_bundle_dir}/Contents/MacOS/$<TARGET_FILE_NAME:crashpad_handler>\" COMMAND_ERROR_IS_FATAL ANY)")
|
||||
endif ()
|
||||
install(CODE "execute_process(COMMAND /usr/bin/codesign --force --sign - --entitlements \"${DUSK_ENTITLEMENTS}\" \"${_bundle_dir}\" COMMAND_ERROR_IS_FATAL ANY)")
|
||||
endif ()
|
||||
return ()
|
||||
endif ()
|
||||
|
||||
if (DUSK_PACKAGE_INSTALL)
|
||||
set(_mods_dest "${CMAKE_INSTALL_DATAROOTDIR}/dusklight/mods")
|
||||
else ()
|
||||
set(_mods_dest "${CMAKE_INSTALL_PREFIX}/mods")
|
||||
endif ()
|
||||
if (WIN32)
|
||||
foreach (_target IN LISTS _targets)
|
||||
install(FILES "${CMAKE_BINARY_DIR}/bundled_mods/${_target}.dusk" DESTINATION "${_mods_dest}")
|
||||
endforeach ()
|
||||
else ()
|
||||
foreach (_i RANGE ${_last})
|
||||
list(GET _ids ${_i} _id)
|
||||
list(GET _stages ${_i} _stage)
|
||||
install(DIRECTORY "${_stage}/" DESTINATION "${_mods_dest}/${_id}")
|
||||
endforeach ()
|
||||
endif ()
|
||||
endfunction()
|
||||
@@ -0,0 +1,13 @@
|
||||
# Patches capstone's CMakeLists.txt for compatibility with CMake >= 4.0:
|
||||
# - Bumps cmake_minimum_required to 3.10 (CMake >= 4.0 dropped < 3.5 support; < 3.10 warns)
|
||||
# - Removes cmake_policy(SET CMP0048 OLD) (rejected by CMake >= 3.27)
|
||||
file(READ "${DIR}/CMakeLists.txt" _content)
|
||||
string(REGEX REPLACE
|
||||
"cmake_minimum_required[ \t]*\\([ \t]*VERSION[ \t]+[0-9]+\\.[0-9]+(\\.[0-9]+)?[ \t]*\\)"
|
||||
"cmake_minimum_required(VERSION 3.10)"
|
||||
_content "${_content}")
|
||||
string(REGEX REPLACE
|
||||
"cmake_policy[ \t]*\\([ \t]*SET[ \t]+CMP0048[ \t]+OLD[ \t]*\\)"
|
||||
"# cmake_policy(SET CMP0048 OLD)"
|
||||
_content "${_content}")
|
||||
file(WRITE "${DIR}/CMakeLists.txt" "${_content}")
|
||||
@@ -0,0 +1,60 @@
|
||||
file(READ "${SOURCE_DIR}/cmake/capstone.cmake.in" _content)
|
||||
|
||||
# Insert PATCH_COMMAND before CONFIGURE_COMMAND in the ExternalProject_Add.
|
||||
# Bracket args prevent cmake from substituting ${...} while writing this file.
|
||||
string(REPLACE
|
||||
" CONFIGURE_COMMAND \"\""
|
||||
[=[ PATCH_COMMAND "${CMAKE_COMMAND}" -DDIR=${CMAKE_CURRENT_BINARY_DIR}/capstone-src -P "${CAPSTONE_FIX_SCRIPT}"
|
||||
CONFIGURE_COMMAND ""]=]
|
||||
_content "${_content}")
|
||||
|
||||
file(WRITE "${SOURCE_DIR}/cmake/capstone.cmake.in" "${_content}")
|
||||
|
||||
file(READ "${SOURCE_DIR}/src/funchook_unix.c" _unix_content)
|
||||
|
||||
# macOS rejects the POSIX mprotect RWX/RW transition for executable image pages on arm64.
|
||||
# Use Mach VM_PROT_COPY for the short patch window, then restore RX permissions.
|
||||
if (NOT _unix_content MATCHES "VM_PROT_READ \\| VM_PROT_WRITE \\| VM_PROT_COPY")
|
||||
string(REPLACE
|
||||
[=[ rv = mprotect(mstate->addr, mstate->size, prot);]=]
|
||||
[=[#ifdef __APPLE__
|
||||
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)mstate->addr,
|
||||
(vm_size_t)mstate->size, FALSE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
|
||||
if (kr == KERN_SUCCESS) {
|
||||
funchook_log(funchook, " unprotect memory %p (size=%"PRIuPTR", prot=read,write,copy) <- %p (size=%"PRIuPTR")\n",
|
||||
mstate->addr, mstate->size, start, len);
|
||||
return 0;
|
||||
}
|
||||
funchook_set_error_message(funchook, "Failed to unprotect memory %p (size=%"PRIuPTR", prot=read,write,copy) <- %p (size=%"PRIuPTR", error=%s)",
|
||||
mstate->addr, mstate->size, start, len,
|
||||
mach_error_string(kr));
|
||||
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
|
||||
#endif
|
||||
rv = mprotect(mstate->addr, mstate->size, prot);]=]
|
||||
_unix_content "${_unix_content}")
|
||||
|
||||
string(REPLACE
|
||||
[=[ char errbuf[128];
|
||||
int rv = mprotect(mstate->addr, mstate->size, PROT_READ | PROT_EXEC);]=]
|
||||
[=[ char errbuf[128];
|
||||
#ifdef __APPLE__
|
||||
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)mstate->addr,
|
||||
(vm_size_t)mstate->size, FALSE,
|
||||
VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
|
||||
if (kr == KERN_SUCCESS) {
|
||||
funchook_log(funchook, " protect memory %p (size=%"PRIuPTR", prot=read,exec)\n",
|
||||
mstate->addr, mstate->size);
|
||||
return 0;
|
||||
}
|
||||
funchook_set_error_message(funchook, "Failed to protect memory %p (size=%"PRIuPTR", prot=read,exec, error=%s)",
|
||||
mstate->addr, mstate->size,
|
||||
mach_error_string(kr));
|
||||
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
|
||||
#endif
|
||||
int rv = mprotect(mstate->addr, mstate->size, PROT_READ | PROT_EXEC);]=]
|
||||
_unix_content "${_unix_content}")
|
||||
endif ()
|
||||
|
||||
file(WRITE "${SOURCE_DIR}/src/funchook_unix.c" "${_unix_content}")
|
||||
@@ -0,0 +1,153 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
get_filename_component(_SYMBOL_MANIFEST_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||
|
||||
set(_SYMGEN_VERSION "1.3.2")
|
||||
set(_SYMGEN_RELEASE_BASE_URL "https://github.com/encounter/symgen/releases/download/v${_SYMGEN_VERSION}")
|
||||
set(SYMGEN_PATH "" CACHE FILEPATH "Path to a symgen executable; empty downloads the pinned release")
|
||||
mark_as_advanced(SYMGEN_PATH)
|
||||
|
||||
function(symgen_host_asset out_name out_hash)
|
||||
string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" _host_processor)
|
||||
set(_asset "")
|
||||
set(_asset_hash "")
|
||||
|
||||
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||
if (_host_processor MATCHES "^(arm64|aarch64)$")
|
||||
set(_asset "symgen-macos-arm64")
|
||||
set(_asset_hash "SHA256=0344838d1674df09c17c3eeddcf26eb89c333fdb85dfd78f68adc436070eccbe")
|
||||
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
|
||||
set(_asset "symgen-macos-x86_64")
|
||||
set(_asset_hash "SHA256=ae0674f4a1e9d0dedfa02d35939ac28cdd229276b331c1f507df5df809cbec7e")
|
||||
endif ()
|
||||
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
|
||||
if (_host_processor MATCHES "^(aarch64|arm64)$")
|
||||
set(_asset "symgen-linux-aarch64")
|
||||
set(_asset_hash "SHA256=af766de2bfaeb0a06f6d7bc17bb2510b4a9c40f44a56e49bc4a4b798a6223042")
|
||||
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
|
||||
set(_asset "symgen-linux-x86_64")
|
||||
set(_asset_hash "SHA256=ebd62fb9623acc942b6295609e2306f85a73043b0a8a117f2072b761dd08e68f")
|
||||
elseif (_host_processor MATCHES "^(i[3-6]86|x86)$")
|
||||
set(_asset "symgen-linux-i686")
|
||||
set(_asset_hash "SHA256=07780a4513fd29726578efc4ff2d88736b22f407ed821b32a68e35ff1e9af5f4")
|
||||
endif ()
|
||||
elseif (CMAKE_HOST_WIN32)
|
||||
if (_host_processor MATCHES "^(arm64|aarch64)$")
|
||||
set(_asset "symgen-windows-arm64.exe")
|
||||
set(_asset_hash "SHA256=5bb22b4a4a9b5ad45646af411bfb09b8321a732ff3a077eb4c9de1feaef27d2b")
|
||||
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
|
||||
set(_asset "symgen-windows-x86_64.exe")
|
||||
set(_asset_hash "SHA256=1d1ac087f991a96932d108969e15998becfec643e88f3e7d182ca97ebfc6a46f")
|
||||
elseif (_host_processor MATCHES "^(i[3-6]86|x86)$")
|
||||
set(_asset "symgen-windows-x86.exe")
|
||||
set(_asset_hash "SHA256=c113f4cd05f813efe2b1878dbfcf44d6302aee3974271736e0036430b0cced78")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(${out_name} "${_asset}" PARENT_SCOPE)
|
||||
set(${out_hash} "${_asset_hash}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(ensure_symgen required)
|
||||
if (TARGET symgen)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
if (SYMGEN_PATH)
|
||||
get_filename_component(_symgen "${SYMGEN_PATH}" ABSOLUTE)
|
||||
if (NOT EXISTS "${_symgen}")
|
||||
if (required)
|
||||
message(FATAL_ERROR "symgen: SYMGEN_PATH does not exist: ${_symgen}")
|
||||
endif ()
|
||||
message(STATUS "symgen: SYMGEN_PATH does not exist, symbol manifest generation "
|
||||
"skipped (by-name hook resolution will be unavailable)")
|
||||
return()
|
||||
endif ()
|
||||
else ()
|
||||
symgen_host_asset(_asset _asset_hash)
|
||||
if (_asset STREQUAL "")
|
||||
if (required)
|
||||
message(FATAL_ERROR "symgen: no prebuilt binary for host "
|
||||
"${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR} "
|
||||
"(configure with -DDUSK_ENABLE_CODE_MODS=OFF)")
|
||||
endif ()
|
||||
message(STATUS "symgen: no prebuilt binary for host "
|
||||
"${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR}; "
|
||||
"symbol manifest generation skipped (by-name hook resolution will be unavailable)")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(_symgen_dir "${CMAKE_BINARY_DIR}/_deps/symgen")
|
||||
set(_symgen "${_symgen_dir}/${_asset}")
|
||||
set(_url "${_SYMGEN_RELEASE_BASE_URL}/${_asset}")
|
||||
message(STATUS "dusklight: Fetching symgen ${_SYMGEN_VERSION} (${_asset})")
|
||||
file(MAKE_DIRECTORY "${_symgen_dir}")
|
||||
file(DOWNLOAD "${_url}" "${_symgen}"
|
||||
TLS_VERIFY ON
|
||||
STATUS _download_status
|
||||
SHOW_PROGRESS
|
||||
EXPECTED_HASH "${_asset_hash}")
|
||||
list(GET _download_status 0 _download_code)
|
||||
if (NOT _download_code EQUAL 0)
|
||||
list(GET _download_status 1 _download_message)
|
||||
file(REMOVE "${_symgen}")
|
||||
if (required)
|
||||
message(FATAL_ERROR "symgen: failed to download ${_url}: ${_download_message}")
|
||||
endif ()
|
||||
message(STATUS "symgen: failed to download ${_url}: ${_download_message}; "
|
||||
"symbol manifest generation skipped (by-name hook resolution will be unavailable)")
|
||||
return()
|
||||
endif ()
|
||||
if (NOT CMAKE_HOST_WIN32)
|
||||
file(CHMOD "${_symgen}" PERMISSIONS
|
||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
add_custom_target(symgen DEPENDS "${_symgen}")
|
||||
set(SYMGEN_EXE "${_symgen}" CACHE INTERNAL "symgen executable" FORCE)
|
||||
endfunction()
|
||||
|
||||
function(setup_symbol_manifest target)
|
||||
ensure_symgen(TRUE)
|
||||
if (NOT TARGET symgen)
|
||||
return()
|
||||
endif ()
|
||||
add_dependencies(${target} symgen)
|
||||
|
||||
# Reserve an ELF program-header entry when the linker supports it (mold).
|
||||
# symgen can replace the PT_NULL entry without relocating the table, keeping later post-link tools safe.
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
include(CheckLinkerFlag)
|
||||
check_linker_flag(CXX "LINKER:--spare-program-headers=1" _linker_supports_spare_program_headers)
|
||||
if (_linker_supports_spare_program_headers)
|
||||
target_link_options(${target} PRIVATE "LINKER:--spare-program-headers=1")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
set(_input --pdb "$<TARGET_PDB_FILE:${target}>")
|
||||
else ()
|
||||
set(_input --binary "$<TARGET_FILE:${target}>")
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
# Room for the symbol manifest and several prepatch arenas.
|
||||
target_link_options(${target} PRIVATE "LINKER:-headerpad,0x1000")
|
||||
# ld64 may update an existing output, which breaks our symdb insertion. Remove it first.
|
||||
add_custom_command(TARGET ${target} PRE_LINK
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -f "$<TARGET_FILE:${target}>"
|
||||
VERBATIM)
|
||||
endif ()
|
||||
|
||||
# The manifest is embedded into the image as a new section, located at runtime through
|
||||
# the descriptor manifest.cpp reserves. On Apple platforms this command must stay
|
||||
# attached before the ad-hoc codesign POST_BUILD command: the patch removes any existing
|
||||
# signature.
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND "${SYMGEN_EXE}" manifest ${_input} --embed "$<TARGET_FILE:${target}>"
|
||||
COMMENT "Embedding symbol manifest"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
@@ -0,0 +1,93 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
get_filename_component(_DUSK_WINDOWS_EXPORTS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||
|
||||
# Windows mod linking: generate the curated export surface for the game executable and the
|
||||
# import library mods link against. symgen scans the built objects, filters by source, and
|
||||
# writes a .def used by the main link and import library generation.
|
||||
function(setup_windows_exports target)
|
||||
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _implib_arch)
|
||||
if (_implib_arch STREQUAL "arm64")
|
||||
set(_dlltool_machine "arm64")
|
||||
set(_lib_machine "arm64")
|
||||
elseif (_implib_arch MATCHES "^(amd64|x86_64)$")
|
||||
set(_dlltool_machine "i386:x86-64")
|
||||
set(_lib_machine "x64")
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"dusklight: no Windows mod linking support for ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif ()
|
||||
|
||||
include("${_DUSK_WINDOWS_EXPORTS_CMAKE_DIR}/SymbolManifest.cmake")
|
||||
ensure_symgen(TRUE)
|
||||
set(_symgen "${SYMGEN_EXE}")
|
||||
add_dependencies(${target} symgen)
|
||||
|
||||
set(_config_subdir "")
|
||||
if (CMAKE_CONFIGURATION_TYPES)
|
||||
set(_config_subdir "$<CONFIG>/")
|
||||
endif ()
|
||||
|
||||
set(_rsp_lines "$<TARGET_OBJECTS:${target}>")
|
||||
foreach (_lib IN LISTS JSYSTEM_LIBRARIES)
|
||||
list(APPEND _rsp_lines "$<TARGET_FILE:${_lib}>")
|
||||
endforeach ()
|
||||
list(JOIN _rsp_lines "\n" _rsp_content)
|
||||
set(_rsp "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports_input.rsp")
|
||||
file(GENERATE OUTPUT "${_rsp}" CONTENT "${_rsp_content}")
|
||||
|
||||
set(_sdk_args)
|
||||
foreach (_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
|
||||
aurora_os aurora_pad aurora_si aurora_vi)
|
||||
if (TARGET ${_lib})
|
||||
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:${_lib}>")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
set(_forward_args)
|
||||
if (TARGET dawn::webgpu_dawn)
|
||||
get_target_property(_dawn_type dawn::webgpu_dawn TYPE)
|
||||
if (_dawn_type STREQUAL "SHARED_LIBRARY")
|
||||
list(APPEND _forward_args
|
||||
--forward-dll "$<TARGET_FILE:dawn::webgpu_dawn>"
|
||||
--forward-sym-prefix wgpu)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Generate curated exports list from the main binary
|
||||
set(_def "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports.def")
|
||||
add_custom_command(TARGET ${target} PRE_LINK
|
||||
COMMAND "${_symgen}" def
|
||||
"@${_rsp}"
|
||||
--out "${_def}"
|
||||
--exclude cmake_pch
|
||||
--exclude miniz
|
||||
--exclude asan_options
|
||||
--exclude src/dusk
|
||||
--max-exports 58000
|
||||
${_sdk_args}
|
||||
${_forward_args}
|
||||
COMMENT "Generating dusklight exports"
|
||||
VERBATIM)
|
||||
target_link_options(${target} PRIVATE "/DEF:${_def}")
|
||||
|
||||
# Generate import library for mods to link against.
|
||||
set(_implib "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_imports.lib")
|
||||
get_filename_component(_compiler_dir "${CMAKE_CXX_COMPILER}" DIRECTORY)
|
||||
find_program(DUSK_LLVM_DLLTOOL llvm-dlltool HINTS "${_compiler_dir}")
|
||||
if (DUSK_LLVM_DLLTOOL)
|
||||
set(_implib_cmd "${DUSK_LLVM_DLLTOOL}" -d "${_def}" -D dusklight.exe
|
||||
-m "${_dlltool_machine}" -l "${_implib}")
|
||||
else ()
|
||||
set(_implib_cmd "${CMAKE_AR}" /nologo "/def:${_def}" "/machine:${_lib_machine}"
|
||||
/name:dusklight.exe "/out:${_implib}")
|
||||
endif ()
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND ${_implib_cmd}
|
||||
BYPRODUCTS "${_implib}"
|
||||
COMMENT "Generating dusklight import library"
|
||||
VERBATIM)
|
||||
set(DUSK_GAME_IMPLIB "${_implib}" CACHE INTERNAL "Import library for Windows mod linking")
|
||||
|
||||
install(FILES "${_implib}" DESTINATION sdk RENAME "windows-${_implib_arch}.lib")
|
||||
endfunction()
|
||||
Reference in New Issue
Block a user