Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package sdrangel for openSUSE:Factory 
checked in at 2026-11-11 16:29:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/sdrangel (Old)
 and      /work/SRC/openSUSE:Factory/.sdrangel.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "sdrangel"

Wed Nov 11 16:29:53 2026 rev:2 rq:1396856 version:8.1.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/sdrangel/sdrangel.changes        2026-10-26 
10:00:42.919506875 +0100
+++ /work/SRC/openSUSE:Factory/.sdrangel.new.3759290/sdrangel.changes   
2026-11-11 16:31:04.931351631 +0100
@@ -1,0 +2,54 @@
+Mon Nov  9 10:55:25 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 8.1.0
+  New plugins for aero communications
+  * Add ACARS demodulator channel plugin, decoding various digital
+    aero messages.
+  * Add Aircraft feature plugin, collating aircraft related data
+    from various sources.
+  Other changes
+  * Analog TV demodulator: add colour support.
+  * Record software identification in WAV files.
+  * Fix Coverity and static-analysis findings and compiler
+    warnings.
+  * Fix Flatpak build issue and update faad2 download URL.
+  * libSGP4 range rate API compatibility.
+  * Fix MCP tune_gain tool input schema.
+- Add pkgconfig(libacars-2) BuildRequires for the new ACARS
+  demodulator plugin and pkgconfig(libzstd) for the map plugin's
+  MSFS converter (upstream falls back to a network download
+  without it, which fails in offline builds).
+- Declare the new demodacars plugin's accidentally committed
+  Windows build objects as other-platform (the Linux build compiles
+  only the C sources).
+- Build the ThirdParty wasm blobs from source instead of
+  shipping upstream's prebuilts, and drop them from the tarball:
+  * wasm_splats_bg.wasm from CesiumGS/cesium-wasm-utils
+    (wasm-splats 0.1.0-alpha.2, Apache-2.0), built offline
+    with the pinned Rust 1.98 toolchain (wasm32 std compiled
+    from the matching rust-src) plus a wasm-bindgen CLI
+    built from the same vendored sources.
+  * zip-module.wasm from gildas-lormeau/zlib-streams
+    (BSD-3-Clause, zlib submodule at 5a82f71), built with
+    system emscripten (upstream make zip_module recipe).
+  * draco_decoder.wasm from google/draco 1.5.7 (Apache-2.0),
+    built with system emscripten (upstream Emscripten decoder
+    recipe); the fresh module's linkage names are adapted to
+    the older interface the shipped Cesium glue was generated
+    against (draco-relink.py; symbol-joined bindings, proven by
+    a decode smoke test with that glue). Backports the
+    upstream FindPython3 switch (draco-emscripten-python3.patch)
+    so the build configures with cmake>=4.
+  * basis_transcoder.wasm from BinomialLLC/basis_universal
+    1.15_final (Apache-2.0), built with system emscripten (upstream
+    webgl/transcoder recipe). The new module needs the new
+    emscripten runtime, so basis-splice.py swaps the fresh
+    factory into the vendored worker bundle (outer Cesium
+    logic untouched; restores the wasmBinaryFile contract),
+    proven by a worker end-to-end transcode test. Needs
+    basis-cxx17.patch (system emscripten embind requires
+    C++17; upstream pins C++11). All four
+    ThirdParty wasm now build from source.
+- Spec cleanup: drop Group tag, sort BuildRequires.
+
+-------------------------------------------------------------------

Old:
----
  sdrangel-8.0.0.tar.gz

New:
----
  basis-check-data.tar.xz
  basis-cxx17.patch
  basis-e2e.cjs
  basis-splice.py
  basis_universal-1.15_final.tar.gz
  cesium-wasm-utils-96a2fba.tar.gz
  draco-1.5.7.tar.gz
  draco-check.mjs
  draco-emscripten-python3.patch
  draco-relink.py
  rust-src-1.98.1.tar.xz
  sdrangel-8.1.0.tar.gz
  splats-check.mjs
  wasm-bindgen-cli-0.2.100.crate
  wasm-splats-vendor.tar.xz
  zlib-5a82f71.tar.gz
  zlib-streams-ff804b0.tar.gz

----------(New B)----------
  New:    proven by a worker end-to-end transcode test. Needs
    basis-cxx17.patch (system emscripten embind requires
    C++17; upstream pins C++11). All four
  New:    a decode smoke test with that glue). Backports the
    upstream FindPython3 switch (draco-emscripten-python3.patch)
    so the build configures with cmake>=4.
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ sdrangel.spec ++++++
--- /var/tmp/diff_new_pack.NYEPhe/_old  2026-11-11 16:31:14.164739047 +0100
+++ /var/tmp/diff_new_pack.NYEPhe/_new  2026-11-11 16:31:14.168739215 +0100
@@ -26,41 +26,113 @@
 %bcond_without freedv
 %bcond_with qt6
 Name:           sdrangel
-Version:        8.0.0
+Version:        8.1.0
 Release:        0
 Summary:        SDR/Analyzer frontend for Airspy, BladeRF, HackRF, RTL-SDR and 
FunCube
 License:        GPL-3.0-or-later
-Group:          Productivity/Hamradio/Other
 URL:            https://github.com/f4exb/sdrangel
-Source:         
https://github.com/f4exb/sdrangel/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
+# Repacked tarball minus the four rebuilt ThirdParty wasm (all built from
+# source in %%build; pristine at 
https://github.com/f4exb/sdrangel/archive/v8.1.0.tar.gz)
+Source:         %{name}-%{version}.tar.gz
+Source1:        
https://github.com/CesiumGS/cesium-wasm-utils/archive/96a2fbae7ab1d117dd533fe558f0e061bed6762b.tar.gz#/cesium-wasm-utils-96a2fba.tar.gz
+Source2:        wasm-splats-vendor.tar.xz
+Source3:        
https://static.crates.io/crates/wasm-bindgen-cli/wasm-bindgen-cli-0.2.100.crate
+Source4:        rust-src-1.98.1.tar.xz
+# Repacked upstream rust-src minus wit-bindgen's prebuilt WASI objects
+# (only its sources are read; the wasm32 std is compiled from them)
+Source5:        zlib-streams-ff804b0.tar.gz
+Source6:        
https://github.com/madler/zlib/archive/5a82f71ed1dfc0bec044d9702463dbdf84ea3b71.tar.gz#/zlib-5a82f71.tar.gz
+Source7:        splats-check.mjs
+Source8:        draco-1.5.7.tar.gz
+# Repacked upstream draco minus its prebuilt wasm + nested Unity/Maya
+# plugin archives (the Emscripten decoder build needs none of them)
+Source9:        basis_universal-1.15_final.tar.gz
+# Repacked upstream 1.15_final tag tarball minus its 3 prebuilt wasm
+# (webgl encoder/transcoder/videotest builds; the transcoder is rebuilt below)
+Source10:       basis-splice.py
+# Swaps the source-built factory into the vendored worker bundle (the
+# outer Cesium logic is untouched; restores the wasmBinaryFile contract)
+Source11:       draco-check.mjs
+Source12:       basis-e2e.cjs
+# Worker end-to-end smoke test for the rebuilt basis factory + wasm.
+Source13:       draco-relink.py
+# Adapts the rebuilt draco_decoder.wasm to the linkage interface of the
+# shipped Cesium glue (symbol/role-joined names; fails loudly on drift).
+Source14:       basis-check-data.tar.xz
+# Khronos KTX-Software sample transcode input for the %%check worker test.
+# PATCH-FIX-UPSTREAM: draco-emscripten-python3.patch -- use FindPython3 module 
(cmake>=4 removed FindPythonInterp); backport of upstream 8b1f23e04391 + 
a4365c7935ca
+Patch0:         draco-emscripten-python3.patch
+# PATCH-FIX-OPENSUSE: basis-cxx17.patch -- embind needs C++17 (upstream pins 
C++11)
+Patch1:         basis-cxx17.patch
+# Repacked upstream archive minus its dist/ prebuilt blobs (rebuilt below)
+# and the empty src/zlib submodule placeholder filled from Source6
+# Source-Binary-Exception: 
wasm-splats-vendor.tar.xz:vendor-full/ring-*/pregenerated/* -- other-platform: 
Windows-only objects; Linux builds ring from its perlasm sources
+# Source-Binary-Exception: wasm-splats-vendor.tar.xz:vendor-full/windows_* -- 
other-platform: Windows import libraries unused by the Linux build
+# Source-Binary-Exception: 
wasm-splats-vendor.tar.xz:vendor-full/walrus-*/benches/fixtures/* -- test-data: 
benchmark input parsed by tests, never executed
+# Source-Binary-Exception: */plugins/channelrx/demodacars/atn/asn1/*.obj -- 
other-platform: Windows MSVC objects committed upstream; the Linux build 
compiles only the *.c files
+BuildRequires:  binaryen
+BuildRequires:  cargo1.98
 BuildRequires:  cmake
 BuildRequires:  doxygen
 BuildRequires:  dsdcc-devel >= 1.9.3
+BuildRequires:  emscripten
 BuildRequires:  gcc-c++
 BuildRequires:  hicolor-icon-theme
+BuildRequires:  libboost_headers-devel
+BuildRequires:  limesuite-devel
+BuildRequires:  nodejs
+BuildRequires:  patch
+BuildRequires:  pkgconfig
+BuildRequires:  python3-ply
+BuildRequires:  serialdv-devel
+BuildRequires:  pkgconfig(SoapySDR)
+BuildRequires:  pkgconfig(alsa)
+BuildRequires:  pkgconfig(codec2)
+BuildRequires:  pkgconfig(faad2)
+BuildRequires:  pkgconfig(fftw3f)
+BuildRequires:  pkgconfig(flac)
+BuildRequires:  pkgconfig(libacars-2)
+BuildRequires:  pkgconfig(libairspy)
+BuildRequires:  pkgconfig(libairspyhf)
+BuildRequires:  pkgconfig(libavcodec)
+BuildRequires:  pkgconfig(libavformat)
+BuildRequires:  pkgconfig(libavutil)
+BuildRequires:  pkgconfig(libbladeRF)
+BuildRequires:  pkgconfig(libhackrf)
+BuildRequires:  pkgconfig(libiio)
+BuildRequires:  pkgconfig(liblz4)
+BuildRequires:  pkgconfig(libpostproc)
+BuildRequires:  pkgconfig(librtlsdr)
+BuildRequires:  pkgconfig(libswscale)
+# It does not build with libmirisdr from upstream
+# https://github.com/f4exb/libmirisdr-4 is needed
+#BuildRequires:  pkgconfig(libmirisdr)
+BuildRequires:  pkgconfig(libunwind)
+BuildRequires:  pkgconfig(libusb-1.0)
+BuildRequires:  pkgconfig(libxtrx)
+BuildRequires:  pkgconfig(libzstd)
+BuildRequires:  pkgconfig(opus)
+BuildRequires:  pkgconfig(uhd)
+Requires:       python3-requests
 %if 0%{with qt6}
 #BuildRequires:  libQt6Gui-private-headers-devel
 #BuildRequires:  qt6-core-private-devel
 %else
 BuildRequires:  libQt5Gui-private-headers-devel
 %endif
-BuildRequires:  libboost_headers-devel
-BuildRequires:  limesuite-devel
-BuildRequires:  pkgconfig
-BuildRequires:  serialdv-devel
 %if 0%{with qt6}
 BuildRequires:  pkgconfig(Qt6Charts)
 BuildRequires:  pkgconfig(Qt6Core)
-BuildRequires:  pkgconfig(Qt6Quick)
-BuildRequires:  pkgconfig(Qt6QuickWidgets)
 BuildRequires:  pkgconfig(Qt6Multimedia)
 BuildRequires:  pkgconfig(Qt6MultimediaWidgets)
 BuildRequires:  pkgconfig(Qt6OpenGL)
 BuildRequires:  pkgconfig(Qt6OpenGLWidgets)
 BuildRequires:  pkgconfig(Qt6Positioning)
+BuildRequires:  pkgconfig(Qt6Quick)
+BuildRequires:  pkgconfig(Qt6QuickWidgets)
 BuildRequires:  pkgconfig(Qt6SerialPort)
-BuildRequires:  pkgconfig(Qt6Svg)
 BuildRequires:  pkgconfig(Qt6StateMachine)
+BuildRequires:  pkgconfig(Qt6Svg)
 BuildRequires:  pkgconfig(Qt6WebSockets)
 BuildRequires:  pkgconfig(Qt6Widgets)
 %else
@@ -77,33 +149,6 @@
 BuildRequires:  pkgconfig(Qt5WebSockets)
 BuildRequires:  pkgconfig(Qt5Widgets)
 %endif
-BuildRequires:  pkgconfig(SoapySDR)
-BuildRequires:  pkgconfig(alsa)
-BuildRequires:  pkgconfig(faad2)
-BuildRequires:  pkgconfig(codec2)
-BuildRequires:  pkgconfig(flac)
-BuildRequires:  pkgconfig(fftw3f)
-BuildRequires:  pkgconfig(libairspy)
-BuildRequires:  pkgconfig(libairspyhf)
-BuildRequires:  pkgconfig(libavcodec)
-BuildRequires:  pkgconfig(libavformat)
-BuildRequires:  pkgconfig(libavutil)
-BuildRequires:  pkgconfig(libbladeRF)
-BuildRequires:  pkgconfig(libhackrf)
-BuildRequires:  pkgconfig(libiio)
-BuildRequires:  pkgconfig(liblz4)
-BuildRequires:  pkgconfig(libpostproc)
-BuildRequires:  pkgconfig(librtlsdr)
-BuildRequires:  pkgconfig(libswscale)
-# It does not build with libmirisdr from upstream
-# https://github.com/f4exb/libmirisdr-4 is needed
-#BuildRequires:  pkgconfig(libmirisdr)
-BuildRequires:  pkgconfig(libunwind)
-BuildRequires:  pkgconfig(libusb-1.0)
-BuildRequires:  pkgconfig(libxtrx)
-BuildRequires:  pkgconfig(opus)
-BuildRequires:  pkgconfig(uhd)
-Requires:       python3-requests
 %if 0%{?suse_version} >= 1550
 BuildRequires:  pkgconfig(opencv4)
 %else
@@ -125,8 +170,126 @@
 %setup -q
 sed -i 's/\r$//' Readme.md
 sed -i 's|#!%{_bindir}/env python|#!%{_bindir}/python3|g' 
swagger/sdrangel/examples/*.py
+# Stage the wasm sources (built in %%build to replace the dropped blobs)
+mkdir wasm-src wasm-zlib wasm-vendor wasm-cli rust-src-full draco-1.5.7 
basis-univ
+tar -xzf %{SOURCE1} -C wasm-src --strip-components=1
+tar -xzf %{SOURCE5} -C wasm-zlib --strip-components=1
+mkdir -p wasm-zlib/src/zlib
+tar -xzf %{SOURCE6} -C wasm-zlib/src/zlib --strip-components=1
+tar -xJf %{SOURCE2} -C wasm-vendor --strip-components=1
+tar -xzf %{SOURCE3} -C wasm-cli --strip-components=1
+tar -xJf %{SOURCE4} -C rust-src-full
+tar -xzf %{SOURCE8} -C draco-1.5.7 --strip-components=1
+tar -xzf %{SOURCE9} -C basis-univ --strip-components=1
+# Upstream backport: FindPythonInterp is gone in cmake>=4
+patch -p1 -s --no-backup-if-mismatch < %{PATCH0}
+# System emscripten embind needs C++17 (upstream pins C++11)
+patch -d basis-univ -p1 -s --no-backup-if-mismatch < %{PATCH1}
 
 %build
+# Rebuild the four dropped ThirdParty wasm blobs from source in place
+# of the prebuilt ones
+cd %{_builddir}/%{name}-%{version}
+export CARGO_NET_OFFLINE=true RUSTC_BOOTSTRAP=1
+export CARGO_TARGET_DIR=$PWD/wasm-target
+WASM_CFG1='source.crates-io.replace-with="vendored-sources"'
+WASM_CFG2="source.vendored-sources.directory=\"$PWD/wasm-vendor\""
+# wasm32 std for the pinned toolchain: system rustc ships host std only and
+# /usr is read-only, so shadow its sysroot with the matching rust-src.
+mkdir -p shadow-sysroot/lib/rustlib/src/rust
+cp -r rust-src-full/rust-src-*/rust-src/lib/rustlib/src/rust/library 
shadow-sysroot/lib/rustlib/src/rust/
+RUSTC_REAL=$(command -v rustc)
+REAL_SYSROOT=$($RUSTC_REAL --print sysroot)
+export RUSTC_REAL REAL_SYSROOT SHADOW_SYSROOT
+SHADOW_SYSROOT=$PWD/shadow-sysroot
+# Shadow answers rustc's sysroot probes (read-only /usr); the wrapper also
+# adds the forgotten panic-runtime --extern to cdylib builds once built.
+cat > rustc-wasm.sh <<'WRAPEOF'
+#!/bin/bash
+if [ " $* " = " --print sysroot " ] || [ " $* " = " --print=sysroot " ]; then
+  echo "$SHADOW_SYSROOT"
+  exit 0
+fi
+case " $* " in
+  *" --print=sysroot "*|*" --print sysroot "*)
+    set -o pipefail
+    "$RUSTC_REAL" "$@" | sed "s|^${REAL_SYSROOT}\$|${SHADOW_SYSROOT}|"
+    exit $?
+    ;;
+esac
+case " $* " in
+  *" --target wasm32-unknown-unknown "*)
+    case " $* " in
+      *" --crate-type cdylib "*)
+        case " $* " in
+          *" --print "*|*" --crate-name ___ "*) ;;
+          *)
+            ABORT=$(ls -t 
"$CARGO_TARGET_DIR"/wasm32-unknown-unknown/release/deps/libpanic_abort-*.rlib 
2>/dev/null | head -1)
+            [ -n "$ABORT" ] && set -- "$@" --extern "panic_abort=$ABORT"
+            ;;
+        esac
+        ;;
+    esac
+    ;;
+esac
+exec "$RUSTC_REAL" "$@"
+WRAPEOF
+chmod +x rustc-wasm.sh
+export RUSTC=$PWD/rustc-wasm.sh
+# wasm_splats_bg.wasm (CesiumGS/cesium-wasm-utils, Apache-2.0): plain cargo
+# build plus the era-matching wasm-bindgen CLI from the same vendor tree
+cargo build --offline --release -Z build-std --target wasm32-unknown-unknown \
+  --manifest-path wasm-src/wasm-splats/Cargo.toml \
+  --config "$WASM_CFG1" --config "$WASM_CFG2"
+rm -f wasm-cli/Cargo.lock
+(cd wasm-cli && cargo build --offline --release \
+  --config "$WASM_CFG1" --config "$WASM_CFG2")
+wasm-target/release/wasm-bindgen --target web --no-typescript \
+  --out-dir wasm-out \
+  wasm-target/wasm32-unknown-unknown/release/wasm_splats.wasm
+cp wasm-out/wasm_splats_bg.wasm \
+  plugins/feature/map/Cesium/ThirdParty/wasm_splats_bg.wasm
+# zip-module.wasm (gildas-lormeau/zlib-streams, BSD-3-Clause): upstream recipe
+%make_build -C wasm-zlib zip_module EMCC=emcc WASM_OPT=wasm-opt
+cp wasm-zlib/dist/zip-module.wasm \
+  plugins/feature/map/Cesium/ThirdParty/zip-module.wasm
+# draco_decoder.wasm (google/draco 1.5.7, Apache-2.0): upstream Emscripten
+# decoder recipe; draco-relink.py adapts its linkage to the shipped glue.
+export EMSCRIPTEN=%{_datadir}/emscripten
+mkdir draco-build && cd draco-build
+# FIXME: you should use the %%cmake macros
+emcmake cmake ../draco-1.5.7 -DCMAKE_BUILD_TYPE=Release -DDRACO_WASM=ON
+%make_build draco_decoder
+cd ..
+python3 %{SOURCE13} draco-1.5.7/javascript/npm/draco3d/draco_decoder_nodejs.js 
\
+  draco-build/draco_decoder.js draco-build/draco_decoder.wasm \
+  draco-build/draco_decoder.relinked.wasm "a:b,b:c,c:e,d:d,e:a"
+# new->old runtime imports: throw->b, fd_write->c, resize_heap->e,
+# fd_close->d (decoder opens no fds), abort->a; rest derived in script.
+cp draco-build/draco_decoder.relinked.wasm \
+  plugins/feature/map/Cesium/ThirdParty/draco_decoder.wasm
+# basis (BinomialLLC/basis_universal 1.15_final): upstream webgl/transcoder
+# recipe; basis-splice.py swaps the fresh factory into the worker bundle.
+mkdir basis-build && cd basis-build
+# Upstream declares cmake_minimum_required(VERSION 3.0), refused by cmake>=4
+export CMAKE_POLICY_VERSION_MINIMUM=3.5
+# FIXME: you should use the %%cmake macros
+emcmake cmake ../basis-univ/webgl/transcoder -DCMAKE_BUILD_TYPE=Release
+%make_build
+# Link paths in objects1.rsp are relative to this dir (as in the recipe run)
+mkdir -p ../basis-glue
+%{_datadir}/emscripten/em++ --bind -s ALLOW_MEMORY_GROWTH=1 -O3 -s 
ASSERTIONS=0 \
+  -s MODULARIZE=1 -s EXPORT_NAME=BASIS -sEXPORTED_RUNTIME_METHODS=HEAP8,HEAPU8 
\
+  -Wl,--export-all @CMakeFiles/basis_transcoder.js.dir/objects1.rsp \
+  -o ../basis-glue/basis_transcoder.js
+cd ..
+python3 %{SOURCE10} basis-glue/basis_transcoder.js \
+  plugins/feature/map/Cesium/Workers/transcodeKTX2.js \
+  plugins/feature/map/Cesium/Workers/transcodeKTX2.js.new
+mv plugins/feature/map/Cesium/Workers/transcodeKTX2.js.new \
+  plugins/feature/map/Cesium/Workers/transcodeKTX2.js
+cp basis-glue/basis_transcoder.wasm \
+  plugins/feature/map/Cesium/ThirdParty/basis_transcoder.wasm
 export CXXFLAGS="%{optflags} -Wno-return-type -fpermissive"
 %cmake \
 %if 0%{with qt6}
@@ -142,6 +305,26 @@
   -DRX_SAMPLE_24BIT=ON
 %make_jobs
 
+%check
+cd %{_builddir}/%{name}-%{version}
+# Upstream's own AES-HMAC suite for the rebuilt zip-module.wasm (needs node)
+%make_build -C wasm-zlib test_zip_module
+# Smoke-test the rebuilt wasm_splats_bg.wasm against verified outputs
+node %{SOURCE7} wasm-out
+# Draco decode tests with draco's own glue and test data (needs node)
+mkdir -p draco-check
+cp draco-1.5.7/javascript/npm/draco3d/draco_decoder_nodejs.js 
draco-1.5.7/javascript/npm/draco3d/bunny.drc draco-check/
+cp draco-1.5.7/testdata/cube_pc.drc draco-1.5.7/testdata/cube_att.drc 
draco-check/
+cp draco-build/draco_decoder.relinked.wasm draco-check/draco_decoder.wasm
+node %{SOURCE11} draco-check
+# Basis worker end-to-end: the spliced bundle + rebuilt wasm transcode
+# kodim23.ktx2 through the real worker task path (needs node)
+mkdir -p basis-check
+tar -xf %{SOURCE14} -C basis-check
+mv basis-check/test/kodim23.ktx2 basis-check/ && rmdir basis-check/test
+cp plugins/feature/map/Cesium/ThirdParty/basis_transcoder.wasm basis-check/
+node %{SOURCE12} plugins/feature/map/Cesium/Workers/transcodeKTX2.js 
basis-check
+
 %install
 %cmake_install
 rm %{buildroot}%{_datadir}/sdrangel/Readme.md

++++++ basis-cxx17.patch ++++++
--- a/webgl/transcoder/CMakeLists.txt
+++ b/webgl/transcoder/CMakeLists.txt
@@ -15,7 +15,7 @@
 message("KTX2_ZSTANDARD=${KTX2_ZSTANDARD}")
 
 if (EMSCRIPTEN)
-  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD 17)
   
   set(SRC_LIST
        ../../transcoder/basisu_transcoder.cpp

++++++ basis-e2e.cjs ++++++
'use strict';
// End-to-end smoke test for the source-built basis transcoder inside the
// shipped Cesium worker bundle: drives Workers/transcodeKTX2.js through its
// real two-message task protocol (init, then KTX2 transcode of kodim23.ktx2)
// and checks dimensions and level bytes against verified values.
// Usage: node basis-e2e.cjs <bundle-path> <workdir>
//   <workdir> holds copies of kodim23.ktx2 and basis_transcoder.wasm.
const fs = require('node:fs');
const path = require('node:path');
const assert = require('node:assert');

const bundlePath = path.resolve(process.argv[2]);
const dir = path.resolve(process.argv[3]);
// Test-only shims: the Emscripten factory's node branch reads __dirname and
// require (in production it runs in a WebEngine worker, neither is touched).
globalThis.require = require;
globalThis.__dirname = dir;
globalThis.__filename = path.join(dir, 'basis-e2e.cjs');

const posted = [];
globalThis.self = {};
globalThis.postMessage = (msg) => { posted.push(msg); };

(async () => {
  await import(bundlePath);
  const onmessage = globalThis.self.onmessage;
  assert.equal(typeof onmessage, 'function');
  const ktx2 = new Uint8Array(fs.readFileSync(path.join(dir, 'kodim23.ktx2')));
  const wasmFile = path.join(dir, 'basis_transcoder.wasm');

  await onmessage({ data: { id: 1, baseUrl: 'http://localhost/', 
canTransferArrayBuffer: true,
    parameters: { webAssemblyConfig: { wasmBinaryFile: wasmFile } } } });
  await new Promise((r) => setTimeout(r, 500));
  assert.equal(posted.length, 1);
  assert.equal(posted[0].result, true);
  console.log('basis-e2e: init OK');

  await onmessage({ data: { id: 2, baseUrl: 'http://localhost/', 
canTransferArrayBuffer: true,
    parameters: { ktx2Buffer: ktx2, supportedTargetFormats: { etc: true } } } 
});
  await new Promise((r) => setTimeout(r, 500));
  assert.equal(posted.length, 2);
  const m = posted[1];
  if (m.error) { console.error('WORKER ERROR:', m.error); process.exit(1); }
  assert.equal(m.result.length, 10);
  const px = m.result[0].positiveX;
  console.log('basis-e2e: level0 ' + px.width + 'x' + px.height +
    ' internalFormat=' + px.internalFormat + ' bufBytes=' + 
px.levelBuffer.byteLength);
  assert.equal(px.width, 768);
  assert.equal(px.height, 512);
  assert.equal(px.levelBuffer.byteLength, 192 * 128 * 8);
  console.log('basis-e2e: OK');
})().catch((e) => { console.error('HARNESS FAIL:', e); process.exit(1); });

++++++ basis-splice.py ++++++
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Splice a source-built Emscripten basis factory into Cesium's worker bundle.

Provenance:
- BUNDLE (vendored, Cesium 1.145.0): 
plugins/feature/map/Cesium/Workers/transcodeKTX2.js
  The Emscripten MODULARIZE factory blob ("var Pt=(function(){...})();", built 
by the
  Cesium team with 2021-era emscripten from BinomialLLC/basis_universal
  webgl/transcoder/basis_wasm_transcoder.cpp) is byte-replaced with a factory 
built
  from source in %build with system emscripten from the SAME upstream C++ file.
- The surrounding Cesium worker logic (KTX2 parse, format selection, task 
wiring) is
  NOT touched: it only uses the stable embind public API (initializeBasis, 
KTX2File
  + methods, transcoder_texture_format), which the replacement provides 
identically.
- The wasm instantiated by the factory is likewise built from source in %build.

Usage: basis-splice.py <new-glue.js> <bundle-in.js> <bundle-out.js>
  <new-glue.js> is the emcc MODULARIZE output (UMD wrapper stripped here).
"""
import sys

START = 'var Pt=(function(){'
END = '})();typeof ot'


def extract_factory(glue: str) -> str:
    head = 'var BASIS='
    assert glue.startswith(head), 'unexpected glue wrapper'
    tail = ';if(typeof exports==="object"'
    i = glue.find(tail)
    assert i > 0, 'UMD tail anchor not found'
    expr = glue[len(head):i]
    assert expr.startswith('(()=>{') and expr.endswith('})()'), 'factory IIFE 
shape changed'
    # Cesium always configures this worker with {wasmBinaryFile: url} (see the
    # initWebAssemblyModule call site in Cesium.js). emscripten >= 4 no longer
    # reads Module['wasmBinaryFile'], so without help the factory would ignore
    # the URL and fetch its own baked-in filename next to the worker (404 in
    # production). Wrap the factory to restore the old contract via the
    # still-supported Module['locateFile'] hook (default 
INCOMING_MODULE_JS_API):
    # when the caller passes wasmBinaryFile and no custom locateFile, every
    # findWasmBinary() call resolves to that URL. Pure JS, per factory call,
    # no emcc involvement; the Emscripten factory blob itself is untouched.
    return ('(()=>{var F=' + expr + ';return function(m){m=m||{};'
            'if(m.wasmBinaryFile&&!m.locateFile){var u=m.wasmBinaryFile;'
            'm.locateFile=function(){return u}}return F(m)}})()')


def main():
    glue_p, in_p, out_p = sys.argv[1:4]
    glue = open(glue_p).read()
    bundle = open(in_p).read()
    factory = extract_factory(glue)
    a = bundle.find(START)
    assert a >= 0 and bundle.count(START) == 1, 'factory start anchor not 
unique'
    e = bundle.find(END)
    assert e > a, 'factory end anchor missing'
    e += len('})();')
    spliced = bundle[:a] + 'var Pt=' + factory + ';' + bundle[e:]
    # The outer Cesium logic must be byte-identical: only the blob changed.
    assert bundle[:a] == spliced[:a]
    assert bundle[e:] == spliced[a + len('var Pt=' + factory + ';'):]
    open(out_p, 'w').write(spliced)
    print(f'splice ok: blob {e - a} -> {len(factory) + len("var Pt=;")} bytes')


main()

++++++ draco-check.mjs ++++++
// Decode smoke test for the rebuilt draco_decoder.wasm: instantiates draco's
// own nodejs glue (the same interface version the shipped Cesium glue was
// built against) with the freshly relinked wasm and checks structural output
// against values verified with the prebuilt module. Covers both Cesium decode
// paths: mesh (GltfDraco) and point cloud (I3S), plus graceful rejection of
// corrupt input (Status, never a throw).
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { strict as assert } from 'node:assert';
import path from 'node:path';

const dir = path.resolve(process.argv[2]);
const require = createRequire(import.meta.url);
const createDecoderModule = require(path.join(dir, 'draco_decoder_nodejs.js'));

const module = await createDecoderModule({});

// Mesh path (bunny.drc, draco's own sample)
{
  const data = new Uint8Array(readFileSync(path.join(dir, 'bunny.drc')));
  const decoder = new module.Decoder();
  const buf = new module.DecoderBuffer();
  buf.Init(data, data.length);
  assert.equal(decoder.GetEncodedGeometryType(buf), module.TRIANGULAR_MESH);
  const geom = new module.Mesh();
  const status = decoder.DecodeBufferToMesh(buf, geom);
  assert.ok(status.ok(), 'decode failed');
  const faces = geom.num_faces(), points = geom.num_points();
  console.log('draco-check: faces=' + faces + ' points=' + points);
  assert.equal(faces, 69451);
  assert.equal(points, 34834);
  const posId = decoder.GetAttributeId(geom, module.POSITION);
  const pos = decoder.GetAttribute(geom, posId);
  const tmp = new module.DracoFloat32Array();
  decoder.GetAttributeFloatForAllPoints(geom, pos, tmp);
  const vals = [tmp.GetValue(0), tmp.GetValue(1), tmp.GetValue(2)];
  console.log('draco-check: pos0=' + vals.join(','));
  const expected = [-0.09055579453706741, 0.14438968896865845, 
0.027556151151657104];
  for (let i = 0; i < 3; ++i) {
    assert.ok(Math.abs(vals[i] - expected[i]) < 1e-9, 'pos mismatch at ' + i);
  }
  // face indices (the Cesium decodeDraco path)
  const fi = new module.DracoInt32Array();
  assert.ok(decoder.GetFaceFromMesh(geom, 0, fi));
  console.log('draco-check: face0=' + [fi.GetValue(0), fi.GetValue(1), 
fi.GetValue(2)].join(','));
  module.destroy(fi);
  // unique-id lookup (the Cesium compressed-attributes path)
  const pa2 = decoder.GetAttributeByUniqueId(geom, pos.unique_id());
  const tmp2 = new module.DracoFloat32Array();
  decoder.GetAttributeFloatForAllPoints(geom, pa2, tmp2);
  assert.equal(tmp2.GetValue(0), tmp.GetValue(0));
  assert.equal(tmp2.GetValue(1), tmp.GetValue(1));
  console.log('draco-check: unique-id roundtrip OK');
  module.destroy(tmp2);
  module.destroy(buf);
  module.destroy(geom);
  module.destroy(decoder);
}
// Point-cloud path (cube_pc.drc from draco's testdata)
{
  const data = new Uint8Array(readFileSync(path.join(dir, 'cube_pc.drc')));
  const decoder = new module.Decoder();
  const buf = new module.DecoderBuffer();
  buf.Init(data, data.length);
  assert.equal(decoder.GetEncodedGeometryType(buf), module.POINT_CLOUD);
  const geom = new module.PointCloud();
  assert.ok(decoder.DecodeBufferToPointCloud(buf, geom).ok());
  assert.equal(geom.num_points(), 24);
  assert.equal(geom.num_attributes(), 3);
  const pa = decoder.GetAttribute(geom, decoder.GetAttributeId(geom, 
module.POSITION));
  const tmp = new module.DracoFloat32Array();
  decoder.GetAttributeFloatForAllPoints(geom, pa, tmp);
  assert.equal(tmp.size(), 72);
  const vals = [];
  for (let i = 0; i < 6; ++i) vals.push(tmp.GetValue(i));
  assert.deepEqual(vals, [0, 0, 0, 1, 1, 0]);
  console.log('draco-check: pointcloud OK (24 pts, pos head 0,0,0,1,1,0)');
  module.destroy(buf);
  module.destroy(geom);
  module.destroy(decoder);
}
// Small quantized mesh (cube_att.drc from draco's testdata)
{
  const data = new Uint8Array(readFileSync(path.join(dir, 'cube_att.drc')));
  const decoder = new module.Decoder();
  const buf = new module.DecoderBuffer();
  buf.Init(data, data.length);
  assert.equal(decoder.GetEncodedGeometryType(buf), module.TRIANGULAR_MESH);
  const geom = new module.Mesh();
  assert.ok(decoder.DecodeBufferToMesh(buf, geom).ok());
  assert.equal(geom.num_faces(), 12);
  assert.equal(geom.num_points(), 24);
  console.log('draco-check: small mesh OK (12 faces, 24 points)');
  module.destroy(buf);
  module.destroy(geom);
  module.destroy(decoder);
}
// Corrupt input must fail gracefully (Status, never a throw)
{
  const decoder = new module.Decoder();
  const buf = new module.DecoderBuffer();
  buf.Init(new Uint8Array([68, 82, 65, 67, 79, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
10]), 16);
  const geom = new module.Mesh();
  assert.ok(!decoder.DecodeBufferToMesh(buf, geom).ok(), 'corrupt input 
decoded?!');
  console.log('draco-check: corrupt input rejected as expected');
  module.destroy(buf);
  module.destroy(geom);
  module.destroy(decoder);
}
console.log('draco-check: OK');

++++++ draco-emscripten-python3.patch ++++++
>From 8b1f23e04391f60f9e2c7566c39d93e53fb54e33 Mon Sep 17 00:00:00 2001
From: upstream google/draco main
Date: backported to v1.5.7
Subject: [PATCH] Use FindPython module for Emscripten builds

CMake >= 4 removed the FindPythonInterp module, so the Emscripten
configure step fails. Switch to find_package(Python COMPONENTS
Interpreter) like upstream main does (commits 8b1f23e04391 and the
a4365c7935ca follow-up fix).

--- a/draco-1.5.7/CMakeLists.txt
+++ b/draco-1.5.7/CMakeLists.txt
@@ -37,7 +37,7 @@ if("${draco_root}" STREQUAL "${draco_bu
       "And re-run CMake from the draco_build directory.")
 endif()
 
-include(FindPythonInterp)
+find_package(Python COMPONENTS Interpreter)
 include("${draco_root}/cmake/draco_build_definitions.cmake")
 include("${draco_root}/cmake/draco_cpu_detection.cmake")
 include("${draco_root}/cmake/draco_dependencies.cmake")
--- a/draco-1.5.7/cmake/draco_emscripten.cmake
+++ b/draco-1.5.7/cmake/draco_emscripten.cmake
@@ -18,7 +18,7 @@ endif() # DRACO_CMAKE_DRACO_EMSCRIPTEN_C
 
 # Checks environment for Emscripten prerequisites.
 macro(draco_check_emscripten_environment)
-  if(NOT PYTHONINTERP_FOUND)
+  if(NOT Python_Interpreter_FOUND)
     message(
         FATAL_ERROR
         "Python required for Emscripten builds, but cmake cannot find it.")
@@ -118,7 +118,7 @@ macro(draco_generate_emscripten_glue)
 
   # Generate the glue source.
   execute_process(
-    COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
+    COMMAND ${Python_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
             ${glue_INPUT_IDL} ${glue_OUTPUT_PATH})
   if(NOT EXISTS "${glue_OUTPUT_PATH}.cpp")
     message(FATAL_ERROR "JS glue generation failed for ${glue_INPUT_IDL}.")
@@ -129,7 +129,7 @@ macro(draco_generate_emscripten_glue)
   # Create a dependency so that it regenerated on edits.
   add_custom_command(
     OUTPUT "${glue_OUTPUT_PATH}.cpp"
-    COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
+    COMMAND ${Python_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
             ${glue_INPUT_IDL} ${glue_OUTPUT_PATH}
     DEPENDS ${draco_js_dec_idl}
     COMMENT "Generating ${glue_OUTPUT_PATH}.cpp."

++++++ draco-relink.py ++++++
"""Interface-adapt a fresh emscripten-6 wasm to the OLD vendored glue.

The shipped Cesium glue (pinned in the sdrangel tree) was generated with an
old emscripten; its wasm import/export names are version-specific. A rebuild
with the current toolchain produces a functionally identical module with
different linkage names. This script rewrites the new module's import/export
sections so the OLD glue loads it, verified by:

- symbol-identity for every bound function (joined on the binding symbol
  the two glues agree on; sets must match EXACTLY),
- role-identity for memory/table/ctors/malloc/free (joined by section kind
  and by the glue's init protocol, not by name),
- explicit semantic mapping for the runtime imports (the only part that
  needs human judgment; everything else is derived),
- dropping every export the old glue never references (asserted), and
- a node smoke test with the OLD glue as the behavioral proof.

Usage:
  relink.py OLDGLUE NEWGLUE IN.wasm OUT.wasm IMPORTMAP
  IMPORTMAP: comma list newimportname:oldimportname (module is "a")

Fails loudly on any toolchain drift (changed symbol sets, missing names,
unexpected import counts). Deterministic: same inputs -> same bytes.
"""
import re
import sys


def read_uleb(buf, pos):
    r = 0
    s = 0
    while True:
        b = buf[pos]
        pos += 1
        r |= (b & 0x7f) << s
        if not (b & 0x80):
            return r, pos
        s += 7


def write_uleb(v):
    out = bytearray()
    while True:
        b = v & 0x7f
        v >>= 7
        if v:
            out.append(b | 0x80)
        else:
            out.append(b)
            return bytes(out)


def read_str(buf, pos):
    ln, pos = read_uleb(buf, pos)
    return buf[pos:pos + ln].decode(), pos + ln


def split_sections(wasm):
    parts = []
    pos = 8
    while pos < len(wasm):
        sid = wasm[pos]
        pos += 1
        size, pos = read_uleb(wasm, pos)
        parts.append((sid, bytes(wasm[pos:pos + size])))
        pos += size
    return parts


def parse_imports(body):
    n, p = read_uleb(body, 0)
    out = []
    for _ in range(n):
        mod, p = read_str(body, p)
        nm, p = read_str(body, p)
        kind = body[p]
        p += 1
        start = p
        if kind == 0:
            _, p = read_uleb(body, p)
        elif kind == 1:
            p += 1
            fl = body[p]
            p += 1
            _, p = read_uleb(body, p)
            if fl & 1:
                _, p = read_uleb(body, p)
        elif kind == 2:
            p += 1
            fl = body[p]
            p += 1
            _, p = read_uleb(body, p)
            if fl & 1:
                _, p = read_uleb(body, p)
        elif kind == 3:
            fl = body[p]
            p += 1
            _, p = read_uleb(body, p)
            if fl & 1:
                _, p = read_uleb(body, p)
        elif kind == 4:
            p += 2
        else:
            raise AssertionError(f'import kind {kind}')
        out.append((mod, nm, kind, body[start:p]))
    assert p == len(body)
    return out


def parse_exports(body):
    n, p = read_uleb(body, 0)
    out = []
    for _ in range(n):
        nm, p = read_str(body, p)
        kind = body[p]
        p += 1
        idx, p = read_uleb(body, p)
        out.append((nm, kind, idx))
    assert p == len(body)
    return out


def old_npm_symbols(src):
    """{symbol: exportname} from draco3d-npm-style glue (a.asm.X refs)."""
    m = {}
    pat1 = (r'a\.(_emscripten_\w+|_[A-Za-z][A-Za-z0-9_]*)\s*=\s*function\(\)'
            r'\s*\{\s*return\s*\(\s*[A-Za-z$]+\s*=\s*a\.\1\s*=\s*a\.asm\.'
            r'([A-Za-z0-9_$]+)\s*\)')
    for x in re.finditer(pat1, src):
        m[x.group(1)] = x.group(2)
    pat2 = (r'[\w$]+\s*=\s*function\(\)\s*\{\s*return\s*\(\s*[\w$]+\s*=\s*a\.'
            r'(_emscripten_\w+|_[A-Za-z][A-Za-z0-9_]*)\s*=\s*a\.asm\.'
            r'([A-Za-z0-9_$]+)\s*\)')
    for x in re.finditer(pat2, src):
        m[x.group(1)] = x.group(2)
    # direct form: a._malloc=function(){return(a._malloc=a.asm.Tb).apply...}
    pat3 = (r'a\.(_emscripten_\w+|_[A-Za-z][A-Za-z0-9_]*)\s*=\s*function\(\)'
            r'\s*\{\s*return\s*\(\s*a\.\1\s*=\s*a\.asm\.'
            r'([A-Za-z0-9_$]+)\s*\)')
    for x in re.finditer(pat3, src):
        m[x.group(1)] = x.group(2)
    return m


def old_npm_direct_refs(src):
    """Set of bare a.asm.X names (memory/ctors/table slots)."""
    return set(re.findall(r'a\.asm\.([A-Za-z0-9_$]+)(?![A-Za-z0-9_$])', src))


def new_symbols(src):
    """{symbol: exportname} from emscripten-6 glue (wasmExports refs)."""
    m = {}
    for x in 
re.finditer(r'(_emscripten_\w+|_[A-Za-z][A-Za-z0-9_]*|__[A-Za-z]\w*)'
                         r'=Module\["\1"\]=wasmExports\["([^"]+)"\]', src):
        m[x.group(1)] = x.group(2)
    return m


def new_init_ctors(src):
    """Export name invoked by the new glue's initRuntime (ctors runner)."""
    m = re.search(r'initRuntime\(\)\{[^}]*?wasmExports\["([^"]+)"\]\(\)', src)
    assert m, 'no ctors call in new glue initRuntime'
    return m.group(1)


def main():
    old_glue_path, new_glue_path, in_wasm, out_wasm = sys.argv[1:5]
    import_map = {}
    if len(sys.argv) > 5 and sys.argv[5]:
        for pair in sys.argv[5].split(','):
            n, o = pair.split(':')
            import_map[n] = o
    old_src = open(old_glue_path).read()
    new_src = open(new_glue_path).read()

    old = old_npm_symbols(old_src)
    new = new_symbols(new_src)
    # _webidl_* are new-glue-internal malloc aliases: the old glue never
    # mentions them (proved below), its _malloc/_free mapping covers them.
    glue_internal = {s for s in new if s not in old}
    assert all('webidl' in s for s in glue_internal), glue_internal
    assert 'webidl' not in old_src, 'old glue needs webidl symbols'
    for s in glue_internal:
        del new[s]
    only_old = sorted(set(old) - set(new))
    only_new = sorted(set(new) - set(old))
    assert not only_old, f'symbols lost in rebuild: {only_old}'
    assert not only_new, f'symbols added in rebuild: {only_new}'
    print(f'symbol sets identical: {len(new)} symbols')

    wasm = bytearray(open(in_wasm, 'rb').read())
    assert wasm[:4] == b'\0asm'
    parts = split_sections(wasm)
    imp_body = next(b for s, b in parts if s == 2)
    exp_body = next(b for s, b in parts if s == 7)
    imports = parse_imports(imp_body)
    exports = parse_exports(exp_body)

    # --- import mapping: every module-"a" import must be covered ---
    new_a_imports = sorted(nm for mod, nm, k, d in imports if mod == 'a')
    assert set(import_map) == set(new_a_imports), \
        f'import map mismatch: map={sorted(import_map)} wasm={new_a_imports}'

    # --- export mapping ---
    exp_by_name = {nm: (kind, idx) for nm, kind, idx in exports}
    old_refs = old_npm_direct_refs(old_src)

    # memory: unique kind=2 on both sides; old name = the one old glue derefs
    mems = [nm for nm, k, i in exports if k == 2]
    assert len(mems) == 1, mems
    # old memory name by role from the init sequence:
    #   <var>=a.asm.<MEM>;  (e.g. ja=a.asm.h;) — unique in the glue.
    memstmts = re.findall(r'(\w+)=a\.asm\.([A-Za-z0-9_$]+);', old_src)
    assert len(memstmts) == 1, memstmts
    old_mem = memstmts[0][1]
    # ctors: the export queued into the init callbacks
    cm = re.search(r'(?:unshift|addOnInit)\(a\.asm\.([A-Za-z0-9_$]+)\)', 
old_src)
    assert cm, 'old ctors hook not found'
    old_ctors = cm.group(1)
    new_ctors = new_init_ctors(new_src)
    assert new_ctors in exp_by_name, f'ctors {new_ctors} not exported'

    export_map = {}
    for sym in new:
        if new[sym] != old[sym]:
            export_map[new[sym]] = old[sym]
    # runtime roles
    assert old_mem in old_refs, old_mem
    export_map[mems[0]] = old_mem
    export_map[new_ctors] = old_ctors
    # malloc/free by jsname (already in symbol tables via _malloc/_free)
    # every old-referenced name must resolve, except the one known-dead
    # alias: Vb is an orphan lazy binding (var ya=...a.asm.Vb, never called
    # except from the C++ exception-unwind path). Draco reports errors via
    # Status, never via C++ exceptions, so that path is unreachable; the
    # corrupt-input smoke test proves graceful Status failure instead.
    assigned_old = set(export_map.values())
    dead = sorted(r for r in old_refs if r not in assigned_old)
    assert dead == ['Vb'], f'unexpected dead refs: {dead}'

    # drop unmapped new exports (provably unused: old glue never names them)
    keep = set(export_map) | {mems[0], new_ctors}
    new_exp = [(export_map.get(nm, nm), kind, idx) for nm, kind, idx in exports
               if nm in keep or nm in export_map]
    # collision check: kept original names must not clash with mapped names
    final_names = [nm for nm, k, i in new_exp]
    assert len(set(final_names)) == len(final_names), 'export name collision'
    assert len(new_exp) == len(keep), (len(new_exp), len(keep))

    new_imp = []
    for mod, nm, kind, desc in imports:
        nm2 = import_map.get(nm, nm) if mod == 'a' else nm
        if mod == 'a':
            assert nm in import_map, nm
        new_imp.append((mod, nm2, kind, desc))

    def enc_section(entries, is_imp):
        out = bytearray(write_uleb(len(entries)))
        for e in entries:
            if is_imp:
                mod, nm, kind, desc = e
                out += write_uleb(len(mod)) + mod.encode()
                out += write_uleb(len(nm)) + nm.encode()
                out += bytes([kind]) + desc
            else:
                nm, kind, idx = e
                out += write_uleb(len(nm)) + nm.encode()
                out += bytes([kind]) + write_uleb(idx)
        return bytes(out)

    out = bytearray(wasm[:8])
    for sid, body in parts:
        if sid == 2:
            body = enc_section(new_imp, True)
        elif sid == 7:
            body = enc_section(new_exp, False)
        out += bytes([sid]) + write_uleb(len(body)) + body
    open(out_wasm, 'wb').write(out)
    print(f'relinked: {len(export_map)} exports renamed, '
          f'{len(new_exp)}/{len(exports)} exports kept, '
          f'{len(new_imp)} imports mapped -> {out_wasm}')


if __name__ == '__main__':
    main()

++++++ sdrangel-8.0.0.tar.gz -> sdrangel-8.1.0.tar.gz ++++++
/work/SRC/openSUSE:Factory/sdrangel/sdrangel-8.0.0.tar.gz 
/work/SRC/openSUSE:Factory/.sdrangel.new.3759290/sdrangel-8.1.0.tar.gz differ: 
char 4, line 1

++++++ splats-check.mjs ++++++
// Smoke-test for the from-source wasm_splats_bg.wasm build: instantiates the
// module with its generated JS glue and checks both exported functions against
// outputs verified bit-identical with the previously shipped prebuilt module.
import { readFileSync } from 'node:fs';
import { pathToFileURL } from 'node:url';
import { strict as assert } from 'node:assert';

const outDir = process.argv[2];
const glue = await import(pathToFileURL(`${outDir}/wasm_splats.js`).href);
const bytes = readFileSync(`${outDir}/wasm_splats_bg.wasm`);
await glue.default(new Response(bytes, { headers: { 'Content-Type': 
'application/wasm' } }));

const positions = new Float32Array([0,0,1, 1,2,3, -1,0,2, 0.5,0.5,0.5, 2,2,2]);
const scales = new Float32Array([1,1,1, 2,2,2, 0.5,0.5,0.5, 1,2,1, 3,3,3]);
const rotations = new Float32Array([0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 
0,0,0,1]);
const colors = new Uint8Array([255,0,0,255, 0,255,0,255, 0,0,255,255, 
255,255,0,255, 0,255,255,255]);
const modelView = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);

const tex = glue.generate_splat_texture(positions, scales, rotations, colors, 
5);
assert.equal(tex.width, 2048);
assert.equal(tex.height, 1);
assert.deepEqual(Array.from(tex.data.slice(0, 8)), [0, 0, 1065353216, 0, 17408, 
1140850688, 1140850688, 4278190335]);

const idx = glue.radix_sort_gaussians_indexes(positions, modelView, 5);
assert.deepEqual(Array.from(idx), [3, 0, 2, 4, 1]);

console.log('splats-check: generate_splat_texture + 
radix_sort_gaussians_indexes OK');

Reply via email to