aboutsummaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
* Preserve Steam shortcut ID for Game Mode launchesSulfurNitride2026-06-251-8/+23
|
* Log VFS create failuresSulfurNitride2026-06-251-0/+17
|
* Fix VFS not intercepting writes for games on SD cardSulfurNitride2026-06-251-0/+8
| | | | | | | | | | | | | | | | | | | | | pressure-vessel's default sharing of /run/media and home uses non-recursive bind mounts that don't propagate FUSE submounts into the container. The shouldExposeMountPointToPressureVessel function excluded all paths under /run (isSystemRootPath) and under /run/media (pressureVesselSharesByDefault), so the fuse.mo2linux mount point was never added to PRESSURE_VESSEL_FILESYSTEMS_RW. Inside the container, the game saw the real SD card Data directory instead of the FUSE overlay, so all VFS writes (shader cache, config files, overwrite) bypassed staging entirely — VFS_staging stayed empty and write-heavy mods like Community Shaders logged "Failed to save shader" on every compile. Internal-drive games were unaffected because pressure-vessel's home directory handling is recursive and propagates FUSE submounts correctly. Fix: detect fuse.mo2linux in the mountinfo scan and always add it to PRESSURE_VESSEL_FILESYSTEMS_RW regardless of its parent path classification. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix VFS empty dirs in overwrite and add LOOT Sort buttonSulfurNitride2026-06-247-28/+359
| | | | | | | | | | | | | | | | | | | | | | VFS: Remove physical directory creation from mo2_lookup auto-mkdir. Previously, any dotless path component that missed the VFS tree was immediately created on disk, leaving hundreds of empty directories in overwrite (Shaders/, STRINGS/, DLLPlugins/, etc.) after a session. Now a phantom virtual node is inserted instead — createFile() already calls create_directories on the parent path, so the real directory materialises on disk only when a file is actually written inside it. mo2_rmdir already handles phantom (disk-absent) directories correctly. LOOT: Add lootmanager.h/cpp which downloads the latest win64 .7z from the GitHub releases API, extracts it to ~/.local/share/fluorine/tools/loot/ using the bundled 7zz or any system 7z tool, and exposes isLootInstalled() / getLootExePath(). Add a Sort button to the plugins tab that downloads LOOT on first use (same progress-dialog pattern as SLR) then launches LOOT.exe hooked through the active Proton via ProcessRunner so it sees the full merged VFS. Button is hidden for games that don't use LOOT sorting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Merge pull request #108 from tristan-iu/openmw-supportSulfurNitride2026-06-243-27/+98
|\ | | | | OpenMW support
| * Don't kill launcher-spawned engine on force-unlock for non-VFS gamestristan-iu2026-06-231-6/+44
| |
| * Forward both stdio channels on native launch to avoid engine SIGPIPEtristan-iu2026-06-231-18/+38
| |
| * Skip FUSE mount for games that manage their own VFStristan-iu2026-06-231-3/+16
| |
* | Fix fuse_req_session CI build errorSulfurNitride2026-06-243-4/+9
| | | | | | | | | | | | | | | | | | fuse_req_session() is not available in all libfuse3 builds in the CI image. Instead, store the fuse_session* directly in Mo2FsContext (set once after fuse_session_new in fuseconnector.cpp) and use ctx->session in the fuse_lowlevel_notify_inval_inode calls that fix the INI key clobber. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* | Fix shader cache write failures and INI key clobberSulfurNitride2026-06-241-1/+65
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Shader cache (mo2_lookup): Starfield skips mkdir("ShaderCache/Lighting/") and goes straight to CreateFile("ShaderCache/Lighting/X.pso"). USVFS handles this by intercepting at Win32 level and auto-creating parent dirs in overwrite. FUSE operates below the kernel path-resolver, so if a parent component is missing the kernel returns ENOENT before mo2_create is ever called. Fix: in mo2_lookup, when a name has no '.' (directory-like) and is not found in the VFS, auto-create it in staging and return a positive inode so the kernel can continue resolving the path. createDirectory uses create_directories internally, so staging parent dirs are also created when the parent came from overwrite rather than this session. INI key clobber (mo2_open / mo2_write): keep_cache=1 with AUTO_INVAL_DATA disabled let the kernel serve stale pre-COW page cache on re-reads. Wine's WritePrivateProfileString does open→read→close→open(O_TRUNC)→write per key; after the first write triggered COW, subsequent reads got old cached content, so each new key was written on top of the original file — only the last key survived. Fix: call fuse_lowlevel_notify_inval_inode at all four COW and O_TRUNC paths to flush stale kernel pages precisely at the moment the backing path changes, without the blanket performance cost of keep_cache=0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* | Let Wayland restore main window geometrySulfurNitride2026-06-231-2/+10
| |
* | Fix FOMOD casing and VFS test coverageSulfurNitride2026-06-232-22/+24
| |
* | Fix SLR update settings accessSulfurNitride2026-06-211-7/+13
| |
* | Prompt for SLR updates safelySulfurNitride2026-06-214-41/+288
|/ | | | | | Fixes #98 Addresses #102
* Revert "Disable VFS cache for writable handles"SulfurNitride2026-06-191-6/+2
| | | | This reverts commit 41a336cc8e53ba062ab791ffd6dc9b90d2c72899.
* Disable VFS cache for writable handlesSulfurNitride2026-06-191-2/+6
|
* Fix VFS writes and stylesheet URLsSulfurNitride2026-06-192-24/+77
|
* ensure everything uses new font after applying settingSaghm Rossi2026-06-181-0/+16
|
* utilize font size settingsSaghm Rossi2026-06-183-1/+25
|
* add support for user-configured fontSaghm Rossi2026-06-187-34/+119
|
* Fix bundled font fallbackSulfurNitride2026-06-172-9/+68
|
* Prepare Nexus-safe release cleanupSulfurNitride2026-06-1729-667/+609
|
* Improve Proton launch contextSulfurNitride2026-06-105-17/+130
|
* Improve overwrite VFS case handlingSulfurNitride2026-06-104-31/+383
|
* Fix NuGet cert setup and fontconfig envSulfurNitride2026-06-086-6/+484
|
* clean pgpatcher logs before launchSulfurNitride2026-05-311-0/+2
|
* fix updater restart and log cleanupSulfurNitride2026-05-314-16/+130
|
* Detect Steam Protons across librariesSulfurNitride2026-05-303-15/+62
|
* release: prepare 0.2.3 stableSulfurNitride2026-05-307-82/+295
|
* Reduce FUSE mutation overheadSulfurNitride2026-05-285-47/+209
|
* Add FUSE io_uring toggle and update libfuseSulfurNitride2026-05-283-6/+195
|
* Add FUSE io_uring negotiation and bottleneck loggingSulfurNitride2026-05-273-40/+381
|
* Clarify lazy FUSE read fd handlingSulfurNitride2026-05-271-3/+3
|
* Improve FUSE VFS performance and rename handlingSulfurNitride2026-05-277-42/+467
|
* Require injected xrandr for prefix initSulfurNitride2026-05-261-9/+8
|
* Force SLR for Proton launchesSulfurNitride2026-05-266-84/+112
|
* vfs: bypass userspace lookup cacheSulfurNitride2026-05-241-65/+7
|
* vfs: invalidate ancestor caches on mutationSulfurNitride2026-05-241-46/+70
|
* Update MO2 beta 12 integrationSulfurNitride2026-05-2415-956/+1348
|
* Fix native Stardew launch pathsSulfurNitride2026-05-234-5/+26
|
* startup: clarify invalid instance recoverySulfurNitride2026-05-232-29/+3
|
* prefix: describe installer exit codesSulfurNitride2026-05-232-7/+30
|
* prefix: improve setup progress and failure logsSulfurNitride2026-05-234-42/+217
|
* prefix: align dependency setup and VFS fixesSulfurNitride2026-05-239-76/+778
|
* vfs: preserve canonical casing for write destinationsSulfurNitride2026-05-191-3/+6
|
* mo2: catch up to upstream 2.5.3 Betas 3-11SulfurNitride2026-05-1622-1673/+1136
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Multi-area sync against the upstream MO2 2.5.3 beta line. Roughly three logical sections share the diff: Catch-up (Betas 3-11): - nxmaccessmanager: drop "supporter" from validRoles so non-premium supporters get the right download path (Beta 10) - mainwindow: disable tutorial triggers at the trigger sites; Qt 6.11 lockup on child windows (Beta 4) - basic_games: add Kingdom Come Deliverance 2, Slay the Spire 2 (Betas 11, 6) - uibase: add NXM collections parsing fields/accessors + (?:nxm|modl) scheme support in NXMUrl (Beta 9) - Starfield: blueprintPrefix() on IPluginGame; plugins.txt write suppression for blueprint plugins; hasInvalidBlueprint / hasUnpairedBlueprint diagnoses; Title-keyed content catalog consolidation (Betas 3-5) - downloadmanager + systemtraymanager + organizercore + iuserinterface + settings: add Beta 11 "show notifications when downloads complete or fail" toggle and the showNotification plumbing - nxmaccessmanager: restore upstream re-entry guard for OAuth refresh and the styled OAuth response page (Beta 11) - organizer_en.ts and game-bethesda *_en.ts: pull upstream HEAD strings Workarounds tab removed: - Delete settingsdialogworkarounds.{h,cpp}; drop the tab in settingsdialog.ui; move "Enable archives parsing" into General - Hardcode GameSettings::forceEnableCoreFiles() to true so the primary-master toggle-off bug (DLCs unchecked on tab refresh) can't recur; setter becomes a no-op - No-op stubs for offlineMode, useProxy, useCustomBrowser, Steam appID/credentials, executablesBlacklist, skipFileSuffixes, skipDirectories — UI gone, accessors keep call sites green - settingsdialognexus drops the custom-browser wiring; spawn drops the "Change the blacklist" Retry path Beta 9 download manager port: - fileID-first match in nxmFilesAvailable and nxmFileInfoFromMd5Available; filename remains the fallback - NexusInterface::isActiveFileStatus() helper; rewrite of nxmUpdatesAvailable to use pickNewestVersion / findUpdateChainSuccessors / resolveInstalledFileId. ARCHIVED_HIDDEN now correctly treated as inactive - std::optional<unsigned int> reservedID on DownloadInfo factories and on the three addDownload overloads; startDownloadURLs and startDownloadURLWithMeta return the canonical DownloadID instead of m_ActiveDownloads.size()-1 (which was the index, not an ID) - QHash<unsigned int, DownloadInfo*> m_ByID maintained alongside m_ActiveDownloads at every mutation site; downloadInfoByID is now O(1) via m_ByID.value(id, nullptr) LOOT remains intentionally stripped (lootcli build cruft cleaned up separately). BSA wide-path skipped — Linux UTF-8 paths already work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ui: inline trash icon on backup-restore dialogSulfurNitride2026-05-163-5/+154
| | | | | | | | | | | | | | | | | | | | | | User feedback: the only way to clean up old backups was to hand-edit the profile directory, since the "Choose backup to restore" dialog exposed select-or-cancel and no delete affordance. Add an addChoice overload on SelectionDialog that takes a per-row delete callback. Each row is rendered as a horizontal layout — QCommandLinkButton (stretched) plus a small auto-raised QToolButton with a trash icon (user-trash → edit-delete → SP_TrashIcon fallback chain). Clicking the trash prompts for confirmation, invokes the callback, and hides the row on success; the QCommandLink remains live for restore. numChoices() now counts visible rows under scrollAreaWidgetContents so the "no backups" check stays correct as rows disappear. MainWindow::queryRestore wires the callback to delete the matching Plugins.txt.<ts>, loadorder.txt.<ts>, and lockedorder.txt.<ts> triplet via QFile::remove. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* plugins: surface plugin name and Python exception on init failureSulfurNitride2026-05-161-1/+8
| | | | | | | | | | | | | | | | | | | | | | | "plugin failed to initialize" with no name and no exception text is the canonical first-line indicator for a Python plugin that didn't register — but it tells the user nothing about *which* plugin or *why*. Encountered while diagnosing a flaky first-launch failure where a Python plugin's init() silently returned False on cold boot and succeeded on the next run, plus a Ready or Not instance that wouldn't open. Wrap the IPlugin::init trampoline in pyplugins.h manually instead of relying on PYBIND11_OVERRIDE_PURE so we can: - log the plugin's name and the surrounding hint when init() returns False (typically a plugin swallowing an exception in its own try/except); - catch pybind11::error_already_set and log the Python exception text + traceback before returning False to the C++ caller. plugincontainer.cpp's "plugin failed to initialize" line now also prints the plugin name so the proxied-candidate context is clear. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ui: recover from stale FUSE mounts without bothering the userSulfurNitride2026-05-162-13/+57
| | | | | | | | | | | | | | | | | | | | | | | | | Two complementary changes against ENOTCONN — "Transport endpoint is not connected" — from a wedged FUSE mount left by a previous crashed or unclean session. MOApplication::notify already caught filesystem_error and called FuseConnector::tryCleanupStaleMount on the affected path, but it still went on to show the user a hard error dialog about the exception. If we've already cleared the wedged mount the workflow that triggered the iterator (refresh, restore backup, etc.) just needs to be retried — the dialog is noise on a state we just recovered from. Probe stat() after cleanup; if the path is no longer wedged, log + swallow. OrganizerCore::afterRun's post-game chmod loop used the throwing recursive_directory_iterator constructor with no error_code, so a single ENOTCONN under the game dir unwound out of afterRun and left the rest of the post-run sync (save/INI/plugin sync-back, load-order refresh, FinishedRun callback) un-run. Switch to error_code-based iteration; on ENOTCONN, call tryCleanupStaleMount on the game dir and continue cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* vfs: invalidate full subtree from node_cache on mutation (fixes #210)SulfurNitride2026-05-162-43/+72
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | invalidateNodeCache's docstring promised "Clear all node_cache entries whose path starts with the given prefix" but the implementation only cleared the exact path's entry and its parent. removeFromTree on a directory destroys every descendant VfsNode via cascading unique_ptr destruction; every descendant inode still cached in node_cache became a dangling pointer. Next time Wine called getattr on a cached descendant ino, resolveByInode returned the dangling pointer and snapshotFromNode read freed memory — depending on what reused the allocation, this manifested as either std::bad_alloc (corrupted string length in real_path triggers a huge allocation request) or outright SIGSEGV. ENOMEM bubbled up to Wine's NT loader as ERROR_MOD_NOT_FOUND (0x7E), which is exactly the F4SE "couldn't load plugin (0000007E)" pattern reported in #210 after mod-state transitions. Rewrite invalidateNodeCache to actually walk node_cache and erase every entry whose path is the target or a descendant under target/. Lock order is tree_mutex (exclusive, caller) → inode_mutex (shared) → node_cache_mutex (exclusive), consistent with existing sites. Top-level mutations also invalidate root (inode 1) since its children map changed. Also enrich the FUSE wrap_* exception macro to log the inode being processed alongside the exception text. Future "[VFS] getattr(ino=N): caught exception: ..." entries point straight at the path that triggered cache/tree corruption. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>