diff options
Diffstat (limited to 'flatpak')
| -rw-r--r-- | flatpak/com.fluorine.manager.desktop | 22 | ||||
| -rw-r--r-- | flatpak/com.fluorine.manager.metainfo.xml | 38 | ||||
| -rw-r--r-- | flatpak/com.fluorine.manager.png | bin | 0 -> 51022 bytes | |||
| -rw-r--r-- | flatpak/com.fluorine.manager.yml | 364 | ||||
| -rwxr-xr-x | flatpak/flatpak-install.sh | 58 | ||||
| -rwxr-xr-x | flatpak/fluorine-manager-wrapper.sh | 109 |
6 files changed, 591 insertions, 0 deletions
diff --git a/flatpak/com.fluorine.manager.desktop b/flatpak/com.fluorine.manager.desktop new file mode 100644 index 0000000..ff62c76 --- /dev/null +++ b/flatpak/com.fluorine.manager.desktop @@ -0,0 +1,22 @@ +[Desktop Entry] +Type=Application +Name=Fluorine Manager +GenericName=Mod Manager +Comment=Mod Organizer 2 for Linux - manage your game mods +Exec=fluorine-manager +Icon=com.fluorine.manager +Terminal=false +Categories=Game;Utility; +Keywords=mod;organizer;modding;skyrim;fallout; +MimeType=x-scheme-handler/nxm; +Actions=create-portable;list-instances; + +[Desktop Action create-portable] +Name=Create Portable Instance +Exec=fluorine-manager create-portable +Icon=com.fluorine.manager + +[Desktop Action list-instances] +Name=List Instances +Exec=fluorine-manager list-instances +Icon=com.fluorine.manager diff --git a/flatpak/com.fluorine.manager.metainfo.xml b/flatpak/com.fluorine.manager.metainfo.xml new file mode 100644 index 0000000..1200d3e --- /dev/null +++ b/flatpak/com.fluorine.manager.metainfo.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component type="desktop-application"> + <id>com.fluorine.manager</id> + <name>Fluorine Manager</name> + <summary>Mod Organizer 2 for Linux</summary> + <metadata_license>CC0-1.0</metadata_license> + <project_license>GPL-3.0-or-later</project_license> + + <description> + <p> + Fluorine Manager is a Linux port of Mod Organizer 2, a powerful mod management + tool for Bethesda games and other titles. It uses a virtual filesystem to keep + your game directory clean while allowing complex mod setups with load order + management, conflict resolution, and profile support. + </p> + <p>Features:</p> + <ul> + <li>Virtual filesystem - mods never touch your game folder</li> + <li>Profile system for different mod configurations</li> + <li>Plugin load order management</li> + <li>Mod conflict detection and resolution</li> + <li>Nexus Mods integration for downloading</li> + <li>Proton/Wine prefix management for running Windows games</li> + <li>UMU launcher support</li> + </ul> + </description> + + <launchable type="desktop-id">com.fluorine.manager.desktop</launchable> + + <url type="homepage">https://github.com/ModOrganizer2/modorganizer</url> + <url type="bugtracker">https://github.com/ModOrganizer2/modorganizer/issues</url> + + <provides> + <binary>fluorine-manager</binary> + </provides> + + <content_rating type="oars-1.1" /> +</component> diff --git a/flatpak/com.fluorine.manager.png b/flatpak/com.fluorine.manager.png Binary files differnew file mode 100644 index 0000000..3a9fbdd --- /dev/null +++ b/flatpak/com.fluorine.manager.png diff --git a/flatpak/com.fluorine.manager.yml b/flatpak/com.fluorine.manager.yml new file mode 100644 index 0000000..a0e2122 --- /dev/null +++ b/flatpak/com.fluorine.manager.yml @@ -0,0 +1,364 @@ +app-id: com.fluorine.manager +runtime: org.kde.Platform +runtime-version: '6.10' +sdk: org.kde.Sdk +sdk-extensions: + - org.freedesktop.Sdk.Extension.rust-stable +command: fluorine-manager + +finish-args: + - --share=ipc + - --share=network + - --socket=x11 + - --socket=wayland + - --device=all + - --filesystem=home + - --filesystem=/run/media:ro + - --filesystem=~/.steam:ro + - --filesystem=~/.local/share/Steam + - --filesystem=~/.var/app/com.valvesoftware.Steam:ro + - --talk-name=org.freedesktop.Flatpak + +cleanup: + - /include + - /lib/cmake + - /lib/pkgconfig + - /share/doc + - /share/man + - '*.a' + - '*.la' + +modules: + # ── 1. Boost ── + - name: boost + buildsystem: simple + build-commands: + - ./bootstrap.sh --prefix=/app --with-libraries=program_options,thread,filesystem,system,locale + - ./b2 install -j$FLATPAK_BUILDER_N_JOBS link=shared variant=release + sources: + - type: archive + url: https://archives.boost.io/release/1.87.0/source/boost_1_87_0.tar.gz + sha256: f55c340aa49763b1925ccf02b2e83f35fdcf634c9d5164a2acb87540173c741d + + # ── 2. spdlog ── + - name: spdlog + buildsystem: cmake-ninja + config-opts: + - -DCMAKE_BUILD_TYPE=Release + - -DSPDLOG_BUILD_SHARED=ON + sources: + - type: git + url: https://github.com/gabime/spdlog.git + tag: v1.15.2 + + # ── 3. tinyxml2 ── + - name: tinyxml2 + buildsystem: cmake-ninja + config-opts: + - -DCMAKE_BUILD_TYPE=Release + sources: + - type: git + url: https://github.com/leethomason/tinyxml2.git + tag: 10.0.0 + + # ── 4. tomlplusplus ── + - name: tomlplusplus + buildsystem: cmake-ninja + config-opts: + - -DCMAKE_BUILD_TYPE=Release + sources: + - type: git + url: https://github.com/marzer/tomlplusplus.git + tag: v3.4.0 + + # ── 5. libfuse3 ── + # Built manually to install fusermount3 without setuid (chown root fails in sandbox). + # default_library=both produces libfuse3.a so the VFS helper can link statically. + - name: libfuse3 + buildsystem: simple + build-commands: + - meson setup _build --prefix=/app -Dexamples=false -Dtests=false -Dutils=true -Ddefault_library=both + - ninja -C _build + # Install library and headers (skip meson install which runs chown). + - ninja -C _build install || true + # Ensure fusermount3 is installed even if install_helper.sh failed. + - install -Dm755 _build/util/fusermount3 /app/bin/fusermount3 2>/dev/null || true + sources: + - type: git + url: https://github.com/libfuse/libfuse.git + tag: fuse-3.16.2 + + # ── 6. pybind11 ── + - name: pybind11 + buildsystem: cmake-ninja + config-opts: + - -DCMAKE_BUILD_TYPE=Release + - -DPYBIND11_TEST=OFF + sources: + - type: git + url: https://github.com/pybind/pybind11.git + tag: v2.13.6 + + # ── 7. libloot ── + # libloot has a Rust crate at the root and a C++ cmake project in cpp/. + # The Rust static library must be built first, then cmake links against it. + - name: libloot + buildsystem: simple + build-options: + append-path: /usr/lib/sdk/rust-stable/bin + env: + CARGO_HOME: /run/build/libloot/cargo + # Network needed for cargo deps and cmake FetchContent (googletest). + build-args: + - --share=network + build-commands: + # Build Rust static library first. + - cargo build --release + # Build the C++ shared library (links against Rust static lib). + - | + cmake -S cpp -B cpp/build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=ON \ + -DLIBLOOT_INSTALL_DOCS=OFF \ + -DCMAKE_INSTALL_PREFIX=/app + - cmake --build cpp/build --parallel + - cmake --install cpp/build + # Create pkg-config metadata. + - mkdir -p /app/lib/pkgconfig + - | + printf '%s\n' \ + 'prefix=/app' \ + 'exec_prefix=${prefix}' \ + 'libdir=${prefix}/lib' \ + 'includedir=${prefix}/include' \ + '' \ + 'Name: libloot' \ + 'Description: LOOT C++ API library' \ + 'Version: 0.29.0' \ + 'Libs: -L${libdir} -lloot' \ + 'Cflags: -I${includedir}' \ + > /app/lib/pkgconfig/libloot.pc + sources: + - type: git + url: https://github.com/loot/libloot.git + branch: master + + # ── 8. icoutils ── + - name: icoutils + buildsystem: autotools + build-options: + # icoutils 0.32.3 uses K&R-style () declarations that GCC 14+ rejects. + cflags: -std=gnu17 + sources: + - type: archive + url: https://download.savannah.gnu.org/releases/icoutils/icoutils-0.32.3.tar.bz2 + sha256: 17abe02d043a253b68b47e3af69c9fc755b895db68fdc8811786125df564c6e0 + + # ── 9. cabextract ── + # Required by winetricks for extracting Windows cab archives. + - name: cabextract + buildsystem: autotools + sources: + - type: archive + url: https://www.cabextract.org.uk/cabextract-1.11.tar.gz + sha256: b5546db1155e4c718ff3d4b278573604f30dd64c3c5bfd4657cd089b823a3ac6 + + # ── 10. Portable Python runtime ── + # Pre-built Python used as the embedded interpreter for MO2's Python plugins. + # pip packages are installed via network during build (--share=network). + - name: portable-python + buildsystem: simple + build-options: + build-args: + - --share=network + build-commands: + # Extract, trim, symlink, and install pip packages in one step + # (each build-command runs in its own shell). + - | + set -e + mkdir -p /app/lib/fluorine/python + unzip portable-python.zip -d /tmp/pp + PP_DIR="$(find /tmp/pp -maxdepth 1 -mindepth 1 -type d | head -1)" + cp -a "${PP_DIR}/." /app/lib/fluorine/python/ + rm -rf /tmp/pp + # Versioned soname symlink (pybind11 links against libpython3.13.so.1.0). + if [ -f /app/lib/fluorine/python/lib/libpython3.13.so ] && \ + [ ! -f /app/lib/fluorine/python/lib/libpython3.13.so.1.0 ]; then + ln -sf libpython3.13.so /app/lib/fluorine/python/lib/libpython3.13.so.1.0 + fi + # Trim test suites and unnecessary files. + rm -rf /app/lib/fluorine/python/lib/python3.13/test \ + /app/lib/fluorine/python/lib/python3.13/unittest/test \ + /app/lib/fluorine/python/lib/python3.13/idlelib \ + /app/lib/fluorine/python/lib/python3.13/tkinter \ + /app/lib/fluorine/python/lib/python3.13/turtledemo \ + /app/lib/fluorine/python/include \ + /app/lib/fluorine/python/share \ + 2>/dev/null || true + find /app/lib/fluorine/python -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true + find /app/lib/fluorine/python -name "*.pyc" -delete 2>/dev/null || true + # Install pip packages (PyQt6 for plugin UI, psutil, vdf). + /app/lib/fluorine/python/bin/python3 -m ensurepip --default-pip 2>/dev/null || true + /app/lib/fluorine/python/bin/python3 -m pip install --no-cache-dir PyQt6 psutil vdf + sources: + - type: file + url: https://github.com/bjia56/portable-python/releases/download/cpython-v3.13.9-build.0/python-headless-3.13.9-linux-x86_64.zip + sha256: d03507d107da86d74aa38bbc4957b8db5753de567024c5724a254909ae6b86d6 + dest-filename: portable-python.zip + + # ── 10. Fluorine Manager (main project) ── + - name: fluorine + buildsystem: simple + build-options: + append-path: /usr/lib/sdk/rust-stable/bin + env: + CARGO_HOME: /run/build/fluorine/cargo + PYTHONPATH: /app/lib/python3/dist-packages + # Network needed for FetchContent (7zip, etc.) and cargo deps. + build-args: + - --share=network + build-commands: + # Install sip build tools into /app (SDK /usr is read-only). + - /usr/bin/python3 -m pip install --target=/app/lib/python3/dist-packages sip + + # ── Configure and build ── + - | + cmake -S . -B _build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH=/app \ + -DPython3_ROOT_DIR=/app/lib/fluorine/python \ + -DPython3_EXECUTABLE=/app/lib/fluorine/python/bin/python3 \ + -DBUILD_PLUGIN_PYTHON=ON + - cmake --build _build --parallel + + # ── Create output layout ── + - mkdir -p /app/lib/fluorine/plugins /app/lib/fluorine/dlls /app/lib/fluorine/lib + + # Main binary. + - cp -f _build/src/src/ModOrganizer /app/lib/fluorine/ModOrganizer-core + + # umu-run (patch and repackage if present). + - | + if [ -f _build/src/src/umu-run ]; then + UMU_PATCH_DIR="$(mktemp -d)" + cd "${UMU_PATCH_DIR}" + python3 << 'PATCHEOF' + import zipfile, pathlib + zf = zipfile.ZipFile('/run/build/fluorine/_build/src/src/umu-run') + zf.extractall('src') + run_py = pathlib.Path('src/umu/umu_run.py') + src = run_py.read_text() + old1 = ' env["STEAM_COMPAT_INSTALL_PATH"] = os.environ.get("STEAM_COMPAT_INSTALL_PATH", "")' + new1 = (old1 + + '\n env["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = os.environ.get(' + + '\n "STEAM_COMPAT_CLIENT_INSTALL_PATH", ""' + + '\n )') + if old1 in src and 'STEAM_COMPAT_CLIENT_INSTALL_PATH"] = os.environ' not in src: + src = src.replace(old1, new1) + old2 = ' os.environ[key] = val' + new2 = (old2 + + '\n\n # GE-Proton treats games with UMU_ID as non-Steam titles and skips' + + '\n # the steam.exe DRM bridge. Remove it for Steam games.' + + '\n if "UMU_ID" in os.environ and env.get("SteamAppId", "0") != "0":' + + '\n del os.environ["UMU_ID"]') + if old2 in src and 'del os.environ["UMU_ID"]' not in src: + src = src.replace(old2, new2, 1) + run_py.write_text(src) + PATCHEOF + python3 -m zipapp src -o /app/lib/fluorine/umu-run -p '/usr/bin/env python3' + chmod +x /app/lib/fluorine/umu-run + cd /run/build/fluorine + rm -rf "${UMU_PATCH_DIR}" + fi + + # VFS helper (standalone, runs on host for Flatpak FUSE support). + # Statically linked against libfuse3 — no runtime deps beyond glibc. + - test -f _build/src/src/mo2-vfs-helper && cp -f _build/src/src/mo2-vfs-helper /app/lib/fluorine/ || true + + # lootcli. + - test -f _build/libs/lootcli/src/lootcli && cp -f _build/libs/lootcli/src/lootcli /app/lib/fluorine/ || true + + # icoutils (symlink from /app/bin where the icoutils module installed them). + - ln -sf /app/bin/wrestool /app/lib/fluorine/wrestool + - ln -sf /app/bin/icotool /app/lib/fluorine/icotool + - ln -sf /app/bin/fusermount3 /app/lib/fluorine/fusermount3 + - ln -sf /app/bin/cabextract /app/lib/fluorine/cabextract + + # fluorine-manager CLI helper. + - test -f src/fluorine-manager && cp -f src/fluorine-manager /app/lib/fluorine/ || true + + # MO2 plugins (.so). + - | + find _build/libs -type f \( \ + -name "libgame_*.so" -o \ + -name "libinstaller_*.so" -o \ + -name "libpreview_*.so" -o \ + -name "libdiagnose_*.so" -o \ + -name "libcheck_*.so" -o \ + -name "libtool_*.so" -o \ + -name "libinieditor.so" -o \ + -name "libinibakery.so" -o \ + -name "libbsa_extractor.so" -o \ + -name "libbsa_packer.so" -o \ + -name "libproxy.so" \ + \) -exec cp -f {} /app/lib/fluorine/plugins/ \; + + # Python plugin payload. + - | + for f in libplugin_python.so lzokay.py winreg.py pyCfg.py \ + DDSPreview.py Form43Checker.py ScriptExtenderPluginChecker.py; do + [ -f "_build/src/src/plugins/${f}" ] && cp -f "_build/src/src/plugins/${f}" /app/lib/fluorine/plugins/ + done + for d in basic_games data libs dlls; do + [ -d "_build/src/src/plugins/${d}" ] && cp -a "_build/src/src/plugins/${d}" /app/lib/fluorine/plugins/ + done + rm -f /app/lib/fluorine/plugins/FNIS*.py + + # 7z runtime. + - | + if [ -f _build/src/src/dlls/7z.so ]; then + cp -f _build/src/src/dlls/7z.so /app/lib/fluorine/dlls/7z.so + cp -f _build/src/src/dlls/7z.so /app/lib/fluorine/dlls/7zip.dll + fi + + # Project shared libraries. + - cp -f _build/libs/uibase/src/libuibase.so /app/lib/fluorine/lib/ + - cp -f _build/libs/libbsarch/liblibbsarch.so /app/lib/fluorine/lib/ + - cp -f _build/libs/archive/src/libarchive.so /app/lib/fluorine/lib/ + - test -f _build/libs/plugin_python/src/runner/librunner.so && cp -f _build/libs/plugin_python/src/runner/librunner.so /app/lib/fluorine/lib/ || true + - test -f libs/bsa_ffi/target/release/libbsa_ffi.so && cp -f libs/bsa_ffi/target/release/libbsa_ffi.so /app/lib/fluorine/lib/ || true + - test -f libs/nak_ffi/target/release/libnak_ffi.so && cp -f libs/nak_ffi/target/release/libnak_ffi.so /app/lib/fluorine/lib/ || true + + # Boost libraries (from /app/lib where the boost module installed them). + - cp -Lf /app/lib/libboost_program_options.so* /app/lib/fluorine/lib/ 2>/dev/null || true + - cp -Lf /app/lib/libboost_thread.so* /app/lib/fluorine/lib/ 2>/dev/null || true + + # libloot (from /app/lib where the libloot module installed it). + - | + if [ -f /app/lib/libloot.so.0 ]; then + cp -Lf /app/lib/libloot.so.0 /app/lib/fluorine/lib/ + ln -sf libloot.so.0 /app/lib/fluorine/lib/libloot.so + elif [ -f /app/lib/libloot.so ]; then + cp -Lf /app/lib/libloot.so /app/lib/fluorine/lib/ + fi + + # Build-tree Python plugin payload. + - test -d _build/src/src/python && cp -a _build/src/src/python/. /app/lib/fluorine/python/ || true + + # Strip binaries. + - strip --strip-unneeded /app/lib/fluorine/ModOrganizer-core 2>/dev/null || true + - find /app/lib/fluorine/plugins -name "*.so" -exec strip --strip-unneeded {} \; 2>/dev/null || true + - find /app/lib/fluorine/dlls \( -name "*.so" -o -name "*.dll" \) -exec strip --strip-unneeded {} \; 2>/dev/null || true + - find /app/lib/fluorine/lib -name "*.so*" -exec strip --strip-unneeded {} \; 2>/dev/null || true + - test -f /app/lib/fluorine/lootcli && strip --strip-unneeded /app/lib/fluorine/lootcli 2>/dev/null || true + - test -f /app/lib/fluorine/mo2-vfs-helper && strip --strip-unneeded /app/lib/fluorine/mo2-vfs-helper 2>/dev/null || true + + # Install wrapper script, desktop file, metainfo, and icon. + - install -Dm755 flatpak/fluorine-manager-wrapper.sh /app/bin/fluorine-manager + - install -Dm644 flatpak/com.fluorine.manager.desktop /app/share/applications/com.fluorine.manager.desktop + - install -Dm644 flatpak/com.fluorine.manager.metainfo.xml /app/share/metainfo/com.fluorine.manager.metainfo.xml + - install -Dm644 flatpak/com.fluorine.manager.png /app/share/icons/hicolor/256x256/apps/com.fluorine.manager.png + + sources: + - type: dir + path: .. diff --git a/flatpak/flatpak-install.sh b/flatpak/flatpak-install.sh new file mode 100755 index 0000000..e52497e --- /dev/null +++ b/flatpak/flatpak-install.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Build the Fluorine Manager Flatpak. +# +# Usage: +# cd flatpak && bash flatpak-install.sh # build & install locally +# cd flatpak && bash flatpak-install.sh bundle # build a .flatpak file to share +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "${SCRIPT_DIR}")" +MANIFEST="${SCRIPT_DIR}/com.fluorine.manager.yml" +APP_ID="com.fluorine.manager" +BUILD_DIR="${PROJECT_DIR}/.flatpak-build" +MODE="${1:-install}" + +# ── Ensure flathub remote exists ── +if ! flatpak remote-list --user | grep -q flathub; then + echo "Adding flathub remote..." + flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo +fi + +# ── Install required Flatpak runtime and SDK ── +echo "Installing KDE Platform and SDK (may take a while on first run)..." +flatpak install --user --noninteractive flathub org.kde.Platform//6.10 org.kde.Sdk//6.10 + +echo "Installing Rust SDK extension..." +flatpak install --user --noninteractive flathub org.freedesktop.Sdk.Extension.rust-stable//25.08 + +cd "${PROJECT_DIR}" + +if [ "${MODE}" = "bundle" ]; then + # ── Build a distributable .flatpak file ── + echo "" + echo "Building Flatpak bundle (this may take a while)..." + flatpak-builder --repo="${PROJECT_DIR}/flatpak-repo" --force-clean \ + "${BUILD_DIR}" "${MANIFEST}" + flatpak build-bundle "${PROJECT_DIR}/flatpak-repo" "${PROJECT_DIR}/fluorine-manager.flatpak" "${APP_ID}" + BUNDLE_SIZE="$(du -sh fluorine-manager.flatpak | cut -f1)" + echo "" + echo "=== Bundle created: fluorine-manager.flatpak (${BUNDLE_SIZE}) ===" + echo "" + echo "Share this file with testers. They install it with:" + echo " flatpak install --user fluorine-manager.flatpak" +else + # ── Build and install locally ── + echo "" + echo "Building and installing Flatpak locally (this may take a while)..." + flatpak-builder --install --user --force-clean \ + "${BUILD_DIR}" "${MANIFEST}" + echo "" + echo "=== Flatpak installed successfully ===" +fi + +echo "" +echo "Usage:" +echo " flatpak run ${APP_ID} # launch GUI" +echo " flatpak run ${APP_ID} create-portable --name myinstance --game falloutnv" +echo " flatpak run ${APP_ID} list-instances" diff --git a/flatpak/fluorine-manager-wrapper.sh b/flatpak/fluorine-manager-wrapper.sh new file mode 100755 index 0000000..fabe535 --- /dev/null +++ b/flatpak/fluorine-manager-wrapper.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# Flatpak wrapper for Fluorine Manager (MO2 Linux) +# Sets up a writable overlay so users can add custom plugins while still +# loading the bundled ones from the read-only /app tree. +# +# For portable instances (--instance /path), the overlay is created INSIDE the +# instance directory so each portable install is fully self-contained. +# For the global instance (no --instance), a shared overlay is used. + +BUNDLED="/app/lib/fluorine" + +# ── Detect --instance argument ── +# If launching a portable instance, use its directory as the overlay target. +INSTANCE_DIR="" +PREV="" +for arg in "$@"; do + if [ "$PREV" = "--instance" ] || [ "$PREV" = "-i" ]; then + INSTANCE_DIR="$arg" + break + fi + PREV="$arg" +done + +if [ -n "$INSTANCE_DIR" ] && [ -d "$INSTANCE_DIR" ]; then + # Portable instance: overlay goes into the instance directory itself. + # Everything is self-contained: mods, profiles, plugins, dlls, libs. + USER_DIR="$INSTANCE_DIR" +else + # Global instance: shared overlay at ~/.local/share/fluorine/ + USER_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/fluorine" +fi + +# ── Create writable overlay with symlinks to bundled files ── +# This lets MO2 load bundled plugins AND any custom ones the user drops in. +# Existing files are never overwritten (user overrides take priority). +setup_overlay() { + mkdir -p "${USER_DIR}/plugins" "${USER_DIR}/dlls" "${USER_DIR}/lib" + + # Symlink bundled plugins (skip existing - user overrides take priority) + for f in "${BUNDLED}/plugins/"*; do + [ -e "$f" ] || continue + local base="$(basename "$f")" + local target="${USER_DIR}/plugins/${base}" + [ -e "$target" ] || [ -L "$target" ] || ln -sf "$f" "$target" + done + + # Symlink bundled dlls + for f in "${BUNDLED}/dlls/"*; do + [ -e "$f" ] || continue + local base="$(basename "$f")" + local target="${USER_DIR}/dlls/${base}" + [ -e "$target" ] || [ -L "$target" ] || ln -sf "$f" "$target" + done + + # Symlink bundled libs + for f in "${BUNDLED}/lib/"*; do + [ -e "$f" ] || continue + local base="$(basename "$f")" + local target="${USER_DIR}/lib/${base}" + [ -e "$target" ] || [ -L "$target" ] || ln -sf "$f" "$target" + done + + # Symlink other bundled files (binaries, tools) directly + for f in ModOrganizer-core lootcli wrestool icotool fusermount3 cabextract; do + [ -e "${BUNDLED}/$f" ] || continue + [ -e "${USER_DIR}/$f" ] || [ -L "${USER_DIR}/$f" ] || ln -sf "${BUNDLED}/$f" "${USER_DIR}/$f" + done + + # umu-run must be a real copy (not symlink to /app/) because it runs on + # the host via flatpak-spawn --host, where /app/ doesn't exist. + # Remove any stale symlink first (cp -f follows symlinks, won't replace them). + if [ -e "${BUNDLED}/umu-run" ]; then + rm -f "${USER_DIR}/umu-run" + cp "${BUNDLED}/umu-run" "${USER_DIR}/umu-run" + chmod +x "${USER_DIR}/umu-run" + fi + + # VFS helper must be a real binary copy (not a symlink to /app/) because + # it runs on the host via flatpak-spawn --host, where /app/ doesn't exist. + # libfuse3 is statically linked, so no extra .so files needed. + VFS_HELPER_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/fluorine/bin" + if [ -e "${BUNDLED}/mo2-vfs-helper" ]; then + mkdir -p "${VFS_HELPER_DIR}" + cp -f "${BUNDLED}/mo2-vfs-helper" "${VFS_HELPER_DIR}/mo2-vfs-helper" + chmod +x "${VFS_HELPER_DIR}/mo2-vfs-helper" + fi +} + +setup_overlay + +export PATH="${USER_DIR}:${BUNDLED}:${PATH}" +export LD_LIBRARY_PATH="${USER_DIR}/lib:${BUNDLED}/lib:${BUNDLED}/python/lib:${LD_LIBRARY_PATH:-}" + +# MO2 resolves plugins/dlls relative to MO2_BASE_DIR (or basePath()). +# Point it at the overlay so custom plugins are found. +export MO2_BASE_DIR="${USER_DIR}" +export MO2_PLUGINS_DIR="${USER_DIR}/plugins" +export MO2_DLLS_DIR="${USER_DIR}/dlls" +export MO2_PYTHON_DIR="${BUNDLED}/python" + +# Do NOT set PYTHONHOME globally -- it leaks into child processes (umu-run, +# Proton, winetricks) and breaks their Python. The plugin_python runner reads +# MO2_PYTHON_DIR and sets PYTHONHOME internally before loading the interpreter. +unset PYTHONHOME PYTHONPATH PYTHONNOUSERSITE + +# Qt6 plugins from KDE runtime. +export QT_PLUGIN_PATH="/usr/lib/plugins" + +exec "${BUNDLED}/ModOrganizer-core" "$@" |
