Skip to content

FFX Compatibility Tool v0.1.0

Choose a tag to compare

@github-actions github-actions released this 09 Sep 15:14
a2867c8

FFX Compatibility Tool v0.1.0 — the portable Windows build.

What's changed

  • ci: the first release can publish its own notes — the changelog anchors on HEAD's parent and only v-tags act as boundaries, so the not-yet-created tag and the moving nightly tag can never break it again, and an unreadable history degrades to a fallback bullet instead of killing the release
  • docs: RESEARCH_NOTES keeps only the .ffx format research — every round-by-round build log section is gone, the surviving findings drop their round labels and read as timeless format notes, and the intro points at FfxTool.Core instead of the long-gone Python files
  • build: a nightly says what it is — the app version reads the latest release followed by -nightly, or just nightly when nothing is released yet, stamped into Windows' product version and shown in the About page, status bar, log banner and crash reports, while the numeric version and update checks stay untouched
  • build: a nightly says what it is — the app version reads the latest release followed by -nightly, or just nightly when nothing is released yet, stamped into Windows' product version and shown in the About page, status bar, log banner and crash reports, while the numeric version and update checks stay untouched
  • build: the exe ships as FFXCompatibilityTool.exe with marbou92 in its Windows details, About and crash reports — and the release pages carry just the exe, no zip beside it
  • build: the exe ships as FFXCompatibilityTool.exe with marbou92 in its Windows details, About and crash reports — and the release pages carry just the exe, no zip beside it
  • build: the portable build is one single exe — a Release ILRepack step merges FfxTool.Core and the System.Text.Json chain into FfxTool.Gui.exe, the seed tables ship embedded inside Core, and the Release + Nightly pages attach the bare exe beside the zip
  • build: the portable build is one single exe — a Release ILRepack step merges FfxTool.Core and the System.Text.Json chain into FfxTool.Gui.exe, the seed tables ship embedded inside Core, and the Release + Nightly pages attach the bare exe beside the zip
  • feat(gui): an icon-only bigger-list button and a crash dialog that files the issue for you — Report on GitHub opens a prefilled new issue with the trace on the clipboard; README cut to what a user needs
  • fix(gui): the big list stretches the workspace instead of reparenting the queue card — the CTA row was never the card's sibling and the first click threw; CI + Nightly + Release badges and a clean MIT license
  • feat(gui): a bigger list for Convert's file manager, folder-targeted handoffs in the Lister's, and two file managers that share one look but deliberately not one behaviour
  • fix(gui): the conversion report logs through the page's console — WriteConvertReport was static, but Console is the XAML-named M3Console control
  • fix(gui): the mirrored outputs mirror only real folders, releases can be versioned from Run workflow, and derived outputs report themselves
  • fix(gui): the mirrored outputs mirror only real folders, releases can be versioned from Run workflow, and derived outputs report themselves
  • feat(gui): the file managers become real explorer trees, presets get plugin-profile flags, and the wrong-.aex engine is fixed
  • feat(gui): the file managers grow subfolders, a double-click, and a handoff that carries the whole tree - both file managers (Convert's queue list and the Lister's folder list) now group their rows under the subfolders the presets came from, one contiguous group per folder via a ListCollectionView property group on a new shared FolderScan.RelUnder primitive (queues are re-sorted by relative folder then name before the queue is stored, so the worker's per-file status always lands on the matching row), each group headed by a folder glyph + path caption that only appears when subfolders actually exist; in Convert a double-click on a row opens that preset in the existing checklist - its effects with their plugins as match names + vendor labels, every one toggleable - while the queue waits behind a new "All presets" toolbar link, and the toggles are remembered per file (a dictionary keyed by full path, re-applied over the profile-driven defaults on reopen) and ride into the batch run on top of the recognition chain, so turning a plugin's effects off in the checklist changes what the queue converts; converting a folder gains two mirror outputs beside the three existing ones - "One ZIP that mirrors the subfolders" stages the converted set into a temp tree in the source layout and zips it with System.IO.Compression.ZipFile (references added to the GUI csproj) into "FolderName (converted).zip", "One folder that mirrors the subfolders" writes the same tree as a plain folder, both reuse-overwrite their own previous result and never touch the inputs, a cancelled or failed run never hands over a broken archive, and the save banner's Explorer handoff selects the zip or opens the mirrored folder; the Effect Lister's file manager becomes multi-select (Extended) where plain clicks still open a preset like round 35 but ctrl/shift clicks only grow the selection, the handoff button relabels itself to what a click would send ("Convert this preset…" with a preset open, "Convert selection…" with picks, "Convert folder…" with none), and its ConvertRequested event now carries (root, files) - the folder root riding along so Convert can mirror the subfolders either way - with MainWindow sending a single file through the old LoadExternal checklist flow and a selection or whole folder through a new LoadExternalQueue that keeps the tree; the header's status captions move to their own row under the toolbar, because the right-aligned "FFX Compatibility Tool v0.1.0 — Ready" strip shared the grid cell with the buttons and painted over them whenever the window narrowed, and its own row makes that overlap structurally impossible; and the folder report learns three things: every row carries its relative subfolder (shown in the row tooltip, exported as a new Folder column in the same quoted UTF-8-BOM CSV), each row's tooltip shows the full path, and double-clicking a row opens that preset in the workspace with the flyout closing - the report becomes a way in, not just a readout
  • fix(gui): the handoff event fires the way it was declared - ConvertRequested went out as Action (one argument, the open preset's path - the exact shape MainWindow's subscription lambda and ConvertPage.LoadExternal already speak) but SendConvertBtn_Click raised it with the classic two-argument (sender, args) event pattern, so the Effect Lister's 'Convert this preset...' button died at compile time with CS1501 'No overload for method Invoke takes 2 arguments' at ListerPage.cs(758,30) before the feature ever ran a single click; the invocation now passes just the path, matching declaration, subscriber and target, and the whole round-35 diff was re-audited for CI's first-error blindness - csc reported exactly one error project-wide and the manual sweep confirms it: every new x:Name resolves in both directions, all three new handler signatures match their XAML events (MouseButtonUp/SelectionChanged/Click), the PropertyChanged-style 2-arg Invoke inside ConvertPage.QueueFileVm is correct for PropertyChangedEventHandler, B.SCLow/B.OnSurfaceVariant/B.Error exist in all eight theme dictionaries, and the worker's new _queueRows[i] indexing cannot race a queue reload because LoadQueue and LoadFile both refuse entry while _running
  • fix(gui): the handoff event fires the way it was declared - ConvertRequested went out as Action (one argument, the open preset's path - the exact shape MainWindow's subscription lambda and ConvertPage.LoadExternal already speak) but SendConvertBtn_Click raised it with the classic two-argument (sender, args) event pattern, so the Effect Lister's 'Convert this preset...' button died at compile time with CS1501 'No overload for method Invoke takes 2 arguments' at ListerPage.cs(758,30) before the feature ever ran a single click; the invocation now passes just the path, matching declaration, subscriber and target, and the whole round-35 diff was re-audited for CI's first-error blindness - csc reported exactly one error project-wide and the manual sweep confirms it: every new x:Name resolves in both directions, all three new handler signatures match their XAML events (MouseButtonUp/SelectionChanged/Click), the PropertyChanged-style 2-arg Invoke inside ConvertPage.QueueFileVm is correct for PropertyChangedEventHandler, B.SCLow/B.OnSurfaceVariant/B.Error exist in all eight theme dictionaries, and the worker's new _queueRows[i] indexing cannot race a queue reload because LoadQueue and LoadFile both refuse entry while _running
  • feat(gui): a folder is a first-class citizen in both sections now - Convert's queue card grows the file manager: the checklist slot becomes a scrolling list of every preset with its size and a live per-file status cell that flips to Converting/OK/WARN/FAILED as the batch runs (an INPC row VM, the worker loop indexed so each row is reached by dispatch, WARN/FAILED colored by the same triggers as the Lister's report), while the Effect Lister drops the combo and gains a real file-manager state: a folder (or a multi-file drop) opens a card listing the folder's presets - name, size, path tooltip - and clicking one opens it with the full single-preset anatomy, All presets in the toolbar returns to the list, and the empty state can no longer linger underneath it; the Lister also learns to hand off: a Convert this preset button rides the toolbar (visible once any preset is open), fires a ConvertRequested event MainWindow turns into Rail.Select(0) + ConvertPage.LoadExternal so the preset you were reading is already loaded in Convert when you get there; the window stops floating flat - WindowChrome's GlassFrameThickness goes 0 to 1, extending the DWM frame one pixel into the client area so the DWM draws its shadow again (the Aero glass shadow on Windows 7, the standard shadow on 8/10/11; the 1px glass edge is painted over by the background so the silhouette is unchanged, and DWM-off Win7 Basic gets no shadow exactly like stock); Check for Updates stops depending on VERSION.txt entirely - it resolves the project's releases/latest redirect and reads the tag from the Location header (a redirect to the releases list and an HTTP 404 both honestly mean no full release exists yet; prereleases like the rolling nightly never answer), so publishing a release IS the announcement and the repo loses the one-line file that had to move in lockstep with every version; and the README finally describes the product as it exists - download/install per OS, a per-section tour including the new file managers and the handoff, privacy, building from source, the CI/release/nightly pipeline, and the preserved format rules - instead of round-1's "I could not compile or run this C# code myself"
  • feat(gui): a folder is a first-class citizen in both sections now - Convert's queue card grows the file manager: the checklist slot becomes a scrolling list of every preset with its size and a live per-file status cell that flips to Converting/OK/WARN/FAILED as the batch runs (an INPC row VM, the worker loop indexed so each row is reached by dispatch, WARN/FAILED colored by the same triggers as the Lister's report), while the Effect Lister drops the combo and gains a real file-manager state: a folder (or a multi-file drop) opens a card listing the folder's presets - name, size, path tooltip - and clicking one opens it with the full single-preset anatomy, All presets in the toolbar returns to the list, and the empty state can no longer linger underneath it; the Lister also learns to hand off: a Convert this preset button rides the toolbar (visible once any preset is open), fires a ConvertRequested event MainWindow turns into Rail.Select(0) + ConvertPage.LoadExternal so the preset you were reading is already loaded in Convert when you get there; the window stops floating flat - WindowChrome's GlassFrameThickness goes 0 to 1, extending the DWM frame one pixel into the client area so the DWM draws its shadow again (the Aero glass shadow on Windows 7, the standard shadow on 8/10/11; the 1px glass edge is painted over by the background so the silhouette is unchanged, and DWM-off Win7 Basic gets no shadow exactly like stock); Check for Updates stops depending on VERSION.txt entirely - it resolves the project's releases/latest redirect and reads the tag from the Location header (a redirect to the releases list and an HTTP 404 both honestly mean no full release exists yet; prereleases like the rolling nightly never answer), so publishing a release IS the announcement and the repo loses the one-line file that had to move in lockstep with every version; and the README finally describes the product as it exists - download/install per OS, a per-section tour including the new file managers and the handoff, privacy, building from source, the CI/release/nightly pipeline, and the preserved format rules - instead of round-1's "I could not compile or run this C# code myself"
  • fix(gui): Settings finally wears the ISection hat the nav switch makes it wear - the round-33 rebuild moved Settings from 'PageHost.Content = _settings' (an object assignment that never cared) into ActiveSection()'s 'case 2: return _settings;' (an ISection return that very much does), and SettingsPage - which never implemented the interface - turned that into CS0266 at MainWindow.cs(135,32), the first red CI of the round ('Cannot implicitly convert type FfxTool.Gui.SettingsPage to FfxTool.Gui.ISection'); the fix gives SettingsPage the two no-op members OpenFile/OnShown, deliberately empty because Settings never had either behavior (Ctrl+O on Settings was a null ActiveSection no-op before round 33, and OnShown was never called on it), and every other audit thread that CI's first-error-only report could not show gets closed too: the Lister's Folder report button could die forever when a queue reload landed mid-scan (the final dispatcher callback returned early on the generation check without releasing _scanRunning, so the guard 'if (_scanRunning) return' locked the report until an app restart - the reset now happens unconditionally before the generation check), and the release workflow gains permissions: contents: write, because a read-only default token would 403 the very first v* tag's publish exactly when the description bot is supposed to prove itself
  • fix(gui): Settings finally wears the ISection hat the nav switch makes it wear - the round-33 rebuild moved Settings from 'PageHost.Content = _settings' (an object assignment that never cared) into ActiveSection()'s 'case 2: return _settings;' (an ISection return that very much does), and SettingsPage - which never implemented the interface - turned that into CS0266 at MainWindow.cs(135,32), the first red CI of the round ('Cannot implicitly convert type FfxTool.Gui.SettingsPage to FfxTool.Gui.ISection'); the fix gives SettingsPage the two no-op members OpenFile/OnShown, deliberately empty because Settings never had either behavior (Ctrl+O on Settings was a null ActiveSection no-op before round 33, and OnShown was never called on it), and every other audit thread that CI's first-error-only report could not show gets closed too: the Lister's Folder report button could die forever when a queue reload landed mid-scan (the final dispatcher callback returned early on the generation check without releasing _scanRunning, so the guard 'if (_scanRunning) return' locked the report until an app restart - the reset now happens unconditionally before the generation check), and the release workflow gains permissions: contents: write, because a read-only default token would 403 the very first v* tag's publish exactly when the description bot is supposed to prove itself
  • feat(gui): the Batch section dissolves into the two pages that actually use it - the nav is back to three items (Convert, Effect Lister, Settings on Ctrl+3) and both working pages now accept a whole folder of presets natively: Convert gains an Open folder button beside the status chip plus folder drops and a multi-select file dialog, and a folder (or several dropped files) swaps the effect checklist for a queue card carrying the three batch settings (include subfolders, remove effects missing from my profile, and the three output modes - converted subfolder, version suffix beside the originals, or an in-place overwrite that demands a Yes/No first), the CTA converts every .ffx in one pass streaming one console line per file through the cancel that lands between files, and the save banner becomes a batch-completion handoff to the output folder while the single-preset checklist flow stays byte-for-byte untouched (loading one file returns from queue mode); the Effect Lister takes the same folder inputs and turns them into a queue - a combo in the header lists the folder presets, picking one deep-reads it with the exact single-preset anatomy, the file chip reads 3 / 27 - name.ffx, and the batch-inspect table survives as the Folder report flyout deep-reading every preset into one row (status, effect/parameter/animated counts, size, decode notes) exportable as the same quoted UTF-8-BOM CSV, with a scan generation bump so loading a new queue can never mix an in-flight report across folders; the folder walk and size formatter move to one shared FolderScan helper so the two pages cannot drift, the per-directory try/catch stands (one locked subtree contributes nothing instead of killing the scan on .NET Framework), and every new state-event handler guards on IsInitialized per the round-32 lesson (the queue pre-checked subfolder box fires during the BAML parse exactly like its BatchPage ancestor did) and ci: the tag release gains a description bot - on a v* push the workflow reads the commit subjects between the previous tag and this one (fetch-depth 0 so the history exists; the first tag ever reads the whole history, honest for a first release), splits the repo multi-entry commit messages at their " and (" joins so every change becomes its own bullet, and writes a release body with the changes, install steps (unzip anywhere, run the exe, .NET Framework 4.8 preinstalled on Windows 10/11 and a one-time install on Windows 7 SP1), the SHA-256 of the attached zip and the Check-for-Updates note, with GitHub generated compare link riding on top via generate_release_notes, a manual dispatch skipping both and producing just the artifact, and the release now carries a proper title and fail_on_unmatched_files so a missing zip cannot publish an empty release
  • feat(gui): the Batch section dissolves into the two pages that actually use it - the nav is back to three items (Convert, Effect Lister, Settings on Ctrl+3) and both working pages now accept a whole folder of presets natively: Convert gains an Open folder button beside the status chip plus folder drops and a multi-select file dialog, and a folder (or several dropped files) swaps the effect checklist for a queue card carrying the three batch settings (include subfolders, remove effects missing from my profile, and the three output modes - converted subfolder, version suffix beside the originals, or an in-place overwrite that demands a Yes/No first), the CTA converts every .ffx in one pass streaming one console line per file through the cancel that lands between files, and the save banner becomes a batch-completion handoff to the output folder while the single-preset checklist flow stays byte-for-byte untouched (loading one file returns from queue mode); the Effect Lister takes the same folder inputs and turns them into a queue - a combo in the header lists the folder presets, picking one deep-reads it with the exact single-preset anatomy, the file chip reads 3 / 27 - name.ffx, and the batch-inspect table survives as the Folder report flyout deep-reading every preset into one row (status, effect/parameter/animated counts, size, decode notes) exportable as the same quoted UTF-8-BOM CSV, with a scan generation bump so loading a new queue can never mix an in-flight report across folders; the folder walk and size formatter move to one shared FolderScan helper so the two pages cannot drift, the per-directory try/catch stands (one locked subtree contributes nothing instead of killing the scan on .NET Framework), and every new state-event handler guards on IsInitialized per the round-32 lesson (the queue pre-checked subfolder box fires during the BAML parse exactly like its BatchPage ancestor did) and ci: the tag release gains a description bot - on a v* push the workflow reads the commit subjects between the previous tag and this one (fetch-depth 0 so the history exists; the first tag ever reads the whole history, honest for a first release), splits the repo multi-entry commit messages at their " and (" joins so every change becomes its own bullet, and writes a release body with the changes, install steps (unzip anywhere, run the exe, .NET Framework 4.8 preinstalled on Windows 10/11 and a one-time install on Windows 7 SP1), the SHA-256 of the attached zip and the Check-for-Updates note, with GitHub generated compare link riding on top via generate_release_notes, a manual dispatch skipping both and producing just the artifact, and the release now carries a proper title and fail_on_unmatched_files so a missing zip cannot publish an empty release
  • feat(gui): the Batch section dissolves into the two pages that actually use it - the nav is back to three items (Convert, Effect Lister, Settings on Ctrl+3) and both working pages now accept a whole folder of presets natively: Convert gains an Open folder button beside the status chip plus folder drops and a multi-select file dialog, and a folder (or several dropped files) swaps the effect checklist for a queue card carrying the three batch settings (include subfolders, remove effects missing from my profile, and the three output modes - converted subfolder, version suffix beside the originals, or an in-place overwrite that demands a Yes/No first), the CTA converts every .ffx in one pass streaming one console line per file through the cancel that lands between files, and the save banner becomes a batch-completion handoff to the output folder while the single-preset checklist flow stays byte-for-byte untouched (loading one file returns from queue mode); the Effect Lister takes the same folder inputs and turns them into a queue - a combo in the header lists the folder presets, picking one deep-reads it with the exact single-preset anatomy, the file chip reads 3 / 27 - name.ffx, and the batch-inspect table survives as the Folder report flyout deep-reading every preset into one row (status, effect/parameter/animated counts, size, decode notes) exportable as the same quoted UTF-8-BOM CSV, with a scan generation bump so loading a new queue can never mix an in-flight report across folders; the folder walk and size formatter move to one shared FolderScan helper so the two pages cannot drift, the per-directory try/catch stands (one locked subtree contributes nothing instead of killing the scan on .NET Framework), and every new state-event handler guards on IsInitialized per the round-32 lesson (the queue pre-checked subfolder box fires during the BAML parse exactly like its BatchPage ancestor did) and ci: the tag release gains a description bot - on a v* push the workflow reads the commit subjects between the previous tag and this one (fetch-depth 0 so the history exists; the first tag ever reads the whole history, honest for a first release), splits the repo multi-entry commit messages at their " and (" joins so every change becomes its own bullet, and writes a release body with the changes, install steps (unzip anywhere, run the exe, .NET Framework 4.8 preinstalled on Windows 10/11 and a one-time install on Windows 7 SP1), the SHA-256 of the attached zip and the Check-for-Updates note, with GitHub generated compare link riding on top via generate_release_notes, a manual dispatch skipping both and producing just the artifact, and the release now carries a proper title and fail_on_unmatched_files so a missing zip cannot publish an empty release
  • feat(gui): the Batch section dissolves into the two pages that actually use it - the nav is back to three items (Convert, Effect Lister, Settings on Ctrl+3) and both working pages now accept a whole folder of presets natively: Convert gains an Open folder button beside the status chip plus folder drops and a multi-select file dialog, and a folder (or several dropped files) swaps the effect checklist for a queue card carrying the three batch settings (include subfolders, remove effects missing from my profile, and the three output modes - converted subfolder, version suffix beside the originals, or an in-place overwrite that demands a Yes/No first), the CTA converts every .ffx in one pass streaming one console line per file through the cancel that lands between files, and the save banner becomes a batch-completion handoff to the output folder while the single-preset checklist flow stays byte-for-byte untouched (loading one file returns from queue mode); the Effect Lister takes the same folder inputs and turns them into a queue - a combo in the header lists the folder presets, picking one deep-reads it with the exact single-preset anatomy, the file chip reads 3 / 27 - name.ffx, and the batch-inspect table survives as the Folder report flyout deep-reading every preset into one row (status, effect/parameter/animated counts, size, decode notes) exportable as the same quoted UTF-8-BOM CSV, with a scan generation bump so loading a new queue can never mix an in-flight report across folders; the folder walk and size formatter move to one shared FolderScan helper so the two pages cannot drift, the per-directory try/catch stands (one locked subtree contributes nothing instead of killing the scan on .NET Framework), and every new state-event handler guards on IsInitialized per the round-32 lesson (the queue pre-checked subfolder box fires during the BAML parse exactly like its BatchPage ancestor did) and ci: the tag release gains a description bot - on a v* push the workflow reads the commit subjects between the previous tag and this one (fetch-depth 0 so the history exists; the first tag ever reads the whole history, honest for a first release), splits the repo multi-entry commit messages at their " and (" joins so every change becomes its own bullet, and writes a release body with the changes, install steps (unzip anywhere, run the exe, .NET Framework 4.8 preinstalled on Windows 10/11 and a one-time install on Windows 7 SP1), the SHA-256 of the attached zip and the Check-for-Updates note, with GitHub generated compare link riding on top via generate_release_notes, a manual dispatch skipping both and producing just the artifact, and the release now carries a proper title and fail_on_unmatched_files so a missing zip cannot publish an empty release
  • fix(gui): the pre-checked checkbox can no longer kill the app at startup - RecursiveCheck is IsChecked="True" in the batch page XAML and WPF raises ToggleButton.OnIsCheckedChanged DURING the BAML parse, so SourceOption_Changed fired while the page was still half-built; its guard checked SourceCountText, declared BEFORE the checkbox and already alive, so it passed and UpdateCta went on to touch RunBtn, declared AFTER it and still null - a deterministic NullReferenceException out of BatchPage..ctor inside MainWindow..ctor inside App.OnStartup on every machine, on every 1.0.31 start (exactly the trace the user's crash.log captured); the fix guards on FrameworkElement.IsInitialized - false for the whole parse, true only after EndInit, the one check that cannot be defeated by XAML declaration order - applied to all three handlers that demonstrably fire during load (BatchPage ModeNav + SourceOption, SettingsPage SubNav), while the initial state is still applied right after InitializeComponent, so nothing observable changes except the app starts; the audit closes the whole class (the Lister's IsChecked="True" Click="..." toggles cannot fire during parse, no XAML pre-selects anything else, NavRail wires in code)
  • chore(release): the version scheme leaves the internal round counter behind - the app ships as 0.1.0 and VERSION.txt, the live Check-for-Updates endpoint, moves to 0.1.0 in the same commit so a fresh install reports up-to-date, the numeric per-part compare keeping 0.1.9 < 0.1.10 ready for the first patch and ci: a nightly channel - every push to main plus a 02:30 UTC daily schedule plus manual dispatch builds the solution on windows-latest with the release workflow's toolchain, runs the tests BEFORE packaging (a red run leaves the previous nightly published), zips the whole Release output folder as FFXCompatibilityTool-nightly--.zip with 14-day retention, and republishes one rolling pre-release tagged nightly at the commit that was just built (old release and tag deleted, tag re-pointed, prerelease created with the zip; v* tagged releases keep their separate Latest slot) with a concurrency group so close pushes cannot race the swap
  • fix(gui): the pre-checked checkbox can no longer kill the app at startup - RecursiveCheck is IsChecked="True" in the batch page XAML and WPF raises ToggleButton.OnIsCheckedChanged DURING the BAML parse, so SourceOption_Changed fired while the page was still half-built; its guard checked SourceCountText, declared BEFORE the checkbox and already alive, so it passed and UpdateCta went on to touch RunBtn, declared AFTER it and still null - a deterministic NullReferenceException out of BatchPage..ctor inside MainWindow..ctor inside App.OnStartup on every machine, on every 1.0.31 start (exactly the trace the user's crash.log captured); the fix guards on FrameworkElement.IsInitialized - false for the whole parse, true only after EndInit, the one check that cannot be defeated by XAML declaration order - applied to all three handlers that demonstrably fire during load (BatchPage ModeNav + SourceOption, SettingsPage SubNav), while the initial state is still applied right after InitializeComponent, so nothing observable changes except the app starts; the audit closes the whole class (the Lister's IsChecked="True" Click="..." toggles cannot fire during parse, no XAML pre-selects anything else, NavRail wires in code)
  • chore(release): the version scheme leaves the internal round counter behind - the app ships as 0.1.0 and VERSION.txt, the live Check-for-Updates endpoint, moves to 0.1.0 in the same commit so a fresh install reports up-to-date, the numeric per-part compare keeping 0.1.9 < 0.1.10 ready for the first patch and ci: a nightly channel - every push to main plus a 02:30 UTC daily schedule plus manual dispatch builds the solution on windows-latest with the release workflow's toolchain, runs the tests BEFORE packaging (a red run leaves the previous nightly published), zips the whole Release output folder as FFXCompatibilityTool-nightly--.zip with 14-day retention, and republishes one rolling pre-release tagged nightly at the commit that was just built (old release and tag deleted, tag re-pointed, prerelease created with the zip; v* tagged releases keep their separate Latest slot) with a concurrency group so close pushes cannot race the swap
  • feat(gui): a Batch section between Effect Lister and Settings - drop a folder (or files) and run one of two jobs across every .ffx inside: Inspect mirrors the effect lister's deep read into one summary row per file (status, effect/parameter/animated counts, size, decode notes, zero-effect files flagged WARN) while Convert runs the proven pipeline (CS5.5 downgrade plus optional removal of effects your profile says you don't own) across the folder with three explicit output modes - a 'converted' subfolder inside the source, a version suffix beside the originals, or an in-place overwrite that demands a Yes/No first - both jobs stream determinate progress with a cancel that lands between files, the folder walk catches per-directory so one locked subtree cannot kill the scan, results export as a quoted UTF-8-BOM CSV, and originals are only ever written when the user explicitly chose it (a derived output can overwrite its own re-run result, never the input file)
  • feat(gui): Check for Updates goes live on the About page - one GET of the repository's one-line VERSION.txt (TLS 1.2 pinned for Windows 7, 6s timeout, bypass-cache, no telemetry) compares against the running build numerically (1.0.9 < 1.0.31) and says 'v1.0.31 is out' with a link to the releases page, while the repo now carries that VERSION.txt so every future round publishes its own availability in the same commit as its code; build stamped 1.0.31
  • feat(core): the no-sspc preset class - property/animator presets inspect as one entry per target path (a contributed 27-preset pack, kept out of the repository, held six files that decoded to ZERO effects: two single-property value snaps and four text-animator presets whose besc carries target-path tdsp entries + tdsn display names + tdgp/tdbs data blocks but NO sspc snapshot - each real tdsp is one property group ('Amplitude', 'Path Options', 'More Options', 'Animator 1'), so they inspect as N entries in path order and the effect list keeps pairing by position; their tdix values are 0xFFFFFFFF path sentinels, not sspc indexes, so effect removal is an honest no-op, renumbering skips, and the verify contiguity check branches on sspc-count-0 instead of corrupting the path identity or aborting the conversion) and duplicates share the first copy's parT (AE writes the parT descriptor tree only on an effect's FIRST sspc - every later copy of S_Sharpen x2 / MB LookSuite3 x4 / BCC Unsharp Mask x2 / Wave Warp x2 / Drop Shadow x2 degraded to Unknown kinds, an 89-flat-row BCC dump where the first copy shows 61 grouped rows, AE-hidden rows leaking past the 0x8/0x200 rules, popup menus stripped - an empty map now falls back to the first copy's by match name) and parT kind 10 joins the slider family as BoundedSlider (Exposure/Offset/Gamma, the Reverb and High-Low Pass rows, Deep Glow's sliders, CSpice, MB Strength - all flags 0x0, all with tdum/tduM bounds)
  • feat(gui): the dark editor stops glaring - AE's sampled #656565 field (L~41) sat as a mid-gray island on the app's near-black dark chrome (surface L~6, cards L~12) and read as broken dark mode; the dark skin re-derives its tones from a neutral #3B3F41 field (L~25) keeping every AE anatomical element - grid rhythm, emphasized zero line, #FFEE00 picked key, #FC0000 current-time line, in-field readout - so dark mode finally reads as one window; build stamped 1.0.30
  • fix(core): the pard flag 0x8 is the writer's not-in-AE bit - the round-23 reading is retracted (BCC Directional Blur's superseded legacy PixelChooser block - Legacy PixelChooser, Apply PixelChooser, PC Intensity, Mask, Shape, Point 1/2 and the matte controls From..Invert Matte, 23 rows all carrying 0x8 - is drawn by NEITHER pane of AE's Effect Controls, while every row AE does draw carries 0x0 or the group-internal 0x20, so 0x20 is no visibility bit either; our panel surfaced 7 of those rows under PixelChooser / Mocha and 16 more flat above an empty Beat Reactor - the grouping that never matched AE's tree)
  • feat(gui): the graph editor wears AE's chrome in both themes - the pane gains a 1px outline so the light editor reads as a panel on the card, the current-value readout moves inside the field top-left like AE's corner readout, both panes draw AE's fainter half-step minor gridlines plus an emphasized zero line in the value pane too, dark-mode ruler numbers brighten to AE-readable gray, and the speed graph's jump lines get round caps to meet their arcs cleanly
  • fix(core): the ldat tick is 1/1024 of a 30fps frame - 30720 ticks per second, not 1/1024 second (the old base stretched every curve 30x, which the value graph hid - value shape is scale-free - and endpoint speeds masked too, since the stored slopes are already per-second; only the interior of the speed graph betrayed it, squashed to 1/30 of AE's values, the exact lopsided curves that never looked like AE no matter how the editor was restyled). Three independent proofs from an AE-authored preset contributed for side-by-side testing: the beso chunk carries 30720 outright; integrating AE's own drawn speed graph column-by-column only reproduces the stream's 365.88-unit travel when 13312 ticks = 0.4333s; and the decoded tangents then land AE's curve to the pixel (early notch -1643 vs AE's -1637, main valley -3213 vs AE's -3226, keyframe x-ratio 1.111 vs AE's 1.113) where the old base drew -70 in a 13-second world. The old fixture evidence re-reads cleanly: its times are whole-or-half FRAMES in 1024-per-frame units that the earlier revision mislabeled as seconds. Also re-homes the Storage tab's folder link onto System.Diagnostics.Process (CS0234 killed the round-27 Release build before any exe existed) and stamps the build 1.0.28
  • feat(gui): a Storage tab directly below Appearance (the settings sub-nav's second pill wearing a drive glyph) shows what the tool keeps on this machine — the plugin scan catalog and the recently-opened history, each row with its live on-disk readout and a Delete button that asks first (a Yes/No box, a locked file can't take Settings down), resets the in-memory catalog so recognition falls back honestly to the reference tables until the next scan, and an Open Storage Folder link for the logs that live in the same tree — while the GUI assembly's version now stamps every build (1.0.27 on the About page and in the session log banner) so a downloaded exe can prove which round of code it actually contains
  • feat(core): padded keyframe records carry their tangent blocks (the proven 48-byte layout lives at the record's start and an all-zero tail proves it, so a padded stream draws AE's eased curve again instead of the silent straight lines that kept the graph looking unlike AE no matter how the editor was restyled) and the group's own tdmn is finally consumed (the match name AE writes directly BEFORE a tdgp - how Compositing Options declares itself - now names the group through its parT descriptor instead of leaking into the next parameter's kind/flags/menu or leaving BCC's groups anonymous with every parameter hoisted a level up)
  • feat(gui): the graph editor finally follows the app theme (a light app gets the light editor built from its own surface/outline/primary tokens, a dark app keeps AE's sampled #656565 palette - the same way AE's editor follows AE's UI brightness) and the 2D streams draw AE's per-dimension pair: the value pane plots ONE CURVE PER DIMENSION (X and Y, each from its own decoded tangent block) while the speed pane plots the ONE combined magnitude curve sqrt(vx^2+vy^2) that AE draws for multidimensional properties, with dimension 1's tangents parsed from the second interleaved ldat record, geometry-clamped like dimension 0's, and the value range, dashed carries, hover probe and readouts (X, Y pairs / combined units-per-second) covering both dimensions
  • feat(gui): both graph modes now live on AE's single dark Graph Editor skin (the value curve no longer turns light with the app theme - AE has no light editor), the value pane drops the teal glow and the unselected keys' direction lines (AE draws those only on the picked key, which now paints itself editor yellow - square in value, circle in speed - instead of wearing a ring), the value carry beyond the keyed span draws DASHED like AE's stub after the last key, and the speed graph finally draws the way AE draws it: one analytic arc per segment evaluated straight from the control points, TRUE VERTICAL jump lines at every speed discontinuity, zero carries filling the window outside the keyed span, no area fill, a denser bare-number ruler, and AE's own readout grammar (N units / N units/sec)
  • feat(gui): the graphs redraw as AE's Graph Editor (the speed pane always in AE's dark #656565 editor with signed speed dipping under its zero line, AE's shaped key icons and yellow picked key, the value pane keeping AE's light teal card with every bezier key's direction lines, left rulers, red current-time line and a current-value readout) while the parser finally reads lhd3's dimension count so a 2D point's interleaved per-dimension records stop plotting X and Y as consecutive keyframes (the spike-and-decay shape AE never drew), the pard flags word's 0x200 hidden bit plus BCC's placeholder/Hidden padding rows and ARB_DATA blobs leave the panel the way AE renders it, and a nested tdgp can be named through its parT descriptor when the tdsn is missing
  • feat(gui): the inspector's list colors only the clicked row (per-status tint on selection, calm neutral chips), the Effect Controls block drops its caption strip for AE's quiet anatomy, the plugin catalog hands every match name to the most specific finding file (the effect's own .aex beats a bundle that merely harvested the string), same-named groups stay separate across root blocks and nested tdgps file UNDER the groups they are declared in, and the graph guard clamps handle geometry instead of slope rates so honest fast-exit eases keep their true shape while garbage stays bounded
  • feat(gui): a clearer Lister — two-line status-chip effect rows with animated badges, an EC panel with the fx badge, visible sub lines and in-place folding disclosure in both views, separately-filed same-named parameter groups, and AE-shaped graphs with padded windows, drawn value holds and a garbage-tangent envelope
  • fix(gui): the profiles page's empty-linked hint compiles again — the italic hint set TextBlock's property through the static class name (CS0117 'TextBlock' does not contain a definition for 'FontStyles'); the property is FontStyle (singular), the static class is FontStyles, and the line now reads FontStyle = FontStyles.Italic
  • feat(gui): curated fully described plugin profiles with linked cards sectioned apart, AE-shaped graph tangents (percent influences, proportional overlap clamp, proven-record tangent gating), and an Effect Controls panel with Reset links, bold section titles and real slider tracks
  • fix(gui): the system scan is now the FIRST recognition option — it catalogs every .aex (file names, stems and match-name strings read from the plugin binaries) to %APPDATA% and a preset name found there reads Installed with the finding file, with the Torno/aescripts tables demoted to second option — and the round-18 null-key patch's bare-declaration foreach body is braced, fixing CS1023/CS0103
  • fix(gui): the plugin scan actually scans — recursive, access-tolerant, live result line, AE folder pre-selected — and unrecognized effects are now recognized through a fallback chain (CC* namespace rule, then the AE reference dataset's vendor), so bundled Cycore reads Native, every dataset vendor gets a profile switch, and the three CI compile errors are gone (Canvas-positioned graph backdrop, cast minor-tick step)
  • feat(gui): AE effect identities — 741-effect name/category reference from David Torno's public match-name spreadsheet plus curated vendor prefixes, human effect titles in the list and Effect Controls, an About popover naming the effect and its AE menu group with vendor and compatibility, and AE-styled graphs with inset panel, curve halo and minor frame ticks
  • Add files via upload
  • feat(gui): a fresh preset opens the Inspector section, Effect Controls sub-settings start collapsed like AE (flip default mirrored so the first click always lands), and the EC panel gets AE's tighter rhythm — 19px rows, 24px headers, medium-weight effect titles
  • fix(gui): recursive group templates resolve by key from the live tree — the self-referencing StaticResource inside InspGroupTpl/EcSubGroupTpl could never resolve (WPF expands template content off-tree) and crashed the Effect Controls panel and the Inspector — and every inline failure reason now carries the full exception chain
  • fix(gui): the crash system can no longer pile up dialogs — one report per burst with full traces everywhere, preset-load failures now open the copyable report, inspector/graph panes degrade with named reasons, and garbage keyframe doubles can no longer poison the panel
  • fix(gui): the Effect Controls panel decodes every effect again — the sentinel-first pairing bug that emptied the panel — and a real crash system: problem-report window, per-block reasons, and decode warnings instead of silence
  • fix(gui): preset loading can no longer take the panel down — per-effect guards, layout-pass realization inside the guard, stale inspector indexes reset per file, and a crash dialog that names the failing method
  • fix(gui): compile-safe point value read and Reset removed from the Effect Controls header
  • feat(gui): reverse-engineered FFX parameter types render as AE controls — checkboxes, popups with real menus, color swatches — and the inspector's parameter UI is fully separated
  • fix(gui): value graph axis labels that never collapse to zero, single-keyframe plots, an AE-true Effect Controls panel and a calmer inspector
  • feat(gui): value graph rides the true cubic with an analytic speed graph, Effect Controls rebuilt to AE's exact anatomy as the default view, and a simpler inspector
  • feat(gui): AE Effect Controls with real property anatomy, parameter groups and effect eye, Inspector as the default view, compact 40px caption, Easy Ease recognition and value-graph tangent fix
  • feat(gui): AE Effect Controls panel as the default Lister view with a switcher to the split inspector, artifact-proof square window edge, and native OS resize
  • feat(gui): AE property toggles with inline keyframe chips and a timeline strip, Graph Editor squares with playhead and right value axis, rim-colored window backdrop and stretch-freeze Win7 resize
  • feat(gui): AE parameter rows with stopwatch and keyframe navigator, selectable timecoded keyframes, refined value graph with tangent handles, inset rim edge and jank-free Win7 resize
  • feat(gui): AE-style graph editor with value + speed modes, timed keyframes in seconds/frames, richer parameter rows, and flicker-free Win7 resize
  • feat(gui): decode preset parameters and keyframes into a Lister inspector with parameters/keyframes/graph tabs, and cut true rounded window corners with SetWindowRgn
  • fix(gui): rebuild app.ico with native per-size frames and exact-size in-app PNG marks, style every rail item on first paint, blend window corners and stroke an anti-aliased rim
  • fix(gui): restore Win7 minimize animations via SingleBorderWindow chrome, round all four window corners, use the real app icon inside the app
  • fix(gui): grid-render every icon dead-center, rebuild nav rail pill anatomy, upgrade custom window overlay with rim, red close hover and maximize padding fix
  • fix(gui): round-2 screenshot review — swatch circles no longer clipped, sub-nav rows honor padding, on-palette accent strip, flyout columns, Win7 text sharpening
  • fix(gui): live re-theming for code-built UI via SetResourceReference, unified settings card widths, crisp appearance glyph
  • feat(gui): recent-files flyout, persisted console logs with working Logs button, animated section transitions
  • feat(gui): interactive drag-drop feedback, save handoff banner, live CTA summary, About links and M3 focus rings
  • fix(gui): repair mojibake strings, clipped chip, orphan separator, icon semantics, vendor contrast and version format
  • refactor(gui): Convert-first navigation, Settings sub-settings hub with embedded Plugin Profiles, centered icons, rail polish, badge/footer fixes
  • fix(gui): null Tag in palette swatch refresh crashed startup; crash log now captures stack traces
  • feat(gui)!: full WPF rewrite — Material You 2026 teal theme, vector icons, native chrome, rich console, keyboard + window UX
  • fix(gui): profile flow scaffold + console visibility + parent-aware control backing, redrawn swap/gear icons, rail tooltips & press states
  • feat(gui): M3 Expressive overhaul — two-pane Convert, bento Profile grid, balanced Settings, springy rail, contrast/clipping fixes
  • fix(gui): theme-switch correctness, DPI awareness, functional filter/sort, GDI/perf cleanup, dead code removal
  • fix(gui): theme-switch correctness, DPI awareness, functional filter/sort, GDI/perf cleanup, dead code removal
  • fix(gui): theme-switch correctness, DPI awareness, functional filter/sort, GDI/perf cleanup, dead code removal
  • feat(gui): M3 Expressive Bold massive — whole layout 12-col bento adaptive, Rail 88→Adaptive FAB-menu, Rounded Soft real SVG paths (PC font), compact
  • feat(gui): M3 Expressive 2026 massive — whole layout 12-col single-scroll, FAB-menu rail 88 real SVG paths (PC font), themed console/history, all tabs rebuilt compact adaptive, Win7 net48 Keep Blue
  • feat(gui): M3 Expressive 2026 massive — whole layout 12-col single-scroll, FAB-menu rail 88 real SVG paths (PC font), themed console/history, all tabs rebuilt, Win7 net48 Keep Blue
  • feat(gui): M3 Expressive 2026 full — whole layout 12-col single-scroll, FAB-menu rail 88, real SVG paths (PC font), themed console, HistoryStore wired, Win7 net48 Keep Blue
  • fix(gui): MainForm CS0841 supportPane before DragDrop + full M3 expressive layout/sidebar (12-col, FAB-menu, real Symbols/Inter, history) Win7 net48
  • feat(gui): M3 Expressive 2026 full — HistoryStore wired, real Symbols+Inter, FAB-menu rail 88, 12-col single-scroll, all tabs layout+components, Win7 net48
  • fix(gui): correct SettingsTab compile — add System, ClientSize.Width, order darkSwitch before capture
  • fix(gui): M3 foundations adaptive — single scroll, Flow wrap, responsive columns/cards, Win7-safe tonal (keeps net48)
  • feat(gui): M3 Expressive rewrite from stitch — 1440 max, 88px FAB rail, 64px glass header, 32px containers, H1 headings + 360 supporting pane & 48 footer
  • fix(gui): correct Md3Theme token SurfaceVariant→SurfaceContainerHigh in ListerTab empty state
  • fix(gui): stitch alignment — fix Convert/Profile clipping, 88px rail glass title, 32px dashed empty states, drag-drop and xl insights pane
  • fix(gui): add missing System import in ListerTab and finalize sln/ci hygiene
  • fix(gui): add missing System import in ListerTab and finalize sln/ci hygiene

Install

  1. Download FFXCompatibilityTool-0.1.0.exe below — that single file is the whole app.
  2. Run it — no installer, no zip, no DLL pile, no data folder: everything is inside the exe.
  3. Requires .NET Framework 4.8 (preinstalled on Windows 10/11; Windows 7 SP1 needs the 4.8 runtime installed once).

Verify

SHA-256  a56bee1d822b0c8fedd23ffcae3b748b9cc24e194d70cf73baf38631069bd4d5  FFXCompatibilityTool-0.1.0.exe

The in-app Check for Updates (Settings → About) resolves this releases page — publishing a release is what announces the new version to installed builds.

What's Changed

New Contributors

Full Changelog: https://github.com/marbou92/FFX-Compatibility-Tool/commits/v0.1.0