mirror of
https://github.com/4sval/FModel.git
synced 2026-03-22 01:34:37 -05:00
Merge branch 'dev' into dotnet-10
This commit is contained in:
commit
0e54e9025f
|
|
@ -1 +1 @@
|
|||
Subproject commit 7772c6ccf0f6f195876d20d4b5d49fe533fc564e
|
||||
Subproject commit 6865562475cb0661843438c4a20bd8748ba9c5e2
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Application x:Class="FModel.App"
|
||||
<Application x:Class="FModel.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
|
|
@ -9,6 +9,14 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/AdonisUI;component/ColorSchemes/Dark.xaml"/>
|
||||
<ResourceDictionary Source="pack://application:,,,/AdonisUI.ClassicTheme;component/Resources.xaml"/>
|
||||
|
||||
<ResourceDictionary Source="Views/Resources/Colors.xaml" />
|
||||
<ResourceDictionary Source="Views/Resources/Icons.xaml" />
|
||||
<ResourceDictionary Source="Views/Resources/Controls/ContextMenus/FileContextMenu.xaml" />
|
||||
<ResourceDictionary Source="Views/Resources/Controls/ContextMenus/FolderContextMenu.xaml" />
|
||||
|
||||
<ResourceDictionary Source="Views/Resources/Resources.xaml" />
|
||||
<ResourceDictionary Source="Views/Resources/Controls/TiledExplorer/Resources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Color x:Key="{x:Static adonisUi:Colors.AccentColor}">#206BD4</Color>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
|
|
@ -13,6 +13,7 @@ public static class Constants
|
|||
public static readonly string APP_VERSION = FileVersionInfo.GetVersionInfo(APP_PATH).FileVersion;
|
||||
public static readonly string APP_COMMIT_ID = FileVersionInfo.GetVersionInfo(APP_PATH).ProductVersion?.SubstringAfter('+');
|
||||
public static readonly string APP_SHORT_COMMIT_ID = APP_COMMIT_ID[..7];
|
||||
public static readonly DateTime APP_BUILD_DATE = File.GetLastWriteTime(APP_PATH);
|
||||
|
||||
public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
public static readonly FGuid ZERO_GUID = new(0U);
|
||||
|
|
|
|||
|
|
@ -1,320 +1,320 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using CUE4Parse.GameTypes.FN.Enums;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
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 CUE4Parse_Conversion.Textures;
|
||||
using FModel.Settings;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseIcon : UCreator
|
||||
{
|
||||
public SKBitmap SeriesBackground { get; protected set; }
|
||||
protected string ShortDescription { get; set; }
|
||||
protected string CosmeticSource { get; set; }
|
||||
protected Dictionary<string, SKBitmap> UserFacingFlags { get; set; }
|
||||
|
||||
public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style) { }
|
||||
|
||||
public void ParseForReward(bool isUsingDisplayAsset)
|
||||
{
|
||||
// rarity
|
||||
if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series);
|
||||
else if (Object.TryGetValue(out FStructFallback componentContainer, "ComponentContainer")) GetSeries(componentContainer);
|
||||
else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon
|
||||
|
||||
if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList"))
|
||||
{
|
||||
GetSeries(dataList);
|
||||
Preview = Utils.GetBitmap(dataList);
|
||||
}
|
||||
|
||||
// preview
|
||||
if (Preview is null)
|
||||
{
|
||||
if (isUsingDisplayAsset && Utils.TryGetDisplayAsset(Object, out var preview))
|
||||
Preview = preview;
|
||||
else if (Object.TryGetValue(out FPackageIndex itemDefinition, "HeroDefinition", "WeaponDefinition"))
|
||||
Preview = Utils.GetBitmap(itemDefinition);
|
||||
else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "BundleImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon"))
|
||||
Preview = Utils.GetBitmap(largePreview);
|
||||
else if (Object.TryGetValue(out string s, "LargePreviewImage") && !string.IsNullOrEmpty(s))
|
||||
Preview = Utils.GetBitmap(s);
|
||||
else if (Object.TryGetValue(out FPackageIndex otherPreview, "SmallPreviewImage", "ToastIcon", "access_item"))
|
||||
Preview = Utils.GetBitmap(otherPreview);
|
||||
else if (Object.TryGetValue(out UMaterialInstanceConstant materialInstancePreview, "EventCalloutImage"))
|
||||
Preview = Utils.GetBitmap(materialInstancePreview);
|
||||
else if (Object.TryGetValue(out FStructFallback brush, "IconBrush") && brush.TryGetValue(out UTexture2D res, "ResourceObject"))
|
||||
Preview = Utils.GetBitmap(res);
|
||||
}
|
||||
|
||||
// text
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName", "BundleName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "Description", "ItemDescription", "SetDescription", "BundleDescription", "GeneralDescription", "DefaultBodyText", "UIDescription", "UIDisplayDescription", "EntryDescription", "EventCalloutDescription"))
|
||||
Description = description.Text;
|
||||
else if (Object.TryGetValue(out FText[] descriptions, "Description"))
|
||||
Description = string.Join('\n', descriptions.Select(x => x.Text));
|
||||
if (Object.TryGetValue(out FText shortDescription, "ShortDescription", "UIDisplaySubName"))
|
||||
ShortDescription = shortDescription.Text;
|
||||
else if (Object.ExportType.Equals("AthenaItemWrapDefinition", StringComparison.OrdinalIgnoreCase))
|
||||
ShortDescription = Utils.GetLocalizedResource("Fort.Cosmetics", "ItemWrapShortDescription", "Wrap");
|
||||
|
||||
// Only works on non-cataba designs
|
||||
if (Object.TryGetValue(out FStructFallback eventArrowColor, "EventArrowColor") &&
|
||||
eventArrowColor.TryGetValue(out FLinearColor specifiedArrowColor, "SpecifiedColor") &&
|
||||
Object.TryGetValue(out FStructFallback eventArrowShadowColor, "EventArrowShadowColor") &&
|
||||
eventArrowShadowColor.TryGetValue(out FLinearColor specifiedShadowColor, "SpecifiedColor"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(specifiedArrowColor.Hex), SKColor.Parse(specifiedShadowColor.Hex) };
|
||||
Border = new[] { SKColor.Parse(specifiedShadowColor.Hex), SKColor.Parse(specifiedArrowColor.Hex) };
|
||||
}
|
||||
|
||||
Description = Utils.RemoveHtmlTags(Description);
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
|
||||
|
||||
if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList"))
|
||||
CheckGameplayTags(dataList);
|
||||
if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
|
||||
CheckGameplayTags(gameplayTags);
|
||||
if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item"))
|
||||
CosmeticSource = cosmeticItem.Name;
|
||||
}
|
||||
|
||||
protected void Draw(SKCanvas c)
|
||||
{
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.NoBackground:
|
||||
DrawPreview(c);
|
||||
break;
|
||||
case EIconStyle.NoText:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawUserFacingFlags(c);
|
||||
break;
|
||||
default:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
DrawToBottom(c, SKTextAlign.Right, CosmeticSource);
|
||||
if (Description != ShortDescription)
|
||||
DrawToBottom(c, SKTextAlign.Left, ShortDescription);
|
||||
DrawUserFacingFlags(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
Draw(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void GetSeries(FPackageIndex s)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(s, out UObject export)) return;
|
||||
|
||||
GetSeries(export);
|
||||
}
|
||||
|
||||
private void GetSeries(FInstancedStruct[] s)
|
||||
{
|
||||
if (s.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FPackageIndex _, "Series") == true) is { } dl)
|
||||
GetSeries(dl.NonConstStruct?.Get<FPackageIndex>("Series"));
|
||||
}
|
||||
|
||||
private void GetSeries(FStructFallback s)
|
||||
{
|
||||
if (!s.TryGetValue(out FPackageIndex[] components, "Components")) return;
|
||||
if (components.FirstOrDefault(c => c.Name.Contains("Series")) is not { } seriesDef ||
|
||||
!seriesDef.TryLoad(out var seriesDefObj) || seriesDefObj is null ||
|
||||
!seriesDefObj.TryGetValue(out UObject series, "Series")) return;
|
||||
|
||||
GetSeries(series);
|
||||
}
|
||||
|
||||
protected void GetSeries(UObject uObject)
|
||||
{
|
||||
if (uObject is UTexture2D texture2D)
|
||||
{
|
||||
SeriesBackground = texture2D.Decode().ToSkBitmap();
|
||||
return;
|
||||
}
|
||||
|
||||
if (uObject.TryGetValue(out FSoftObjectPath backgroundTexture, "BackgroundTexture"))
|
||||
{
|
||||
SeriesBackground = Utils.GetBitmap(backgroundTexture);
|
||||
}
|
||||
|
||||
if (uObject.TryGetValue(out FStructFallback colors, "Colors") &&
|
||||
colors.TryGetValue(out FLinearColor color1, "Color1") &&
|
||||
colors.TryGetValue(out FLinearColor color2, "Color2") &&
|
||||
colors.TryGetValue(out FLinearColor color3, "Color3"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
|
||||
Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
|
||||
if (uObject.Name.Equals("PlatformSeries") &&
|
||||
uObject.TryGetValue(out FSoftObjectPath itemCardMaterial, "ItemCardMaterial") &&
|
||||
Utils.TryLoadObject(itemCardMaterial.AssetPathName.Text, out UMaterialInstanceConstant material))
|
||||
{
|
||||
foreach (var vectorParameter in material.VectorParameterValues)
|
||||
{
|
||||
if (vectorParameter.ParameterValue == null || !vectorParameter.ParameterInfo.Name.Text.Equals("ColorCircuitBackground"))
|
||||
continue;
|
||||
|
||||
Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetRarity(EFortRarity r)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export)) return;
|
||||
|
||||
if (export.GetByIndex<FStructFallback>((int) r) is { } data &&
|
||||
data.TryGetValue(out FLinearColor color1, "Color1") &&
|
||||
data.TryGetValue(out FLinearColor color2, "Color2") &&
|
||||
data.TryGetValue(out FLinearColor color3, "Color3"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
|
||||
Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetCosmeticSet(string setName)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Items/Cosmetics/Metadata/CosmeticSets.CosmeticSets", out UDataTable cosmeticSets))
|
||||
return string.Empty;
|
||||
|
||||
if (!cosmeticSets.TryGetDataTableRow(setName, StringComparison.OrdinalIgnoreCase, out var uObject))
|
||||
return string.Empty;
|
||||
|
||||
var name = string.Empty;
|
||||
if (uObject.TryGetValue(out FText displayName, "DisplayName"))
|
||||
name = displayName.Text;
|
||||
|
||||
var format = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_SetMembership", "\nPart of the <SetName>{0}</> set.");
|
||||
return Utils.RemoveHtmlTags(string.Format(format, name));
|
||||
}
|
||||
|
||||
protected (string, string, bool) GetInternalSID(string number)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Plugins/GameFeatures/BattlePassBase/Content/DataTables/Athena_SeasonTitles.Athena_SeasonTitles", out UDataTable seasonTitles) ||
|
||||
!seasonTitles.TryGetDataTableRow(number, StringComparison.InvariantCulture, out var row) ||
|
||||
!row.TryGetValue(out FText chapterText, "DisplayChapterText") ||
|
||||
!row.TryGetValue(out FText seasonText, "DisplaySeasonText") ||
|
||||
!row.TryGetValue(out FName displayType, "DisplayType"))
|
||||
return (string.Empty, string.Empty, true);
|
||||
|
||||
var onlySeason = displayType.Text.EndsWith("::OnlySeason") || (chapterText.Text == seasonText.Text && !int.TryParse(seasonText.Text, out _));
|
||||
return (chapterText.Text, seasonText.Text, onlySeason);
|
||||
}
|
||||
|
||||
protected string GetCosmeticSeason(string seasonNumber)
|
||||
{
|
||||
var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
|
||||
(string chapterIdx, string seasonIdx, bool onlySeason) = GetInternalSID(s);
|
||||
|
||||
var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
|
||||
var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in <SeasonText>{0}</>.");
|
||||
if (onlySeason) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, seasonIdx)));
|
||||
|
||||
var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}");
|
||||
var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}");
|
||||
var d = string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format(season, seasonIdx));
|
||||
return Utils.RemoveHtmlTags(string.Format(introduced, d));
|
||||
}
|
||||
|
||||
protected void CheckGameplayTags(FInstancedStruct[] dataList)
|
||||
{
|
||||
if (dataList.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FGameplayTagContainer _, "Tags") ?? false) is { NonConstStruct: not null } tags)
|
||||
{
|
||||
CheckGameplayTags(tags.NonConstStruct.Get<FGameplayTagContainer>("Tags"));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CheckGameplayTags(FGameplayTagContainer gameplayTags)
|
||||
{
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source))
|
||||
CosmeticSource = source.Text["Cosmetics.Source.".Length..];
|
||||
else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action))
|
||||
CosmeticSource = action.Text["Athena.ItemAction.".Length..];
|
||||
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set))
|
||||
Description += GetCosmeticSet(set.Text);
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
|
||||
Description += GetCosmeticSeason(season.Text);
|
||||
|
||||
GetUserFacingFlags(gameplayTags.GetAllGameplayTags(
|
||||
"Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender."));
|
||||
}
|
||||
|
||||
protected void GetUserFacingFlags(IList<string> userFacingFlags)
|
||||
{
|
||||
if (userFacingFlags.Count < 1 || !Utils.TryLoadObject("FortniteGame/Content/Items/ItemCategories.ItemCategories", out UObject itemCategories))
|
||||
return;
|
||||
|
||||
if (!itemCategories.TryGetValue(out FStructFallback[] tertiaryCategories, "TertiaryCategories"))
|
||||
return;
|
||||
|
||||
UserFacingFlags = new Dictionary<string, SKBitmap>(userFacingFlags.Count);
|
||||
foreach (var flag in userFacingFlags)
|
||||
{
|
||||
if (flag.Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Object.ExportType.Equals("AthenaPetCarrierItemDefinition", StringComparison.OrdinalIgnoreCase))
|
||||
UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png"))?.Stream);
|
||||
else UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png"))?.Stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var category in tertiaryCategories)
|
||||
{
|
||||
if (category.TryGetValue(out FGameplayTagContainer tagContainer, "TagContainer") && tagContainer.TryGetGameplayTag(flag, out _) &&
|
||||
category.TryGetValue(out FStructFallback categoryBrush, "CategoryBrush") && categoryBrush.TryGetValue(out FStructFallback brushXxs, "Brush_XXS") &&
|
||||
brushXxs.TryGetValue(out FPackageIndex resourceObject, "ResourceObject") && Utils.TryGetPackageIndexExport(resourceObject, out UTexture2D texture))
|
||||
{
|
||||
UserFacingFlags[flag] = Utils.GetBitmap(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawUserFacingFlags(SKCanvas c)
|
||||
{
|
||||
if (UserFacingFlags == null) return;
|
||||
|
||||
const int size = 25;
|
||||
var x = Margin * (int) 2.5;
|
||||
foreach (var flag in UserFacingFlags.Values.Where(flag => flag != null))
|
||||
{
|
||||
c.DrawBitmap(flag.Resize(size), new SKPoint(x, Margin * (int) 2.5), ImagePaint);
|
||||
x += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using CUE4Parse.GameTypes.FN.Enums;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
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 CUE4Parse_Conversion.Textures;
|
||||
using FModel.Settings;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseIcon : UCreator
|
||||
{
|
||||
public SKBitmap SeriesBackground { get; protected set; }
|
||||
protected string ShortDescription { get; set; }
|
||||
protected string CosmeticSource { get; set; }
|
||||
protected Dictionary<string, SKBitmap> UserFacingFlags { get; set; }
|
||||
|
||||
public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style) { }
|
||||
|
||||
public void ParseForReward(bool isUsingDisplayAsset)
|
||||
{
|
||||
// rarity
|
||||
if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series);
|
||||
else if (Object.TryGetValue(out FStructFallback componentContainer, "ComponentContainer")) GetSeries(componentContainer);
|
||||
else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon
|
||||
|
||||
if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList"))
|
||||
{
|
||||
GetSeries(dataList);
|
||||
Preview = Utils.GetBitmap(dataList);
|
||||
}
|
||||
|
||||
// preview
|
||||
if (Preview is null)
|
||||
{
|
||||
if (isUsingDisplayAsset && Utils.TryGetDisplayAsset(Object, out var preview))
|
||||
Preview = preview;
|
||||
else if (Object.TryGetValue(out FPackageIndex itemDefinition, "HeroDefinition", "WeaponDefinition"))
|
||||
Preview = Utils.GetBitmap(itemDefinition);
|
||||
else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "BundleImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon"))
|
||||
Preview = Utils.GetBitmap(largePreview);
|
||||
else if (Object.TryGetValue(out string s, "LargePreviewImage") && !string.IsNullOrEmpty(s))
|
||||
Preview = Utils.GetBitmap(s);
|
||||
else if (Object.TryGetValue(out FPackageIndex otherPreview, "SmallPreviewImage", "ToastIcon", "access_item"))
|
||||
Preview = Utils.GetBitmap(otherPreview);
|
||||
else if (Object.TryGetValue(out UMaterialInstanceConstant materialInstancePreview, "EventCalloutImage"))
|
||||
Preview = Utils.GetBitmap(materialInstancePreview);
|
||||
else if (Object.TryGetValue(out FStructFallback brush, "IconBrush") && brush.TryGetValue(out UTexture2D res, "ResourceObject"))
|
||||
Preview = Utils.GetBitmap(res);
|
||||
}
|
||||
|
||||
// text
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName", "BundleName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "Description", "ItemDescription", "SetDescription", "BundleDescription", "GeneralDescription", "DefaultBodyText", "UIDescription", "UIDisplayDescription", "EntryDescription", "EventCalloutDescription"))
|
||||
Description = description.Text;
|
||||
else if (Object.TryGetValue(out FText[] descriptions, "Description"))
|
||||
Description = string.Join('\n', descriptions.Select(x => x.Text));
|
||||
if (Object.TryGetValue(out FText shortDescription, "ShortDescription", "UIDisplaySubName"))
|
||||
ShortDescription = shortDescription.Text;
|
||||
else if (Object.ExportType.Equals("AthenaItemWrapDefinition", StringComparison.OrdinalIgnoreCase))
|
||||
ShortDescription = Utils.GetLocalizedResource("Fort.Cosmetics", "ItemWrapShortDescription", "Wrap");
|
||||
|
||||
// Only works on non-cataba designs
|
||||
if (Object.TryGetValue(out FStructFallback eventArrowColor, "EventArrowColor") &&
|
||||
eventArrowColor.TryGetValue(out FLinearColor specifiedArrowColor, "SpecifiedColor") &&
|
||||
Object.TryGetValue(out FStructFallback eventArrowShadowColor, "EventArrowShadowColor") &&
|
||||
eventArrowShadowColor.TryGetValue(out FLinearColor specifiedShadowColor, "SpecifiedColor"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(specifiedArrowColor.Hex), SKColor.Parse(specifiedShadowColor.Hex) };
|
||||
Border = new[] { SKColor.Parse(specifiedShadowColor.Hex), SKColor.Parse(specifiedArrowColor.Hex) };
|
||||
}
|
||||
|
||||
Description = Utils.RemoveHtmlTags(Description);
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
|
||||
|
||||
if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList"))
|
||||
CheckGameplayTags(dataList);
|
||||
if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
|
||||
CheckGameplayTags(gameplayTags);
|
||||
if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item"))
|
||||
CosmeticSource = cosmeticItem.Name;
|
||||
}
|
||||
|
||||
protected void Draw(SKCanvas c)
|
||||
{
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.NoBackground:
|
||||
DrawPreview(c);
|
||||
break;
|
||||
case EIconStyle.NoText:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawUserFacingFlags(c);
|
||||
break;
|
||||
default:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
DrawToBottom(c, SKTextAlign.Right, CosmeticSource);
|
||||
if (Description != ShortDescription)
|
||||
DrawToBottom(c, SKTextAlign.Left, ShortDescription);
|
||||
DrawUserFacingFlags(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
Draw(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void GetSeries(FPackageIndex s)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(s, out UObject export)) return;
|
||||
|
||||
GetSeries(export);
|
||||
}
|
||||
|
||||
private void GetSeries(FInstancedStruct[] s)
|
||||
{
|
||||
if (s.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FPackageIndex _, "Series") == true) is { } dl)
|
||||
GetSeries(dl.NonConstStruct?.Get<FPackageIndex>("Series"));
|
||||
}
|
||||
|
||||
private void GetSeries(FStructFallback s)
|
||||
{
|
||||
if (!s.TryGetValue(out FPackageIndex[] components, "Components")) return;
|
||||
if (components.FirstOrDefault(c => c.Name.Contains("Series")) is not { } seriesDef ||
|
||||
!seriesDef.TryLoad(out var seriesDefObj) || seriesDefObj is null ||
|
||||
!seriesDefObj.TryGetValue(out UObject series, "Series")) return;
|
||||
|
||||
GetSeries(series);
|
||||
}
|
||||
|
||||
protected void GetSeries(UObject uObject)
|
||||
{
|
||||
if (uObject is UTexture2D texture2D)
|
||||
{
|
||||
SeriesBackground = texture2D.Decode().ToSkBitmap();
|
||||
return;
|
||||
}
|
||||
|
||||
if (uObject.TryGetValue(out FSoftObjectPath backgroundTexture, "BackgroundTexture"))
|
||||
{
|
||||
SeriesBackground = Utils.GetBitmap(backgroundTexture);
|
||||
}
|
||||
|
||||
if (uObject.TryGetValue(out FStructFallback colors, "Colors") &&
|
||||
colors.TryGetValue(out FLinearColor color1, "Color1") &&
|
||||
colors.TryGetValue(out FLinearColor color2, "Color2") &&
|
||||
colors.TryGetValue(out FLinearColor color3, "Color3"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
|
||||
Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
|
||||
if (uObject.Name.Equals("PlatformSeries") &&
|
||||
uObject.TryGetValue(out FSoftObjectPath itemCardMaterial, "ItemCardMaterial") &&
|
||||
Utils.TryLoadObject(itemCardMaterial.AssetPathName.Text, out UMaterialInstanceConstant material))
|
||||
{
|
||||
foreach (var vectorParameter in material.VectorParameterValues)
|
||||
{
|
||||
if (vectorParameter.ParameterValue == null || !vectorParameter.ParameterInfo.Name.Text.Equals("ColorCircuitBackground"))
|
||||
continue;
|
||||
|
||||
Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetRarity(EFortRarity r)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export)) return;
|
||||
|
||||
if (export.GetByIndex<FStructFallback>((int) r) is { } data &&
|
||||
data.TryGetValue(out FLinearColor color1, "Color1") &&
|
||||
data.TryGetValue(out FLinearColor color2, "Color2") &&
|
||||
data.TryGetValue(out FLinearColor color3, "Color3"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
|
||||
Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetCosmeticSet(string setName)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Items/Cosmetics/Metadata/CosmeticSets.CosmeticSets", out UDataTable cosmeticSets))
|
||||
return string.Empty;
|
||||
|
||||
if (!cosmeticSets.TryGetDataTableRow(setName, StringComparison.OrdinalIgnoreCase, out var uObject))
|
||||
return string.Empty;
|
||||
|
||||
var name = string.Empty;
|
||||
if (uObject.TryGetValue(out FText displayName, "DisplayName"))
|
||||
name = displayName.Text;
|
||||
|
||||
var format = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_SetMembership", "\nPart of the <SetName>{0}</> set.");
|
||||
return Utils.RemoveHtmlTags(string.Format(format, name));
|
||||
}
|
||||
|
||||
protected (string, string, bool) GetInternalSID(string number)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Plugins/GameFeatures/BattlePassBase/Content/DataTables/Athena_SeasonTitles.Athena_SeasonTitles", out UDataTable seasonTitles) ||
|
||||
!seasonTitles.TryGetDataTableRow(number, StringComparison.InvariantCulture, out var row) ||
|
||||
!row.TryGetValue(out FText chapterText, "DisplayChapterText") ||
|
||||
!row.TryGetValue(out FText seasonText, "DisplaySeasonText") ||
|
||||
!row.TryGetValue(out FName displayType, "DisplayType"))
|
||||
return (string.Empty, string.Empty, true);
|
||||
|
||||
var onlySeason = displayType.Text.EndsWith("::OnlySeason") || (chapterText.Text == seasonText.Text && !int.TryParse(seasonText.Text, out _));
|
||||
return (chapterText.Text, seasonText.Text, onlySeason);
|
||||
}
|
||||
|
||||
protected string GetCosmeticSeason(string seasonNumber)
|
||||
{
|
||||
var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
|
||||
(string chapterIdx, string seasonIdx, bool onlySeason) = GetInternalSID(s);
|
||||
|
||||
var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
|
||||
var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in <SeasonText>{0}</>.");
|
||||
if (onlySeason) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, seasonIdx)));
|
||||
|
||||
var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}");
|
||||
var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}");
|
||||
var d = string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format(season, seasonIdx));
|
||||
return Utils.RemoveHtmlTags(string.Format(introduced, d));
|
||||
}
|
||||
|
||||
protected void CheckGameplayTags(FInstancedStruct[] dataList)
|
||||
{
|
||||
if (dataList.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FGameplayTagContainer _, "Tags") ?? false) is { NonConstStruct: not null } tags)
|
||||
{
|
||||
CheckGameplayTags(tags.NonConstStruct.Get<FGameplayTagContainer>("Tags"));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CheckGameplayTags(FGameplayTagContainer gameplayTags)
|
||||
{
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source))
|
||||
CosmeticSource = source.Text["Cosmetics.Source.".Length..];
|
||||
else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action))
|
||||
CosmeticSource = action.Text["Athena.ItemAction.".Length..];
|
||||
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set))
|
||||
Description += GetCosmeticSet(set.Text);
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
|
||||
Description += GetCosmeticSeason(season.Text);
|
||||
|
||||
GetUserFacingFlags(gameplayTags.GetAllGameplayTags(
|
||||
"Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender."));
|
||||
}
|
||||
|
||||
protected void GetUserFacingFlags(IList<string> userFacingFlags)
|
||||
{
|
||||
if (userFacingFlags.Count < 1 || !Utils.TryLoadObject("FortniteGame/Content/Items/ItemCategories.ItemCategories", out UObject itemCategories))
|
||||
return;
|
||||
|
||||
if (!itemCategories.TryGetValue(out FStructFallback[] tertiaryCategories, "TertiaryCategories"))
|
||||
return;
|
||||
|
||||
UserFacingFlags = new Dictionary<string, SKBitmap>(userFacingFlags.Count);
|
||||
foreach (var flag in userFacingFlags)
|
||||
{
|
||||
if (flag.Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Object.ExportType.Equals("AthenaPetCarrierItemDefinition", StringComparison.OrdinalIgnoreCase))
|
||||
UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png"))?.Stream);
|
||||
else UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png"))?.Stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var category in tertiaryCategories)
|
||||
{
|
||||
if (category.TryGetValue(out FGameplayTagContainer tagContainer, "TagContainer") && tagContainer.TryGetGameplayTag(flag, out _) &&
|
||||
category.TryGetValue(out FStructFallback categoryBrush, "CategoryBrush") && categoryBrush.TryGetValue(out FStructFallback brushXxs, "Brush_XXS") &&
|
||||
brushXxs.TryGetValue(out FPackageIndex resourceObject, "ResourceObject") && Utils.TryGetPackageIndexExport(resourceObject, out UTexture2D texture))
|
||||
{
|
||||
UserFacingFlags[flag] = Utils.GetBitmap(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawUserFacingFlags(SKCanvas c)
|
||||
{
|
||||
if (UserFacingFlags == null) return;
|
||||
|
||||
const int size = 25;
|
||||
var x = Margin * (int) 2.5;
|
||||
foreach (var flag in UserFacingFlags.Values.Where(flag => flag != null))
|
||||
{
|
||||
c.DrawBitmap(flag.Resize(size), new SKPoint(x, Margin * (int) 2.5), ImagePaint);
|
||||
x += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Framework;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
|
|
|
|||
|
|
@ -1,266 +1,267 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using FModel.Creator.Bases.MV;
|
||||
|
||||
namespace FModel.Creator;
|
||||
|
||||
public class CreatorPackage : IDisposable
|
||||
{
|
||||
private string _pkgName;
|
||||
private string _exportType;
|
||||
private Lazy<UObject> _object;
|
||||
private EIconStyle _style;
|
||||
|
||||
public CreatorPackage(string packageName, string exportType, Lazy<UObject> uObject, EIconStyle style)
|
||||
{
|
||||
_pkgName = packageName;
|
||||
_exportType = exportType;
|
||||
_object = uObject;
|
||||
_style = style;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UCreator ConstructCreator()
|
||||
{
|
||||
TryConstructCreator(out var creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryConstructCreator([MaybeNullWhen(false)] out UCreator creator)
|
||||
{
|
||||
switch (_exportType)
|
||||
{
|
||||
// Fortnite
|
||||
case "AthenaConsumableEmoteItemDefinition":
|
||||
case "AthenaSkyDiveContrailItemDefinition":
|
||||
case "AthenaLoadingScreenItemDefinition":
|
||||
case "AthenaVictoryPoseItemDefinition":
|
||||
case "AthenaPetCarrierItemDefinition":
|
||||
case "AthenaMusicPackItemDefinition":
|
||||
case "AthenaBattleBusItemDefinition":
|
||||
case "AthenaCharacterItemDefinition":
|
||||
case "AthenaMapMarkerItemDefinition":
|
||||
case "AthenaBackpackItemDefinition":
|
||||
case "CosmeticShoesItemDefinition":
|
||||
case "CosmeticCompanionItemDefinition":
|
||||
case "CosmeticCompanionReactFXItemDefinition":
|
||||
case "AthenaPickaxeItemDefinition":
|
||||
case "AthenaGadgetItemDefinition":
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using FModel.Creator.Bases.MV;
|
||||
|
||||
namespace FModel.Creator;
|
||||
|
||||
public class CreatorPackage : IDisposable
|
||||
{
|
||||
private string _pkgName;
|
||||
private string _exportType;
|
||||
private Lazy<UObject> _object;
|
||||
private EIconStyle _style;
|
||||
|
||||
public CreatorPackage(string packageName, string exportType, Lazy<UObject> uObject, EIconStyle style)
|
||||
{
|
||||
_pkgName = packageName;
|
||||
_exportType = exportType;
|
||||
_object = uObject;
|
||||
_style = style;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UCreator ConstructCreator()
|
||||
{
|
||||
TryConstructCreator(out var creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryConstructCreator([MaybeNullWhen(false)] out UCreator creator)
|
||||
{
|
||||
// TODO: convert to a type based system
|
||||
switch (_exportType)
|
||||
{
|
||||
// Fortnite
|
||||
case "AthenaConsumableEmoteItemDefinition":
|
||||
case "AthenaSkyDiveContrailItemDefinition":
|
||||
case "AthenaLoadingScreenItemDefinition":
|
||||
case "AthenaVictoryPoseItemDefinition":
|
||||
case "AthenaPetCarrierItemDefinition":
|
||||
case "AthenaMusicPackItemDefinition":
|
||||
case "AthenaBattleBusItemDefinition":
|
||||
case "AthenaCharacterItemDefinition":
|
||||
case "AthenaMapMarkerItemDefinition":
|
||||
case "AthenaBackpackItemDefinition":
|
||||
case "CosmeticShoesItemDefinition":
|
||||
case "CosmeticCompanionItemDefinition":
|
||||
case "CosmeticCompanionReactFXItemDefinition":
|
||||
case "AthenaPickaxeItemDefinition":
|
||||
case "AthenaGadgetItemDefinition":
|
||||
case "AthenaGliderItemDefinition":
|
||||
case "AthenaHatItemDefinition":
|
||||
case "AthenaSprayItemDefinition":
|
||||
case "AthenaDanceItemDefinition":
|
||||
case "AthenaEmojiItemDefinition":
|
||||
case "AthenaItemWrapDefinition":
|
||||
case "AthenaToyItemDefinition":
|
||||
case "FortHeroType":
|
||||
case "FortTokenType":
|
||||
case "FortAbilityKit":
|
||||
case "FortWorkerType":
|
||||
case "RewardGraphToken":
|
||||
case "JunoKnowledgeBundle":
|
||||
case "FortBannerTokenType":
|
||||
case "FortVariantTokenType":
|
||||
case "FortDecoItemDefinition":
|
||||
case "FortStatItemDefinition":
|
||||
case "FortAmmoItemDefinition":
|
||||
case "FortEmoteItemDefinition":
|
||||
case "FortBadgeItemDefinition":
|
||||
case "SparksMicItemDefinition":
|
||||
case "FortAwardItemDefinition":
|
||||
case "FortStackItemDefinition":
|
||||
case "FortWorldItemDefinition":
|
||||
case "SparksAuraItemDefinition":
|
||||
case "SparksDrumItemDefinition":
|
||||
case "SparksBassItemDefinition":
|
||||
case "FortGadgetItemDefinition":
|
||||
case "AthenaCharmItemDefinition":
|
||||
case "FortPlaysetItemDefinition":
|
||||
case "FortGiftBoxItemDefinition":
|
||||
case "FortOutpostItemDefinition":
|
||||
case "FortVehicleItemDefinition":
|
||||
case "FortMissionItemDefinition":
|
||||
case "FortAccountItemDefinition":
|
||||
case "SparksGuitarItemDefinition":
|
||||
case "FortCardPackItemDefinition":
|
||||
case "FortDefenderItemDefinition":
|
||||
case "FortCurrencyItemDefinition":
|
||||
case "FortResourceItemDefinition":
|
||||
case "FortBackpackItemDefinition":
|
||||
case "FortEventQuestMapDataAsset":
|
||||
case "FortBuildingItemDefinition":
|
||||
case "FortItemCacheItemDefinition":
|
||||
case "FortWeaponModItemDefinition":
|
||||
case "FortCodeTokenItemDefinition":
|
||||
case "FortSchematicItemDefinition":
|
||||
case "FortAlterableItemDefinition":
|
||||
case "SparksKeyboardItemDefinition":
|
||||
case "FortWorldMultiItemDefinition":
|
||||
case "FortAlterationItemDefinition":
|
||||
case "FortExpeditionItemDefinition":
|
||||
case "FortIngredientItemDefinition":
|
||||
case "FortConsumableItemDefinition":
|
||||
case "StWFortAccoladeItemDefinition":
|
||||
case "FortAccountBuffItemDefinition":
|
||||
case "FortFOBCoreDecoItemDefinition":
|
||||
case "FortPlayerPerksItemDefinition":
|
||||
case "FortPlaysetPropItemDefinition":
|
||||
case "FortPrerollDataItemDefinition":
|
||||
case "JunoRecipeBundleItemDefinition":
|
||||
case "FortHomebaseNodeItemDefinition":
|
||||
case "FortNeverPersistItemDefinition":
|
||||
case "FortPlayerAugmentItemDefinition":
|
||||
case "FortSmartBuildingItemDefinition":
|
||||
case "FortGiftBoxUnlockItemDefinition":
|
||||
case "FortCreativeGadgetItemDefinition":
|
||||
case "FortWeaponModItemDefinitionOptic":
|
||||
case "RadioContentSourceItemDefinition":
|
||||
case "FortPlaysetGrenadeItemDefinition":
|
||||
case "JunoWeaponCreatureItemDefinition":
|
||||
case "FortEventDependentItemDefinition":
|
||||
case "FortPersonalVehicleItemDefinition":
|
||||
case "FortGameplayModifierItemDefinition":
|
||||
case "FortHardcoreModifierItemDefinition":
|
||||
case "FortWeaponModItemDefinitionMagazine":
|
||||
case "FortConsumableAccountItemDefinition":
|
||||
case "FortConversionControlItemDefinition":
|
||||
case "FortAccountBuffCreditItemDefinition":
|
||||
case "JunoBuildInstructionsItemDefinition":
|
||||
case "FortCharacterCosmeticItemDefinition":
|
||||
case "JunoBuildingSetAccountItemDefinition":
|
||||
case "FortEventCurrencyItemDefinitionRedir":
|
||||
case "FortPersistentResourceItemDefinition":
|
||||
case "FortWeaponMeleeOffhandItemDefinition":
|
||||
case "FortHomebaseBannerIconItemDefinition":
|
||||
case "FortVehicleCosmeticsVariantTokenType":
|
||||
case "JunoBuildingPropAccountItemDefinition":
|
||||
case "FortCampaignHeroLoadoutItemDefinition":
|
||||
case "FortConditionalResourceItemDefinition":
|
||||
case "FortChallengeBundleScheduleDefinition":
|
||||
case "FortDailyRewardScheduleTokenDefinition":
|
||||
case "FortVehicleCosmeticsItemDefinition_Body":
|
||||
case "FortVehicleCosmeticsItemDefinition_Skin":
|
||||
case "FortVehicleCosmeticsItemDefinition_Wheel":
|
||||
case "FortCreativeRealEstatePlotItemDefinition":
|
||||
case "FortDeployableBaseCloudSaveItemDefinition":
|
||||
case "FortVehicleCosmeticsItemDefinition_Booster":
|
||||
case "AthenaDanceItemDefinition_AdHocSquadsJoin_C":
|
||||
case "FortVehicleCosmeticsItemDefinition_DriftSmoke":
|
||||
case "FortVehicleCosmeticsItemDefinition_EngineAudio":
|
||||
creator = _style switch
|
||||
{
|
||||
EIconStyle.Cataba => new BaseCommunity(_object.Value, _style, "Cataba"),
|
||||
_ => new BaseIcon(_object.Value, _style)
|
||||
};
|
||||
return true;
|
||||
case "JunoAthenaCharacterItemOverrideDefinition":
|
||||
case "JunoAthenaDanceItemOverrideDefinition":
|
||||
creator = new BaseJuno(_object.Value, _style);
|
||||
return true;
|
||||
case "FortTandemCharacterData":
|
||||
creator = new BaseTandem(_object.Value, _style);
|
||||
return true;
|
||||
case "FortTrapItemDefinition":
|
||||
case "FortSpyTechItemDefinition":
|
||||
case "FortAccoladeItemDefinition":
|
||||
case "FortContextTrapItemDefinition":
|
||||
case "FortWeaponMeleeItemDefinition":
|
||||
case "FortWeaponRangedItemDefinition":
|
||||
case "FortCreativeWeaponMeleeItemDefinition":
|
||||
case "FortWeaponMeleeDualWieldItemDefinition":
|
||||
case "FortCreativeWeaponRangedItemDefinition":
|
||||
case "Daybreak_LevelExitVehicle_PartItemDefinition_C":
|
||||
creator = new BaseIconStats(_object.Value, _style);
|
||||
return true;
|
||||
case "FortItemSeriesDefinition":
|
||||
creator = new BaseSeries(_object.Value, _style);
|
||||
return true;
|
||||
case "MaterialInstanceConstant"
|
||||
when _pkgName.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) ||
|
||||
_pkgName.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) ||
|
||||
_pkgName.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase):
|
||||
creator = new BaseMaterialInstance(_object.Value, _style);
|
||||
return true;
|
||||
case "AthenaItemShopOfferDisplayData":
|
||||
creator = new BaseOfferDisplayData(_object.Value, _style);
|
||||
return true;
|
||||
case "FortMtxOfferData":
|
||||
creator = new BaseMtxOffer(_object.Value, _style);
|
||||
return true;
|
||||
case "FortPlaylistAthena":
|
||||
creator = new BasePlaylist(_object.Value, _style);
|
||||
return true;
|
||||
case "FortFeatItemDefinition":
|
||||
case "FortQuestItemDefinition":
|
||||
case "FortQuestItemDefinition_Athena":
|
||||
case "FortQuestItemDefinition_Campaign":
|
||||
case "AthenaDailyQuestDefinition":
|
||||
case "FortUrgentQuestItemDefinition":
|
||||
creator = new Bases.FN.BaseQuest(_object.Value, _style);
|
||||
return true;
|
||||
case "FortCompendiumItemDefinition":
|
||||
case "FortCompendiumBundleDefinition":
|
||||
case "FortChallengeBundleItemDefinition":
|
||||
creator = new BaseBundle(_object.Value, _style);
|
||||
return true;
|
||||
// case "AthenaSeasonItemDefinition":
|
||||
// creator = new BaseSeason(_object, _style);
|
||||
// return true;
|
||||
case "FortItemAccessTokenType":
|
||||
creator = new BaseItemAccessToken(_object.Value, _style);
|
||||
return true;
|
||||
case "FortCreativeOption":
|
||||
case "PlaylistUserOptionEnum":
|
||||
case "PlaylistUserOptionBool":
|
||||
case "PlaylistUserOptionString":
|
||||
case "PlaylistUserOptionIntEnum":
|
||||
case "PlaylistUserOptionIntRange":
|
||||
case "PlaylistUserOptionColorEnum":
|
||||
case "PlaylistUserOptionFloatEnum":
|
||||
case "PlaylistUserOptionFloatRange":
|
||||
case "PlaylistUserTintedIconIntEnum":
|
||||
case "PlaylistUserOptionPrimaryAsset":
|
||||
case "PlaylistUserOptionCollisionProfileEnum":
|
||||
creator = new BaseUserControl(_object.Value, _style);
|
||||
return true;
|
||||
// PandaGame
|
||||
case "CharacterData":
|
||||
creator = new BaseFighter(_object.Value, _style);
|
||||
return true;
|
||||
case "PerkGroup":
|
||||
creator = new BasePerkGroup(_object.Value, _style);
|
||||
return true;
|
||||
case "StatTrackingBundleData":
|
||||
case "HydraSyncedDataAsset":
|
||||
case "AnnouncerPackData":
|
||||
case "CharacterGiftData":
|
||||
case "ProfileIconData":
|
||||
case "RingOutVfxData":
|
||||
case "BannerData":
|
||||
case "EmoteData":
|
||||
case "TauntData":
|
||||
case "SkinData":
|
||||
case "PerkData":
|
||||
creator = new BasePandaIcon(_object.Value, _style);
|
||||
return true;
|
||||
case "QuestData":
|
||||
creator = new Bases.MV.BaseQuest(_object.Value, _style);
|
||||
return true;
|
||||
default:
|
||||
creator = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => $"{_exportType} | {_style}";
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_object = null;
|
||||
}
|
||||
}
|
||||
case "AthenaHatItemDefinition":
|
||||
case "AthenaSprayItemDefinition":
|
||||
case "AthenaDanceItemDefinition":
|
||||
case "AthenaEmojiItemDefinition":
|
||||
case "AthenaItemWrapDefinition":
|
||||
case "AthenaToyItemDefinition":
|
||||
case "FortHeroType":
|
||||
case "FortTokenType":
|
||||
case "FortAbilityKit":
|
||||
case "FortWorkerType":
|
||||
case "RewardGraphToken":
|
||||
case "JunoKnowledgeBundle":
|
||||
case "FortBannerTokenType":
|
||||
case "FortVariantTokenType":
|
||||
case "FortDecoItemDefinition":
|
||||
case "FortStatItemDefinition":
|
||||
case "FortAmmoItemDefinition":
|
||||
case "FortEmoteItemDefinition":
|
||||
case "FortBadgeItemDefinition":
|
||||
case "SparksMicItemDefinition":
|
||||
case "FortAwardItemDefinition":
|
||||
case "FortStackItemDefinition":
|
||||
case "FortWorldItemDefinition":
|
||||
case "SparksAuraItemDefinition":
|
||||
case "SparksDrumItemDefinition":
|
||||
case "SparksBassItemDefinition":
|
||||
case "FortGadgetItemDefinition":
|
||||
case "AthenaCharmItemDefinition":
|
||||
case "FortPlaysetItemDefinition":
|
||||
case "FortGiftBoxItemDefinition":
|
||||
case "FortOutpostItemDefinition":
|
||||
case "FortVehicleItemDefinition":
|
||||
case "FortMissionItemDefinition":
|
||||
case "FortAccountItemDefinition":
|
||||
case "SparksGuitarItemDefinition":
|
||||
case "FortCardPackItemDefinition":
|
||||
case "FortDefenderItemDefinition":
|
||||
case "FortCurrencyItemDefinition":
|
||||
case "FortResourceItemDefinition":
|
||||
case "FortBackpackItemDefinition":
|
||||
case "FortEventQuestMapDataAsset":
|
||||
case "FortBuildingItemDefinition":
|
||||
case "FortItemCacheItemDefinition":
|
||||
case "FortWeaponModItemDefinition":
|
||||
case "FortCodeTokenItemDefinition":
|
||||
case "FortSchematicItemDefinition":
|
||||
case "FortAlterableItemDefinition":
|
||||
case "SparksKeyboardItemDefinition":
|
||||
case "FortWorldMultiItemDefinition":
|
||||
case "FortAlterationItemDefinition":
|
||||
case "FortExpeditionItemDefinition":
|
||||
case "FortIngredientItemDefinition":
|
||||
case "FortConsumableItemDefinition":
|
||||
case "StWFortAccoladeItemDefinition":
|
||||
case "FortAccountBuffItemDefinition":
|
||||
case "FortFOBCoreDecoItemDefinition":
|
||||
case "FortPlayerPerksItemDefinition":
|
||||
case "FortPlaysetPropItemDefinition":
|
||||
case "FortPrerollDataItemDefinition":
|
||||
case "JunoRecipeBundleItemDefinition":
|
||||
case "FortHomebaseNodeItemDefinition":
|
||||
case "FortNeverPersistItemDefinition":
|
||||
case "FortPlayerAugmentItemDefinition":
|
||||
case "FortSmartBuildingItemDefinition":
|
||||
case "FortGiftBoxUnlockItemDefinition":
|
||||
case "FortCreativeGadgetItemDefinition":
|
||||
case "FortWeaponModItemDefinitionOptic":
|
||||
case "RadioContentSourceItemDefinition":
|
||||
case "FortPlaysetGrenadeItemDefinition":
|
||||
case "JunoWeaponCreatureItemDefinition":
|
||||
case "FortEventDependentItemDefinition":
|
||||
case "FortPersonalVehicleItemDefinition":
|
||||
case "FortGameplayModifierItemDefinition":
|
||||
case "FortHardcoreModifierItemDefinition":
|
||||
case "FortWeaponModItemDefinitionMagazine":
|
||||
case "FortConsumableAccountItemDefinition":
|
||||
case "FortConversionControlItemDefinition":
|
||||
case "FortAccountBuffCreditItemDefinition":
|
||||
case "JunoBuildInstructionsItemDefinition":
|
||||
case "FortCharacterCosmeticItemDefinition":
|
||||
case "JunoBuildingSetAccountItemDefinition":
|
||||
case "FortEventCurrencyItemDefinitionRedir":
|
||||
case "FortPersistentResourceItemDefinition":
|
||||
case "FortWeaponMeleeOffhandItemDefinition":
|
||||
case "FortHomebaseBannerIconItemDefinition":
|
||||
case "FortVehicleCosmeticsVariantTokenType":
|
||||
case "JunoBuildingPropAccountItemDefinition":
|
||||
case "FortCampaignHeroLoadoutItemDefinition":
|
||||
case "FortConditionalResourceItemDefinition":
|
||||
case "FortChallengeBundleScheduleDefinition":
|
||||
case "FortDailyRewardScheduleTokenDefinition":
|
||||
case "FortVehicleCosmeticsItemDefinition_Body":
|
||||
case "FortVehicleCosmeticsItemDefinition_Skin":
|
||||
case "FortVehicleCosmeticsItemDefinition_Wheel":
|
||||
case "FortCreativeRealEstatePlotItemDefinition":
|
||||
case "FortDeployableBaseCloudSaveItemDefinition":
|
||||
case "FortVehicleCosmeticsItemDefinition_Booster":
|
||||
case "AthenaDanceItemDefinition_AdHocSquadsJoin_C":
|
||||
case "FortVehicleCosmeticsItemDefinition_DriftSmoke":
|
||||
case "FortVehicleCosmeticsItemDefinition_EngineAudio":
|
||||
creator = _style switch
|
||||
{
|
||||
EIconStyle.Cataba => new BaseCommunity(_object.Value, _style, "Cataba"),
|
||||
_ => new BaseIcon(_object.Value, _style)
|
||||
};
|
||||
return true;
|
||||
case "JunoAthenaCharacterItemOverrideDefinition":
|
||||
case "JunoAthenaDanceItemOverrideDefinition":
|
||||
creator = new BaseJuno(_object.Value, _style);
|
||||
return true;
|
||||
case "FortTandemCharacterData":
|
||||
creator = new BaseTandem(_object.Value, _style);
|
||||
return true;
|
||||
case "FortTrapItemDefinition":
|
||||
case "FortSpyTechItemDefinition":
|
||||
case "FortAccoladeItemDefinition":
|
||||
case "FortContextTrapItemDefinition":
|
||||
case "FortWeaponMeleeItemDefinition":
|
||||
case "FortWeaponRangedItemDefinition":
|
||||
case "FortCreativeWeaponMeleeItemDefinition":
|
||||
case "FortWeaponMeleeDualWieldItemDefinition":
|
||||
case "FortCreativeWeaponRangedItemDefinition":
|
||||
case "Daybreak_LevelExitVehicle_PartItemDefinition_C":
|
||||
creator = new BaseIconStats(_object.Value, _style);
|
||||
return true;
|
||||
case "FortItemSeriesDefinition":
|
||||
creator = new BaseSeries(_object.Value, _style);
|
||||
return true;
|
||||
case "MaterialInstanceConstant"
|
||||
when _pkgName.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) ||
|
||||
_pkgName.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) ||
|
||||
_pkgName.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase):
|
||||
creator = new BaseMaterialInstance(_object.Value, _style);
|
||||
return true;
|
||||
case "AthenaItemShopOfferDisplayData":
|
||||
creator = new BaseOfferDisplayData(_object.Value, _style);
|
||||
return true;
|
||||
case "FortMtxOfferData":
|
||||
creator = new BaseMtxOffer(_object.Value, _style);
|
||||
return true;
|
||||
case "FortPlaylistAthena":
|
||||
creator = new BasePlaylist(_object.Value, _style);
|
||||
return true;
|
||||
case "FortFeatItemDefinition":
|
||||
case "FortQuestItemDefinition":
|
||||
case "FortQuestItemDefinition_Athena":
|
||||
case "FortQuestItemDefinition_Campaign":
|
||||
case "AthenaDailyQuestDefinition":
|
||||
case "FortUrgentQuestItemDefinition":
|
||||
creator = new Bases.FN.BaseQuest(_object.Value, _style);
|
||||
return true;
|
||||
case "FortCompendiumItemDefinition":
|
||||
case "FortCompendiumBundleDefinition":
|
||||
case "FortChallengeBundleItemDefinition":
|
||||
creator = new BaseBundle(_object.Value, _style);
|
||||
return true;
|
||||
// case "AthenaSeasonItemDefinition":
|
||||
// creator = new BaseSeason(_object, _style);
|
||||
// return true;
|
||||
case "FortItemAccessTokenType":
|
||||
creator = new BaseItemAccessToken(_object.Value, _style);
|
||||
return true;
|
||||
case "FortCreativeOption":
|
||||
case "PlaylistUserOptionEnum":
|
||||
case "PlaylistUserOptionBool":
|
||||
case "PlaylistUserOptionString":
|
||||
case "PlaylistUserOptionIntEnum":
|
||||
case "PlaylistUserOptionIntRange":
|
||||
case "PlaylistUserOptionColorEnum":
|
||||
case "PlaylistUserOptionFloatEnum":
|
||||
case "PlaylistUserOptionFloatRange":
|
||||
case "PlaylistUserTintedIconIntEnum":
|
||||
case "PlaylistUserOptionPrimaryAsset":
|
||||
case "PlaylistUserOptionCollisionProfileEnum":
|
||||
creator = new BaseUserControl(_object.Value, _style);
|
||||
return true;
|
||||
// PandaGame
|
||||
case "CharacterData":
|
||||
creator = new BaseFighter(_object.Value, _style);
|
||||
return true;
|
||||
case "PerkGroup":
|
||||
creator = new BasePerkGroup(_object.Value, _style);
|
||||
return true;
|
||||
case "StatTrackingBundleData":
|
||||
case "HydraSyncedDataAsset":
|
||||
case "AnnouncerPackData":
|
||||
case "CharacterGiftData":
|
||||
case "ProfileIconData":
|
||||
case "RingOutVfxData":
|
||||
case "BannerData":
|
||||
case "EmoteData":
|
||||
case "TauntData":
|
||||
case "SkinData":
|
||||
case "PerkData":
|
||||
creator = new BasePandaIcon(_object.Value, _style);
|
||||
return true;
|
||||
case "QuestData":
|
||||
creator = new Bases.MV.BaseQuest(_object.Value, _style);
|
||||
return true;
|
||||
default:
|
||||
creator = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => $"{_exportType} | {_style}";
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_object = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using FModel.Extensions;
|
||||
|
||||
namespace FModel;
|
||||
|
||||
|
|
@ -65,16 +66,6 @@ public enum ELoadingMode
|
|||
AllButPatched,
|
||||
}
|
||||
|
||||
// public enum EUpdateMode
|
||||
// {
|
||||
// [Description("Stable")]
|
||||
// Stable,
|
||||
// [Description("Beta")]
|
||||
// Beta,
|
||||
// [Description("QA Testing")]
|
||||
// Qa
|
||||
// }
|
||||
|
||||
public enum ECompressedAudio
|
||||
{
|
||||
[Description("Play the decompressed data")]
|
||||
|
|
@ -113,7 +104,56 @@ public enum EBulkType
|
|||
Properties = 1 << 1,
|
||||
Textures = 1 << 2,
|
||||
Meshes = 1 << 3,
|
||||
Skeletons = 1 << 4,
|
||||
Animations = 1 << 5,
|
||||
Audio = 1 << 6
|
||||
Animations = 1 << 4,
|
||||
Audio = 1 << 5,
|
||||
Code = 1 << 6,
|
||||
}
|
||||
|
||||
public enum EAssetCategory : uint
|
||||
{
|
||||
All = AssetCategoryExtensions.CategoryBase + (0 << 16),
|
||||
Blueprints = AssetCategoryExtensions.CategoryBase + (1 << 16),
|
||||
BlueprintGeneratedClass = Blueprints + 1,
|
||||
WidgetBlueprintGeneratedClass = Blueprints + 2,
|
||||
AnimBlueprintGeneratedClass = Blueprints + 3,
|
||||
RigVMBlueprintGeneratedClass = Blueprints + 4,
|
||||
UserDefinedEnum = Blueprints + 5,
|
||||
UserDefinedStruct = Blueprints + 6,
|
||||
//Metadata
|
||||
Blueprint = Blueprints + 8,
|
||||
CookedMetaData = Blueprints + 9,
|
||||
Mesh = AssetCategoryExtensions.CategoryBase + (2 << 16),
|
||||
StaticMesh = Mesh + 1,
|
||||
SkeletalMesh = Mesh + 2,
|
||||
CustomizableObject = Mesh + 3,
|
||||
NaniteDisplacedMesh = Mesh + 4,
|
||||
Texture = AssetCategoryExtensions.CategoryBase + (3 << 16),
|
||||
Materials = AssetCategoryExtensions.CategoryBase + (4 << 16),
|
||||
Material = Materials + 1,
|
||||
MaterialEditorData = Materials + 2,
|
||||
MaterialFunction = Materials + 3,
|
||||
MaterialFunctionEditorData = Materials + 4,
|
||||
MaterialParameterCollection = Materials + 5,
|
||||
Animation = AssetCategoryExtensions.CategoryBase + (5 << 16),
|
||||
Skeleton = Animation + 1,
|
||||
Rig = Animation + 2,
|
||||
Level = AssetCategoryExtensions.CategoryBase + (6 << 16),
|
||||
World = Level + 1,
|
||||
BuildData = Level + 2,
|
||||
LevelSequence = Level + 3,
|
||||
Foliage = Level + 4,
|
||||
Data = AssetCategoryExtensions.CategoryBase + (7 << 16),
|
||||
ItemDefinitionBase = Data + 1,
|
||||
CurveBase = Data + 2,
|
||||
PhysicsAsset = Data + 3,
|
||||
ObjectRedirector = Data + 4,
|
||||
PhysicalMaterial = Data + 5,
|
||||
ByteCode = Data + 6,
|
||||
Media = AssetCategoryExtensions.CategoryBase + (8 << 16),
|
||||
Audio = Media + 1,
|
||||
Video = Media + 2,
|
||||
Font = Media + 3,
|
||||
SoundBank = Media + 4,
|
||||
AudioEvent = Media + 5,
|
||||
Particle = AssetCategoryExtensions.CategoryBase + (9 << 16),
|
||||
}
|
||||
|
|
|
|||
30
FModel/Extensions/AssetCategoryExtensions.cs
Normal file
30
FModel/Extensions/AssetCategoryExtensions.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public static class AssetCategoryExtensions
|
||||
{
|
||||
public const uint CategoryBase = 0x00010000;
|
||||
|
||||
public static EAssetCategory GetBaseCategory(this EAssetCategory category)
|
||||
{
|
||||
return (EAssetCategory) ((uint) category & 0xFFFF0000);
|
||||
}
|
||||
|
||||
public static bool IsOfCategory(this EAssetCategory item, EAssetCategory category)
|
||||
{
|
||||
return item.GetBaseCategory() == category.GetBaseCategory();
|
||||
}
|
||||
|
||||
public static bool IsBaseCategory(this EAssetCategory category)
|
||||
{
|
||||
return category == category.GetBaseCategory();
|
||||
}
|
||||
|
||||
public static IEnumerable<EAssetCategory> GetBaseCategories()
|
||||
{
|
||||
return Enum.GetValues<EAssetCategory>().Where(c => c.IsBaseCategory());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml;
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using CUE4Parse.FileProvider;
|
||||
using CUE4Parse.FileProvider.Objects;
|
||||
using CUE4Parse.UE4.Assets;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using SkiaSharp;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
|
|
|||
|
|
@ -166,8 +166,9 @@
|
|||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.88.9" />
|
||||
<PackageReference Include="SkiaSharp.Svg" Version="1.60.0" />
|
||||
<PackageReference Include="Svg.Skia" Version="3.2.1" />
|
||||
<PackageReference Include="Twizzle.ImGui-Bundle.NET" Version="1.91.5.2" />
|
||||
<PackageReference Include="VirtualizingWrapPanel" Version="2.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using HarfBuzzSharp;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace FModel.Framework;
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class FStatus : ViewModel
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,26 +1,22 @@
|
|||
using System;
|
||||
using System;
|
||||
using CUE4Parse.Compression;
|
||||
using CUE4Parse.FileProvider.Objects;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Readers;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class FakeGameFile : GameFile
|
||||
public class FakeGameFile(string path) : GameFile(path, 0)
|
||||
{
|
||||
public FakeGameFile(string path) : base(path, 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
public override CompressionMethod CompressionMethod => CompressionMethod.None;
|
||||
|
||||
public override byte[] Read()
|
||||
public override byte[] Read(FByteBulkDataHeader? header = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override FArchive CreateReader()
|
||||
public override FArchive CreateReader(FByteBulkDataHeader? header = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text;
|
||||
using System.Text;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using RestSharp;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Serilog.Core;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public static class Helper
|
|||
else
|
||||
{
|
||||
var w = GetOpenedWindow<T>(windowName);
|
||||
if (windowName == "Search View") w.WindowState = WindowState.Normal;
|
||||
if (windowName == "Search For Packages") w.WindowState = WindowState.Normal;
|
||||
w.Focus();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:FModel"
|
||||
xmlns:controls="clr-namespace:FModel.Views.Resources.Controls"
|
||||
xmlns:inputs="clr-namespace:FModel.Views.Resources.Controls.Inputs"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:settings="clr-namespace:FModel.Settings"
|
||||
xmlns:services="clr-namespace:FModel.Services"
|
||||
|
|
@ -10,8 +11,18 @@
|
|||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" Closing="OnClosing" Loaded="OnLoaded" PreviewKeyDown="OnWindowKeyDown"
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.85'}"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.75'}">
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.95'}"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.90'}">
|
||||
<Window.TaskbarItemInfo>
|
||||
<TaskbarItemInfo ProgressValue="1.0">
|
||||
<TaskbarItemInfo.ProgressState>
|
||||
<MultiBinding Converter="{converters:StatusToTaskbarStateConverter}">
|
||||
<Binding Path="Status.Kind" />
|
||||
<Binding Path="IsActive" RelativeSource="{RelativeSource AncestorType=Window}" />
|
||||
</MultiBinding>
|
||||
</TaskbarItemInfo.ProgressState>
|
||||
</TaskbarItemInfo>
|
||||
</Window.TaskbarItemInfo>
|
||||
<adonisControls:AdonisWindow.Style>
|
||||
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
|
||||
<Setter Property="Title" Value="{Binding DataContext.InitialWindowTitle, RelativeSource={RelativeSource Self}}" />
|
||||
|
|
@ -30,158 +41,179 @@
|
|||
</Style.Triggers>
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Views/Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="{adonisUi:Space 1}" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Menu Grid.Row="0">
|
||||
<MenuItem Header="Directory">
|
||||
<MenuItem Header="Selector" Command="{Binding MenuCommand}" CommandParameter="Directory_Selector">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource DirectoryIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Menu Grid.Column="0">
|
||||
<MenuItem Header="Directory">
|
||||
<MenuItem Header="Selector" Command="{Binding MenuCommand}" CommandParameter="Directory_Selector">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource DirectoryIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="AES" Command="{Binding MenuCommand}" CommandParameter="Directory_AES" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource KeyIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Backup" Command="{Binding MenuCommand}" CommandParameter="Directory_Backup" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource BackupIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Archives Info" Command="{Binding MenuCommand}" CommandParameter="Directory_ArchivesInfo" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource InfoIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="AES" Command="{Binding MenuCommand}" CommandParameter="Directory_AES" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource KeyIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
<MenuItem Header="Packages">
|
||||
<MenuItem Header="Search" IsEnabled="{Binding Status.IsReady}" InputGestureText="Ctrl+Shift+F" Click="OnSearchViewClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SearchIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="References" IsEnabled="{Binding Status.IsReady}" InputGestureText="Ctrl+Shift+R" Click="OnRefViewClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SearchIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Favorite Directories" ItemsSource="{Binding CustomDirectories.Directories}" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource DirectoriesIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Backup" Command="{Binding MenuCommand}" CommandParameter="Directory_Backup" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource BackupIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
<MenuItem Header="Views">
|
||||
<MenuItem Header="3D Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_3dViewer">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource MeshIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Audio Player" Command="{Binding MenuCommand}" CommandParameter="Views_AudioPlayer">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Image Merger" Command="{Binding MenuCommand}" CommandParameter="Views_ImageMerger">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource ImageMergerIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Archives Info" Command="{Binding MenuCommand}" CommandParameter="Directory_ArchivesInfo" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource InfoIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
<MenuItem Header="Settings" Command="{Binding MenuCommand}" CommandParameter="Settings" />
|
||||
<MenuItem Header="Help" >
|
||||
<MenuItem Header="Donate" Command="{Binding MenuCommand}" CommandParameter="Help_Donate">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource GiftIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Releases" Command="{Binding MenuCommand}" CommandParameter="Help_Releases">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource GitHubIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Bugs Report" Command="{Binding MenuCommand}" CommandParameter="Help_BugsReport">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource BugIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Discord Server" Command="{Binding MenuCommand}" CommandParameter="Help_Discord">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource DiscordIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="About FModel" Command="{Binding MenuCommand}" CommandParameter="Help_About">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource InfoIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Packages">
|
||||
<MenuItem Header="Search" IsEnabled="{Binding Status.IsReady}" InputGestureText="Ctrl+Shift+F" Click="OnSearchViewClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SearchIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Favorite Directories" ItemsSource="{Binding CustomDirectories.Directories}" IsEnabled="{Binding Status.IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource DirectoriesIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Views">
|
||||
<MenuItem Header="3D Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_3dViewer">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource MeshIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Audio Player" Command="{Binding MenuCommand}" CommandParameter="Views_AudioPlayer">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Image Merger" Command="{Binding MenuCommand}" CommandParameter="Views_ImageMerger">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource ImageMergerIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Settings" Command="{Binding MenuCommand}" CommandParameter="Settings" />
|
||||
<MenuItem Header="Help" >
|
||||
<MenuItem Header="Donate" Command="{Binding MenuCommand}" CommandParameter="Help_Donate">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource GiftIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Releases" Command="{Binding MenuCommand}" CommandParameter="Help_Releases">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource GitHubIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Bugs Report" Command="{Binding MenuCommand}" CommandParameter="Help_BugsReport">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource BugIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Discord Server" Command="{Binding MenuCommand}" CommandParameter="Help_Discord">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource DiscordIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="About FModel" Command="{Binding MenuCommand}" CommandParameter="Help_About">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource InfoIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Menu>
|
||||
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="Preview New Explorer System" VerticalAlignment="Center" />
|
||||
<CheckBox Grid.Column="1" Margin="5 2 5 0" Unchecked="FeaturePreviewOnUnchecked" KeyboardNavigation.TabNavigation="None" KeyboardNavigation.ControlTabNavigation="None"
|
||||
IsChecked="{Binding FeaturePreviewNewAssetExplorer, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid x:Name="RootGrid" Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
|
|
@ -191,8 +223,8 @@
|
|||
</Grid.ColumnDefinitions>
|
||||
|
||||
<GroupBox Grid.Column="0" adonisExtensions:LayerExtension.Layer="2"
|
||||
Padding="{adonisUi:Space 0}" Background="Transparent">
|
||||
<TabControl x:Name="LeftTabControl" SelectionChanged="OnTabItemChange">
|
||||
Padding="0" Background="Transparent">
|
||||
<TabControl x:Name="LeftTabControl" SelectionChanged="OnTabItemChange" SelectedIndex="{Binding SelectedLeftTabIndex, Mode=TwoWay}">
|
||||
<TabItem Style="{StaticResource TabItemFillSpace}" Header="Archives">
|
||||
<DockPanel>
|
||||
<Grid DockPanel.Dock="Top">
|
||||
|
|
@ -257,7 +289,8 @@
|
|||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<TabItem Style="{StaticResource TabItemFillSpace}" Header="Folders">
|
||||
<TabItem Style="{StaticResource TabItemFillSpace}"
|
||||
Header="Folders">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
|
@ -315,94 +348,12 @@
|
|||
</StackPanel>
|
||||
</Grid>
|
||||
<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 Packages" 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> -->
|
||||
<!-- <Separator /> -->
|
||||
<MenuItem Header="Export Folder's Packages Raw Data (.uasset)" 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 Packages Properties (.json)" 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>
|
||||
<MenuItem Header="Save Folder's Packages Textures" Click="OnFolderTextureClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource TextureIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Packages Models" Click="OnFolderModelClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ModelIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Packages Animations" Click="OnFolderAnimationClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AnimationIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Packages Audio" Click="OnFolderAudioClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Favorite Directory" Click="OnFavoriteDirectoryClick">
|
||||
<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 Grid.Row="2"
|
||||
x:Name="AssetsFolderName"
|
||||
Style="{StaticResource AssetsFolderTreeView}"
|
||||
SelectedItemChanged="OnAssetsTreeSelectedItemChanged"
|
||||
PreviewKeyDown="OnFoldersPreviewKeyDown"
|
||||
PreviewMouseDoubleClick="OnAssetsTreeMouseDoubleClick">
|
||||
</TreeView>
|
||||
<Separator Grid.Row="3" Style="{StaticResource CustomSeparator}" Tag="INFORMATION" />
|
||||
<StackPanel Grid.Row="4" Orientation="Vertical" Margin="0 0 0 5">
|
||||
|
|
@ -437,32 +388,11 @@
|
|||
Header="{Binding SelectedItem.AssetsList.Assets.Count, FallbackValue=0, ElementName=AssetsFolderName}"
|
||||
HeaderStringFormat="{}{0} Packages">
|
||||
<DockPanel>
|
||||
<Grid DockPanel.Dock="Top" ZIndex="0">
|
||||
<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" x:Name="AssetsSearchName" AcceptsTab="False" AcceptsReturn="False"
|
||||
Padding="25 0 0 0" HorizontalAlignment="Stretch" TextChanged="OnFilterTextChanged"
|
||||
adonisExtensions:WatermarkExtension.Watermark="Search by name..." />
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button ToolTip="Clear Search Filter" Padding="5" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}" Click="OnDeleteSearchClick">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource BackspaceIcon}"/>
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<inputs:SearchTextBox DockPanel.Dock="Top" x:Name="AssetsSearchTextBox"
|
||||
Text="{Binding SelectedItem.SearchText, ElementName=AssetsFolderName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
ClearButtonClick="OnClearFilterClick" />
|
||||
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
|
@ -471,217 +401,15 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<controls:Breadcrumb Grid.Row="0" MaxWidth="{Binding ActualWidth, ElementName=AssetsSearchName}" HorizontalAlignment="Left" Margin="0 5 0 5"
|
||||
<controls:Breadcrumb Grid.Row="0" HorizontalAlignment="Left" Margin="0 5 0 5"
|
||||
MaxWidth="{Binding ActualWidth, ElementName=AssetsSearchTextBox}"
|
||||
DataContext="{Binding SelectedItem.PathAtThisPoint, ElementName=AssetsFolderName, FallbackValue='No/Directory/Detected/In/Folder'}"/>
|
||||
|
||||
<ListBox Grid.Row="1" x:Name="AssetsListName" Style="{StaticResource AssetsListBox}" PreviewMouseDoubleClick="OnAssetsListMouseDoubleClick" PreviewKeyDown="OnPreviewKeyDown">
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
|
||||
<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">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ExtractIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Show Metadata" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Show_Metadata" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource InfoIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Decompile Blueprint" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Decompile" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource InfoIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
<MenuItem.Style>
|
||||
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ShowDecompileOption, Source={x:Static settings:UserSettings.Default}}" Value="False">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</MenuItem.Style>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.Header>
|
||||
<TextBlock
|
||||
Text="{Binding DataContext.SelectedItem.Extension,
|
||||
FallbackValue='Export Raw Data',
|
||||
StringFormat='Export Raw Data (.{0})',
|
||||
RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
</MenuItem.Header>
|
||||
<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">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ExportIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Properties (.json)" 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">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SaveIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Texture" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Textures" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource TextureIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Model" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Models" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource ModelIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Animation" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Animations" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AnimationIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Audio" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Audio" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Copy">
|
||||
<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 Header="Package Path" Command="{Binding DataContext.CopyCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="File_Path" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Package Name" Command="{Binding DataContext.CopyCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="File_Name" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Directory Path" Command="{Binding DataContext.CopyCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Directory_Path" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Package Path w/o Extension" Command="{Binding DataContext.CopyCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="File_Path_No_Extension" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Package Name w/o Extension" Command="{Binding DataContext.CopyCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="File_Name_No_Extension" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</ListBox.ContextMenu>
|
||||
</ListBox>
|
||||
<ListBox Grid.Row="1" x:Name="AssetsListName"
|
||||
Style="{StaticResource AssetsListBox}"
|
||||
PreviewMouseDoubleClick="OnAssetsListMouseDoubleClick"
|
||||
PreviewKeyDown="OnPreviewKeyDown" />
|
||||
|
||||
<Separator Grid.Row="2" Style="{StaticResource CustomSeparator}" Tag="INFORMATION" />
|
||||
<StackPanel Grid.Row="3" Orientation="Vertical" Margin="0 0 0 5">
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
|
|
@ -697,15 +425,15 @@
|
|||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding SelectedItem.Offset, ElementName=AssetsListName, FallbackValue=0, StringFormat='{}0x{0:X}'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding SelectedItem.Asset.Offset, ElementName=AssetsListName, FallbackValue=0, StringFormat='{}0x{0:X}'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="Offset" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding SelectedItem.Size, ElementName=AssetsListName, FallbackValue=0, Converter={x:Static converters:SizeToStringConverter.Instance}}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding SelectedItem.Asset.Size, ElementName=AssetsListName, FallbackValue=0, Converter={x:Static converters:SizeToStringConverter.Instance}}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="Size" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding SelectedItem.CompressionMethod, ElementName=AssetsListName, FallbackValue='Unknown'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding SelectedItem.Asset.CompressionMethod, ElementName=AssetsListName, FallbackValue='Unknown'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="Compression Method" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding SelectedItem.IsEncrypted, ElementName=AssetsListName, FallbackValue='False'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding SelectedItem.Asset.IsEncrypted, ElementName=AssetsListName, FallbackValue='False'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Text="Is Encrypted" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="{Binding SelectedItem.Vfs.Name, ElementName=AssetsListName, FallbackValue='None'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="{Binding SelectedItem.Asset.Vfs.Name, ElementName=AssetsListName, FallbackValue='None'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="1" Text="Included In Archive" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
|
@ -720,14 +448,130 @@
|
|||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer0BackgroundBrush}}" />
|
||||
|
||||
<GroupBox Grid.Column="2" adonisExtensions:LayerExtension.Layer="2"
|
||||
Padding="{adonisUi:Space 0}" Background="Transparent">
|
||||
Padding="0" Background="Transparent">
|
||||
<Grid Margin="0 0 3 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TabControl Grid.Row="0" x:Name="TabControlName" Style="{StaticResource GameFilesTabControl}" />
|
||||
<Grid Grid.Row="0">
|
||||
<Border BorderThickness="1" Padding="10"
|
||||
BorderBrush="{DynamicResource {x:Static adonisUi:Brushes.Layer3BorderBrush}}"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}"
|
||||
Visibility="{Binding IsAssetsExplorerVisible, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" adonisExtensions:LayerExtension.Layer="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="Auto" MinWidth="150" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<inputs:SearchTextBox x:Name="AssetsExplorerSearch" Grid.Column="0"
|
||||
Text="{Binding SelectedItem.SearchText, ElementName=AssetsFolderName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
ClearButtonClick="OnClearFilterClick" />
|
||||
|
||||
<ComboBox x:Name="CategoriesSelector" Grid.Column="2"
|
||||
ItemsSource="{Binding Categories}"
|
||||
SelectedItem="{Binding SelectedItem.SelectedCategory, ElementName=AssetsFolderName, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
|
||||
<controls:Breadcrumb Grid.Row="1" Margin="0 5 0 0"
|
||||
HorizontalAlignment="Left"
|
||||
DataContext="{Binding SelectedItem.PathAtThisPoint, ElementName=AssetsFolderName}" />
|
||||
|
||||
<ListBox x:Name="AssetsExplorer" Grid.Row="2"
|
||||
ItemsSource="{Binding SelectedItem.CombinedEntries, ElementName=AssetsFolderName, IsAsync=True}"
|
||||
Style="{StaticResource TiledExplorer}"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}"
|
||||
PreviewKeyDown="OnPreviewKeyDown" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<TabControl x:Name="TabControlName"
|
||||
Style="{StaticResource GameFilesTabControl}"
|
||||
Visibility="{Binding IsAssetsExplorerVisible, Converter={x:Static converters:InvertBoolToVisibilityConverter.Instance}, ConverterParameter=Hidden}" />
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="0"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="12,12,20,12"
|
||||
Opacity="0.5"
|
||||
Visibility="{Binding FeaturePreviewNewAssetExplorer, Source={x:Static settings:UserSettings.Default}, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer2BackgroundBrush}}"
|
||||
CornerRadius="8"
|
||||
Padding="4"
|
||||
Effect="{DynamicResource ShadowEffect}">
|
||||
<Border.Triggers>
|
||||
<EventTrigger RoutedEvent="MouseEnter">
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
<EventTrigger RoutedEvent="MouseLeave">
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
||||
To="0.5"
|
||||
Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
</Border.Triggers>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<ToggleButton Width="32"
|
||||
Height="32"
|
||||
Cursor="Hand"
|
||||
Margin="0,0,2,0"
|
||||
IsChecked="{Binding IsAssetsExplorerVisible, Mode=TwoWay}"
|
||||
Style="{StaticResource AssetsExplorerToggleButtonStyle}" />
|
||||
<ToggleButton Width="32"
|
||||
Height="32"
|
||||
Cursor="Hand"
|
||||
Checked="OnPreviewTexturesToggled"
|
||||
Focusable="False"
|
||||
IsChecked="{Binding PreviewTexturesAssetExplorer, Source={x:Static settings:UserSettings.Default}, Mode=TwoWay}">
|
||||
<ToggleButton.Style>
|
||||
<Style TargetType="ToggleButton"
|
||||
BasedOn="{StaticResource ModernToggleButtonStyle}">
|
||||
<Setter Property="ToolTip"
|
||||
Value="Preview Textures (OFF)" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsChecked"
|
||||
Value="True">
|
||||
<Setter Property="Background"
|
||||
Value="MediumPurple" />
|
||||
<Setter Property="ToolTip"
|
||||
Value="Preview Textures (ON)" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ToggleButton.Style>
|
||||
<Viewbox Width="16"
|
||||
Height="16">
|
||||
<Canvas Width="24"
|
||||
Height="24">
|
||||
<Path Data="{StaticResource TextureIconAlt}"
|
||||
Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Expander Grid.Row="1" Margin="0 5 0 5" ExpandDirection="Down"
|
||||
IsExpanded="{Binding IsLoggerExpanded, Source={x:Static settings:UserSettings.Default}}">
|
||||
<Grid>
|
||||
|
|
@ -740,7 +584,7 @@
|
|||
<controls:CustomRichTextBox Grid.Column="0" Grid.ColumnSpan="3" x:Name="LogRtbName" Style="{StaticResource CustomRichTextBox}" />
|
||||
<StackPanel Grid.Column="2" Orientation="Vertical" VerticalAlignment="Bottom" Margin="-5 -5 5 5">
|
||||
<Button Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}" ToolTip="Open Output Folder" Padding="0,4,0,4"
|
||||
Command="{Binding MenuCommand}" CommandParameter="ToolBox_Open_Output_Directory">
|
||||
Command="{Binding MenuCommand}" CommandParameter="ToolBox_Open_Output_Directory" Focusable="False">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource FolderIcon}" />
|
||||
|
|
@ -748,7 +592,7 @@
|
|||
</Viewbox>
|
||||
</Button>
|
||||
<Button Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}" ToolTip="Clear Logs" Padding="0,4,0,4"
|
||||
Command="{Binding MenuCommand}" CommandParameter="ToolBox_Clear_Logs">
|
||||
Command="{Binding MenuCommand}" CommandParameter="ToolBox_Clear_Logs" Focusable="False">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource TrashIcon}"/>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using AdonisUI.Controls;
|
||||
using CUE4Parse.FileProvider.Objects;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
|
|
@ -30,14 +29,54 @@ public partial class MainWindow
|
|||
{
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("ReloadMappings", typeof(MainWindow), new InputGestureCollection { new KeyGesture(Key.F12) }), OnMappingsReload));
|
||||
CommandBindings.Add(new CommandBinding(ApplicationCommands.Find, (_, _) => OnOpenAvalonFinder()));
|
||||
CommandBindings.Add(new CommandBinding(NavigationCommands.BrowseBack, (_, _) =>
|
||||
{
|
||||
if (UserSettings.Default.FeaturePreviewNewAssetExplorer && !_applicationView.IsAssetsExplorerVisible)
|
||||
{
|
||||
// back browsing the json view will reopen the assets explorer
|
||||
_applicationView.IsAssetsExplorerVisible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (LeftTabControl.SelectedIndex == 2)
|
||||
{
|
||||
LeftTabControl.SelectedIndex = 1;
|
||||
}
|
||||
else if (LeftTabControl.SelectedIndex == 1 && AssetsFolderName.SelectedItem is TreeItem { Parent: TreeItem parent })
|
||||
{
|
||||
AssetsFolderName.Focus();
|
||||
parent.IsSelected = true;
|
||||
}
|
||||
}));
|
||||
|
||||
DataContext = _applicationView;
|
||||
InitializeComponent();
|
||||
|
||||
AssetsExplorer.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
|
||||
AssetsListName.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
|
||||
AssetsExplorer.SelectionChanged += (_, e) => SyncSelection(AssetsListName, e);
|
||||
AssetsListName.SelectionChanged += (_, e) => SyncSelection(AssetsExplorer, e);
|
||||
|
||||
FLogger.Logger = LogRtbName;
|
||||
YesWeCats = this;
|
||||
}
|
||||
|
||||
// Hack to sync selection between packages tab and explorer
|
||||
private void SyncSelection(ListBox target, SelectionChangedEventArgs e)
|
||||
{
|
||||
foreach (var added in e.AddedItems.OfType<GameFileViewModel>())
|
||||
{
|
||||
if (!target.SelectedItems.Contains(added))
|
||||
target.SelectedItems.Add(added);
|
||||
}
|
||||
|
||||
foreach (var removed in e.RemovedItems.OfType<GameFileViewModel>())
|
||||
{
|
||||
if (target.SelectedItems.Contains(removed))
|
||||
target.SelectedItems.Remove(removed);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClosing(object sender, CancelEventArgs e)
|
||||
{
|
||||
_discordHandler.Dispose();
|
||||
|
|
@ -61,14 +100,18 @@ public partial class MainWindow
|
|||
break;
|
||||
}
|
||||
|
||||
await ApplicationViewModel.InitOodle();
|
||||
await ApplicationViewModel.InitZlib();
|
||||
await Task.WhenAll(
|
||||
ApplicationViewModel.InitOodle(),
|
||||
ApplicationViewModel.InitZlib()
|
||||
);
|
||||
|
||||
await _applicationView.CUE4Parse.Initialize();
|
||||
await _applicationView.AesManager.InitAes();
|
||||
await _applicationView.UpdateProvider(true);
|
||||
#if !DEBUG
|
||||
await _applicationView.CUE4Parse.InitInformation();
|
||||
#endif
|
||||
|
||||
await Task.WhenAll(
|
||||
_applicationView.CUE4Parse.VerifyConsoleVariables(),
|
||||
_applicationView.CUE4Parse.VerifyOnDemandArchives(),
|
||||
|
|
@ -107,10 +150,33 @@ public partial class MainWindow
|
|||
}
|
||||
else if (_applicationView.Status.IsReady && e.Key == Key.F && Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
|
||||
OnSearchViewClick(null, null);
|
||||
else if (e.Key == Key.Left && _applicationView.CUE4Parse.TabControl.SelectedTab.HasImage)
|
||||
else if (_applicationView.Status.IsReady && e.Key == Key.R && Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
|
||||
OnRefViewClick(null, null);
|
||||
else if (e.Key == Key.F3)
|
||||
OnOpenAvalonFinder();
|
||||
else if (e.Key == Key.Left && !_applicationView.IsAssetsExplorerVisible && _applicationView.CUE4Parse.TabControl.SelectedTab is { HasImage: true })
|
||||
_applicationView.CUE4Parse.TabControl.SelectedTab.GoPreviousImage();
|
||||
else if (e.Key == Key.Right && _applicationView.CUE4Parse.TabControl.SelectedTab.HasImage)
|
||||
else if (e.Key == Key.Right && !_applicationView.IsAssetsExplorerVisible && _applicationView.CUE4Parse.TabControl.SelectedTab is { HasImage: true })
|
||||
_applicationView.CUE4Parse.TabControl.SelectedTab.GoNextImage();
|
||||
else if (_applicationView.Status.IsReady && _applicationView.IsAssetsExplorerVisible && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
|
||||
{
|
||||
CategoriesSelector.SelectedIndex = e.SystemKey switch
|
||||
{
|
||||
Key.D0 or Key.NumPad0 => 0,
|
||||
Key.D1 or Key.NumPad1 => 1,
|
||||
Key.D2 or Key.NumPad2 => 2,
|
||||
Key.D3 or Key.NumPad3 => 3,
|
||||
Key.D4 or Key.NumPad4 => 4,
|
||||
Key.D5 or Key.NumPad5 => 5,
|
||||
Key.D6 or Key.NumPad6 => 6,
|
||||
Key.D7 or Key.NumPad7 => 7,
|
||||
Key.D8 or Key.NumPad8 => 8,
|
||||
Key.D9 or Key.NumPad9 => 9,
|
||||
_ => CategoriesSelector.SelectedIndex
|
||||
};
|
||||
}
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.FeaturePreviewNewAssetExplorer && UserSettings.Default.SwitchAssetExplorer.IsTriggered(e.Key))
|
||||
_applicationView.IsAssetsExplorerVisible = !_applicationView.IsAssetsExplorerVisible;
|
||||
else if (UserSettings.Default.AssetAddTab.IsTriggered(e.Key))
|
||||
_applicationView.CUE4Parse.TabControl.AddTab();
|
||||
else if (UserSettings.Default.AssetRemoveTab.IsTriggered(e.Key))
|
||||
|
|
@ -119,15 +185,22 @@ public partial class MainWindow
|
|||
_applicationView.CUE4Parse.TabControl.GoLeftTab();
|
||||
else if (UserSettings.Default.AssetRightTab.IsTriggered(e.Key))
|
||||
_applicationView.CUE4Parse.TabControl.GoRightTab();
|
||||
else if (UserSettings.Default.DirLeftTab.IsTriggered(e.Key) && LeftTabControl.SelectedIndex > 0)
|
||||
LeftTabControl.SelectedIndex--;
|
||||
else if (UserSettings.Default.DirRightTab.IsTriggered(e.Key) && LeftTabControl.SelectedIndex < LeftTabControl.Items.Count - 1)
|
||||
LeftTabControl.SelectedIndex++;
|
||||
else if (UserSettings.Default.DirLeftTab.IsTriggered(e.Key) && _applicationView.SelectedLeftTabIndex > 0)
|
||||
_applicationView.SelectedLeftTabIndex--;
|
||||
else if (UserSettings.Default.DirRightTab.IsTriggered(e.Key) && _applicationView.SelectedLeftTabIndex < LeftTabControl.Items.Count - 1)
|
||||
_applicationView.SelectedLeftTabIndex++;
|
||||
}
|
||||
|
||||
private void OnSearchViewClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Helper.OpenWindow<AdonisWindow>("Search View", () => new SearchView().Show());
|
||||
var searchView = Helper.GetWindow<SearchView>("Search For Packages", () => new SearchView().Show());
|
||||
searchView.FocusTab(ESearchViewTab.SearchView);
|
||||
}
|
||||
|
||||
private void OnRefViewClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var searchView = Helper.GetWindow<SearchView>("Search For Packages", () => new SearchView().Show());
|
||||
searchView.FocusTab(ESearchViewTab.RefView);
|
||||
}
|
||||
|
||||
private void OnTabItemChange(object sender, SelectionChangedEventArgs e)
|
||||
|
|
@ -135,7 +208,18 @@ public partial class MainWindow
|
|||
if (e.OriginalSource is not TabControl tabControl)
|
||||
return;
|
||||
|
||||
(tabControl.SelectedItem as System.Windows.Controls.TabItem)?.Focus();
|
||||
switch (tabControl.SelectedIndex)
|
||||
{
|
||||
case 0:
|
||||
DirectoryFilesListBox.Focus();
|
||||
break;
|
||||
case 1:
|
||||
AssetsFolderName.Focus();
|
||||
break;
|
||||
case 2:
|
||||
AssetsListName.Focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnMappingsReload(object sender, ExecutedRoutedEventArgs e)
|
||||
|
|
@ -145,142 +229,79 @@ public partial class MainWindow
|
|||
|
||||
private void OnOpenAvalonFinder()
|
||||
{
|
||||
_applicationView.CUE4Parse.TabControl.SelectedTab.HasSearchOpen = true;
|
||||
AvalonEditor.YesWeSearch.Focus();
|
||||
AvalonEditor.YesWeSearch.SelectAll();
|
||||
if (_applicationView.IsAssetsExplorerVisible)
|
||||
{
|
||||
AssetsExplorerSearch.TextBox.Focus();
|
||||
AssetsExplorerSearch.TextBox.SelectAll();
|
||||
}
|
||||
else if (_applicationView.CUE4Parse.TabControl.SelectedTab is { } tab)
|
||||
{
|
||||
tab.HasSearchOpen = true;
|
||||
AvalonEditor.YesWeSearch.Focus();
|
||||
AvalonEditor.YesWeSearch.SelectAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAssetsTreeMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not TreeView { SelectedItem: TreeItem treeItem } || treeItem.Folders.Count > 0) return;
|
||||
|
||||
LeftTabControl.SelectedIndex++;
|
||||
_applicationView.SelectedLeftTabIndex++;
|
||||
}
|
||||
|
||||
private void OnPreviewTexturesToggled(object sender, RoutedEventArgs e) => ItemContainerGenerator_StatusChanged(AssetsExplorer.ItemContainerGenerator, EventArgs.Empty);
|
||||
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is not ItemContainerGenerator { Status: GeneratorStatus.ContainersGenerated } generator)
|
||||
return;
|
||||
|
||||
var foundVisibleItem = false;
|
||||
var itemCount = generator.Items.Count;
|
||||
|
||||
for (var i = 0; i < itemCount; i++)
|
||||
{
|
||||
var container = generator.ContainerFromIndex(i);
|
||||
if (container == null)
|
||||
{
|
||||
if (foundVisibleItem) break; // we're past the visible range already
|
||||
continue; // keep scrolling to find visible items
|
||||
}
|
||||
|
||||
if (container is FrameworkElement { IsVisible: true } && generator.Items[i] is GameFileViewModel file)
|
||||
{
|
||||
foundVisibleItem = true;
|
||||
file.OnIsVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAssetsTreeSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
if (sender is not TreeView { SelectedItem: TreeItem }) return;
|
||||
|
||||
_applicationView.IsAssetsExplorerVisible = true;
|
||||
_applicationView.SelectedLeftTabIndex = 1;
|
||||
}
|
||||
|
||||
private async void OnAssetsListMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not ListBox listBox) return;
|
||||
|
||||
var selectedItems = listBox.SelectedItems.Cast<GameFile>().ToList();
|
||||
var selectedItems = listBox.SelectedItems.OfType<GameFileViewModel>().Select(gvm => gvm.Asset).ToArray();
|
||||
if (selectedItems.Length == 0) return;
|
||||
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ExtractSelected(cancellationToken, selectedItems); });
|
||||
}
|
||||
|
||||
private async void OnFolderExtractClick(object sender, RoutedEventArgs e)
|
||||
private void OnClearFilterClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ExtractFolder(cancellationToken, folder); });
|
||||
folder.SearchText = string.Empty;
|
||||
folder.SelectedCategory = EAssetCategory.All;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFolderExportClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ExportFolder(cancellationToken, folder); });
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully exported ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.RawDataDirectory, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFolderSaveClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.SaveFolder(cancellationToken, folder); });
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.PropertiesDirectory, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFolderTextureClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.TextureFolder(cancellationToken, folder); });
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved textures from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.TextureDirectory, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFolderModelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ModelFolder(cancellationToken, folder); });
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved models from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.ModelDirectory, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFolderAnimationClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.AnimationFolder(cancellationToken, folder); });
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved animations from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.ModelDirectory, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFolderAudioClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.AudioFolder(cancellationToken, folder); });
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved audio from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.AudioDirectory, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFavoriteDirectoryClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is not TreeItem folder) return;
|
||||
|
||||
_applicationView.CustomDirectories.Add(new CustomDirectory(folder.Header, folder.PathAtThisPoint));
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
FLogger.Text($"Successfully saved '{folder.PathAtThisPoint}' as a new favorite directory", Constants.WHITE, true));
|
||||
}
|
||||
|
||||
private void OnCopyDirectoryPathClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is not TreeItem folder) return;
|
||||
Clipboard.SetText(folder.PathAtThisPoint);
|
||||
}
|
||||
|
||||
private void OnDeleteSearchClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AssetsSearchName.Text = string.Empty;
|
||||
AssetsListName.ScrollIntoView(AssetsListName.SelectedItem);
|
||||
}
|
||||
|
||||
private void OnFilterTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (sender is not TextBox textBox || AssetsFolderName.SelectedItem is not TreeItem folder)
|
||||
return;
|
||||
|
||||
var filters = textBox.Text.Trim().Split(' ');
|
||||
folder.AssetsList.AssetsView.Filter = o => { return o is GameFile entry && filters.All(x => entry.Name.Contains(x, StringComparison.OrdinalIgnoreCase)); };
|
||||
}
|
||||
|
||||
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!_applicationView.Status.IsReady || sender is not ListBox listBox) return;
|
||||
|
|
@ -290,14 +311,67 @@ public partial class MainWindow
|
|||
|
||||
private async void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (!_applicationView.Status.IsReady || sender is not ListBox listBox) return;
|
||||
if (!_applicationView.Status.IsReady || sender is not ListBox listBox)
|
||||
return;
|
||||
if (e.Key != Key.Enter)
|
||||
return;
|
||||
if (listBox.SelectedItem == null)
|
||||
return;
|
||||
|
||||
switch (e.Key)
|
||||
switch (listBox.SelectedItem)
|
||||
{
|
||||
case Key.Enter:
|
||||
var selectedItems = listBox.SelectedItems.Cast<GameFile>().ToList();
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ExtractSelected(cancellationToken, selectedItems); });
|
||||
case GameFileViewModel file:
|
||||
_applicationView.IsAssetsExplorerVisible = false;
|
||||
ApplicationService.ApplicationView.SelectedLeftTabIndex = 2;
|
||||
await _threadWorkerView.Begin(cancellationToken => _applicationView.CUE4Parse.ExtractSelected(cancellationToken, [file.Asset]));
|
||||
break;
|
||||
case TreeItem folder:
|
||||
ApplicationService.ApplicationView.SelectedLeftTabIndex = 1;
|
||||
|
||||
var parent = folder.Parent;
|
||||
while (parent != null)
|
||||
{
|
||||
parent.IsExpanded = true;
|
||||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
var childFolder = folder;
|
||||
while (childFolder.Folders.Count == 1 && childFolder.AssetsList.Assets.Count == 0)
|
||||
{
|
||||
childFolder.IsExpanded = true;
|
||||
childFolder = childFolder.Folders[0];
|
||||
}
|
||||
|
||||
childFolder.IsExpanded = true;
|
||||
childFolder.IsSelected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void FeaturePreviewOnUnchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_applicationView.IsAssetsExplorerVisible = false;
|
||||
}
|
||||
|
||||
private async void OnFoldersPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key != Key.Enter || sender is not TreeView treeView || treeView.SelectedItem is not TreeItem folder)
|
||||
return;
|
||||
|
||||
if ((folder.IsExpanded || folder.Folders.Count == 0) && folder.AssetsList.Assets.Count > 0)
|
||||
{
|
||||
_applicationView.SelectedLeftTabIndex++;
|
||||
return;
|
||||
}
|
||||
|
||||
var childFolder = folder;
|
||||
while (childFolder.Folders.Count == 1 && childFolder.AssetsList.Assets.Count == 0)
|
||||
{
|
||||
childFolder.IsExpanded = true;
|
||||
childFolder = childFolder.Folders[0];
|
||||
}
|
||||
|
||||
childFolder.IsExpanded = true;
|
||||
childFolder.IsSelected = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
FModel/Properties/Resources.Designer.cs
generated
2
FModel/Properties/Resources.Designer.cs
generated
|
|
@ -1,4 +1,4 @@
|
|||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Ce code a été généré par un outil.
|
||||
// Version du runtime :4.0.30319.42000
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using FModel.ViewModels;
|
||||
using FModel.ViewModels;
|
||||
|
||||
namespace FModel.Services
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using DiscordRPC;
|
||||
using FModel.Extensions;
|
||||
using FModel.Settings;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using FModel.Framework;
|
||||
|
||||
namespace FModel.Settings;
|
||||
|
|
|
|||
|
|
@ -1,109 +1,109 @@
|
|||
using System.Linq;
|
||||
using FModel.Framework;
|
||||
using FModel.ViewModels.ApiEndpoints;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace FModel.Settings;
|
||||
|
||||
public class EndpointSettings : ViewModel
|
||||
{
|
||||
public static EndpointSettings[] Default(string gameName)
|
||||
{
|
||||
switch (gameName)
|
||||
{
|
||||
case "Fortnite":
|
||||
case "Fortnite [LIVE]":
|
||||
return new EndpointSettings[]
|
||||
{
|
||||
new("https://uedb.dev/svc/api/v1/fortnite/aes", "$.['mainKey','dynamicKeys']"),
|
||||
new("https://uedb.dev/svc/api/v1/fortnite/mappings", "$.mappings.ZStandard")
|
||||
};
|
||||
default:
|
||||
return new EndpointSettings[] { new(), new() };
|
||||
}
|
||||
}
|
||||
|
||||
private string _url;
|
||||
public string Url
|
||||
{
|
||||
get => _url;
|
||||
set => SetProperty(ref _url, value);
|
||||
}
|
||||
|
||||
private string _path;
|
||||
public string Path
|
||||
{
|
||||
get => _path;
|
||||
set => SetProperty(ref _path, value);
|
||||
}
|
||||
|
||||
private bool _overwrite;
|
||||
public bool Overwrite
|
||||
{
|
||||
get => _overwrite;
|
||||
set => SetProperty(ref _overwrite, value);
|
||||
}
|
||||
|
||||
private string _filePath;
|
||||
public string FilePath
|
||||
{
|
||||
get => _filePath;
|
||||
set => SetProperty(ref _filePath, value);
|
||||
}
|
||||
|
||||
private bool _isValid;
|
||||
public bool IsValid
|
||||
{
|
||||
get => _isValid;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _isValid, value);
|
||||
RaisePropertyChanged(nameof(Label));
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string Label => IsValid ?
|
||||
"Your endpoint configuration is valid! Please, avoid any unnecessary modifications!" :
|
||||
"Your endpoint configuration DOES NOT seem to be valid yet! Please, test it out!";
|
||||
|
||||
public EndpointSettings() {}
|
||||
public EndpointSettings(string url, string path)
|
||||
{
|
||||
Url = url;
|
||||
Path = path;
|
||||
IsValid = !string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(path); // be careful with this
|
||||
}
|
||||
|
||||
public void TryValidate(DynamicApiEndpoint endpoint, EEndpointType type, out JToken response)
|
||||
{
|
||||
response = null;
|
||||
if (string.IsNullOrEmpty(Url) || string.IsNullOrEmpty(Path))
|
||||
{
|
||||
IsValid = false;
|
||||
}
|
||||
else switch (type)
|
||||
{
|
||||
case EEndpointType.Aes:
|
||||
{
|
||||
var r = endpoint.GetAesKeys(default, Url, Path);
|
||||
response = JToken.FromObject(r);
|
||||
IsValid = r.IsValid;
|
||||
break;
|
||||
}
|
||||
case EEndpointType.Mapping:
|
||||
{
|
||||
var r = endpoint.GetMappings(default, Url, Path);
|
||||
response = JToken.FromObject(r);
|
||||
IsValid = r.Any(x => x.IsValid);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
IsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Linq;
|
||||
using FModel.Framework;
|
||||
using FModel.ViewModels.ApiEndpoints;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace FModel.Settings;
|
||||
|
||||
public class EndpointSettings : ViewModel
|
||||
{
|
||||
public static EndpointSettings[] Default(string gameName)
|
||||
{
|
||||
return gameName switch
|
||||
{
|
||||
"Fortnite" or "Fortnite [LIVE]" => [
|
||||
new("https://uedb.dev/svc/api/v1/fortnite/aes", "$.['mainKey','dynamicKeys']"),
|
||||
new("https://uedb.dev/svc/api/v1/fortnite/mappings", "$.mappings.ZStandard")
|
||||
],
|
||||
"VALORANT" or "VALORANT [LIVE]" => [
|
||||
new("https://uedb.dev/svc/api/v1/valorant/aes", "$.['mainKey','dynamicKeys']"),
|
||||
new("https://uedb.dev/svc/api/v1/valorant/mappings", "$.mappings.ZStandard")
|
||||
],
|
||||
_ => [new(), new()],
|
||||
};
|
||||
}
|
||||
|
||||
private string _url;
|
||||
public string Url
|
||||
{
|
||||
get => _url;
|
||||
set => SetProperty(ref _url, value);
|
||||
}
|
||||
|
||||
private string _path;
|
||||
public string Path
|
||||
{
|
||||
get => _path;
|
||||
set => SetProperty(ref _path, value);
|
||||
}
|
||||
|
||||
private bool _overwrite;
|
||||
public bool Overwrite
|
||||
{
|
||||
get => _overwrite;
|
||||
set => SetProperty(ref _overwrite, value);
|
||||
}
|
||||
|
||||
private string _filePath;
|
||||
public string FilePath
|
||||
{
|
||||
get => _filePath;
|
||||
set => SetProperty(ref _filePath, value);
|
||||
}
|
||||
|
||||
private bool _isValid;
|
||||
public bool IsValid
|
||||
{
|
||||
get => _isValid;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _isValid, value);
|
||||
RaisePropertyChanged(nameof(Label));
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string Label => IsValid ?
|
||||
"Your endpoint configuration is valid! Please, avoid any unnecessary modifications!" :
|
||||
"Your endpoint configuration DOES NOT seem to be valid yet! Please, test it out!";
|
||||
|
||||
public EndpointSettings() {}
|
||||
public EndpointSettings(string url, string path)
|
||||
{
|
||||
Url = url;
|
||||
Path = path;
|
||||
IsValid = !string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(path); // be careful with this
|
||||
}
|
||||
|
||||
public void TryValidate(DynamicApiEndpoint endpoint, EEndpointType type, out JToken response)
|
||||
{
|
||||
response = null;
|
||||
if (string.IsNullOrEmpty(Url) || string.IsNullOrEmpty(Path))
|
||||
{
|
||||
IsValid = false;
|
||||
}
|
||||
else switch (type)
|
||||
{
|
||||
case EEndpointType.Aes:
|
||||
{
|
||||
var r = endpoint.GetAesKeys(default, Url, Path);
|
||||
response = JToken.FromObject(r);
|
||||
IsValid = r.IsValid;
|
||||
break;
|
||||
}
|
||||
case EEndpointType.Mapping:
|
||||
{
|
||||
var r = endpoint.GetMappings(default, Url, Path);
|
||||
response = JToken.FromObject(r);
|
||||
IsValid = r.Any(x => x.IsValid);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
IsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Nanite;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion;
|
||||
using CUE4Parse_Conversion.Animations;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using CUE4Parse_Conversion.UEFormat.Enums;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Nanite;
|
||||
using FModel.Framework;
|
||||
using FModel.ViewModels;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
|
|
@ -307,6 +307,13 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _dirRightTab, value);
|
||||
}
|
||||
|
||||
private Hotkey _switchAssetExplorer = new(Key.Z);
|
||||
public Hotkey SwitchAssetExplorer
|
||||
{
|
||||
get => _switchAssetExplorer;
|
||||
set => SetProperty(ref _switchAssetExplorer, value);
|
||||
}
|
||||
|
||||
private Hotkey _assetLeftTab = new(Key.Q);
|
||||
public Hotkey AssetLeftTab
|
||||
{
|
||||
|
|
@ -516,5 +523,19 @@ namespace FModel.Settings
|
|||
get => _saveHdrTexturesAsHdr;
|
||||
set => SetProperty(ref _saveHdrTexturesAsHdr, value);
|
||||
}
|
||||
|
||||
private bool _featurePreviewNewAssetExplorer = true;
|
||||
public bool FeaturePreviewNewAssetExplorer
|
||||
{
|
||||
get => _featurePreviewNewAssetExplorer;
|
||||
set => SetProperty(ref _featurePreviewNewAssetExplorer, value);
|
||||
}
|
||||
|
||||
private bool _previewTexturesAssetExplorer = true;
|
||||
public bool PreviewTexturesAssetExplorer
|
||||
{
|
||||
get => _previewTexturesAssetExplorer;
|
||||
set => SetProperty(ref _previewTexturesAssetExplorer, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using CUE4Parse.UE4.Objects.Core.Serialization;
|
||||
using FModel.Framework;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using RestSharp;
|
||||
using RestSharp;
|
||||
using RestSharp.Interceptors;
|
||||
|
||||
namespace FModel.ViewModels.ApiEndpoints;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CUE4Parse.Utils;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using AdonisUI.Controls;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
using RestSharp;
|
||||
using System.Threading.Tasks;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FModel.Framework;
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ using RestSharp;
|
|||
|
||||
namespace FModel.ViewModels.ApiEndpoints;
|
||||
|
||||
public class GitHubApiEndpoint : AbstractApiProvider
|
||||
public class GitHubApiEndpoint(RestClient client) : AbstractApiProvider(client)
|
||||
{
|
||||
public GitHubApiEndpoint(RestClient client) : base(client) { }
|
||||
|
||||
public async Task<GitHubCommit[]> GetCommitHistoryAsync(string branch = "dev", int page = 1, int limit = 30)
|
||||
{
|
||||
var request = new FRestRequest(Constants.GH_COMMITS_HISTORY);
|
||||
|
|
@ -25,4 +23,11 @@ public class GitHubApiEndpoint : AbstractApiProvider
|
|||
var response = await _client.ExecuteAsync<GitHubRelease>(request).ConfigureAwait(false);
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
public async Task<Author> GetUserAsync(string username)
|
||||
{
|
||||
var request = new FRestRequest($"https://api.github.com/users/{username}");
|
||||
var response = await _client.ExecuteAsync<Author>(request).ConfigureAwait(false);
|
||||
return response.Data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using J = Newtonsoft.Json.JsonPropertyAttribute;
|
||||
using I = Newtonsoft.Json.JsonIgnoreAttribute;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using J = Newtonsoft.Json.JsonPropertyAttribute;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using AdonisUI.Controls;
|
||||
using AutoUpdaterDotNET;
|
||||
|
|
@ -37,8 +38,7 @@ public class GitHubAsset : ViewModel
|
|||
public class GitHubCommit : ViewModel
|
||||
{
|
||||
private string _sha;
|
||||
[J("sha")]
|
||||
public string Sha
|
||||
[J("sha")] public string Sha
|
||||
{
|
||||
get => _sha;
|
||||
set
|
||||
|
|
@ -52,6 +52,35 @@ public class GitHubCommit : ViewModel
|
|||
[J("commit")] public Commit Commit { get; set; }
|
||||
[J("author")] public Author Author { get; set; }
|
||||
|
||||
private Author[] _coAuthors = [];
|
||||
public Author[] CoAuthors
|
||||
{
|
||||
get => _coAuthors;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _coAuthors, value);
|
||||
RaisePropertyChanged(nameof(Authors));
|
||||
RaisePropertyChanged(nameof(AuthorNames));
|
||||
}
|
||||
}
|
||||
|
||||
public Author[] Authors => Author != null ? new[] { Author }.Concat(CoAuthors).ToArray() : CoAuthors;
|
||||
|
||||
public string AuthorNames
|
||||
{
|
||||
get
|
||||
{
|
||||
var authors = Authors;
|
||||
return authors.Length switch
|
||||
{
|
||||
0 => string.Empty,
|
||||
1 => authors[0].Login,
|
||||
2 => $"{authors[0].Login} and {authors[1].Login}",
|
||||
_ => string.Join(", ", authors.Take(authors.Length - 1).Select(a => a.Login)) + $", and {authors[^1].Login}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private GitHubAsset _asset;
|
||||
public GitHubAsset Asset
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using J = Newtonsoft.Json.JsonPropertyAttribute;
|
||||
using I = Newtonsoft.Json.JsonIgnoreAttribute;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using J = Newtonsoft.Json.JsonPropertyAttribute;
|
||||
using I = Newtonsoft.Json.JsonIgnoreAttribute;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using CUE4Parse.Compression;
|
|||
using CUE4Parse.Encryption.Aes;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using CUE4Parse.UE4.VirtualFileSystem;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
|
|
@ -43,6 +44,32 @@ public class ApplicationViewModel : ViewModel
|
|||
private init => SetProperty(ref _status, value);
|
||||
}
|
||||
|
||||
public IEnumerable<EAssetCategory> Categories { get; } = AssetCategoryExtensions.GetBaseCategories();
|
||||
|
||||
private bool _isAssetsExplorerVisible;
|
||||
public bool IsAssetsExplorerVisible
|
||||
{
|
||||
get => _isAssetsExplorerVisible;
|
||||
set
|
||||
{
|
||||
if (value && !UserSettings.Default.FeaturePreviewNewAssetExplorer)
|
||||
return;
|
||||
|
||||
SetProperty(ref _isAssetsExplorerVisible, value);
|
||||
}
|
||||
}
|
||||
|
||||
private int _selectedLeftTabIndex;
|
||||
public int SelectedLeftTabIndex
|
||||
{
|
||||
get => _selectedLeftTabIndex;
|
||||
set
|
||||
{
|
||||
if (value is < 0 or > 2) return;
|
||||
SetProperty(ref _selectedLeftTabIndex, value);
|
||||
}
|
||||
}
|
||||
|
||||
public RightClickMenuCommand RightClickMenuCommand => _rightClickMenuCommand ??= new RightClickMenuCommand(this);
|
||||
private RightClickMenuCommand _rightClickMenuCommand;
|
||||
public MenuCommand MenuCommand => _menuCommand ??= new MenuCommand(this);
|
||||
|
|
@ -50,7 +77,7 @@ public class ApplicationViewModel : ViewModel
|
|||
public CopyCommand CopyCommand => _copyCommand ??= new CopyCommand(this);
|
||||
private CopyCommand _copyCommand;
|
||||
|
||||
public string InitialWindowTitle => $"FModel ({Constants.APP_SHORT_COMMIT_ID})";
|
||||
public string InitialWindowTitle => $"FModel ({Constants.APP_SHORT_COMMIT_ID} - {Constants.APP_BUILD_DATE:MMM d, yyyy})";
|
||||
public string GameDisplayName => CUE4Parse.Provider.GameDisplayName ?? "Unknown";
|
||||
public string TitleExtra => $"({UserSettings.Default.CurrentDir.UeVersion}){(Build != EBuildKind.Release ? $" ({Build})" : "")}";
|
||||
|
||||
|
|
@ -196,32 +223,37 @@ public class ApplicationViewModel : ViewModel
|
|||
public static async Task InitVgmStream()
|
||||
{
|
||||
var vgmZipFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "vgmstream-win.zip");
|
||||
if (File.Exists(vgmZipFilePath)) return;
|
||||
var vgmFileInfo = new FileInfo(vgmZipFilePath);
|
||||
|
||||
await ApplicationService.ApiEndpointView.DownloadFileAsync("https://github.com/vgmstream/vgmstream/releases/latest/download/vgmstream-win.zip", vgmZipFilePath);
|
||||
if (new FileInfo(vgmZipFilePath).Length > 0)
|
||||
if (!vgmFileInfo.Exists || vgmFileInfo.LastWriteTimeUtc < DateTime.UtcNow.AddMonths(-4))
|
||||
{
|
||||
var zipDir = Path.GetDirectoryName(vgmZipFilePath)!;
|
||||
await using var zipFs = File.OpenRead(vgmZipFilePath);
|
||||
using var zip = new ZipArchive(zipFs, ZipArchiveMode.Read);
|
||||
await ApplicationService.ApiEndpointView.DownloadFileAsync("https://github.com/vgmstream/vgmstream/releases/latest/download/vgmstream-win.zip", vgmZipFilePath);
|
||||
vgmFileInfo.Refresh();
|
||||
|
||||
foreach (var entry in zip.Entries)
|
||||
if (vgmFileInfo.Length > 0)
|
||||
{
|
||||
var entryPath = Path.Combine(zipDir, entry.FullName);
|
||||
await using var entryFs = File.Create(entryPath);
|
||||
await using var entryStream = entry.Open();
|
||||
await entryStream.CopyToAsync(entryFs);
|
||||
var zipDir = Path.GetDirectoryName(vgmZipFilePath)!;
|
||||
await using var zipFs = File.OpenRead(vgmZipFilePath);
|
||||
using var zip = new ZipArchive(zipFs, ZipArchiveMode.Read);
|
||||
|
||||
foreach (var entry in zip.Entries)
|
||||
{
|
||||
var entryPath = Path.Combine(zipDir, entry.FullName);
|
||||
await using var entryFs = File.Create(entryPath);
|
||||
await using var entryStream = entry.Open();
|
||||
await entryStream.CopyToAsync(entryFs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FLogger.Append(ELog.Error, () => FLogger.Text("Could not download VgmStream", Constants.WHITE, true));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FLogger.Append(ELog.Error, () => FLogger.Text("Could not download VgmStream", Constants.WHITE, true));
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task InitImGuiSettings(bool forceDownload)
|
||||
{
|
||||
var imgui = "imgui.ini";
|
||||
const string imgui = "imgui.ini";
|
||||
var imguiPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", imgui);
|
||||
|
||||
if (File.Exists(imgui)) File.Move(imgui, imguiPath, true);
|
||||
|
|
@ -234,7 +266,7 @@ public class ApplicationViewModel : ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
public static async ValueTask InitOodle()
|
||||
public static async Task InitOodle()
|
||||
{
|
||||
if (File.Exists(OodleHelper.OODLE_DLL_NAME_OLD))
|
||||
{
|
||||
|
|
@ -245,7 +277,11 @@ public class ApplicationViewModel : ViewModel
|
|||
catch { /* ignored */}
|
||||
}
|
||||
|
||||
var oodlePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", OodleHelper.OODLE_DLL_NAME);
|
||||
var oodlePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", OodleHelper.OODLE_DLL_NAME_OLD);
|
||||
if (!File.Exists(oodlePath))
|
||||
{
|
||||
oodlePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", OodleHelper.OODLE_DLL_NAME);
|
||||
}
|
||||
|
||||
if (!File.Exists(oodlePath))
|
||||
{
|
||||
|
|
@ -259,7 +295,7 @@ public class ApplicationViewModel : ViewModel
|
|||
OodleHelper.Initialize(oodlePath);
|
||||
}
|
||||
|
||||
public static async ValueTask InitZlib()
|
||||
public static async Task InitZlib()
|
||||
{
|
||||
var zlibPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", ZlibHelper.DLL_NAME);
|
||||
var zlibFileInfo = new FileInfo(zlibPath);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using CUE4Parse.FileProvider.Objects;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse.UE4.VirtualFileSystem;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
||||
|
|
@ -14,11 +17,11 @@ namespace FModel.ViewModels;
|
|||
|
||||
public class TreeItem : ViewModel
|
||||
{
|
||||
private string _header;
|
||||
private readonly string _header;
|
||||
public string Header
|
||||
{
|
||||
get => _header;
|
||||
private set => SetProperty(ref _header, value);
|
||||
private init => SetProperty(ref _header, value);
|
||||
}
|
||||
|
||||
private bool _isExpanded;
|
||||
|
|
@ -56,10 +59,91 @@ public class TreeItem : ViewModel
|
|||
private set => SetProperty(ref _version, value);
|
||||
}
|
||||
|
||||
private string _searchText = string.Empty;
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchText, value))
|
||||
{
|
||||
RefreshFilters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EAssetCategory _selectedCategory = EAssetCategory.All;
|
||||
public EAssetCategory SelectedCategory
|
||||
{
|
||||
get => _selectedCategory;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedCategory, value))
|
||||
_ = OnSelectedCategoryChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string PathAtThisPoint { get; }
|
||||
public AssetsListViewModel AssetsList { get; }
|
||||
public RangeObservableCollection<TreeItem> Folders { get; }
|
||||
public ICollectionView FoldersView { get; }
|
||||
public AssetsListViewModel AssetsList { get; } = new();
|
||||
public RangeObservableCollection<TreeItem> Folders { get; } = [];
|
||||
|
||||
private ICollectionView _foldersView;
|
||||
public ICollectionView FoldersView
|
||||
{
|
||||
get
|
||||
{
|
||||
_foldersView ??= new ListCollectionView(Folders)
|
||||
{
|
||||
SortDescriptions = { new SortDescription(nameof(Header), ListSortDirection.Ascending) }
|
||||
};
|
||||
return _foldersView;
|
||||
}
|
||||
}
|
||||
|
||||
private ICollectionView? _filteredFoldersView;
|
||||
public ICollectionView? FilteredFoldersView
|
||||
{
|
||||
get
|
||||
{
|
||||
_filteredFoldersView ??= new ListCollectionView(Folders)
|
||||
{
|
||||
SortDescriptions = { new SortDescription(nameof(Header), ListSortDirection.Ascending) },
|
||||
Filter = e => ItemFilter(e, SearchText.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries))
|
||||
};
|
||||
return _filteredFoldersView;
|
||||
}
|
||||
}
|
||||
|
||||
private CompositeCollection _combinedEntries;
|
||||
public CompositeCollection CombinedEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_combinedEntries == null)
|
||||
{
|
||||
void CreateCombinedEntries()
|
||||
{
|
||||
_combinedEntries = new CompositeCollection
|
||||
{
|
||||
new CollectionContainer { Collection = FilteredFoldersView },
|
||||
new CollectionContainer { Collection = AssetsList.AssetsView }
|
||||
};
|
||||
}
|
||||
|
||||
if (!Application.Current.Dispatcher.CheckAccess())
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(CreateCombinedEntries);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateCombinedEntries();
|
||||
}
|
||||
}
|
||||
return _combinedEntries;
|
||||
}
|
||||
}
|
||||
|
||||
public TreeItem Parent { get; init; }
|
||||
|
||||
public TreeItem(string header, GameFile entry, string pathHere)
|
||||
{
|
||||
|
|
@ -71,9 +155,43 @@ public class TreeItem : ViewModel
|
|||
Version = vfsEntry.Vfs.Ver;
|
||||
}
|
||||
PathAtThisPoint = pathHere;
|
||||
AssetsList = new AssetsListViewModel();
|
||||
Folders = new RangeObservableCollection<TreeItem>();
|
||||
FoldersView = new ListCollectionView(Folders) { SortDescriptions = { new SortDescription("Header", ListSortDirection.Ascending) } };
|
||||
|
||||
AssetsList.AssetsView.Filter = o => ItemFilter(o, SearchText.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
|
||||
private void RefreshFilters()
|
||||
{
|
||||
AssetsList.AssetsView.Refresh();
|
||||
FilteredFoldersView?.Refresh();
|
||||
}
|
||||
|
||||
private bool ItemFilter(object item, IEnumerable<string> filters)
|
||||
{
|
||||
var f = filters.ToArray();
|
||||
switch (item)
|
||||
{
|
||||
case GameFileViewModel entry:
|
||||
{
|
||||
bool matchesSearch = f.Length == 0 || f.All(x => entry.Asset.Name.Contains(x, StringComparison.OrdinalIgnoreCase));
|
||||
bool matchesCategory = SelectedCategory == EAssetCategory.All || entry.AssetCategory.IsOfCategory(SelectedCategory);
|
||||
|
||||
return matchesSearch && matchesCategory;
|
||||
}
|
||||
case TreeItem folder:
|
||||
{
|
||||
bool matchesSearch = f.Length == 0 || f.All(x => folder.Header.Contains(x, StringComparison.OrdinalIgnoreCase));
|
||||
bool matchesCategory = SelectedCategory == EAssetCategory.All;
|
||||
|
||||
return matchesSearch && matchesCategory;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task OnSelectedCategoryChanged()
|
||||
{
|
||||
await Task.WhenAll(AssetsList.Assets.Select(asset => asset.ResolveAsync(EResolveCompute.Category)));
|
||||
RefreshFilters();
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Header} | {Folders.Count} Folders | {AssetsList.Assets.Count} Files";
|
||||
|
|
@ -86,7 +204,7 @@ public class AssetsFolderViewModel
|
|||
|
||||
public AssetsFolderViewModel()
|
||||
{
|
||||
Folders = new RangeObservableCollection<TreeItem>();
|
||||
Folders = [];
|
||||
FoldersView = new ListCollectionView(Folders) { SortDescriptions = { new SortDescription("Header", ListSortDirection.Ascending) } };
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +221,7 @@ public class AssetsFolderViewModel
|
|||
foreach (var entry in entries)
|
||||
{
|
||||
TreeItem lastNode = null;
|
||||
TreeItem parentItem = null;
|
||||
var folders = entry.Path.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
var builder = new StringBuilder(64);
|
||||
var parentNode = treeItems;
|
||||
|
|
@ -127,20 +246,30 @@ public class AssetsFolderViewModel
|
|||
if (lastNode == null)
|
||||
{
|
||||
var nodePath = builder.ToString();
|
||||
lastNode = new TreeItem(folder, entry, nodePath[..^1]);
|
||||
lastNode = new TreeItem(folder, entry, nodePath[..^1])
|
||||
{
|
||||
Parent = parentItem
|
||||
};
|
||||
lastNode.Folders.SetSuppressionState(true);
|
||||
lastNode.AssetsList.Assets.SetSuppressionState(true);
|
||||
parentNode.Add(lastNode);
|
||||
}
|
||||
|
||||
parentItem = lastNode;
|
||||
parentNode = lastNode.Folders;
|
||||
}
|
||||
|
||||
lastNode?.AssetsList.Assets.Add(entry);
|
||||
lastNode?.AssetsList.Add(entry);
|
||||
}
|
||||
|
||||
if (treeItems.Count > 0)
|
||||
{
|
||||
var projectName = ApplicationService.ApplicationView.CUE4Parse.Provider.ProjectName;
|
||||
(treeItems.FirstOrDefault(x => x.Header.Equals(projectName, StringComparison.OrdinalIgnoreCase)) ?? treeItems[0]).IsSelected = true;
|
||||
}
|
||||
|
||||
Folders.AddRange(treeItems);
|
||||
ApplicationService.ApplicationView.CUE4Parse.SearchVm.SearchResults.AddRange(entries);
|
||||
ApplicationService.ApplicationView.CUE4Parse.SearchVm.ChangeCollection(entries);
|
||||
|
||||
foreach (var folder in Folders)
|
||||
InvokeOnCollectionChanged(folder);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Data;
|
||||
using CUE4Parse.FileProvider.Objects;
|
||||
using FModel.Framework;
|
||||
|
|
@ -7,15 +7,20 @@ namespace FModel.ViewModels;
|
|||
|
||||
public class AssetsListViewModel
|
||||
{
|
||||
public RangeObservableCollection<GameFile> Assets { get; }
|
||||
public ICollectionView AssetsView { get; }
|
||||
public RangeObservableCollection<GameFileViewModel> Assets { get; } = [];
|
||||
|
||||
public AssetsListViewModel()
|
||||
private ICollectionView _assetsView;
|
||||
public ICollectionView AssetsView
|
||||
{
|
||||
Assets = new RangeObservableCollection<GameFile>();
|
||||
AssetsView = new ListCollectionView(Assets)
|
||||
get
|
||||
{
|
||||
SortDescriptions = { new SortDescription("Path", ListSortDirection.Ascending) }
|
||||
};
|
||||
_assetsView ??= new ListCollectionView(Assets)
|
||||
{
|
||||
SortDescriptions = { new SortDescription("Asset.Path", ListSortDirection.Ascending) }
|
||||
};
|
||||
return _assetsView;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(GameFile gameFile) => Assets.Add(new GameFileViewModel(gameFile));
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,4 @@
|
|||
using AdonisUI.Controls;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Framework;
|
||||
using FModel.Settings;
|
||||
using FModel.Views;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using FModel.Framework;
|
||||
using FModel.Framework;
|
||||
|
||||
namespace FModel.ViewModels.Commands;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using FModel.Framework;
|
||||
using FModel.Framework;
|
||||
|
||||
namespace FModel.ViewModels.Commands;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
|
@ -18,8 +19,16 @@ public class CopyCommand : ViewModelCommand<ApplicationViewModel>
|
|||
if (parameter is not object[] parameters || parameters[0] is not string trigger)
|
||||
return;
|
||||
|
||||
var entries = ((IList) parameters[1]).Cast<GameFile>().ToArray();
|
||||
if (!entries.Any()) return;
|
||||
var entries = (parameters[1] as IEnumerable)?.OfType<object>()
|
||||
.SelectMany(item => item switch
|
||||
{
|
||||
GameFile gf => new[] { gf },
|
||||
GameFileViewModel gvm => new[] { gvm.Asset },
|
||||
_ => []
|
||||
}) ?? [];
|
||||
|
||||
if (!entries.Any())
|
||||
return;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
switch (trigger)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using FModel.Framework;
|
||||
using FModel.Framework;
|
||||
using FModel.Settings;
|
||||
|
||||
namespace FModel.ViewModels.Commands;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ public class GoToCommand : ViewModelCommand<CustomDirectoriesViewModel>
|
|||
|
||||
public TreeItem JumpTo(string directory)
|
||||
{
|
||||
MainWindow.YesWeCats.LeftTabControl.SelectedIndex = 1; // folders tab
|
||||
_applicationView.SelectedLeftTabIndex = 1; // folders tab
|
||||
var root = _applicationView.CUE4Parse.AssetsFolder.Folders;
|
||||
if (root is not { Count: > 0 }) return null;
|
||||
|
||||
|
|
@ -54,4 +54,4 @@ public class GoToCommand : ViewModelCommand<CustomDirectoriesViewModel>
|
|||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AdonisUI.Controls;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Views.Resources.Controls;
|
||||
|
|
|
|||
|
|
@ -55,8 +55,9 @@ public class LoadCommand : ViewModelCommand<LoadingModesViewModel>
|
|||
#endif
|
||||
_applicationView.CUE4Parse.AssetsFolder.Folders.Clear();
|
||||
_applicationView.CUE4Parse.SearchVm.SearchResults.Clear();
|
||||
MainWindow.YesWeCats.LeftTabControl.SelectedIndex = 1; // folders tab
|
||||
Helper.CloseWindow<AdonisWindow>("Search View"); // close search window if opened
|
||||
_applicationView.SelectedLeftTabIndex = 1; // folders tab
|
||||
_applicationView.IsAssetsExplorerVisible = true;
|
||||
Helper.CloseWindow<AdonisWindow>("Search For Packages"); // close search window if opened
|
||||
|
||||
await Task.WhenAll(
|
||||
_applicationView.CUE4Parse.LoadLocalizedResources(), // load locres if not already loaded,
|
||||
|
|
@ -70,7 +71,11 @@ public class LoadCommand : ViewModelCommand<LoadingModesViewModel>
|
|||
case ELoadingMode.Multiple:
|
||||
{
|
||||
var l = (IList) parameter;
|
||||
if (l.Count < 1) return;
|
||||
if (l.Count == 0)
|
||||
{
|
||||
UserSettings.Default.LoadingMode = ELoadingMode.All;
|
||||
goto case ELoadingMode.All;
|
||||
}
|
||||
|
||||
var directoryFilesToShow = l.Cast<FileItem>();
|
||||
FilterDirectoryFilesToDisplay(cancellationToken, directoryFilesToShow);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public class MenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
Helper.OpenWindow<AdonisWindow>("Backup Manager", () => new BackupManager(contextViewModel.CUE4Parse.Provider.ProjectName).Show());
|
||||
break;
|
||||
case "Directory_ArchivesInfo":
|
||||
ApplicationService.ApplicationView.IsAssetsExplorerVisible = false;
|
||||
contextViewModel.CUE4Parse.TabControl.AddTab("Archives Info");
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json");
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(contextViewModel.CUE4Parse.GameDirectory.DirectoryFiles, Formatting.Indented), false, false);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using FModel.Framework;
|
||||
using FModel.Settings;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ using System.Threading;
|
|||
using CUE4Parse.FileProvider.Objects;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.Views.Resources.Controls;
|
||||
|
||||
namespace FModel.ViewModels.Commands;
|
||||
|
||||
|
|
@ -20,16 +22,28 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
if (parameter is not object[] parameters || parameters[0] is not string trigger)
|
||||
return;
|
||||
|
||||
var entries = ((IList) parameters[1]).Cast<GameFile>().ToArray();
|
||||
if (!entries.Any()) return;
|
||||
var param = (parameters[1] as IEnumerable)?.OfType<object>().ToArray() ?? [];
|
||||
if (param.Length == 0) return;
|
||||
|
||||
var updateUi = entries.Length > 1 ? EBulkType.Auto : EBulkType.None;
|
||||
var folders = param.OfType<TreeItem>().ToArray();
|
||||
var assets = param.SelectMany(item => item switch
|
||||
{
|
||||
GameFile gf => new[] { gf }, // search view passes GameFile directly
|
||||
GameFileViewModel gvm => new[] { gvm.Asset },
|
||||
_ => []
|
||||
}).ToArray();
|
||||
|
||||
if (folders.Length == 0 && assets.Length == 0)
|
||||
return;
|
||||
|
||||
var updateUi = assets.Length > 1 ? EBulkType.Auto : EBulkType.None;
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
switch (trigger)
|
||||
{
|
||||
#region Asset Commands
|
||||
case "Assets_Extract_New_Tab":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -37,15 +51,22 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
}
|
||||
break;
|
||||
case "Assets_Show_Metadata":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.ShowMetadata(entry);
|
||||
}
|
||||
break;
|
||||
case "Assets_Show_References":
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.FindReferences(assets.FirstOrDefault());
|
||||
}
|
||||
break;
|
||||
case "Assets_Decompile":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -53,7 +74,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
}
|
||||
break;
|
||||
case "Assets_Export_Data":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -61,7 +82,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
}
|
||||
break;
|
||||
case "Assets_Save_Properties":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -69,7 +90,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
}
|
||||
break;
|
||||
case "Assets_Save_Textures":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -77,7 +98,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
}
|
||||
break;
|
||||
case "Assets_Save_Models":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -85,7 +106,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
}
|
||||
break;
|
||||
case "Assets_Save_Animations":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -93,13 +114,100 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
}
|
||||
break;
|
||||
case "Assets_Save_Audio":
|
||||
foreach (var entry in entries)
|
||||
foreach (var entry in assets)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Audio | updateUi);
|
||||
}
|
||||
break;
|
||||
#endregion
|
||||
|
||||
#region Folder Commands
|
||||
case "Folders_Export_Data":
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.ExportFolder(cancellationToken, folder);
|
||||
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully exported ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.RawDataDirectory, true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "Folders_Save_Properties":
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.SaveFolder(cancellationToken, folder);
|
||||
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.PropertiesDirectory, true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "Folders_Save_Textures":
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.TextureFolder(cancellationToken, folder);
|
||||
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved textures from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.TextureDirectory, true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "Folders_Save_Models":
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.ModelFolder(cancellationToken, folder);
|
||||
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved models from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.ModelDirectory, true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "Folders_Save_Animations":
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.AnimationFolder(cancellationToken, folder);
|
||||
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved animations from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.ModelDirectory, true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "Folders_Save_Audio":
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.AudioFolder(cancellationToken, folder);
|
||||
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved audio from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.AudioDirectory, true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
#endregion
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ public class TabCommand : ViewModelCommand<TabItem>
|
|||
case "Close_Other_Tabs":
|
||||
_applicationView.CUE4Parse.TabControl.RemoveOtherTabs(tabViewModel);
|
||||
break;
|
||||
case "Find_References":
|
||||
_applicationView.CUE4Parse.FindReferences(tabViewModel.Entry);
|
||||
break;
|
||||
case "Asset_Export_Data":
|
||||
await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(tabViewModel.Entry));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
|
|
|||
459
FModel/ViewModels/GameFileViewModel.cs
Normal file
459
FModel/ViewModels/GameFileViewModel.cs
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using CUE4Parse.FileProvider.Objects;
|
||||
using CUE4Parse.GameTypes.FN.Assets.Exports.DataAssets;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Assets;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||
using CUE4Parse.UE4.Assets.Exports.BuildData;
|
||||
using CUE4Parse.UE4.Assets.Exports.Component;
|
||||
using CUE4Parse.UE4.Assets.Exports.CriWare;
|
||||
using CUE4Parse.UE4.Assets.Exports.CustomizableObject;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine.Font;
|
||||
using CUE4Parse.UE4.Assets.Exports.Fmod;
|
||||
using CUE4Parse.UE4.Assets.Exports.Foliage;
|
||||
using CUE4Parse.UE4.Assets.Exports.Internationalization;
|
||||
using CUE4Parse.UE4.Assets.Exports.LevelSequence;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material.Editor;
|
||||
using CUE4Parse.UE4.Assets.Exports.Nanite;
|
||||
using CUE4Parse.UE4.Assets.Exports.Niagara;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Sound;
|
||||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Engine;
|
||||
using CUE4Parse.UE4.Objects.Engine.Animation;
|
||||
using CUE4Parse.UE4.Objects.Engine.Curves;
|
||||
using CUE4Parse.UE4.Objects.MediaAssets;
|
||||
using CUE4Parse.UE4.Objects.Niagara;
|
||||
using CUE4Parse.UE4.Objects.PhysicsEngine;
|
||||
using CUE4Parse.UE4.Objects.RigVM;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse.UE4.Objects.UObject.Editor;
|
||||
using CUE4Parse.Utils;
|
||||
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
using Svg.Skia;
|
||||
|
||||
namespace FModel.ViewModels;
|
||||
|
||||
public class GameFileViewModel(GameFile asset) : ViewModel
|
||||
{
|
||||
private const int MaxPreviewSize = 128;
|
||||
|
||||
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
|
||||
|
||||
public EResolveCompute Resolved { get; private set; } = EResolveCompute.None;
|
||||
public GameFile Asset { get; } = asset;
|
||||
|
||||
private string _resolvedAssetType = asset.Extension;
|
||||
public string ResolvedAssetType
|
||||
{
|
||||
get => _resolvedAssetType;
|
||||
private set => SetProperty(ref _resolvedAssetType, value);
|
||||
}
|
||||
|
||||
private bool _isSelected;
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set => SetProperty(ref _isSelected, value);
|
||||
}
|
||||
|
||||
private EAssetCategory _assetCategory = EAssetCategory.All;
|
||||
public EAssetCategory AssetCategory
|
||||
{
|
||||
get => _assetCategory;
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _assetCategory, value);
|
||||
Resolved |= EResolveCompute.Category; // blindly assume category is resolved when set, even if unchanged
|
||||
}
|
||||
}
|
||||
|
||||
private EBulkType _assetActions = EBulkType.None;
|
||||
public EBulkType AssetActions
|
||||
{
|
||||
get => _assetActions;
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _assetActions, value);
|
||||
}
|
||||
}
|
||||
|
||||
private ImageSource _previewImage;
|
||||
public ImageSource PreviewImage
|
||||
{
|
||||
get => _previewImage;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _previewImage, value))
|
||||
{
|
||||
Resolved |= EResolveCompute.Preview;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _numTextures = 0;
|
||||
public int NumTextures
|
||||
{
|
||||
get => _numTextures;
|
||||
private set => SetProperty(ref _numTextures, value);
|
||||
}
|
||||
|
||||
public Task ExtractAsync()
|
||||
=> ApplicationService.ThreadWorkerView.Begin(cancellationToken =>
|
||||
_applicationView.CUE4Parse.ExtractSelected(cancellationToken, [Asset]));
|
||||
|
||||
public Task ResolveAsync(EResolveCompute resolve)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResolveInternalAsync(resolve);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Failed to resolve asset {AssetName} ({Resolver})", Asset.Path, resolve.ToStringBitfield());
|
||||
|
||||
Resolved = EResolveCompute.All;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private Task ResolveInternalAsync(EResolveCompute resolve)
|
||||
{
|
||||
if (!_applicationView.IsAssetsExplorerVisible || !UserSettings.Default.PreviewTexturesAssetExplorer)
|
||||
{
|
||||
resolve &= ~EResolveCompute.Preview;
|
||||
}
|
||||
|
||||
resolve &= ~Resolved;
|
||||
if (resolve == EResolveCompute.None)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!Asset.IsUePackage || _applicationView.CUE4Parse is null)
|
||||
return ResolveByExtensionAsync(resolve);
|
||||
|
||||
return ResolveByPackageAsync(resolve);
|
||||
}
|
||||
|
||||
private Task ResolveByPackageAsync(EResolveCompute resolve)
|
||||
{
|
||||
if (Asset.Extension is "umap")
|
||||
{
|
||||
AssetCategory = EAssetCategory.World;
|
||||
AssetActions = EBulkType.Meshes | EBulkType.Textures | EBulkType.Audio | EBulkType.Code;
|
||||
ResolvedAssetType = "World";
|
||||
Resolved |= EResolveCompute.Preview;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (Asset.NameWithoutExtension.EndsWith("_BuiltData"))
|
||||
{
|
||||
AssetCategory = EAssetCategory.BuildData;
|
||||
AssetActions = EBulkType.Textures;
|
||||
ResolvedAssetType = "MapBuildDataRegistry";
|
||||
Resolved |= EResolveCompute.Preview;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
// TODO: cache and reuse packages
|
||||
var pkg = _applicationView.CUE4Parse?.Provider.LoadPackage(Asset);
|
||||
if (pkg is null)
|
||||
throw new InvalidOperationException($"Failed to load {Asset.Path} as UE package.");
|
||||
|
||||
var mainIndex = pkg.GetExportIndex(Asset.NameWithoutExtension, StringComparison.OrdinalIgnoreCase);
|
||||
if (mainIndex < 0) mainIndex = pkg.GetExportIndex($"{Asset.NameWithoutExtension}_C", StringComparison.OrdinalIgnoreCase);
|
||||
if (mainIndex < 0) mainIndex = 0;
|
||||
|
||||
var pointer = new FPackageIndex(pkg, mainIndex + 1).ResolvedObject;
|
||||
if (pointer?.Object is null)
|
||||
return;
|
||||
|
||||
var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class?.Object?.Value as UStruct, pkg);
|
||||
ResolvedAssetType = dummy.ExportType;
|
||||
|
||||
(AssetCategory, AssetActions) = dummy switch
|
||||
{
|
||||
URigVMBlueprintGeneratedClass => (EAssetCategory.RigVMBlueprintGeneratedClass, EBulkType.Code),
|
||||
UAnimBlueprintGeneratedClass => (EAssetCategory.AnimBlueprintGeneratedClass, EBulkType.Code),
|
||||
UWidgetBlueprintGeneratedClass => (EAssetCategory.WidgetBlueprintGeneratedClass, EBulkType.Code),
|
||||
UBlueprintGeneratedClass or UFunction => (EAssetCategory.BlueprintGeneratedClass, EBulkType.Code),
|
||||
UUserDefinedEnum => (EAssetCategory.UserDefinedEnum, EBulkType.None),
|
||||
UUserDefinedStruct => (EAssetCategory.UserDefinedStruct, EBulkType.Code),
|
||||
UBlueprintCore => (EAssetCategory.Blueprint, EBulkType.Code),
|
||||
UClassCookedMetaData or UStructCookedMetaData or UEnumCookedMetaData => (EAssetCategory.CookedMetaData, EBulkType.None),
|
||||
|
||||
UStaticMesh => (EAssetCategory.StaticMesh, EBulkType.Meshes),
|
||||
USkeletalMesh => (EAssetCategory.SkeletalMesh, EBulkType.Meshes),
|
||||
UCustomizableObject => (EAssetCategory.CustomizableObject, EBulkType.None),
|
||||
UNaniteDisplacedMesh => (EAssetCategory.NaniteDisplacedMesh, EBulkType.None),
|
||||
|
||||
UTexture => (EAssetCategory.Texture, EBulkType.Textures),
|
||||
|
||||
UMaterialInterface => (EAssetCategory.Material, EBulkType.None),
|
||||
UMaterialInterfaceEditorOnlyData => (EAssetCategory.MaterialEditorData, EBulkType.None),
|
||||
UMaterialFunction => (EAssetCategory.MaterialFunction, EBulkType.None),
|
||||
UMaterialFunctionEditorOnlyData => (EAssetCategory.MaterialFunctionEditorData, EBulkType.None),
|
||||
UMaterialParameterCollection => (EAssetCategory.MaterialParameterCollection, EBulkType.None),
|
||||
|
||||
UAnimationAsset => (EAssetCategory.Animation, EBulkType.Animations),
|
||||
USkeleton => (EAssetCategory.Skeleton, EBulkType.Meshes),
|
||||
URig => (EAssetCategory.Rig, EBulkType.None),
|
||||
|
||||
UWorld => (EAssetCategory.World, EBulkType.Meshes | EBulkType.Textures | EBulkType.Audio | EBulkType.Code),
|
||||
UMapBuildDataRegistry => (EAssetCategory.BuildData, EBulkType.Textures),
|
||||
ULevelSequence => (EAssetCategory.LevelSequence, EBulkType.Code),
|
||||
UFoliageType => (EAssetCategory.Foliage, EBulkType.None),
|
||||
|
||||
UItemDefinitionBase => (EAssetCategory.ItemDefinitionBase, EBulkType.Textures),
|
||||
UDataAsset or UDataTable or UCurveTable or UStringTable => (EAssetCategory.Data, EBulkType.None),
|
||||
UCurveBase => (EAssetCategory.CurveBase, EBulkType.None),
|
||||
UPhysicsAsset => (EAssetCategory.PhysicsAsset, EBulkType.None),
|
||||
UObjectRedirector => (EAssetCategory.ObjectRedirector, EBulkType.None),
|
||||
UPhysicalMaterial => (EAssetCategory.PhysicalMaterial, EBulkType.None),
|
||||
|
||||
USoundAtomCue or UAkAudioEvent or USoundCue or UFMODEvent => (EAssetCategory.AudioEvent, EBulkType.Audio),
|
||||
|
||||
UFMODBank or UAkAudioBank or UAtomWaveBank or UAkInitBank => (EAssetCategory.SoundBank, EBulkType.Audio),
|
||||
|
||||
UWwiseAssetLibrary or USoundBase or UAkMediaAssetData or UAtomCueSheet
|
||||
or USoundAtomCueSheet or UAkAudioType or UExternalSource or UExternalSourceBank => (EAssetCategory.Audio, EBulkType.Audio),
|
||||
|
||||
UFileMediaSource => (EAssetCategory.Video, EBulkType.None),
|
||||
UFont or UFontFace or USMGLocaleFontUMG => (EAssetCategory.Font, EBulkType.None),
|
||||
|
||||
UNiagaraSystem or UNiagaraScriptBase or UParticleSystem => (EAssetCategory.Particle, EBulkType.None),
|
||||
|
||||
_ => (EAssetCategory.All, EBulkType.None),
|
||||
};
|
||||
|
||||
switch (AssetCategory)
|
||||
{
|
||||
case EAssetCategory.Texture when pointer.Object.Value is UTexture texture:
|
||||
{
|
||||
if (!resolve.HasFlag(EResolveCompute.Preview))
|
||||
break;
|
||||
|
||||
if (pointer.Object.Value is UTexture2DArray textureArray && textureArray.GetFirstMip() is { SizeZ: > 1 } firstMip)
|
||||
NumTextures = firstMip.SizeZ;
|
||||
|
||||
var img = texture.Decode(MaxPreviewSize, UserSettings.Default.CurrentDir.TexturePlatform);
|
||||
if (img != null)
|
||||
{
|
||||
using var bitmap = img.ToSkBitmap();
|
||||
using var image = bitmap.Encode(SKEncodedImageFormat.Png, 100);
|
||||
SetPreviewImage(image);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EAssetCategory.ItemDefinitionBase:
|
||||
if (!resolve.HasFlag(EResolveCompute.Preview))
|
||||
break;
|
||||
|
||||
if (pointer.Object.Value is UItemDefinitionBase itemDef)
|
||||
{
|
||||
if (LookupPreview(itemDef.DataList)) break;
|
||||
|
||||
if (itemDef is UAthenaPickaxeItemDefinition pickaxe && pickaxe.WeaponDefinition.TryLoad(out UItemDefinitionBase weaponDef))
|
||||
{
|
||||
LookupPreview(weaponDef.DataList);
|
||||
}
|
||||
|
||||
bool LookupPreview(FInstancedStruct[] dataList)
|
||||
{
|
||||
foreach (var data in dataList)
|
||||
{
|
||||
if (!data.NonConstStruct.TryGetValue(out FSoftObjectPath icon, "Icon", "LargeIcon") ||
|
||||
!icon.TryLoad<UTexture2D>(out var texture))
|
||||
continue;
|
||||
|
||||
var img = texture.Decode(MaxPreviewSize, UserSettings.Default.CurrentDir.TexturePlatform);
|
||||
if (img == null) return false;
|
||||
|
||||
using var bitmap = img.ToSkBitmap();
|
||||
using var image = bitmap.Encode(SKEncodedImageFormat.Png, 100);
|
||||
SetPreviewImage(image);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Resolved |= EResolveCompute.Preview;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Task ResolveByExtensionAsync(EResolveCompute resolve)
|
||||
{
|
||||
Resolved |= EResolveCompute.Preview;
|
||||
switch (Asset.Extension)
|
||||
{
|
||||
case "uproject":
|
||||
case "uefnproject":
|
||||
case "upluginmanifest":
|
||||
case "uplugin":
|
||||
case "ini":
|
||||
case "locmeta":
|
||||
case "locres":
|
||||
case "verse":
|
||||
case "lua":
|
||||
case "luac":
|
||||
case "json5":
|
||||
case "json":
|
||||
case "bin":
|
||||
case "txt":
|
||||
case "log":
|
||||
case "pem":
|
||||
case "xml":
|
||||
AssetCategory = EAssetCategory.Data;
|
||||
break;
|
||||
case "ushaderbytecode":
|
||||
AssetCategory = EAssetCategory.ByteCode;
|
||||
break;
|
||||
case "wav":
|
||||
case "awb": // This is technically soundbank and should be below but I want it to be distinguishable from "acb"
|
||||
case "xvag":
|
||||
case "flac":
|
||||
case "at9":
|
||||
case "wem":
|
||||
case "ogg":
|
||||
AssetCategory = EAssetCategory.Audio;
|
||||
AssetActions = EBulkType.Audio;
|
||||
break;
|
||||
case "acb":
|
||||
case "bank":
|
||||
case "bnk":
|
||||
case "pck":
|
||||
AssetCategory = EAssetCategory.SoundBank;
|
||||
AssetActions = EBulkType.Audio;
|
||||
break;
|
||||
case "ufont":
|
||||
case "otf":
|
||||
case "ttf":
|
||||
AssetCategory = EAssetCategory.Font;
|
||||
break;
|
||||
case "mp4":
|
||||
AssetCategory = EAssetCategory.Video;
|
||||
break;
|
||||
case "jpg":
|
||||
case "png":
|
||||
case "bmp":
|
||||
case "svg":
|
||||
{
|
||||
Resolved |= ~EResolveCompute.Preview;
|
||||
AssetCategory = EAssetCategory.Texture;
|
||||
AssetActions = EBulkType.Textures;
|
||||
if (!resolve.HasFlag(EResolveCompute.Preview))
|
||||
break;
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var data = _applicationView.CUE4Parse.Provider.SaveAsset(Asset);
|
||||
using var stream = new MemoryStream(data);
|
||||
stream.Position = 0;
|
||||
|
||||
SKBitmap bitmap;
|
||||
if (Asset.Extension == "svg")
|
||||
{
|
||||
var svg = new SKSvg();
|
||||
svg.Load(stream);
|
||||
if (svg.Picture == null)
|
||||
return;
|
||||
|
||||
bitmap = new SKBitmap(MaxPreviewSize, MaxPreviewSize);
|
||||
using var canvas = new SKCanvas(bitmap);
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
|
||||
var bounds = svg.Picture.CullRect;
|
||||
float scale = Math.Min(MaxPreviewSize / bounds.Width, MaxPreviewSize / bounds.Height);
|
||||
canvas.Scale(scale);
|
||||
canvas.Translate(-bounds.Left, -bounds.Top);
|
||||
canvas.DrawPicture(svg.Picture);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap = SKBitmap.Decode(stream);
|
||||
}
|
||||
|
||||
using var image = bitmap.Encode(Asset.Extension == "jpg" ? SKEncodedImageFormat.Jpeg : SKEncodedImageFormat.Png, 100);
|
||||
SetPreviewImage(image);
|
||||
|
||||
bitmap.Dispose();
|
||||
});
|
||||
}
|
||||
default:
|
||||
AssetCategory = EAssetCategory.All; // just so it sets resolved
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void SetPreviewImage(SKData data)
|
||||
{
|
||||
using var ms = new MemoryStream(data.ToArray());
|
||||
ms.Position = 0;
|
||||
|
||||
var bitmap = new BitmapImage();
|
||||
bitmap.BeginInit();
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmap.StreamSource = ms;
|
||||
bitmap.EndInit();
|
||||
bitmap.Freeze();
|
||||
|
||||
Application.Current.Dispatcher.InvokeAsync(() => PreviewImage = bitmap);
|
||||
}
|
||||
|
||||
private CancellationTokenSource _previewCts;
|
||||
public void OnIsVisible()
|
||||
{
|
||||
if (Resolved == EResolveCompute.All)
|
||||
return;
|
||||
|
||||
_previewCts?.Cancel();
|
||||
_previewCts = new CancellationTokenSource();
|
||||
var token = _previewCts.Token;
|
||||
|
||||
Task.Delay(100, token).ContinueWith(t =>
|
||||
{
|
||||
if (t.IsCanceled) return;
|
||||
ResolveAsync(EResolveCompute.All);
|
||||
}, TaskScheduler.FromCurrentSynchronizationContext());
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum EResolveCompute
|
||||
{
|
||||
None = 0,
|
||||
Category = 1 << 0,
|
||||
Preview = 1 << 1,
|
||||
|
||||
All = Category | Preview
|
||||
}
|
||||
|
|
@ -87,8 +87,8 @@ public class GameSelectorViewModel : ViewModel
|
|||
.OrderBy(value => ((int)value & 0xFF) == 0);
|
||||
private IEnumerable<DirectorySettings> EnumerateDetectedGames()
|
||||
{
|
||||
yield return GetUnrealEngineGame("Fortnite", "\\FortniteGame\\Content\\Paks", EGame.GAME_UE5_7);
|
||||
yield return DirectorySettings.Default("Fortnite [LIVE]", Constants._FN_LIVE_TRIGGER, ue: EGame.GAME_UE5_7);
|
||||
yield return GetUnrealEngineGame("Fortnite", "\\FortniteGame\\Content\\Paks", EGame.GAME_UE5_8);
|
||||
yield return DirectorySettings.Default("Fortnite [LIVE]", Constants._FN_LIVE_TRIGGER, ue: EGame.GAME_UE5_8);
|
||||
yield return GetUnrealEngineGame("Pewee", "\\RogueCompany\\Content\\Paks", EGame.GAME_RogueCompany);
|
||||
yield return GetUnrealEngineGame("Rosemallow", "\\Indiana\\Content\\Paks", EGame.GAME_UE4_21);
|
||||
yield return GetUnrealEngineGame("Catnip", "\\OakGame\\Content\\Paks", EGame.GAME_Borderlands3);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -20,7 +19,7 @@ public class SearchViewModel : ViewModel
|
|||
Descending
|
||||
}
|
||||
|
||||
private string _filterText;
|
||||
private string _filterText = string.Empty;
|
||||
public string FilterText
|
||||
{
|
||||
get => _filterText;
|
||||
|
|
@ -48,22 +47,45 @@ public class SearchViewModel : ViewModel
|
|||
set => SetProperty(ref _currentSortSizeMode, value);
|
||||
}
|
||||
|
||||
public int ResultsCount => SearchResults?.Count ?? 0;
|
||||
private int _resultsCount = 0;
|
||||
public int ResultsCount
|
||||
{
|
||||
get => _resultsCount;
|
||||
private set => SetProperty(ref _resultsCount, value);
|
||||
}
|
||||
|
||||
private GameFile _refFile;
|
||||
public GameFile RefFile
|
||||
{
|
||||
get => _refFile;
|
||||
private set => SetProperty(ref _refFile, value);
|
||||
}
|
||||
|
||||
public RangeObservableCollection<GameFile> SearchResults { get; }
|
||||
public ICollectionView SearchResultsView { get; }
|
||||
public ListCollectionView SearchResultsView { get; }
|
||||
|
||||
public SearchViewModel()
|
||||
{
|
||||
SearchResults = new RangeObservableCollection<GameFile>();
|
||||
SearchResults = [];
|
||||
SearchResultsView = new ListCollectionView(SearchResults)
|
||||
{
|
||||
Filter = e => ItemFilter(e, FilterText?.Trim().Split(' ') ?? []),
|
||||
Filter = e => ItemFilter(e, FilterText.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries)),
|
||||
};
|
||||
ResultsCount = SearchResultsView.Count;
|
||||
}
|
||||
|
||||
public void RefreshFilter()
|
||||
{
|
||||
SearchResultsView.Refresh();
|
||||
ResultsCount = SearchResultsView.Count;
|
||||
}
|
||||
|
||||
public void ChangeCollection(IEnumerable<GameFile> files, GameFile refFile = null)
|
||||
{
|
||||
SearchResults.Clear();
|
||||
SearchResults.AddRange(files);
|
||||
RefFile = refFile;
|
||||
ResultsCount = SearchResultsView.Count;
|
||||
}
|
||||
|
||||
public async Task CycleSortSizeMode()
|
||||
|
|
@ -105,7 +127,7 @@ public class SearchViewModel : ViewModel
|
|||
});
|
||||
|
||||
SearchResults.Clear();
|
||||
SearchResults.AddRange(sorted.ToList());
|
||||
SearchResults.AddRange(sorted);
|
||||
}
|
||||
|
||||
private bool ItemFilter(object item, IEnumerable<string> filters)
|
||||
|
|
|
|||
|
|
@ -40,10 +40,6 @@ public class ThreadWorkerViewModel : ViewModel
|
|||
|
||||
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
|
||||
private readonly AsyncQueue<Action<CancellationToken>> _jobs;
|
||||
private const string _at = " at ";
|
||||
private const char _dot = '.';
|
||||
private const char _colon = ':';
|
||||
private const string _gray = "#999";
|
||||
|
||||
public ThreadWorkerViewModel()
|
||||
{
|
||||
|
|
@ -104,37 +100,7 @@ public class ThreadWorkerViewModel : ViewModel
|
|||
CurrentCancellationTokenSource = null; // kill token
|
||||
|
||||
Log.Error("{Exception}", e);
|
||||
|
||||
FLogger.Append(ELog.Error, () =>
|
||||
{
|
||||
if ((e.InnerException ?? e) is { TargetSite.DeclaringType: not null } exception)
|
||||
{
|
||||
if (exception.TargetSite.ToString() == "CUE4Parse.FileProvider.GameFile get_Item(System.String)")
|
||||
{
|
||||
FLogger.Text(e.Message, Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = exception.GetType();
|
||||
FLogger.Text(t.Namespace + _dot, Constants.GRAY);
|
||||
FLogger.Text(t.Name, Constants.WHITE);
|
||||
FLogger.Text(_colon + " ", Constants.GRAY);
|
||||
FLogger.Text(exception.Message, Constants.RED, true);
|
||||
|
||||
FLogger.Text(_at, _gray);
|
||||
FLogger.Text(exception.TargetSite.DeclaringType.FullName + _dot, Constants.GRAY);
|
||||
FLogger.Text(exception.TargetSite.Name, Constants.YELLOW);
|
||||
|
||||
var p = exception.TargetSite.GetParameters();
|
||||
var parameters = new string[p.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
parameters[i] = p[i].ParameterType.Name + " " + p[i].Name;
|
||||
}
|
||||
FLogger.Text("(" + string.Join(", ", parameters) + ")", Constants.GRAY, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
FLogger.Append(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
using CUE4Parse.Utils;
|
||||
|
|
@ -13,7 +15,7 @@ using FModel.Views.Resources.Converters;
|
|||
|
||||
namespace FModel.ViewModels;
|
||||
|
||||
public class UpdateViewModel : ViewModel
|
||||
public partial class UpdateViewModel : ViewModel
|
||||
{
|
||||
private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView;
|
||||
|
||||
|
|
@ -25,7 +27,7 @@ public class UpdateViewModel : ViewModel
|
|||
|
||||
public UpdateViewModel()
|
||||
{
|
||||
Commits = new RangeObservableCollection<GitHubCommit>();
|
||||
Commits = [];
|
||||
CommitsView = new ListCollectionView(Commits)
|
||||
{
|
||||
GroupDescriptions = { new PropertyGroupDescription("Commit.Author.Date", new DateTimeToDateConverter()) }
|
||||
|
|
@ -35,43 +37,128 @@ public class UpdateViewModel : ViewModel
|
|||
RemindMeCommand.Execute(this, null);
|
||||
}
|
||||
|
||||
public async Task Load()
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
Commits.AddRange(await _apiEndpointView.GitHubApi.GetCommitHistoryAsync());
|
||||
var commits = await _apiEndpointView.GitHubApi.GetCommitHistoryAsync();
|
||||
if (commits == null || commits.Length == 0)
|
||||
return;
|
||||
|
||||
var qa = await _apiEndpointView.GitHubApi.GetReleaseAsync("qa");
|
||||
var assets = qa.Assets.OrderByDescending(x => x.CreatedAt).ToList();
|
||||
Commits.AddRange(commits);
|
||||
|
||||
for (var i = 0; i < assets.Count; i++)
|
||||
try
|
||||
{
|
||||
var asset = assets[i];
|
||||
asset.IsLatest = i == 0;
|
||||
|
||||
var commitSha = asset.Name.SubstringBeforeLast(".zip");
|
||||
var commit = Commits.FirstOrDefault(x => x.Sha == commitSha);
|
||||
if (commit != null)
|
||||
{
|
||||
commit.Asset = asset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Commits.Add(new GitHubCommit
|
||||
{
|
||||
Sha = commitSha,
|
||||
Commit = new Commit
|
||||
{
|
||||
Message = $"FModel ({commitSha[..7]})",
|
||||
Author = new Author { Name = asset.Uploader.Login, Date = asset.CreatedAt }
|
||||
},
|
||||
Author = asset.Uploader,
|
||||
Asset = asset
|
||||
});
|
||||
}
|
||||
_ = LoadCoAuthors();
|
||||
_ = LoadAssets();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
private Task LoadCoAuthors()
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var coAuthorMap = new Dictionary<GitHubCommit, HashSet<string>>();
|
||||
foreach (var commit in Commits)
|
||||
{
|
||||
if (!commit.Commit.Message.Contains("Co-authored-by"))
|
||||
continue;
|
||||
|
||||
var regex = GetCoAuthorRegex();
|
||||
var matches = regex.Matches(commit.Commit.Message);
|
||||
if (matches.Count == 0) continue;
|
||||
|
||||
commit.Commit.Message = regex.Replace(commit.Commit.Message, string.Empty).Trim();
|
||||
|
||||
coAuthorMap[commit] = [];
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
if (match.Groups.Count < 3) continue;
|
||||
|
||||
var username = match.Groups[1].Value;
|
||||
if (username.Equals("Asval", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
username = "4sval"; // found out the hard way co-authored usernames can't be trusted
|
||||
}
|
||||
|
||||
coAuthorMap[commit].Add(username);
|
||||
}
|
||||
}
|
||||
|
||||
if (coAuthorMap.Count == 0) return;
|
||||
|
||||
var uniqueUsernames = coAuthorMap.Values.SelectMany(x => x).Distinct().ToArray();
|
||||
var authorCache = new Dictionary<string, Author>();
|
||||
foreach (var username in uniqueUsernames)
|
||||
{
|
||||
try
|
||||
{
|
||||
var author = await _apiEndpointView.GitHubApi.GetUserAsync(username);
|
||||
if (author != null)
|
||||
authorCache[username] = author;
|
||||
}
|
||||
catch
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (commit, usernames) in coAuthorMap)
|
||||
{
|
||||
var coAuthors = usernames
|
||||
.Where(username => authorCache.ContainsKey(username))
|
||||
.Select(username => authorCache[username])
|
||||
.ToArray();
|
||||
|
||||
if (coAuthors.Length > 0)
|
||||
commit.CoAuthors = coAuthors;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Task LoadAssets()
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var qa = await _apiEndpointView.GitHubApi.GetReleaseAsync("qa");
|
||||
var assets = qa.Assets.OrderByDescending(x => x.CreatedAt).ToList();
|
||||
|
||||
for (var i = 0; i < assets.Count; i++)
|
||||
{
|
||||
var asset = assets[i];
|
||||
asset.IsLatest = i == 0;
|
||||
|
||||
var commitSha = asset.Name.SubstringBeforeLast(".zip");
|
||||
var commit = Commits.FirstOrDefault(x => x.Sha == commitSha);
|
||||
if (commit != null)
|
||||
{
|
||||
commit.Asset = asset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Commits.Add(new GitHubCommit
|
||||
{
|
||||
Sha = commitSha,
|
||||
Commit = new Commit
|
||||
{
|
||||
Message = $"FModel ({commitSha[..7]})",
|
||||
Author = new Author { Name = asset.Uploader.Login, Date = asset.CreatedAt }
|
||||
},
|
||||
Author = asset.Uploader,
|
||||
Asset = asset
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void DownloadLatest()
|
||||
{
|
||||
Commits.FirstOrDefault(x => x.IsDownloadable && x.Asset.IsLatest)?.Download();
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"Co-authored-by:\s*(.+?)\s*<(.+?)>", RegexOptions.IgnoreCase | RegexOptions.Multiline, "en-US")]
|
||||
private static partial Regex GetCoAuthorRegex();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,6 @@
|
|||
<Setter Property="Title" Value="About" />
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<StackPanel Margin="30 10">
|
||||
<StackPanel HorizontalAlignment="Center" Margin="0 0 0 30">
|
||||
<TextBlock Text="{Binding Source={x:Static local:Constants.APP_VERSION}, StringFormat={}FModel {0}}" FontSize="15" FontWeight="500" Foreground="#9DA3DD" FontStretch="Expanded" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Windows;
|
||||
using System.Windows;
|
||||
using FModel.ViewModels;
|
||||
|
||||
namespace FModel.Views;
|
||||
|
|
|
|||
|
|
@ -14,13 +14,6 @@
|
|||
<Setter Property="Title" Value="AES Manager" />
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using FModel.Services;
|
||||
using FModel.ViewModels;
|
||||
|
|
|
|||
|
|
@ -16,13 +16,6 @@
|
|||
<Setter Property="Title" Value="Audio Player" />
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="350" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
|
|
|||
|
|
@ -12,13 +12,6 @@
|
|||
<Setter Property="Title" Value="Backup Manager" />
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid Column="2" adonisExtensions:LayerExtension.Layer="2" Margin="10" Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Windows;
|
||||
using System.Windows;
|
||||
using FModel.ViewModels;
|
||||
|
||||
namespace FModel.Views;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Windows;
|
||||
using System.Windows;
|
||||
using FModel.Settings;
|
||||
|
||||
namespace FModel.Views;
|
||||
|
|
|
|||
|
|
@ -14,13 +14,6 @@
|
|||
<Setter Property="Title" Value="Directory Selector" />
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FModel.ViewModels;
|
||||
|
|
|
|||
|
|
@ -14,14 +14,6 @@
|
|||
<Setter Property="Title" Value="Image Merger"/>
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AdonisUI.Controls;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Extensions;
|
||||
using FModel.Settings;
|
||||
using FModel.Views.Resources.Controls;
|
||||
|
|
|
|||
45
FModel/Views/Resources/Colors.xaml
Normal file
45
FModel/Views/Resources/Colors.xaml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<SolidColorBrush x:Key="NeutralBrush" Color="White" />
|
||||
|
||||
<SolidColorBrush x:Key="BlueprintBrush" Color="DodgerBlue" />
|
||||
<SolidColorBrush x:Key="BlueprintWidgetBrush" Color="DarkViolet" />
|
||||
<SolidColorBrush x:Key="BlueprintAnimBrush" Color="Crimson" />
|
||||
<SolidColorBrush x:Key="BlueprintRigVMBrush" Color="Teal" />
|
||||
|
||||
<SolidColorBrush x:Key="CookedMetaDataBrush" Color="Yellow" />
|
||||
<SolidColorBrush x:Key="UserDefinedEnumBrush" Color="DarkGoldenrod" />
|
||||
<SolidColorBrush x:Key="UserDefinedStructBrush" Color="Tan" />
|
||||
|
||||
<SolidColorBrush x:Key="CustomizableObjectBrush" Color="DarkRed" />
|
||||
<SolidColorBrush x:Key="NaniteDisplacedMeshBrush" Color="ForestGreen" />
|
||||
|
||||
<SolidColorBrush x:Key="MaterialBrush" Color="BurlyWood" />
|
||||
<SolidColorBrush x:Key="MaterialEditorBrush" Color="Yellow" />
|
||||
|
||||
<SolidColorBrush x:Key="RigBrush" Color="YellowGreen" />
|
||||
|
||||
<SolidColorBrush x:Key="BinaryBrush" Color="Yellow" />
|
||||
<SolidColorBrush x:Key="TextureBrush" Color="MediumPurple" />
|
||||
<SolidColorBrush x:Key="ConfigBrush" Color="LightSlateGray" />
|
||||
|
||||
<SolidColorBrush x:Key="AudioBrush" Color="MediumSeaGreen" />
|
||||
<SolidColorBrush x:Key="SoundBankBrush" Color="DarkSeaGreen" />
|
||||
<SolidColorBrush x:Key="AudioEventBrush" Color="DarkTurquoise" />
|
||||
|
||||
<SolidColorBrush x:Key="VideoBrush" Color="IndianRed" />
|
||||
<SolidColorBrush x:Key="DataTableBrush" Color="SteelBlue" />
|
||||
<SolidColorBrush x:Key="CurveBrush" Color="HotPink" />
|
||||
<SolidColorBrush x:Key="PluginBrush" Color="GreenYellow" />
|
||||
<SolidColorBrush x:Key="ProjectBrush" Color="DeepSkyBlue" />
|
||||
<SolidColorBrush x:Key="LocalizationBrush" Color="CornflowerBlue" />
|
||||
<SolidColorBrush x:Key="WorldBrush" Color="Orange" />
|
||||
<SolidColorBrush x:Key="BuildDataBrush" Color="Tomato" />
|
||||
<SolidColorBrush x:Key="LevelSequenceBrush" Color="Coral" />
|
||||
<SolidColorBrush x:Key="FoliageBrush" Color="ForestGreen" />
|
||||
<SolidColorBrush x:Key="ParticleBrush" Color="Gold" />
|
||||
<SolidColorBrush x:Key="AnimationBrush" Color="Coral" />
|
||||
<SolidColorBrush x:Key="LuaBrush" Color="DarkBlue" />
|
||||
<SolidColorBrush x:Key="JsonXmlBrush" Color="LightGreen" />
|
||||
<SolidColorBrush x:Key="CodeBrush" Color="SandyBrown" />
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using ICSharpCode.AvalonEdit;
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Folding;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using FModel.Extensions;
|
||||
using ICSharpCode.AvalonEdit.Rendering;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICSharpCode.AvalonEdit.Rendering;
|
||||
|
||||
namespace FModel.Views.Resources.Controls;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.TextFormatting;
|
||||
using ICSharpCode.AvalonEdit.Rendering;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using CSCore;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using FModel.ViewModels;
|
||||
|
||||
namespace FModel.Views.Resources.Controls.Aup;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using CSCore;
|
||||
using NVorbis;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace FModel.Views.Resources.Controls.Aup;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace FModel.Views.Resources.Controls.Aup;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CSCore.DSP;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user