mirror of
https://github.com/4sval/FModel.git
synced 2026-09-20 07:24:45 -05:00
Fix new exporter / Export hotkeys / Marvel Tokon, Gears of War E-Day, Tamas: Shadowveil support
Some checks failed
FModel QA Builder / build (push) Has been cancelled
Some checks failed
FModel QA Builder / build (push) Has been cancelled
This commit is contained in:
Submodule CUE4Parse updated: 7afcbb323c...c99a1d6dfc
@@ -129,11 +129,11 @@ public partial class App
|
||||
.WriteTo.Logger(lc => lc
|
||||
.Filter.ByExcluding(IsConversionLibrary)
|
||||
.WriteTo.Console(outputTemplate: template1, theme: AnsiConsoleTheme.Literate)
|
||||
.WriteTo.File(outputTemplate: template1, path: filePath))
|
||||
.WriteTo.File(outputTemplate: template1, path: filePath, shared: true))
|
||||
.WriteTo.Logger(lc => lc
|
||||
.Filter.ByIncludingOnly(IsConversionLibrary)
|
||||
.WriteTo.Console(outputTemplate: template2, theme: AnsiConsoleTheme.Literate)
|
||||
.WriteTo.File(outputTemplate: template2, path: filePath))
|
||||
.WriteTo.File(outputTemplate: template2, path: filePath, shared: true))
|
||||
.MinimumLevel.Override("CUE4Parse_Conversion", LogEventLevel.Verbose).WriteTo.Sink(ImGuiSink.Instance)
|
||||
.CreateLogger();
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ public enum EAssetCategory : uint
|
||||
SkeletalMesh = Mesh + 2,
|
||||
CustomizableObject = Mesh + 3,
|
||||
NaniteDisplacedMesh = Mesh + 4,
|
||||
GeometryCollection = Mesh + 5,
|
||||
Texture = AssetCategoryExtensions.CategoryBase + (3 << 16),
|
||||
Materials = AssetCategoryExtensions.CategoryBase + (4 << 16),
|
||||
Material = Materials + 1,
|
||||
@@ -165,6 +166,7 @@ public enum EAssetCategory : uint
|
||||
RocoKingdomWorld = GameSpecific + 3,
|
||||
DeltaForce = GameSpecific + 4,
|
||||
LegoBatman = GameSpecific + 5,
|
||||
ArcSys = GameSpecific + 6,
|
||||
}
|
||||
|
||||
public enum EUnluacMode
|
||||
|
||||
18
FModel/Extensions/VisualTreeExtensions.cs
Normal file
18
FModel/Extensions/VisualTreeExtensions.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public static class VisualTreeExtensions
|
||||
{
|
||||
public static T FindAncestor<T>(this DependencyObject current) where T : DependencyObject
|
||||
{
|
||||
while (current != null)
|
||||
{
|
||||
if (current is T t)
|
||||
return t;
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -27,11 +27,13 @@ public class Hotkey : ViewModel
|
||||
|
||||
public bool IsTriggered(Key e)
|
||||
{
|
||||
return e == Key && Keyboard.Modifiers.HasFlag(Modifiers);
|
||||
return Key != Key.None && e == Key && Keyboard.Modifiers.HasFlag(Modifiers);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Key == Key.None) return string.Empty;
|
||||
|
||||
var str = new StringBuilder();
|
||||
|
||||
if (Modifiers.HasFlag(ModifierKeys.Control))
|
||||
@@ -46,4 +48,4 @@ public class Hotkey : ViewModel
|
||||
str.Append(Key);
|
||||
return str.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -7,6 +8,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using FModel.Extensions;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
@@ -179,6 +181,22 @@ public partial class MainWindow
|
||||
_ => CategoriesSelector.SelectedIndex
|
||||
};
|
||||
}
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportData.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Data");
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportProperties.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Properties");
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportTextures.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Textures");
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportModels.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Models");
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportWorlds.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Worlds");
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportAnimations.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Animations");
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportAudio.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Audio");
|
||||
else if (_applicationView.Status.IsReady && UserSettings.Default.ExportCode.IsTriggered(e.Key))
|
||||
OnExportHotkey("Save_Code");
|
||||
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))
|
||||
@@ -379,6 +397,22 @@ public partial class MainWindow
|
||||
childFolder.IsSelected = true;
|
||||
}
|
||||
|
||||
private void OnExportHotkey(string trigger)
|
||||
{
|
||||
if (!_applicationView.Status.IsReady || Keyboard.FocusedElement is not DependencyObject focused)
|
||||
return;
|
||||
|
||||
IList selection = focused.FindAncestor<TreeView>() == AssetsFolderName
|
||||
? new[] { AssetsFolderName.SelectedItem }
|
||||
: focused.FindAncestor<ListBox>()?.SelectedItems;
|
||||
|
||||
var exportable = selection?.OfType<object>().Where(static item => item is TreeItem or GameFileViewModel).ToArray() ?? [];
|
||||
if (exportable.Length == 0)
|
||||
return;
|
||||
|
||||
_applicationView.RightClickMenuCommand.Execute(new object[] { trigger, exportable });
|
||||
}
|
||||
|
||||
private CustomPopupPlacement[] OnQueueToastCustomPopupPlacement(Size popupSize, Size targetSize, Point offset)
|
||||
{
|
||||
return
|
||||
|
||||
@@ -64,12 +64,12 @@ public sealed class UserSettings : ViewModel
|
||||
Default.TextureExportFormat,
|
||||
Default.TextureQuality,
|
||||
Default.SaveHdrTexturesAsHdr,
|
||||
Default.ExportAllTextureMips,
|
||||
Default.MaterialExportFormat,
|
||||
Default.SaveEmbeddedMaterials,
|
||||
Default.SaveMorphTargets,
|
||||
Default.SocketExportFormat,
|
||||
Default.CompressionFormat,
|
||||
Default.ExportAllTextureMips
|
||||
Default.CompressionFormat
|
||||
);
|
||||
}
|
||||
|
||||
@@ -431,6 +431,62 @@ public sealed class UserSettings : ViewModel
|
||||
set => SetProperty(ref _nextAudio, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportData = new(Key.None);
|
||||
public Hotkey ExportData
|
||||
{
|
||||
get => _exportData;
|
||||
set => SetProperty(ref _exportData, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportProperties = new(Key.None);
|
||||
public Hotkey ExportProperties
|
||||
{
|
||||
get => _exportProperties;
|
||||
set => SetProperty(ref _exportProperties, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportTextures = new(Key.None);
|
||||
public Hotkey ExportTextures
|
||||
{
|
||||
get => _exportTextures;
|
||||
set => SetProperty(ref _exportTextures, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportModels = new(Key.None);
|
||||
public Hotkey ExportModels
|
||||
{
|
||||
get => _exportModels;
|
||||
set => SetProperty(ref _exportModels, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportWorlds = new(Key.None);
|
||||
public Hotkey ExportWorlds
|
||||
{
|
||||
get => _exportWorlds;
|
||||
set => SetProperty(ref _exportWorlds, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportAnimations = new(Key.None);
|
||||
public Hotkey ExportAnimations
|
||||
{
|
||||
get => _exportAnimations;
|
||||
set => SetProperty(ref _exportAnimations, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportAudio = new(Key.None);
|
||||
public Hotkey ExportAudio
|
||||
{
|
||||
get => _exportAudio;
|
||||
set => SetProperty(ref _exportAudio, value);
|
||||
}
|
||||
|
||||
private Hotkey _exportCode = new(Key.None);
|
||||
public Hotkey ExportCode
|
||||
{
|
||||
get => _exportCode;
|
||||
set => SetProperty(ref _exportCode, value);
|
||||
}
|
||||
|
||||
private EMeshFormat _meshExportFormat = EMeshFormat.UEFormat;
|
||||
public EMeshFormat MeshExportFormat
|
||||
{
|
||||
|
||||
@@ -48,6 +48,7 @@ using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Exports.Verse;
|
||||
using CUE4Parse.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.BinaryConfig;
|
||||
using CUE4Parse.UE4.CriWare;
|
||||
using CUE4Parse.UE4.CriWare.Readers;
|
||||
@@ -86,6 +87,7 @@ using Serilog;
|
||||
using SkiaSharp;
|
||||
using Svg.Skia;
|
||||
using UE4Config.Parsing;
|
||||
using static CUE4Parse.UE4.Versions.EGame;
|
||||
using Application = System.Windows.Application;
|
||||
using FGuid = CUE4Parse.UE4.Objects.Core.Misc.FGuid;
|
||||
|
||||
@@ -192,24 +194,24 @@ public class CUE4ParseViewModel : ViewModel
|
||||
{
|
||||
Provider = versionContainer.Game switch
|
||||
{
|
||||
EGame.GAME_StateOfDecay2 => new DefaultFileProvider(new DirectoryInfo(gameDirectory),
|
||||
GAME_StateOfDecay2 => new DefaultFileProvider(new DirectoryInfo(gameDirectory),
|
||||
[
|
||||
new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\Paks"),
|
||||
new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\DisabledPaks")
|
||||
], SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
EGame.GAME_eFootball => new DefaultFileProvider(new DirectoryInfo(gameDirectory),
|
||||
GAME_eFootball => new DefaultFileProvider(new DirectoryInfo(gameDirectory),
|
||||
[
|
||||
new(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\KONAMI\\eFootball\\ST\\Download")
|
||||
], SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
EGame.GAME_DeadByDaylight => new DefaultFileProvider(new DirectoryInfo(gameDirectory),
|
||||
GAME_DeadByDaylight => new DefaultFileProvider(new DirectoryInfo(gameDirectory),
|
||||
[
|
||||
new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\DeadByDaylight\\Saved\\PersistentDownloadDir\\DynamicContent")
|
||||
], SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
EGame.GAME_AshEchoes => new AEDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
EGame.GAME_BlackStigma => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, StringComparer.Ordinal),
|
||||
EGame.GAME_HonorofKingsWorld => new HoKWDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
EGame.GAME_LordOfMysteries => new LoMDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
EGame.GAME_ArcRaiders => new TheiaFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
GAME_AshEchoes => new AEDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
GAME_BlackStigma => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, StringComparer.Ordinal),
|
||||
GAME_HonorofKingsWorld => new HoKWDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
GAME_LordOfMysteries => new LoMDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
GAME_ArcRaiders or GAME_Highguard or GAME_MARVELTokonFightingSouls => new TheiaFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer),
|
||||
_ => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer)
|
||||
};
|
||||
|
||||
@@ -514,7 +516,7 @@ public class CUE4ParseViewModel : ViewModel
|
||||
FLogger.Text("Additive animations have their reference pose stripped, which will lead to inaccurate preview and export", Constants.WHITE, true));
|
||||
}
|
||||
|
||||
if (Provider.Versions.Game is EGame.GAME_UE4_LATEST or EGame.GAME_UE5_LATEST && !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) // ignore fortnite globally
|
||||
if (Provider.Versions.Game is GAME_UE4_LATEST or GAME_UE5_LATEST && !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) // ignore fortnite globally
|
||||
{
|
||||
FLogger.Append(ELog.Warning, () =>
|
||||
FLogger.Text($"Experimental UE version selected, likely unsuitable for '{Provider.GameDisplayName ?? Provider.ProjectName}'", Constants.WHITE, true));
|
||||
@@ -734,17 +736,17 @@ public class CUE4ParseViewModel : ViewModel
|
||||
|
||||
break;
|
||||
}
|
||||
case "dat" when Provider.Versions.Game is EGame.GAME_Aion2:
|
||||
case "dat" when Provider.Versions.Game is GAME_Aion2:
|
||||
{
|
||||
ProcessAion2DatFile(entry, updateUi, saveProperties);
|
||||
break;
|
||||
}
|
||||
case "bytes" when Provider.Versions.Game is EGame.GAME_RocoKingdomWorld:
|
||||
case "bytes" when Provider.Versions.Game is GAME_RocoKingdomWorld:
|
||||
{
|
||||
ProcessRocoBinFile(entry, updateUi, saveProperties);
|
||||
break;
|
||||
}
|
||||
case "dbc" when Provider.Versions.Game is EGame.GAME_AshesOfCreation:
|
||||
case "dbc" when Provider.Versions.Game is GAME_AshesOfCreation:
|
||||
{
|
||||
ProcessCacheDBFile(entry, updateUi, saveProperties);
|
||||
break;
|
||||
@@ -820,8 +822,8 @@ public class CUE4ParseViewModel : ViewModel
|
||||
case "py":
|
||||
case "md":
|
||||
case "h":
|
||||
case "non" when Provider.Versions.Game is EGame.GAME_RocoKingdomWorld:
|
||||
case "cam" when Provider.Versions.Game is EGame.GAME_RocoKingdomWorld:
|
||||
case "non" when Provider.Versions.Game is GAME_RocoKingdomWorld:
|
||||
case "cam" when Provider.Versions.Game is GAME_RocoKingdomWorld:
|
||||
// Uncharted Waters Origin
|
||||
case "crn":
|
||||
case "uwt":
|
||||
@@ -841,7 +843,7 @@ public class CUE4ParseViewModel : ViewModel
|
||||
|
||||
break;
|
||||
}
|
||||
case "ebd" when Provider.Versions.Game is EGame.GAME_ArcRaiders:
|
||||
case "ebd" when Provider.Versions.Game is GAME_ArcRaiders:
|
||||
case "json":
|
||||
case "Json":
|
||||
{
|
||||
@@ -982,7 +984,7 @@ public class CUE4ParseViewModel : ViewModel
|
||||
|
||||
break;
|
||||
}
|
||||
case "ustbin" when Provider.Versions.Game is EGame.GAME_DeltaForce:
|
||||
case "ustbin" when Provider.Versions.Game is GAME_DeltaForce:
|
||||
{
|
||||
var archive = entry.CreateReader();
|
||||
var ustbin = new FDeltaStringTable(archive);
|
||||
@@ -1453,6 +1455,11 @@ public class CUE4ParseViewModel : ViewModel
|
||||
|
||||
SaveAndPlaySound(cancellationToken, outputPath, audioFormat, data, saveAudio, updateUi);
|
||||
}
|
||||
if (Provider.Versions.Game is GAME_GearsofWarEDay && akMediaAsset.CustomGameData is FByteBulkData bulkData)
|
||||
{
|
||||
var shouldDecompress = UserSettings.Default.CompressedAudioMode is ECompressedAudio.PlayDecompressed;
|
||||
SaveAndPlaySound(cancellationToken, outputPath, "WEM", bulkData.Data, saveAudio, updateUi);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case UAkAudioEventData when (isNone || saveAudio) && pointer.Object.Value is UAkAudioEventData akAudioEventData:
|
||||
@@ -1470,6 +1477,12 @@ public class CUE4ParseViewModel : ViewModel
|
||||
|
||||
SaveAndPlaySound(cancellationToken, outputPath, audioFormat, data, saveAudio, updateUi);
|
||||
}
|
||||
if (Provider.Versions.Game is GAME_GearsofWarEDay && akMediaAsset.CustomGameData is FByteBulkData bulkData)
|
||||
{
|
||||
var audioName = akMediaAsset.MediaName ?? $"{akAudioEventData.Outer.Name} ({akMediaAsset.ID})";
|
||||
var outputPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), audioName);
|
||||
SaveAndPlaySound(cancellationToken, outputPath, "WEM", bulkData.Data, saveAudio, updateUi);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1487,7 +1500,7 @@ public class CUE4ParseViewModel : ViewModel
|
||||
// Borderlands 4
|
||||
case UFaceFXAnimSet when (isNone || saveAudio) && pointer.Object.Value is UFaceFXAnimSet faceFXAnimSet:
|
||||
{
|
||||
if (Provider.Versions.Game is not EGame.GAME_Borderlands4)
|
||||
if (Provider.Versions.Game is not GAME_Borderlands4)
|
||||
return false;
|
||||
|
||||
var ownerDirectory = WwiseProvider.GetOwnerDirectory(faceFXAnimSet);
|
||||
|
||||
@@ -61,7 +61,6 @@ public class ExportOptionsViewModel : ViewModel
|
||||
set
|
||||
{
|
||||
if (!SetProperty(ref field, value)) return;
|
||||
RaisePropertyChanged(nameof(SocketSettingsEnabled));
|
||||
RaisePropertyChanged(nameof(CompressionSettingsEnabled));
|
||||
RaisePropertyChanged(nameof(TextureFormatsEnabled));
|
||||
|
||||
@@ -105,8 +104,6 @@ public class ExportOptionsViewModel : ViewModel
|
||||
set => SetProperty(ref field, value);
|
||||
}
|
||||
|
||||
public bool SocketSettingsEnabled => SelectedMeshFormat == EMeshFormat.ActorX;
|
||||
|
||||
public IEnumerable<EFileCompressionFormat> CompressionFormats { get; } = Enum.GetValues<EFileCompressionFormat>();
|
||||
public EFileCompressionFormat SelectedCompressionFormat
|
||||
{
|
||||
@@ -228,11 +225,11 @@ public class ExportOptionsViewModel : ViewModel
|
||||
SelectedTextureFormat,
|
||||
TextureQuality,
|
||||
ExportHdrTexturesAsHdr,
|
||||
ExportAllTextureMips,
|
||||
SelectedMaterialDepth,
|
||||
ExportMaterials,
|
||||
ExportMorphTargets,
|
||||
SelectedSocketFormat,
|
||||
SelectedCompressionFormat,
|
||||
ExportAllTextureMips
|
||||
SelectedCompressionFormat
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -15,6 +17,7 @@ using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Settings;
|
||||
using FModel.Views;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using FModel.Views.Snooper;
|
||||
using Serilog.Events;
|
||||
|
||||
@@ -175,9 +178,9 @@ public class ExportSessionViewModel : ViewModel
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ExportAsync()
|
||||
public async Task<IReadOnlyList<ExportResult>> ExportAsync()
|
||||
{
|
||||
if (IsRunning || Session.TotalQueued == 0) return;
|
||||
if (IsRunning || Session.TotalQueued == 0) return null;
|
||||
|
||||
IsRunning = true;
|
||||
IsFinished = false;
|
||||
@@ -218,9 +221,10 @@ public class ExportSessionViewModel : ViewModel
|
||||
});
|
||||
});
|
||||
|
||||
IReadOnlyList<ExportResult> results = null;
|
||||
try
|
||||
{
|
||||
await Session.RunAsync(exportDirectory, exportOptions, progress, _cts.Token).ConfigureAwait(false);
|
||||
results = await Session.RunAsync(exportDirectory, exportOptions, progress, _cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -237,14 +241,86 @@ public class ExportSessionViewModel : ViewModel
|
||||
UpdateElapsedAndEta();
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task ExportAutomaticallyAsync()
|
||||
{
|
||||
if (UserSettings.Default.ExportImmediately)
|
||||
if (!UserSettings.Default.ExportImmediately) return;
|
||||
|
||||
var results = await ExportAsync();
|
||||
if (results is { Count: > 0 }) LogSummary(results);
|
||||
}
|
||||
|
||||
private static void LogSummary(IReadOnlyList<ExportResult> results)
|
||||
{
|
||||
if (results.Count == 1)
|
||||
{
|
||||
await ExportAsync();
|
||||
var result = results[0];
|
||||
switch (result.Success)
|
||||
{
|
||||
case true when result.DiskFilePaths is { Count: > 0 } files:
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully exported ", Constants.WHITE);
|
||||
FLogger.Link(Path.GetFileName(files[0]), files[0], true);
|
||||
});
|
||||
break;
|
||||
case false when result.Error is { } exception:
|
||||
FLogger.Append(exception);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var failed = 0;
|
||||
var groups = new Dictionary<string, (int Count, string[] Source, string[] Directory)>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (!result.Success)
|
||||
{
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.DiskFilePaths == null) continue;
|
||||
|
||||
foreach (var file in result.DiskFilePaths)
|
||||
{
|
||||
var extension = Path.GetExtension(file).TrimStart('.');
|
||||
var source = SplitDirectory(result.ObjectPath);
|
||||
var directory = SplitDirectory(file);
|
||||
groups[extension] = groups.TryGetValue(extension, out var group)
|
||||
? (group.Count + 1, CommonPrefix(group.Source, source), CommonPrefix(group.Directory, directory))
|
||||
: (1, source, directory);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (extension, group) in groups)
|
||||
{
|
||||
var source = string.Join('/', group.Source);
|
||||
var directory = string.Join(Path.DirectorySeparatorChar, group.Directory);
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text($"Successfully exported {group.Count} {extension} from ", Constants.WHITE);
|
||||
if (directory.Length > 0) FLogger.Link(source, directory, true);
|
||||
else FLogger.Text(source, Constants.WHITE, true);
|
||||
});
|
||||
}
|
||||
|
||||
if (failed > 0)
|
||||
{
|
||||
FLogger.Append(ELog.Error, () => FLogger.Text($"Failed to export {failed} asset{(failed == 1 ? "" : "s")}, open the Export Session window for more details.", Constants.WHITE, true));
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] SplitDirectory(string path) => Path.GetDirectoryName(path)?.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) ?? [];
|
||||
private static string[] CommonPrefix(string[] a, string[] b)
|
||||
{
|
||||
var i = 0;
|
||||
while (i < a.Length && i < b.Length && string.Equals(a[i], b[i], StringComparison.OrdinalIgnoreCase)) i++;
|
||||
return i == a.Length ? a : a[..i];
|
||||
}
|
||||
|
||||
public void CancelExport()
|
||||
@@ -384,6 +460,7 @@ public class ClassGroupViewModel(string name) : ViewModel
|
||||
public class ObjectGroupViewModel(string path) : ViewModel
|
||||
{
|
||||
public string Path { get; } = path;
|
||||
public string Directory { get; } = path.SubstringBeforeLast('/');
|
||||
public string Name { get; } = path.SubstringAfterLast('.');
|
||||
public ObservableCollection<LogEntryViewModel> Entries { get; } = [];
|
||||
|
||||
@@ -426,4 +503,29 @@ public class LogEntryViewModel(LogEvent log)
|
||||
_ => log.Exception?.Message ?? log.RenderMessage()
|
||||
};
|
||||
public Exception? Exception { get; } = log.Exception;
|
||||
public IReadOnlyList<ExceptionDetailsViewModel> ExceptionDetails { get; } =
|
||||
log.Exception is { } exception ? [new ExceptionDetailsViewModel(exception)] : [];
|
||||
}
|
||||
|
||||
public class ExceptionDetailsViewModel
|
||||
{
|
||||
public string Header { get; }
|
||||
public string Details { get; }
|
||||
|
||||
public ExceptionDetailsViewModel(Exception exception)
|
||||
{
|
||||
var text = exception.ToString().ReplaceLineEndings("\n");
|
||||
var newline = text.IndexOf('\n');
|
||||
|
||||
if (newline < 0)
|
||||
{
|
||||
Header = text;
|
||||
Details = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
Header = text[..newline];
|
||||
Details = text[(newline + 1)..];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using CUE4Parse.GameTypes.Borderlands3.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.Borderlands4.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.FN.Assets.Exports.DataAssets;
|
||||
using CUE4Parse.GameTypes.LegoBatman.Assets;
|
||||
using CUE4Parse.GameTypes.RED.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Objects;
|
||||
using CUE4Parse.GameTypes.SquareEnix.UE4.Assets.Exports;
|
||||
@@ -26,6 +27,7 @@ using CUE4Parse.UE4.Assets.Exports.Engine.Font;
|
||||
using CUE4Parse.UE4.Assets.Exports.Fmod;
|
||||
using CUE4Parse.UE4.Assets.Exports.FMod;
|
||||
using CUE4Parse.UE4.Assets.Exports.Foliage;
|
||||
using CUE4Parse.UE4.Assets.Exports.GeometryCollection;
|
||||
using CUE4Parse.UE4.Assets.Exports.Internationalization;
|
||||
using CUE4Parse.UE4.Assets.Exports.LevelSequence;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
@@ -217,6 +219,7 @@ public class GameFileViewModel(GameFile asset) : ViewModel
|
||||
USkeletalMesh => (EAssetCategory.SkeletalMesh, EBulkType.Meshes),
|
||||
UCustomizableObject => (EAssetCategory.CustomizableObject, EBulkType.None),
|
||||
UNaniteDisplacedMesh => (EAssetCategory.NaniteDisplacedMesh, EBulkType.None),
|
||||
UGeometryCollection => (EAssetCategory.GeometryCollection, EBulkType.None),
|
||||
|
||||
UTexture => (EAssetCategory.Texture, EBulkType.Textures),
|
||||
|
||||
@@ -265,6 +268,7 @@ public class GameFileViewModel(GameFile asset) : ViewModel
|
||||
UGbxGraphAsset or UDialogScriptData or UDialogPerformanceData when GameVersion is EGame.GAME_Borderlands4 or EGame.GAME_Borderlands3 => (EAssetCategory.Borderlands, EBulkType.Audio), // Borderlands 4; Borderlands 3;
|
||||
UFaceFXAnimSet when GameVersion is EGame.GAME_Borderlands4 => (EAssetCategory.Borderlands, EBulkType.Audio), // Borderlands 4;
|
||||
UWubAudioEvent or UWubDialogueEvent when GameVersion is EGame.GAME_LEGOBatmanLegacyoftheDarkKnight => (EAssetCategory.LegoBatman, EBulkType.Audio), // Lego Batman: Legacy of the Dark Knight;
|
||||
UREDBinaryObject => (EAssetCategory.ArcSys, EBulkType.None), // Arc System Works games;
|
||||
|
||||
_ => (EAssetCategory.All, EBulkType.None),
|
||||
};
|
||||
|
||||
@@ -517,8 +517,16 @@ public class TabControlViewModel : ViewModel
|
||||
}
|
||||
|
||||
public event EventHandler OnTabRemove;
|
||||
public void GoLeftTab() => SelectedTab = _tabItems.Previous(SelectedTab);
|
||||
public void GoRightTab() => SelectedTab = _tabItems.Next(SelectedTab);
|
||||
public void GoLeftTab()
|
||||
{
|
||||
if (_tabItems.Count > 0)
|
||||
SelectedTab = _tabItems.Previous(SelectedTab);
|
||||
}
|
||||
public void GoRightTab()
|
||||
{
|
||||
if (_tabItems.Count > 0)
|
||||
SelectedTab = _tabItems.Next(SelectedTab);
|
||||
}
|
||||
|
||||
public void RemoveOtherTabs(TabItem tab)
|
||||
{
|
||||
|
||||
@@ -131,8 +131,25 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ExceptionTreeViewItemStyle"
|
||||
TargetType="TreeViewItem">
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="Focusable"
|
||||
Value="False" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TreeViewItem">
|
||||
<ContentPresenter ContentSource="Header"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,2,8,6" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="LogEntryTemplate" DataType="{x:Type vm:LogEntryViewModel}">
|
||||
<Grid Margin="0 1" ToolTip="{Binding Exception}">
|
||||
<Grid Margin="0 1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -182,7 +199,6 @@
|
||||
<DataTemplate x:Key="ObjectGroupHeaderTemplate" DataType="{x:Type vm:ObjectGroupViewModel}">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -196,11 +212,19 @@
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
|
||||
<TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}"
|
||||
ToolTip="{Binding Path}" />
|
||||
<TextBlock Grid.Column="1" Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="{Binding Path}">
|
||||
<Run Text="{Binding Name, Mode=OneWay}"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" />
|
||||
<Run Text=" - "
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.DisabledForegroundBrush}}" />
|
||||
<Run Text="{Binding Directory, Mode=OneWay}" FontSize="11"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.DisabledForegroundBrush}}" />
|
||||
</TextBlock>
|
||||
|
||||
<Button Grid.Column="3" ToolTip="Remove from queue" Click="OnRemoveFromQueueClick"
|
||||
<Button Grid.Column="2" ToolTip="Remove from queue" Click="OnRemoveFromQueueClick"
|
||||
Visibility="{Binding DataContext.CanExport, RelativeSource={RelativeSource AncestorType=adonisControls:AdonisWindow}, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Padding="0" Margin="0" Focusable="False"
|
||||
Foreground="#EF5350" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}">
|
||||
@@ -212,7 +236,7 @@
|
||||
</Viewbox>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="4" ToolTip="Open in Explorer" Click="OnOpenInExplorerClick"
|
||||
<Button Grid.Column="3" ToolTip="Open in Explorer" Click="OnOpenInExplorerClick"
|
||||
Tag="{Binding FirstFilePath}"
|
||||
Visibility="{Binding HasFilePath, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}"
|
||||
@@ -362,7 +386,7 @@
|
||||
|
||||
<ProgressBar Grid.Row="1" Height="5" Margin="0 5 0 5" Maximum="1" Value="{Binding ProgressValue, Mode=OneWay, FallbackValue=0.5}" />
|
||||
|
||||
<TabControl Grid.Row="2" adonisExtensions:LayerExtension.Layer="2"
|
||||
<TabControl x:Name="Tabs" Grid.Row="2" adonisExtensions:LayerExtension.Layer="2"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer0BackgroundBrush}}">
|
||||
<TabItem Header="Export Logs">
|
||||
<Grid>
|
||||
@@ -389,7 +413,8 @@
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
VirtualizingPanel.ScrollUnit="Item"
|
||||
ScrollViewer.CanContentScroll="True">
|
||||
ScrollViewer.CanContentScroll="True"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="TreeViewItem" BasedOn="{StaticResource StretchTreeViewItemStyle}">
|
||||
<Style.Triggers>
|
||||
@@ -414,9 +439,43 @@
|
||||
HorizontalAlignment="Stretch" />
|
||||
|
||||
<HierarchicalDataTemplate.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:LogEntryViewModel}">
|
||||
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource LogEntryTemplate}" />
|
||||
</DataTemplate>
|
||||
<HierarchicalDataTemplate DataType="{x:Type vm:LogEntryViewModel}"
|
||||
ItemsSource="{Binding ExceptionDetails}"
|
||||
ItemContainerStyle="{StaticResource ExceptionTreeViewItemStyle}">
|
||||
<ContentPresenter Content="{Binding}"
|
||||
ContentTemplate="{StaticResource LogEntryTemplate}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
<HierarchicalDataTemplate.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:ExceptionDetailsViewModel}">
|
||||
<Border Padding="8"
|
||||
CornerRadius="3"
|
||||
BorderThickness="2,0,0,0"
|
||||
BorderBrush="#EF5350"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer1BackgroundBrush}}">
|
||||
<RichTextBox IsReadOnly="True"
|
||||
IsDocumentEnabled="False"
|
||||
MaxHeight="220"
|
||||
FontSize="10"
|
||||
BorderThickness="0"
|
||||
Padding="0"
|
||||
Background="Transparent"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<FlowDocument PagePadding="0">
|
||||
<Paragraph Margin="0">
|
||||
<Run Text="{Binding Header, Mode=OneWay}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="#EF5350" />
|
||||
<LineBreak />
|
||||
<Run Text="{Binding Details, Mode=OneWay}"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" />
|
||||
</Paragraph>
|
||||
</FlowDocument>
|
||||
</RichTextBox>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</HierarchicalDataTemplate.ItemTemplate>
|
||||
</HierarchicalDataTemplate>
|
||||
</HierarchicalDataTemplate.ItemTemplate>
|
||||
</HierarchicalDataTemplate>
|
||||
</HierarchicalDataTemplate.ItemTemplate>
|
||||
|
||||
@@ -16,7 +16,10 @@ public partial class ExportSessionWindow
|
||||
private async void OnExportClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button { DataContext: ExportSessionViewModel { CanExport: true } viewModel })
|
||||
{
|
||||
Tabs.SelectedIndex = 0;
|
||||
await viewModel.ExportAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOkClick(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -58,4 +58,5 @@
|
||||
<SolidColorBrush x:Key="RocoKingdomWorldBrush" Color="#fecf4d"></SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DeltaForceBrush" Color="LightGreen"></SolidColorBrush>
|
||||
<SolidColorBrush x:Key="BatmanBrush" Color="Gold"></SolidColorBrush>
|
||||
<SolidColorBrush x:Key="ArcSysBrush" Color="Red"></SolidColorBrush>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -122,7 +122,8 @@
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
|
||||
<MenuItem Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportData, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.Header>
|
||||
<TextBlock
|
||||
Text="{Binding PlacementTarget.SelectedItem.Asset.Extension,
|
||||
@@ -144,7 +145,9 @@
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Properties (.json)" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem Header="Properties (.json)"
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportProperties, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Properties" />
|
||||
@@ -159,7 +162,9 @@
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Texture" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem Header="Texture"
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportTextures, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Textures" />
|
||||
@@ -185,7 +190,9 @@
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Model" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem Header="Model"
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportModels, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Models" />
|
||||
@@ -211,7 +218,9 @@
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="World" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem Header="World"
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportWorlds, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Worlds" />
|
||||
@@ -241,7 +250,9 @@
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Animation" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem Header="Animation"
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportAnimations, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Animations" />
|
||||
@@ -267,7 +278,9 @@
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Audio" Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem Header="Audio"
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportAudio, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Audio" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:settings="clr-namespace:FModel.Settings"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
x:Class="FModel.Views.Resources.Controls.ContextMenus.FolderContextMenuDictionary">
|
||||
@@ -18,7 +19,8 @@
|
||||
</MenuItem.Icon>
|
||||
|
||||
<MenuItem Header="Raw Data (.uasset)"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportData, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Data" />
|
||||
@@ -38,7 +40,8 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Properties (.json)"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportProperties, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Properties" />
|
||||
@@ -58,7 +61,8 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Textures"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportTextures, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Textures" />
|
||||
@@ -78,7 +82,8 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Decompiled Blueprints"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportCode, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Code" />
|
||||
@@ -98,7 +103,8 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Models"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportModels, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Models" />
|
||||
@@ -118,7 +124,8 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Worlds"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportWorlds, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Worlds" />
|
||||
@@ -143,7 +150,8 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Animations"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportAnimations, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Animations" />
|
||||
@@ -163,7 +171,8 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Audio"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
Command="{Binding RightClickMenuCommand}"
|
||||
InputGestureText="{Binding ExportAudio, Source={x:Static settings:UserSettings.Default}}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Audio" />
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using FModel.Extensions;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
@@ -23,7 +24,7 @@ public partial class FolderContextMenuDictionary
|
||||
if (sender is not ContextMenu { PlacementTarget: FrameworkElement fe } menu)
|
||||
return;
|
||||
|
||||
var listBox = FindAncestor<ListBox>(fe);
|
||||
var listBox = fe.FindAncestor<ListBox>();
|
||||
if (listBox != null)
|
||||
{
|
||||
menu.DataContext = listBox.DataContext;
|
||||
@@ -31,7 +32,7 @@ public partial class FolderContextMenuDictionary
|
||||
return;
|
||||
}
|
||||
|
||||
var treeView = FindAncestor<TreeView>(fe);
|
||||
var treeView = fe.FindAncestor<TreeView>();
|
||||
if (treeView != null)
|
||||
{
|
||||
menu.DataContext = treeView.DataContext;
|
||||
@@ -39,17 +40,6 @@ public partial class FolderContextMenuDictionary
|
||||
}
|
||||
}
|
||||
|
||||
private static T FindAncestor<T>(DependencyObject current) where T : DependencyObject
|
||||
{
|
||||
while (current != null)
|
||||
{
|
||||
if (current is T t)
|
||||
return t;
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnFavoriteDirectoryClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not MenuItem { CommandParameter: IEnumerable<object> list } || list.FirstOrDefault() is not TreeItem folder)
|
||||
|
||||
@@ -47,14 +47,22 @@
|
||||
<Button Grid.Row="0" Grid.Column="4" Content="..." MinWidth="28" Click="OnBrowseOutputDirectory" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Export Immediately" VerticalAlignment="Center" Margin="0 8 0 0"
|
||||
Visibility="{Binding ShowExportImmediatelyOption, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
ToolTip="Start exporting automatically after all selected assets have been queued." />
|
||||
<CheckBox Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="3" Margin="0 8 0 0"
|
||||
IsChecked="{Binding ExportImmediately, Mode=TwoWay}"
|
||||
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}"
|
||||
Visibility="{Binding ShowExportImmediatelyOption, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
ToolTip="Start exporting automatically after all selected assets have been queued." />
|
||||
Visibility="{Binding ShowExportImmediatelyOption, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<Grid Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="3" Margin="0 8 0 0"
|
||||
Visibility="{Binding ShowExportImmediatelyOption, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<CheckBox Grid.Column="0"
|
||||
IsChecked="{Binding ExportImmediately, Mode=TwoWay}"
|
||||
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="5 0 0 0" FontSize="11"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.DisabledForegroundBrush}}"
|
||||
Text="Start exporting automatically after all selected assets have been queued." />
|
||||
</Grid>
|
||||
|
||||
<Separator Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="5" Style="{StaticResource CustomSeparator}" Tag="MESH" Margin="0 10 0 10" />
|
||||
|
||||
@@ -117,12 +125,10 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Socket Format" VerticalAlignment="Center" Margin="0 0 0 5"
|
||||
IsEnabled="{Binding SocketSettingsEnabled}" />
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Socket Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" Margin="0 0 0 5"
|
||||
ItemsSource="{Binding SocketFormats}"
|
||||
SelectedItem="{Binding SelectedSocketFormat, Mode=TwoWay}"
|
||||
IsEnabled="{Binding SocketSettingsEnabled}">
|
||||
SelectedItem="{Binding SelectedSocketFormat, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
|
||||
24
FModel/Views/Resources/Controls/HotkeyPresenter.cs
Normal file
24
FModel/Views/Resources/Controls/HotkeyPresenter.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace FModel.Views.Resources.Controls;
|
||||
|
||||
public class HotkeyPresenter : ItemsControl
|
||||
{
|
||||
public static readonly DependencyProperty GestureProperty = DependencyProperty.Register(
|
||||
nameof(Gesture), typeof(string), typeof(HotkeyPresenter), new PropertyMetadata(null, OnGestureChanged));
|
||||
|
||||
public string Gesture
|
||||
{
|
||||
get => (string) GetValue(GestureProperty);
|
||||
set => SetValue(GestureProperty, value);
|
||||
}
|
||||
|
||||
private static void OnGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((HotkeyPresenter) d).ItemsSource = e.NewValue is string gesture
|
||||
? gesture.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
@@ -89,4 +89,4 @@ public class HotkeyTextBox : TextBox
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public class FileToGeometryConverter : IMultiValueConverter
|
||||
EAssetCategory.SkeletalMesh => ("SkeletalMeshIconAlt", "NeutralBrush"),
|
||||
EAssetCategory.CustomizableObject => ("StaticMeshIconAlt", "CustomizableObjectBrush"),
|
||||
EAssetCategory.NaniteDisplacedMesh => ("StaticMeshIconAlt", "NaniteDisplacedMeshBrush"),
|
||||
EAssetCategory.GeometryCollection => ("GeometryCollectionIcon", "NeutralBrush"),
|
||||
|
||||
EAssetCategory.Material => ("MaterialIcon", "MaterialBrush"),
|
||||
EAssetCategory.MaterialEditorData => ("MaterialIcon", "MaterialEditorBrush"),
|
||||
@@ -93,11 +94,13 @@ public class FileToGeometryConverter : IMultiValueConverter
|
||||
|
||||
EAssetCategory.ByteCode => ("CodeIcon", "CodeBrush"),
|
||||
|
||||
// Game specific below
|
||||
EAssetCategory.Borderlands => ("BorderlandsIcon", "BorderlandsBrush"),
|
||||
EAssetCategory.Aion2 => ("AionIcon", "AionBrush"),
|
||||
EAssetCategory.RocoKingdomWorld => ("RocoKingdomWorldIcon", "RocoKingdomWorldBrush"),
|
||||
EAssetCategory.DeltaForce => ("DeltaForceIcon", "DeltaForceBrush"),
|
||||
EAssetCategory.LegoBatman => ("BatmanIcon", "BatmanBrush"),
|
||||
EAssetCategory.ArcSys => ("ArcSysIcon", "ArcSysBrush"),
|
||||
|
||||
_ => ("AssetIcon", "NeutralBrush")
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1124,6 +1124,28 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type controls:HotkeyPresenter}">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="IsHitTestVisible" Value="False" />
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Border Margin="2, 0, 0, 0" Padding="5, 2" CornerRadius="3"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer1HighlightBrush}}">
|
||||
<TextBlock Text="{Binding}" FontSize="10" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="{x:Type MenuItem}" TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
@@ -1175,13 +1197,13 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="{Binding Margin, ElementName=ItemHeader}"/>
|
||||
|
||||
<TextBlock x:Name="RippleLayerInputGesturePresenter"
|
||||
Grid.Column="2"
|
||||
Text="{TemplateBinding InputGestureText}"
|
||||
TextElement.Foreground="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type adonisControls:RippleHost}}}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="{Binding Margin, ElementName=InputGesturePresenter}"/>
|
||||
<controls:HotkeyPresenter x:Name="RippleLayerInputGesturePresenter"
|
||||
Grid.Column="2"
|
||||
Gesture="{TemplateBinding InputGestureText}"
|
||||
TextElement.Foreground="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type adonisControls:RippleHost}}}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="{Binding Margin, ElementName=InputGesturePresenter}"/>
|
||||
|
||||
<Viewbox x:Name="RippleLayerArrowPresenter"
|
||||
Grid.Column="3"
|
||||
@@ -1227,13 +1249,13 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="4, 0"/>
|
||||
|
||||
<TextBlock x:Name="InputGesturePresenter"
|
||||
Grid.Column="3"
|
||||
Text="{TemplateBinding InputGestureText}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="4, 0"/>
|
||||
<controls:HotkeyPresenter x:Name="InputGesturePresenter"
|
||||
Grid.Column="3"
|
||||
Gesture="{TemplateBinding InputGestureText}"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="4, 0"/>
|
||||
|
||||
<Viewbox x:Name="ArrowPresenter"
|
||||
Grid.Column="4"
|
||||
|
||||
@@ -428,6 +428,15 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -435,24 +444,24 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Left Switch on Directory Tab" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="0" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding DirLeftTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Right Switch on Directory Tab" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="1" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding DirRightTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
||||
<Separator Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
<!-- <TextBlock Grid.Row="0" Grid.Column="0" Text="Left Switch on Directory Tab" VerticalAlignment="Center" Margin="0 0 0 5" /> -->
|
||||
<!-- <controls:HotkeyTextBox Grid.Row="0" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5" -->
|
||||
<!-- HotKey="{Binding DirLeftTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" /> -->
|
||||
<!-- <TextBlock Grid.Row="1" Grid.Column="0" Text="Right Switch on Directory Tab" VerticalAlignment="Center" /> -->
|
||||
<!-- <controls:HotkeyTextBox Grid.Row="1" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" -->
|
||||
<!-- HotKey="{Binding DirRightTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" /> -->
|
||||
<!-- -->
|
||||
<!-- <Separator Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" /> -->
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Switch Asset Explorer" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="3" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding SwitchAssetExplorer, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Left Switch on Asset Tab" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="4" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AssetLeftTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="5" Grid.Column="0" Text="Right Switch on Asset Tab" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="5" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AssetRightTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<!-- <TextBlock Grid.Row="4" Grid.Column="0" Text="Left Switch on Asset Tab" VerticalAlignment="Center" Margin="0 0 0 5" /> -->
|
||||
<!-- <controls:HotkeyTextBox Grid.Row="4" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5" -->
|
||||
<!-- HotKey="{Binding AssetLeftTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" /> -->
|
||||
<!-- <TextBlock Grid.Row="5" Grid.Column="0" Text="Right Switch on Asset Tab" VerticalAlignment="Center" Margin="0 0 0 5" /> -->
|
||||
<!-- <controls:HotkeyTextBox Grid.Row="5" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5" -->
|
||||
<!-- HotKey="{Binding AssetRightTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" /> -->
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Add Asset Tab" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="6" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AssetAddTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
@@ -462,20 +471,47 @@
|
||||
|
||||
<Separator Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Export Raw Data" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="9" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AddAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
HotKey="{Binding ExportData, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Export Properties" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="10" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PlayPauseAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="11" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
HotKey="{Binding ExportProperties, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="11" Grid.Column="0" Text="Export Textures" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="11" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PreviousAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
HotKey="{Binding ExportTextures, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Export Models" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding NextAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="13" Grid.Column="0" Text="Remove Selected Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
HotKey="{Binding ExportModels, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="13" Grid.Column="0" Text="Export Worlds" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding ExportWorlds, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="14" Grid.Column="0" Text="Export Animations" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="14" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding ExportAnimations, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="15" Grid.Column="0" Text="Export Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="15" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding ExportAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="Export Decompiled Blueprints" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="16" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding ExportCode, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
||||
<Separator Grid.Row="17" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="18" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AddAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="19" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="19" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PlayPauseAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="20" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="20" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PreviousAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="21" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="21" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding NextAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="22" Grid.Column="0" Text="Remove Selected Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="22" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding RemoveAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
@@ -549,14 +585,13 @@
|
||||
ToolTip="Emulate Luaj's permissive parser">
|
||||
</CheckBox>
|
||||
|
||||
<Separator Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="11" Style="{StaticResource CustomSeparator}" />
|
||||
<Separator Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="11"
|
||||
Tag="OPCODE MAPPING"
|
||||
Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Text="OpcodeMap"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0 0 0 5"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="10"
|
||||
<TextBox Grid.Row="3" Grid.ColumnSpan="12"
|
||||
Margin="0 0 0 5" Height="300"
|
||||
FontSize="14" Padding="8"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top"
|
||||
@@ -703,7 +738,7 @@
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource KeyboardIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
<TextBlock Text="Keybindings" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
<TextBlock Text="Hotkeys" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
|
||||
@@ -252,13 +252,15 @@ public partial class SettingsView
|
||||
if (sender is not TextBox textBox)
|
||||
return;
|
||||
|
||||
string input = textBox.Text?.Trim() ?? string.Empty;
|
||||
|
||||
var input = textBox.Text.Trim();
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return;
|
||||
|
||||
if (TryParseKey(input, out ulong parsed))
|
||||
{
|
||||
_applicationView.SettingsView.CriwareDecryptionKey = 0;
|
||||
}
|
||||
else if (TryParseKey(input, out ulong parsed))
|
||||
{
|
||||
_applicationView.SettingsView.CriwareDecryptionKey = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseKey(string text, out ulong value)
|
||||
|
||||
@@ -136,9 +136,9 @@ public class StaticModel : UModel<MeshVertex>
|
||||
}
|
||||
|
||||
Box = staticMesh.Bounds * Constants.SCALE_DOWN_RATIO;
|
||||
for (int i = 0; i < export.Sockets.Length; i++)
|
||||
foreach (var s in export.Sockets ?? [])
|
||||
{
|
||||
if (export.Sockets[i].Load<UStaticMeshSocket>() is not { } socket) continue;
|
||||
if (s.Load<UStaticMeshSocket>() is not { } socket) continue;
|
||||
Sockets.Add(new Socket(socket));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,6 @@ public class Options
|
||||
{
|
||||
var guid = o.LightingGuid;
|
||||
if (Textures.TryGetValue(guid, out texture)) return texture != null;
|
||||
if (o.Format == EPixelFormat.PF_BC6H) return false; // BC6H is not supported by Decode thus randomly crashes the app
|
||||
|
||||
var bitmap = o switch
|
||||
{
|
||||
|
||||
@@ -160,9 +160,10 @@ public class Renderer : IDisposable
|
||||
model.Skeleton.Animate(animSet);
|
||||
Options.AddAnimation(animation);
|
||||
|
||||
foreach (var notifyEvent in animBase.Notifies)
|
||||
foreach (var notifyEvent in animBase.Notifies ?? [])
|
||||
{
|
||||
if (!notifyEvent.NotifyStateClass.TryLoad(out UObject notifyClass) ||
|
||||
if (notifyEvent.NotifyStateClass is null ||
|
||||
!notifyEvent.NotifyStateClass.TryLoad(out UObject notifyClass) ||
|
||||
!notifyClass.TryGetValue(out UObject export, "SkeletalMeshProp", "StaticMeshProp", "Mesh", "SkeletalMeshTemplate"))
|
||||
continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user