From 35fd461720c1f7111ef051e3bfa9dcd23af3b36f Mon Sep 17 00:00:00 2001 From: gamer147 Date: Fri, 31 Jul 2026 09:38:01 -0400 Subject: [PATCH] Add Linux x64 FFmpeg native bundle --- .gitattributes | 1 + docs/PROJECT-STRUCTURE.md | 4 +- docs/platform-portability.md | 43 +++--- docs/tools-reference.md | 17 ++- .../age_movie_ffmpeg/bootstrap-linux-x64.sh | 65 +++++++++ native/age_movie_ffmpeg/build-linux-x64.sh | 131 ++++++++++++++++++ .../dependency-linux-x64.json | 12 ++ 7 files changed, 248 insertions(+), 25 deletions(-) create mode 100755 native/age_movie_ffmpeg/bootstrap-linux-x64.sh create mode 100755 native/age_movie_ffmpeg/build-linux-x64.sh create mode 100644 native/age_movie_ffmpeg/dependency-linux-x64.json diff --git a/.gitattributes b/.gitattributes index 9f533f0..02c8bf7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ docs/opcode-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol docs/global-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol docs/engine-ctx-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol tools/age_opcodes_himegari.py whitespace=trailing-space,space-before-tab,cr-at-eol +*.sh text eol=lf diff --git a/docs/PROJECT-STRUCTURE.md b/docs/PROJECT-STRUCTURE.md index bd2e6c3..0697c8c 100644 --- a/docs/PROJECT-STRUCTURE.md +++ b/docs/PROJECT-STRUCTURE.md @@ -113,8 +113,8 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings) │ └── Age.Engine.Text.Windows/ optional exact Windows GDI gray-4 reference backend; kept │ outside the platform-neutral VM/runtime core ├── native/ authored native runtime boundaries - │ └── age_movie_ffmpeg/ project-owned FFmpeg C ABI, immutable Windows dependency manifest, - │ and bootstrap/build scripts (outputs stay under disposable build/) + │ └── age_movie_ffmpeg/ project-owned FFmpeg C ABI, immutable Windows/Linux dependency manifests, + │ and target bootstrap/build scripts (outputs stay under disposable build/) ├── tools/frida/ runtime-capture + engine-dump scripts (see tools/frida/README.md) └── godot/ DELIVERABLE — the Godot/C# ADV front-end (references Age.Engine plus the optional exact Windows text adapter). Its assembly embeds diff --git a/docs/platform-portability.md b/docs/platform-portability.md index 2ab73d5..fbf7228 100644 --- a/docs/platform-portability.md +++ b/docs/platform-portability.md @@ -27,10 +27,11 @@ The VM and content pipeline are already mostly platform-neutral: The managed FFmpeg resolver derives reserved Windows, Linux, and macOS x64/arm64 RIDs from the actual process and rejects unreserved architectures instead of silently looking in the wrong directory. -The sole movie path now uses the project-owned FFmpeg C ABI rather than a Windows multimedia API, but only a -Windows-x64 native bundle is built and staged today. The accepted DirectShow fallback was deleted after the -corpus and audible LOGO/OP/CHAPTER gates passed. There are also softer Windows assumptions that should be tested -or replaced before claiming portable exports. +The sole movie path now uses the project-owned FFmpeg C ABI rather than a Windows multimedia API. Reproducible +Windows-x64 and Linux-x64 native bundles are built and staged; the Linux ABI and complete installed corpus pass +under WSL, but an exported Linux Godot executable has not yet received window/audio/font/filesystem smoke tests. +The accepted DirectShow fallback was deleted after the corpus and audible LOGO/OP/CHAPTER gates passed. There +are also softer Windows assumptions that should be tested or replaced before claiming portable exports. ## Dependency inventory @@ -38,8 +39,8 @@ or replaced before claiming portable exports. |---|---|---|---| | SYS4INI per-game startup profile | `Sys4AssetCatalog` parses and retains the bounded ordered startup trailer; `Sys4LogicalCanvas` applies `SCREENX`/`SCREENY` with AGE's independent `640x480` fallbacks. Native SYS4INI also carries text, ADV input/skip, save ABI/path, audio, legacy renderer, and Windows registration settings | Godot uses the selected canvas for content scaling, backbuffers/compositor bounds, primary surfaces, layout/input fallbacks, and the default windowed client. `--window-width`/`--window-height` vary only the physical client while preserving the canvas and letterbox policy. The port still does not source `CancelMesSkipOnClick`, `CoexistMesSkip`, cursor/redraw policy, or wheel action ids from the trailer | Validate the existing presentation policy on Linux/macOS window managers. Apply further semantic keys explicitly, translate save roots through the host, and classify DirectDraw/fullscreen-bit and registration/key settings as native compatibility metadata. Canonical inventory and consumers: `sys4-format-notes.md` and `engine-re.md` | | Retained graphics presentation | Backend-neutral `GfxState`; accepted default Godot `Sprite2D` GPU stage plus the retained software pixel oracle, using runtime `ImageTexture`, canvas transforms/materials, and no native graphics API | GPU backend caches static/color-key variants, updates dynamic surfaces, handles retained range transitions, and falls back whole-frame for the legacy host screen-transition path | Godot owns D3D/Vulkan/Metal/OpenGL selection; validate shader/blend/filter behavior per target rather than adding a platform renderer | -| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED; `0x24d` movie masks) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 passes the complete 213-payload installed video/audio corpus gate plus audible LOGO/OP/CHAPTER playback and real TEST.AGF green-mask decode | Add target-specific native builds and export packaging | -| Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock. Opcode `0x24d` redirects decoded green bytes through the platform-neutral retained rasterizer and exact managed packed-alpha mask helper | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state. Ordinary and mask movies share VFS/FFmpeg ownership; the mask result is a backend-neutral dynamic RGBA surface consumed by either Godot renderer. Managed code is no longer Windows-annotated, while only the win-x64 native bundle exists today | Add Linux/macOS native builds and smoke gates | +| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED; `0x24d` movie masks) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 and Linux-x64 both pass the complete 213-payload installed video/audio corpus gate; Windows also passes audible LOGO/OP/CHAPTER playback and real TEST.AGF green-mask decode | Add macOS native builds, distributable source-offer packaging, and Linux/macOS Godot runtime smoke gates | +| Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock. Opcode `0x24d` redirects decoded green bytes through the platform-neutral retained rasterizer and exact managed packed-alpha mask helper | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state. Ordinary and mask movies share VFS/FFmpeg ownership; the mask result is a backend-neutral dynamic RGBA surface consumed by either Godot renderer. Managed code is no longer Windows-annotated, and target-aware builds stage either the win-x64 or Linux-x64 native bundle | Add macOS native builds and Linux/macOS in-engine smoke gates | | Movie audio | ABI v2 returns timestamped stereo float PCM; bounded managed buffering feeds a per-playback Godot `AudioStreamGenerator` and routes native movie flags to engine buses | All 29 installed audio-bearing streams decode with signal; synchronized LOGO/OP/CHAPTER playback is audibly accepted | Treat absent, distorted, or unsynchronized audio from an audio-bearing movie as a runtime bug | | ADV font discovery/raster fidelity | `Age.Engine.Text` remains OS-neutral. On compatible ACP-932 Windows, `auto` selects the separate exact `Age.Engine.Text.Windows` GDI gray-4 backend. Otherwise Godot TextServer supplies Unicode masks and metrics from ordered system-font substitutions in `godot/config/himegari-text-rendering.json`; this policy is explicitly not GDI pixel-exact. Immediate `0x204`/`0x205`, live ADV, retained History, and the wait atlas all enter ordinary RGBA surfaces/retained handles | Three Mincho/Gothic regular/bold samples byte-match an independent Unicode GDI oracle. Forced exact and forced portable self-tests cover immediate, live, and History rasterization, bounded caching, complete-line materialization, retained ordering, alpha/tint/fade, transforms, clipping, capture, HIDEWIN suspension, partial erase/republication, reset, and teardown. Gameplay `Label` pools and overlay projection are gone | Produce and smoke-test Linux/macOS exports, confirm installed family availability/fallback selection there, and keep the GDI project out of non-Windows deliverables | | Filesystem semantics | Several filename and containment comparisons use `OrdinalIgnoreCase`; installed assets are conventionally uppercase | Needs validation on case-sensitive filesystems; may hide casing or containment mistakes | Add Linux/macOS tests with mixed-case synthetic roots and use filesystem-appropriate containment rules | @@ -72,9 +73,9 @@ VM op 0x236 (non-modal) / op 0x20f (modal) / op 0x24d (green-mask transition) Everything before and after the selected decoder is portable. The replacement decision is an in-process FFmpeg backend behind a project-owned C ABI, not raw FFmpeg structs in Godot/C# and not a subprocess. FFmpeg -`n8.1.2-29-g703dcc25b9` is pinned by immutable release URL and SHA-256 in -`native/age_movie_ffmpeg/dependency-win64.json`; changing that pin requires rerunning the full installed-movie -gate. The shim dynamically links an LGPL build made without GPL or nonfree components and uses +`n8.1.2-29-g703dcc25b9` is pinned by immutable release URL and SHA-256 in each target manifest under +`native/age_movie_ffmpeg/`; changing either pin requires rerunning the full installed-movie gate. The shim +dynamically links an LGPL build made without GPL or nonfree components and uses `libavformat`, `libavcodec`, `libavutil`, `libswscale`, and `libswresample`. Release artifacts must carry the matching FFmpeg source/configuration and notices required by FFmpeg's [license checklist](https://ffmpeg.org/legal.html). @@ -167,11 +168,10 @@ DirectShow renderer to the existing D3D target without assigning an RGB key. The DirectShow preroll/stream-origin inference are recorded in `docs/engine-re.md`. Native deliverables are RID-specific and bundled with the Godot export; the runtime must not discover an -arbitrary system FFmpeg. The first implementation gate is Windows x64 because that is the current runnable -target, but the C ABI and loader paths must reserve Windows x64, Linux x64, macOS x64, and macOS arm64 from the -start. Builds use shared libraries, `$ORIGIN`/`@loader_path`-style local lookup on Unix targets, recorded source -hashes and configure arguments, and no committed original-game data. Packaging automation is part of completing -the backend, not a prerequisite for the first native decode spike. +arbitrary system FFmpeg. Windows x64 and Linux x64 now have pinned builds; the C ABI and loader paths also reserve +macOS x64 and macOS arm64. Builds use shared libraries, `$ORIGIN`/`@loader_path`-style local lookup on Unix +targets, recorded source hashes and configure arguments, and no committed original-game data. Packaging +automation is part of completing the backend, including the corresponding-source/configuration offer. The current movie ABI is version 3. Its position-seek entry point resets both independent demux/decoder pipelines before managed keyframe/audio preroll, so opcode `0x241` has the same bounded, synchronized behavior @@ -188,8 +188,19 @@ packaging. `FfmpegMovieDecoder` now adds cancellable timestamp pacing and is the copies the shim, five required shared libraries, and FFmpeg license beside `Himegari.dll`; a natural SYSTEM4 smoke completed 7288 ms `LOGO.AGF`, opened 106919 ms `OP.AGF`, and published frames from both without loading DirectShow; the user subsequently confirmed both opening movies work in normal windowed playback. DirectShow -was deleted after the later synchronized-audio gate. Full -export/source-offer packaging and non-Windows builds remain outstanding. +was deleted after the later synchronized-audio gate. Full export/source-offer packaging, Linux Godot runtime +smoke coverage, and macOS native builds remain outstanding. + +The Linux-x64 bundle landed on 2026-07-31 from the matching pinned BtbN LGPL shared build. Its bootstrap verifies +the immutable archive hash and reported FFmpeg commit; its build emits an ELF64 x86-64 ABI v3 shim plus only the +five required SONAME libraries and license under disposable `build/native/linux-x64`. The shim records +`RUNPATH=$ORIGIN`, every FFmpeg dependency resolves beside it, and the bundle's newest required glibc symbol is +2.28, matching the provider baseline enforced by the build script. A self-contained Linux corpus-gate executable +under Ubuntu 20.04 decoded all 213 installed payloads with 0 failures in 10.696 seconds. A zero-warning +`ExportRelease` Linux-target assembly build staged the shim, five libraries, and license beside `Himegari.dll` +without the Windows GDI assembly or DLLs. These gates establish Linux loader/ABI/decoder and managed staging +compatibility; they do not yet establish an exported Godot window, live audio timing, font realization, or +case-sensitive install/profile behavior. The Windows-x64 installed-corpus gate passed on 2026-07-22 with the pinned dependency: all 213 signature-selected MPEG payloads opened and reached EOF under a 30-second per-item bound. It decoded all 15,788 frames across twelve diff --git a/docs/tools-reference.md b/docs/tools-reference.md index 39b99e5..71ff221 100644 --- a/docs/tools-reference.md +++ b/docs/tools-reference.md @@ -221,22 +221,25 @@ script/offset/opcode, that frame chain, the hottest sites in the bounded final 1 final 16-instruction sequence. This makes the last ADV locator unnecessary for identifying a post-dialogue loop; send either the `step-limit` console block or the generated JSON. -## Native FFmpeg movie shim (Windows x64) +## Native FFmpeg movie shim (Windows and Linux x64) -These PowerShell tools build the selected Windows-x64 live movie backend. The dependency manifest pins an -immutable LGPL shared FFmpeg archive and SHA-256. +The target-specific dependency manifests pin immutable LGPL shared FFmpeg archives and SHA-256 hashes. The +Windows tools use PowerShell/MSVC; the Linux tools run in Bash with Python 3, curl, a C compiler, binutils, and +the ordinary archive/core utilities available. | Tool | Purpose | Run | Reads → Writes | |---|---|---|---| | `native/age_movie_ffmpeg/bootstrap-win64.ps1` | Download, hash-check, extract, and version-check the pinned FFmpeg SDK. Returns the resolved SDK root. | `.\\native\\age_movie_ffmpeg\\bootstrap-win64.ps1 [-Destination ]` | `dependency-win64.json`, network/archive cache → disposable `build/downloads/`, `build/ffmpeg-sdk/` | | `native/age_movie_ffmpeg/build-win64.ps1` | Discover the MSVC x64 toolchain, build `age_movie_ffmpeg.dll`, and stage its exact shared-library/license dependencies. | `.\\native\\age_movie_ffmpeg\\build-win64.ps1 -SdkRoot [-OutputDirectory ]` | C ABI source + FFmpeg SDK → disposable `build/native/win-x64/` by default | +| `native/age_movie_ffmpeg/bootstrap-linux-x64.sh` | Download, SHA-256-check, extract, and version-check the pinned Linux x64 FFmpeg SDK. Prints the resolved SDK root. | `native/age_movie_ffmpeg/bootstrap-linux-x64.sh [destination]` | `dependency-linux-x64.json`, network/archive cache → disposable `build/downloads/`, `build/ffmpeg-sdk/` | +| `native/age_movie_ffmpeg/build-linux-x64.sh` | Build the ELF64 x86-64 shim; stage the five exact FFmpeg SONAME libraries and license; reject stale `.so` files, unresolved/nonlocal FFmpeg dependencies, a missing `$ORIGIN` runpath, or a glibc requirement newer than the pinned 2.28 baseline. | `sdk_root="$(native/age_movie_ffmpeg/bootstrap-linux-x64.sh)"` then `native/age_movie_ffmpeg/build-linux-x64.sh "$sdk_root" [output-directory]` | C ABI source + FFmpeg SDK → disposable `build/native/linux-x64/` by default | | `tools/movie-corpus-gate` | Discover every MPEG program stream stored under an `.AGF` catalog entry; decode every video frame and, when present, every audio block through the unpaced FFmpeg session; validate independent sequence dimensions, metadata, RGBA size, stereo finite PCM, monotonic video/audio timestamps, EOF, timeout, and teardown; then emit a per-asset JSON report. Audio fields include sample rate/channels, block and PCM-frame counts, first/last PTS, signal presence, and decode time. Exit 0 means the expected corpus count and every asset passed; exit 1 is a gate failure; exit 2 means the native shim is absent. | `dotnet run --project tools/movie-corpus-gate -- --output build/movie-corpus-ffmpeg.json --expected-count 213 --max-item-ms 30000` · optional `--native-dir ` | `SYS4INI.BIN` + loose/ALF VFS assets + staged FFmpeg shim → stdout progress + disposable `build/movie-corpus-ffmpeg.json` | The managed isolated probes load from `AGE_FFMPEG_NATIVE_DIR` when set, then application-local and -`runtimes/win-x64/native` locations. Build the shim before running the `FfmpegShim*` tests; no original-game -movie is copied into the repository or native output. When `build/native/win-x64/age_movie_ffmpeg.dll` exists, -`dotnet build godot/Himegari.csproj` also stages the shim, its five DLL dependencies, and `FFmpeg-LICENSE.txt` -beside `Himegari.dll` for development playback. +`runtimes//native` locations. Build the target shim before running the `FfmpegShim*` tests; no original-game +movie is copied into the repository or native output. A target-aware `dotnet build godot/Himegari.csproj` +stages the matching shim, five shared-library dependencies, and `FFmpeg-LICENSE.txt` beside `Himegari.dll` +when the corresponding `build/native/win-x64/` or `build/native/linux-x64/` bundle exists. The current native movie ABI is version 3. In addition to sequential video/audio decode, it exposes a synchronous position seek for both independent demuxers; exact video-frame selection and audio trimming are diff --git a/native/age_movie_ffmpeg/bootstrap-linux-x64.sh b/native/age_movie_ffmpeg/bootstrap-linux-x64.sh new file mode 100755 index 0000000..4e3d527 --- /dev/null +++ b/native/age_movie_ffmpeg/bootstrap-linux-x64.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd -- "$script_dir/../.." && pwd)" +destination="${1:-$repo_root/build/ffmpeg-sdk}" +manifest="$script_dir/dependency-linux-x64.json" + +manifest_value() { + python3 -c 'import json, sys; print(json.load(open(sys.argv[1], encoding="utf-8"))[sys.argv[2]])' \ + "$manifest" "$1" +} + +for command in python3 curl sha256sum tar awk; do + command -v "$command" >/dev/null 2>&1 || { + echo "required command was not found: $command" >&2 + exit 1 + } +done + +archive="$(manifest_value archive)" +url="$(manifest_value url)" +expected_hash="$(manifest_value sha256)" +expected_version="$(manifest_value ffmpeg_version)" +archive_root="${archive%.tar.xz}" +download_dir="$repo_root/build/downloads" +archive_path="$download_dir/$archive" +sdk_root="$destination/$archive_root" + +mkdir -p -- "$download_dir" "$destination" +if [[ ! -f "$archive_path" ]]; then + curl --fail --location --retry 3 --output "$archive_path" "$url" +fi + +actual_hash="$(sha256sum "$archive_path" | awk '{ print $1 }')" +if [[ "$actual_hash" != "$expected_hash" ]]; then + echo "FFmpeg archive SHA-256 mismatch: expected $expected_hash, got $actual_hash" >&2 + exit 1 +fi + +if [[ ! -d "$sdk_root" ]]; then + tar -xJf "$archive_path" -C "$destination" +fi + +for required in \ + "$sdk_root/include/libavformat/avformat.h" \ + "$sdk_root/lib/libavformat.so" \ + "$sdk_root/bin/ffmpeg" \ + "$sdk_root/LICENSE.txt"; do + if [[ ! -e "$required" ]]; then + echo "FFmpeg SDK file was not found after extraction: $required" >&2 + exit 1 + fi +done + +reported="$(LD_LIBRARY_PATH="$sdk_root/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ + "$sdk_root/bin/ffmpeg" -version)" +reported="${reported%%$'\n'*}" +version_without_date="${expected_version%-????????}" +if [[ "$reported" != *"$version_without_date"* ]]; then + echo "unexpected FFmpeg build: $reported" >&2 + exit 1 +fi + +printf '%s\n' "$sdk_root" diff --git a/native/age_movie_ffmpeg/build-linux-x64.sh b/native/age_movie_ffmpeg/build-linux-x64.sh new file mode 100755 index 0000000..949f14d --- /dev/null +++ b/native/age_movie_ffmpeg/build-linux-x64.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -lt 1 || $# -gt 2 ]]; then + echo "usage: $0 [output-directory]" >&2 + exit 2 +fi + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd -- "$script_dir/../.." && pwd)" +sdk_root="$(cd -- "$1" && pwd)" +output_directory_input="${2:-$repo_root/build/native/linux-x64}" +mkdir -p -- "$output_directory_input" +output_directory="$(cd -- "$output_directory_input" && pwd)" +include_directory="$sdk_root/include" +library_directory="$sdk_root/lib" +shim="$output_directory/libage_movie_ffmpeg.so" +manifest="$script_dir/dependency-linux-x64.json" + +for command in cc readelf ldd python3 sort; do + command -v "$command" >/dev/null 2>&1 || { + echo "required command was not found: $command" >&2 + exit 1 + } +done + +for required in \ + "$include_directory/libavformat/avformat.h" \ + "$library_directory/libavformat.so" \ + "$library_directory/libavcodec.so" \ + "$library_directory/libavutil.so" \ + "$library_directory/libswscale.so" \ + "$library_directory/libswresample.so" \ + "$sdk_root/LICENSE.txt"; do + if [[ ! -e "$required" ]]; then + echo "missing FFmpeg SDK file: $required" >&2 + exit 1 + fi +done + +cc \ + -std=c11 -fPIC -fvisibility=hidden -O2 -Wall -Wextra \ + -I "$include_directory" \ + -shared -Wl,-z,defs -Wl,-z,origin -Wl,--enable-new-dtags -Wl,-rpath,'$ORIGIN' \ + -o "$shim" "$script_dir/age_movie.c" \ + -L "$library_directory" \ + -lavformat -lavcodec -lavutil -lswscale -lswresample + +runtime_libraries=( + libavformat.so.62 + libavcodec.so.62 + libavutil.so.60 + libswscale.so.9 + libswresample.so.6 +) +for runtime in "${runtime_libraries[@]}"; do + source_library="$library_directory/$runtime" + if [[ ! -e "$source_library" ]]; then + echo "missing FFmpeg runtime library: $source_library" >&2 + exit 1 + fi + cp --dereference -- "$source_library" "$output_directory/$runtime" +done +cp -- "$sdk_root/LICENSE.txt" "$output_directory/FFmpeg-LICENSE.txt" + +expected_outputs=(libage_movie_ffmpeg.so "${runtime_libraries[@]}") +shopt -s nullglob +for existing in "$output_directory"/*.so*; do + existing_name="$(basename -- "$existing")" + expected=false + for expected_name in "${expected_outputs[@]}"; do + if [[ "$existing_name" == "$expected_name" ]]; then + expected=true + break + fi + done + if [[ "$expected" != true ]]; then + echo "unexpected stale shared library in output directory: $existing" >&2 + exit 1 + fi +done + +if ! readelf -h "$shim" | grep -Eq 'Class:[[:space:]]+ELF64'; then + echo "native movie shim is not an ELF64 binary: $shim" >&2 + exit 1 +fi +if ! readelf -h "$shim" | grep -Eq 'Machine:[[:space:]]+Advanced Micro Devices X86-64'; then + echo "native movie shim is not an x86-64 binary: $shim" >&2 + exit 1 +fi +if ! readelf -d "$shim" | grep -Fq '$ORIGIN'; then + echo "native movie shim does not have an \$ORIGIN runtime search path: $shim" >&2 + exit 1 +fi + +ldd_output="$(ldd "$shim")" +if [[ "$ldd_output" == *"not found"* ]]; then + printf '%s\n' "$ldd_output" >&2 + echo "native movie bundle has unresolved shared-library dependencies" >&2 + exit 1 +fi +for runtime in "${runtime_libraries[@]}"; do + resolution="$(printf '%s\n' "$ldd_output" | grep -F "$runtime =>" || true)" + if [[ "$resolution" != *"=> $output_directory/$runtime "* ]]; then + printf '%s\n' "$ldd_output" >&2 + echo "$runtime did not resolve from the bundle directory" >&2 + exit 1 + fi +done + +minimum_glibc="$(python3 -c \ + 'import json, sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["minimum_glibc"])' \ + "$manifest")" +versioned_outputs=("$shim") +for runtime in "${runtime_libraries[@]}"; do + versioned_outputs+=("$output_directory/$runtime") +done +highest_glibc="$(readelf --version-info "${versioned_outputs[@]}" \ + | grep -Eo 'GLIBC_[0-9]+(\.[0-9]+)*' | sort -Vu | tail -n 1 || true)" +if [[ -z "$highest_glibc" ]]; then + echo "could not determine the bundle's glibc symbol-version requirement" >&2 + exit 1 +fi +highest_glibc="${highest_glibc#GLIBC_}" +newest_glibc="$(printf '%s\n%s\n' "$minimum_glibc" "$highest_glibc" | sort -Vu | tail -n 1)" +if [[ "$newest_glibc" != "$minimum_glibc" ]]; then + echo "bundle requires glibc $highest_glibc, newer than the pinned $minimum_glibc baseline" >&2 + exit 1 +fi + +printf '%s\n' "$shim" diff --git a/native/age_movie_ffmpeg/dependency-linux-x64.json b/native/age_movie_ffmpeg/dependency-linux-x64.json new file mode 100644 index 0000000..ac80f0c --- /dev/null +++ b/native/age_movie_ffmpeg/dependency-linux-x64.json @@ -0,0 +1,12 @@ +{ + "provider": "BtbN/FFmpeg-Builds", + "release_tag": "autobuild-2026-07-21-13-38", + "archive": "ffmpeg-n8.1.2-29-g703dcc25b9-linux64-lgpl-shared-8.1.tar.xz", + "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-07-21-13-38/ffmpeg-n8.1.2-29-g703dcc25b9-linux64-lgpl-shared-8.1.tar.xz", + "sha256": "d0026954f10d303d1fc6517ed6b2290c42323b3958f11baafb1b330dfc564b36", + "ffmpeg_version": "n8.1.2-29-g703dcc25b9-20260721", + "ffmpeg_commit": "703dcc25b9", + "variant": "linux64 LGPL shared 8.1", + "minimum_glibc": "2.28", + "minimum_linux_kernel": "4.18" +}