| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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>
|
| |\
| |
| | |
OpenMW support
|
| | | |
|
| | | |
|
| | | |
|
| | |
| |
| |
| |
| |
| |
| |
| |
| | |
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>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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>
|
| | | |
|
| | | |
|
| | | |
|
| |/
|
|
|
|
| |
Fixes #98
Addresses #102
|
| |
|
|
| |
This reverts commit 41a336cc8e53ba062ab791ffd6dc9b90d2c72899.
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
"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>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The "Lock GUI when running executable" toggle is removed from the
Workarounds settings tab. Locking is required for the post-run refresh
to know when files changed under the prefix, and the off-state was a
well-documented foot-gun. The setter/getter on InterfaceSettings, the
QSettings key (Settings/lock_gui), the checkbox widget, the tabstop
entry, and the two no-lock branches in ProcessRunner::postRun all go.
When the user clicks Unlock in the lock dialog, also SIGTERM the
tracked game pid and hard-kill the prefix's wineserver. The Cancelled
branch (Cancel in PreventExit mode) already did this; the ForceUnlocked
branch silently returned and left wineserver running, so Proton's
session manager held the prefix open for tens of seconds and the next
launch inherited a dirty state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Stylesheet themes ported from Windows can reference assets with either
case convention - some QSS files use lowercase url() refs against
MixedCase.svg files on disk, others do the reverse. The original shim
only covered the first direction. Add the second so themes whose disk
assets are lowercase but whose QSS uses TitleCase also resolve on
case-sensitive filesystems.
Rename createLowercaseStylesheetShims -> createStylesheetCaseShims and
extract the symlink-creation into a tryShim lambda that's called twice
per file (lowercase + TitleCase target names).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Removes the FUSE-bypass fast-path: fluorine_vfs.dll + fluorine_vfs_hid.dll
(mingw-cross PE-side AppInit_DLLs injector with MinHook), the LD_PRELOAD
helper libfluorine_vfs_preload.so, the bridge-index export from FuseConnector,
the prewarm + Wine-loader-trace plumbing in ProtonLauncher, AppInit_DLLs
prefix-init staging, the per-executable UseVfsBridge flag plus its
checkbox/handlers in editexecutablesdialog, and the third_party/minhook
vendored tree.
Core FUSE VFS (mo2filesystem, vfstree, inodetable, overwritemanager,
scancache, trackedwrites) is untouched - this only removes the sidecar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|