From ead24714e47b3f8c5147f150538f73998ba6bd13 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 14 May 2026 02:38:05 +0000 Subject: [PATCH 1/6] Port MMLaunch from WPF to Avalonia 11 / .NET 8 The old MMLaunch was locked to .NET Framework 4.6.2 and FsXaml/WPF, which required Visual Studio on Windows to build. Replace it with an SDK-style F# Avalonia project on net8.0 so both Windows and Linux contributors can build with just `dotnet build`. The launcher still only runs on Windows (Linux users run it under Proton). - Convert all .xaml + .xaml.fs files to Avalonia .axaml + .axaml.fs. - Drop FsXaml type providers, FSharp.ViewModule, MonoGame, SharpDX, WpfInteropSample, and the linked MMView preview host. Replace the MVVM bits with a hand-rolled INotifyPropertyChanged / RelayCommand pair. - Inline a slim Domain layer (CoreTypes, RegConfig, State, SnapshotProfile) so MMLaunch builds standalone on net8.0 without pulling in the net462-only MMManaged / MMManaged.Engine assemblies (which depend on MonoGame and SharpDX type aliases at the module level). - Replace the old D3D11 PreviewHost with a small Avalonia MeshPreviewControl that parses .mmobj/.obj files inline and draws an orbiting wireframe via Skia. Mouse drag rotates, mouse wheel zooms. - Replace Win32 file/folder pickers with Avalonia StorageProvider. - Update build.fsx to invoke `dotnet build` for MMLaunch via a new BuildMMLaunch target, and remove WpfInteropSample from MMDotNet.sln. --- .gitignore | 1 + MMDotNet.sln | 24 +- MMLaunch/App.axaml | 8 + MMLaunch/App.axaml.fs | 19 + MMLaunch/App.config | 14 - MMLaunch/App.fs | 11 - MMLaunch/App.xaml | 10 - MMLaunch/AssemblyInfo.fs | 14 - MMLaunch/BlenderUtil.fs | 9 +- MMLaunch/BlenderWindow.axaml | 25 + MMLaunch/BlenderWindow.axaml.fs | 123 +++ MMLaunch/BlenderWindow.xaml | 30 - MMLaunch/BlenderWindow.xaml.fs | 156 --- MMLaunch/ConfirmDialog.axaml | 23 + MMLaunch/ConfirmDialog.axaml.fs | 61 ++ MMLaunch/ConfirmDialog.xaml | 31 - MMLaunch/ConfirmDialog.xaml.fs | 88 -- MMLaunch/CreateModWindow.axaml | 38 + MMLaunch/CreateModWindow.axaml.fs | 201 ++++ MMLaunch/CreateModWindow.xaml | 58 -- MMLaunch/CreateModWindow.xaml.fs | 229 ----- MMLaunch/Domain.fs | 102 ++ MMLaunch/GameProfileWindow.axaml | 23 + MMLaunch/GameProfileWindow.axaml.fs | 57 ++ MMLaunch/GameProfileWindow.xaml | 24 - MMLaunch/GameProfileWindow.xaml.fs | 79 -- MMLaunch/MMLaunch.fsproj | 232 ++--- MMLaunch/MainWindow.axaml | 91 ++ MMLaunch/MainWindow.axaml.fs | 797 +++++++++++++++ MMLaunch/MainWindow.xaml | 107 -- MMLaunch/MainWindow.xaml.fs | 920 ------------------ MMLaunch/MeshPreviewControl.fs | 214 ++++ MMLaunch/PreferencesWindow.axaml | 17 + MMLaunch/PreferencesWindow.axaml.fs | 37 + MMLaunch/PreferencesWindow.xaml | 22 - MMLaunch/PreferencesWindow.xaml.fs | 64 -- MMLaunch/PreviewHost.fs | 69 -- MMLaunch/ProcessUtil.fs | 4 +- MMLaunch/Program.fs | 17 + MMLaunch/Resource.rc | 1 - MMLaunch/Resource.res | Bin 36792 -> 0 bytes MMLaunch/SnapshotProfile.fs | 88 ++ MMLaunch/State.fs | 55 ++ MMLaunch/TestFSI.fsx | 6 - MMLaunch/ViewModelBase.fs | 32 + MMLaunch/ViewModelUtil.fs | 255 ++++- MMLaunch/WpfInteropSample/App.xaml | 8 - MMLaunch/WpfInteropSample/App.xaml.cs | 6 - MMLaunch/WpfInteropSample/D3D11Host.cs | 448 --------- MMLaunch/WpfInteropSample/D3D11Image.cs | 190 ---- MMLaunch/WpfInteropSample/D3D9.cs | 178 ---- MMLaunch/WpfInteropSample/MainWindow.xaml | 43 - MMLaunch/WpfInteropSample/MainWindow.xaml.cs | 10 - .../Properties/AssemblyInfo.cs | 55 -- .../Properties/Resources.Designer.cs | 63 -- .../Properties/Resources.resx | 117 --- .../Properties/Settings.Designer.cs | 26 - .../Properties/Settings.settings | 7 - MMLaunch/WpfInteropSample/ReadMe.txt | 3 - .../WpfInteropSample/WpfInteropSample.csproj | 204 ---- .../WpfInteropSample/WpfInteropSample.sln | 47 - MMLaunch/WpfInteropSample/app.config | 3 - MMLaunch/app.manifest | 18 + MMLaunch/paket.references | 4 - build.fsx | 37 +- 65 files changed, 2351 insertions(+), 3602 deletions(-) create mode 100644 MMLaunch/App.axaml create mode 100644 MMLaunch/App.axaml.fs delete mode 100644 MMLaunch/App.config delete mode 100644 MMLaunch/App.fs delete mode 100644 MMLaunch/App.xaml delete mode 100644 MMLaunch/AssemblyInfo.fs create mode 100644 MMLaunch/BlenderWindow.axaml create mode 100644 MMLaunch/BlenderWindow.axaml.fs delete mode 100644 MMLaunch/BlenderWindow.xaml delete mode 100644 MMLaunch/BlenderWindow.xaml.fs create mode 100644 MMLaunch/ConfirmDialog.axaml create mode 100644 MMLaunch/ConfirmDialog.axaml.fs delete mode 100644 MMLaunch/ConfirmDialog.xaml delete mode 100644 MMLaunch/ConfirmDialog.xaml.fs create mode 100644 MMLaunch/CreateModWindow.axaml create mode 100644 MMLaunch/CreateModWindow.axaml.fs delete mode 100644 MMLaunch/CreateModWindow.xaml delete mode 100644 MMLaunch/CreateModWindow.xaml.fs create mode 100644 MMLaunch/Domain.fs create mode 100644 MMLaunch/GameProfileWindow.axaml create mode 100644 MMLaunch/GameProfileWindow.axaml.fs delete mode 100644 MMLaunch/GameProfileWindow.xaml delete mode 100644 MMLaunch/GameProfileWindow.xaml.fs create mode 100644 MMLaunch/MainWindow.axaml create mode 100644 MMLaunch/MainWindow.axaml.fs delete mode 100644 MMLaunch/MainWindow.xaml delete mode 100644 MMLaunch/MainWindow.xaml.fs create mode 100644 MMLaunch/MeshPreviewControl.fs create mode 100644 MMLaunch/PreferencesWindow.axaml create mode 100644 MMLaunch/PreferencesWindow.axaml.fs delete mode 100644 MMLaunch/PreferencesWindow.xaml delete mode 100644 MMLaunch/PreferencesWindow.xaml.fs delete mode 100644 MMLaunch/PreviewHost.fs create mode 100644 MMLaunch/Program.fs delete mode 100644 MMLaunch/Resource.rc delete mode 100644 MMLaunch/Resource.res create mode 100644 MMLaunch/SnapshotProfile.fs create mode 100644 MMLaunch/State.fs delete mode 100644 MMLaunch/TestFSI.fsx create mode 100644 MMLaunch/ViewModelBase.fs delete mode 100644 MMLaunch/WpfInteropSample/App.xaml delete mode 100644 MMLaunch/WpfInteropSample/App.xaml.cs delete mode 100644 MMLaunch/WpfInteropSample/D3D11Host.cs delete mode 100644 MMLaunch/WpfInteropSample/D3D11Image.cs delete mode 100644 MMLaunch/WpfInteropSample/D3D9.cs delete mode 100644 MMLaunch/WpfInteropSample/MainWindow.xaml delete mode 100644 MMLaunch/WpfInteropSample/MainWindow.xaml.cs delete mode 100644 MMLaunch/WpfInteropSample/Properties/AssemblyInfo.cs delete mode 100644 MMLaunch/WpfInteropSample/Properties/Resources.Designer.cs delete mode 100644 MMLaunch/WpfInteropSample/Properties/Resources.resx delete mode 100644 MMLaunch/WpfInteropSample/Properties/Settings.Designer.cs delete mode 100644 MMLaunch/WpfInteropSample/Properties/Settings.settings delete mode 100644 MMLaunch/WpfInteropSample/ReadMe.txt delete mode 100644 MMLaunch/WpfInteropSample/WpfInteropSample.csproj delete mode 100644 MMLaunch/WpfInteropSample/WpfInteropSample.sln delete mode 100644 MMLaunch/WpfInteropSample/app.config create mode 100644 MMLaunch/app.manifest delete mode 100644 MMLaunch/paket.references diff --git a/.gitignore b/.gitignore index 2b6225d4..39415124 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ Test.MMManaged/obj Native/.vscode/settings.json Native/.ionide/symbolCache.db MMLaunch/obj +MMLaunch/bin .ionide/symbolCache.db .rustc_info.json Native/snaplib/target diff --git a/MMDotNet.sln b/MMDotNet.sln index 521b2a61..40595189 100644 --- a/MMDotNet.sln +++ b/MMDotNet.sln @@ -20,8 +20,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution BlenderScripts\install.py = BlenderScripts\install.py EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfInteropSample", "MMLaunch\WpfInteropSample\WpfInteropSample.csproj", "{B7751FAB-AE42-457F-8D69-51BEE5DC080A}" -EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MMLaunch", "MMLaunch\MMLaunch.fsproj", "{5FC4A84B-A153-4927-B6AA-015C78F6D03D}" EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MMView", "MMView\MMView.fsproj", "{404FA43F-143A-4EB1-AE53-24FC9F65A538}" @@ -52,22 +50,12 @@ Global {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Debug|x86.ActiveCfg = Debug|x86 - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Debug|x86.Build.0 = Debug|x86 - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Release|Any CPU.Build.0 = Release|Any CPU - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Release|x86.ActiveCfg = Release|x86 - {B7751FAB-AE42-457F-8D69-51BEE5DC080A}.Release|x86.Build.0 = Release|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Debug|Any CPU.ActiveCfg = Debug|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Debug|Any CPU.Build.0 = Debug|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Debug|x86.ActiveCfg = Debug|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Debug|x86.Build.0 = Debug|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Release|Any CPU.ActiveCfg = Release|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Release|Any CPU.Build.0 = Release|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Release|x86.ActiveCfg = Release|x86 - {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Release|x86.Build.0 = Release|x86 + {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Debug|x86.ActiveCfg = Debug|Any CPU + {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Release|Any CPU.Build.0 = Release|Any CPU + {5FC4A84B-A153-4927-B6AA-015C78F6D03D}.Release|x86.ActiveCfg = Release|Any CPU {404FA43F-143A-4EB1-AE53-24FC9F65A538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {404FA43F-143A-4EB1-AE53-24FC9F65A538}.Debug|Any CPU.Build.0 = Debug|Any CPU {404FA43F-143A-4EB1-AE53-24FC9F65A538}.Debug|x86.ActiveCfg = Debug|x86 diff --git a/MMLaunch/App.axaml b/MMLaunch/App.axaml new file mode 100644 index 00000000..4831259d --- /dev/null +++ b/MMLaunch/App.axaml @@ -0,0 +1,8 @@ + + + + + diff --git a/MMLaunch/App.axaml.fs b/MMLaunch/App.axaml.fs new file mode 100644 index 00000000..771968f0 --- /dev/null +++ b/MMLaunch/App.axaml.fs @@ -0,0 +1,19 @@ +namespace MMLaunch + +open Avalonia +open Avalonia.Controls.ApplicationLifetimes +open Avalonia.Markup.Xaml + +type App() = + inherit Application() + + override this.Initialize() = + AvaloniaXamlLoader.Load(this) + + override this.OnFrameworkInitializationCompleted() = + match this.ApplicationLifetime with + | :? IClassicDesktopStyleApplicationLifetime as desktop -> + desktop.MainWindow <- MainWindow() + | _ -> () + + base.OnFrameworkInitializationCompleted() diff --git a/MMLaunch/App.config b/MMLaunch/App.config deleted file mode 100644 index e1a25799..00000000 --- a/MMLaunch/App.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MMLaunch/App.fs b/MMLaunch/App.fs deleted file mode 100644 index df840628..00000000 --- a/MMLaunch/App.fs +++ /dev/null @@ -1,11 +0,0 @@ -module main - -open System -open FsXaml - -type App = XAML<"App.xaml"> - -[] -[] -let main argv = - App().Root.Run() \ No newline at end of file diff --git a/MMLaunch/App.xaml b/MMLaunch/App.xaml deleted file mode 100644 index f61c4ba3..00000000 --- a/MMLaunch/App.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MMLaunch/AssemblyInfo.fs b/MMLaunch/AssemblyInfo.fs deleted file mode 100644 index 625be1eb..00000000 --- a/MMLaunch/AssemblyInfo.fs +++ /dev/null @@ -1,14 +0,0 @@ -namespace System -open System.Reflection -open System.Runtime.InteropServices - -[] -[] -[] -[] -[] -[] -do () - -module internal AssemblyVersionInformation = - let [] Version = "1.2.0.0" diff --git a/MMLaunch/BlenderUtil.fs b/MMLaunch/BlenderUtil.fs index 24b671f7..fcbc6774 100644 --- a/MMLaunch/BlenderUtil.fs +++ b/MMLaunch/BlenderUtil.fs @@ -163,9 +163,14 @@ module BlenderUtil = let rawMsg = sprintf "\n\nTried to run: %s\n\nStdout:\n%s\n\nStderr:\n%s" cmd rawOut "" failwithf "No addon paths detected; install script may not be compatible with this version of blender:%s" rawMsg - let isWritable (p:string) = + let isWritable (p:string) = + // Try to write a probe file. Directory.GetAccessControl from + // .NET Framework no longer ships in core; this is a portable + // equivalent. try - Directory.GetAccessControl(p) |> ignore + let probe = Path.Combine(p, ".mmlaunch_probe_" + System.Guid.NewGuid().ToString("N")) + File.WriteAllText(probe, "") + File.Delete(probe) true with | _ -> false diff --git a/MMLaunch/BlenderWindow.axaml b/MMLaunch/BlenderWindow.axaml new file mode 100644 index 00000000..530b61e6 --- /dev/null +++ b/MMLaunch/BlenderWindow.axaml @@ -0,0 +1,25 @@ + + + + + + +