mirror of
https://github.com/4sval/FModel.git
synced 2026-03-21 17:24:26 -05:00
commit
6afd2090ef
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
|
|
@ -18,6 +18,9 @@ jobs:
|
|||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Fetch Submodules Recursively
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: .NET 5 Setup
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
|
|
@ -27,7 +30,7 @@ jobs:
|
|||
run: dotnet restore FModel
|
||||
|
||||
- name: .NET Publish
|
||||
run: dotnet publish FModel -c Release -f net5.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }} --no-self-contained -r win-x64
|
||||
run: dotnet publish FModel -c Release --no-self-contained -r win-x64 -f net5.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }}
|
||||
|
||||
- name: ZIP File
|
||||
uses: papeloto/action-zip@v1
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit a520ada39b9736d06db1df3321c7695873b23c44
|
||||
Subproject commit 6d911668e730b3f4c1eab0b3f7e9a99bc3dde943
|
||||
|
|
@ -97,7 +97,7 @@ namespace FModel
|
|||
if (messageBox.Result == MessageBoxResult.Custom && (EErrorKind) messageBox.ButtonPressed.Id != EErrorKind.Ignore)
|
||||
{
|
||||
if ((EErrorKind) messageBox.ButtonPressed.Id == EErrorKind.ResetSettings)
|
||||
UserSettings.Delete();
|
||||
UserSettings.Default = new UserSettings();
|
||||
|
||||
ApplicationService.ApplicationView.Restart();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@ namespace FModel
|
|||
public const string GREEN = "#98C379";
|
||||
public const string YELLOW = "#E5C07B";
|
||||
public const string BLUE = "#528BCC";
|
||||
|
||||
public const string DONATE_LINK = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EP9SSWG8MW4UC&source=url";
|
||||
public const string CHANGELOG_LINK = "https://github.com/iAmAsval/FModel/releases/latest";
|
||||
public const string ISSUE_LINK = "https://github.com/iAmAsval/FModel/issues/new";
|
||||
public const string DISCORD_LINK = "https://discord.gg/fdkNYYQ";
|
||||
|
||||
public const string ISSUE_LINK = "https://github.com/iAmAsval/FModel/issues/new/choose";
|
||||
public const string DONATE_LINK = "https://fmodel.app/donate";
|
||||
public const string DISCORD_LINK = "https://fmodel.app/discord";
|
||||
|
||||
public const string _FN_LIVE_TRIGGER = "fortnite-live.manifest";
|
||||
public const string _VAL_LIVE_TRIGGER = "valorant-live.manifest";
|
||||
|
||||
public const string _NO_PRESET_TRIGGER = "Hand Made";
|
||||
}
|
||||
}
|
||||
|
|
@ -64,13 +64,13 @@ namespace FModel.Creator.Bases.FN
|
|||
|
||||
if (Object.TryGetValue(out FStructFallback maxStackSize, "MaxStackSize"))
|
||||
{
|
||||
if (maxStackSize.TryGetValue(out float v, "Value") && v > -1)
|
||||
if (maxStackSize.TryGetValue(out float v, "Value") && v > 0)
|
||||
{
|
||||
_statistics.Add(new IconStat("Max Stack", v));
|
||||
_statistics.Add(new IconStat("Max Stack", v , 15));
|
||||
}
|
||||
else if (TryGetCurveTableStat(maxStackSize, out var s))
|
||||
{
|
||||
_statistics.Add(new IconStat("Max Stack", s));
|
||||
_statistics.Add(new IconStat("Max Stack", s , 15));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,9 +84,9 @@ namespace FModel.Creator.Bases.FN
|
|||
weaponStatHandle.TryGetValue(out UDataTable dataTable, "DataTable") &&
|
||||
dataTable.TryGetDataTableRow(weaponRowName.Text, StringComparison.OrdinalIgnoreCase, out var weaponRowValue))
|
||||
{
|
||||
if (weaponRowValue.TryGetValue(out float dmgPb, "DmgPB") && dmgPb != 0f && weaponRowValue.TryGetValue(out int bpc , "BulletsPerCartridge")&& bpc != 0f)
|
||||
if (weaponRowValue.TryGetValue(out float dmgPb, "DmgPB") && dmgPb != 0f && weaponRowValue.TryGetValue(out int bpc , "BulletsPerCartridge"))
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "BF7E3CF34A9ACFF52E95EAAD4F09F133", "Damage to Player"), dmgPb * bpc, 200));
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "BF7E3CF34A9ACFF52E95EAAD4F09F133", "Damage to Player"), dmgPb * (bpc != 0f ? bpc : 1), 200));
|
||||
}
|
||||
|
||||
if (weaponRowValue.TryGetValue(out int clipSize, "ClipSize") && clipSize != 0)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace FModel.Creator.Bases.FN
|
|||
texture_finding:
|
||||
foreach (var textureParameter in material.TextureParameterValues) // get texture from base material
|
||||
{
|
||||
if (textureParameter.ParameterValue is not UTexture2D texture || Preview != null) continue;
|
||||
if (!textureParameter.ParameterValue.TryLoad<UTexture2D>(out var texture) || Preview != null) continue;
|
||||
switch (textureParameter.ParameterInfo.Name.Text)
|
||||
{
|
||||
case "SeriesTexture":
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ namespace FModel.Creator.Bases.FN
|
|||
!_apiEndpointView.FortniteApi.TryGetBytes(playlist.Data.Images.Showcase, out var image))
|
||||
return;
|
||||
|
||||
Preview = Utils.GetBitmap(image).Resize(1024, 512); // Force size to 1024x512 to prevent huge previews.
|
||||
Preview = Utils.GetBitmap(image).ResizeWithRatio(1024, 512);
|
||||
Width = Preview.Width;
|
||||
Height = Preview.Height;
|
||||
}
|
||||
|
||||
public override SKImage Draw()
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ namespace FModel.Creator
|
|||
case "FortBadgeItemDefinition":
|
||||
case "FortAwardItemDefinition":
|
||||
case "FortGadgetItemDefinition":
|
||||
case "AthenaCharmItemDefinition":
|
||||
case "FortPlaysetItemDefinition":
|
||||
case "FortGiftBoxItemDefinition":
|
||||
case "FortOutpostItemDefinition":
|
||||
|
|
@ -75,6 +76,7 @@ namespace FModel.Creator
|
|||
case "FortResourceItemDefinition":
|
||||
case "FortBackpackItemDefinition":
|
||||
case "FortEventQuestMapDataAsset":
|
||||
case "FortWeaponModItemDefinition":
|
||||
case "FortCodeTokenItemDefinition":
|
||||
case "FortSchematicItemDefinition":
|
||||
case "FortWorldMultiItemDefinition":
|
||||
|
|
@ -125,9 +127,9 @@ namespace FModel.Creator
|
|||
return true;
|
||||
case "MaterialInstanceConstant"
|
||||
when _object.Owner != null &&
|
||||
(_object.Owner.Name.EndsWith($"/MI_OfferImages/{_object.Name}") ||
|
||||
_object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}") ||
|
||||
_object.Owner.Name.EndsWith($"/MI_BPTile/{_object.Name}")):
|
||||
(_object.Owner.Name.EndsWith($"/MI_OfferImages/{_object.Name}", StringComparison.OrdinalIgnoreCase) ||
|
||||
_object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}", StringComparison.OrdinalIgnoreCase) ||
|
||||
_object.Owner.Name.EndsWith($"/MI_BPTile/{_object.Name}", StringComparison.OrdinalIgnoreCase)):
|
||||
creator = new BaseMaterialInstance(_object, _style);
|
||||
return true;
|
||||
case "FortMtxOfferData":
|
||||
|
|
@ -160,6 +162,7 @@ namespace FModel.Creator
|
|||
case "PlaylistUserOptionColorEnum":
|
||||
case "PlaylistUserOptionFloatEnum":
|
||||
case "PlaylistUserOptionFloatRange":
|
||||
case "PlaylistUserTintedIconIntEnum":
|
||||
case "PlaylistUserOptionPrimaryAsset":
|
||||
case "PlaylistUserOptionCollisionProfileEnum":
|
||||
creator = new BaseUserControl(_object, _style);
|
||||
|
|
@ -272,4 +275,4 @@ namespace FModel.Creator
|
|||
_object = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ namespace FModel.Creator
|
|||
if (material == null) return null;
|
||||
foreach (var textureParameter in material.TextureParameterValues)
|
||||
{
|
||||
if (textureParameter.ParameterValue is not UTexture2D texture) continue;
|
||||
if (!textureParameter.ParameterValue.TryLoad<UTexture2D>(out var texture)) continue;
|
||||
switch (textureParameter.ParameterInfo.Name.Text)
|
||||
{
|
||||
case "MainTex":
|
||||
|
|
@ -107,8 +107,14 @@ namespace FModel.Creator
|
|||
public static SKBitmap GetBitmap(UTexture2D texture) => texture.IsVirtual ? null : SKBitmap.Decode(texture.Decode()?.Encode());
|
||||
public static SKBitmap GetBitmap(byte[] data) => SKBitmap.Decode(data);
|
||||
|
||||
public static SKBitmap ResizeWithRatio(this SKBitmap me, double width, double height)
|
||||
{
|
||||
var ratioX = width / me.Width;
|
||||
var ratioY = height / me.Height;
|
||||
var ratio = ratioX < ratioY ? ratioX : ratioY;
|
||||
return me.Resize(Convert.ToInt32(me.Width * ratio), Convert.ToInt32(me.Height * ratio));
|
||||
}
|
||||
public static SKBitmap Resize(this SKBitmap me, int size) => me.Resize(size, size);
|
||||
|
||||
public static SKBitmap Resize(this SKBitmap me, int width, int height)
|
||||
{
|
||||
var bmp = new SKBitmap(new SKImageInfo(width, height), SKBitmapAllocFlags.ZeroPixels);
|
||||
|
|
@ -116,37 +122,19 @@ namespace FModel.Creator
|
|||
me.ScalePixels(pixmap, SKFilterQuality.Medium);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public static void ClearToTransparent(this SKBitmap me) {
|
||||
var colors = me.Pixels;
|
||||
for (var n = 0; n < colors.Length; n++) {
|
||||
if (colors[n] != SKColors.Black) continue;
|
||||
colors[n] = SKColors.Transparent;
|
||||
}
|
||||
me.Pixels = colors;
|
||||
}
|
||||
|
||||
public static bool TryGetPackageIndexExport<T>(FPackageIndex packageIndex, out T export) where T : UObject
|
||||
{
|
||||
if (packageIndex.ResolvedObject == null)
|
||||
{
|
||||
export = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var outerChain = new List<string>();
|
||||
var current = packageIndex.ResolvedObject.Outer;
|
||||
while (current != null)
|
||||
{
|
||||
outerChain.Add(current.Name.Text);
|
||||
current = current.Outer;
|
||||
}
|
||||
|
||||
if (outerChain.Count < 1)
|
||||
{
|
||||
export = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_applicationView.CUE4Parse.Provider.TryLoadPackage(outerChain[^1], out var pkg))
|
||||
{
|
||||
export = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
export = pkg.GetExport(packageIndex.ResolvedObject.Index) as T;
|
||||
return export != null;
|
||||
return packageIndex.TryLoad(out export);
|
||||
}
|
||||
|
||||
// fullpath must be either without any extension or with the export objectname
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ namespace FModel.Extensions
|
|||
private static readonly IHighlightingDefinition _iniHighlighter = LoadHighlighter("Ini.xshd");
|
||||
private static readonly IHighlightingDefinition _xmlHighlighter = LoadHighlighter("Xml.xshd");
|
||||
private static readonly IHighlightingDefinition _cppHighlighter = LoadHighlighter("Cpp.xshd");
|
||||
private static readonly IHighlightingDefinition _changelogHighlighter = LoadHighlighter("Changelog.xshd");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static IHighlightingDefinition LoadHighlighter(string resourceName)
|
||||
|
|
@ -35,6 +36,8 @@ namespace FModel.Extensions
|
|||
case "h":
|
||||
case "cpp":
|
||||
return _cppHighlighter;
|
||||
case "changelog":
|
||||
return _changelogHighlighter;
|
||||
case "bat":
|
||||
case "txt":
|
||||
case "po":
|
||||
|
|
|
|||
198
FModel/Extensions/ClipboardExtensions.cs
Normal file
198
FModel/Extensions/ClipboardExtensions.cs
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
using SkiaSharp;
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
namespace FModel.Extensions
|
||||
{
|
||||
public static class ClipboardExtensions
|
||||
{
|
||||
public static void SetImage(byte[] pngBytes, string fileName = null)
|
||||
{
|
||||
Clipboard.Clear();
|
||||
var data = new DataObject();
|
||||
using var pngMs = new MemoryStream(pngBytes);
|
||||
using var image = Image.FromStream(pngMs);
|
||||
// As standard bitmap, without transparency support
|
||||
data.SetData(DataFormats.Bitmap, image, true);
|
||||
// As PNG. Gimp will prefer this over the other two
|
||||
data.SetData("PNG", pngMs, false);
|
||||
// As DIB. This is (wrongly) accepted as ARGB by many applications
|
||||
using var dibMemStream = new MemoryStream(ConvertToDib(image));
|
||||
data.SetData(DataFormats.Dib, dibMemStream, false);
|
||||
// Optional fileName
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
var htmlFragment = GenerateHTMLFragment($"<img src=\"{fileName}\"/>");
|
||||
data.SetData(DataFormats.Html, htmlFragment);
|
||||
}
|
||||
// The 'copy=true' argument means the MemoryStreams can be safely disposed after the operation
|
||||
Clipboard.SetDataObject(data, true);
|
||||
}
|
||||
|
||||
public static byte[] ConvertToDib(Image image)
|
||||
{
|
||||
byte[] bm32bData;
|
||||
var width = image.Width;
|
||||
var height = image.Height;
|
||||
|
||||
// Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
|
||||
using (var bm32b = new Bitmap(width, height, PixelFormat.Format32bppPArgb))
|
||||
{
|
||||
using (var gr = Graphics.FromImage(bm32b))
|
||||
{
|
||||
gr.DrawImage(image, new Rectangle(0, 0, width, height));
|
||||
}
|
||||
// Bitmap format has its lines reversed.
|
||||
bm32b.RotateFlip(RotateFlipType.Rotate180FlipX);
|
||||
bm32bData = GetRawBytes(bm32b);
|
||||
}
|
||||
|
||||
// BITMAPINFOHEADER struct for DIB.
|
||||
const int hdrSize = 0x28;
|
||||
var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
|
||||
//Int32 biSize;
|
||||
WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
|
||||
//Int32 biWidth;
|
||||
WriteIntToByteArray(fullImage, 0x04, 4, true, (uint)width);
|
||||
//Int32 biHeight;
|
||||
WriteIntToByteArray(fullImage, 0x08, 4, true, (uint)height);
|
||||
//Int16 biPlanes;
|
||||
WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
|
||||
//Int16 biBitCount;
|
||||
WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
|
||||
//BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
|
||||
WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
|
||||
//Int32 biSizeImage;
|
||||
WriteIntToByteArray(fullImage, 0x14, 4, true, (uint)bm32bData.Length);
|
||||
// These are all 0. Since .net clears new arrays, don't bother writing them.
|
||||
//Int32 biXPelsPerMeter = 0;
|
||||
//Int32 biYPelsPerMeter = 0;
|
||||
//Int32 biClrUsed = 0;
|
||||
//Int32 biClrImportant = 0;
|
||||
|
||||
// The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
|
||||
WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
|
||||
|
||||
Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint)bm32bData.Length);
|
||||
return fullImage;
|
||||
}
|
||||
|
||||
private static byte[] ConvertToDib(byte[] pngBytes = null)
|
||||
{
|
||||
byte[] bm32bData;
|
||||
int width, height;
|
||||
|
||||
using (var skBmp = SKBitmap.Decode(pngBytes))
|
||||
{
|
||||
width = skBmp.Width;
|
||||
height = skBmp.Height;
|
||||
using var rotated = new SKBitmap(new SKImageInfo(width, height, skBmp.ColorType));
|
||||
using var canvas = new SKCanvas(rotated);
|
||||
canvas.Scale(1, -1, 0, height / 2.0f);
|
||||
canvas.DrawBitmap(skBmp, SKPoint.Empty);
|
||||
canvas.Flush();
|
||||
bm32bData = rotated.Bytes;
|
||||
}
|
||||
|
||||
// BITMAPINFOHEADER struct for DIB.
|
||||
const int hdrSize = 0x28;
|
||||
var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
|
||||
//Int32 biSize;
|
||||
WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
|
||||
//Int32 biWidth;
|
||||
WriteIntToByteArray(fullImage, 0x04, 4, true, (uint)width);
|
||||
//Int32 biHeight;
|
||||
WriteIntToByteArray(fullImage, 0x08, 4, true, (uint)height);
|
||||
//Int16 biPlanes;
|
||||
WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
|
||||
//Int16 biBitCount;
|
||||
WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
|
||||
//BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
|
||||
WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
|
||||
//Int32 biSizeImage;
|
||||
WriteIntToByteArray(fullImage, 0x14, 4, true, (uint)bm32bData.Length);
|
||||
// These are all 0. Since .net clears new arrays, don't bother writing them.
|
||||
//Int32 biXPelsPerMeter = 0;
|
||||
//Int32 biYPelsPerMeter = 0;
|
||||
//Int32 biClrUsed = 0;
|
||||
//Int32 biClrImportant = 0;
|
||||
|
||||
// The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
|
||||
WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
|
||||
|
||||
Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint)bm32bData.Length);
|
||||
return fullImage;
|
||||
}
|
||||
|
||||
public static unsafe byte[] GetRawBytes(Bitmap bmp)
|
||||
{
|
||||
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
|
||||
var bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
|
||||
var bytes = (uint)(Math.Abs(bmpData.Stride) * bmp.Height);
|
||||
var buffer = new byte[bytes];
|
||||
fixed (byte* pBuffer = buffer)
|
||||
{
|
||||
Unsafe.CopyBlockUnaligned(pBuffer, bmpData.Scan0.ToPointer(), bytes);
|
||||
}
|
||||
bmp.UnlockBits(bmpData);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static void WriteIntToByteArray(byte[] data, int startIndex, int bytes, bool littleEndian, uint value)
|
||||
{
|
||||
var lastByte = bytes - 1;
|
||||
|
||||
if (data.Length < startIndex + bytes)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
|
||||
}
|
||||
|
||||
for (var index = 0; index < bytes; index++)
|
||||
{
|
||||
var offs = startIndex + (littleEndian ? index : lastByte - index);
|
||||
data[offs] = (byte)(value >> 8 * index & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateHTMLFragment(string html)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
const string header = "Version:0.9\r\nStartHTML:<<<<<<<<<1\r\nEndHTML:<<<<<<<<<2\r\nStartFragment:<<<<<<<<<3\r\nEndFragment:<<<<<<<<<4\r\n";
|
||||
const string startHTML = "<html>\r\n<body>\r\n";
|
||||
const string startFragment = "<!--StartFragment-->";
|
||||
const string endFragment = "<!--EndFragment-->";
|
||||
const string endHTML = "\r\n</body>\r\n</html>";
|
||||
|
||||
sb.Append(header);
|
||||
|
||||
var startHTMLLength = header.Length;
|
||||
var startFragmentLength = startHTMLLength + startHTML.Length + startFragment.Length;
|
||||
var endFragmentLength = startFragmentLength + Encoding.UTF8.GetByteCount(html);
|
||||
var endHTMLLength = endFragmentLength + endFragment.Length + endHTML.Length;
|
||||
|
||||
sb.Replace("<<<<<<<<<1", startHTMLLength.ToString("D10"));
|
||||
sb.Replace("<<<<<<<<<2", endHTMLLength.ToString("D10"));
|
||||
sb.Replace("<<<<<<<<<3", startFragmentLength.ToString("D10"));
|
||||
sb.Replace("<<<<<<<<<4", endFragmentLength.ToString("D10"));
|
||||
|
||||
sb.Append(startHTML);
|
||||
sb.Append(startFragment);
|
||||
sb.Append(html);
|
||||
sb.Append(endFragment);
|
||||
sb.Append(endHTML);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>FModel.ico</ApplicationIcon>
|
||||
<Version>4.0.0</Version>
|
||||
<AssemblyVersion>4.0.1.0</AssemblyVersion>
|
||||
<FileVersion>4.0.1.0</FileVersion>
|
||||
<AssemblyVersion>4.0.1.1</AssemblyVersion>
|
||||
<FileVersion>4.0.1.1</FileVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsPublishable>true</IsPublishable>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
<None Remove="Resources\fallenorder.png" />
|
||||
<None Remove="Resources\FModel.ico" />
|
||||
<None Remove="Resources\folder.png" />
|
||||
<None Remove="Resources\label.png" />
|
||||
<None Remove="Resources\fortnite.png" />
|
||||
<None Remove="Resources\fortnitebr.png" />
|
||||
<None Remove="Resources\gear.png" />
|
||||
|
|
@ -70,6 +71,7 @@
|
|||
<None Remove="Resources\Ini.xshd" />
|
||||
<None Remove="Resources\Xml.xshd" />
|
||||
<None Remove="Resources\Cpp.xshd" />
|
||||
<None Remove="Resources\Changelog.xshd" />
|
||||
<None Remove="Resources\unix.png" />
|
||||
<None Remove="Resources\linux.png" />
|
||||
<None Remove="Resources\stateofdecay2.png" />
|
||||
|
|
@ -96,24 +98,28 @@
|
|||
<EmbeddedResource Include="Resources\Ini.xshd" />
|
||||
<EmbeddedResource Include="Resources\Xml.xshd" />
|
||||
<EmbeddedResource Include="Resources\Cpp.xshd" />
|
||||
<EmbeddedResource Include="Resources\Changelog.xshd" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AdonisUI.ClassicTheme.NET5" Version="1.17.1" />
|
||||
<PackageReference Include="AdonisUI.NET5" Version="1.17.1" />
|
||||
<PackageReference Include="Autoupdater.NET.Official" Version="1.6.4" />
|
||||
<PackageReference Include="AvalonEdit" Version="6.0.1" />
|
||||
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.0" />
|
||||
<PackageReference Include="AvalonEdit" Version="6.1.2.30" />
|
||||
<PackageReference Include="CSCore" Version="1.2.1.2" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
<PackageReference Include="EpicManifestParser" Version="1.2.0" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" />
|
||||
<PackageReference Include="EpicManifestParser" Version="1.2.69-temp" />
|
||||
<PackageReference Include="HelixToolkit.SharpDX.Core.Wpf" Version="2.17.0" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.2.12" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.3" />
|
||||
<PackageReference Include="Oodle.NET" Version="1.0.1" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="3.1.0" />
|
||||
<PackageReference Include="RestSharp" Version="106.11.7" />
|
||||
<PackageReference Include="RestSharp" Version="106.12.0" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
|
||||
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.3" />
|
||||
<PackageReference Include="SkiaSharp.Svg" Version="1.60.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -131,6 +137,7 @@
|
|||
<Resource Include="Resources\fallenorder.png" />
|
||||
<Resource Include="Resources\FModel.ico" />
|
||||
<Resource Include="Resources\folder.png" />
|
||||
<Resource Include="Resources\label.png" />
|
||||
<Resource Include="Resources\fortnite.png" />
|
||||
<Resource Include="Resources\fortnitebr.png" />
|
||||
<Resource Include="Resources\empty_folder.png" />
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<Setter Property="Title" Value="FModel" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding DataContext.TitleExtra, RelativeSource={RelativeSource Self}, Converter={x:Static converters:IsNullToBoolReversedConverter.Instance}}" Value="True">
|
||||
<Setter Property="Title" Value="{Binding DataContext.TitleExtra, RelativeSource={RelativeSource Self}, StringFormat={}FModel - {0}}" />
|
||||
<Setter Property="Title" Value="{Binding DataContext.TitleExtra, RelativeSource={RelativeSource Self}, StringFormat={}FModel {0}}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
|
@ -85,7 +85,13 @@
|
|||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Export Data" Command="{Binding ExportDataCommand}" CommandParameter="{Binding SelectedItems, ElementName=AssetsListName}">
|
||||
<MenuItem Header="Export Data" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Export_Data" />
|
||||
<Binding Path="SelectedItems" ElementName="AssetsListName" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -94,7 +100,13 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Property" Command="{Binding SavePropertyCommand}" CommandParameter="{Binding SelectedItems, ElementName=AssetsListName}">
|
||||
<MenuItem Header="Save Property" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Properties" />
|
||||
<Binding Path="SelectedItems" ElementName="AssetsListName" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -103,7 +115,13 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Texture" Command="{Binding SaveTextureCommand}" CommandParameter="{Binding SelectedItems, ElementName=AssetsListName}">
|
||||
<MenuItem Header="Save Texture" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Texture" />
|
||||
<Binding Path="SelectedItems" ElementName="AssetsListName" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -135,6 +153,9 @@
|
|||
<MenuItem Header="Save Meshes" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Save Animations" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
|
|
@ -194,7 +215,7 @@
|
|||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource GitHubIcon}" />
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource NoteIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
|
|
@ -304,99 +325,143 @@
|
|||
</DockPanel>
|
||||
</TabItem>
|
||||
<TabItem Style="{StaticResource TabItemFillSpace}" Header="Folders">
|
||||
<DockPanel>
|
||||
<TextBlock DockPanel.Dock="Top" TextAlignment="Center" TextWrapping="Wrap" HorizontalAlignment="Center" MaxWidth="375"
|
||||
Text="Open folders to navigate through your loaded files. Badges indicate how many assets are in the folder. To better optimize things, it is recommended to use your hotkeys, to quickly switch between tabs." />
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Separator Grid.Row="0" Style="{StaticResource CustomSeparator}" />
|
||||
<TreeView Grid.Row="1" x:Name="AssetsFolderName" Style="{StaticResource AssetsFolderTreeView}" PreviewMouseDoubleClick="OnAssetsTreeMouseDoubleClick">
|
||||
<TreeView.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Extract Folder's Assets" Click="OnFolderExtractClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ExtractIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Export Folder's Assets Data" Click="OnFolderExportClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ExportIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Assets Properties" Click="OnFolderSaveClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SaveIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Save Directory" Click="OnSaveDirectoryClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource DirectoriesAddIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Copy Directory Path" Click="OnCopyDirectoryPathClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource CopyIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</TreeView.ContextMenu>
|
||||
</TreeView>
|
||||
<Separator Grid.Row="2" Style="{StaticResource CustomSeparator}" Tag="INFORMATION" />
|
||||
<StackPanel Grid.Row="3" Orientation="Vertical" Margin="0 0 0 5">
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding SelectedItem.AssetsList.Assets.Count, ElementName=AssetsFolderName, FallbackValue=0}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="Assets Count" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding SelectedItem.FoldersView.Count, ElementName=AssetsFolderName, FallbackValue=0}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="Folders Count" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding SelectedItem.Package, ElementName=AssetsFolderName, FallbackValue='None'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="Included In Package" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding SelectedItem.MountPoint, ElementName=AssetsFolderName, FallbackValue='/', Converter={x:Static converters:TrimRightToLeftConverter.Instance}, ConverterParameter=275}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Text="Package Mount Point" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="{Binding SelectedItem.Version, ElementName=AssetsFolderName, FallbackValue='VER_UE4_LATEST', Converter={x:Static converters:TrimRightToLeftConverter.Instance}, ConverterParameter=275}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="1" Text="Package Version" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Image Source="/FModel;component/Resources/label.png"
|
||||
Width="16" Height="16" HorizontalAlignment="Center" Margin="0 0 3.5 0" />
|
||||
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" TextTrimming="CharacterEllipsis">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}'{0}' has {1} folders and {2} files">
|
||||
<Binding Path="SelectedItem.Header" ElementName="AssetsFolderName" FallbackValue="None" />
|
||||
<Binding Path="SelectedItem.FoldersView.Count" ElementName="AssetsFolderName" FallbackValue="0" />
|
||||
<Binding Path="SelectedItem.AssetsList.Assets.Count" ElementName="AssetsFolderName" FallbackValue="0" />
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}" ToolTip="Bring Selected Folder To View" Padding="4"
|
||||
Command="{Binding MenuCommand}" CommandParameter="{Binding SelectedItem, ElementName=AssetsFolderName}">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource LocateMeIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Button Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}" ToolTip="Expand All (not appropriate for huge amount of folders)" Padding="4"
|
||||
Command="{Binding MenuCommand}" CommandParameter="ToolBox_Expand_All">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource UnfoldIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Button Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}" ToolTip="Collapse All (not appropriate for huge amount of folders)" Padding="4"
|
||||
Command="{Binding MenuCommand}" CommandParameter="ToolBox_Collapse_All">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource FoldIcon}"/>
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
<Separator Grid.Row="1" Style="{StaticResource CustomSeparator}" Margin="0" />
|
||||
<TreeView Grid.Row="2" x:Name="AssetsFolderName" Style="{StaticResource AssetsFolderTreeView}" PreviewMouseDoubleClick="OnAssetsTreeMouseDoubleClick">
|
||||
<TreeView.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Extract Folder's Assets" Click="OnFolderExtractClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ExtractIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Export Folder's Assets Data" Click="OnFolderExportClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ExportIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Assets Properties" Click="OnFolderSaveClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SaveIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Save Directory" Click="OnSaveDirectoryClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource DirectoriesAddIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Copy Directory Path" Click="OnCopyDirectoryPathClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource CopyIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</TreeView.ContextMenu>
|
||||
</TreeView>
|
||||
<Separator Grid.Row="3" Style="{StaticResource CustomSeparator}" Tag="INFORMATION" />
|
||||
<StackPanel Grid.Row="4" Orientation="Vertical" Margin="0 0 0 5">
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding SelectedItem.AssetsList.Assets.Count, ElementName=AssetsFolderName, FallbackValue=0}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="Assets Count" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding SelectedItem.FoldersView.Count, ElementName=AssetsFolderName, FallbackValue=0}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="Folders Count" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding SelectedItem.Package, ElementName=AssetsFolderName, FallbackValue='None'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="Included In Package" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding SelectedItem.MountPoint, ElementName=AssetsFolderName, FallbackValue='/', Converter={x:Static converters:TrimRightToLeftConverter.Instance}, ConverterParameter=275}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Text="Package Mount Point" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="{Binding SelectedItem.Version, ElementName=AssetsFolderName, FallbackValue='VER_UE4_LATEST', Converter={x:Static converters:TrimRightToLeftConverter.Instance}, ConverterParameter=275}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="1" Text="Package Version" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Style="{StaticResource TabItemFillSpace}"
|
||||
Header="{Binding SelectedItem.AssetsList.Assets.Count, FallbackValue=0, ElementName=AssetsFolderName}"
|
||||
|
|
@ -442,7 +507,13 @@
|
|||
<ListBox Grid.Row="1" x:Name="AssetsListName" Style="{StaticResource AssetsListBox}" PreviewMouseDoubleClick="OnAssetsListMouseDoubleClick">
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Extract in New Tab" Command="{Binding DataContext.ExtractNewTabCommand}" CommandParameter="{Binding SelectedItems}">
|
||||
<MenuItem Header="Extract in New Tab" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Extract_New_Tab" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -451,7 +522,13 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Export Data" Command="{Binding DataContext.ExportDataCommand}" CommandParameter="{Binding SelectedItems}">
|
||||
<MenuItem Header="Export Data" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Export_Data" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -460,7 +537,13 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Properties" Command="{Binding DataContext.SavePropertyCommand}" CommandParameter="{Binding SelectedItems}">
|
||||
<MenuItem Header="Save Properties" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Properties" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -469,7 +552,13 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Texture" Command="{Binding DataContext.SaveTextureCommand}" CommandParameter="{Binding SelectedItems}">
|
||||
<MenuItem Header="Save Texture" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Texture" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -772,6 +861,19 @@
|
|||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="MSH" />
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Width="35" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Animations Enabled">
|
||||
<StatusBarItem.Style>
|
||||
<Style TargetType="StatusBarItem">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsAutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
|
||||
<Setter Property="Visibility" Value="Hidden" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StatusBarItem.Style>
|
||||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="ANM" />
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Open Sounds Enabled">
|
||||
<StatusBarItem.Style>
|
||||
<Style TargetType="StatusBarItem">
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ namespace FModel
|
|||
{new KeyGesture(UserSettings.Default.AutoSaveMaterials.Key, UserSettings.Default.AutoSaveMaterials.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveMeshes", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoSaveMeshes.Key, UserSettings.Default.AutoSaveMeshes.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveAnimations", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoSaveAnimations.Key, UserSettings.Default.AutoSaveAnimations.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoOpenSounds", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoOpenSounds.Key, UserSettings.Default.AutoOpenSounds.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("ReloadMappings", typeof(MainWindow), new InputGestureCollection {new KeyGesture(Key.F12)}), OnMappingsReload));
|
||||
|
|
@ -78,6 +80,7 @@ namespace FModel
|
|||
await _applicationView.AesManager.UpdateProvider(true);
|
||||
await _applicationView.CUE4Parse.InitBenMappings();
|
||||
await _applicationView.InitVgmStream();
|
||||
await _applicationView.InitOodle();
|
||||
|
||||
if (UserSettings.Default.DiscordRpc == EDiscordRpc.Always)
|
||||
_discordHandler.Initialize(_applicationView.CUE4Parse.Game);
|
||||
|
|
@ -151,6 +154,9 @@ namespace FModel
|
|||
case "AutoSaveMeshes":
|
||||
UserSettings.Default.IsAutoSaveMeshes = !UserSettings.Default.IsAutoSaveMeshes;
|
||||
break;
|
||||
case "AutoSaveAnimations":
|
||||
UserSettings.Default.IsAutoSaveAnimations = !UserSettings.Default.IsAutoSaveAnimations;
|
||||
break;
|
||||
case "AutoOpenSounds":
|
||||
UserSettings.Default.IsAutoOpenSounds = !UserSettings.Default.IsAutoOpenSounds;
|
||||
break;
|
||||
|
|
|
|||
39
FModel/Resources/Changelog.xshd
Normal file
39
FModel/Resources/Changelog.xshd
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<SyntaxDefinition name="Changelog" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
|
||||
|
||||
<RuleSet name="diff">
|
||||
<Span multiline="false" foreground="#98C379">
|
||||
<Begin>^\+</Begin>
|
||||
<End>.*(?:\t|\s{2,})+</End>
|
||||
</Span>
|
||||
<Span multiline="false" foreground="#E06C75">
|
||||
<Begin>^\-</Begin>
|
||||
<End>.*(?:\t|\s{2,})+</End>
|
||||
</Span>
|
||||
<Span multiline="false" foreground="#61AFEF">
|
||||
<Begin>^\~</Begin>
|
||||
<End>.*(?:\t|\s{2,})+</End>
|
||||
</Span>
|
||||
</RuleSet>
|
||||
|
||||
<RuleSet name="doc" ignoreCase="false">
|
||||
<Span multiline="false" foreground="#7F848E">
|
||||
<Begin>.*(?:\t|\#{1}|\s{2,})+</Begin>
|
||||
<End>\r\n</End>
|
||||
</Span>
|
||||
<Span multiline="false" underline="true">
|
||||
<Begin>^[0-9]\..*</Begin>
|
||||
</Span>
|
||||
<Keywords underline="true">
|
||||
<Word>ADDED</Word>
|
||||
<Word>FIXED</Word>
|
||||
<Word>REMOVED</Word>
|
||||
<Word>IMPROVED</Word>
|
||||
</Keywords>
|
||||
</RuleSet>
|
||||
|
||||
<RuleSet>
|
||||
<Import ruleSet="diff" />
|
||||
<Import ruleSet="doc" />
|
||||
</RuleSet>
|
||||
|
||||
</SyntaxDefinition>
|
||||
BIN
FModel/Resources/label.png
Normal file
BIN
FModel/Resources/label.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 517 B |
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Windows.Input;
|
||||
using CUE4Parse.UE4.Objects.Core.Serialization;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
|
|
@ -35,6 +36,13 @@ namespace FModel.Settings
|
|||
{
|
||||
if (File.Exists(FilePath)) File.Delete(FilePath);
|
||||
}
|
||||
|
||||
private bool _showChangelog = true;
|
||||
public bool ShowChangelog
|
||||
{
|
||||
get => _showChangelog;
|
||||
set => SetProperty(ref _showChangelog, value);
|
||||
}
|
||||
|
||||
private string _outputDirectory;
|
||||
public string OutputDirectory
|
||||
|
|
@ -92,6 +100,13 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _isAutoSaveMeshes, value);
|
||||
}
|
||||
|
||||
private bool _isAutoSaveAnimations;
|
||||
public bool IsAutoSaveAnimations
|
||||
{
|
||||
get => _isAutoSaveAnimations;
|
||||
set => SetProperty(ref _isAutoSaveAnimations, value);
|
||||
}
|
||||
|
||||
private bool _isAutoOpenSounds = true;
|
||||
public bool IsAutoOpenSounds
|
||||
{
|
||||
|
|
@ -197,20 +212,44 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _imageMergerMargin, value);
|
||||
}
|
||||
|
||||
private IDictionary<FGame, string> _presets = new Dictionary<FGame, string>
|
||||
{
|
||||
{FGame.Unknown, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.FortniteGame, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.ShooterGame, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.DeadByDaylight, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.OakGame, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.Dungeons, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.WorldExplorers, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.g3, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.StateOfDecay2, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.Prospect, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.Indiana, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.RogueCompany, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.SwGame, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.Platform, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.BendGame, Constants._NO_PRESET_TRIGGER}
|
||||
};
|
||||
public IDictionary<FGame, string> Presets
|
||||
{
|
||||
get => _presets;
|
||||
set => SetProperty(ref _presets, value);
|
||||
}
|
||||
|
||||
private IDictionary<FGame, EGame> _overridedGame = new Dictionary<FGame, EGame>
|
||||
{
|
||||
{FGame.Unknown, EGame.GAME_UE4_LATEST},
|
||||
{FGame.FortniteGame, EGame.GAME_UE4_LATEST},
|
||||
{FGame.ShooterGame, EGame.GAME_VALORANT},
|
||||
{FGame.ShooterGame, EGame.GAME_Valorant},
|
||||
{FGame.DeadByDaylight, EGame.GAME_UE4_LATEST},
|
||||
{FGame.OakGame, EGame.GAME_BORDERLANDS3},
|
||||
{FGame.OakGame, EGame.GAME_Borderlands3},
|
||||
{FGame.Dungeons, EGame.GAME_UE4_LATEST},
|
||||
{FGame.WorldExplorers, EGame.GAME_UE4_LATEST},
|
||||
{FGame.g3, EGame.GAME_UE4_22},
|
||||
{FGame.StateOfDecay2, EGame.GAME_SOD2},
|
||||
{FGame.StateOfDecay2, EGame.GAME_StateOfDecay2},
|
||||
{FGame.Prospect, EGame.GAME_UE4_LATEST},
|
||||
{FGame.Indiana, EGame.GAME_UE4_LATEST},
|
||||
{FGame.RogueCompany, EGame.GAME_UE4_LATEST},
|
||||
{FGame.RogueCompany, EGame.GAME_RogueCompany},
|
||||
{FGame.SwGame, EGame.GAME_UE4_LATEST},
|
||||
{FGame.Platform, EGame.GAME_UE4_25},
|
||||
{FGame.BendGame, EGame.GAME_UE4_11}
|
||||
|
|
@ -244,6 +283,54 @@ namespace FModel.Settings
|
|||
get => _overridedUEVersion;
|
||||
set => SetProperty(ref _overridedUEVersion, value);
|
||||
}
|
||||
|
||||
private IDictionary<FGame, List<FCustomVersion>> _overridedCustomVersions = new Dictionary<FGame, List<FCustomVersion>>
|
||||
{
|
||||
{FGame.Unknown, null},
|
||||
{FGame.FortniteGame, null},
|
||||
{FGame.ShooterGame, null},
|
||||
{FGame.DeadByDaylight, null},
|
||||
{FGame.OakGame, null},
|
||||
{FGame.Dungeons, null},
|
||||
{FGame.WorldExplorers, null},
|
||||
{FGame.g3, null},
|
||||
{FGame.StateOfDecay2, null},
|
||||
{FGame.Prospect, null},
|
||||
{FGame.Indiana, null},
|
||||
{FGame.RogueCompany, null},
|
||||
{FGame.SwGame, null},
|
||||
{FGame.Platform, null},
|
||||
{FGame.BendGame, null}
|
||||
};
|
||||
public IDictionary<FGame, List<FCustomVersion>> OverridedCustomVersions
|
||||
{
|
||||
get => _overridedCustomVersions;
|
||||
set => SetProperty(ref _overridedCustomVersions, value);
|
||||
}
|
||||
|
||||
private IDictionary<FGame, Dictionary<string, bool>> _overridedOptions = new Dictionary<FGame, Dictionary<string, bool>>
|
||||
{
|
||||
{FGame.Unknown, null},
|
||||
{FGame.FortniteGame, null},
|
||||
{FGame.ShooterGame, null},
|
||||
{FGame.DeadByDaylight, null},
|
||||
{FGame.OakGame, null},
|
||||
{FGame.Dungeons, null},
|
||||
{FGame.WorldExplorers, null},
|
||||
{FGame.g3, null},
|
||||
{FGame.StateOfDecay2, null},
|
||||
{FGame.Prospect, null},
|
||||
{FGame.Indiana, null},
|
||||
{FGame.RogueCompany, null},
|
||||
{FGame.SwGame, null},
|
||||
{FGame.Platform, null},
|
||||
{FGame.BendGame, null}
|
||||
};
|
||||
public IDictionary<FGame, Dictionary<string, bool>> OverridedOptions
|
||||
{
|
||||
get => _overridedOptions;
|
||||
set => SetProperty(ref _overridedOptions, value);
|
||||
}
|
||||
|
||||
private IDictionary<FGame, IList<CustomDirectory>> _customDirectories = new Dictionary<FGame, IList<CustomDirectory>>
|
||||
{
|
||||
|
|
@ -274,6 +361,9 @@ namespace FModel.Settings
|
|||
{
|
||||
FGame.Dungeons, new List<CustomDirectory>
|
||||
{
|
||||
new("Levels", "Dungeons/Content/data/Lovika/Levels"),
|
||||
new("Friendlies", "Dungeons/Content/Actor/Characters/Friendlies"),
|
||||
new("Skins", "Dungeons/Content/Actor/Characters/Player/Master/Skins"),
|
||||
new("Strings", "Dungeons/Content/Localization/")
|
||||
}
|
||||
},
|
||||
|
|
@ -396,7 +486,14 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _autoSaveMeshes, value);
|
||||
}
|
||||
|
||||
private Hotkey _autoOpenSounds = new(Key.F6);
|
||||
private Hotkey _autoSaveAnimations = new(Key.F6);
|
||||
public Hotkey AutoSaveAnimations
|
||||
{
|
||||
get => _autoSaveAnimations;
|
||||
set => SetProperty(ref _autoSaveAnimations, value);
|
||||
}
|
||||
|
||||
private Hotkey _autoOpenSounds = new(Key.F7);
|
||||
public Hotkey AutoOpenSounds
|
||||
{
|
||||
get => _autoOpenSounds;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ namespace FModel.ViewModels
|
|||
if (key.StartsWith("0x"))
|
||||
key = key[2..];
|
||||
|
||||
return "0x" + key.ToUpper();
|
||||
return "0x" + key.ToUpper().Trim();
|
||||
}
|
||||
|
||||
private IEnumerable<FileItem> EnumerateAesKeys()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AutoUpdaterDotNET;
|
||||
using FModel.Extensions;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -22,7 +24,9 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
private News _news;
|
||||
private Info _infos;
|
||||
private Backup[] _backups;
|
||||
private Game _game;
|
||||
private readonly IDictionary<string, CommunityDesign> _communityDesigns = new Dictionary<string, CommunityDesign>();
|
||||
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
|
||||
|
||||
public FModelApi(IRestClient client) : base(client)
|
||||
{
|
||||
|
|
@ -66,6 +70,19 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
{
|
||||
return _backups ??= GetBackupsAsync(token, gameName).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<Game> GetGamesAsync(CancellationToken token, string gameName)
|
||||
{
|
||||
var request = new RestRequest($"https://api.fmodel.app/v1/games/{gameName}", Method.GET);
|
||||
var response = await _client.ExecuteAsync<Game>(request, token).ConfigureAwait(false);
|
||||
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, request.Resource);
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
public Game GetGames(CancellationToken token, string gameName)
|
||||
{
|
||||
return _game ??= GetGamesAsync(token, gameName).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<CommunityDesign> GetDesignAsync(string designName)
|
||||
{
|
||||
|
|
@ -110,8 +127,13 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
{
|
||||
if (args is {CurrentVersion: { }})
|
||||
{
|
||||
var currentVersion = new Version(args.CurrentVersion);
|
||||
if (currentVersion == args.InstalledVersion) return;
|
||||
var currentVersion = new System.Version(args.CurrentVersion);
|
||||
if (currentVersion == args.InstalledVersion)
|
||||
{
|
||||
if (UserSettings.Default.ShowChangelog)
|
||||
ShowChangelog(args);
|
||||
return;
|
||||
}
|
||||
|
||||
var downgrade = currentVersion < args.InstalledVersion;
|
||||
var messageBox = new MessageBoxModel
|
||||
|
|
@ -125,16 +147,18 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
|
||||
MessageBox.Show(messageBox);
|
||||
if (messageBox.Result != MessageBoxResult.Yes) return;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (AutoUpdater.DownloadUpdate(args))
|
||||
{
|
||||
UserSettings.Default.ShowChangelog = true;
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
UserSettings.Default.ShowChangelog = false;
|
||||
MessageBox.Show(exception.Message, exception.GetType().ToString(), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
|
@ -145,5 +169,17 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
"Update Check Failed", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowChangelog(UpdateInfoEventArgs args)
|
||||
{
|
||||
var request = new RestRequest(args.ChangelogURL, Method.GET);
|
||||
var response = _client.Execute(request);
|
||||
if (string.IsNullOrEmpty(response.Content)) return;
|
||||
|
||||
_applicationView.CUE4Parse.TabControl.AddTab($"Release Notes: {args.CurrentVersion}");
|
||||
_applicationView.CUE4Parse.TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("changelog");
|
||||
_applicationView.CUE4Parse.TabControl.SelectedTab.SetDocumentText(response.Content, false);
|
||||
UserSettings.Default.ShowChangelog = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,22 @@ namespace FModel.ViewModels.ApiEndpoints.Models
|
|||
[J] public string DownloadUrl { get; private set; }
|
||||
[J] public long FileSize { get; private set; }
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{" + nameof(DisplayName) + "}")]
|
||||
public class Game
|
||||
{
|
||||
[J] public string DisplayName { get; private set; }
|
||||
[J] public Dictionary<string, Version> Versions { get; private set; }
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{" + nameof(GameEnum) + "}")]
|
||||
public class Version
|
||||
{
|
||||
[J("game")] public string GameEnum { get; private set; }
|
||||
[J] public int UeVer { get; private set; }
|
||||
[J] public Dictionary<string, int> CustomVersions { get; private set; }
|
||||
[J] public Dictionary<string, bool> Options { get; private set; }
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{" + nameof(Mode) + "}")]
|
||||
public class Info
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
|
@ -10,10 +5,21 @@ using FModel.Settings;
|
|||
using FModel.ViewModels.Commands;
|
||||
using FModel.Views;
|
||||
using FModel.Views.Resources.Controls;
|
||||
|
||||
using Ionic.Zip;
|
||||
|
||||
using Oodle.NET;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
using MessageBox = AdonisUI.Controls.MessageBox;
|
||||
using MessageBoxButton = AdonisUI.Controls.MessageBoxButton;
|
||||
using MessageBoxImage = AdonisUI.Controls.MessageBoxImage;
|
||||
using OodleCUE4 = CUE4Parse.Compression.Oodle;
|
||||
|
||||
namespace FModel.ViewModels
|
||||
{
|
||||
|
|
@ -48,20 +54,15 @@ namespace FModel.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private MenuCommand _menuCommand;
|
||||
public ExtractNewTabCommand ExtractNewTabCommand => _extractNewTabCommand ??= new ExtractNewTabCommand(this);
|
||||
private ExtractNewTabCommand _extractNewTabCommand;
|
||||
public RightClickMenuCommand RightClickMenuCommand => _rightClickMenuCommand ??= new RightClickMenuCommand(this);
|
||||
private RightClickMenuCommand _rightClickMenuCommand;
|
||||
public MenuCommand MenuCommand => _menuCommand ??= new MenuCommand(this);
|
||||
private ExportDataCommand _exportDataCommand;
|
||||
public ExportDataCommand ExportDataCommand => _exportDataCommand ??= new ExportDataCommand(this);
|
||||
private SavePropertyCommand _savePropertyCommand;
|
||||
public SavePropertyCommand SavePropertyCommand => _savePropertyCommand ??= new SavePropertyCommand(this);
|
||||
private SaveTextureCommand _saveTextureCommand;
|
||||
public SaveTextureCommand SaveTextureCommand => _saveTextureCommand ??= new SaveTextureCommand(this);
|
||||
private CopyCommand _copyCommand;
|
||||
private MenuCommand _menuCommand;
|
||||
public CopyCommand CopyCommand => _copyCommand ??= new CopyCommand(this);
|
||||
private CopyCommand _copyCommand;
|
||||
|
||||
public string TitleExtra => $"{UserSettings.Default.UpdateMode} - {CUE4Parse.Game.GetDescription()} ({UserSettings.Default.OverridedGame[CUE4Parse.Game]}){(Build != EBuildKind.Release ? $" ({Build})" : "")}";
|
||||
|
||||
public string TitleExtra => $"{CUE4Parse.Game.GetDescription()} ({UserSettings.Default.OverridedGame[CUE4Parse.Game]}){(Build != EBuildKind.Release ? $" ({Build})" : "")}";
|
||||
public LoadingModesViewModel LoadingModes { get; }
|
||||
public CustomDirectoriesViewModel CustomDirectories { get; }
|
||||
public CUE4ParseViewModel CUE4Parse { get; }
|
||||
|
|
@ -69,6 +70,8 @@ namespace FModel.ViewModels
|
|||
public AesManagerViewModel AesManager { get; }
|
||||
public AudioPlayerViewModel AudioPlayer { get; }
|
||||
public MapViewerViewModel MapViewer { get; }
|
||||
public ModelViewerViewModel ModelViewer { get; }
|
||||
private OodleCompressor _oodle;
|
||||
|
||||
public ApplicationViewModel()
|
||||
{
|
||||
|
|
@ -89,6 +92,7 @@ namespace FModel.ViewModels
|
|||
AesManager = new AesManagerViewModel(CUE4Parse);
|
||||
MapViewer = new MapViewerViewModel(CUE4Parse);
|
||||
AudioPlayer = new AudioPlayerViewModel();
|
||||
ModelViewer = new ModelViewerViewModel();
|
||||
Status = EStatusKind.Ready;
|
||||
}
|
||||
|
||||
|
|
@ -151,10 +155,10 @@ namespace FModel.ViewModels
|
|||
|
||||
public async Task InitVgmStream()
|
||||
{
|
||||
var vgmZipFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "test.zip");
|
||||
var vgmZipFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "vgmstream-win.zip");
|
||||
if (File.Exists(vgmZipFilePath)) return;
|
||||
|
||||
await ApplicationService.ApiEndpointView.BenbotApi.DownloadFileAsync("https://github.com/vgmstream/vgmstream/releases/latest/download/test.zip", vgmZipFilePath);
|
||||
await ApplicationService.ApiEndpointView.BenbotApi.DownloadFileAsync("https://github.com/vgmstream/vgmstream/releases/latest/download/vgmstream-win.zip", vgmZipFilePath);
|
||||
if (new FileInfo(vgmZipFilePath).Length > 0)
|
||||
{
|
||||
var zip = ZipFile.Read(vgmZipFilePath);
|
||||
|
|
@ -167,5 +171,33 @@ namespace FModel.ViewModels
|
|||
FLogger.AppendText("Could not download VgmStream", Constants.WHITE, true);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InitOodle()
|
||||
{
|
||||
var dataDir = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data"));
|
||||
var oodlePath = Path.Combine(dataDir.FullName, OodleCUE4.OODLE_DLL_NAME);
|
||||
|
||||
if (File.Exists(OodleCUE4.OODLE_DLL_NAME))
|
||||
{
|
||||
File.Move(OodleCUE4.OODLE_DLL_NAME, oodlePath, true);
|
||||
}
|
||||
else if (!File.Exists(oodlePath))
|
||||
{
|
||||
var result = await OodleCUE4.DownloadOodleDll(oodlePath);
|
||||
if (!result) return;
|
||||
}
|
||||
|
||||
if (File.Exists("oo2core_8_win64.dll"))
|
||||
File.Delete("oo2core_8_win64.dll");
|
||||
|
||||
_oodle = new OodleCompressor(oodlePath);
|
||||
|
||||
unsafe
|
||||
{
|
||||
OodleCUE4.DecompressFunc = (bufferPtr, bufferSize, outputPtr, outputSize, a, b, c, d, e, f, g, h, i, threadModule) =>
|
||||
_oodle.Decompress(new IntPtr(bufferPtr), bufferSize, new IntPtr(outputPtr), outputSize,
|
||||
(OodleLZ_FuzzSafe)a, (OodleLZ_CheckCRC)b, (OodleLZ_Verbosity)c, d, e, f, g, h, i, (OodleLZ_Decode_ThreadPhase)threadModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using AdonisUI.Controls;
|
||||
using CUE4Parse.Encryption.Aes;
|
||||
using CUE4Parse.FileProvider;
|
||||
|
|
@ -15,6 +13,7 @@ using CUE4Parse.FileProvider.Vfs;
|
|||
using CUE4Parse.MappingsProvider;
|
||||
using CUE4Parse.UE4.AssetRegistry;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Sound;
|
||||
|
|
@ -22,13 +21,11 @@ using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
|||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.UE4.Localization;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse.UE4.Oodle.Objects;
|
||||
using CUE4Parse.UE4.Shaders;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse.UE4.Wwise;
|
||||
using CUE4Parse_Conversion;
|
||||
using CUE4Parse_Conversion.Materials;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Sounds;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using EpicManifestParser.Objects;
|
||||
|
|
@ -67,6 +64,7 @@ namespace FModel.ViewModels
|
|||
public SearchViewModel SearchVm { get; }
|
||||
public TabControlViewModel TabControl { get; }
|
||||
public int LocalizedResourcesCount { get; set; }
|
||||
public int VirtualPathCount { get; set; }
|
||||
|
||||
public CUE4ParseViewModel(string gameDirectory)
|
||||
{
|
||||
|
|
@ -75,20 +73,31 @@ namespace FModel.ViewModels
|
|||
case Constants._FN_LIVE_TRIGGER:
|
||||
{
|
||||
Game = FGame.FortniteGame;
|
||||
Provider = new StreamedFileProvider("FortniteLive", true, UserSettings.Default.OverridedGame[Game],
|
||||
UserSettings.Default.OverridedUEVersion[Game]);
|
||||
Provider = new StreamedFileProvider("FortniteLive", true,
|
||||
new VersionContainer(
|
||||
UserSettings.Default.OverridedGame[Game],
|
||||
UserSettings.Default.OverridedUEVersion[Game],
|
||||
UserSettings.Default.OverridedCustomVersions[Game],
|
||||
UserSettings.Default.OverridedOptions[Game]));
|
||||
break;
|
||||
}
|
||||
case Constants._VAL_LIVE_TRIGGER:
|
||||
{
|
||||
Game = FGame.ShooterGame;
|
||||
Provider = new StreamedFileProvider("ValorantLive", true, UserSettings.Default.OverridedGame[Game],
|
||||
UserSettings.Default.OverridedUEVersion[Game]);
|
||||
Provider = new StreamedFileProvider("ValorantLive", true,
|
||||
new VersionContainer(
|
||||
UserSettings.Default.OverridedGame[Game],
|
||||
UserSettings.Default.OverridedUEVersion[Game],
|
||||
UserSettings.Default.OverridedCustomVersions[Game],
|
||||
UserSettings.Default.OverridedOptions[Game]));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Game = gameDirectory.SubstringBeforeLast("\\Content").SubstringAfterLast("\\").ToEnum(FGame.Unknown);
|
||||
var versions = new VersionContainer(
|
||||
UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedUEVersion[Game],
|
||||
UserSettings.Default.OverridedCustomVersions[Game], UserSettings.Default.OverridedOptions[Game]);
|
||||
|
||||
if (Game == FGame.StateOfDecay2)
|
||||
Provider = new DefaultFileProvider(new DirectoryInfo(gameDirectory), new List<DirectoryInfo>
|
||||
|
|
@ -96,9 +105,9 @@ namespace FModel.ViewModels
|
|||
new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\Paks"),
|
||||
new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\DisabledPaks")
|
||||
},
|
||||
SearchOption.AllDirectories, true, UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedUEVersion[Game]);
|
||||
SearchOption.AllDirectories, true, versions);
|
||||
else
|
||||
Provider = new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, true, UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedUEVersion[Game]);
|
||||
Provider = new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, true, versions);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -142,7 +151,7 @@ namespace FModel.ViewModels
|
|||
|
||||
var manifest = new Manifest(manifestData, new ManifestOptions
|
||||
{
|
||||
ChunkBaseUri = new Uri("http://epicgames-download1.akamaized.net/Builds/Fortnite/CloudDir/ChunksV3/", UriKind.Absolute),
|
||||
ChunkBaseUri = new Uri("http://epicgames-download1.akamaized.net/Builds/Fortnite/CloudDir/ChunksV4/", UriKind.Absolute),
|
||||
ChunkCacheDirectory = chunksDir
|
||||
});
|
||||
|
||||
|
|
@ -210,7 +219,8 @@ namespace FModel.ViewModels
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested(); // cancel if needed
|
||||
|
||||
var k = key.Key.Length == 66 ? key.Key : Constants.ZERO_64_CHAR;
|
||||
var k = key.Key.Trim();
|
||||
if (k.Length != 66) k = Constants.ZERO_64_CHAR;
|
||||
Provider.SubmitKey(key.Guid, new FAesKey(k));
|
||||
}
|
||||
|
||||
|
|
@ -322,7 +332,7 @@ namespace FModel.ViewModels
|
|||
}
|
||||
else
|
||||
{
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText($"Could not load localized resources in '{UserSettings.Default.AssetLanguage.GetDescription()}', language may not exist", Constants.WHITE, true);
|
||||
}
|
||||
|
||||
|
|
@ -330,6 +340,25 @@ namespace FModel.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
public async Task LoadVirtualPaths()
|
||||
{
|
||||
if (VirtualPathCount > 0) return;
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
VirtualPathCount = Provider.LoadVirtualPaths(cancellationToken);
|
||||
if (VirtualPathCount > 0)
|
||||
{
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"{VirtualPathCount} virtual paths loaded", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText("Could not load virtual paths, plugin manifest may not exist", Constants.WHITE, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder)
|
||||
{
|
||||
foreach (var asset in folder.AssetsList.Assets)
|
||||
|
|
@ -379,22 +408,36 @@ namespace FModel.ViewModels
|
|||
public void Extract(string fullPath, bool addNewTab = false, bool bulkSave = false)
|
||||
{
|
||||
Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", fullPath);
|
||||
|
||||
var directory = fullPath.SubstringBeforeLast('/');
|
||||
var fileName = fullPath.SubstringAfterLast('/');
|
||||
var ext = fullPath.SubstringAfterLast('.').ToLower();
|
||||
|
||||
if (addNewTab && TabControl.CanAddTabs)
|
||||
{
|
||||
TabControl.AddTab(fullPath.SubstringAfterLast('/'), fullPath.SubstringBeforeLast('/'));
|
||||
TabControl.AddTab(fileName, directory);
|
||||
}
|
||||
else
|
||||
{
|
||||
TabControl.SelectedTab.Header = fullPath.SubstringAfterLast('/');
|
||||
TabControl.SelectedTab.Directory = fullPath.SubstringBeforeLast('/');
|
||||
TabControl.SelectedTab.Header = fileName;
|
||||
TabControl.SelectedTab.Directory = directory;
|
||||
}
|
||||
|
||||
var ext = fullPath.SubstringAfterLast('.').ToLower();
|
||||
TabControl.SelectedTab.ResetDocumentText();
|
||||
TabControl.SelectedTab.ScrollTrigger = null;
|
||||
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(ext);
|
||||
switch (ext)
|
||||
{
|
||||
case "uasset":
|
||||
case "umap":
|
||||
{
|
||||
var exports = Provider.LoadObjectExports(fullPath);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), bulkSave);
|
||||
|
||||
if (bulkSave || !exports.Any(CheckExport))
|
||||
TabControl.SelectedTab.Image = null;
|
||||
break;
|
||||
}
|
||||
case "ini":
|
||||
case "txt":
|
||||
case "log":
|
||||
|
|
@ -418,7 +461,6 @@ namespace FModel.ViewModels
|
|||
|
||||
TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), bulkSave);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "locmeta":
|
||||
|
|
@ -429,7 +471,6 @@ namespace FModel.ViewModels
|
|||
var metadata = new FTextLocalizationMetaDataResource(archive);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(metadata, Formatting.Indented), bulkSave);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "locres":
|
||||
|
|
@ -440,10 +481,9 @@ namespace FModel.ViewModels
|
|||
var locres = new FTextLocalizationResource(archive);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(locres, Formatting.Indented), bulkSave);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "bin":
|
||||
case "bin" when fileName.Contains("AssetRegistry"):
|
||||
{
|
||||
TabControl.SelectedTab.Image = null;
|
||||
if (Provider.TryCreateReader(fullPath, out var archive))
|
||||
|
|
@ -451,7 +491,6 @@ namespace FModel.ViewModels
|
|||
var registry = new FAssetRegistryState(archive);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), bulkSave);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "bnk":
|
||||
|
|
@ -467,7 +506,6 @@ namespace FModel.ViewModels
|
|||
SaveAndPlaySound(fullPath.SubstringBeforeWithLast("/") + name, "WEM", data);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "wem":
|
||||
|
|
@ -486,7 +524,6 @@ namespace FModel.ViewModels
|
|||
var header = new FOodleDictionaryArchive(archive).Header;
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), bulkSave);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "png":
|
||||
|
|
@ -496,35 +533,44 @@ namespace FModel.ViewModels
|
|||
if (Provider.TrySaveAsset(fullPath, out var data))
|
||||
{
|
||||
using var stream = new MemoryStream(data) {Position = 0};
|
||||
SetImage(SKImage.FromBitmap(SKBitmap.Decode(stream)));
|
||||
TabControl.SelectedTab.SetImage(SKImage.FromBitmap(SKBitmap.Decode(stream)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "svg":
|
||||
{
|
||||
if (Provider.TrySaveAsset(fullPath, out var data))
|
||||
{
|
||||
using var stream = new MemoryStream(data) { Position = 0 };
|
||||
var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(512, 512));
|
||||
TabControl.SelectedTab.SetImage(SKImage.FromPicture(svg.Load(stream), new SKSizeI(512, 512)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "ufont":
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText($"Export '{fullPath.SubstringAfterLast('/')}' and change its extension if you want it to be an installable font file", Constants.WHITE, true);
|
||||
FLogger.AppendText($"Export '{fileName}' and change its extension if you want it to be an installable font file", Constants.WHITE, true);
|
||||
break;
|
||||
case "otf":
|
||||
case "ttf":
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText($"Export '{fileName}' if you want it to be an installable font file", Constants.WHITE, true);
|
||||
break;
|
||||
case "ushaderbytecode":
|
||||
case "ushadercode":
|
||||
{
|
||||
TabControl.SelectedTab.Image = null;
|
||||
|
||||
if (Provider.TryCreateReader(fullPath, out var archive))
|
||||
{
|
||||
var ar = new FShaderCodeArchive(archive);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), bulkSave);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var exports = Provider.LoadObjectExports(fullPath);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), bulkSave);
|
||||
|
||||
if (bulkSave || !exports.Any(CheckExport))
|
||||
TabControl.SelectedTab.Image = null;
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText($"The file '{fileName}' is of an unknown type. If this is a mistake, we are already working to fix it!", Constants.WHITE, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -550,19 +596,14 @@ namespace FModel.ViewModels
|
|||
ExportData(fullPath);
|
||||
}
|
||||
|
||||
private bool CheckExport(UObject export) // return if this loads an image
|
||||
private bool CheckExport(UObject export) // return true once you wanna stop searching for exports
|
||||
{
|
||||
switch (export)
|
||||
{
|
||||
case UTexture2D texture:
|
||||
{
|
||||
var bNearest = false;
|
||||
if (texture.TryGetValue(out FName trigger, "LODGroup", "Filter") && !trigger.IsNone)
|
||||
bNearest = trigger.Text.EndsWith("TEXTUREGROUP_Pixels2D", StringComparison.OrdinalIgnoreCase) ||
|
||||
trigger.Text.EndsWith("TF_Nearest", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
TabControl.SelectedTab.ImageRender = bNearest ? BitmapScalingMode.NearestNeighbor : BitmapScalingMode.Linear;
|
||||
SetImage(texture.Decode(bNearest));
|
||||
TabControl.SelectedTab.RenderNearestNeighbor = texture.bRenderNearestNeighbor;
|
||||
TabControl.SelectedTab.SetImage(texture.Decode());
|
||||
return true;
|
||||
}
|
||||
case UAkMediaAssetData:
|
||||
|
|
@ -576,26 +617,37 @@ namespace FModel.ViewModels
|
|||
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
|
||||
return false;
|
||||
}
|
||||
case UMaterialInterface when UserSettings.Default.IsAutoSaveMaterials:
|
||||
case UStaticMesh when UserSettings.Default.IsAutoSaveMeshes:
|
||||
case USkeletalMesh when UserSettings.Default.IsAutoSaveMeshes:
|
||||
case UStaticMesh:
|
||||
case USkeletalMesh:
|
||||
case UMaterialInterface when UserSettings.Default.IsAutoSaveMaterials: // don't trigger model viewer if false
|
||||
case UAnimSequence when UserSettings.Default.IsAutoSaveAnimations: // don't trigger model viewer if false
|
||||
{
|
||||
var toSave = new Exporter(export, UserSettings.Default.TextureExportFormat, UserSettings.Default.LodExportFormat);
|
||||
var toSaveDirectory = new DirectoryInfo(Path.Combine(UserSettings.Default.OutputDirectory, "Saves"));
|
||||
if (toSave.TryWriteToDir(toSaveDirectory, out var savedFileName))
|
||||
if (UserSettings.Default.IsAutoSaveMaterials || UserSettings.Default.IsAutoSaveMeshes || UserSettings.Default.IsAutoSaveAnimations)
|
||||
{
|
||||
Log.Information("Successfully saved {FileName}", savedFileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved {savedFileName}", Constants.WHITE, true);
|
||||
var toSave = new Exporter(export, UserSettings.Default.TextureExportFormat, UserSettings.Default.LodExportFormat);
|
||||
var toSaveDirectory = new DirectoryInfo(Path.Combine(UserSettings.Default.OutputDirectory, "Saves"));
|
||||
if (toSave.TryWriteToDir(toSaveDirectory, out var savedFileName))
|
||||
{
|
||||
Log.Information("Successfully saved {FileName}", savedFileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved {savedFileName}", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("{FileName} could not be saved", savedFileName);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not save '{savedFileName}'", Constants.WHITE, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("{FileName} could not be saved", savedFileName);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not save '{savedFileName}'", Constants.WHITE, true);
|
||||
Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
|
||||
modelViewer.Load(export);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
|
@ -603,27 +655,12 @@ namespace FModel.ViewModels
|
|||
if (!package.TryConstructCreator(out var creator)) return false;
|
||||
|
||||
creator.ParseForInfo();
|
||||
SetImage(creator.Draw());
|
||||
TabControl.SelectedTab.SetImage(creator.Draw());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetImage(SKImage img)
|
||||
{
|
||||
using var stream = img.Encode().AsStream();
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
image.StreamSource = stream;
|
||||
image.EndInit();
|
||||
image.Freeze();
|
||||
|
||||
TabControl.SelectedTab.Image = image;
|
||||
if (UserSettings.Default.IsAutoSaveTextures)
|
||||
TabControl.SelectedTab.SaveImage(true);
|
||||
}
|
||||
|
||||
private void SaveAndPlaySound(string fullPath, string ext, byte[] data)
|
||||
{
|
||||
var userDir = Path.Combine(UserSettings.Default.OutputDirectory, "Sounds");
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
||||
namespace FModel.ViewModels.Commands
|
||||
{
|
||||
public class ExportDataCommand : ViewModelCommand<ApplicationViewModel>
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
|
||||
public ExportDataCommand(ApplicationViewModel contextViewModel) : base(contextViewModel)
|
||||
{
|
||||
}
|
||||
|
||||
public override async void Execute(ApplicationViewModel contextViewModel, object parameter)
|
||||
{
|
||||
if (parameter == null) return;
|
||||
|
||||
var assetItems = ((IList) parameter).Cast<AssetItem>().ToArray();
|
||||
if (!assetItems.Any()) return;
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
contextViewModel.CUE4Parse.ExportData(asset.FullPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
||||
namespace FModel.ViewModels.Commands
|
||||
{
|
||||
public class ExtractNewTabCommand : ViewModelCommand<ApplicationViewModel>
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
|
||||
public ExtractNewTabCommand(ApplicationViewModel contextViewModel) : base(contextViewModel)
|
||||
{
|
||||
}
|
||||
|
||||
public override async void Execute(ApplicationViewModel contextViewModel, object parameter)
|
||||
{
|
||||
if (parameter == null) return;
|
||||
|
||||
var assetItems = ((IList) parameter).Cast<AssetItem>().ToArray();
|
||||
if (!assetItems.Any()) return;
|
||||
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.Extract(asset.FullPath, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using AdonisUI.Controls;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using FModel.Views.Resources.Converters;
|
||||
|
||||
namespace FModel.ViewModels.Commands
|
||||
{
|
||||
|
|
@ -30,13 +33,13 @@ namespace FModel.ViewModels.Commands
|
|||
WindowState = contextViewModel.Image.Height > 1000 ? WindowState.Maximized : WindowState.Normal,
|
||||
ImageCtrl = {Source = contextViewModel.Image}
|
||||
};
|
||||
RenderOptions.SetBitmapScalingMode(popout.ImageCtrl, contextViewModel.ImageRender);
|
||||
RenderOptions.SetBitmapScalingMode(popout.ImageCtrl, BoolToRenderModeConverter.Instance.Convert(contextViewModel.RenderNearestNeighbor));
|
||||
popout.Show();
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "Copy":
|
||||
Clipboard.SetImage(contextViewModel.Image);
|
||||
ClipboardExtensions.SetImage(contextViewModel.ImageBuffer, Path.ChangeExtension(contextViewModel.Header, ".png"));
|
||||
break;
|
||||
case "Save":
|
||||
contextViewModel.SaveImage(false);
|
||||
|
|
|
|||
|
|
@ -39,12 +39,20 @@ namespace FModel.ViewModels.Commands
|
|||
public override async void Execute(LoadingModesViewModel contextViewModel, object parameter)
|
||||
{
|
||||
if (_applicationView.CUE4Parse.GameDirectory.HasNoFile) return;
|
||||
if (_applicationView.CUE4Parse.Provider.Files.Count <= 0)
|
||||
{
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText("An encrypted file has been found. In order to decrypt it, please specify a working AES encryption key", Constants.WHITE, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_applicationView.CUE4Parse.Game == FGame.FortniteGame &&
|
||||
_applicationView.CUE4Parse.Provider.MappingsContainer == null)
|
||||
{
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText("Mappings could not get pulled, extracting assets might not work properly. If so, press F12 or please restart.", Constants.WHITE, true);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
var loadingTime = Stopwatch.StartNew();
|
||||
#endif
|
||||
|
|
@ -53,6 +61,7 @@ namespace FModel.ViewModels.Commands
|
|||
MainWindow.YesWeCats.LeftTabControl.SelectedIndex = 1; // folders tab
|
||||
|
||||
await _applicationView.CUE4Parse.LoadLocalizedResources(); // load locres if not already loaded
|
||||
await _applicationView.CUE4Parse.LoadVirtualPaths(); // load virtual paths if not already loaded
|
||||
Helper.CloseWindow<AdonisWindow>("Search View"); // close search window if opened
|
||||
|
||||
await _threadWorkerView.Begin(async cancellationToken =>
|
||||
|
|
@ -89,7 +98,7 @@ namespace FModel.ViewModels.Commands
|
|||
#if DEBUG
|
||||
loadingTime.Stop();
|
||||
FLogger.AppendDebug();
|
||||
FLogger.AppendText($"{_applicationView.CUE4Parse.SearchVm.SearchResults.Count} packages and a lot of localized resources loaded in {loadingTime.Elapsed.TotalSeconds.ToString("F3", CultureInfo.InvariantCulture)} seconds", Constants.WHITE, true);
|
||||
FLogger.AppendText($"{_applicationView.CUE4Parse.SearchVm.SearchResults.Count} packages, {_applicationView.CUE4Parse.LocalizedResourcesCount} localized resources, and {_applicationView.CUE4Parse.VirtualPathCount} virtual paths loaded in {loadingTime.Elapsed.TotalSeconds.ToString("F3", CultureInfo.InvariantCulture)} seconds", Constants.WHITE, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.Views;
|
||||
using FModel.Views.Resources.Controls;
|
||||
|
|
@ -13,7 +15,7 @@ namespace FModel.ViewModels.Commands
|
|||
{
|
||||
}
|
||||
|
||||
public override void Execute(ApplicationViewModel contextViewModel, object parameter)
|
||||
public override async void Execute(ApplicationViewModel contextViewModel, object parameter)
|
||||
{
|
||||
switch (parameter)
|
||||
{
|
||||
|
|
@ -45,7 +47,8 @@ namespace FModel.ViewModels.Commands
|
|||
Process.Start(new ProcessStartInfo {FileName = Constants.DONATE_LINK, UseShellExecute = true});
|
||||
break;
|
||||
case "Help_Changelog":
|
||||
Process.Start(new ProcessStartInfo {FileName = Constants.CHANGELOG_LINK, UseShellExecute = true});
|
||||
UserSettings.Default.ShowChangelog = true;
|
||||
ApplicationService.ApiEndpointView.FModelApi.CheckForUpdates(UserSettings.Default.UpdateMode);
|
||||
break;
|
||||
case "Help_BugsReport":
|
||||
Process.Start(new ProcessStartInfo {FileName = Constants.ISSUE_LINK, UseShellExecute = true});
|
||||
|
|
@ -59,7 +62,41 @@ namespace FModel.ViewModels.Commands
|
|||
case "ToolBox_Open_Output_Directory":
|
||||
Process.Start(new ProcessStartInfo {FileName = UserSettings.Default.OutputDirectory, UseShellExecute = true});
|
||||
break;
|
||||
case "ToolBox_Expand_All":
|
||||
await ApplicationService.ThreadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
foreach (var folder in contextViewModel.CUE4Parse.AssetsFolder.Folders)
|
||||
{
|
||||
LoopFolders(cancellationToken, folder, true);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "ToolBox_Collapse_All":
|
||||
await ApplicationService.ThreadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
foreach (var folder in contextViewModel.CUE4Parse.AssetsFolder.Folders)
|
||||
{
|
||||
LoopFolders(cancellationToken, folder, false);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case TreeItem selectedFolder:
|
||||
selectedFolder.IsSelected = false;
|
||||
selectedFolder.IsSelected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoopFolders(CancellationToken cancellationToken, TreeItem parent, bool isExpanded)
|
||||
{
|
||||
if (parent.IsExpanded != isExpanded)
|
||||
{
|
||||
parent.IsExpanded = isExpanded;
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
foreach (var f in parent.Folders) LoopFolders(cancellationToken, f, isExpanded);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
FModel/ViewModels/Commands/RightClickMenuCommand.cs
Normal file
66
FModel/ViewModels/Commands/RightClickMenuCommand.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
||||
namespace FModel.ViewModels.Commands
|
||||
{
|
||||
public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
|
||||
public RightClickMenuCommand(ApplicationViewModel contextViewModel) : base(contextViewModel)
|
||||
{
|
||||
}
|
||||
|
||||
public override async void Execute(ApplicationViewModel contextViewModel, object parameter)
|
||||
{
|
||||
if (parameter is not object[] parameters || parameters[0] is not string trigger)
|
||||
return;
|
||||
|
||||
var assetItems = ((IList) parameters[1]).Cast<AssetItem>().ToArray();
|
||||
if (!assetItems.Any()) return;
|
||||
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
switch (trigger)
|
||||
{
|
||||
case "Assets_Extract_New_Tab":
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.Extract(asset.FullPath, true);
|
||||
}
|
||||
break;
|
||||
case "Assets_Export_Data":
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.ExportData(asset.FullPath);
|
||||
}
|
||||
break;
|
||||
case "Assets_Save_Properties":
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.Extract(asset.FullPath);
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveProperty(false);
|
||||
}
|
||||
break;
|
||||
case "Assets_Save_Texture":
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.Extract(asset.FullPath);
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveImage(false);
|
||||
}
|
||||
break;
|
||||
// case "Assets_Save_Material":
|
||||
// break;
|
||||
// case "Assets_Save_Mesh":
|
||||
// break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
||||
namespace FModel.ViewModels.Commands
|
||||
{
|
||||
public class SavePropertyCommand : ViewModelCommand<ApplicationViewModel>
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
|
||||
public SavePropertyCommand(ApplicationViewModel contextViewModel) : base(contextViewModel)
|
||||
{
|
||||
}
|
||||
|
||||
public override async void Execute(ApplicationViewModel contextViewModel, object parameter)
|
||||
{
|
||||
if (parameter == null) return;
|
||||
|
||||
var assetItems = ((IList) parameter).Cast<AssetItem>().ToArray();
|
||||
if (!assetItems.Any()) return;
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
contextViewModel.CUE4Parse.Extract(asset.FullPath);
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveProperty(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
||||
namespace FModel.ViewModels.Commands
|
||||
{
|
||||
public class SaveTextureCommand : ViewModelCommand<ApplicationViewModel>
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
|
||||
public SaveTextureCommand(ApplicationViewModel contextViewModel) : base(contextViewModel)
|
||||
{
|
||||
}
|
||||
|
||||
public override async void Execute(ApplicationViewModel contextViewModel, object parameter)
|
||||
{
|
||||
if (parameter == null) return;
|
||||
|
||||
var assetItems = ((IList) parameter).Cast<AssetItem>().ToArray();
|
||||
if (!assetItems.Any()) return;
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
foreach (var asset in assetItems)
|
||||
{
|
||||
contextViewModel.CUE4Parse.Extract(asset.FullPath);
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveImage(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ namespace FModel.ViewModels
|
|||
private DeleteDirectoryCommand _deleteDirectoryCommand;
|
||||
public DeleteDirectoryCommand DeleteDirectoryCommand => _deleteDirectoryCommand ??= new DeleteDirectoryCommand(this);
|
||||
|
||||
public readonly ObservableCollection<Control> _directories;
|
||||
private readonly ObservableCollection<Control> _directories;
|
||||
public ReadOnlyObservableCollection<Control> Directories { get; }
|
||||
|
||||
private readonly FGame _game;
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ namespace FModel.ViewModels
|
|||
|
||||
private DetectedGame GetMojangGame(string gameName, string pakDirectory)
|
||||
{
|
||||
_launcherSettings ??= GetDataLauncherInstalls<LauncherSettings>("\\.minecraft_dungeons\\launcher_settings.json");
|
||||
_launcherSettings ??= GetDataLauncherInstalls<LauncherSettings>("\\.minecraft\\launcher_settings.json");
|
||||
if (_launcherSettings is {ProductLibraryDir: { }})
|
||||
return new DetectedGame {GameName = gameName, GameDirectory = $"{_launcherSettings.ProductLibraryDir}{pakDirectory}"};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using CUE4Parse.UE4.Assets.Exports.Texture;
|
|||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.GameplayTags;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Creator;
|
||||
using FModel.Extensions;
|
||||
|
|
@ -54,6 +53,13 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _brLandmarks, value, "ApolloGameplay_MapLandmarks");
|
||||
}
|
||||
|
||||
private bool _brTagsLocation;
|
||||
public bool BrTagsLocation
|
||||
{
|
||||
get => _brTagsLocation;
|
||||
set => SetProperty(ref _brTagsLocation, value, "ApolloGameplay_TagsLocation");
|
||||
}
|
||||
|
||||
private bool _brPatrolsPath;
|
||||
public bool BrPatrolsPath
|
||||
{
|
||||
|
|
@ -75,18 +81,32 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _brPhonebooths, value, "ApolloGameplay_Phonebooths");
|
||||
}
|
||||
|
||||
private bool _brTagsLocation;
|
||||
public bool BrTagsLocation
|
||||
private bool _brVendingMachines;
|
||||
public bool BrVendingMachines
|
||||
{
|
||||
get => _brTagsLocation;
|
||||
set => SetProperty(ref _brTagsLocation, value, "ApolloGameplay_TagsLocation");
|
||||
get => _brVendingMachines;
|
||||
set => SetProperty(ref _brVendingMachines, value, "ApolloGameplay_VendingMachines");
|
||||
}
|
||||
|
||||
private bool _brAlienArtifacts;
|
||||
public bool BrAlienArtifacts
|
||||
private bool _brFireflies;
|
||||
public bool BrFireflies
|
||||
{
|
||||
get => _brAlienArtifacts;
|
||||
set => SetProperty(ref _brAlienArtifacts, value, "ApolloGameplay_AlienArtifacts");
|
||||
get => _brFireflies;
|
||||
set => SetProperty(ref _brFireflies, value, "ApolloGameplay_Fireflies");
|
||||
}
|
||||
|
||||
private bool _brCorruptionZones;
|
||||
public bool BrCorruptionZones
|
||||
{
|
||||
get => _brCorruptionZones;
|
||||
set => SetProperty(ref _brCorruptionZones, value, "ApolloGameplay_CorruptionZones");
|
||||
}
|
||||
|
||||
private bool _brCubeMovements;
|
||||
public bool BrCubeMovements
|
||||
{
|
||||
get => _brCubeMovements;
|
||||
set => SetProperty(ref _brCubeMovements, value, "ApolloGameplay_CubeMovements");
|
||||
}
|
||||
|
||||
private bool _prLandmarks;
|
||||
|
|
@ -214,8 +234,14 @@ namespace FModel.ViewModels
|
|||
|
||||
foreach (var (key, value) in _bitmaps[MapIndex])
|
||||
{
|
||||
if (!value.IsEnabled || !withMap && key == _FIRST_BITMAP) continue;
|
||||
c.DrawBitmap(value.Layer, new SKRect(0, 0, _widthHeight, _widthHeight));
|
||||
if (!value.IsEnabled || !withMap && key == _FIRST_BITMAP)
|
||||
continue;
|
||||
|
||||
SKPaint p = null;
|
||||
if (key == "ApolloGameplay_CorruptionZones")
|
||||
p = new SKPaint { BlendMode = SKBlendMode.Color };
|
||||
|
||||
c.DrawBitmap(value.Layer, new SKRect(0, 0, _widthHeight, _widthHeight), p);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -243,6 +269,9 @@ namespace FModel.ViewModels
|
|||
case "PapayaGameplay_MapLandmarks":
|
||||
await LoadQuestIndicatorData();
|
||||
break;
|
||||
case "ApolloGameplay_TagsLocation":
|
||||
await LoadTagsLocation();
|
||||
break;
|
||||
case "ApolloGameplay_PatrolsPath":
|
||||
await LoadPatrolsPath();
|
||||
break;
|
||||
|
|
@ -252,11 +281,17 @@ namespace FModel.ViewModels
|
|||
case "ApolloGameplay_Phonebooths":
|
||||
await LoadPhonebooths();
|
||||
break;
|
||||
case "ApolloGameplay_AlienArtifacts":
|
||||
await LoadAlienArtifacts();
|
||||
case "ApolloGameplay_VendingMachines":
|
||||
await LoadBrVendingMachines();
|
||||
break;
|
||||
case "ApolloGameplay_TagsLocation":
|
||||
await LoadTagsLocation();
|
||||
case "ApolloGameplay_Fireflies":
|
||||
await LoadFireflies();
|
||||
break;
|
||||
case "ApolloGameplay_CorruptionZones":
|
||||
await LoadCorruptionZones();
|
||||
break;
|
||||
case "ApolloGameplay_CubeMovements":
|
||||
await LoadCubeMovements();
|
||||
break;
|
||||
case "PapayaGameplay_CannonballGame":
|
||||
await LoadCannonballGame();
|
||||
|
|
@ -274,7 +309,7 @@ namespace FModel.ViewModels
|
|||
await LoadWaypoint(EWaypointType.TimeTrials);
|
||||
break;
|
||||
case "PapayaGameplay_VendingMachines":
|
||||
await LoadVendingMachines();
|
||||
await LoadPrVendingMachines();
|
||||
break;
|
||||
case "PapayaGameplay_MusicBlocks":
|
||||
await LoadMusicBlocks();
|
||||
|
|
@ -367,7 +402,7 @@ namespace FModel.ViewModels
|
|||
!mapMaterial.TryGetValue(out FStructFallback cachedExpressionData, "CachedExpressionData") ||
|
||||
!cachedExpressionData.TryGetValue(out FStructFallback parameters, "Parameters") ||
|
||||
!parameters.TryGetValue(out UTexture2D[] textureValues, "TextureValues")) return;
|
||||
|
||||
|
||||
_bitmaps[0][_FIRST_BITMAP] = new MapLayer{Layer = Utils.GetBitmap(textureValues[0]), IsEnabled = true};
|
||||
_brMiniMapImage = GetImageSource(_bitmaps[0][_FIRST_BITMAP].Layer);
|
||||
});
|
||||
|
|
@ -453,24 +488,20 @@ namespace FModel.ViewModels
|
|||
var patrolsPathBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(patrolsPathBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Apollo_Terrain_NPCLibraryS17_Overlay");
|
||||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Apollo_Terrain_NPCLibrary_Overlay_S18");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("FortAthenaPatrolPath", StringComparison.OrdinalIgnoreCase) ||
|
||||
!uObject.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags") ||
|
||||
!uObject.TryGetValue(out FPackageIndex[] patrolPoints, "PatrolPoints")) continue;
|
||||
if (!export.ExportType.Equals("FortAthenaPatrolPath", StringComparison.OrdinalIgnoreCase) ||
|
||||
!export.TryGetValue(out FPackageIndex[] patrolPoints, "PatrolPoints")) continue;
|
||||
|
||||
var displayName = gameplayTags.GameplayTags[0].Text.SubstringAfterLast(".");
|
||||
if (displayName.Equals("Generic", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!Utils.TryGetPackageIndexExport(patrolPoints[0], out uObject) ||
|
||||
if (!Utils.TryGetPackageIndexExport(patrolPoints[0], out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var path = new SKPath();
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
var displayName = export.Name["FortAthenaPatrolPath_Tandem_S18_".Length..];
|
||||
path.MoveTo(vector.X, vector.Y);
|
||||
|
||||
for (var i = 1; i < patrolPoints.Length; i++)
|
||||
|
|
@ -504,12 +535,11 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_CannonballGame");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_CannonballGame_Target_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("CannonballGame_VehicleSpawner_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (!export.ExportType.Equals("BP_CannonballGame_Target_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("CannonballGame_VehicleSpawner_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var displayName = Utils.GetLocalizedResource("", "D998BEF44F051E0885C6C58565934BEA", "Cannonball");
|
||||
|
|
@ -535,12 +565,11 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_SkydiveGame");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_Waypoint_Papaya_Skydive_Start_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (!export.ExportType.Equals("BP_Waypoint_Papaya_Skydive_Start_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!uObject.TryGetValue(out FText minigameActivityName, "MinigameActivityName") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!export.TryGetValue(out FText minigameActivityName, "MinigameActivityName") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
|
|
@ -566,11 +595,10 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_ShootingTargets");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("PapayaShootingTarget_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (!export.ExportType.Equals("PapayaShootingTarget_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
|
|
@ -614,21 +642,20 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports($"/PapayaGameplay/LevelOverlays/{file}");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_Waypoint_Parent_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (!export.ExportType.Equals("BP_Waypoint_Parent_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject root) ||
|
||||
!root.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
if (path.IsEmpty || uObject.TryGetValue(out bool startsTrial, "StartsTrial") && startsTrial)
|
||||
if (path.IsEmpty || export.TryGetValue(out bool startsTrial, "StartsTrial") && startsTrial)
|
||||
{
|
||||
path.MoveTo(vector.X, vector.Y);
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
else if (uObject.TryGetValue(out bool endsTrial, "EndsTrial") && endsTrial)
|
||||
else if (export.TryGetValue(out bool endsTrial, "EndsTrial") && endsTrial)
|
||||
{
|
||||
path.LineTo(vector.X, vector.Y);
|
||||
c.DrawPath(path, _pathPaint);
|
||||
|
|
@ -641,7 +668,7 @@ namespace FModel.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private async Task LoadVendingMachines()
|
||||
private async Task LoadPrVendingMachines()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
|
|
@ -653,27 +680,26 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_VendingMachines");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("B_Papaya_VendingMachine_Boat_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_BoogieBomb_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Burger_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_CrashPad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_FishingPole_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Grappler_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Jetpack_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PlungerBow_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Quad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Tomato_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (!export.ExportType.Equals("B_Papaya_VendingMachine_Boat_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_BoogieBomb_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Burger_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_CrashPad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_FishingPole_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Grappler_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Jetpack_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PlungerBow_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Quad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Tomato_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject root) ||
|
||||
!root.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var name = uObject.ExportType.SubstringAfter("B_Papaya_VendingMachine_").SubstringBeforeLast("_C");
|
||||
var name = export.ExportType.SubstringAfter("B_Papaya_VendingMachine_").SubstringBeforeLast("_C");
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
if (!set.Add(name)) continue;
|
||||
|
|
@ -698,11 +724,10 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_MusicBlocks");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("MusicBlock_Piano3_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (!export.ExportType.Equals("MusicBlock_Piano3_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
|
|
@ -729,12 +754,11 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Apollo_Terrain_NPCLibrary_Stations_UpgradeBenches");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("B_Athena_Spawner_UpgradeStation_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = uObject.Name["B_Athena_Spawner_".Length..];
|
||||
if (!export.ExportType.Equals("B_Athena_Spawner_UpgradeStation_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = export.Name["B_Athena_Spawner_".Length..];
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
|
|
@ -758,12 +782,11 @@ namespace FModel.ViewModels
|
|||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Apollo_Terrain_NPCLibrary_Stations_Phonebooths");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("B_Athena_Spawner_Payphone_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = uObject.Name["B_Athena_Spawner_".Length..];
|
||||
if (!export.ExportType.Equals("B_Athena_Spawner_Payphone_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = export.Name["B_Athena_Spawner_".Length..];
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
|
|
@ -776,23 +799,23 @@ namespace FModel.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private async Task LoadAlienArtifacts()
|
||||
private async Task LoadBrVendingMachines()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var alienArtifactsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(alienArtifactsBitmap);
|
||||
var vendingMachinesBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(vendingMachinesBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/BattlepassS17/Maps/Apollo_ItemCollect_S17_Overlay");
|
||||
var exports = Utils.LoadExports("FortniteGame/Content/Athena/Apollo/Maps/Special/ItemCollections/Apollo_Item_VendingMachines");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_S17_AlienArtifact_Variant1_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = uObject.Name;
|
||||
if (!export.ExportType.Equals("B_Athena_Spawner_VendingMachine_MendingOnly_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Athena_Spawner_VendingMachine_Random_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = $"{(export.ExportType.Contains("Mending") ? "MM" : "WOM")}_{export.Name["B_Athena_Spawner_VendingMachine_Random".Length..]}";
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
|
|
@ -801,7 +824,35 @@ namespace FModel.ViewModels
|
|||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_AlienArtifacts"] = new MapLayer {Layer = alienArtifactsBitmap, IsEnabled = false};
|
||||
_bitmaps[0]["ApolloGameplay_VendingMachines"] = new MapLayer {Layer = vendingMachinesBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadFireflies()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var firefliesBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(firefliesBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("FortniteGame/Content/Athena/Apollo/Maps/Special/ItemCollections/Apollo_Item_Fireflies");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("BP_BGACSpawner_Fireflies_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = $"FF_{export.Name["BP_BGACSpawnerFireFlies".Length..]}";
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_Fireflies"] = new MapLayer {Layer = firefliesBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -810,11 +861,11 @@ namespace FModel.ViewModels
|
|||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var tagsLocationBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(tagsLocationBitmap);
|
||||
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Quests/QuestTagToLocationDataRows.QuestTagToLocationDataRows", out UDataTable locationData))
|
||||
return;
|
||||
|
||||
var tagsLocationBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(tagsLocationBitmap);
|
||||
|
||||
foreach (var (key, uObject) in locationData.RowMap)
|
||||
{
|
||||
|
|
@ -837,5 +888,124 @@ namespace FModel.ViewModels
|
|||
_bitmaps[0]["ApolloGameplay_TagsLocation"] = new MapLayer {Layer = tagsLocationBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadCorruptionZones()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Apollo/Environments/Landscape/Materials/Corruption/T_InitialCorruptionAreas.T_InitialCorruptionAreas", out UTexture2D corruption))
|
||||
return;
|
||||
|
||||
var overlay = Utils.GetBitmap(corruption);
|
||||
var width = overlay.Width;
|
||||
var height = overlay.Height;
|
||||
var rotatedBitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
|
||||
using var c = new SKCanvas(rotatedBitmap);
|
||||
c.Clear();
|
||||
c.Translate(0, width);
|
||||
c.RotateDegrees(-90);
|
||||
c.DrawRect(0, 0, width, height, new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateCompose(SKShader.CreateSweepGradient(new SKPoint(width / 2f, height / 2f),new [] {
|
||||
SKColor.Parse("#352176"), SKColor.Parse("#fd78fa"), SKColor.Parse("#f0b843"), SKColor.Parse("#e54a21")
|
||||
}, null), SKShader.CreatePerlinNoiseTurbulence(0.05f, 0.05f, 4, 0), SKBlendMode.SrcOver)
|
||||
});
|
||||
c.DrawBitmap(overlay, 0, 0, new SKPaint { BlendMode = SKBlendMode.Darken });
|
||||
rotatedBitmap.ClearToTransparent();
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_CorruptionZones"] = new MapLayer {Layer = rotatedBitmap.Resize(_widthHeight, _widthHeight), IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FortniteGame/Plugins/GameFeatures/CorruptionGameplay/Content/CorruptionGameplay_LevelOverlay.uasset
|
||||
/// too lazy to filters
|
||||
/// </summary>
|
||||
private async Task LoadCubeMovements()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var cubeMovementsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(cubeMovementsBitmap);
|
||||
|
||||
if (!Utils.TryLoadObject("/CorruptionGameplay/Levels/CorruptionGameplay_ApolloTerrain_Overlay.BP_CubeMovementGradient_2", out UObject overlay) ||
|
||||
!overlay.TryGetValue(out FSoftObjectPath[] cubeMovementStaticPaths, "cubeMovementStaticPaths") || cubeMovementStaticPaths.Length < 1)
|
||||
return;
|
||||
|
||||
var oldColor = _pathPaint.Color;
|
||||
_pathPaint.Color = SKColors.Purple;
|
||||
foreach (var cubeMovementStaticPath in cubeMovementStaticPaths)
|
||||
{
|
||||
var objectPath = cubeMovementStaticPath.AssetPathName.Text.SubstringBeforeLast(".");
|
||||
var objectName = cubeMovementStaticPath.SubPathString.SubstringAfterLast(".");
|
||||
if (!Utils.TryLoadObject($"{objectPath}.{objectName}", out UObject staticPath))
|
||||
return;
|
||||
|
||||
DrawCubeMovements(c, staticPath, true);
|
||||
}
|
||||
|
||||
if (Utils.TryLoadObject("/CorruptionGameplay/Levels/CubeMovement/Apollo_CM_Gold_Overlay.CM_Spline_Gold", out UObject goldPath))
|
||||
{
|
||||
_pathPaint.Color = SKColors.Gold;
|
||||
DrawCubeMovements(c, goldPath, false);
|
||||
}
|
||||
|
||||
_pathPaint.Color = oldColor;
|
||||
_bitmaps[0]["ApolloGameplay_CubeMovements"] = new MapLayer {Layer = cubeMovementsBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawCubeMovements(SKCanvas c, UObject staticPath, bool fixLocation)
|
||||
{
|
||||
if (!staticPath.TryGetValue(out FStructFallback[] pathTravelers, "PathTravelers") || pathTravelers.Length < 1 ||
|
||||
!pathTravelers[0].TryGetValue(out FPackageIndex[] generatedSplinesArray, "GeneratedSplinesArray") || generatedSplinesArray.Length < 1)
|
||||
return;
|
||||
|
||||
UObject uObject;
|
||||
var parentRelativeLocation = new FVector();
|
||||
if (fixLocation)
|
||||
{
|
||||
if (!pathTravelers[0].TryGetValue(out FPackageIndex pathTraveler, "PathTraveler") ||
|
||||
!Utils.TryGetPackageIndexExport(pathTraveler, out uObject) ||
|
||||
!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
!uObject.TryGetValue(out parentRelativeLocation, "RelativeLocation"))
|
||||
return;
|
||||
}
|
||||
|
||||
var bDone = false;
|
||||
var path = new SKPath();
|
||||
foreach (var generatedSpline in generatedSplinesArray)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(generatedSpline, out uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FStructFallback splineCurves, "SplineCurves") ||
|
||||
!splineCurves.TryGetValue(out FStructFallback positions, "Position") ||
|
||||
!positions.TryGetValue(out FStructFallback[] positionPoints, "Points")) continue;
|
||||
|
||||
foreach (var positionPoint in positionPoints)
|
||||
{
|
||||
if (!positionPoint.TryGetValue(out FVector point, "OutVal")) continue;
|
||||
|
||||
var vector = GetMapPosition(parentRelativeLocation + relativeLocation + point, _brRadius);
|
||||
if (!bDone)
|
||||
{
|
||||
path.MoveTo(vector.X, vector.Y);
|
||||
bDone = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
path.LineTo(vector.X, vector.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.DrawPath(path, _pathPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
216
FModel/ViewModels/ModelViewerViewModel.cs
Normal file
216
FModel/ViewModels/ModelViewerViewModel.cs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Meshes.PSK;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using HelixToolkit.SharpDX.Core;
|
||||
using HelixToolkit.Wpf.SharpDX;
|
||||
using SharpDX;
|
||||
|
||||
namespace FModel.ViewModels
|
||||
{
|
||||
public class ModelViewerViewModel : ViewModel
|
||||
{
|
||||
private EffectsManager _effectManager;
|
||||
public EffectsManager EffectManager
|
||||
{
|
||||
get => _effectManager;
|
||||
set => SetProperty(ref _effectManager, value);
|
||||
}
|
||||
|
||||
private Camera _cam;
|
||||
public Camera Cam
|
||||
{
|
||||
get => _cam;
|
||||
set => SetProperty(ref _cam, value);
|
||||
}
|
||||
|
||||
private Geometry3D _xAxis;
|
||||
public Geometry3D XAxis
|
||||
{
|
||||
get => _xAxis;
|
||||
set => SetProperty(ref _xAxis, value);
|
||||
}
|
||||
|
||||
private Geometry3D _yAxis;
|
||||
public Geometry3D YAxis
|
||||
{
|
||||
get => _yAxis;
|
||||
set => SetProperty(ref _yAxis, value);
|
||||
}
|
||||
|
||||
private Geometry3D _zAxis;
|
||||
public Geometry3D ZAxis
|
||||
{
|
||||
get => _zAxis;
|
||||
set => SetProperty(ref _zAxis, value);
|
||||
}
|
||||
|
||||
private bool _showWireframe;
|
||||
public bool ShowWireframe
|
||||
{
|
||||
get => _showWireframe;
|
||||
set => SetProperty(ref _showWireframe, value);
|
||||
}
|
||||
|
||||
private Geometry3D _mesh;
|
||||
public Geometry3D Mesh
|
||||
{
|
||||
get => _mesh;
|
||||
set => SetProperty(ref _mesh, value);
|
||||
}
|
||||
|
||||
private Material _meshMat;
|
||||
public Material MeshMat
|
||||
{
|
||||
get => _meshMat;
|
||||
set => SetProperty(ref _meshMat, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<Geometry3D> Lods { get; }
|
||||
|
||||
public ModelViewerViewModel()
|
||||
{
|
||||
Lods = new ObservableCollection<Geometry3D>();
|
||||
EffectManager = new DefaultEffectsManager();
|
||||
Cam = new PerspectiveCamera
|
||||
{
|
||||
NearPlaneDistance = 0.1,
|
||||
FarPlaneDistance = 10000000,
|
||||
FieldOfView = 80
|
||||
};
|
||||
}
|
||||
|
||||
public void NextLod() => Mesh = Lods.Next(Mesh);
|
||||
public void PreviousLod() => Mesh = Lods.Previous(Mesh);
|
||||
|
||||
public void LoadExport(UObject export)
|
||||
{
|
||||
Lods.Clear();
|
||||
Mesh = null;
|
||||
MeshMat = PhongMaterials.Bisque;
|
||||
|
||||
switch (export)
|
||||
{
|
||||
case UStaticMesh st:
|
||||
LoadStaticMesh(st);
|
||||
break;
|
||||
case USkeletalMesh sk:
|
||||
LoadSkeletalMesh(sk);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadStaticMesh(UStaticMesh mesh)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox.Min, convertedMesh.BoundingBox.Max);
|
||||
|
||||
var pushedMaterial = false;
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
|
||||
PushLod(lod.Verts, lod.Indices.Value);
|
||||
if (!pushedMaterial)
|
||||
{
|
||||
PushMaterial(lod.Sections.Value);
|
||||
pushedMaterial = true;
|
||||
}
|
||||
}
|
||||
Mesh = Lods.First();
|
||||
}
|
||||
|
||||
private void LoadSkeletalMesh(USkeletalMesh mesh)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox.Min, convertedMesh.BoundingBox.Max);
|
||||
|
||||
var pushedMaterial = false;
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
|
||||
PushLod(lod.Verts, lod.Indices.Value);
|
||||
if (!pushedMaterial)
|
||||
{
|
||||
PushMaterial(lod.Sections.Value);
|
||||
pushedMaterial = true;
|
||||
}
|
||||
}
|
||||
Mesh = Lods.First();
|
||||
}
|
||||
|
||||
private void PushLod(CMeshVertex[] verts, FRawStaticIndexBuffer indices)
|
||||
{
|
||||
var builder = new MeshBuilder {TextureCoordinates = new Vector2Collection()};
|
||||
for (var i = 0; i < verts.Length; i++)
|
||||
{
|
||||
builder.AddNode(
|
||||
new Vector3(verts[i].Position.X, -verts[i].Position.Y, verts[i].Position.Z),
|
||||
new Vector3(verts[i].Normal.X, verts[i].Normal.Y, verts[i].Normal.Z),
|
||||
new Vector2(verts[i].UV.U, verts[i].UV.V));
|
||||
}
|
||||
|
||||
for (var i = 0; i < indices.Length; i++)
|
||||
{
|
||||
builder.TriangleIndices.Add(indices[i]);
|
||||
}
|
||||
|
||||
Lods.Add(builder.ToMesh());
|
||||
}
|
||||
|
||||
private void PushMaterial(CMeshSection[] sections)
|
||||
{
|
||||
for (var j = 0; j < sections.Length; j++)
|
||||
{
|
||||
if (sections[j].Material == null || !sections[j].Material.TryLoad<UMaterialInterface>(out var unrealMaterial))
|
||||
continue;
|
||||
|
||||
var parameters = new CMaterialParams();
|
||||
unrealMaterial.GetParams(parameters);
|
||||
if (parameters.Diffuse is not UTexture2D diffuse) continue;
|
||||
MeshMat = new DiffuseMaterial {DiffuseMap = new TextureModel(diffuse.Decode()?.Encode().AsStream())};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupCameraAndAxis(FVector min, FVector max)
|
||||
{
|
||||
var minOfMin = min.Min();
|
||||
var maxOfMax = max.Max();
|
||||
Cam.UpDirection = new System.Windows.Media.Media3D.Vector3D(0, 0, 1);
|
||||
Cam.Position = new System.Windows.Media.Media3D.Point3D(maxOfMax, maxOfMax, (minOfMin + maxOfMax) / 1.25);
|
||||
Cam.LookDirection = new System.Windows.Media.Media3D.Vector3D(-Cam.Position.X, -Cam.Position.Y, 0);
|
||||
|
||||
var lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(0, 0, 0), new Vector3(max.X, 0, 0));
|
||||
XAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(0, 0, 0), new Vector3(0, max.Y, 0));
|
||||
YAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(0, 0, 0), new Vector3(0, 0, max.Z));
|
||||
ZAxis = lineBuilder.ToLineGeometry3D();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,17 +2,24 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using CUE4Parse.UE4.Objects.Core.Serialization;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
|
||||
namespace FModel.ViewModels
|
||||
{
|
||||
public class SettingsViewModel : ViewModel
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView;
|
||||
private readonly DiscordHandler _discordHandler = DiscordService.DiscordHandler;
|
||||
|
||||
private EUpdateMode _selectedUpdateMode;
|
||||
|
|
@ -22,6 +29,17 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _selectedUpdateMode, value);
|
||||
}
|
||||
|
||||
private string _selectedPreset;
|
||||
public string SelectedPreset
|
||||
{
|
||||
get => _selectedPreset;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedPreset, value);
|
||||
RaisePropertyChanged("EnableElements");
|
||||
}
|
||||
}
|
||||
|
||||
private EGame _selectedUeGame;
|
||||
public EGame SelectedUeGame
|
||||
{
|
||||
|
|
@ -35,6 +53,20 @@ namespace FModel.ViewModels
|
|||
get => _selectedUeVersion;
|
||||
set => SetProperty(ref _selectedUeVersion, value);
|
||||
}
|
||||
|
||||
private List<FCustomVersion> _selectedCustomVersions;
|
||||
public List<FCustomVersion> SelectedCustomVersions
|
||||
{
|
||||
get => _selectedCustomVersions;
|
||||
set => SetProperty(ref _selectedCustomVersions, value);
|
||||
}
|
||||
|
||||
private Dictionary<string, bool> _selectedOptions;
|
||||
public Dictionary<string, bool> SelectedOptions
|
||||
{
|
||||
get => _selectedOptions;
|
||||
set => SetProperty(ref _selectedOptions, value);
|
||||
}
|
||||
|
||||
private ELanguage _selectedAssetLanguage;
|
||||
public ELanguage SelectedAssetLanguage
|
||||
|
|
@ -107,6 +139,7 @@ namespace FModel.ViewModels
|
|||
}
|
||||
|
||||
public ReadOnlyObservableCollection<EUpdateMode> UpdateModes { get; private set; }
|
||||
public ObservableCollection<string> Presets { get; private set; }
|
||||
public ReadOnlyObservableCollection<EGame> UeGames { get; private set; }
|
||||
public ReadOnlyObservableCollection<UE4Version> UeVersions { get; private set; }
|
||||
public ReadOnlyObservableCollection<ELanguage> AssetLanguages { get; private set; }
|
||||
|
|
@ -120,12 +153,18 @@ namespace FModel.ViewModels
|
|||
public ReadOnlyObservableCollection<ELodFormat> LodExportFormats { get; private set; }
|
||||
public ReadOnlyObservableCollection<ETextureFormat> TextureExportFormats { get; private set; }
|
||||
|
||||
public bool EnableElements => SelectedPreset == Constants._NO_PRESET_TRIGGER;
|
||||
|
||||
private readonly FGame _game;
|
||||
private Game _gamePreset;
|
||||
private string _outputSnapshot;
|
||||
private string _gameSnapshot;
|
||||
private EUpdateMode _updateModeSnapshot;
|
||||
private string _presetSnapshot;
|
||||
private EGame _ueGameSnapshot;
|
||||
private UE4Version _ueVersionSnapshot;
|
||||
private List<FCustomVersion> _customVersionsSnapshot;
|
||||
private Dictionary<string, bool> _optionsSnapshot;
|
||||
private ELanguage _assetLanguageSnapshot;
|
||||
private EEnabledDisabled _directoryStructureSnapshot;
|
||||
private ECompressedAudio _compressedAudioSnapshot;
|
||||
|
|
@ -145,8 +184,11 @@ namespace FModel.ViewModels
|
|||
_outputSnapshot = UserSettings.Default.OutputDirectory;
|
||||
_gameSnapshot = UserSettings.Default.GameDirectory;
|
||||
_updateModeSnapshot = UserSettings.Default.UpdateMode;
|
||||
_presetSnapshot = UserSettings.Default.Presets[_game];
|
||||
_ueGameSnapshot = UserSettings.Default.OverridedGame[_game];
|
||||
_ueVersionSnapshot = UserSettings.Default.OverridedUEVersion[_game];
|
||||
_customVersionsSnapshot = UserSettings.Default.OverridedCustomVersions[_game];
|
||||
_optionsSnapshot = UserSettings.Default.OverridedOptions[_game];
|
||||
_assetLanguageSnapshot = UserSettings.Default.AssetLanguage;
|
||||
_directoryStructureSnapshot = UserSettings.Default.KeepDirectoryStructure;
|
||||
_compressedAudioSnapshot = UserSettings.Default.CompressedAudioMode;
|
||||
|
|
@ -157,8 +199,11 @@ namespace FModel.ViewModels
|
|||
_textureExportFormatSnapshot = UserSettings.Default.TextureExportFormat;
|
||||
|
||||
SelectedUpdateMode = _updateModeSnapshot;
|
||||
SelectedPreset = _presetSnapshot;
|
||||
SelectedUeGame = _ueGameSnapshot;
|
||||
SelectedUeVersion = _ueVersionSnapshot;
|
||||
SelectedCustomVersions = _customVersionsSnapshot;
|
||||
SelectedOptions = _optionsSnapshot;
|
||||
SelectedAssetLanguage = _assetLanguageSnapshot;
|
||||
SelectedDirectoryStructure = _directoryStructureSnapshot;
|
||||
SelectedCompressedAudio = _compressedAudioSnapshot;
|
||||
|
|
@ -171,6 +216,7 @@ namespace FModel.ViewModels
|
|||
SelectedDiscordRpc = UserSettings.Default.DiscordRpc;
|
||||
|
||||
UpdateModes = new ReadOnlyObservableCollection<EUpdateMode>(new ObservableCollection<EUpdateMode>(EnumerateUpdateModes()));
|
||||
Presets = new ObservableCollection<string>(EnumeratePresets());
|
||||
UeGames = new ReadOnlyObservableCollection<EGame>(new ObservableCollection<EGame>(EnumerateUeGames()));
|
||||
UeVersions = new ReadOnlyObservableCollection<UE4Version>(new ObservableCollection<UE4Version>(EnumerateUeVersions()));
|
||||
AssetLanguages = new ReadOnlyObservableCollection<ELanguage>(new ObservableCollection<ELanguage>(EnumerateAssetLanguages()));
|
||||
|
|
@ -185,11 +231,54 @@ namespace FModel.ViewModels
|
|||
TextureExportFormats = new ReadOnlyObservableCollection<ETextureFormat>(new ObservableCollection<ETextureFormat>(EnumerateTextureExportFormat()));
|
||||
}
|
||||
|
||||
public async Task InitPresets(string gameName)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(gameName)) return;
|
||||
_gamePreset = _apiEndpointView.FModelApi.GetGames(cancellationToken, gameName);
|
||||
});
|
||||
|
||||
if (_gamePreset?.Versions == null) return;
|
||||
foreach (var version in _gamePreset.Versions.Keys)
|
||||
{
|
||||
Presets.Add(version);
|
||||
}
|
||||
}
|
||||
|
||||
public void SwitchPreset(string key)
|
||||
{
|
||||
if (_gamePreset?.Versions == null || !_gamePreset.Versions.TryGetValue(key, out var version)) return;
|
||||
SelectedUeGame = version.GameEnum.ToEnum(EGame.GAME_UE4_LATEST);
|
||||
SelectedUeVersion = (UE4Version)version.UeVer;
|
||||
|
||||
SelectedCustomVersions = new List<FCustomVersion>();
|
||||
foreach (var (guid, v) in version.CustomVersions)
|
||||
{
|
||||
SelectedCustomVersions.Add(new FCustomVersion {Key = new FGuid(guid), Version = v});
|
||||
}
|
||||
|
||||
SelectedOptions = new Dictionary<string, bool>();
|
||||
foreach (var (k, v) in version.Options)
|
||||
{
|
||||
SelectedOptions[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetPreset()
|
||||
{
|
||||
SelectedUeGame = _ueGameSnapshot;
|
||||
SelectedUeVersion = _ueVersionSnapshot;
|
||||
SelectedCustomVersions = _customVersionsSnapshot;
|
||||
SelectedOptions = _optionsSnapshot;
|
||||
}
|
||||
|
||||
public SettingsOut Save()
|
||||
{
|
||||
var ret = SettingsOut.Nothing;
|
||||
|
||||
if (_ueGameSnapshot != SelectedUeGame || _ueVersionSnapshot != SelectedUeVersion || // comboboxes
|
||||
_customVersionsSnapshot != SelectedCustomVersions || _optionsSnapshot != SelectedOptions ||
|
||||
_outputSnapshot != UserSettings.Default.OutputDirectory || // textbox
|
||||
_gameSnapshot != UserSettings.Default.GameDirectory) // textbox
|
||||
ret = SettingsOut.Restart;
|
||||
|
|
@ -198,8 +287,11 @@ namespace FModel.ViewModels
|
|||
ret = SettingsOut.ReloadLocres;
|
||||
|
||||
UserSettings.Default.UpdateMode = SelectedUpdateMode;
|
||||
UserSettings.Default.Presets[_game] = SelectedPreset;
|
||||
UserSettings.Default.OverridedGame[_game] = SelectedUeGame;
|
||||
UserSettings.Default.OverridedUEVersion[_game] = SelectedUeVersion;
|
||||
UserSettings.Default.OverridedCustomVersions[_game] = SelectedCustomVersions;
|
||||
UserSettings.Default.OverridedOptions[_game] = SelectedOptions;
|
||||
UserSettings.Default.AssetLanguage = SelectedAssetLanguage;
|
||||
UserSettings.Default.KeepDirectoryStructure = SelectedDirectoryStructure;
|
||||
UserSettings.Default.CompressedAudioMode = SelectedCompressedAudio;
|
||||
|
|
@ -218,6 +310,10 @@ namespace FModel.ViewModels
|
|||
}
|
||||
|
||||
private IEnumerable<EUpdateMode> EnumerateUpdateModes() => Enum.GetValues(SelectedUpdateMode.GetType()).Cast<EUpdateMode>();
|
||||
private IEnumerable<string> EnumeratePresets()
|
||||
{
|
||||
yield return Constants._NO_PRESET_TRIGGER;
|
||||
}
|
||||
private IEnumerable<EGame> EnumerateUeGames() => Enum.GetValues(SelectedUeGame.GetType()).Cast<EGame>();
|
||||
private IEnumerable<UE4Version> EnumerateUeVersions() => Enum.GetValues(SelectedUeVersion.GetType()).Cast<UE4Version>();
|
||||
private IEnumerable<ELanguage> EnumerateAssetLanguages() => Enum.GetValues(SelectedAssetLanguage.GetType()).Cast<ELanguage>();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,18 @@
|
|||
using FModel.Framework;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.Commands;
|
||||
using FModel.Views.Resources.Controls;
|
||||
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
|
||||
using Microsoft.Win32;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
|
|
@ -7,13 +20,6 @@ using System.Linq;
|
|||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using FModel.Extensions;
|
||||
using FModel.Settings;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
using Microsoft.Win32;
|
||||
using Serilog;
|
||||
|
||||
namespace FModel.ViewModels
|
||||
{
|
||||
|
|
@ -113,6 +119,8 @@ namespace FModel.ViewModels
|
|||
SetProperty(ref _highlighter, value);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ImageBuffer { get; private set; }
|
||||
|
||||
private BitmapImage _image;
|
||||
public BitmapImage Image
|
||||
|
|
@ -125,12 +133,23 @@ namespace FModel.ViewModels
|
|||
RaisePropertyChanged("HasImage");
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapScalingMode _imageRender = BitmapScalingMode.Linear;
|
||||
public BitmapScalingMode ImageRender
|
||||
|
||||
private bool _noAlpha;
|
||||
public bool NoAlpha
|
||||
{
|
||||
get => _imageRender;
|
||||
set => SetProperty(ref _imageRender, value);
|
||||
get => _noAlpha;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _noAlpha, value);
|
||||
ResetImage();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _renderNearestNeighbor;
|
||||
public bool RenderNearestNeighbor
|
||||
{
|
||||
get => _renderNearestNeighbor;
|
||||
set => SetProperty(ref _renderNearestNeighbor, value);
|
||||
}
|
||||
|
||||
public bool HasImage => Image != null;
|
||||
|
|
@ -185,8 +204,8 @@ namespace FModel.ViewModels
|
|||
InitialDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Saves"),
|
||||
Filter = "JSON Files (*.json)|*.json|INI Files (*.ini)|*.ini|XML Files (*.xml)|*.xml|All Files (*.*)|*.*"
|
||||
};
|
||||
|
||||
if (!(bool) saveFileDialog.ShowDialog()) return;
|
||||
var result = saveFileDialog.ShowDialog();
|
||||
if (!result.HasValue || !result.Value) return;
|
||||
directory = saveFileDialog.FileName;
|
||||
}
|
||||
else
|
||||
|
|
@ -198,12 +217,32 @@ namespace FModel.ViewModels
|
|||
SaveCheck(directory, fileName);
|
||||
}
|
||||
|
||||
private SKImage _img;
|
||||
public void ResetImage() => SetImage(_img);
|
||||
public void SetImage(SKImage img)
|
||||
{
|
||||
_img = img;
|
||||
|
||||
using var data = _img.Encode(NoAlpha ? SKEncodedImageFormat.Jpeg : SKEncodedImageFormat.Png, 100);
|
||||
using var stream = new MemoryStream(ImageBuffer = data.ToArray(), false);
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
image.StreamSource = stream;
|
||||
image.EndInit();
|
||||
image.Freeze();
|
||||
|
||||
Image = image;
|
||||
if (UserSettings.Default.IsAutoSaveTextures)
|
||||
SaveImage(true);
|
||||
}
|
||||
|
||||
public void SaveImage(bool autoSave)
|
||||
{
|
||||
if (!HasImage) return;
|
||||
var fileName = Path.ChangeExtension(Header, ".png");
|
||||
var directory = Path.Combine(UserSettings.Default.OutputDirectory, "Textures",
|
||||
UserSettings.Default.KeepDirectoryStructure == EEnabledDisabled.Enabled ? Directory : "", fileName).Replace('\\', '/');
|
||||
UserSettings.Default.KeepDirectoryStructure == EEnabledDisabled.Enabled ? Directory : "", fileName!).Replace('\\', '/');
|
||||
|
||||
if (!autoSave)
|
||||
{
|
||||
|
|
@ -214,8 +253,8 @@ namespace FModel.ViewModels
|
|||
InitialDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Textures"),
|
||||
Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*"
|
||||
};
|
||||
|
||||
if (!(bool) saveFileDialog.ShowDialog()) return;
|
||||
var result = saveFileDialog.ShowDialog();
|
||||
if (!result.HasValue || !result.Value) return;
|
||||
directory = saveFileDialog.FileName;
|
||||
}
|
||||
else
|
||||
|
|
@ -223,14 +262,15 @@ namespace FModel.ViewModels
|
|||
System.IO.Directory.CreateDirectory(directory.SubstringBeforeLast('/'));
|
||||
}
|
||||
|
||||
using var fileStream = new FileStream(directory, FileMode.Create);
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create(Image));
|
||||
encoder.Save(fileStream);
|
||||
using (var fs = new FileStream(directory, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||
{
|
||||
fs.Write(ImageBuffer, 0, ImageBuffer.Length);
|
||||
}
|
||||
|
||||
SaveCheck(directory, fileName);
|
||||
}
|
||||
|
||||
private void SaveCheck(string path, string fileName)
|
||||
private static void SaveCheck(string path, string fileName)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
|
|
@ -325,7 +365,7 @@ namespace FModel.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private IEnumerable<TabItem> EnumerateTabs()
|
||||
private static IEnumerable<TabItem> EnumerateTabs()
|
||||
{
|
||||
yield return new TabItem("New Tab", string.Empty);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using FModel.Services;
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FModel.Extensions;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ namespace FModel.ViewModels
|
|||
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText(e.Message, Constants.WHITE, true);
|
||||
FLogger.AppendText(" " + e.StackTrace.SubstringBefore('\n').Trim(), Constants.WHITE, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
<TextBlock Text="				" FontSize="25" FontWeight="700" Height="2" Foreground="Transparent" HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
<TextBlock FontSize="12" Foreground="#727272" TextWrapping="Wrap" Margin="0 0 0 30"
|
||||
Text="Maiky ♥, HYPEX ♥, VenomLeaks ♥, JayKey ♥, Fevers ♥, Netu ♥, TheGameVlog ♥, Quentin ♥, Laggy ♥, s0ll ♥, RazTracker, Mikey, kyle, Yanteh, Shiina, SexyNutella, Alexander, Jinx, Tector, imatrix, LamZykoss, Frenzy Leaks, LlamaLeaks, XTigerHyperX, FunGames, WeLoveFortnite." />
|
||||
Text="TheGameVlog ♥, Quentin ♥, Maiky ♥, HYPEX ♥, Evan, VenomLeaks, JayKey, Fevers, Netu, Laggy, s0ll, RazTracker, Mikey, kyle, Yanteh, Shiina, SexyNutella, Alexander, Jinx, Tector, imatrix, LamZykoss, Frenzy Leaks, LlamaLeaks, XTigerHyperX, FunGames, WeLoveFortnite." />
|
||||
|
||||
<StackPanel HorizontalAlignment="Center">
|
||||
<TextBlock Text="Powered by" FontSize="15" FontWeight="700" Foreground="#9DA3DD" FontStretch="Expanded" />
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" Closing="OnClosing" PreviewKeyDown="OnPreviewKeyDown"
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.50'}"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.55'}">
|
||||
|
|
@ -28,14 +29,8 @@
|
|||
</Grid.ColumnDefinitions>
|
||||
|
||||
<GroupBox Grid.Column="0" Padding="{adonisUi:Space 0}" Background="Transparent">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<DockPanel Margin="10">
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
|
|
@ -73,8 +68,25 @@
|
|||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Bytes Per Second" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="2" Text="{Binding AudioPlayer.PlayedFile.BytesPerSecond}" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
<Separator Grid.Row="1" Tag="PLAYLIST" Style="{StaticResource CustomSeparator}" />
|
||||
<ListBox Grid.Row="2" Style="{StaticResource AudioFilesListBox}" PreviewMouseDoubleClick="OnAudioFileMouseDoubleClick">
|
||||
<Separator DockPanel.Dock="Top" Tag="PLAYLIST" Style="{StaticResource CustomSeparator}" />
|
||||
<Grid DockPanel.Dock="Top" Height="24" Margin="0 0 0 5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0" ZIndex="1" HorizontalAlignment="Left" Margin="5 2 0 0">
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SearchIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
<TextBox Grid.Column="0" Grid.ColumnSpan="2" AcceptsTab="False" AcceptsReturn="False"
|
||||
Padding="25 0 0 0" HorizontalAlignment="Stretch" TextChanged="OnFilterTextChanged"
|
||||
adonisExtensions:WatermarkExtension.Watermark="Search by name..." />
|
||||
</Grid>
|
||||
<ListBox DockPanel.Dock="Top" Style="{StaticResource AudioFilesListBox}" PreviewMouseDoubleClick="OnAudioFileMouseDoubleClick">
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Play" Command="{Binding AudioPlayer.AudioCommand}" CommandParameter="ForcePlayPause">
|
||||
|
|
@ -116,7 +128,7 @@
|
|||
</ContextMenu>
|
||||
</ListBox.ContextMenu>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</GroupBox>
|
||||
|
||||
<Grid Grid.Column="1">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
|
@ -49,6 +51,9 @@ namespace FModel.Views
|
|||
|
||||
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is TextBox)
|
||||
return;
|
||||
|
||||
if (UserSettings.Default.AddAudio.IsTriggered(e.Key))
|
||||
{
|
||||
var openFileDialog = new OpenFileDialog
|
||||
|
|
@ -77,5 +82,17 @@ namespace FModel.Views
|
|||
{
|
||||
_applicationView.AudioPlayer.PlayPauseOnForce();
|
||||
}
|
||||
|
||||
private void OnFilterTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (sender is not TextBox textBox)
|
||||
return;
|
||||
|
||||
var filters = textBox.Text.Trim().Split(' ');
|
||||
_applicationView.AudioPlayer.AudioFilesView.Filter = o =>
|
||||
{
|
||||
return o is AudioFile audio && filters.All(x => audio.FileName.Contains(x, StringComparison.OrdinalIgnoreCase));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,16 @@
|
|||
using System;
|
||||
using AdonisUI.Controls;
|
||||
|
||||
using FModel.Extensions;
|
||||
using FModel.Settings;
|
||||
using FModel.Views.Resources.Controls;
|
||||
|
||||
using Microsoft.Win32;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
|
@ -9,18 +21,14 @@ using System.Windows.Controls;
|
|||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Imaging;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Extensions;
|
||||
using FModel.Settings;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using Microsoft.Win32;
|
||||
using Serilog;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Views
|
||||
{
|
||||
public partial class ImageMerger
|
||||
{
|
||||
private const string FILENAME = "Preview.png";
|
||||
private byte[] _imagebuffer;
|
||||
|
||||
public ImageMerger()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
|
@ -34,7 +42,7 @@ namespace FModel.Views
|
|||
private async void Click_DrawPreview(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (ImagePreview.Source != null)
|
||||
await DrawPreview().ConfigureAwait(false);
|
||||
await DrawPreview().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task DrawPreview()
|
||||
|
|
@ -60,9 +68,9 @@ namespace FModel.Views
|
|||
|
||||
if (item.ContentStringFormat.EndsWith(".tif"))
|
||||
{
|
||||
var mem = new MemoryStream();
|
||||
await stream.CopyToAsync(mem);
|
||||
System.Drawing.Image.FromStream(mem).Save(ms, ImageFormat.Png);
|
||||
await using var tmp = new MemoryStream();
|
||||
await stream.CopyToAsync(tmp);
|
||||
System.Drawing.Image.FromStream(tmp).Save(ms, ImageFormat.Png);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -97,20 +105,19 @@ namespace FModel.Views
|
|||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
using var ret = new SKBitmap(maxWidth - margin, maxHeight - margin, SKColorType.Rgba8888, SKAlphaType.Unpremul);
|
||||
using var c = new SKCanvas(ret);
|
||||
using var bmp = new SKBitmap(maxWidth - margin, maxHeight - margin, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var canvas = new SKCanvas(bmp);
|
||||
|
||||
for (var i = 0; i < images.Length; i++)
|
||||
{
|
||||
using (images[i])
|
||||
{
|
||||
c.DrawBitmap(images[i], positions[i], new SKPaint {FilterQuality = SKFilterQuality.High, IsAntialias = true});
|
||||
canvas.DrawBitmap(images[i], positions[i], new SKPaint {FilterQuality = SKFilterQuality.High, IsAntialias = true});
|
||||
}
|
||||
}
|
||||
|
||||
var image = SKImage.FromBitmap(ret);
|
||||
using var encoded = image.Encode();
|
||||
using var stream = encoded.AsStream();
|
||||
using var data = bmp.Encode(SKEncodedImageFormat.Png, 100);
|
||||
using var stream = new MemoryStream(_imagebuffer = data.ToArray());
|
||||
var photo = new BitmapImage();
|
||||
photo.BeginInit();
|
||||
photo.CacheOption = BitmapCacheOption.OnLoad;
|
||||
|
|
@ -141,8 +148,9 @@ namespace FModel.Views
|
|||
Multiselect = true,
|
||||
Filter = "Image Files (*.png,*.bmp,*.jpg,*.jpeg,*.jfif,*.jpe,*.tiff,*.tif)|*.png;*.bmp;*.jpg;*.jpeg;*.jfif;*.jpe;*.tiff;*.tif|All Files (*.*)|*.*"
|
||||
};
|
||||
var result = fileBrowser.ShowDialog();
|
||||
if (!result.HasValue || !result.Value) return;
|
||||
|
||||
if (!(bool) fileBrowser.ShowDialog()) return;
|
||||
foreach (var file in fileBrowser.FileNames)
|
||||
{
|
||||
ImagesListBox.Items.Add(new ListBoxItem
|
||||
|
|
@ -254,25 +262,41 @@ namespace FModel.Views
|
|||
var saveFileDialog = new SaveFileDialog
|
||||
{
|
||||
Title = "Save Image",
|
||||
FileName = "Preview.png",
|
||||
FileName = FILENAME,
|
||||
InitialDirectory = UserSettings.Default.OutputDirectory,
|
||||
Filter = "Png Files (*.png)|*.png|All Files (*.*)|*.*"
|
||||
};
|
||||
var result = saveFileDialog.ShowDialog();
|
||||
if (!result.HasValue || !result.Value) return;
|
||||
|
||||
if (!(bool) saveFileDialog.ShowDialog()) return;
|
||||
using var fileStream = new FileStream(saveFileDialog.FileName, FileMode.Create);
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create((BitmapSource) ImagePreview.Source));
|
||||
encoder.Save(fileStream);
|
||||
using (var fs = new FileStream(saveFileDialog.FileName, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||
{
|
||||
fs.Write(_imagebuffer, 0, _imagebuffer.Length);
|
||||
}
|
||||
|
||||
if (!File.Exists(saveFileDialog.FileName)) return;
|
||||
Log.Information("{FileName} successfully saved", saveFileDialog.FileName.SubstringAfterLast('\\'));
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved '{saveFileDialog.FileName.SubstringAfterLast('\\')}'", Constants.WHITE, true);
|
||||
SaveCheck(saveFileDialog.FileName, Path.GetFileName(saveFileDialog.FileName));
|
||||
});
|
||||
}
|
||||
|
||||
private void OnCopyImage(object sender, RoutedEventArgs e) => Clipboard.SetImage((BitmapSource) ImagePreview.Source);
|
||||
|
||||
private static void SaveCheck(string path, string fileName)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Log.Information("{FileName} successfully saved", fileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved '{fileName}'", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("{FileName} could not be saved", fileName);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not save '{fileName}'", Constants.WHITE, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCopyImage(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ClipboardExtensions.SetImage(_imagebuffer, FILENAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,15 +27,22 @@
|
|||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Landmarks" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrLandmarks}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Tags Location" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrTagsLocation}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Patrols Path" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrPatrolsPath}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Upgrade Benches" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrUpgradeBenches}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Phonebooths" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrPhonebooths}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Alien Artifacts" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrAlienArtifacts}"
|
||||
<CheckBox Content="Weapon-o-matic/Mending Machines" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrVendingMachines}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Tags Location" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrTagsLocation}"
|
||||
<CheckBox Content="Fireflies" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrFireflies}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Corruption Zones" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrCorruptionZones}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}"
|
||||
ToolTip="Saving the image with Corruption Zones enabled will smooth these ugly sharp edges"/>
|
||||
<CheckBox Content="Cube Movements" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrCubeMovements}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
|
|
|||
47
FModel/Views/ModelViewer.xaml
Normal file
47
FModel/Views/ModelViewer.xaml
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<adonisControls:AdonisWindow x:Class="FModel.Views.ModelViewer"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:helix="http://helix-toolkit.org/wpf/SharpDX"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanResize" IconVisibility="Collapsed" Background="#262630"
|
||||
PreviewKeyDown="OnWindowKeyDown"
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.60'}"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.50'}">
|
||||
<adonisControls:AdonisWindow.Style>
|
||||
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
|
||||
<Setter Property="Title" Value="Model Viewer" />
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<helix:Viewport3DX EffectsManager="{Binding ModelViewer.EffectManager}" Camera="{Binding ModelViewer.Cam}"
|
||||
IsChangeFieldOfViewEnabled="False" IsMoveEnabled="False" UseDefaultGestures="False"
|
||||
ShowViewCube="False" ShowCameraTarget="False" ModelUpDirection="0,0,1"
|
||||
EnableSSAO="True" MSAA="Maximum" FXAALevel="Ultra" SSAOQuality="High"
|
||||
BackgroundColor="#262630">
|
||||
<helix:Viewport3DX.InputBindings>
|
||||
<MouseBinding Command="helix:ViewportCommands.Rotate" Gesture="LeftClick" />
|
||||
<MouseBinding Command="helix:ViewportCommands.Zoom" Gesture="RightClick" />
|
||||
<MouseBinding Command="helix:ViewportCommands.Pan" Gesture="MiddleClick" />
|
||||
</helix:Viewport3DX.InputBindings>
|
||||
|
||||
<helix:DirectionalLight3D Direction="0, 0, -1" Color="White" />
|
||||
<helix:DirectionalLight3D Direction="0, -1, 0" Color="White"/>
|
||||
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.XAxis}" Color="#FC3854" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.YAxis}" Color="#85CB22" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.ZAxis}" Color="#388EED" />
|
||||
|
||||
<helix:MeshGeometryModel3D Geometry="{Binding ModelViewer.Mesh}"
|
||||
Material="{Binding ModelViewer.MeshMat}"
|
||||
RenderWireframe="{Binding ModelViewer.ShowWireframe}" />
|
||||
</helix:Viewport3DX>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
31
FModel/Views/ModelViewer.xaml.cs
Normal file
31
FModel/Views/ModelViewer.xaml.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System.Windows.Input;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
|
||||
namespace FModel.Views
|
||||
{
|
||||
public partial class ModelViewer
|
||||
{
|
||||
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
|
||||
|
||||
public ModelViewer()
|
||||
{
|
||||
DataContext = _applicationView;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void Load(UObject export) => _applicationView.ModelViewer.LoadExport(export);
|
||||
|
||||
private void OnWindowKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (UserSettings.Default.DirLeftTab.IsTriggered(e.Key))
|
||||
_applicationView.ModelViewer.PreviousLod();
|
||||
else if (UserSettings.Default.DirRightTab.IsTriggered(e.Key))
|
||||
_applicationView.ModelViewer.NextLod();
|
||||
else if (e.Key == Key.W)
|
||||
_applicationView.ModelViewer.ShowWireframe = !_applicationView.ModelViewer.ShowWireframe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,10 +47,18 @@ namespace FModel.Views.Resources.Controls
|
|||
if (_foldingManager.AllFoldings == null)
|
||||
return;
|
||||
|
||||
var dowhat = -1;
|
||||
var foldunfold = false;
|
||||
foreach (var folding in _foldingManager.AllFoldings)
|
||||
{
|
||||
if (folding.Tag is not CustomNewFolding realFolding) continue;
|
||||
if (realFolding.Level == level) folding.IsFolded = !folding.IsFolded;
|
||||
if (folding.Tag is not CustomNewFolding realFolding || realFolding.Level != level) continue;
|
||||
|
||||
if (dowhat < 0) // determine if we fold or unfold based on the first one
|
||||
{
|
||||
dowhat = 1;
|
||||
foldunfold = !folding.IsFolded;
|
||||
}
|
||||
folding.IsFolded = foldunfold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,16 @@ namespace FModel.Views.Resources.Controls
|
|||
case Key.Escape:
|
||||
((TabItem) DataContext).HasSearchOpen = false;
|
||||
break;
|
||||
case Key.Enter when ((TabItem) DataContext).HasSearchOpen:
|
||||
case Key.Enter when !Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) && ((TabItem) DataContext).HasSearchOpen:
|
||||
FindNext();
|
||||
break;
|
||||
case Key.Enter when Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) && ((TabItem) DataContext).HasSearchOpen:
|
||||
var dc = (TabItem)DataContext;
|
||||
var old = dc.SearchUp;
|
||||
dc.SearchUp = true;
|
||||
FindNext();
|
||||
dc.SearchUp = old;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
49
FModel/Views/Resources/Controls/DictionaryEditor.xaml
Normal file
49
FModel/Views/Resources/Controls/DictionaryEditor.xaml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<adonisControls:AdonisWindow x:Class="FModel.Views.Resources.Controls.DictionaryEditor"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" ResizeMode="NoResize" SizeToContent="Width"
|
||||
MinWidth="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.30'}"
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.20'}">
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="../Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<avalonEdit:TextEditor x:Name="MyAvalonEditor" Grid.Row="0" Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}"
|
||||
FontFamily="Consolas" FontSize="8pt" IsReadOnly="True" ShowLineNumbers="True" Foreground="#DAE5F2" />
|
||||
|
||||
<Border Grid.Row="1"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer1BackgroundBrush}}"
|
||||
adonisExtensions:LayerExtension.IncreaseLayer="True">
|
||||
<Grid Margin="30, 12, 6, 12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock x:Name="HeBrokeIt" Grid.Column="0" Text="IF YOU DON'T KNOW WHAT THIS DOES, DON'T TOUCH IT, EVER!"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="11" Margin="0 0 10 0" FontWeight="DemiBold"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.Layer1InteractionForegroundBrush}}" />
|
||||
|
||||
<Button Grid.Column="1" MinWidth="78" Margin="0 0 12 0" IsDefault="True" IsCancel="False"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="OK" Click="OnClick" />
|
||||
<Button Grid.Column="2" MinWidth="78" Margin="0 0 12 0" IsDefault="False" IsCancel="True"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Cancel" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
86
FModel/Views/Resources/Controls/DictionaryEditor.xaml.cs
Normal file
86
FModel/Views/Resources/Controls/DictionaryEditor.xaml.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using CUE4Parse.UE4.Objects.Core.Serialization;
|
||||
using FModel.Extensions;
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FModel.Views.Resources.Controls
|
||||
{
|
||||
public partial class DictionaryEditor
|
||||
{
|
||||
private readonly bool _enableElements;
|
||||
private readonly List<FCustomVersion> _defaultCustomVersions;
|
||||
private readonly Dictionary<string, bool> _defaultOptions;
|
||||
|
||||
public List<FCustomVersion> CustomVersions { get; private set; }
|
||||
public Dictionary<string, bool> Options { get; private set; }
|
||||
|
||||
public DictionaryEditor(string title, bool enableElements)
|
||||
{
|
||||
_enableElements = enableElements;
|
||||
_defaultCustomVersions = new List<FCustomVersion> {new() { Key = new FGuid(), Version = 0 }};
|
||||
_defaultOptions = new Dictionary<string, bool> {{ "key1", true }, { "key2", false }};
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Title = title;
|
||||
MyAvalonEditor.IsReadOnly = !_enableElements;
|
||||
MyAvalonEditor.SyntaxHighlighting = AvalonExtensions.HighlighterSelector("");
|
||||
}
|
||||
|
||||
public DictionaryEditor(List<FCustomVersion> customVersions, string title, bool enableElements) : this(title, enableElements)
|
||||
{
|
||||
MyAvalonEditor.Document = new TextDocument
|
||||
{
|
||||
Text = JsonConvert.SerializeObject(customVersions ?? _defaultCustomVersions, Formatting.Indented)
|
||||
};
|
||||
}
|
||||
|
||||
public DictionaryEditor(Dictionary<string, bool> options, string title, bool enableElements) : this(title, enableElements)
|
||||
{
|
||||
MyAvalonEditor.Document = new TextDocument
|
||||
{
|
||||
Text = JsonConvert.SerializeObject(options ?? _defaultOptions, Formatting.Indented)
|
||||
};
|
||||
}
|
||||
|
||||
private void OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_enableElements)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch (Title)
|
||||
{
|
||||
case "Versioning Configuration (Custom Versions)":
|
||||
CustomVersions = JsonConvert.DeserializeObject<List<FCustomVersion>>(MyAvalonEditor.Document.Text);
|
||||
DialogResult = !CustomVersions.SequenceEqual(_defaultCustomVersions);
|
||||
Close();
|
||||
break;
|
||||
case "Versioning Configuration (Options)":
|
||||
Options = JsonConvert.DeserializeObject<Dictionary<string, bool>>(MyAvalonEditor.Document.Text);
|
||||
DialogResult = !Options.SequenceEqual(_defaultOptions);
|
||||
Close();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
HeBrokeIt.Text = "GG YOU BROKE THE FORMAT, FIX THE JSON OR CANCEL THE CHANGES!";
|
||||
HeBrokeIt.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(Constants.RED));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" PreviewKeyDown="OnPreviewKeyDown"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.40'}">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace FModel.Views.Resources.Converters
|
||||
{
|
||||
public class BoolToRenderModeConverter : IValueConverter
|
||||
{
|
||||
public static readonly BoolToRenderModeConverter Instance = new();
|
||||
|
||||
public BitmapScalingMode Convert(bool value) => (BitmapScalingMode) Convert(value, null, null, null);
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
true => BitmapScalingMode.NearestNeighbor,
|
||||
_ => BitmapScalingMode.Linear
|
||||
};
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
<Geometry x:Key="DiscordIcon">M22,24L16.75,19L17.38,21H4.5A2.5,2.5 0 0,1 2,18.5V3.5A2.5,2.5 0 0,1 4.5,1H19.5A2.5,2.5 0 0,1 22,3.5V24M12,6.8C9.32,6.8 7.44,7.95 7.44,7.95C8.47,7.03 10.27,6.5 10.27,6.5L10.1,6.33C8.41,6.36 6.88,7.53 6.88,7.53C5.16,11.12 5.27,14.22 5.27,14.22C6.67,16.03 8.75,15.9 8.75,15.9L9.46,15C8.21,14.73 7.42,13.62 7.42,13.62C7.42,13.62 9.3,14.9 12,14.9C14.7,14.9 16.58,13.62 16.58,13.62C16.58,13.62 15.79,14.73 14.54,15L15.25,15.9C15.25,15.9 17.33,16.03 18.73,14.22C18.73,14.22 18.84,11.12 17.12,7.53C17.12,7.53 15.59,6.36 13.9,6.33L13.73,6.5C13.73,6.5 15.53,7.03 16.56,7.95C16.56,7.95 14.68,6.8 12,6.8M9.93,10.59C10.58,10.59 11.11,11.16 11.1,11.86C11.1,12.55 10.58,13.13 9.93,13.13C9.29,13.13 8.77,12.55 8.77,11.86C8.77,11.16 9.28,10.59 9.93,10.59M14.1,10.59C14.75,10.59 15.27,11.16 15.27,11.86C15.27,12.55 14.75,13.13 14.1,13.13C13.46,13.13 12.94,12.55 12.94,11.86C12.94,11.16 13.45,10.59 14.1,10.59Z</Geometry>
|
||||
<Geometry x:Key="BugIcon">M14,12H10V10H14M14,16H10V14H14M20,8H17.19C16.74,7.22 16.12,6.55 15.37,6.04L17,4.41L15.59,3L13.42,5.17C12.96,5.06 12.5,5 12,5C11.5,5 11.04,5.06 10.59,5.17L8.41,3L7,4.41L8.62,6.04C7.88,6.55 7.26,7.22 6.81,8H4V10H6.09C6.04,10.33 6,10.66 6,11V12H4V14H6V15C6,15.34 6.04,15.67 6.09,16H4V18H6.81C7.85,19.79 9.78,21 12,21C14.22,21 16.15,19.79 17.19,18H20V16H17.91C17.96,15.67 18,15.34 18,15V14H20V12H18V11C18,10.66 17.96,10.33 17.91,10H20V8Z</Geometry>
|
||||
<Geometry x:Key="GiftIcon">M22 10.92L19.26 9.33C21.9 7.08 19.25 2.88 16.08 4.31L15.21 4.68L15.1 3.72C15 2.64 14.44 1.87 13.7 1.42C12.06 .467 9.56 1.12 9.16 3.5L6.41 1.92C5.45 1.36 4.23 1.69 3.68 2.65L2.68 4.38C2.4 4.86 2.57 5.47 3.05 5.75L10.84 10.25L12.34 7.65L14.07 8.65L12.57 11.25L20.36 15.75C20.84 16 21.46 15.86 21.73 15.38L22.73 13.65C23.28 12.69 22.96 11.47 22 10.92M12.37 5C11.5 5.25 10.8 4.32 11.24 3.55C11.5 3.07 12.13 2.91 12.61 3.18C13.38 3.63 13.23 4.79 12.37 5M17.56 8C16.7 8.25 16 7.32 16.44 6.55C16.71 6.07 17.33 5.91 17.8 6.18C18.57 6.63 18.42 7.79 17.56 8M20.87 16.88C21.28 16.88 21.67 16.74 22 16.5V20C22 21.11 21.11 22 20 22H4C2.9 22 2 21.11 2 20V11H10.15L11 11.5V20H13V12.65L19.87 16.61C20.17 16.79 20.5 16.88 20.87 16.88Z</Geometry>
|
||||
<Geometry x:Key="GitHubIcon">M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z</Geometry>
|
||||
<Geometry x:Key="NoteIcon">M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM8 19h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1s.45 1 1 1zm0-6h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1s.45 1 1 1zM7 6c0 .55.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1z</Geometry>
|
||||
<Geometry x:Key="InfoIcon">M11.5,2C6.81,2,3,5.81,3,10.5S6.81,19,11.5,19H12v3c4.86-2.34,8-7,8-11.5C20,5.81,16.19,2,11.5,2z M11.48,16 c-0.59,0-1.05-0.47-1.05-1.05c0-0.59,0.47-1.04,1.05-1.04c0.59,0,1.04,0.45,1.04,1.04C12.52,15.53,12.08,16,11.48,16z M13.99,9.83 c-0.63,0.93-1.23,1.21-1.56,1.81c-0.08,0.14-0.13,0.26-0.16,0.49c-0.05,0.39-0.36,0.68-0.75,0.68h-0.03 c-0.44,0-0.79-0.38-0.75-0.82c0.03-0.28,0.09-0.57,0.25-0.84c0.41-0.73,1.18-1.16,1.63-1.8c0.48-0.68,0.21-1.94-1.14-1.94 c-0.61,0-1.01,0.32-1.26,0.7c-0.19,0.29-0.57,0.39-0.89,0.25l0,0c-0.42-0.18-0.6-0.7-0.34-1.07C9.5,6.55,10.35,6,11.47,6 c1.23,0,2.08,0.56,2.51,1.26C14.34,7.87,14.56,8.99,13.99,9.83z</Geometry>
|
||||
<Geometry x:Key="TrashIcon">M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M9.17,12.59c-0.39-0.39-0.39-1.02,0-1.41c0.39-0.39,1.02-0.39,1.41,0 L12,12.59l1.41-1.41c0.39-0.39,1.02-0.39,1.41,0s0.39,1.02,0,1.41L13.41,14l1.41,1.41c0.39,0.39,0.39,1.02,0,1.41 s-1.02,0.39-1.41,0L12,15.41l-1.41,1.41c-0.39,0.39-1.02,0.39-1.41,0c-0.39-0.39-0.39-1.02,0-1.41L10.59,14L9.17,12.59z M18,4h-2.5 l-0.71-0.71C14.61,3.11,14.35,3,14.09,3H9.91c-0.26,0-0.52,0.11-0.7,0.29L8.5,4H6C5.45,4,5,4.45,5,5s0.45,1,1,1h12 c0.55,0,1-0.45,1-1S18.55,4,18,4z</Geometry>
|
||||
<Geometry x:Key="HomeIcon">M18,4v16H6V4H18 M18,2H6C4.9,2,4,2.9,4,4v16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C20,2.9,19.1,2,18,2L18,2z M7,19h10v-6H7 V19z M10,10h4v1h3V5H7v6h3V10z</Geometry>
|
||||
|
|
@ -60,6 +60,11 @@
|
|||
<Geometry x:Key="ImageMergerIcon">M10 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-4 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z</Geometry>
|
||||
<Geometry x:Key="GliderIcon">M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,17,12,17z M17.95,14c-0.52,0-0.94,0.4-0.99,0.92 c-0.2,2.03-1.05,2.68-1.48,3.02C14.68,18.54,14,19,12,19s-2.68-0.46-3.48-1.06c-0.43-0.34-1.28-0.99-1.48-3.02 C6.99,14.4,6.57,14,6.05,14c-0.59,0-1.06,0.51-1,1.09c0.22,2.08,1.07,3.47,2.24,4.41c0.5,0.4,1.1,0.7,1.7,0.9L9,24h6v-3.6 c0.6-0.2,1.2-0.5,1.7-0.9c1.17-0.94,2.03-2.32,2.24-4.41C19.01,14.51,18.53,14,17.95,14z M12,0C5.92,0,1,1.9,1,4.25v3.49 C1,8.55,1.88,9,2.56,8.57C2.7,8.48,2.84,8.39,3,8.31L5,13h2l1.5-6.28C9.6,6.58,10.78,6.5,12,6.5s2.4,0.08,3.5,0.22L17,13h2l2-4.69 c0.16,0.09,0.3,0.17,0.44,0.26C22.12,9,23,8.55,23,7.74V4.25C23,1.9,18.08,0,12,0z M5.88,11.24L4.37,7.69 c0.75-0.28,1.6-0.52,2.53-0.71L5.88,11.24z M18.12,11.24L17.1,6.98c0.93,0.19,1.78,0.43,2.53,0.71L18.12,11.24z</Geometry>
|
||||
<Geometry x:Key="AnchorIcon">M13,9V7.82C14.16,7.4,15,6.3,15,5c0-1.65-1.35-3-3-3S9,3.35,9,5c0,1.3,0.84,2.4,2,2.82V9H9c-0.55,0-1,0.45-1,1v0 c0,0.55,0.45,1,1,1h2v8.92c-2.22-0.33-4.59-1.68-5.55-3.37l1.14-1.14c0.22-0.22,0.19-0.57-0.05-0.75L3.8,12.6 C3.47,12.35,3,12.59,3,13v2c0,3.88,4.92,7,9,7s9-3.12,9-7v-2c0-0.41-0.47-0.65-0.8-0.4l-2.74,2.05c-0.24,0.18-0.27,0.54-0.05,0.75 l1.14,1.14c-0.96,1.69-3.33,3.04-5.55,3.37V11h2c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H13z M12,4c0.55,0,1,0.45,1,1s-0.45,1-1,1 s-1-0.45-1-1S11.45,4,12,4z</Geometry>
|
||||
<Geometry x:Key="FoldIcon">M8.12 19.3c.39.39 1.02.39 1.41 0L12 16.83l2.47 2.47c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41l-3.17-3.17c-.39-.39-1.02-.39-1.41 0l-3.17 3.17c-.4.38-.4 1.02-.01 1.41zm7.76-14.6c-.39-.39-1.02-.39-1.41 0L12 7.17 9.53 4.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.03 0 1.42l3.17 3.17c.39.39 1.02.39 1.41 0l3.17-3.17c.4-.39.4-1.03.01-1.42z</Geometry>
|
||||
<Geometry x:Key="UnfoldIcon">M12 5.83l2.46 2.46c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L12.7 3.7c-.39-.39-1.02-.39-1.41 0L8.12 6.88c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 5.83zm0 12.34l-2.46-2.46c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41l3.17 3.18c.39.39 1.02.39 1.41 0l3.17-3.17c.39-.39.39-1.02 0-1.41-.39-.39-1.02-.39-1.41 0L12 18.17z</Geometry>
|
||||
<Geometry x:Key="LocateMeIcon">M11.71,17.99C8.53,17.84,6,15.22,6,12c0-3.31,2.69-6,6-6c3.22,0,5.84,2.53,5.99,5.71l-2.1-0.63C15.48,9.31,13.89,8,12,8 c-2.21,0-4,1.79-4,4c0,1.89,1.31,3.48,3.08,3.89L11.71,17.99z M22,12c0,0.3-0.01,0.6-0.04,0.9l-1.97-0.59C20,12.21,20,12.1,20,12 c0-4.42-3.58-8-8-8s-8,3.58-8,8s3.58,8,8,8c0.1,0,0.21,0,0.31-0.01l0.59,1.97C12.6,21.99,12.3,22,12,22C6.48,22,2,17.52,2,12 C2,6.48,6.48,2,12,2S22,6.48,22,12z M18.23,16.26l2.27-0.76c0.46-0.15,0.45-0.81-0.01-0.95l-7.6-2.28 c-0.38-0.11-0.74,0.24-0.62,0.62l2.28,7.6c0.14,0.47,0.8,0.48,0.95,0.01l0.76-2.27l3.91,3.91c0.2,0.2,0.51,0.2,0.71,0l1.27-1.27 c0.2-0.2,0.2-0.51,0-0.71L18.23,16.26z</Geometry>
|
||||
<Geometry x:Key="MeshIcon">M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm8 7h-5v12c0 .55-.45 1-1 1s-1-.45-1-1v-5h-2v5c0 .55-.45 1-1 1s-1-.45-1-1V9H4c-.55 0-1-.45-1-1s.45-1 1-1h16c.55 0 1 .45 1 1s-.45 1-1 1z</Geometry>
|
||||
<Geometry x:Key="MaterialIcon">M11 9h2v2h-2V9zm-2 2h2v2H9v-2zm4 0h2v2h-2v-2zm2-2h2v2h-2V9zM7 9h2v2H7V9zm12-6H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 18H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm2-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v5z</Geometry>
|
||||
|
||||
<Style x:Key="TabItemFillSpace" TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
|
||||
<Setter Property="Width">
|
||||
|
|
@ -648,12 +653,13 @@
|
|||
<controls:AvalonEditor x:Name="DynamicArea" Grid.Column="0" Grid.ColumnSpan="3" DataContext="{Binding SelectedItem, ElementName=TabControlName}" />
|
||||
<Image Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform"
|
||||
Visibility="{Binding SelectedItem.HasImage, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Source="{Binding SelectedItem.Image, ElementName=TabControlName}" RenderOptions.BitmapScalingMode="{Binding SelectedItem.ImageRender, ElementName=TabControlName}">
|
||||
Source="{Binding SelectedItem.Image, ElementName=TabControlName}" RenderOptions.BitmapScalingMode="{Binding SelectedItem.RenderNearestNeighbor, ElementName=TabControlName, Converter={x:Static converters:BoolToRenderModeConverter.Instance}}">
|
||||
<Image.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding SelectedItem.ImageCommand, ElementName=TabControlName}" CommandParameter="Open"/>
|
||||
</Image.InputBindings>
|
||||
<Image.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Disable Alpha Channel" IsCheckable="True" IsChecked="{Binding DataContext.NoAlpha}" />
|
||||
<MenuItem Header="Open Image" Command="{Binding DataContext.ImageCommand}" CommandParameter="Open">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" IconVisibility="Collapsed" SizeToContent="Height"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" IconVisibility="Collapsed" SizeToContent="Height" Loaded="OnLoaded"
|
||||
MinHeight="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.10'}"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.35'}">
|
||||
<adonisControls:AdonisWindow.Style>
|
||||
|
|
@ -36,6 +36,8 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
|
@ -85,8 +87,47 @@
|
|||
|
||||
<Separator Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="5" Style="{StaticResource CustomSeparator}" Tag="ADVANCED"></Separator>
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="UE4 Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeGames}" SelectedItem="{Binding SettingsView.SelectedUeGame, Mode=TwoWay}"
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Presets" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Use a fine tuned preset for the game you're trying to load and its version" />
|
||||
<ComboBox Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.Presets}" SelectedItem="{Binding SettingsView.SelectedPreset, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" SelectionChanged="OnSelectionChanged" Margin="0 0 0 5">
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="7" Grid.Column="0" Text="UE4 Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeGames}" SelectedItem="{Binding SettingsView.SelectedUeGame, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" IsEnabled="{Binding SettingsView.EnableElements}"
|
||||
Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="8" Grid.Column="0" Text="UE4 Object Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 object version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeVersions}" SelectedItem="{Binding SettingsView.SelectedUeVersion, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" IsEnabled="{Binding SettingsView.EnableElements}"
|
||||
Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Versioning Configuration *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<Grid Grid.Row="9" Grid.Column="2" Grid.ColumnSpan="3" Margin="0 0 0 5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="0" Content="Custom Versions" Click="OpenCustomVersions" />
|
||||
<Button Grid.Column="2" Content="Options" Click="OpenOptions" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Keep Directory Structure" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Auto Export & Save assets inside their game directory" />
|
||||
<ComboBox Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DirectoryStructures}" SelectedItem="{Binding SettingsView.SelectedDirectoryStructure, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
|
@ -95,8 +136,8 @@
|
|||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="7" Grid.Column="0" Text="UE4 Sub-Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 sub-version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeVersions}" SelectedItem="{Binding SettingsView.SelectedUeVersion, Mode=TwoWay}"
|
||||
<TextBlock Grid.Row="11" Grid.Column="0" Text="Compressed Audio" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="What to do when encountering a compressed audio file" />
|
||||
<ComboBox Grid.Row="11" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.CompressedAudios}" SelectedItem="{Binding SettingsView.SelectedCompressedAudio, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
|
@ -105,27 +146,7 @@
|
|||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="8" Grid.Column="0" Text="Keep Directory Structure" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Auto Export & Save assets inside their game directory" />
|
||||
<ComboBox Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DirectoryStructures}" SelectedItem="{Binding SettingsView.SelectedDirectoryStructure, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Compressed Audio" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="What to do when encountering a compressed audio file" />
|
||||
<ComboBox Grid.Row="9" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.CompressedAudios}" SelectedItem="{Binding SettingsView.SelectedCompressedAudio, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="AES Reload at Launch" VerticalAlignment="Center" Margin="0 0 0 5"
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="AES Reload at Launch" VerticalAlignment="Center" Margin="0 0 0 5"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
|
|
@ -138,7 +159,7 @@
|
|||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<ComboBox Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.AesReloads}" SelectedItem="{Binding SettingsView.SelectedAesReload, Mode=TwoWay}"
|
||||
<ComboBox Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.AesReloads}" SelectedItem="{Binding SettingsView.SelectedAesReload, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
|
||||
|
|
@ -249,7 +270,7 @@
|
|||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Mesh Export Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Mesh Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding SettingsView.MeshExportFormats}" SelectedItem="{Binding SettingsView.SelectedMeshExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
|
|
@ -259,7 +280,7 @@
|
|||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Level Of Detail Export Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Level Of Detail Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding SettingsView.LodExportFormats}" SelectedItem="{Binding SettingsView.SelectedLodExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
|
|
@ -269,7 +290,7 @@
|
|||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Texture Export Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Texture Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="2" ItemsSource="{Binding SettingsView.TextureExportFormats}" SelectedItem="{Binding SettingsView.SelectedTextureExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
|
|
@ -302,6 +323,7 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
|
@ -348,23 +370,26 @@
|
|||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Auto Save Meshes *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="13" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
<TextBlock Grid.Row="13" Grid.Column="0" Text="Auto Save Animations *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="14" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="14" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
||||
<Separator Grid.Row="14" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
<Separator Grid.Row="15" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="15" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="15" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AddAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="16" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PlayPauseAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="17" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
HotKey="{Binding AddAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="17" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="17" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PreviousAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
HotKey="{Binding PlayPauseAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="18" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PreviousAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="19" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="19" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding NextAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Windows.Controls;
|
|||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
|
||||
namespace FModel.Views
|
||||
|
|
@ -27,6 +28,11 @@ namespace FModel.Views
|
|||
}
|
||||
}
|
||||
|
||||
private async void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await _applicationView.SettingsView.InitPresets(_applicationView.CUE4Parse.Provider.GameName);
|
||||
}
|
||||
|
||||
private async void OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var whatShouldIDo = _applicationView.SettingsView.Save();
|
||||
|
|
@ -82,5 +88,38 @@ namespace FModel.Views
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not ComboBox {SelectedItem: string s}) return;
|
||||
if (s == Constants._NO_PRESET_TRIGGER) _applicationView.SettingsView.ResetPreset();
|
||||
else _applicationView.SettingsView.SwitchPreset(s);
|
||||
}
|
||||
|
||||
private void OpenCustomVersions(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dictionary = new DictionaryEditor(
|
||||
_applicationView.SettingsView.SelectedCustomVersions,
|
||||
"Versioning Configuration (Custom Versions)",
|
||||
_applicationView.SettingsView.EnableElements);
|
||||
var result = dictionary.ShowDialog();
|
||||
if (!result.HasValue || !result.Value)
|
||||
return;
|
||||
|
||||
_applicationView.SettingsView.SelectedCustomVersions = dictionary.CustomVersions;
|
||||
}
|
||||
|
||||
private void OpenOptions(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dictionary = new DictionaryEditor(
|
||||
_applicationView.SettingsView.SelectedOptions,
|
||||
"Versioning Configuration (Options)",
|
||||
_applicationView.SettingsView.EnableElements);
|
||||
var result = dictionary.ShowDialog();
|
||||
if (!result.HasValue || !result.Value)
|
||||
return;
|
||||
|
||||
_applicationView.SettingsView.SelectedOptions = dictionary.Options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
# FModel [](https://discord.gg/fdkNYYQ)
|
||||
|
||||
    Open-source software for exploring Unreal Engine games' files. From seeing the properties of an asset to listening to your favorite audio files, it has never been easier to navigate inside a game's assets. FModel support tens of file types and asset types, to display the most information possible and it also has the ability to let you <kbd>CTRL+LMB</kbd> on an asset path to display its properties too.
|
||||
    Open-source software for exploring Unreal Engine games' files. From seeing the properties of an asset to listening to your favorite audio files, it has never been easier to navigate inside a game's assets. FModel supports tens of file types and asset types, to display the most information possible and it also has the ability to let you <kbd>CTRL+LMB</kbd> on an asset path to display its properties too.
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/26126862/119065662-52534800-b9de-11eb-85fd-a47797daa062.png" align="center" alt="FModel">
|
||||
|
||||
## Installation
|
||||
|
||||
To use FModel, you need to have **[.NET 5](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-desktop-5.0.6-windows-x64-installer)** installed on your computer
|
||||
To use FModel, you need to have **[.NET 5](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-desktop-5.0.9-windows-x64-installer)** installed on your computer
|
||||
* **[Download](https://github.com/iAmAsval/FModel/releases/latest/download/FModel.zip)** the latest release.
|
||||
* **Extract FModel.exe** somewhere on your PC and launch it
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user