Skip to content

Commit

Permalink
Merge branch 'dev' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
Noggog committed Mar 12, 2021
2 parents 43d39fb + 40a7c36 commit 3e54399
Show file tree
Hide file tree
Showing 136 changed files with 1,318 additions and 887 deletions.
3 changes: 3 additions & 0 deletions Mutagen.Bethesda.Synthesis.WPF/FodyWeavers.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ReactiveUI />
</Weavers>
26 changes: 26 additions & 0 deletions Mutagen.Bethesda.Synthesis.WPF/FodyWeavers.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ReactiveUI" minOccurs="0" maxOccurs="1" type="xs:anyType" />
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
15 changes: 15 additions & 0 deletions Mutagen.Bethesda.Synthesis.WPF/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

#if NET_5
#else
namespace System.Runtime.CompilerServices
{
public sealed class IsExternalInit
{
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,93 @@

<ItemGroup>
<ProjectReference Include="..\Mutagen.Bethesda.Synthesis\Mutagen.Bethesda.Synthesis.csproj" />
<ProjectReference Include="..\Synthesis.Bethesda.Execution\Synthesis.Bethesda.Execution.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="GitInfo" Version="2.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Mutagen.Bethesda.WPF" Version="0.26.6" />
<PackageReference Include="NuGetizer" Version="0.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="reactiveui" Version="13.1.1" />
<PackageReference Include="ReactiveUI.Fody" Version="13.1.1" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.0.2" />
<PackageReference Include="LibGit2Sharp" Version="0.26.2" />
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
</ItemGroup>

<ItemGroup>
<Page Update="Resources.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\BasicSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\BoolSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\DictionarySettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\EnumerableFormLinkSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\EnumerableModKeySettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\EnumerableObjectSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\EnumerableSimpleSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\EnumSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\FormLinkSettingsView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\ModKeySettingsView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\ObjectSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\StaticEnumDictionaryView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\FieldViews\UnknownSettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\SelectionWrapper.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\SettingDepthView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\Settings\SettingsNodeView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
</ItemGroup>

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="RD /S /Q &quot;%25USERPROFILE%25\.nuget\packages\mutagen.bethesda.synthesis.wpf&quot;" />
</Target>

<!-- Replace inherited target -->
<Target Name="SetVersion" BeforeTargets="GetAssemblyVersion;GetPackageVersion" DependsOnTargets="GitVersion" Returns="$(PackageVersion)">
<PropertyGroup>
<DevLabel Condition="'$(GitCommits)' != '0' or $(GitIsDirty) != ''">.1-dev</DevLabel>
<AssemblyDevLabel Condition="'$(GitCommits)' != '0' or $(GitIsDirty) != ''">.1</AssemblyDevLabel>
<FileVersion>$(GitBaseVersionMajor).$(GitBaseVersionMinor).$(GitBaseVersionPatch)$(AssemblyDevLabel)</FileVersion>
<PackageVersion>$(GitBaseVersionMajor).$(GitBaseVersionMinor).$(GitBaseVersionPatch)$(DevLabel)</PackageVersion>
<InformationalVersion>$(PackageVersion)</InformationalVersion>
</PropertyGroup>
</Target>

</Project>
109 changes: 109 additions & 0 deletions Mutagen.Bethesda.Synthesis.WPF/ReflectionUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using Noggog;
using Noggog.Utility;
using Synthesis.Bethesda;
using Synthesis.Bethesda.Execution;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Mutagen.Bethesda.Synthesis.WPF
{
public static class ReflectionUtility
{
static void CopyDirectory(string source, string target, CancellationToken cancel)
{
var stack = new Stack<Folders>();
stack.Push(new Folders(source, target));

while (stack.Count > 0)
{
if (cancel.IsCancellationRequested) return;
var folders = stack.Pop();
Directory.CreateDirectory(folders.Target);
foreach (var file in Directory.GetFiles(folders.Source, "*.*"))
{
if (cancel.IsCancellationRequested) return;
File.Copy(file, Path.Combine(folders.Target, Path.GetFileName(file)));
}

foreach (var folder in Directory.GetDirectories(folders.Source))
{
if (cancel.IsCancellationRequested) return;
stack.Push(new Folders(folder, Path.Combine(folders.Target, Path.GetFileName(folder))));
}
}
}

class Folders
{
public string Source { get; private set; }
public string Target { get; private set; }

public Folders(string source, string target)
{
Source = source;
Target = target;
}
}

public static async Task<GetResponse<(TRet Item, TempFolder Temp)>> ExtractInfoFromProject<TRet>(string projPath, CancellationToken cancel, Func<Assembly, GetResponse<TRet>> getter, Action<string> log)
{
if (cancel.IsCancellationRequested) return GetResponse<(TRet Item, TempFolder Temp)>.Fail("Cancelled");

// Copy to a temp folder for build + loading, just to keep the main one free to be swapped/modified as needed
var tempFolder = TempFolder.FactoryByPath(Path.Combine(Paths.LoadingFolder, Path.GetRandomFileName()));
if (cancel.IsCancellationRequested) return GetResponse<(TRet Item, TempFolder Temp)>.Fail("Cancelled");
var projDir = Path.GetDirectoryName(projPath)!;
log($"Starting project assembly info extraction. Copying project from {projDir} to {tempFolder.Dir.Path}");
CopyDirectory(projDir, tempFolder.Dir.Path, cancel);
projPath = Path.Combine(tempFolder.Dir.Path, Path.GetFileName(projPath));
log($"Retrieving executable path from {projPath}");
var exec = await DotNetCommands.GetExecutablePath(projPath, cancel, log);
if (exec.Failed) return exec.BubbleFailure<(TRet Item, TempFolder Temp)>();
log($"Located executable path for {projPath}: {exec.Value}");
var ret = ExecuteAndUnload(exec.Value, getter);
if (ret.Failed) return ret.BubbleFailure<(TRet Item, TempFolder Temp)>();
return (ret.Value, tempFolder);
}

private static GetResponse<TRet> ExecuteAndUnload<TRet>(string exec, Func<Assembly, GetResponse<TRet>> getter)
{
return AssemblyLoading.ExecuteAndForceUnload(exec, getter, () => new FormKeyAssemblyLoadContext(exec));
}

class FormKeyAssemblyLoadContext : AssemblyLoadContext
{
// Resolver of the locations of the assemblies that are dependencies of the
// main plugin assembly.
private readonly AssemblyDependencyResolver _resolver;

public FormKeyAssemblyLoadContext(string pluginPath) : base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}

// The Load method override causes all the dependencies present in the plugin's binary directory to get loaded
// into the HostAssemblyLoadContext together with the plugin assembly itself.
// NOTE: The Interface assembly must not be present in the plugin's binary directory, otherwise we would
// end up with the assembly being loaded twice. Once in the default context and once in the HostAssemblyLoadContext.
// The types present on the host and plugin side would then not match even though they would have the same names.
protected override Assembly? Load(AssemblyName name)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(name);

if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}

return null;
}
}
}
}
File renamed without changes.
5 changes: 3 additions & 2 deletions Mutagen.Bethesda.Synthesis.WPF/SynthesisWpfMixIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static class SynthesisWpfMixIn
{
public static SynthesisPipeline SetForWpf(
this SynthesisPipeline pipe,
SynthesisPipeline.OpenForSettingsFunction? openForSettings,
SynthesisPipeline.OpenFunction? openForSettings,
bool adjustArguments = true)
{
bool shutdown = true;
Expand All @@ -28,7 +28,7 @@ public static SynthesisPipeline SetForWpf(
pipe.SetOpenForSettings((r) =>
{
shutdown = false;
openForSettings(r);
return openForSettings(r);
});
}
if (adjustArguments)
Expand All @@ -52,6 +52,7 @@ public static SynthesisPipeline SetForWpf<TWindow>(
window.Left = r.Left;
window.Top = r.Top;
window.ShowDialog();
return 0;
},
adjustArguments: adjustArguments);
}
Expand Down
102 changes: 102 additions & 0 deletions Mutagen.Bethesda.Synthesis.WPF/VMs/AutogeneratedSettingsVM.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using DynamicData;
using Noggog;
using Noggog.WPF;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Synthesis.Bethesda;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mutagen.Bethesda.Synthesis.WPF
{
public class AutogeneratedSettingsVM : ViewModel
{
private readonly ObservableAsPropertyHelper<bool> _SettingsLoading;
public bool SettingsLoading => _SettingsLoading.Value;

private readonly ObservableAsPropertyHelper<ErrorResponse> _Status;
public ErrorResponse Error => _Status.Value;

[Reactive]
public ReflectionSettingsVM? SelectedSettings { get; set; }

private readonly ObservableAsPropertyHelper<ReflectionSettingsBundleVM?> _Bundle;
public ReflectionSettingsBundleVM? Bundle => _Bundle.Value;

public AutogeneratedSettingsVM(
SettingsConfiguration config,
string projPath,
string displayName,
IObservable<IChangeSet<LoadOrderEntryVM>> loadOrder,
IObservable<ILinkCache> linkCache,
Action<string> log)
{
var targetSettingsVM = Observable.Return(Unit.Default)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(_ =>
{
return Observable.Create<(bool Processing, GetResponse<ReflectionSettingsBundleVM> SettingsVM)>(async (observer, cancel) =>
{
try
{
observer.OnNext((true, GetResponse<ReflectionSettingsBundleVM>.Fail("Loading")));
var reflectionBundle = await ReflectionSettingsBundleVM.ExtractBundle(
projPath,
targets: config.Targets,
detectedLoadOrder: loadOrder,
linkCache: linkCache,
displayName: displayName,
log: log,
cancel: cancel);
if (reflectionBundle.Failed)
{
observer.OnNext((false, reflectionBundle));
return;
}
observer.OnNext((false, reflectionBundle.Value));
}
catch (Exception ex)
{
observer.OnNext((false, GetResponse<ReflectionSettingsBundleVM>.Fail(ex)));
}
observer.OnCompleted();
});
})
.Switch()
.DisposePrevious()
.Replay(1)
.RefCount();

_SettingsLoading = targetSettingsVM
.Select(t => t.Processing)
.ToGuiProperty(this, nameof(SettingsLoading), deferSubscription: true);

_Bundle = targetSettingsVM
.Select(x =>
{
if (x.Processing || x.SettingsVM.Failed)
{
return new ReflectionSettingsBundleVM();
}
return x.SettingsVM.Value;
})
.ObserveOnGui()
.Select(x =>
{
SelectedSettings = x.Settings?.FirstOrDefault();
return x;
})
.DisposePrevious()
.ToGuiProperty<ReflectionSettingsBundleVM?>(this, nameof(Bundle), initialValue: null, deferSubscription: true);

_Status = targetSettingsVM
.Select(x => (ErrorResponse)x.SettingsVM)
.ToGuiProperty(this, nameof(Error), ErrorResponse.Success, deferSubscription: true);
}
}
}
Loading

0 comments on commit 3e54399

Please sign in to comment.