mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge Master
This commit is contained in:
commit
35f99b7100
@ -126,6 +126,10 @@ namespace Wabbajack.Common
|
||||
public static AbsolutePath SettingsFile => LocalAppDataPath.Combine("settings.json");
|
||||
public static RelativePath SettingsIni = (RelativePath)"settings.ini";
|
||||
public static byte SettingsVersion => 1;
|
||||
public static Extension SeqExtension = new Extension(".seq");
|
||||
|
||||
public static RelativePath SettingsJson = (RelativePath)"settings.json";
|
||||
|
||||
public static Extension TempExtension = new Extension(".temp");
|
||||
|
||||
public static Extension OctoSig = new Extension(".octo_sig");
|
||||
|
@ -105,6 +105,9 @@ namespace Wabbajack.Lib
|
||||
{
|
||||
if (a.State is IMetaState metaState)
|
||||
{
|
||||
if (metaState.URL == null)
|
||||
return;
|
||||
|
||||
var b = await metaState.LoadMetaData();
|
||||
Utils.Log(b
|
||||
? $"Getting meta data for {a.Name} was successful!"
|
||||
|
@ -40,8 +40,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
return new State();
|
||||
}
|
||||
|
||||
|
||||
private static bool IsAlwaysEnabled(dynamic data)
|
||||
public static bool IsAlwaysEnabled(dynamic data)
|
||||
{
|
||||
if (data == null)
|
||||
return false;
|
||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
public class IncludeThisProfile : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<AbsolutePath> _correctProfiles;
|
||||
private readonly MO2Compiler _mo2Compiler;
|
||||
private MO2Compiler _mo2Compiler;
|
||||
|
||||
public IncludeThisProfile(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
@ -21,18 +21,17 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
||||
{
|
||||
if (_correctProfiles.Any(p => source.AbsolutePath.InFolder(p)))
|
||||
{
|
||||
var data = source.Path.FileName == Consts.ModListTxt
|
||||
? await ReadAndCleanModlist(source.AbsolutePath)
|
||||
: await source.AbsolutePath.ReadAllBytesAsync();
|
||||
if (!_correctProfiles.Any(p => source.AbsolutePath.InFolder(p)))
|
||||
return null;
|
||||
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceDataID = await _compiler.IncludeFile(data);
|
||||
return e;
|
||||
}
|
||||
var data = source.Path.FileName == Consts.ModListTxt
|
||||
? await ReadAndCleanModlist(source.AbsolutePath)
|
||||
: await source.AbsolutePath.ReadAllBytesAsync();
|
||||
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceDataID = await _compiler.IncludeFile(data);
|
||||
return e;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IState GetState()
|
||||
@ -43,8 +42,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
private static async Task<byte[]> ReadAndCleanModlist(AbsolutePath absolutePath)
|
||||
{
|
||||
var lines = await absolutePath.ReadAllLinesAsync();
|
||||
lines = lines.Where(line => !(line.StartsWith("-") && !line.EndsWith("_separator")))
|
||||
.ToArray();
|
||||
lines = lines.Where(line => !(line.StartsWith("-") && !line.EndsWith("_separator"))).ToArray();
|
||||
return Encoding.UTF8.GetBytes(string.Join("\r\n", lines));
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,16 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
var absolute = true;
|
||||
if (url == null || url.Host != SiteURL.Host) return null;
|
||||
|
||||
|
||||
if (url.PathAndQuery.StartsWith("/applications/core/interface/file/attachment"))
|
||||
{
|
||||
return new TState
|
||||
{
|
||||
IsAttachment = true,
|
||||
FullURL = url.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
if (url.PathAndQuery.StartsWith("/index.php?"))
|
||||
{
|
||||
var id2 = HttpUtility.ParseQueryString(url.Query)["r"];
|
||||
@ -38,6 +47,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
var name = parsed[null].Split("/", StringSplitOptions.RemoveEmptyEntries).Last();
|
||||
return new TState
|
||||
{
|
||||
FullURL = url.AbsolutePath,
|
||||
FileID = id2,
|
||||
FileName = name
|
||||
};
|
||||
@ -57,6 +67,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
return new TState
|
||||
{
|
||||
FullURL = url.AbsolutePath,
|
||||
FileID = id,
|
||||
FileName = file
|
||||
};
|
||||
@ -67,26 +78,30 @@ namespace Wabbajack.Lib.Downloaders
|
||||
public class State<TStateDownloader> : AbstractDownloadState, IMetaState where TStateDownloader : IDownloader
|
||||
{
|
||||
[Key(0)]
|
||||
public string FullURL { get; set; }
|
||||
[Key(1)]
|
||||
public bool IsAttachment { get; set; }
|
||||
[Key(2)]
|
||||
public string FileID { get; set; }
|
||||
|
||||
[Key(1)]
|
||||
[Key(3)]
|
||||
public string FileName { get; set; }
|
||||
|
||||
// from IMetaState
|
||||
[Key(2)]
|
||||
[Key(4)]
|
||||
|
||||
public Uri URL => new Uri($"{Site}/files/file/{FileName}");
|
||||
[Key(3)]
|
||||
public string Name { get; set; }
|
||||
[Key(4)]
|
||||
public string Author { get; set; }
|
||||
[Key(5)]
|
||||
public string Version { get; set; }
|
||||
public string Name { get; set; }
|
||||
[Key(6)]
|
||||
public string ImageURL { get; set; }
|
||||
public string Author { get; set; }
|
||||
[Key(7)]
|
||||
public virtual bool IsNSFW { get; set; }
|
||||
public string Version { get; set; }
|
||||
[Key(8)]
|
||||
public string ImageURL { get; set; }
|
||||
[Key(9)]
|
||||
public virtual bool IsNSFW { get; set; }
|
||||
[Key(10)]
|
||||
public string Description { get; set; }
|
||||
|
||||
private static bool IsHTTPS => Downloader.SiteURL.AbsolutePath.StartsWith("https://");
|
||||
@ -105,7 +120,9 @@ namespace Wabbajack.Lib.Downloaders
|
||||
get
|
||||
{
|
||||
return FileID == null
|
||||
? new object[] {Downloader.SiteURL, FileName}
|
||||
? IsAttachment
|
||||
? new object[] {Downloader.SiteURL, IsAttachment, FullURL}
|
||||
: new object[] {Downloader.SiteURL, FileName}
|
||||
: new object[] {Downloader.SiteURL, FileName, FileID};
|
||||
}
|
||||
}
|
||||
@ -127,27 +144,32 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
private async Task<Stream> ResolveDownloadStream()
|
||||
{
|
||||
//var downloader = (AbstractNeedsLoginDownloader)(object)DownloadDispatcher.GetInstance<TDownloader>();
|
||||
|
||||
TOP:
|
||||
var csrfurl = FileID == null
|
||||
? $"{Site}/files/file/{FileName}/?do=download"
|
||||
: $"{Site}/files/file/{FileName}/?do=download&r={FileID}";
|
||||
var html = await Downloader.AuthedClient.GetStringAsync(csrfurl);
|
||||
string url;
|
||||
if (IsAttachment)
|
||||
{
|
||||
url = FullURL;
|
||||
}
|
||||
else
|
||||
{
|
||||
var csrfURL = FileID == null
|
||||
? $"{Site}/files/file/{FileName}/?do=download"
|
||||
: $"{Site}/files/file/{FileName}/?do=download&r={FileID}";
|
||||
var html = await Downloader.AuthedClient.GetStringAsync(csrfURL);
|
||||
|
||||
var pattern = new Regex("(?<=csrfKey=).*(?=[&\"\'])|(?<=csrfKey: \").*(?=[&\"\'])");
|
||||
var matches = pattern.Matches(html).Cast<Match>();
|
||||
var pattern = new Regex("(?<=csrfKey=).*(?=[&\"\'])|(?<=csrfKey: \").*(?=[&\"\'])");
|
||||
var matches = pattern.Matches(html).Cast<Match>();
|
||||
|
||||
var csrfKey = matches.Where(m => m.Length == 32).Select(m => m.ToString()).FirstOrDefault();
|
||||
|
||||
if (csrfKey == null)
|
||||
return null;
|
||||
if (csrfKey == null)
|
||||
return null;
|
||||
|
||||
var sep = Site.EndsWith("?") ? "&" : "?";
|
||||
var url = FileID == null
|
||||
? $"{Site}/files/file/{FileName}/{sep}do=download&confirm=1&t=1&csrfKey={csrfKey}"
|
||||
: $"{Site}/files/file/{FileName}/{sep}do=download&r={FileID}&confirm=1&t=1&csrfKey={csrfKey}";
|
||||
|
||||
var sep = Site.EndsWith("?") ? "&" : "?";
|
||||
url = FileID == null
|
||||
? $"{Site}/files/file/{FileName}/{sep}do=download&confirm=1&t=1&csrfKey={csrfKey}"
|
||||
: $"{Site}/files/file/{FileName}/{sep}do=download&r={FileID}&confirm=1&t=1&csrfKey={csrfKey}";
|
||||
}
|
||||
|
||||
var streamResult = await Downloader.AuthedClient.GetAsync(url);
|
||||
if (streamResult.StatusCode != HttpStatusCode.OK)
|
||||
@ -198,34 +220,33 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
public override string GetManifestURL(Archive a)
|
||||
{
|
||||
return $"{Site}/files/file/{FileName}/?do=download&r={FileID}";
|
||||
return IsAttachment ? FullURL : $"{Site}/files/file/{FileName}/?do=download&r={FileID}";
|
||||
}
|
||||
|
||||
public override string[] GetMetaIni()
|
||||
{
|
||||
if (FileID != null)
|
||||
{
|
||||
if (Site.EndsWith("?"))
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
"[General]", $"directURL={Site}/files/file/{FileName}&do=download&r={FileID}&confirm=1&t=1"
|
||||
};
|
||||
|
||||
}
|
||||
if (IsAttachment)
|
||||
return new[] {"[General]", $"directURL={FullURL}"};
|
||||
|
||||
if (FileID == null)
|
||||
return new[] {"[General]", $"directURL={Site}/files/file/{FileName}"};
|
||||
|
||||
if (Site.EndsWith("?"))
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
"[General]", $"directURL={Site}/files/file/{FileName}/?do=download&r={FileID}&confirm=1&t=1"
|
||||
"[General]", $"directURL={Site}/files/file/{FileName}&do=download&r={FileID}&confirm=1&t=1"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return new[]
|
||||
{
|
||||
"[General]",
|
||||
$"directURL={Site}/files/file/{FileName}"
|
||||
"[General]", $"directURL={Site}/files/file/{FileName}/?do=download&r={FileID}&confirm=1&t=1"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task<bool> LoadMetaData()
|
||||
{
|
||||
return false;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -37,25 +36,80 @@ namespace Wabbajack.Lib
|
||||
|
||||
public class IncludeZEditPatches : ACompilationStep
|
||||
{
|
||||
private Dictionary<AbsolutePath, zEditMerge> _mergesIndexed;
|
||||
private readonly Dictionary<AbsolutePath, zEditMerge> _mergesIndexed;
|
||||
|
||||
private bool _disabled = true;
|
||||
|
||||
public IncludeZEditPatches(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
var zEditPath = FindzEditPath(compiler);
|
||||
var havezEdit = zEditPath != default;
|
||||
|
||||
Utils.Log(havezEdit ? $"Found zEdit at {zEditPath}" : $"zEdit not detected, disabling zEdit routines");
|
||||
Utils.Log(havezEdit ? $"Found zEdit at {zEditPath}" : "zEdit not detected, disabling zEdit routines");
|
||||
|
||||
if (!havezEdit)
|
||||
{
|
||||
_mergesIndexed = new Dictionary<AbsolutePath, zEditMerge>();
|
||||
return;
|
||||
}
|
||||
_mo2Compiler = (MO2Compiler) compiler;
|
||||
|
||||
var settingsFiles = zEditPath.Combine("profiles").EnumerateFiles(false)
|
||||
.Where(f => f.IsFile)
|
||||
.Where(f => f.FileName == Consts.SettingsJson)
|
||||
.Where(f =>
|
||||
{
|
||||
var settings = f.FromJSON<zEditSettings>();
|
||||
|
||||
var merges = zEditPath.Combine("profiles").EnumerateFiles()
|
||||
.Where(f => f.FileName == (RelativePath)"merges.json")
|
||||
.SelectMany(f => f.FromJSON<List<zEditMerge>>())
|
||||
.GroupBy(f => (f.name, f.filename));
|
||||
if (settings.modManager != "Mod Organizer 2")
|
||||
{
|
||||
Utils.Log($"zEdit settings file {f}: modManager is not Mod Organizer 2 but {settings.modManager}!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.managerPath != _mo2Compiler.MO2Folder)
|
||||
{
|
||||
Utils.Log($"zEdit settings file {f}: managerPath is not {_mo2Compiler.MO2Folder} but {settings.managerPath}!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.modsPath != _mo2Compiler.MO2Folder.Combine(Consts.MO2ModFolderName))
|
||||
{
|
||||
Utils.Log($"zEdit settings file {f}: modsPath is not {_mo2Compiler.MO2Folder}\\{Consts.MO2ModFolderName} but {settings.modsPath}!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.mergePath != _mo2Compiler.MO2Folder.Combine(Consts.MO2ModFolderName))
|
||||
{
|
||||
Utils.Log($"zEdit settings file {f}: modsPath is not {_mo2Compiler.MO2Folder}\\{Consts.MO2ModFolderName} but {settings.modsPath}!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).ToList();
|
||||
|
||||
if (!settingsFiles.Any())
|
||||
{
|
||||
Utils.Log($"Found not acceptable settings.json file for zEdit!");
|
||||
return;
|
||||
}
|
||||
|
||||
var profileFolder =
|
||||
settingsFiles.Where(x => x.Parent.Combine("merges.json").IsFile)
|
||||
.Select(x => x == default ? x : x.Parent)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (profileFolder == default)
|
||||
{
|
||||
Utils.Log("Found no acceptable profiles folder for zEdit!");
|
||||
return;
|
||||
}
|
||||
|
||||
var mergeFile = profileFolder.Combine("merges.json");
|
||||
|
||||
Utils.Log($"Using merge file {mergeFile}");
|
||||
|
||||
var merges = mergeFile.FromJSON<List<zEditMerge>>().GroupBy(f => (f.name, f.filename)).ToArray();
|
||||
|
||||
merges.Where(m => m.Count() > 1)
|
||||
.Do(m =>
|
||||
@ -68,11 +122,33 @@ namespace Wabbajack.Lib
|
||||
merges.ToDictionary(
|
||||
m => _mo2Compiler.MO2Folder.Combine((string)Consts.MO2ModFolderName, m.Key.name, m.Key.filename),
|
||||
m => m.First());
|
||||
|
||||
_disabled = false;
|
||||
}
|
||||
|
||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
||||
{
|
||||
if (!_mergesIndexed.TryGetValue(source.AbsolutePath, out var merge)) return null;
|
||||
if (_disabled) return null;
|
||||
if (!_mergesIndexed.TryGetValue(source.AbsolutePath, out var merge))
|
||||
{
|
||||
if(source.AbsolutePath.Extension != Consts.SeqExtension)
|
||||
return null;
|
||||
|
||||
var seqFolder = source.AbsolutePath.Parent;
|
||||
|
||||
if (seqFolder.FileName != (RelativePath)"seq")
|
||||
return null;
|
||||
|
||||
var mergeFolder = seqFolder.Parent;
|
||||
var mergeName = mergeFolder.FileName;
|
||||
|
||||
if (!mergeFolder.Combine(mergeName + ".esp").Exists)
|
||||
return null;
|
||||
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
|
||||
return inline;
|
||||
}
|
||||
var result = source.EvolveTo<MergedPatch>();
|
||||
result.Sources = merge.plugins.Select(f =>
|
||||
{
|
||||
@ -138,6 +214,14 @@ namespace Wabbajack.Lib
|
||||
}
|
||||
}
|
||||
|
||||
public class zEditSettings
|
||||
{
|
||||
public string modManager;
|
||||
public AbsolutePath managerPath;
|
||||
public AbsolutePath modsPath;
|
||||
public AbsolutePath mergePath;
|
||||
}
|
||||
|
||||
public class zEditMerge
|
||||
{
|
||||
public string name;
|
||||
@ -154,19 +238,18 @@ namespace Wabbajack.Lib
|
||||
|
||||
public static void VerifyMerges(MO2Compiler compiler)
|
||||
{
|
||||
var by_name = compiler.InstallDirectives.ToDictionary(f => f.To);
|
||||
var byName = compiler.InstallDirectives.ToDictionary(f => f.To);
|
||||
|
||||
foreach (var directive in compiler.InstallDirectives.OfType<MergedPatch>())
|
||||
{
|
||||
foreach (var source in directive.Sources)
|
||||
{
|
||||
if (by_name.TryGetValue(source.RelativePath, out var result))
|
||||
{
|
||||
if (result.Hash != source.Hash)
|
||||
throw new InvalidDataException($"Hashes for {result.To} needed for zEdit merge sources don't match, this shouldn't happen");
|
||||
continue;
|
||||
}
|
||||
throw new InvalidDataException($"{source.RelativePath} is needed for merged patch {directive.To} but is not included in the install.");
|
||||
if (!byName.TryGetValue(source.RelativePath, out var result))
|
||||
throw new InvalidDataException(
|
||||
$"{source.RelativePath} is needed for merged patch {directive.To} but is not included in the install.");
|
||||
|
||||
if (result.Hash != source.Hash)
|
||||
throw new InvalidDataException($"Hashes for {result.To} needed for zEdit merge sources don't match, this shouldn't happen");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,18 @@ namespace Wabbajack.Test
|
||||
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(utils.MO2Folder, "tools", "mator", "bleh", "profiles", "myprofile"));
|
||||
|
||||
var settings = new zEditIntegration.zEditSettings()
|
||||
{
|
||||
modManager = "Mod Organizer 2",
|
||||
managerPath = utils.MO2Folder,
|
||||
modsPath = Path.Combine(utils.MO2Folder, Consts.MO2ModFolderName),
|
||||
mergePath = Path.Combine(utils.MO2Folder, Consts.MO2ModFolderName)
|
||||
};
|
||||
|
||||
settings.ToJSON(Path.Combine(utils.MO2Folder, "tools", "mator", "bleh", "profiles", "myprofile",
|
||||
"settings.json"));
|
||||
|
||||
new List<zEditIntegration.zEditMerge>()
|
||||
{
|
||||
new zEditIntegration.zEditMerge()
|
||||
|
@ -231,14 +231,14 @@ namespace Wabbajack
|
||||
{
|
||||
if (Completed?.Failed ?? false)
|
||||
{
|
||||
Process.Start("explorer.exe", (string)Utils.LogFolder);
|
||||
Process.Start("explorer.exe", $"/select,\"{Utils.LogFolder}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Start("explorer.exe",
|
||||
(string)(OutputLocation.TargetPath == default
|
||||
? AbsolutePath.EntryPoint
|
||||
: OutputLocation.TargetPath));
|
||||
OutputLocation.TargetPath == default
|
||||
? $"/select,\"{Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location)}\""
|
||||
: $"/select,\"{OutputLocation.TargetPath}\"");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -101,22 +101,24 @@ namespace Wabbajack
|
||||
using var fs = ModListPath.OpenShared();
|
||||
using var ar = new ZipArchive(fs, ZipArchiveMode.Read);
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
var entry = ar.GetEntry(Readme);
|
||||
if (entry == null)
|
||||
{
|
||||
Utils.Log($"Tried to open a non-existent readme: {Readme}");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var e = entry.Open())
|
||||
{
|
||||
e.CopyTo(ms);
|
||||
}
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
using (var reader = new StreamReader(ms))
|
||||
{
|
||||
var viewer = new TextViewer(reader.ReadToEnd(), Name);
|
||||
viewer.Show();
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(ms);
|
||||
|
||||
var viewer = new TextViewer(reader.ReadToEnd(), Name);
|
||||
viewer.Show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ namespace Wabbajack
|
||||
return modList.SourceModList.Archives
|
||||
.Select(m => m.State)
|
||||
.OfType<IMetaState>()
|
||||
.Where(x => x.URL != default && x.ImageURL != default)
|
||||
.DistinctBy(x => x.URL)
|
||||
// Shuffle it
|
||||
.Shuffle(_random)
|
||||
|
@ -1,14 +1,15 @@
|
||||
<Window
|
||||
<mah:MetroWindow
|
||||
x:Class="Wabbajack.TextViewer"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Wabbajack"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Wabbajack"
|
||||
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
|
||||
Title="TextViewer"
|
||||
Width="800"
|
||||
Height="450"
|
||||
Icon="../Resources/Icons/wabbajack.ico"
|
||||
WindowTitleBrush="{StaticResource MahApps.Brushes.Accent}"
|
||||
Style="{StaticResource {x:Type Window}}"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
@ -17,4 +18,4 @@
|
||||
FontSize="20"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</Window>
|
||||
</mah:MetroWindow>
|
||||
|
@ -1,8 +1,6 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace Wabbajack
|
||||
namespace Wabbajack
|
||||
{
|
||||
public partial class TextViewer : Window
|
||||
public partial class TextViewer
|
||||
{
|
||||
public TextViewer(string text, string title)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user