Merge branch 'main' of https://github.com/TwilitRealm/dusk into randomizer

This commit is contained in:
gymnast86
2026-07-15 01:08:32 -07:00
243 changed files with 2746 additions and 1741 deletions
+14 -1
View File
@@ -80,6 +80,7 @@ jobs:
path: |
build/install/Dusklight-*.AppImage
build/install/debug.tar.*
build/install/sdk/
build-apple:
name: Build Apple (${{matrix.name}})
@@ -148,6 +149,7 @@ jobs:
path: |
build/install/Dusklight.app
build/install/debug.tar.*
build/install/sdk/
build-android:
name: Build Android (${{matrix.name}})
@@ -203,6 +205,9 @@ jobs:
- name: Build native library
run: cmake --build --preset ${{matrix.preset}} --target dusklight
- name: Build bundled mods
run: cmake --build --preset ${{matrix.preset}} --target dusklight_mods
- name: Stage stripped JNI library
run: ANDROID_STAGE_ABIS="${{matrix.abi}}" platforms/android/scripts/stage-jni-libs.sh
@@ -210,11 +215,17 @@ jobs:
working-directory: platforms/android
run: ./gradlew :app:assembleRelease --rerun-tasks
- name: Stage artifacts
run: |
mkdir -p upload/sdk
cp build/*/stub-android-*.so upload/sdk/
cp platforms/android/app/build/outputs/apk/release/app-${{matrix.abi}}-release-unsigned.apk upload/
- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: dusklight-${{env.DUSK_VERSION}}-android-${{matrix.artifact_arch}}
path: platforms/android/app/build/outputs/apk/release/app-${{matrix.abi}}-release-unsigned.apk
path: upload/
build-windows:
name: Build Windows (${{matrix.name}})
@@ -280,4 +291,6 @@ jobs:
build/install/*.exe
build/install/*.dll
build/install/res/
build/install/mods/
build/install/debug.7z
build/install/sdk/
+8 -3
View File
@@ -511,9 +511,12 @@ if (WIN32)
target_link_libraries(dusklight PRIVATE Psapi)
endif ()
if (APPLE)
# Mods resolve game symbols from the executable at dlopen time.
target_link_options(dusklight PRIVATE -Wl,-export_dynamic)
elseif (UNIX AND NOT ANDROID)
include(cmake/AppleExports.cmake)
setup_apple_exports(dusklight)
elseif (ANDROID)
include(cmake/AndroidExports.cmake)
setup_android_exports(dusklight)
elseif (UNIX)
target_link_options(dusklight PRIVATE -rdynamic)
endif ()
@@ -536,6 +539,8 @@ endif ()
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
target_link_options(dusklight PRIVATE "-Wl,--build-id=sha1")
target_link_libraries(dusklight PRIVATE dl)
elseif (ANDROID)
target_link_options(dusklight PRIVATE "-Wl,--build-id=sha1")
endif ()
if (NOT APPLE)
+4 -21
View File
@@ -22,18 +22,6 @@
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL"
}
},
{
"name": "release",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL",
"CMAKE_INTERPROCEDURAL_OPTIMIZATION": {
"type": "BOOL",
"value": true
}
}
},
{
"name": "ci",
"hidden": true,
@@ -208,17 +196,13 @@
{
"name": "windows-arm64-msvc",
"displayName": "Windows ARM64 (MSVC)",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"inherits": [
"windows-msvc"
],
"architecture": {
"value": "arm64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_C_COMPILER": "cl",
"CMAKE_CXX_COMPILER": "cl",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"hostOS": [
@@ -445,8 +429,7 @@
"hidden": true,
"inherits": [
"android-base",
"ci",
"release"
"ci"
],
"cacheVariables": {
"DUSK_ENABLE_SENTRY_NATIVE": {
+63
View File
@@ -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()
+90
View File
@@ -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()
+33 -10
View File
@@ -9,24 +9,47 @@ if (ANDROID)
list(APPEND _game_compile_defs TARGET_ANDROID=1)
endif ()
set(_game_include_dirs
# Public game headers
set(_game_abi_include_dirs
${_game_root}/include
${_game_root}/src
${_game_root}/assets/GZ2E01 # TODO: make this dynamic if needed?
${_game_root}/assets/GZ2E01
${_game_root}/libs/JSystem/include
${_game_root}/libs
${_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}
)
# Interface target for mods and sub-projects to inherit game headers/defines.
# 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})
if (TARGET dawn::dawncpp_headers)
target_link_libraries(dusklight_game_headers INTERFACE dawn::dawncpp_headers)
elseif (TARGET dawn::webgpu_dawn)
target_link_libraries(dusklight_game_headers INTERFACE dawn::webgpu_dawn)
endif ()
# 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)
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)
+170 -55
View File
@@ -1,8 +1,9 @@
# add_mod(<target> SOURCES <file>... MOD_JSON <mod.json> [RES_DIR <res>] [OVERLAY_DIR <overlay>]
# 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")
function(_mod_lib_name out_var)
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)
@@ -12,18 +13,20 @@ function(_mod_lib_name out_var)
set(_arch "${CMAKE_OSX_ARCHITECTURES}")
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")
elseif (APPLE)
set(_ext ".dylib")
else ()
set(_ext ".so")
endif ()
set(${out_var} "${_platform}-${_arch}${_ext}" PARENT_SCOPE)
set(${out_platform_var} "${_platform}-${_arch}" PARENT_SCOPE)
set(${out_name_var} "mod${_ext}" PARENT_SCOPE)
endfunction()
function(_mod_resolve_source_path out_var path)
@@ -44,8 +47,28 @@ function(_mod_collect_assets out_var dir)
set(${out_var} ${_files} 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(add_mod target_name)
cmake_parse_arguments(ARG "BUNDLE" "MOD_JSON;RES_DIR;OVERLAY_DIR;TEXTURES_DIR;OUTPUT_DIR" "SOURCES" ${ARGN})
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 ()
@@ -54,12 +77,33 @@ function(add_mod target_name)
message(FATAL_ERROR "add_mod: MOD_JSON does not exist: ${_mod_json}")
endif ()
set(_supported_features 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} SHARED ${ARG_SOURCES})
_mod_lib_name(_lib_name)
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
@@ -67,7 +111,16 @@ function(add_mod target_name)
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_game_headers)
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 "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
@@ -90,50 +143,77 @@ function(add_mod target_name)
endif ()
if (APPLE)
# Game symbols resolve against the host executable at dlopen time.
target_link_options(${target_name} PRIVATE -undefined dynamic_lookup)
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 ()
message(FATAL_ERROR
"add_mod: FEATURES ${_features} requires DUSK_GAME_EXE (game executable)")
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 (TARGET dusklight)
target_link_libraries(${target_name} PRIVATE dusklight)
elseif (DUSK_GAME_SOLIB)
target_link_libraries(${target_name} PRIVATE "${DUSK_GAME_SOLIB}")
else ()
message(FATAL_ERROR "add_mod: DUSK_GAME_SOLIB is not set (libmain.so)")
if (_needs_host_link)
if (TARGET dusklight)
target_link_libraries(${target_name} PRIVATE dusklight)
elseif (DUSK_GAME_EXE)
_mod_resolve_source_path(_game_lib "${DUSK_GAME_EXE}")
target_link_libraries(${target_name} PRIVATE "${_game_lib}")
else ()
message(FATAL_ERROR
"add_mod: FEATURES ${_features} requires DUSK_GAME_EXE (libmain.so or stub)")
endif ()
endif ()
set_target_properties(${target_name} PROPERTIES
BUILD_RPATH "$ORIGIN"
INSTALL_RPATH "$ORIGIN")
elseif (UNIX)
target_link_options(${target_name} PRIVATE -Wl,--allow-shlib-undefined)
elseif (WIN32)
# Link against the generated import library (game ABI surface). Function calls
# resolve through import thunks. Data is toolchain dependent:
# - clang-cl: lld's mingw mode auto-imports data references, fixed up at load by
# the mod SDK's pseudo-relocation runtime (pseudo_reloc.cpp).
# - cl (MSVC): only DUSK_GAME_DATA-annotated data is reachable. Un-annotated
# references fail to link.
if (NOT DUSK_GAME_IMPLIB)
message(FATAL_ERROR "add_mod: DUSK_GAME_IMPLIB is not set.")
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 ()
message(FATAL_ERROR
"add_mod: FEATURES ${_features} requires DUSK_GAME_EXE (import library)")
endif ()
target_link_libraries(${target_name} PRIVATE "${_game_lib}")
endif ()
target_link_libraries(${target_name} PRIVATE "${DUSK_GAME_IMPLIB}")
set_target_properties(${target_name} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
target_compile_definitions(${target_name} PRIVATE _ITERATOR_DEBUG_LEVEL=0)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(${target_name} PRIVATE "$<$<COMPILE_LANGUAGE:C,CXX>:/clang:-mcmodel=large>")
target_sources(${target_name} PRIVATE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../sdk/pseudo_reloc.cpp")
# lld mingw mode rewrites /DEFAULTLIB directives to -l style and skips %LIB%, so
# the CRT libraries and search paths are spelled out explicitly.
target_link_options(${target_name} PRIVATE -lldmingw /nodefaultlib /INCREMENTAL:NO)
target_link_libraries(${target_name} PRIVATE
msvcrt.lib msvcprt.lib vcruntime.lib ucrt.lib
oldnames.lib uuid.lib kernel32.lib user32.lib)
set(_lib_dirs "$ENV{LIB}")
if ("${_lib_dirs}" STREQUAL "")
message(FATAL_ERROR "add_mod: %LIB% is empty; configure from a VS dev shell")
endif ()
foreach (_libdir IN LISTS _lib_dirs)
target_link_options(${target_name} PRIVATE "/libpath:${_libdir}")
endforeach ()
endif ()
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)
@@ -142,18 +222,39 @@ function(add_mod target_name)
set(_stage "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_stage")
set(_out "${_output_dir}/${target_name}.dusk")
set(_zip_args "${_lib_name}" mod.json)
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_name}")
set(_lib_copy_cmd COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:${target_name}>" "${_stage}/${_lib_name}")
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}")
@@ -190,6 +291,7 @@ function(add_mod 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"
@@ -228,9 +330,10 @@ endfunction()
# 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, dylibs
# ad-hoc signed in place, then the whole bundle re-signed.
# - iOS/tvOS: assets into <app>/mods/<id> and the dylib into Frameworks/<id>.dylib.
# - 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)
@@ -239,6 +342,7 @@ function(install_bundled_mods)
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")
@@ -254,19 +358,30 @@ function(install_bundled_mods)
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_name}" EXCLUDE)
PATTERN "lib" EXCLUDE)
install(PROGRAMS "$<TARGET_FILE:${_target}>"
DESTINATION "${_bundle_dir}/Frameworks" RENAME "${_id}.dylib")
DESTINATION "${_bundle_dir}/Frameworks" RENAME "${_id}.so")
install(DIRECTORY "${_stage}/lib/${_lib_platform}/"
DESTINATION "${_bundle_dir}/Frameworks"
PATTERN "${_lib_name}" EXCLUDE)
endforeach ()
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 "execute_process(COMMAND /usr/bin/codesign --force --sign - \"${_bundle_dir}/Contents/Resources/mods/${_id}/${_lib_name}\" COMMAND_ERROR_IS_FATAL ANY)")
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 (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)")
+12 -9
View File
@@ -2,7 +2,7 @@ include_guard(GLOBAL)
get_filename_component(_SYMBOL_MANIFEST_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
set(_SYMGEN_VERSION "1.1.1")
set(_SYMGEN_VERSION "1.2.3")
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)
@@ -108,18 +108,21 @@ function(setup_symbol_manifest target)
if (WIN32)
set(_input --pdb "$<TARGET_PDB_FILE:${target}>")
set(_out "$<TARGET_FILE_DIR:${target}>/dusklight.symdb")
else ()
set(_input --binary "$<TARGET_FILE:${target}>")
if (APPLE)
set(_out "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/dusklight.symdb")
else ()
set(_out "$<TARGET_FILE_DIR:${target}>/dusklight.symdb")
endif ()
endif ()
if (APPLE)
# Room for the load command `symgen manifest --embed` inserts post-link.
target_link_options(${target} PRIVATE "LINKER:-headerpad,0x200")
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 invalidates any
# existing signature.
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${SYMGEN_EXE}" manifest ${_input} --out "${_out}"
COMMENT "Generating symbol manifest"
COMMAND "${SYMGEN_EXE}" manifest ${_input} --embed "$<TARGET_FILE:${target}>"
COMMENT "Embedding symbol manifest"
VERBATIM)
endfunction()
+17 -14
View File
@@ -6,9 +6,16 @@ get_filename_component(_DUSK_WINDOWS_EXPORTS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FIL
# 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)
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(WARNING "dusklight: Windows code-mod exports are x64-only for now; skipping")
return()
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")
@@ -50,14 +57,13 @@ function(setup_windows_exports target)
# 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
# TODO: src/dusk/ is NOT excluded: inline code in game headers
# currently call into it (e.g. dusk::frame_interp::lookup_replacement).
COMMAND "${_symgen}" def
--rsp "${_rsp}"
"@${_rsp}"
--out "${_def}"
--exclude cmake_pch
--exclude miniz
--exclude asan_options
--exclude src/dusk
--max-exports 58000
${_sdk_args}
${_forward_args}
@@ -70,11 +76,11 @@ function(setup_windows_exports target)
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 i386:x86-64
-l "${_implib}")
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:x64 /name:dusklight.exe
"/out:${_implib}")
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}
@@ -82,9 +88,6 @@ function(setup_windows_exports target)
COMMENT "Generating dusklight import library"
VERBATIM)
set(DUSK_GAME_IMPLIB "${_implib}" CACHE INTERNAL "Import library for Windows mod linking")
set(DUSK_GAME_DEF "${_def}" CACHE INTERNAL "Curated export .def for the game executable")
# Ship the import library as sdk/dusklight.lib in the install tree: mods may use it to
# compile against Dusklight without having to build the whole game. (See DUSK_GAME_IMPLIB)
install(FILES "${_implib}" DESTINATION sdk RENAME dusklight.lib)
install(FILES "${_implib}" DESTINATION sdk RENAME "windows-${_implib_arch}.lib")
endfunction()
+86 -72
View File
@@ -22,7 +22,7 @@ function, read and write data fields, and hook the vast majority of game functio
7. [Asset Overlays](#asset-overlays)
8. [Runtime Lifecycle](#runtime-lifecycle)
9. [Error Handling](#error-handling)
10. [Advanced: Exporting Services](#advanced-exporting-services)
10. [Advanced](#advanced)
---
@@ -50,6 +50,7 @@ set(DUSKLIGHT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dusklight" CACHE PATH "Path to du
add_subdirectory("${DUSKLIGHT_DIR}/sdk" dusklight-sdk EXCLUDE_FROM_ALL)
add_mod(my_mod
FEATURES game # optional
SOURCES src/mod.cpp
MOD_JSON mod.json
RES_DIR res # optional
@@ -58,6 +59,12 @@ add_mod(my_mod
)
```
Available features:
- `game`: Allows calling into and hooking game code. Mods that **only** use services may omit it, providing a wider
range of compatibility with Dusklight versions and a slightly faster build process.
- `webgpu`: Allows importing the WebGPU API (`webgpu/webgpu.h`). Must be enabled when using
[GfxService](#gfxservice-modssvcgfxh).
Building produces `my_mod.dusk` in `build/<preset>/mods/` (configurable via the `DUSK_MODS_OUTPUT_DIR` cache variable).
Dusklight searches a `mods/` directory next to the app in addition to the user directory, so a dev build launched from
`build/<preset>/` picks these up automatically: rebuild, relaunch (or click Reload), done.
@@ -131,26 +138,30 @@ A service is a struct of C function pointers with a version header. You declare
loader resolves it before your mod initializes:
```cpp
IMPORT_SERVICE(LogService, svc_log); // required, any minor version
IMPORT_SERVICE_VERSION(LogService, svc_log, 2); // required, minor version >= 2
IMPORT_SERVICE(LogService, svc_log); // required, latest minor version
IMPORT_SERVICE_VERSION(LogService, svc_log, 0); // required, minimum minor version 0 (for backwards compatibility)
IMPORT_OPTIONAL_SERVICE(SomeService, svc_maybe); // may be null
```
Each service is individually versioned, and there may be multiple major versions of a service provided at once,
allowing backwards compatibility with older mods while still changing services fundamentally if necessary. A **major**
bump is a breaking change, treated as a different service entirely. For **additive** changes, a service appends new
functions to the end of the struct without breaking existing callers and simply bumps the minor version. Mods that
want the newer functions may use `IMPORT_SERVICE_VERSION` to require that minor at **load time**, or `SERVICE_HAS` to
check at **runtime** whether a specific function is available.
functions to the end of the struct without breaking existing callers and simply bumps the minor version.
The contract (see `include/mods/api.h` for the full version):
`IMPORT_SERVICE` and `IMPORT_OPTIONAL_SERVICE` require the latest minor version compiled against, making every field in
the service safe to call. A mod can use `IMPORT_SERVICE_VERSION` (or its optional counterpart) with an older minor
version to remain compatible with older Dusklight versions, then use `SERVICE_HAS` to check at runtime for fields added
after that explicitly requested version.
The contract (see `sdk/include/mods/api.h` for the full version):
- **A required import is guaranteed valid.** If the service is missing or too old, the mod fails to load with a clear
error. No need to null check at call sites.
- **Anything at or below the minor version you imported can be called unconditionally.**
- **Anything at or below the minor version you imported can be called unconditionally.** The default macros import
the service type's current minor version; the versioned macros explicitly override that minimum.
- Optional imports may be null; check once in `mod_initialize`.
- Fields newer than your imported minor must be gated behind `SERVICE_HAS(service, ServiceType, field)` plus a null
check.
- Fields newer than your imported minor version must be gated behind `SERVICE_HAS(service, ServiceType, field)` plus a
null check.
---
@@ -200,13 +211,13 @@ const char* dir = svc_host->mod_dir(mod_ctx); // writable per-mod directory
svc_host->fail(mod_ctx, MOD_ERROR, "something unrecoverable happened"); // disables the mod
```
`get_service`/`publish_service` provide dynamic service lookup; see [Advanced](#advanced-exporting-services).
`get_service`/`publish_service` provide dynamic service lookup; see [Exporting Services](#exporting-services).
**Lifecycle watches.** If your mod provides a service that hands out per-caller state (registrations, callbacks,
handles), watch other mods' lifecycle and drop what you hold for a mod when it detaches.
```cpp
IMPORT_SERVICE_VERSION(HostService, svc_host, 1);
IMPORT_SERVICE(HostService, svc_host);
void on_mod_lifecycle(ModContext* ctx, ModContext* subject, const char* subject_id,
ModLifecycleEvent event, void* user_data) {
@@ -411,6 +422,8 @@ unless changing host UI is intentional.
### GfxService (`mods/svc/gfx.h`)
**Requires `add_mod(... FEATURES webgpu)`**
Direct WebGPU access at various stages of the rendering pipeline. Mods use the `wgpu*` C API (via `webgpu/webgpu.h`) for
custom draws and compute dispatches. Mods must manage their own WebGPU state, including pipelines and bind groups.
@@ -461,6 +474,8 @@ first in-game frame. Projection matrices match the renderer's WebGPU clip conven
## Hooking Game Functions
**Requires `add_mod(... FEATURES game)`**
Mods may hook the vast majority of game functions, including file-local static, private and virtual functions.
`mods/hook.hpp` provides typed helpers over the hook service:
@@ -469,8 +484,14 @@ Mods may hook the vast majority of game functions, including file-local static,
#include "mods/svc/hook.h"
IMPORT_SERVICE(HookService, svc_hook);
DEFINE_HOOK(&daAlink_c::posMove, LinkPosMove);
DEFINE_HOOK(&daAlink_c::execute, LinkExecute);
```
Every hook target must be **declared** at namespace scope with `DEFINE_HOOK` (a target you can name in C++) or
`DEFINE_HOOK_SYMBOL` (a symbol name).
### Pre-hooks
Run before the original. Return `HOOK_SKIP_ORIGINAL` to cancel it (post-hooks still run).
@@ -484,7 +505,7 @@ HookAction on_pos_move_pre(ModContext*, void* args, void* retval, void* userdata
return HOOK_CONTINUE;
}
dusk::mods::hook_add_pre<&daAlink_c::posMove>(svc_hook, on_pos_move_pre);
dusk::mods::hook_add_pre<LinkPosMove>(svc_hook, on_pos_move_pre);
```
### Post-hooks
@@ -495,24 +516,22 @@ if any.
```cpp
void on_pos_move_post(ModContext*, void* args, void* retval, void* userdata) { ... }
dusk::mods::hook_add_post<&daAlink_c::posMove>(svc_hook, on_pos_move_post);
dusk::mods::hook_add_post<LinkPosMove>(svc_hook, on_pos_move_post);
```
### Replace-hooks
Substitute the original entirely. Call through to it via `Hook<...>::g_orig` if needed:
Substitute the original entirely. Call through to it via the declaration's `g_orig` if needed:
```cpp
using ExecuteEntry = dusk::mods::Hook<&daAlink_c::execute>;
void on_execute_replace(ModContext*, void* args, void* retval, void*) {
int result = ExecuteEntry::g_orig(dusk::mods::arg<daAlink_c*>(args, 0));
int result = LinkExecute::g_orig(dusk::mods::arg<daAlink_c*>(args, 0));
if (retval != nullptr) {
*static_cast<int*>(retval) = result;
}
}
dusk::mods::hook_replace<&daAlink_c::execute>(svc_hook, on_execute_replace);
dusk::mods::hook_replace<LinkExecute>(svc_hook, on_execute_replace);
```
By default a second replace-hook on the same function is a conflict; `HookOptions` (`replace_policy`, `priority`,
@@ -525,10 +544,8 @@ Functions you can't name in C++ (file-local statics, private class members, anyt
symbol name instead. You must supply the signature along with the name.
```cpp
// static callback used by Link's hookshot collider in d_a_alink_hook.inc
using HookshotHit = dusk::mods::NamedHook<
"daAlink_hookshotAtHitCallBack",
void(fopAc_ac_c*, dCcD_GObjInf*, fopAc_ac_c*, dCcD_GObjInf*)>;
DEFINE_HOOK_SYMBOL("daAlink_hookshotAtHitCallBack",
void(fopAc_ac_c*, dCcD_GObjInf*, fopAc_ac_c*, dCcD_GObjInf*), HookshotHit);
dusk::mods::hook_add_pre<HookshotHit>(svc_hook, on_hookshot_hit_pre);
...
@@ -537,9 +554,16 @@ HookshotHit::g_orig(link, atObjInf, target, tgObjInf); // call through to the o
Class member functions must include `Class*` as the first argument.
The install fails with the resolve error when the name is missing (`MOD_UNAVAILABLE`), ambiguous (`MOD_CONFLICT`),
or the manifest is absent (`MOD_UNSUPPORTED`). Unlike `Hook<&Class::method>`, the signature is **not**
compiler-checked: a mismatched signature will corrupt the call.
Two spellings work on every platform:
- **Display names** (`daAlink_c::posMove`, `fapGm_Before`): the qualified name with no parameter list. They carry no
signature, so overload sets (and file-local statics sharing a name) return `MOD_CONFLICT`.
- **Decorated names** (`_ZN9daAlink_c7posMoveEv` / `?posMove@daAlink_c@@...`): the platform's mangled spelling in
dlsym convention (no Mach-O leading underscore). The escape hatch for overloads.
Installing fails with `MOD_UNAVAILABLE` when it didn't resolve (missing, ambiguous, or no symbol manifest). Unlike
`DEFINE_HOOK`, the signature is **not** compiler-checked: a mismatched signature will corrupt the
call.
### Reading and writing arguments
@@ -552,6 +576,8 @@ T& ref = dusk::mods::arg_ref<T>(args, n); // read/write reference
```
```cpp
DEFINE_HOOK(fopAcM_createItem, CreateItem);
// fpc_ProcID fopAcM_createItem(..., int itemNo, ...): turn heart drops into green rupees
HookAction on_create_item_pre(ModContext*, void* args, void*, void*) {
int& itemNo = dusk::mods::arg_ref<int>(args, 1);
@@ -561,54 +587,11 @@ HookAction on_create_item_pre(ModContext*, void* args, void*, void*) {
return HOOK_CONTINUE;
}
dusk::mods::hook_add_pre<&fopAcM_createItem>(svc_hook, on_create_item_pre);
dusk::mods::hook_add_pre<CreateItem>(svc_hook, on_create_item_pre);
```
For reference parameters (e.g. `const cXyz& pos`), `arg_ref<cXyz>` yields a direct reference.
### Resolving symbols by name
`resolve` looks a symbol up in the **symbol manifest**: a name→address map generated alongside every game build and
keyed to that exact binary. It covers the whole image, including functions that aren't exported (file-local statics),
which makes them hookable:
```cpp
IMPORT_SERVICE(HookService, svc_hook);
void* addr = nullptr;
uint32_t flags = 0;
if (svc_hook->resolve(mod_ctx, "GXSetProjection", &addr, &flags) == MOD_OK) {
// addr is the function's real address in the running game; hook or call it.
}
```
Two spellings work on every platform:
- **Display names** (`daAlink_c::posMove`, `fapGm_Before`): the qualified name with no parameter list. They carry no
signature, so overload sets (and file-local statics sharing a name) return `MOD_CONFLICT`.
- **Decorated names** (`_ZN9daAlink_c7posMoveEv` / `?posMove@daAlink_c@@...`): the platform's mangled spelling in
dlsym convention (no Mach-O leading underscore). The escape hatch for overloads.
`MOD_UNSUPPORTED` means the manifest is missing or was built for a different game binary.
### Game code ABI contract
A primary consideration when letting mods link against the game is maintaining ABI stability across Dusklight
versions. If your mod calls or hooks game code directly (anything beyond the service APIs), import `GameService`
(`mods/svc/game.h`):
```cpp
IMPORT_SERVICE(GameService, svc_game);
```
Its major version is the game code ABI epoch: it's bumped when game struct or vtable layouts change incompatibly, and
the ordinary service version check then rejects your mod with a clear error instead of letting it corrupt memory in a
version it wasn't built for. Service-only and asset-only mods should *not* import it and will continue to work across
game ABI changes.
The more you can do through services, the better: a mod that avoids touching game code directly sidesteps future ABI
breaks entirely and plays nicer with other enabled mods.
---
## Asset Overlays
@@ -680,7 +663,9 @@ explicit results.
---
## Advanced: Exporting Services
## Advanced
### Exporting Services
Mods may export services of their own, permitting framework mods and cross-mod integration. Define the interface in a
header both mods share:
@@ -704,6 +689,7 @@ template <>
struct dusk::mods::ServiceTraits<MyModService> {
static constexpr const char* id = MY_MOD_SERVICE_ID;
static constexpr uint16_t major_version = MY_MOD_SERVICE_MAJOR;
static constexpr uint16_t minor_version = MY_MOD_SERVICE_MINOR;
};
#endif
```
@@ -732,7 +718,7 @@ svc_my_mod->do_thing(mod_ctx, 42);
The loader registers all exports before resolving any imports, so declaration order between mods doesn't matter. Note
that the `ctx` a provider receives identifies the *calling* mod.
### Dependencies between mods
#### Dependencies between mods
Service imports are also dependency declarations: the loader initializes mods in dependency order, so by the time your
`mod_initialize` runs, every mod you import services from (required *or* optional) has already finished its own
@@ -762,3 +748,31 @@ For services whose construction can't happen at static-init time, declare the ex
and publish the pointer later via `svc_host->publish_service(...)`. Consumers can fetch services dynamically with
`svc_host->get_service(...)`; prefer manifest imports whenever possible, since they give the loader dependency
information and fail fast with good errors.
### Native Runtime Libraries
`RUNTIME_LIBRARIES` passed to `add_mod` are packaged beside the mod's native module in `lib/<platform>/`. Dusklight
extracts the whole directory before loading the mod, so libraries linked by the mod resolve normally. The SDK links the
mod itself with `$ORIGIN` on Linux and `@loader_path` on Apple platforms; runtime libraries with their own non-system
dependencies must also be built with origin-relative lookup paths. On Windows, Dusklight uses an isolated DLL search
rooted at this directory.
```cmake
add_mod(my_mod
SOURCES src/mod.cpp
MOD_JSON mod.json
RUNTIME_LIBRARIES "${VENDOR_RUNTIME_LIBRARY}")
```
SDKs that load plugins by directory can pass them the absolute runtime path from the current HostService:
```cpp
IMPORT_SERVICE(HostService, svc_host);
const char* nativeDir = svc_host->native_dir(mod_ctx); // read-only
```
Libraries loaded explicitly by the mod remain its responsibility: stop their threads and unload them during
`mod_shutdown`. Do not write into `native_dir`; use `mod_dir` for writable state. Native library namespaces are
process-wide on some platforms, so two mods cannot safely assume that incompatible libraries with the same filename
will remain isolated.
+88 -49
View File
@@ -1411,70 +1411,103 @@ set(DOLPHIN_FILES
)
set(DUSK_FILES
include/dusk/action_bindings.h
include/dusk/endian_gx.hpp
include/dusk/config.hpp
include/dusk/dvd_asset.hpp
include/dusk/scope_guard.hpp
src/dusk/dvd_asset.cpp
include/helpers/batch.hpp
include/helpers/endian_gx.hpp
src/d/actor/d_a_alink_dusk.cpp
src/dusk/android_frame_rate.hpp
src/dusk/OSContext.cpp
src/dusk/OSMutex.cpp
src/dusk/OSReport.cpp
src/dusk/OSThread.cpp
src/dusk/achievements.cpp
src/dusk/action_bindings.cpp
src/dusk/action_bindings.h
src/dusk/android_frame_rate.cpp
src/dusk/android_frame_rate.hpp
src/dusk/asserts.cpp
src/dusk/batch.cpp
src/dusk/batch.hpp
src/dusk/autosave.cpp
src/dusk/config.cpp
src/dusk/config.hpp
src/dusk/crash_handler.cpp
src/dusk/crash_reporting.cpp
src/dusk/data.cpp
src/dusk/data.hpp
src/dusk/endian.cpp
src/dusk/discord.cpp
src/dusk/discord.hpp
src/dusk/discord_presence.cpp
src/dusk/dvd_asset.cpp
src/dusk/dvd_asset.hpp
src/dusk/extras.c
src/dusk/file_select.cpp
src/dusk/file_select.hpp
src/dusk/frame_interpolation.cpp
src/dusk/game_clock.cpp
src/dusk/gamepad_color.cpp
src/dusk/globals.cpp
src/dusk/gyro.cpp
include/dusk/menu_pointer.h
src/dusk/menu_pointer.cpp
src/dusk/mouse.cpp
src/dusk/gamepad_color.cpp
src/dusk/autosave.cpp
src/dusk/http/http.hpp
src/dusk/io.cpp
src/dusk/layout.cpp
src/dusk/logging.cpp
src/dusk/settings.cpp
src/dusk/speedrun.cpp
src/dusk/string.cpp
src/dusk/stubs.cpp
include/dusk/texture_replacements.hpp
src/dusk/texture_replacements.cpp
src/dusk/touch_camera.cpp
src/dusk/update_check.cpp
src/dusk/update_check.hpp
#src/dusk/m_Do_ext_dusk.cpp
src/dusk/imgui/ImGuiConfig.hpp
src/dusk/imgui/ImGuiConsole.hpp
src/dusk/imgui/ImGuiConsole.cpp
src/dusk/imgui/ImGuiEngine.cpp
src/dusk/imgui/ImGuiEngine.hpp
src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiBloomWindow.cpp
src/dusk/imgui/ImGuiBloomWindow.hpp
src/dusk/imgui/ImGuiCameraOverlay.cpp
src/dusk/imgui/ImGuiConfig.hpp
src/dusk/imgui/ImGuiConsole.cpp
src/dusk/imgui/ImGuiConsole.hpp
src/dusk/imgui/ImGuiControllerOverlay.cpp
src/dusk/imgui/ImGuiEngine.cpp
src/dusk/imgui/ImGuiEngine.hpp
src/dusk/imgui/ImGuiHeapOverlay.cpp
src/dusk/imgui/ImGuiMenuTools.cpp
src/dusk/imgui/ImGuiMenuTools.hpp
src/dusk/imgui/ImGuiMenuRandomizer.cpp
src/dusk/imgui/ImGuiMenuRandomizer.hpp
src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiProcessOverlay.cpp
src/dusk/imgui/ImGuiCameraOverlay.cpp
src/dusk/imgui/ImGuiHeapOverlay.cpp
src/dusk/imgui/ImGuiControllerOverlay.cpp
src/dusk/imgui/ImGuiStubLog.cpp
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStateShare.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStubLog.cpp
src/dusk/io.cpp
src/dusk/iso_validate.cpp
src/dusk/layout.cpp
src/dusk/livesplit.cpp
src/dusk/logging.cpp
src/dusk/menu_pointer.cpp
src/dusk/menu_pointer.h
src/dusk/mods/loader/bundle_disk.cpp
src/dusk/mods/loader/bundle_zip.cpp
src/dusk/mods/loader/context.cpp
src/dusk/mods/loader/depgraph.cpp
src/dusk/mods/loader/depgraph.hpp
src/dusk/mods/loader/loader.cpp
src/dusk/mods/loader/loader.hpp
src/dusk/mods/loader/native_module.cpp
src/dusk/mods/loader/native_module.hpp
src/dusk/mods/log_buffer.cpp
src/dusk/mods/log_buffer.hpp
src/dusk/mods/manifest.cpp
src/dusk/mods/manifest.hpp
src/dusk/mods/svc/camera.cpp
src/dusk/mods/svc/config.cpp
src/dusk/mods/svc/config.hpp
src/dusk/mods/svc/game.cpp
src/dusk/mods/svc/gfx.cpp
src/dusk/mods/svc/hook.cpp
src/dusk/mods/svc/host.cpp
src/dusk/mods/svc/log.cpp
src/dusk/mods/svc/overlay.cpp
src/dusk/mods/svc/registry.cpp
src/dusk/mods/svc/registry.hpp
src/dusk/mods/svc/resource.cpp
src/dusk/mods/svc/texture.cpp
src/dusk/mods/svc/ui.cpp
src/dusk/mods/svc/ui.hpp
src/dusk/mouse.cpp
src/dusk/scope_guard.hpp
src/dusk/settings.cpp
src/dusk/speedrun.cpp
src/dusk/stubs.cpp
src/dusk/texture_replacements.cpp
src/dusk/texture_replacements.hpp
src/dusk/touch_camera.cpp
src/dusk/ui/achievements.cpp
src/dusk/ui/achievements.hpp
src/dusk/ui/bool_button.cpp
@@ -1483,11 +1516,11 @@ set(DUSK_FILES
src/dusk/ui/button.hpp
src/dusk/ui/component.cpp
src/dusk/ui/component.hpp
src/dusk/ui/controls.hpp
src/dusk/ui/controller_config.cpp
src/dusk/ui/controller_config.hpp
src/dusk/ui/cosmetics.hpp
src/dusk/ui/cosmetics.cpp
src/dusk/ui/controls.hpp
src/dusk/ui/document.cpp
src/dusk/ui/document.hpp
src/dusk/ui/editor.cpp
@@ -1496,18 +1529,22 @@ set(DUSK_FILES
src/dusk/ui/event.hpp
src/dusk/ui/graphics_tuner.cpp
src/dusk/ui/graphics_tuner.hpp
src/dusk/ui/input.cpp
src/dusk/ui/input.hpp
src/dusk/ui/icon_provider.cpp
src/dusk/ui/icon_provider.hpp
src/dusk/ui/input.cpp
src/dusk/ui/input.hpp
src/dusk/ui/logs_window.cpp
src/dusk/ui/logs_window.hpp
src/dusk/ui/menu_bar.cpp
src/dusk/ui/menu_bar.hpp
src/dusk/ui/mod_texture_provider.cpp
src/dusk/ui/mod_texture_provider.hpp
src/dusk/ui/mod_window.cpp
src/dusk/ui/mod_window.hpp
src/dusk/ui/modal.cpp
src/dusk/ui/modal.hpp
src/dusk/ui/mods_window.cpp
src/dusk/ui/mods_window.hpp
src/dusk/ui/nav_types.hpp
src/dusk/ui/number_button.cpp
src/dusk/ui/number_button.hpp
@@ -1515,10 +1552,6 @@ set(DUSK_FILES
src/dusk/ui/overlay.hpp
src/dusk/ui/pane.cpp
src/dusk/ui/pane.hpp
src/dusk/ui/menu_bar.cpp
src/dusk/ui/menu_bar.hpp
src/dusk/ui/mods_window.cpp
src/dusk/ui/mods_window.hpp
src/dusk/ui/prelaunch.cpp
src/dusk/ui/prelaunch.hpp
src/dusk/ui/preset.cpp
@@ -1533,10 +1566,10 @@ set(DUSK_FILES
src/dusk/ui/string_button.hpp
src/dusk/ui/tab_bar.cpp
src/dusk/ui/tab_bar.hpp
src/dusk/ui/touch_controls_common.cpp
src/dusk/ui/touch_controls_common.hpp
src/dusk/ui/touch_controls.cpp
src/dusk/ui/touch_controls.hpp
src/dusk/ui/touch_controls_common.cpp
src/dusk/ui/touch_controls_common.hpp
src/dusk/ui/touch_controls_editor.cpp
src/dusk/ui/touch_controls_editor.hpp
src/dusk/ui/ui.cpp
@@ -1552,7 +1585,6 @@ set(DUSK_FILES
src/dusk/achievements.cpp
src/dusk/iso_validate.cpp
src/dusk/livesplit.cpp
src/dusk/offset_ptr.cpp
src/dusk/OSContext.cpp
src/dusk/OSReport.cpp
src/dusk/OSThread.cpp
@@ -1590,6 +1622,9 @@ set(DUSK_FILES
src/dusk/discord_presence.cpp
src/dusk/version.cpp
src/dusk/action_bindings.cpp
src/dusk/update_check.cpp
src/dusk/update_check.hpp
src/dusk/version.cpp
src/dusk/cosmetics/color_utils.hpp
src/dusk/cosmetics/color_utils.cpp
src/dusk/cosmetics/midna_hair_color.hpp
@@ -1684,6 +1719,10 @@ set(DUSK_FILES
src/dusk/randomizer/generator/utility/time.cpp
src/dusk/randomizer/generator/utility/time.hpp
src/dusk/randomizer/generator/utility/yaml.hpp
src/helpers/batch.cpp
src/helpers/endian.cpp
src/helpers/offset_ptr.cpp
src/helpers/string.cpp
)
set(DUSK_HTTP_BACKEND_FILES
+1 -1
View File
@@ -5,7 +5,7 @@
#include "f_pc/f_pc_base.h"
#include "SSystem/SComponent/c_bg_s_grp_pass_chk.h"
#include "SSystem/SComponent/c_bg_s_poly_pass_chk.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
struct cBgD_Vtx_t : public Vec {};
+1 -1
View File
@@ -2,7 +2,7 @@
#define C_M3D_G_TRI_H_
#include "SSystem/SComponent/c_m3d_g_pla.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
class cM3dGCyl;
-6
View File
@@ -95,12 +95,6 @@ static void __THPAudioInitialize(THPAudioDecodeInfo* info, u8* ptr);
#define THP_TEXTURE_SET_COUNT 3
#endif
#if TARGET_PC
namespace dusk {
void MoviePlayerShutdown();
}
#endif
struct daMP_THPPlayer {
/* 0x000 */ DVDFileInfo fileInfo;
/* 0x03C */ THPHeader header;
+6 -6
View File
@@ -5,7 +5,7 @@
#include "SSystem/SComponent/c_xyz.h"
#if TARGET_PC
#include "dusk/batch.hpp"
#include "helpers/batch.hpp"
#endif
class cCcD_Obj;
@@ -112,11 +112,11 @@ public:
TGXTexObj mTexObj_l_J_Ohana00_64TEX;
TGXTexObj mTexObj_l_J_Ohana01_64128_0419TEX;
dusk::batch::LeafTemplate mTplHana00; // l_J_hana00DL
dusk::batch::LeafTemplate mTplHana00Cut; // l_J_hana00_cDL
dusk::batch::LeafTemplate mTplHana01; // l_J_hana01DL
dusk::batch::LeafTemplate mTplHana01Cut00; // l_J_hana01_c_00DL
dusk::batch::LeafTemplate mTplHana01Cut; // l_J_hana01_c_01DL
batch::LeafTemplate mTplHana00; // l_J_hana00DL
batch::LeafTemplate mTplHana00Cut; // l_J_hana00_cDL
batch::LeafTemplate mTplHana01; // l_J_hana01DL
batch::LeafTemplate mTplHana01Cut00; // l_J_hana01_c_00DL
batch::LeafTemplate mTplHana01Cut; // l_J_hana01_c_01DL
#endif
}; // Size: 0x12A54
+4 -4
View File
@@ -5,7 +5,7 @@
#include "SSystem/SComponent/c_xyz.h"
#if TARGET_PC
#include "../../../src/dusk/batch.hpp"
#include <helpers/batch.hpp>
#endif
class cCcD_Obj;
@@ -115,9 +115,9 @@ public:
TGXTexObj mTexObj_l_M_Hijiki00TEX;
TGXTexObj mTexObj_l_M_kusa05_RGBATEX;
dusk::batch::LeafTemplate mTplKusa9q; // l_M_Kusa_9qDL
dusk::batch::LeafTemplate mTplKusa9qCut; // l_M_Kusa_9q_cDL
dusk::batch::LeafTemplate mTplTengusa; // l_M_TenGusaDL
batch::LeafTemplate mTplKusa9q; // l_M_Kusa_9qDL
batch::LeafTemplate mTplKusa9qCut; // l_M_Kusa_9q_cDL
batch::LeafTemplate mTplTengusa; // l_M_TenGusaDL
#endif
}; // Size: 0x1D718
+3 -3
View File
@@ -6,9 +6,9 @@
#include "d/d_bg_w_base.h"
#include <mtx.h>
#include <types.h>
#include "dusk/offset_ptr.h"
#include "dusk/endian.h"
#include "dusk/endian_ssystem.h"
#include "helpers/offset_ptr.h"
#include "helpers/endian.h"
#include "helpers/endian_ssystem.h"
class cBgS_GrpPassChk;
class cBgS_PolyPassChk;
+1 -1
View File
@@ -6,7 +6,7 @@
#include "d/d_bg_plc.h"
#include "d/d_bg_s_sph_chk.h"
#include "d/d_bg_w_base.h"
#include "dusk/offset_ptr.h"
#include "helpers/offset_ptr.h"
class cBgS_GrpPassChk;
class cBgS_PolyPassChk;
+18 -16
View File
@@ -17,8 +17,14 @@
#include "m_Do/m_Do_graphic.h"
#include <cstring>
#if TARGET_PC
#include "dusk/profiling.hpp"
#if defined(DUSK_BUILDING_GAME)
#include <tracy/Tracy.hpp>
#include "dusk/settings.h"
#else
#ifndef ZoneScoped
#define ZoneScoped
#define ZoneScopedN(name)
#endif
#endif
enum dComIfG_ButtonStatus {
@@ -4907,27 +4913,23 @@ inline void dComIfGd_drawXluListDark() {
g_dComIfG_gameInfo.drawlist.drawXluListDark();
}
#if TARGET_PC
void dComIfGd_drawXluListInvisible();
#else
inline void dComIfGd_drawXluListInvisible() {
ZoneScoped;
#ifdef TARGET_PC
if (!dusk::getSettings().game.disableWaterRefraction) {
#endif
g_dComIfG_gameInfo.drawlist.drawXluListInvisible();
#ifdef TARGET_PC
}
#endif
g_dComIfG_gameInfo.drawlist.drawXluListInvisible();
}
#endif
#if TARGET_PC
void dComIfGd_drawOpaListInvisible();
#else
inline void dComIfGd_drawOpaListInvisible() {
ZoneScoped;
#ifdef TARGET_PC
if (!dusk::getSettings().game.disableWaterRefraction) {
#endif
g_dComIfG_gameInfo.drawlist.drawOpaListInvisible();
#ifdef TARGET_PC
}
#endif
g_dComIfG_gameInfo.drawlist.drawOpaListInvisible();
}
#endif
inline void dComIfGd_drawXluListZxlu() {
ZoneScoped;
+1 -1
View File
@@ -5,7 +5,7 @@
#include "JSystem/J2DGraph/J2DScreen.h"
#include "JSystem/J3DGraphBase/J3DSys.h"
#include "SSystem/SComponent/c_m3d_g_pla.h"
#include "dusk/gx_helper.h"
#include "helpers/gx_helper.h"
#include "f_op/f_op_view.h"
#include "global.h"
#include "m_Do/m_Do_ext.h"
+1 -1
View File
@@ -3,7 +3,7 @@
#include "global.h"
#include "f_pc/f_pc_base.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
struct msg_class;
+2 -2
View File
@@ -4,8 +4,8 @@
#include "JSystem/JMessage/control.h"
#include "JSystem/JMessage/JMessage.h"
#include "SSystem/SComponent/c_xyz.h"
#include "dusk/endian.h"
#include "dusk/string.hpp"
#include "helpers/endian.h"
#include "helpers/string.hpp"
#if REGION_JPN
#define D_MSG_CLASS_PAGE_CNT_MAX 30
+1 -1
View File
@@ -2,7 +2,7 @@
#define D_MSG_D_MSG_FLOW_H
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
enum {
NODETYPE_MESSAGE_e = 1,
+3 -3
View File
@@ -8,7 +8,7 @@
#include "d/d_item_data.h"
#include "JSystem/JUtility/JUTAssert.h"
#include "JSystem/JHostIO/JORReflexible.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
#if TARGET_PC
#include <unordered_map>
@@ -522,7 +522,7 @@ public:
#endif
void setPlayerName(const char* i_name) {
#if AVOID_UB
dusk::SafeStringCopyTruncate(mPlayerName, i_name);
SafeStringCopyTruncate(mPlayerName, i_name);
#else
strcpy(mPlayerName, i_name);
#endif
@@ -534,7 +534,7 @@ public:
#endif
void setHorseName(const char* i_name) {
#if AVOID_UB
dusk::SafeStringCopyTruncate(mHorseName, i_name);
SafeStringCopyTruncate(mHorseName, i_name);
#else
strcpy(mHorseName, i_name);
#endif
+1 -1
View File
@@ -4,7 +4,7 @@
#include "SSystem/SComponent/c_lib.h"
#include "d/d_kankyo.h"
#include "d/d_kankyo_data.h"
#include "dusk/offset_ptr.h"
#include "helpers/offset_ptr.h"
#include "f_op/f_op_actor_mng.h"
#include "global.h"
#include "os_report.h"
+1 -1
View File
@@ -2,7 +2,7 @@
#define D_D_TRESURE_H
#include <mtx.h>
#include "dusk/offset_ptr.h"
#include "helpers/offset_ptr.h"
class dTres_c {
public:
+1 -1
View File
@@ -8,7 +8,7 @@
#endif
#ifndef __MWERKS__
#include "dusk/math.h"
#include "helpers/math.h"
#endif
#endif // dolzel.h
-18
View File
@@ -1,18 +0,0 @@
#ifndef _SRC_IMGUI_H_
#define _SRC_IMGUI_H_
#include <aurora/aurora.h>
#ifdef __cplusplus
extern "C"
{
#endif
void imgui_main(const AuroraInfo* info);
void frame_limiter();
#ifdef __cplusplus
}
#endif
#endif
-12
View File
@@ -1,12 +0,0 @@
#pragma once
#if defined(__has_include)
#if __has_include(<tracy/Tracy.hpp>)
#include <tracy/Tracy.hpp>
#endif
#endif
#ifndef ZoneScoped
#define ZoneScoped
#define ZoneScopedN(name)
#endif
+1 -1
View File
@@ -12,7 +12,7 @@
#include "f_pc/f_pc_manager.h"
#include "m_Do/m_Do_hostIO.h"
#include "SSystem/SComponent/c_phase.h"
#include "dusk/endian_ssystem.h"
#include "helpers/endian_ssystem.h"
#if !__MWERKS__
// mwerks compiler makes value initialization act like default initialization so we need
+12
View File
@@ -0,0 +1,12 @@
# Public game helpers
Headers in this directory provide port-specific types and utilities used by ordinary game
headers. Their corresponding implementations live in `src/helpers/`.
These helpers **must not** depend on internal `src/dusk/` declarations in their public interface.
Unlike the internal `dusk::` namespace, they are exposed to mods that use the `game` feature
and therefore must remain ABI stable within `GameService` major versions.
APIs _specifically_ for mod use do not belong here; instead they belong in mod services,
which are individually versioned, can provide backwards compatibility, and are designed to
keep track of per-mod runtime state.
@@ -2,7 +2,7 @@
#include <dolphin/types.h>
namespace dusk::batch {
namespace batch {
struct LeafTemplate {
static constexpr u32 kMaxVtx = 192;
@@ -22,4 +22,4 @@ struct LeafTemplate {
void decode_leaf_template(const u8* dl, u32 size, LeafTemplate& out);
} // namespace dusk
} // namespace batch
@@ -1,5 +1,4 @@
#ifndef DOLPHIN_ENDIAN_H
#define DOLPHIN_ENDIAN_H
#pragma once
#include <bit>
@@ -292,6 +291,3 @@ inline void be_swap(Mtx& val) {
#define BE(T) T
#define BE_HOST(T) (T)
#endif
#endif // DOLPHIN_ENDIAN_H
@@ -1,5 +1,4 @@
#ifndef _DUSK_ENDIAN_SSYSTEM_H_
#define _DUSK_ENDIAN_SSYSTEM_H_
#pragma once
#include "SSystem/SComponent/c_sxyz.h"
#include "endian.h"
@@ -61,5 +60,3 @@ struct BE<cXyz> {
};
}
};
#endif
@@ -1,12 +1,17 @@
#ifndef DUSK_GX_HELPER_H
#define DUSK_GX_HELPER_H
#pragma once
#include <cstring>
#include <dolphin/gx/GXAurora.h>
#include <dolphin/gx/GXExtra.h>
#include "profiling.hpp"
#if defined(DUSK_BUILDING_GAME)
#include <tracy/Tracy.hpp>
#else
#ifndef ZoneScopedN
#define ZoneScopedN(name)
#endif
#endif
#if DUSK_GFX_DEBUG_GROUPS
#define GX_DEBUG_GROUP(name, ...) \
@@ -79,5 +84,3 @@ struct GXScopedDebugGroup {
};
#define GX_AND_TRACY_SCOPED(name) GXScopedDebugGroup scope(name); ZoneScopedN(name);
#endif // DUSK_GX_HELPER_H
@@ -1,5 +1,4 @@
#ifndef _SRC_DUSK_MATH_H_
#define _SRC_DUSK_MATH_H_
#pragma once
#include <cmath>
@@ -17,5 +16,3 @@ inline float i_tanf(float x) { return tan(x); }
inline float i_acosf(float x) { return acos(x); }
#include <dolphin/ppc_math.h>
#endif // _SRC_DUSK_MATH_H_
@@ -1,5 +1,4 @@
#ifndef DUSK_OFFSET_PTR_H
#define DUSK_OFFSET_PTR_H
#pragma once
#if TARGET_PC
@@ -47,21 +46,17 @@ struct OffsetPtrT {
}
operator T*() const {
return (T*) value;
}
return (T*)value; }
template<typename TOther>
template <typename TOther>
explicit operator TOther*() const {
return (TOther*) value;
return (TOther*)value;
}
};
#define OFFSET_PTR(T) OffsetPtrT<T>
#define OFFSET_PTR_RAW OffsetPtr
#else
#define OFFSET_PTR(T) T*
#define OFFSET_PTR_RAW u32
#endif
#endif // DUSK_OFFSET_PTR_H
@@ -1,8 +1,7 @@
#ifndef DUSK_STRING_HPP
#define DUSK_STRING_HPP
#include <cstdarg>
#pragma once
namespace dusk {
#include <cstdarg>
#include <cstddef>
struct TextSpan {
char* buffer;
@@ -44,7 +43,7 @@ private:
};
#if TARGET_PC
#define TEXT_SPAN dusk::TextSpan
#define TEXT_SPAN TextSpan
#else
#define TEXT_SPAN char*
#endif
@@ -111,11 +110,11 @@ int SafeStringPrintf(char (&buffer)[BufSize], const char* format, ...) {
}
#if TARGET_PC
#define SAFE_STRCPY dusk::SafeStringCopy
#define SAFE_STRCAT dusk::SafeStringCat
#define SAFE_SPRINTF dusk::SafeStringPrintf
#define SAFE_STRCPY_BOUNDED dusk::SafeStringCopy
#define SAFE_STRCAT_BOUNDED dusk::SafeStringCat
#define SAFE_STRCPY SafeStringCopy
#define SAFE_STRCAT SafeStringCat
#define SAFE_SPRINTF SafeStringPrintf
#define SAFE_STRCPY_BOUNDED SafeStringCopy
#define SAFE_STRCAT_BOUNDED SafeStringCat
#else
#define SAFE_STRCPY strcpy
#define SAFE_STRCAT strcat
@@ -123,6 +122,3 @@ int SafeStringPrintf(char (&buffer)[BufSize], const char* format, ...) {
#define SAFE_STRCPY_BOUNDED strcpy
#define SAFE_STRCPY_BOUNDED strcat
#endif
}
#endif // DUSK_STRING_HPP
+5 -9
View File
@@ -4,8 +4,12 @@
#include "Z2AudioLib/Z2AudioMgr.h"
#include "Z2AudioLib/Z2EnvSeMgr.h"
#include "Z2AudioLib/Z2LinkMgr.h"
#if defined(DUSK_BUILDING_GAME)
#include "dusk/audio.h"
#include "dusk/settings.h"
#else
#define DUSK_AUDIO_SKIP(...)
#endif
class mDoAud_zelAudio_c : public Z2AudioMgr {
public:
@@ -134,15 +138,7 @@ inline void mDoAud_seStart(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_re
}
#if TARGET_PC
inline void mDoAud_seStartMenu(u32 i_sfxID) {
if (!mDoAud_zelAudio_c::isInitFlag()) {
return;
}
if (!dusk::getSettings().audio.menuSounds.getValue()) {
return;
}
mDoAud_seStart(i_sfxID, nullptr, 0, 0);
}
void mDoAud_seStartMenu(u32 i_sfxID);
#endif
inline void mDoAud_seStartLevel(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_reverb) {
+11 -16
View File
@@ -3,7 +3,10 @@
#include "JSystem/JUtility/JUTGamePad.h"
#include "SSystem/SComponent/c_API_controller_pad.h"
#if defined(DUSK_BUILDING_GAME)
#include "dusk/settings.h"
#endif
// Controller Ports 1 - 4
enum { PAD_1, PAD_2, PAD_3, PAD_4 };
@@ -54,29 +57,21 @@ public:
static f32 getStickValue(u32 pad) { return getCpadInfo(pad).mMainStickValue; }
static s16 getStickAngle(u32 pad) { return getCpadInfo(pad).mMainStickAngle; }
#if TARGET_PC
static s16 getStickAngle3D(u32 pad);
#else
static s16 getStickAngle3D(u32 pad) {
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
return -getCpadInfo(pad).mMainStickAngle;
} else {
return getCpadInfo(pad).mMainStickAngle;
}
#else
return getCpadInfo(pad).mMainStickAngle;
#endif
}
#endif
#if TARGET_PC
static f32 getSubStickX3D(u32 pad);
#else
static f32 getSubStickX3D(u32 pad) {
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
return -getCpadInfo(pad).mCStickPosX;
} else {
return getCpadInfo(pad).mCStickPosX;
}
#else
return getCpadInfo(pad).mCStickPosX;
#endif
}
#endif
static f32 getSubStickX(u32 pad) { return getCpadInfo(pad).mCStickPosX; }
static f32 getSubStickY(u32 pad) { return getCpadInfo(pad).mCStickPosY; }
+1 -1
View File
@@ -8,7 +8,7 @@
#include "JSystem/JGeometry.h"
#endif
#include "dusk/gx_helper.h"
#include "helpers/gx_helper.h"
typedef struct Vec Vec;
struct ResTIMG;
+1 -1
View File
@@ -6,7 +6,7 @@
#include <mtx.h>
#include "JSystem/JMath/JMath.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
extern u8 g_printCurrentHeapDebug;
DUSK_GAME_EXTERN u8 g_printOtherHeapDebug;
+1 -1
View File
@@ -2,7 +2,7 @@
#define M_DO_M_DO_PRINTF_H
#include <os.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
void my_PutString(const char*);
void mDoPrintf_vprintf_Interrupt(char const*, va_list);
-464
View File
@@ -1,464 +0,0 @@
#pragma once
#include "mods/svc/hook.h"
#include <array>
#include <cstddef>
#include <cstring>
#include <memory>
#include <string_view>
#include <type_traits>
namespace dusk::mods {
template <class T>
T arg(void* argsRaw, int n) noexcept {
void** args = static_cast<void**>(argsRaw);
return *static_cast<std::add_pointer_t<std::remove_reference_t<T> > >(args[n]);
}
template <class T>
std::remove_reference_t<T>& arg_ref(void* argsRaw, int n) noexcept {
void** args = static_cast<void**>(argsRaw);
return *static_cast<std::add_pointer_t<std::remove_reference_t<T> > >(args[n]);
}
template <class F>
void* mfp_addr(F fn) noexcept {
void* p = nullptr;
static_assert(sizeof(fn) >= sizeof(void*), "unexpected function pointer size");
std::memcpy(&p, &fn, sizeof(void*));
return p;
}
/* A string usable as a template argument: carries the hook target's symbol name and
* makes each NamedHook instantiation's static state unique. */
template <size_t N>
struct FixedString {
char chars[N]{};
constexpr FixedString(const char (&s)[N]) noexcept {
for (size_t i = 0; i < N; ++i) {
chars[i] = s[i];
}
}
};
namespace detail {
template <class T>
constexpr std::string_view class_name() {
#if defined(__clang__) || defined(__GNUC__)
// "... class_name() [T = daAlink_c]" / "... [with T = daAlink_c; ...]"
constexpr std::string_view fn = __PRETTY_FUNCTION__;
constexpr size_t start = fn.find("T = ") + 4;
return fn.substr(start, fn.find_first_of(";]", start) - start);
#elif defined(_MSC_VER)
// "... class_name<class daAlink_c>(void)"
constexpr std::string_view fn = __FUNCSIG__;
constexpr size_t start = fn.find("class_name<") + 11;
constexpr std::string_view name = fn.substr(start, fn.rfind(">(") - start);
if constexpr (name.starts_with("class ")) {
return name.substr(6);
} else if constexpr (name.starts_with("struct ")) {
return name.substr(7);
} else {
return name;
}
#else
#error "unsupported compiler"
#endif
}
/* The manifest name of C's vtable. Only unscoped, non-template class names are
* supported (an empty result fails resolution and the install reports it). */
template <class C>
constexpr auto vtable_symbol() {
constexpr std::string_view name = class_name<C>();
constexpr bool simple = name.find_first_of(":<> ") == std::string_view::npos;
// "_ZTV" + decimal length + name / "??_7" + name + "@@6B@", NUL-terminated
std::array<char, name.size() + 12> out{};
if constexpr (!simple) {
return out;
}
size_t n = 0;
#if defined(_WIN32)
for (char c : {'?', '?', '_', '7'}) {
out[n++] = c;
}
for (char c : name) {
out[n++] = c;
}
for (char c : {'@', '@', '6', 'B', '@'}) {
out[n++] = c;
}
#else
for (char c : {'_', 'Z', 'T', 'V'}) {
out[n++] = c;
}
size_t len = name.size();
char digits[8]{};
size_t d = 0;
while (len != 0) {
digits[d++] = static_cast<char>('0' + len % 10);
len /= 10;
}
while (d != 0) {
out[n++] = digits[--d];
}
for (char c : name) {
out[n++] = c;
}
#endif
return out;
}
#if defined(_WIN32)
/* Follow jump stubs, then match the MSVC vcall thunk a virtual mfp points at.
* Returns the vtable slot's byte offset, or npos when fn is not a vcall thunk. */
inline size_t vcall_slot_offset(const void*& fn) noexcept {
constexpr size_t npos = static_cast<size_t>(-1);
#if defined(_M_X64) || defined(__x86_64__)
const auto* p = static_cast<const uint8_t*>(fn);
for (int i = 0; i < 8 && p[0] == 0xE9; ++i) { // incremental-link stubs
int32_t rel;
std::memcpy(&rel, p + 1, 4);
p += 5 + rel;
}
fn = p;
// The vptr load. Unoptimized clang-cl thunks spill/reload rcx first
// (push rax; mov [rsp], rcx; mov rcx, [rsp]), so scan a short window.
const uint8_t* q = nullptr;
for (int i = 0; i <= 12; ++i) {
if (p[i] == 0x48 && p[i + 1] == 0x8B && p[i + 2] == 0x01) { // mov rax, [rcx]
q = p + i + 3;
break;
}
}
if (q == nullptr) {
return npos;
}
if (q[0] == 0xFF && q[1] == 0x20) { // jmp [rax] (MSVC)
return 0;
}
if (q[0] == 0xFF && q[1] == 0x60) { // jmp [rax + imm8]
return static_cast<int8_t>(q[2]);
}
if (q[0] == 0xFF && q[1] == 0xA0) { // jmp [rax + imm32]
int32_t off;
std::memcpy(&off, q + 2, 4);
return off;
}
// clang-cl: mov rax, [rax + off]; (pop r10;) jmp rax. Requiring the jmp rax
// distinguishes the thunk from an ordinary getter that begins the same way.
if (q[0] == 0x48 && q[1] == 0x8B && (q[2] == 0x00 || q[2] == 0x40 || q[2] == 0x80)) {
size_t off = 0;
const uint8_t* r = q + 3;
if (q[2] == 0x40) {
off = static_cast<int8_t>(q[3]);
r = q + 4;
} else if (q[2] == 0x80) {
int32_t off32;
std::memcpy(&off32, q + 3, 4);
off = off32;
r = q + 7;
}
for (int i = 0; i <= 8; ++i) {
if (r[i] == 0xFF && r[i + 1] == 0xE0) { // jmp rax (48 REX optional)
return off;
}
}
}
return npos;
#elif defined(_M_ARM64) || defined(__aarch64__)
const auto* p = static_cast<const uint8_t*>(fn);
uint32_t insn[3];
for (int i = 0; i < 8; ++i) { // incremental-link `b` stubs
std::memcpy(insn, p, 4);
if ((insn[0] & 0xFC000000u) != 0x14000000u) {
break;
}
const auto imm26 = static_cast<int32_t>(insn[0] << 6) >> 6;
p += static_cast<intptr_t>(imm26) * 4;
}
fn = p;
std::memcpy(insn, p, 12);
// ldr Xt, [x0]; ldr Xs, [Xt, #imm12*8]; br Xs
if ((insn[0] & 0xFFFFFFE0u) != 0xF9400000u) {
return npos;
}
const uint32_t t = insn[0] & 0x1Fu;
if ((insn[1] & 0xFFC003E0u) != (0xF9400000u | (t << 5))) {
return npos;
}
const uint32_t s = insn[1] & 0x1Fu;
if (insn[2] != (0xD61F0000u | (s << 5))) {
return npos;
}
return ((insn[1] >> 10) & 0xFFFu) * 8;
#else
(void)fn;
return npos;
#endif
}
#endif
/* Code address of the member function a mfp designates. Virtual mfps don't carry
* one; recover it from the class's vtable (resolved from the symbol manifest), so
* Hook works uniformly on virtual and non-virtual members. */
template <class C, class F>
ModResult member_target(const HookService* hooks, F mfp, void** out) {
*out = nullptr;
uintptr_t words[sizeof(F) > sizeof(uintptr_t) ? 2 : 1] = {};
std::memcpy(words, &mfp, sizeof(words) < sizeof(F) ? sizeof(words) : sizeof(F));
#if defined(_WIN32)
const void* fn = reinterpret_cast<const void*>(words[0]);
const size_t slot = vcall_slot_offset(fn);
if (slot == static_cast<size_t>(-1)) { // not a vcall thunk: direct address
*out = const_cast<void*>(fn);
return MOD_OK;
}
void* vtable = nullptr;
const ModResult resolved = hooks->resolve(mod_ctx, vtable_symbol<C>().data(), &vtable, nullptr);
if (resolved != MOD_OK) {
return resolved;
}
// ??_7 points at the first slot.
*out = *reinterpret_cast<void**>(static_cast<char*>(vtable) + slot);
#else
#if defined(__aarch64__) || defined(__arm__)
// AAPCS C++ ABI: the virtual flag is bit 0 of the adjustment word (function
// addresses can't spare their low bit), and ptr holds the slot offset directly.
const bool isVirtual = (words[1] & 1) != 0;
const uintptr_t thisAdjust = words[1] >> 1;
const uintptr_t slotOffset = words[0];
#else
// Itanium C++ ABI: virtual mfps set bit 0 of ptr; the slot offset is ptr - 1.
const bool isVirtual = (words[0] & 1) != 0;
const uintptr_t thisAdjust = words[1];
const uintptr_t slotOffset = words[0] - 1;
#endif
if (!isVirtual) { // non-virtual: the address itself
*out = reinterpret_cast<void*>(words[0]);
return MOD_OK;
}
if (thisAdjust != 0) {
// this-adjusting mfp (member of a secondary base): the slot offset is
// relative to a vtable we can't locate. Hook the overrider by name instead.
return MOD_UNSUPPORTED;
}
void* vtable = nullptr;
const ModResult resolved = hooks->resolve(mod_ctx, vtable_symbol<C>().data(), &vtable, nullptr);
if (resolved != MOD_OK) {
return resolved;
}
// _ZTV points at the offset-to-top slot; the address point mfps index from is
// two pointers in (past offset-to-top and the typeinfo pointer).
*out = *reinterpret_cast<void**>(static_cast<char*>(vtable) + 2 * sizeof(void*) + slotOffset);
#endif
return *out != nullptr ? MOD_OK : MOD_UNAVAILABLE;
}
} // namespace detail
/* Trampoline generator + per-target state shared by Hook and NamedHook. Tag makes
* each hooked target's statics distinct; the target address is filled in at install. */
template <class Tag, class R, class... A>
struct HookImpl {
static inline R (*g_orig)(A...) = nullptr;
static inline const HookService* hooks = nullptr;
static inline void* target = nullptr;
static bool dispatch_pre(void* args, void* retval) {
if (hooks == nullptr) {
return false;
}
int skipOriginal = 0;
const ModResult result = hooks->dispatch_pre(mod_ctx, target, args, retval, &skipOriginal);
return result == MOD_OK && skipOriginal != 0;
}
static void dispatch_post(void* args, void* retval) {
if (hooks != nullptr) {
hooks->dispatch_post(mod_ctx, target, args, retval);
}
}
static R trampoline(A... args) {
if constexpr (sizeof...(A) == 0) {
if constexpr (std::is_void_v<R>) {
const bool skipOriginal = dispatch_pre(nullptr, nullptr);
if (!skipOriginal) {
g_orig(args...);
}
dispatch_post(nullptr, nullptr);
} else {
R result{};
const bool skipOriginal =
dispatch_pre(nullptr, static_cast<void*>(std::addressof(result)));
if (!skipOriginal) {
result = g_orig(args...);
}
dispatch_post(nullptr, static_cast<void*>(std::addressof(result)));
return result;
}
} else {
void* ptrs[] = {static_cast<void*>(std::addressof(args))...};
if constexpr (std::is_void_v<R>) {
const bool skipOriginal = dispatch_pre(static_cast<void*>(ptrs), nullptr);
if (!skipOriginal) {
g_orig(args...);
}
dispatch_post(static_cast<void*>(ptrs), nullptr);
} else {
R result{};
const bool skipOriginal = dispatch_pre(
static_cast<void*>(ptrs), static_cast<void*>(std::addressof(result)));
if (!skipOriginal) {
result = g_orig(args...);
}
dispatch_post(static_cast<void*>(ptrs), static_cast<void*>(std::addressof(result)));
return result;
}
}
}
};
namespace detail {
template <auto Target>
using TargetTag = std::integral_constant<decltype(Target), Target>;
template <FixedString Name>
struct NameTag {};
} // namespace detail
/*
* Typed hook on a function named at compile time (&daAlink_c::execute, &free_fn).
* Member functions may be virtual: the install decodes the member function pointer and hooks the
* class's own overrider.
*/
template <auto Target>
struct Hook;
template <class C, class R, class... A, R (C::*Target)(A...)>
struct Hook<Target> : HookImpl<detail::TargetTag<Target>, R, C*, A...> {
static ModResult resolve_target(const HookService* hooks, void** out) {
return detail::member_target<C>(hooks, Target, out);
}
};
template <class C, class R, class... A, R (C::*Target)(A...) const>
struct Hook<Target> : HookImpl<detail::TargetTag<Target>, R, const C*, A...> {
static ModResult resolve_target(const HookService* hooks, void** out) {
return detail::member_target<C>(hooks, Target, out);
}
};
template <class R, class... A, R (*Target)(A...)>
struct Hook<Target> : HookImpl<detail::TargetTag<Target>, R, A...> {
static ModResult resolve_target(const HookService*, void** out) {
*out = mfp_addr(Target);
return MOD_OK;
}
};
/*
* Typed hook on a function by its symbol name, for targets you can't name in C++: file-local
* statics, private members, or symbols without a header. The signature is written free-style with
* the receiver first and is *not* compiler-checked.
*
* using HookshotHit = dusk::mods::NamedHook<
* "daAlink_hookshotAtHitCallBack",
* void(fopAc_ac_c*, dCcD_GObjInf*, fopAc_ac_c*, dCcD_GObjInf*)>;
* dusk::mods::hook_add_pre<HookshotHit>(svc_hook, on_hookshot_hit);
*/
template <FixedString Name, class Sig>
struct NamedHook;
template <FixedString Name, class R, class... A>
struct NamedHook<Name, R(A...)> : HookImpl<detail::NameTag<Name>, R, A...> {
static ModResult resolve_target(const HookService* hooks, void** out) {
HookSymbolFlags flags{};
const ModResult resolved = hooks->resolve(mod_ctx, Name.chars, out, &flags);
if (resolved == MOD_OK && (flags & HOOK_SYMBOL_CODE) == 0) {
*out = nullptr;
return MOD_INVALID_ARGUMENT;
}
return resolved;
}
};
template <class Entry>
ModResult hook_install(const HookService* hooks) {
if (hooks == nullptr) {
return MOD_UNAVAILABLE;
}
Entry::hooks = hooks;
if (Entry::target == nullptr) {
const ModResult resolved = Entry::resolve_target(hooks, &Entry::target);
if (resolved != MOD_OK) {
return resolved;
}
}
return hooks->install(mod_ctx, Entry::target, reinterpret_cast<void*>(Entry::trampoline),
reinterpret_cast<void**>(&Entry::g_orig));
}
template <auto Target>
ModResult hook_install(const HookService* hooks) {
return hook_install<Hook<Target> >(hooks);
}
template <class Entry>
ModResult hook_add_pre(
const HookService* hooks, HookPreFn callback, const HookOptions* options = nullptr) {
const ModResult installed = hook_install<Entry>(hooks);
if (installed != MOD_OK) {
return installed;
}
return hooks->add_pre(mod_ctx, Entry::target, callback, options);
}
template <auto Target>
ModResult hook_add_pre(
const HookService* hooks, HookPreFn callback, const HookOptions* options = nullptr) {
return hook_add_pre<Hook<Target> >(hooks, callback, options);
}
template <class Entry>
ModResult hook_add_post(
const HookService* hooks, HookPostFn callback, const HookOptions* options = nullptr) {
const ModResult installed = hook_install<Entry>(hooks);
if (installed != MOD_OK) {
return installed;
}
return hooks->add_post(mod_ctx, Entry::target, callback, options);
}
template <auto Target>
ModResult hook_add_post(
const HookService* hooks, HookPostFn callback, const HookOptions* options = nullptr) {
return hook_add_post<Hook<Target> >(hooks, callback, options);
}
template <class Entry>
ModResult hook_replace(
const HookService* hooks, HookReplaceFn callback, const HookOptions* options = nullptr) {
const ModResult installed = hook_install<Entry>(hooks);
if (installed != MOD_OK) {
return installed;
}
return hooks->replace(mod_ctx, Entry::target, callback, options);
}
template <auto Target>
ModResult hook_replace(
const HookService* hooks, HookReplaceFn callback, const HookOptions* options = nullptr) {
return hook_replace<Hook<Target> >(hooks, callback, options);
}
} // namespace dusk::mods
-6
View File
@@ -31,10 +31,4 @@ extern u8 __OSReport_Warning_disable;
extern u8 __OSReport_System_disable;
extern u8 __OSReport_enable;
#if TARGET_PC
namespace dusk {
DUSK_GAME_EXTERN bool OSReportReallyForceEnable;
}
#endif
#endif // _OS_REPORT_H
@@ -4,7 +4,7 @@
#include "JSystem/J2DGraph/J2DManage.h"
#include "JSystem/J2DGraph/J2DMatBlock.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-j2d
@@ -5,7 +5,7 @@
#include "JSystem/JSupport/JSUList.h"
#include <gx.h>
#include <mtx.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
class J2DAnmBase;
class J2DAnmColor;
@@ -4,7 +4,7 @@
#include "JSystem/J2DGraph/J2DPane.h"
#include "JSystem/JUtility/JUTTexture.h"
#include "JSystem/JUtility/TColor.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
class J2DMaterial;
class JUTPalette;
@@ -4,7 +4,7 @@
#include "JSystem/J2DGraph/J2DManage.h"
#include "JSystem/J2DGraph/J2DPane.h"
#include "JSystem/JUtility/TColor.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
class J2DMaterial;
class JUTNameTab;
@@ -4,7 +4,7 @@
#include <gx.h>
#include <mtx.h>
#include "global.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-j2d
@@ -3,8 +3,8 @@
#include "JSystem/J2DGraph/J2DMaterial.h"
#include "JSystem/J2DGraph/J2DPane.h"
#include "dusk/endian.h"
#include "dusk/string.hpp"
#include "helpers/endian.h"
#include "helpers/string.hpp"
class J2DMaterial;
class JUTFont;
@@ -100,7 +100,7 @@ public:
J2DTextBoxVBinding);
void private_readStream(J2DPane*, JSURandomInputStream*, JKRArchive*);
TEXT_SPAN getStringPtr() const;
dusk::TextSpan getSpan() const;
TextSpan getSpan() const;
s32 setString(s16, char const*, ...);
s32 setString(char const*, ...);
@@ -6,7 +6,7 @@
#include <mtx.h>
#include "global.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
#if TARGET_PC
#define OFFSET_PTR_V0 BE(u32)
@@ -3,7 +3,7 @@
#include "JSystem/J3DAssert.h"
#include "JSystem/J3DGraphLoader/J3DClusterLoader.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
class J3DDeformer;
class J3DClusterKey;
@@ -3,7 +3,7 @@
#include "JSystem/J3DAssert.h"
#include "JSystem/J3DGraphBase/J3DTransform.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
class J3DJoint;
class J3DMtxBuffer;
@@ -3,7 +3,6 @@
#include "JSystem/J3DGraphAnimator/J3DSkinDeform.h"
#include "JSystem/J3DGraphBase/J3DPacket.h"
#include "dusk/frame_interpolation.h"
#include <types.h>
enum J3DMdlFlag {
@@ -106,12 +105,13 @@ public:
void setUserArea(uintptr_t area) { mUserArea = area; }
uintptr_t getUserArea() const { return mUserArea; }
Vec* getBaseScale() { return &mBaseScale; }
#if TARGET_PC
void setAnmMtx(int jointNo, Mtx m);
#else
void setAnmMtx(int jointNo, Mtx m) {
mMtxBuffer->setAnmMtx(jointNo, m);
#ifdef TARGET_PC
dusk::frame_interp::record_final_mtx(mMtxBuffer->getAnmMtx(jointNo));
#endif
}
#endif
MtxP getAnmMtx(int jointNo) { return mMtxBuffer->getAnmMtx(jointNo); }
MtxP getWeightAnmMtx(int i) { return mMtxBuffer->getWeightAnmMtx(i); }
J3DSkinDeform* getSkinDeform() { return mSkinDeform; }
@@ -7,7 +7,7 @@
#include "JSystem/JMath/JMath.h"
#include "global.h"
#include <mtx.h>
#include "dusk/endian_gx.hpp"
#include "helpers/endian_gx.hpp"
class J3DShapeMtx;
@@ -4,7 +4,7 @@
#include "JSystem/J3DGraphBase/J3DShape.h"
#include "JSystem/J3DAssert.h"
#include <mtx.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
class J3DTexMtx;
class J3DTexGenBlock;
@@ -7,7 +7,7 @@
#include "global.h"
#include "JSystem/JMath/JMath.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-j3d
@@ -6,8 +6,7 @@
#include "JSystem/J3DAssert.h"
#include "JSystem/JMath/JMath.h"
#include "dusk/frame_interpolation.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
#include "global.h"
enum J3DSysDrawBuf {
@@ -190,15 +189,13 @@ struct J3DSys {
Mtx& getModelDrawMtx(u16 no) { return mModelDrawMtx[no]; }
J3DShapePacket* getShapePacket() { return mShapePacket; }
#if TARGET_PC
void setViewMtx(const Mtx m);
#else
void setViewMtx(const Mtx m) {
#ifdef TARGET_PC
Mtx patched;
if (dusk::frame_interp::lookup_replacement(m, patched)) {
m = patched;
}
#endif
MTXCopy(m, mViewMtx);
}
#endif
J3DModel* getModel() { return mModel; }
@@ -7,7 +7,7 @@
#include "global.h"
#include <stdint.h>
#include "dusk/gx_helper.h"
#include "helpers/gx_helper.h"
/**
* @ingroup jsystem-j3d
@@ -3,7 +3,7 @@
#include <gx.h>
#include <mtx.h>
#include "dusk/endian_gx.hpp"
#include "helpers/endian_gx.hpp"
class J3DModel;
class J3DAnmVtxColor;
@@ -4,7 +4,7 @@
#include "JSystem/J3DGraphAnimator/J3DAnimation.h"
#include "JSystem/J3DGraphAnimator/J3DAnimation.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
#if TARGET_PC
#define OFFSET_PTR_V0 BE(u32)
@@ -4,7 +4,7 @@
#include "JSystem/J3DGraphBase/J3DSys.h"
#include <mtx.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
class J3DModelData;
class J3DMaterialTable;
@@ -5,7 +5,7 @@
#include "JSystem/JAudio2/JAIAudible.h"
#include "JSystem/JUtility/JUTAssert.h"
#include "global.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
#include <cstdint>
class JAISound;
@@ -4,7 +4,7 @@
#include "JSystem/JAudio2/JASTaskThread.h"
#include "JSystem/JUtility/JUTAssert.h"
#include <dvd.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
class JASChannel;
@@ -2,7 +2,7 @@
#define JASOSCILLATOR_H
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-jaudio
@@ -2,7 +2,7 @@
#define JAUAUDIBLEPARAM_H
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-jaudio
@@ -2,7 +2,7 @@
#define JAUAUDIOARCINTERPRETER_H
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-jaudio
@@ -2,7 +2,7 @@
#define JAUSOUNDANIMATOR_H
#include "JSystem/JAudio2/JAISound.h"
#include "dusk/offset_ptr.h"
#include "helpers/offset_ptr.h"
class JAUSoundAnimation;
@@ -3,7 +3,7 @@
#include "JSystem/JAudio2/JAISound.h"
#include "JSystem/JAudio2/JASGadget.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-jaudio
@@ -3,7 +3,7 @@
#include "JSystem/JUtility/JUTAssert.h"
#include "JSystem/JGadget/search.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
namespace JGadget {
namespace binary {
@@ -2,7 +2,7 @@
#define JHICOMMONMEM_H
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
inline u32 JHIhtonl(u32 v) {
return BSWAP32(v);
@@ -4,7 +4,7 @@
#include "JSystem/JKernel/JKRCompression.h"
#include "JSystem/JKernel/JKRFileLoader.h"
#include "global.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
class JKRHeap;
+1 -1
View File
@@ -4,7 +4,7 @@
#include <mtx.h>
#include <cmath>
#include "dusk/math.h"
#include "helpers/math.h"
typedef f32 Mtx33[3][3];
typedef f32 Mtx23[2][3];
@@ -6,7 +6,7 @@
#else
#include <dolphin.h>
#endif
#include "dusk/endian.h"
#include "helpers/endian.h"
// Struct definitions might be wrong
typedef struct bmg_header_t {
@@ -4,7 +4,7 @@
#include "JSystem/JGeometry.h"
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
struct JPAEmitterWorkData;
@@ -2,7 +2,7 @@
#define JPARESOURCE_H
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
class JKRHeap;
struct JPAEmitterWorkData;
@@ -6,7 +6,7 @@
#define m_PI_D 3.141592653589793
#ifndef __MWERKS__
#include "dusk/math.h"
#include "helpers/math.h"
#endif
namespace JStudio {
@@ -2,7 +2,7 @@
#define JSUINPUTSTREAM_H
#include "JSystem/JSupport/JSUIosBase.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-jsupport
@@ -3,7 +3,7 @@
#include "JSystem/JUtility/TColor.h"
#include <cstring>
#include "dusk/endian.h"
#include "helpers/endian.h"
#if TARGET_PC
struct FontDrawContext {
@@ -2,7 +2,7 @@
#define JUTNAMETAB_H
#include <types.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
/**
* @ingroup jsystem-jutility
@@ -3,7 +3,7 @@
#include <gx.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
enum JUTTransparency { UNK0, UNK1 };
@@ -2,7 +2,7 @@
#define JUTRESFONT_H
#include "JSystem/JUtility/JUTFont.h"
#include "dusk/gx_helper.h"
#include "helpers/gx_helper.h"
class JKRHeap;
@@ -3,8 +3,8 @@
#include <gx.h>
#include <stdint.h>
#include "dusk/endian.h"
#include "dusk/gx_helper.h"
#include "helpers/endian.h"
#include "helpers/gx_helper.h"
class JUTPalette;
@@ -2,7 +2,7 @@
#define TCOLOR_H
#include <gx.h>
#include "dusk/endian.h"
#include "helpers/endian.h"
namespace JUtility {
@@ -8,7 +8,7 @@
#include <cstring>
#include <types.h>
#include "dusk/string.hpp"
#include "helpers/string.hpp"
J2DMaterialFactory::J2DMaterialFactory(J2DMaterialBlock const& param_0) {
mMaterialNum = param_0.field_0x8;
+1 -1
View File
@@ -8,7 +8,7 @@
#include "JSystem/JSupport/JSURandomInputStream.h"
#include "JSystem/JUtility/JUTResource.h"
#ifndef __MWERKS__
#include "dusk/math.h"
#include "helpers/math.h"
#endif
J2DPane::J2DPane() : mBounds(), mGlobalBounds(), mClipRect(), mPaneTree(this) {
+1 -1
View File
@@ -9,7 +9,7 @@
#ifdef __MWERKS__
#include <cmath>
#else
#include <dusk/math.h>
#include <helpers/math.h>
#endif
#include <gx.h>
+1 -1
View File
@@ -3,7 +3,7 @@
#include "JSystem/J2DGraph/J2DWindowEx.h"
#include "JSystem/JUtility/JUTTexture.h"
#include "JSystem/JSupport/JSURandomInputStream.h"
#include "dusk/endian.h"
#include "helpers/endian.h"
struct J2DWindowExDef {
BE(u32) field_0x0[4];
@@ -8,7 +8,10 @@
#include "JSystem/J3DGraphBase/J3DShapeMtx.h"
#include "JSystem/J3DGraphBase/J3DSys.h"
#include "JSystem/JKernel/JKRHeap.h"
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#endif
#define J3D_ASSERTMSG(LINE, COND, MSG) JUT_ASSERT_MSG(LINE, (COND) != 0, MSG)
#define J3D_WARN1(LINE, MSG, ARG1) JUT_WARN(LINE, MSG, ARG1)
@@ -105,6 +108,11 @@ void J3DModel::interp_callback(bool isSimFrame, void* pUserWork) {
i_this->diff();
}
}
void J3DModel::setAnmMtx(int jointNo, Mtx m) {
mMtxBuffer->setAnmMtx(jointNo, m);
dusk::frame_interp::record_final_mtx(mMtxBuffer->getAnmMtx(jointNo));
}
#endif
s32 J3DModel::createShapePacket(J3DModelData* pModelData) {
+15 -1
View File
@@ -4,10 +4,14 @@
#include "JSystem/J3DGraphBase/J3DSys.h"
#include "JSystem/J3DGraphBase/J3DTevs.h"
#include "JSystem/J3DGraphBase/J3DTexture.h"
#include "dusk/gx_helper.h"
#include "helpers/gx_helper.h"
#include "global.h"
#include "tracy/Tracy.hpp"
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#endif
DUSK_GAME_DATA J3DSys j3dSys;
DUSK_GAME_DATA Mtx J3DSys::mCurrentMtx;
@@ -370,3 +374,13 @@ void J3DSys::reinitPixelProc() {
GXSetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
GXSetZCompLoc(GX_TRUE);
}
#if TARGET_PC
void J3DSys::setViewMtx(const Mtx m) {
Mtx patched;
if (dusk::frame_interp::lookup_replacement(m, patched)) {
m = patched;
}
MTXCopy(m, mViewMtx);
}
#endif
+1 -1
View File
@@ -2,7 +2,7 @@
#include <JSystem/JUtility/JUTAssert.h>
#include <cstring>
#include "dusk/string.hpp"
#include "helpers/string.hpp"
DUSK_GAME_DATA u32 JAHVirtualNode::smVirNodeNum;
+1 -1
View File
@@ -6,7 +6,7 @@
#include "JSystem/JAHostIO/JAHioNode.h"
#include "JSystem/JHostIO/JORServer.h"
#include "dusk/string.hpp"
#include "helpers/string.hpp"
DUSK_GAME_DATA JAHioNode* JAHioNode::smCurrentNode;
@@ -9,7 +9,7 @@
#include <os.h>
#include <stdint.h>
#include "dusk/string.hpp"
#include "helpers/string.hpp"
DUSK_GAME_DATA JASHeap* JASWaveArcLoader::sAramHeap;
+2 -1
View File
@@ -14,11 +14,12 @@
#ifdef TARGET_PC
#include "dusk/dusk.h"
#include "dusk/gx_helper.h"
#include "dusk/frame_interpolation.h"
#include "dusk/logging.h"
#include "dusk/settings.h"
#include "dusk/time.h"
#include "f_op/f_op_overlap_mng.h"
#include "helpers/gx_helper.h"
#include "SDL3/SDL_timer.h"
#include "tracy/Tracy.hpp"
+1 -1
View File
@@ -8,7 +8,7 @@
#include <cctype>
#include <cstring>
#include "dusk/string.hpp"
#include "helpers/string.hpp"
#include "global.h"
JKRFileCache* JKRFileCache::mount(const char* path, JKRHeap* heap, const char* param_3) {
+1 -1
View File
@@ -8,7 +8,7 @@
#include <cstring>
#include <string>
#include "JSystem/JKernel/JKRHeap.h"
#include "dusk/string.hpp"
#include "helpers/string.hpp"
#include "global.h"
DUSK_GAME_DATA JKRFileLoader* JKRFileLoader::sCurrentVolume;
+2 -2
View File
@@ -15,7 +15,7 @@
#include "JSystem/JUtility/JUTAssert.h"
#include "JSystem/JUtility/JUTException.h"
#include "dusk/string.hpp"
#include "helpers/string.hpp"
#ifdef __MWERKS__
#include <stdint.h>
#else
@@ -702,7 +702,7 @@ JKRHeap* JKRHeap::getCurrentHeap() {
}
void JKRHeap::setName(const char* name) {
dusk::SafeStringCopyTruncate(mName, name);
SafeStringCopyTruncate(mName, name);
}
void JKRHeap::setNamef(const char* fmt, ...) {
+1 -1
View File
@@ -7,7 +7,7 @@
#include "global.h"
#include <stdint.h>
#include "dusk/string.hpp"
#include "helpers/string.hpp"
#if TARGET_PC
#include "dusk/os.h"

Some files were not shown because too many files have changed in this diff Show More