Compare commits

...

7 Commits

Author SHA1 Message Date
Irastris 48de4bcb08 Gradle build revisions (#2229)
* Gradle build revisions

* Update platforms/android/README.md
2026-07-28 23:47:13 -06:00
Pieter-Jan Briers 0cf130d834 symgen: specify EXPECTED_HASH to cmake to avoid redownload (#2244) 2026-07-28 23:39:59 -06:00
Luke Street 38459a97b3 Fix compile error 2026-07-28 23:38:49 -06:00
Jack Wines b6b297f1ac Nix: add miniz and fix submodules (#2220)
* nix: add miniz to dependencies

* nix: enable parallel building

* amend! nix: add miniz to dependencies

nix: add miniz to dependencies

* nix: remove enableParellelBulding, needSubmodules

The flake now includes submodules in inputs, so we no longer
need to throw an error when they aren't there.

EnableParallelBuilding was added in error, cmake already compiles in
parallel when under nix.
2026-07-28 23:25:43 -06:00
Giorgio Mendieta 4d688a8507 Draw shadows before translucent textures (#2206)
* Implement smooth fadeout of dynamic shadows

* Add edge fade out configuration for dynamic shadows

* Tweak default and max values for edge fade out

* Add stage hook for deferred composite after opaque scene draws

* Bump mod version
2026-07-28 23:25:15 -06:00
Luke Street 64789aa5fc Update symgen (fixes AppImage builds) 2026-07-28 19:27:35 -06:00
Luke Street f0e6b88cfd GCC mod section fix 2026-07-28 19:26:53 -06:00
10 changed files with 215 additions and 185 deletions
-3
View File
@@ -208,9 +208,6 @@ jobs:
- 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
- name: Build APK
working-directory: platforms/android
run: ./gradlew :app:assembleRelease --rerun-tasks
+25 -4
View File
@@ -2,40 +2,50 @@ include_guard(GLOBAL)
get_filename_component(_SYMBOL_MANIFEST_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
set(_SYMGEN_VERSION "1.3.1")
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)
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)
@@ -54,7 +64,7 @@ function(ensure_symgen required)
return()
endif ()
else ()
symgen_host_asset(_asset)
symgen_host_asset(_asset _asset_hash)
if (_asset STREQUAL "")
if (required)
message(FATAL_ERROR "symgen: no prebuilt binary for host "
@@ -75,7 +85,8 @@ function(ensure_symgen required)
file(DOWNLOAD "${_url}" "${_symgen}"
TLS_VERIFY ON
STATUS _download_status
SHOW_PROGRESS)
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)
@@ -106,6 +117,16 @@ function(setup_symbol_manifest target)
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 ()
+18 -27
View File
@@ -2,6 +2,7 @@
description = "Dusklight native PC port of the Twilight Princess decompilation";
inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
inputs.self.submodules = true;
outputs =
{ self, nixpkgs }:
@@ -58,21 +59,6 @@
hasNodPrebuilt = nodPrebuiltInfo ? ${system};
aurora = builtins.pathExists "${self}/extern/aurora/CMakeLists.txt";
needSubmodules = ''
dusklight: The aurora submodule is not vendored. Add submodules=1 to build.
As a flake input:
dusklight.url = "git+https://github.com/TwilitRealm/dusklight?ref=main&submodules=1";
nix command:
nix run 'git+https://github.com/TwilitRealm/dusklight?submodules=1'
Local checkout:
nix run '.?submodules=1#dusklight'
'';
dawn = pkgs.fetchzip {
url = "https://github.com/encounter/dawn/releases/download/${dawnVersion}/dawn-${dawnInfo.${system}.triple}.tar.gz";
@@ -140,6 +126,14 @@
JSON = pkgs.nlohmann_json.src;
XXHASH = pkgs.xxhash.src;
ZSTD = pkgs.zstd.src;
MINIZ = pkgs.fetchzip {
url = "https://github.com/richgel999/miniz/releases/download/3.0.2/miniz-3.0.2.zip";
hash = "sha256-DXysXkQEmoDAMMg1F8KexkwpXNyiHNzLJqXR9SMEkxk=";
stripRoot = false;
};
FMT = pkgs.fetchzip {
url = "https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz";
hash = "sha256-ZmI1Dv0ZabPlxa02OpERI47jp7zFfjpeWCy1WyuPYZ0=";
@@ -165,19 +159,16 @@
};
dusklight =
if !aurora then
throw needSubmodules
else
pkgs.stdenv.mkDerivation {
pname = "dusklight";
version = versionSuffix;
src = ./.;
pkgs.stdenv.mkDerivation {
pname = "dusklight";
version = versionSuffix;
src = ./.;
postUnpack = ''
chmod -R u+w "$sourceRoot"
substituteInPlace "$sourceRoot/extern/aurora/CMakeLists.txt" \
--replace-warn "add_subdirectory(tests)" ""
'';
postUnpack = ''
chmod -R u+w "$sourceRoot"
substituteInPlace "$sourceRoot/extern/aurora/CMakeLists.txt" \
--replace-warn "add_subdirectory(tests)" ""
'';
nativeBuildInputs = [
pkgs.cmake
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "dev.twilitrealm.shadow_mod",
"name": "[Demo] Dynamic Shadows",
"version": "1.0.0",
"version": "1.0.1",
"author": "encounter",
"description": "Demo showcasing dynamic shadow maps: re-renders geometry from the sun (or moon) point of view and composites real-time shadows over the world, with screen-space contact shadows for fine detail."
}
+19 -6
View File
@@ -62,6 +62,7 @@ ConfigVarHandle g_cvarDebugView = 0;
GfxDrawTypeHandle g_drawType = 0;
GfxStageHookHandle g_sceneBeginHook = 0;
GfxStageHookHandle g_sceneAfterTerrainHook = 0;
GfxStageHookHandle g_sceneAfterOpaqueHook = 0;
GfxStageHookHandle g_frameBeforeHudHook = 0;
UiWindowHandle g_controlsWindow = 0;
ResourceBuffer g_shaderSource = RESOURCE_BUFFER_INIT;
@@ -749,8 +750,8 @@ void render_shadow_map(
g_mapPass.ready = true;
}
// Game thread, after the full 3D scene: deferred composite.
void on_frame_before_hud(ModContext*, const GfxStageContext*, void*) {
// Game thread, after opaque scene draws and before translucent/fog overlays: deferred composite.
void on_scene_after_opaque(ModContext*, const GfxStageContext*, void*) {
const int64_t debugMode = get_debug_mode();
restore_actual_light_debug();
@@ -797,7 +798,7 @@ void on_frame_before_hud(ModContext*, const GfxStageContext*, void*) {
uniforms.inv_size[0] = 1.0f / uniforms.size[0];
uniforms.inv_size[1] = 1.0f / uniforms.size[1];
uniforms.edge_fade_width =
static_cast<float>(std::clamp<int64_t>(get_int_option(g_cvarEdgeFadeWidth, 32), 0, 128));
static_cast<float>(std::clamp<int64_t>(get_int_option(g_cvarEdgeFadeWidth, 32), 0, 256));
uniforms.strength =
mapPass.fade *
static_cast<float>(std::clamp<int64_t>(get_int_option(g_cvarStrength, 45), 0, 100)) /
@@ -817,6 +818,11 @@ void on_frame_before_hud(ModContext*, const GfxStageContext*, void*) {
svc_gfx->push_draw(mod_ctx, g_drawType, &payload, sizeof(payload));
}
// Frame tail hook: only needed to restore light-view debug camera state before HUD.
void on_frame_before_hud(ModContext*, const GfxStageContext*, void*) {
restore_actual_light_debug();
}
void add_control(UiElementHandle pane, const UiControlDesc& desc) {
svc_ui->pane_add_control(mod_ctx, pane, &desc, nullptr);
}
@@ -873,7 +879,7 @@ ModResult build_controls_tab(
"dynamic shadows. This can be expensive.");
add_number(left, "Coverage", g_cvarBoxRadius, 1000, 20000, 500, nullptr,
"Radius of the shadowed area around the camera, in world units. Smaller is sharper.");
add_number(left, "Fade Out", g_cvarEdgeFadeWidth, 0, 128, 32, " texels",
add_number(left, "Fade Out", g_cvarEdgeFadeWidth, 0, 256, 32, " texels",
"Fade out shadows gradually near the edge of the coverage area.");
svc_ui->pane_add_section(mod_ctx, left, "Appearance");
@@ -1000,7 +1006,7 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
if (result != MOD_OK) {
return result;
}
result = register_int_option("edgeFadeWidth", 32, g_cvarEdgeFadeWidth, error);
result = register_int_option("edgeFadeWidth", 128, g_cvarEdgeFadeWidth, error);
if (result != MOD_OK) {
return result;
}
@@ -1041,6 +1047,12 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
{
return mods::set_error(error, MOD_ERROR, "failed to register stage hook");
}
stageDesc.callback = on_scene_after_opaque;
if (svc_gfx->register_stage_hook(
mod_ctx, GFX_STAGE_SCENE_AFTER_OPAQUE, &stageDesc, &g_sceneAfterOpaqueHook) != MOD_OK)
{
return mods::set_error(error, MOD_ERROR, "failed to register stage hook");
}
stageDesc.callback = on_frame_before_hud;
if (svc_gfx->register_stage_hook(
mod_ctx, GFX_STAGE_FRAME_BEFORE_HUD, &stageDesc, &g_frameBeforeHudHook) != MOD_OK)
@@ -1099,7 +1111,8 @@ MOD_EXPORT ModResult mod_shutdown(ModError*) {
g_cvarStrength = 0;
g_cvarPcf = g_cvarBias = g_cvarBoxRadius = g_cvarEdgeFadeWidth = g_cvarContactShadows =
g_cvarDebugView = 0;
g_drawType = g_sceneBeginHook = g_sceneAfterTerrainHook = g_frameBeforeHudHook = 0;
g_drawType = g_sceneBeginHook = g_sceneAfterTerrainHook = g_sceneAfterOpaqueHook =
g_frameBeforeHudHook = 0;
g_controlsWindow = 0;
g_mapPass = {};
g_sceneCamera.valid = false;
+1 -18
View File
@@ -21,26 +21,9 @@ export JAVA_HOME="/usr/lib/jvm/java-17-openjdk"
```bash
cmake --preset android-arm64
cmake --build --preset android-arm64
cmake --preset android-x86_64
cmake --build --preset android-x86_64
```
These builds produce:
- `build/android-arm64/Binaries/libmain.so`
- `build/android-x86_64/Binaries/libmain.so`
## Stage Libraries Into APK Project
```bash
./android/scripts/stage-jni-libs.sh
```
This copies:
- `libmain.so` -> `android/app/src/main/jniLibs/arm64-v8a/`
- `libmain.so` -> `android/app/src/main/jniLibs/x86_64/`
This build produces `build/android-arm64/libmain.so`
## Refresh SDL Java Shim (Optional)
+100 -11
View File
@@ -6,23 +6,107 @@ def versionNameStr = (System.getenv("DUSK_VERSION") ?: "v0.1.0").replaceFirst("^
def versionCodeInt = (System.getenv("DUSK_VERSION_CODE") ?: "100000").toInteger()
def duskRepoDir = rootProject.projectDir.parentFile.parentFile
def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusklight').get().asFile
def androidNativeBuildDir = new File(duskRepoDir, 'build/android-arm64')
def nativeLibrary = new File(androidNativeBuildDir, 'libmain.so')
def stageStripValue = providers.gradleProperty('ANDROID_STAGE_STRIP')
.orElse(providers.gradleProperty('androidStageStrip'))
.orElse(providers.environmentVariable('ANDROID_STAGE_STRIP'))
.orElse('1')
def androidHome = {
def sdkPath = System.getenv('ANDROID_HOME')
if (!sdkPath) {
throw new GradleException('ANDROID_HOME is not available')
}
def sdkDir = new File(sdkPath)
if (!sdkDir.isDirectory()) {
throw new GradleException("ANDROID_HOME points to a missing directory: ${sdkDir}")
}
sdkDir
}.memoize()
def androidNdkVersion = {
def ndkVersion = System.getenv('ANDROID_NDK_VERSION')
if (!ndkVersion) {
throw new GradleException('ANDROID_NDK_VERSION is not available')
}
ndkVersion
}.memoize()
def androidNdkDir = {
def ndkDir = new File(androidHome(), "ndk/${androidNdkVersion()}")
if (!new File(ndkDir, 'build/cmake/android.toolchain.cmake').isFile()) {
throw new GradleException(
"Android NDK ${androidNdkVersion()} is missing or invalid at ${ndkDir}"
)
}
ndkDir
}.memoize()
def llvmPrebuiltDir = {
def prebuiltRoot = new File(androidNdkDir(), 'toolchains/llvm/prebuilt')
def prebuiltDir = (prebuiltRoot.listFiles()?.findAll { it.isDirectory() } ?: [])
.sort { it.name }
.find { candidate ->
new File(
candidate,
'sysroot/usr/lib/aarch64-linux-android/libc++_shared.so'
).isFile()
}
if (!prebuiltDir) {
throw new GradleException("Cannot find NDK libc++_shared.so under ${prebuiltRoot}")
}
prebuiltDir
}.memoize()
def stlLibrary = {
def library = new File(
llvmPrebuiltDir(),
'sysroot/usr/lib/aarch64-linux-android/libc++_shared.so'
)
if (!library.isFile()) {
throw new GradleException("libc++_shared.so is missing")
}
library
}.memoize()
def stagedJniLibsDir = layout.buildDirectory.dir('generated/jniLibs/dusklight')
def stageJniLibs = tasks.register('stageJniLibs', Sync) {
group = 'build'
from(nativeLibrary) {
rename { 'libmain.so' }
into 'arm64-v8a'
}
from(providers.provider { stlLibrary() }) {
rename { 'libc++_shared.so' }
into 'arm64-v8a'
}
into(stagedJniLibsDir)
doFirst {
if (!nativeLibrary.isFile()) {
throw new GradleException("Native library is missing")
}
}
}
def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusklight')
def syncDuskAssets = tasks.register('syncDuskAssets', Sync) {
from(new File(duskRepoDir, 'res')) {
into 'res'
exclude '**/.DS_Store'
}
// Staged by platforms/android/scripts/stage-jni-libs.sh
from(new File(projectDir, 'src/main/bundled_mods')) {
from(new File(androidNativeBuildDir, 'bundled_mods')) {
into 'mods'
include '*.dusk'
}
into duskGeneratedAssetsDir
into(duskGeneratedAssetsDir)
}
android {
namespace 'dev.twilitrealm.dusk'
compileSdk 36
ndkVersion androidNdkVersion()
defaultConfig {
applicationId 'dev.twilitrealm.dusk'
@@ -44,16 +128,24 @@ android {
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
jniLibs.srcDirs = [stagedJniLibsDir]
assets.srcDirs = [duskGeneratedAssetsDir]
}
}
packaging {
jniLibs {
if (stageStripValue.get() == '0') {
keepDebugSymbols += '**/libmain.so'
}
}
}
splits {
abi {
enable true
reset()
include 'arm64-v8a', 'x86_64'
include 'arm64-v8a'
universalApk false
}
}
@@ -67,9 +159,6 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
tasks.configureEach { task ->
if ((task.name.startsWith('merge') && task.name.endsWith('Assets')) ||
task.name.toLowerCase().contains('lint')) {
task.dependsOn(syncDuskAssets)
}
tasks.named('preBuild').configure {
dependsOn(stageJniLibs, syncDuskAssets)
}
-115
View File
@@ -1,115 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
APP_DIR="$ROOT_DIR/platforms/android/app/src/main/jniLibs"
ANDROID_HOME_DIR="${ANDROID_HOME:-$HOME/Android/Sdk}"
ANDROID_NDK_VER="${ANDROID_NDK_VERSION:-}"
ANDROID_STAGE_ABIS="${ANDROID_STAGE_ABIS:-arm64-v8a x86_64}"
ANDROID_STAGE_STRIP="${ANDROID_STAGE_STRIP:-1}"
STRIP_TOOL=""
if [[ -z "$ANDROID_NDK_VER" ]] && [[ -d "$ANDROID_HOME_DIR/ndk" ]]; then
ANDROID_NDK_VER="$(ls -1 "$ANDROID_HOME_DIR/ndk" | sort -V | tail -n 1)"
fi
if [[ -n "$ANDROID_NDK_VER" ]]; then
case "$(uname -s)" in
Darwin) HOST_TAG="darwin-x86_64" ;;
Linux) HOST_TAG="linux-x86_64" ;;
*) HOST_TAG="" ;;
esac
PREBUILT_DIR="$ANDROID_HOME_DIR/ndk/$ANDROID_NDK_VER/toolchains/llvm/prebuilt"
if [[ -n "$HOST_TAG" && -x "$PREBUILT_DIR/$HOST_TAG/bin/llvm-strip" ]]; then
STRIP_TOOL="$PREBUILT_DIR/$HOST_TAG/bin/llvm-strip"
else
for candidate in "$PREBUILT_DIR"/*/bin/llvm-strip; do
if [[ -x "$candidate" ]]; then
STRIP_TOOL="$candidate"
break
fi
done
fi
fi
copy_lib() {
local abi="$1"
local src="$2"
local dst_dir="$APP_DIR/$abi"
local dst="$dst_dir/libmain.so"
local tmp="$dst_dir/.libmain.so.$$"
if [[ ! -f "$src" ]]; then
echo "Missing native library for $abi: $src" >&2
exit 1
fi
mkdir -p "$dst_dir"
cp -f "$src" "$tmp"
if [[ "$ANDROID_STAGE_STRIP" != "0" ]] && [[ -n "$STRIP_TOOL" ]]; then
"$STRIP_TOOL" --strip-unneeded "$tmp"
mv -f "$tmp" "$dst"
echo "Stripped and staged $src -> $dst"
else
mv -f "$tmp" "$dst"
echo "Staged $src -> $dst (strip disabled or strip tool unavailable)"
fi
}
# Drop any previously staged ABI directories to avoid stale APK contents.
rm -rf "$APP_DIR/x86" "$APP_DIR/arm64-v8a" "$APP_DIR/x86_64"
for abi in $ANDROID_STAGE_ABIS; do
case "$abi" in
arm64-v8a)
src="$ROOT_DIR/build/android-arm64/libmain.so"
triple="aarch64-linux-android"
;;
x86_64)
src="$ROOT_DIR/build/android-x86_64/libmain.so"
triple="x86_64-linux-android"
;;
*)
echo "Unsupported ABI '$abi'. Supported ABIs: arm64-v8a x86_64" >&2
exit 1
;;
esac
copy_lib "$abi" "$src"
if [[ -n "$STRIP_TOOL" ]]; then
stl="$(dirname "$STRIP_TOOL")/../sysroot/usr/lib/$triple/libc++_shared.so"
if [[ -f "$stl" ]]; then
cp -f "$stl" "$APP_DIR/$abi/libc++_shared.so"
echo "Staged $stl -> $APP_DIR/$abi/libc++_shared.so"
else
echo "Missing libc++_shared.so for $abi at $stl" >&2
exit 1
fi
else
echo "Cannot stage libc++_shared.so for $abi (NDK not found)" >&2
exit 1
fi
done
# Stage bundled mod packages into the app's assets source dir.
MODS_STAGING_DIR="$ROOT_DIR/platforms/android/app/src/main/bundled_mods"
rm -rf "$MODS_STAGING_DIR"
mkdir -p "$MODS_STAGING_DIR"
for abi in $ANDROID_STAGE_ABIS; do
case "$abi" in
arm64-v8a) build_dir="$ROOT_DIR/build/android-arm64" ;;
x86_64) build_dir="$ROOT_DIR/build/android-x86_64" ;;
esac
[[ -d "$build_dir/bundled_mods" ]] || continue
for pkg in "$build_dir/bundled_mods"/*.dusk; do
[[ -f "$pkg" ]] || continue
name="$(basename "$pkg")"
if [[ ! -f "$MODS_STAGING_DIR/$name" ]]; then
cp -f "$pkg" "$MODS_STAGING_DIR/$name"
echo "Staged bundled mod $pkg"
else
stage_dir="$build_dir/mods/${name%.dusk}/${name%.dusk}_stage"
(cd "$stage_dir" && zip -q -r "$MODS_STAGING_DIR/$name" lib)
echo "Appended $abi libraries to bundled mod $name"
fi
done
done
+9
View File
@@ -139,6 +139,14 @@ struct NamedHook<Name, R(A...)> : HookImpl<detail::NameTag<Name>, R, A...> {};
* leading underscore) or the demangled qualified display name; overloaded display names are
* ambiguous and need the mangled form.
*/
#if defined(__GNUC__) && !defined(__clang__) && defined(__ELF__)
#define DEFINE_HOOK(target, alias) \
MOD_META_RECORD static constinit auto mod_meta_hook_##alias = \
::mods::detail::make_local_hook_record<(target), ::mods::FixedString{#target}>(); \
struct alias : ::mods::Hook<(target)> { \
static void* resolved_target() { return mod_meta_hook_##alias.resolved; } \
}
#else
#define DEFINE_HOOK(target, alias) \
[[maybe_unused]] static const void* const mod_meta_hook_##alias = \
&::mods::detail::HookRecordFor<(target), ::mods::FixedString{#target}>::Holder::record; \
@@ -148,6 +156,7 @@ struct NamedHook<Name, R(A...)> : HookImpl<detail::NameTag<Name>, R, A...> {};
::mods::FixedString{#target}>::Holder::record.resolved; \
} \
}
#endif
#define DEFINE_HOOK_SYMBOL(name, sig, alias) \
MOD_META_RECORD static constinit auto mod_meta_hook_##alias = \
+42
View File
@@ -241,6 +241,48 @@ consteval auto make_hook_mem_names() {
return r;
}
#if defined(__GNUC__) && !defined(__clang__) && defined(__ELF__)
/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41091 prevents inline static template members from
* sharing an explicit ELF section with ordinary variables. GCC can instead constant-evaluate a
* file-local record at each DEFINE_HOOK. */
template <auto Target>
void materialize_hook_mem(unsigned char* outPmf) {
const auto target = Target;
std::memcpy(outPmf, &target, sizeof(target));
}
template <auto Target, FixedString Disp>
consteval auto make_local_hook_record() {
using F = decltype(Target);
if constexpr (std::is_member_function_pointer_v<F>) {
constexpr auto names = make_hook_mem_names<Target, Disp>();
static_assert(sizeof(F) <= MOD_META_HOOK_MEM_EXT_CAPACITY,
"unsupported pointer-to-member representation");
if constexpr (sizeof(F) > MOD_META_HOOK_MEM_CAPACITY) {
HookMemExtRecord<names.len> record = {
{sizeof(HookMemExtRecord<names.len>), MOD_META_HOOK_MEM_EXT, 0}, sizeof(F),
materialize_hook_mem<Target>, nullptr, {}};
for (size_t i = 0; i < names.len; ++i) {
record.names[i] = names.chars[i];
}
return record;
} else {
HookMemRecord<F, names.len> record = {
{sizeof(HookMemRecord<F, names.len>), MOD_META_HOOK_MEM, 0}, 0, {Target}, nullptr,
{}};
for (size_t i = 0; i < names.len; ++i) {
record.names[i] = names.chars[i];
}
return record;
}
} else {
static_assert(std::is_pointer_v<F> && std::is_function_v<std::remove_pointer_t<F>>,
"hook target must be a function or member function");
return HookFnRecord<F>{{sizeof(HookFnRecord<F>), MOD_META_HOOK_FN, 0}, 0, Target, nullptr};
}
}
#endif
/*
* MSVC constant-evaluates a compact pointer-to-member only when every other operand in the
* initializer is a literal: no consteval calls, constexpr-object copies, or default member