-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
136 changed files
with
1,318 additions
and
887 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
Mutagen.Bethesda.Synthesis.WPF/VMs/AutogeneratedSettingsVM.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
Oops, something went wrong.