Merge remote-tracking branch 'upstream/dev' into UEFNLive

This commit is contained in:
Krowe Moh 2026-03-24 21:49:07 +11:00
commit d09fa33f39
24 changed files with 624 additions and 340 deletions

@ -1 +1 @@
Subproject commit dc0f8183f6551f9b5bccc8aecee65807362ef297
Subproject commit 267d479e721b424e2e913ec195c596b660aa1d09

View File

@ -27,6 +27,7 @@ public enum SettingsOut
public enum EStatusKind
{
Ready, // ready
Configuring, // waiting for user input
Loading, // doing stuff
Stopping, // trying to stop
Stopped, // stopped
@ -107,6 +108,7 @@ public enum EBulkType
Animations = 1 << 4,
Audio = 1 << 5,
Code = 1 << 6,
Raw = 1 << 7,
}
public enum EAssetCategory : uint

View File

@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
@ -62,7 +63,7 @@ public static class Helper
GetOpenedWindow<T>(windowName).Close();
}
private static bool IsWindowOpen<T>(string name = "") where T : Window
public static bool IsWindowOpen<T>(string name = "") where T : Window
{
return string.IsNullOrEmpty(name)
? Application.Current.Windows.OfType<T>().Any()
@ -111,4 +112,24 @@ public static class Helper
const float ratio = 180f / MathF.PI;
return radians * ratio;
}
public static string GetGameName(string path)
{
// install_folder/
// ├─ Engine/
// ├─ GameName/
// │ ├─ Binaries/
// │ ├─ Content/
// │ │ ├─ Paks/
// our goal is to get the GameName folder
var dir = new DirectoryInfo(path);
if (dir.Name.Equals("Paks", StringComparison.InvariantCulture) && dir.Parent is { Parent: not null } &&
dir.Parent.Name.Equals("Content", StringComparison.InvariantCulture) &&
dir.Parent.Parent.GetDirectories().Any(x => x.Name == "Binaries"))
{
return dir.Parent.Parent.Name;
}
return dir.Name;
}
}

View File

@ -12,7 +12,8 @@
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.95'}"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.90'}">
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.90'}"
AllowDrop="True">
<Window.TaskbarItemInfo>
<TaskbarItemInfo ProgressValue="1.0">
<TaskbarItemInfo.ProgressState>
@ -628,6 +629,10 @@
<Setter Property="Background" Value="{DynamicResource {x:Static adonisUi:Brushes.AlertBrush}}" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding Status.Kind}" Value="{x:Static local:EStatusKind.Configuring}">
<Setter Property="Background" Value="{DynamicResource {x:Static adonisUi:Brushes.AlertBrush}}" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding Status.Kind}" Value="{x:Static local:EStatusKind.Stopped}">
<Setter Property="Background" Value="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
<Setter Property="Foreground" Value="White" />
@ -709,5 +714,7 @@
</StackPanel>
</StatusBarItem>
</StatusBar>
<controls:DropOverlay Grid.RowSpan="99"
Panel.ZIndex="1000" />
</Grid>
</adonisControls:AdonisWindow>

View File

@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

View File

@ -461,13 +461,6 @@ namespace FModel.Settings
set => SetProperty(ref _cameraMode, value);
}
private int _wwiseMaxBnkPrefetch;
public int WwiseMaxBnkPrefetch
{
get => _wwiseMaxBnkPrefetch;
set => SetProperty(ref _wwiseMaxBnkPrefetch, value);
}
private int _previewMaxTextureSize = 1024;
public int PreviewMaxTextureSize
{

View File

@ -140,8 +140,10 @@ public class ApplicationViewModel : ViewModel
if (!bAlreadyLaunched && UserSettings.Default.PerDirectory.TryGetValue(gameDirectory, out var currentDir))
return currentDir;
Status.SetStatus(EStatusKind.Configuring);
var gameLauncherViewModel = new GameSelectorViewModel(gameDirectory);
var result = new DirectorySelector(gameLauncherViewModel).ShowDialog();
Status.SetStatus(EStatusKind.Ready);
if (!result.HasValue || !result.Value) return null;
UserSettings.Default.GameDirectory = gameLauncherViewModel.SelectedDirectory.GameDirectory;
@ -154,6 +156,35 @@ public class ApplicationViewModel : ViewModel
return null;
}
public DirectorySettings AddGameDirectory(string directory)
{
if (Status.Kind is EStatusKind.Configuring)
{
var directorySelector = Helper.GetWindow<DirectorySelector>("Directory Selector", null);
directorySelector.AddManualGame(directory);
return null;
}
else
{
Status.SetStatus(EStatusKind.Configuring);
var gameLauncherViewModel = new GameSelectorViewModel(UserSettings.Default.GameDirectory);
var directorySelector = new DirectorySelector(gameLauncherViewModel);
directorySelector.AddManualGame(directory);
var result = directorySelector.ShowDialog();
Status.SetStatus(EStatusKind.Ready);
if (!result.HasValue || !result.Value)
return null;
UserSettings.Default.GameDirectory = gameLauncherViewModel.SelectedDirectory.GameDirectory;
if (UserSettings.Default.CurrentDir.Equals(gameLauncherViewModel.SelectedDirectory))
return gameLauncherViewModel.SelectedDirectory;
UserSettings.Default.CurrentDir = gameLauncherViewModel.SelectedDirectory;
RestartWithWarning();
return null;
}
}
public void RestartWithWarning()
{
MessageBox.Show("It looks like you just changed something.\nFModel will restart to apply your changes.", "Uh oh, a restart is needed", MessageBoxButton.OK, MessageBoxImage.Warning);

View File

@ -340,12 +340,8 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
Directory.CreateDirectory(path.SubstringBeforeLast('/'));
}
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write))
using (var writer = new BinaryWriter(stream))
{
writer.Write(fileToSave.Data);
writer.Flush();
}
using var stream = new FileStream(path, FileMode.Create, FileAccess.Write);
stream.Write(fileToSave.Data);
if (File.Exists(path))
{
@ -684,25 +680,11 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
}
}
Directory.CreateDirectory(inputFilePath.SubstringBeforeLast("/"));
File.WriteAllBytes(inputFilePath, inputFileData);
var success = TryConvertToWAV(inputFilePath, inputFileData, vgmFilePath, true, out var tempWavFilePath);
wavFilePath = Path.ChangeExtension(inputFilePath, ".wav");
var vgmProcess = Process.Start(new ProcessStartInfo
{
FileName = vgmFilePath,
Arguments = $"-o \"{wavFilePath}\" \"{inputFilePath}\"",
UseShellExecute = false,
CreateNoWindow = true
});
vgmProcess?.WaitForExit(5000);
File.Delete(inputFilePath);
var success = vgmProcess?.ExitCode == 0 && File.Exists(wavFilePath);
if (!success)
{
Log.Error("Failed to convert {InputFilePath} to .wav format", inputFilePath);
Log.Error("Failed to convert {InputFilePath} to .wav format", Path.GetFileName(inputFilePath));
if (updateUi)
{
FLogger.Append(ELog.Error, () =>
@ -731,20 +713,37 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
return false;
}
Directory.CreateDirectory(SelectedAudioFile.FilePath.SubstringBeforeLast("/"));
File.WriteAllBytes(SelectedAudioFile.FilePath, SelectedAudioFile.Data);
return TryConvertToWAV(SelectedAudioFile.FilePath, SelectedAudioFile.Data, decoderPath, false, out rawFilePath);
}
rawFilePath = Path.ChangeExtension(SelectedAudioFile.FilePath, ".wav");
var decoderProcess = Process.Start(new ProcessStartInfo
private static bool TryConvertToWAV(string inputFilePath, byte[] inputFileData, string converterPath, bool usevgmstream, out string wavFilePath)
{
wavFilePath = Path.ChangeExtension(inputFilePath, ".wav");
var directory = Path.GetDirectoryName(inputFilePath);
Directory.CreateDirectory(directory);
var tempfile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + Path.GetExtension(inputFilePath));
File.WriteAllBytes(tempfile, inputFileData);
var tempWavFilePath = Path.ChangeExtension(tempfile, ".wav");
var process = Process.Start(new ProcessStartInfo
{
FileName = decoderPath,
Arguments = $"-i \"{SelectedAudioFile.FilePath}\" -o \"{rawFilePath}\"",
FileName = converterPath,
Arguments = usevgmstream ? $"-o \"{tempWavFilePath}\" \"{tempfile}\"" : $"-i \"{tempfile}\" -o \"{tempWavFilePath}\"",
UseShellExecute = false,
CreateNoWindow = true
});
decoderProcess?.WaitForExit(5000);
process?.WaitForExit(5000);
File.Delete(SelectedAudioFile.FilePath);
return decoderProcess?.ExitCode == 0 && File.Exists(rawFilePath);
File.Delete(tempfile);
var success = process?.ExitCode == 0 && File.Exists(tempWavFilePath);
if (success)
{
File.Move(tempWavFilePath, wavFilePath, true);
}
return success;
}
}

View File

@ -162,6 +162,9 @@ public class CUE4ParseViewModel : ViewModel
public CriWareProvider CriWareProvider => _criWareProviderLazy?.Value;
public ConcurrentBag<string> UnknownExtensions = [];
public int ExportedCount;
public int FailedExportCount;
public CUE4ParseViewModel()
{
var currentDir = UserSettings.Default.CurrentDir;
@ -334,7 +337,7 @@ public class CUE4ParseViewModel : ViewModel
}
Provider.Initialize();
_wwiseProviderLazy = new Lazy<WwiseProvider>(() => new WwiseProvider(Provider, UserSettings.Default.GameDirectory, UserSettings.Default.WwiseMaxBnkPrefetch));
_wwiseProviderLazy = new Lazy<WwiseProvider>(() => new WwiseProvider(Provider, UserSettings.Default.GameDirectory));
_fmodProviderLazy = new Lazy<FModProvider>(() => new FModProvider(Provider, UserSettings.Default.GameDirectory));
_criWareProviderLazy = new Lazy<CriWareProvider>(() => new CriWareProvider(Provider, UserSettings.Default.GameDirectory));
Log.Information($"{Provider.Versions.Game} ({Provider.Versions.Platform}) | Archives: x{Provider.UnloadedVfs.Count} | AES: x{Provider.RequiredKeys.Count} | Loose Files: x{Provider.Files.Count}");
@ -610,6 +613,9 @@ public class CUE4ParseViewModel : ViewModel
foreach (var f in folder.Folders) ExportFolder(cancellationToken, f);
}
public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder, EBulkType bulk)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, bulk));
public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs));
@ -825,13 +831,13 @@ public class CUE4ParseViewModel : ViewModel
case "pck":
{
var archive = entry.CreateReader();
var wwise = new WwiseReader(archive);
var wwise = new WwiseReader(archive, new WwiseGameFileSource(entry));
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi);
var medias = WwiseProvider.ExtractBankSounds(wwise);
foreach (var media in medias)
{
SaveAndPlaySound(cancellationToken, media.OutputPath, media.Extension, media.Data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, media.OutputPath, media.Extension, media.Data?.GetData() ?? [], saveAudio, updateUi);
}
break;
@ -1138,7 +1144,8 @@ public class CUE4ParseViewModel : ViewModel
case UExternalSource when (isNone || saveAudio) && pointer.Object.Value is UExternalSource externalSource:
{
var audioName = Path.GetFileNameWithoutExtension(externalSource.ExternalSourcePath);
SaveAndPlaySound(cancellationToken, audioName, "wem", externalSource.Data?.WemFile ?? [], saveAudio, updateUi);
var outputPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), audioName);
SaveAndPlaySound(cancellationToken, outputPath, "wem", externalSource.Data?.WemFile?.GetData() ?? [], saveAudio, updateUi);
return false;
}
case UAkAudioBank when (isNone || saveAudio) && pointer.Object.Value is UAkAudioBank soundBank:
@ -1146,7 +1153,7 @@ public class CUE4ParseViewModel : ViewModel
var extractedSounds = WwiseProvider.ExtractBankSounds(soundBank);
foreach (var sound in extractedSounds)
{
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi);
}
return false;
}
@ -1155,7 +1162,7 @@ public class CUE4ParseViewModel : ViewModel
var extractedSounds = WwiseProvider.ExtractAudioEventSounds(audioEvent);
foreach (var sound in extractedSounds)
{
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi);
}
return false;
}
@ -1220,12 +1227,13 @@ public class CUE4ParseViewModel : ViewModel
case UAkMediaAsset when (isNone || saveAudio) && pointer.Object.Value is UAkMediaAsset akMediaAsset:
{
var audioName = akMediaAsset.MediaName ?? akMediaAsset.Name;
if (akMediaAsset.CurrentMediaAssetData?.TryLoad<UAkMediaAssetData>(out var akMediaAssetData) is true)
var outputPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), audioName);
if (akMediaAsset.CurrentMediaAssetData?.ResolvedObject?.Object?.Value is UAkMediaAssetData akMediaAssetData)
{
var shouldDecompress = UserSettings.Default.CompressedAudioMode is ECompressedAudio.PlayDecompressed;
akMediaAssetData.Decode(shouldDecompress, out var audioFormat, out var data);
SaveAndPlaySound(cancellationToken, audioName, audioFormat, data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, outputPath, audioFormat, data, saveAudio, updateUi);
}
return false;
}
@ -1234,14 +1242,15 @@ public class CUE4ParseViewModel : ViewModel
var shouldDecompress = UserSettings.Default.CompressedAudioMode is ECompressedAudio.PlayDecompressed;
foreach (var mediaIndex in akAudioEventData.MediaList)
{
if (mediaIndex.TryLoad<UAkMediaAsset>(out var akMediaAsset))
if (mediaIndex.ResolvedObject?.Object?.Value is UAkMediaAsset akMediaAsset)
{
if (akMediaAsset.CurrentMediaAssetData?.TryLoad<UAkMediaAssetData>(out var akMediaAssetData) is true)
if (akMediaAsset.CurrentMediaAssetData?.ResolvedObject?.Object?.Value is UAkMediaAssetData akMediaAssetData)
{
var audioName = akMediaAsset.MediaName ?? $"{akAudioEventData.Outer.Name} ({akMediaAsset.ID})";
var outputPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), audioName);
akMediaAssetData.Decode(shouldDecompress, out var audioFormat, out var data);
SaveAndPlaySound(cancellationToken, audioName, audioFormat, data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, outputPath, audioFormat, data, saveAudio, updateUi);
}
}
}
@ -1253,7 +1262,7 @@ public class CUE4ParseViewModel : ViewModel
var extractedSounds = WwiseProvider.ExtractDialogBorderlands3(dialogPerformanceData);
foreach (var sound in extractedSounds)
{
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi);
}
return false;
}
@ -1263,12 +1272,13 @@ public class CUE4ParseViewModel : ViewModel
if (Provider.Versions.Game is not EGame.GAME_Borderlands4)
return false;
var ownerDirectory = WwiseProvider.GetOwnerDirectory(faceFXAnimSet);
foreach (var faceFXAnimData in faceFXAnimSet.FaceFXAnimDataList)
{
var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(faceFXAnimData.ID.Name, false);
var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(ownerDirectory, faceFXAnimData.ID.Name, false);
foreach (var sound in extractedSounds)
{
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi);
}
}
@ -1277,12 +1287,13 @@ public class CUE4ParseViewModel : ViewModel
// Borderlands 4
case UGbxGraphAsset when (isNone || saveAudio) && pointer.Object.Value is UGbxGraphAsset gbxGraphAsset:
{
var ownerDirectory = WwiseProvider.GetOwnerDirectory(gbxGraphAsset);
foreach (var (eventName, useSoundTag) in GbxAudioUtil.GetAndClearEvents())
{
var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(eventName, useSoundTag);
var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(ownerDirectory, eventName, useSoundTag);
foreach (var sound in extractedSounds)
{
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi);
SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi);
}
}
@ -1426,30 +1437,36 @@ public class CUE4ParseViewModel : ViewModel
return cpp.Length > 0;
}
private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool isBulk, bool updateUi)
private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool saveAudio, bool updateUi)
{
if (fullPath.StartsWith('/')) fullPath = fullPath[1..];
var savedAudioPath = Path.Combine(UserSettings.Default.AudioDirectory,
UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLowerInvariant()}";
if (isBulk)
if (saveAudio)
{
cancellationToken.ThrowIfCancellationRequested();
Directory.CreateDirectory(savedAudioPath.SubstringBeforeLast('/'));
using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write);
using (var writer = new BinaryWriter(stream))
{
writer.Write(data);
writer.Flush();
}
var directory = Path.GetDirectoryName(savedAudioPath);
Directory.CreateDirectory(directory);
bool conversionSuccess = true;
if (UserSettings.Default.ConvertAudioOnBulkExport)
{
conversionSuccess = AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath);
if (conversionSuccess) savedAudioPath = wavFilePath;
if (AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath))
savedAudioPath = wavFilePath;
else
{
Interlocked.Increment(ref FailedExportCount);
return;
}
}
else
{
using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write);
stream.Write(data);
}
Interlocked.Increment(ref ExportedCount);
Log.Information("Successfully saved {FilePath}", savedAudioPath);
if (updateUi && conversionSuccess)
{
@ -1463,6 +1480,9 @@ public class CUE4ParseViewModel : ViewModel
return;
}
if (!updateUi)
return;
// TODO
// since we are currently in a thread, the audio player's lifetime (memory-wise) will keep the current thread up and running until fmodel itself closes
// the solution would be to kill the current thread at this line and then open the audio player without "Application.Current.Dispatcher.Invoke"
@ -1480,6 +1500,7 @@ public class CUE4ParseViewModel : ViewModel
var toSaveDirectory = new DirectoryInfo(UserSettings.Default.ModelDirectory);
if (toSave.TryWriteToDir(toSaveDirectory, out var label, out var savedFilePath))
{
Interlocked.Increment(ref ExportedCount);
Log.Information("Successfully saved {FilePath}", savedFilePath);
if (updateUi)
{
@ -1492,6 +1513,7 @@ public class CUE4ParseViewModel : ViewModel
}
else
{
Interlocked.Increment(ref FailedExportCount);
Log.Error("{FileName} could not be saved", export.Name);
FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{export.Name}'", Constants.WHITE, true));
}
@ -1513,6 +1535,7 @@ public class CUE4ParseViewModel : ViewModel
}
});
Interlocked.Increment(ref ExportedCount);
Log.Information("{FileName} successfully exported", entry.Name);
if (updateUi)
{
@ -1525,6 +1548,7 @@ public class CUE4ParseViewModel : ViewModel
}
else
{
Interlocked.Increment(ref FailedExportCount);
Log.Error("{FileName} could not be exported", entry.Name);
if (updateUi)
FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{entry.Name}'", Constants.WHITE, true));

View File

@ -1,7 +1,11 @@
using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading;
using CUE4Parse.FileProvider.Objects;
using CUE4Parse.Utils;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;
@ -13,8 +17,21 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
public RightClickMenuCommand(ApplicationViewModel contextViewModel) : base(contextViewModel)
public RightClickMenuCommand(ApplicationViewModel contextViewModel) : base(contextViewModel) { }
private enum EAction
{
Show,
Export,
}
private enum EShowAssetType
{
None,
JSON,
Metadata,
References,
Decompile,
}
public override async void Execute(ApplicationViewModel contextViewModel, object parameter)
@ -26,203 +43,151 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
if (param.Length == 0) return;
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();
var assets = param
.Select(static item => item switch
{
GameFile gf => gf, // Search view passes GameFile directly
GameFileViewModel gvm => gvm.Asset,
_ => null
})
.Where(static gf => gf is not null).ToArray();
if (folders.Length == 0 && assets.Length == 0)
return;
var updateUi = assets.Length > 1 ? EBulkType.Auto : EBulkType.None;
var assetsGroups = assets.GroupBy(static gf => gf.Directory);
var (action, showtype, bulktype) = trigger switch
{
"Assets_Extract_New_Tab" => (EAction.Show, EShowAssetType.JSON, EBulkType.None),
"Assets_Show_Metadata" => (EAction.Show, EShowAssetType.Metadata, EBulkType.None),
"Assets_Show_References" => (EAction.Show, EShowAssetType.References, EBulkType.None),
"Assets_Decompile" => (EAction.Show, EShowAssetType.Decompile, EBulkType.Code),
"Save_Data" => (EAction.Export, EShowAssetType.None, EBulkType.Raw),
"Save_Properties" => (EAction.Export, EShowAssetType.None, EBulkType.Properties),
"Save_Textures" => (EAction.Export, EShowAssetType.None, EBulkType.Textures),
"Save_Models" => (EAction.Export, EShowAssetType.None, EBulkType.Meshes),
"Save_Animations" => (EAction.Export, EShowAssetType.None, EBulkType.Animations),
"Save_Audio" => (EAction.Export, EShowAssetType.None, EBulkType.Audio),
"Save_Code" => (EAction.Export, EShowAssetType.None, EBulkType.Code),
_ => throw new ArgumentOutOfRangeException("Unsupported asset action."),
};
Interlocked.Exchange(ref contextViewModel.CUE4Parse.ExportedCount, 0);
Interlocked.Exchange(ref contextViewModel.CUE4Parse.FailedExportCount, 0);
await _threadWorkerView.Begin(cancellationToken =>
{
switch (trigger)
if (action is EAction.Show)
{
#region Asset Commands
case "Assets_Extract_New_Tab":
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, true);
}
break;
case "Assets_Show_Metadata":
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 assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Decompile(entry);
}
break;
case "Assets_Export_Data":
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.ExportData(entry);
}
break;
case "Assets_Save_Properties":
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Properties | updateUi);
}
break;
case "Assets_Save_Textures":
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Textures | updateUi);
}
break;
case "Assets_Save_Models":
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Meshes | updateUi);
}
break;
case "Assets_Save_Animations":
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Animations | updateUi);
}
break;
case "Assets_Save_Audio":
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Audio | updateUi);
}
break;
#endregion
if (showtype is EShowAssetType.References)
assets = [assets.FirstOrDefault()];
#region Folder Commands
case "Folders_Export_Data":
foreach (var folder in folders)
{
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.ExportFolder(cancellationToken, folder);
Action<GameFile> entryAction = showtype switch
{
EShowAssetType.JSON => entry => contextViewModel.CUE4Parse.Extract(cancellationToken, entry, true),
EShowAssetType.Metadata => entry => contextViewModel.CUE4Parse.ShowMetadata(entry),
EShowAssetType.Decompile => entry => contextViewModel.CUE4Parse.Decompile(entry),
EShowAssetType.References => entry => contextViewModel.CUE4Parse.FindReferences(entry),
_ => throw new ArgumentOutOfRangeException("Unsupported asset action type."),
};
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);
foreach (var entry in assets)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
entryAction(entry);
}
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);
return;
}
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);
var (dirType, filetype) = bulktype switch
{
EBulkType.Raw => (UserSettings.Default.RawDataDirectory, "files"),
EBulkType.Properties => (UserSettings.Default.PropertiesDirectory, "json files"),
EBulkType.Textures => (UserSettings.Default.TextureDirectory, "textures"),
EBulkType.Meshes => (UserSettings.Default.ModelDirectory, "models"),
EBulkType.Animations => (UserSettings.Default.ModelDirectory, "animations"),
EBulkType.Audio => (UserSettings.Default.AudioDirectory, "audio files"),
EBulkType.Code => (UserSettings.Default.CodeDirectory, "code files"),
_ => (null, null),
};
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);
if (string.IsNullOrEmpty(dirType))
return;
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);
Action<TreeItem> folderAction = bulktype switch
{
EBulkType.Raw => folder => contextViewModel.CUE4Parse.ExportFolder(cancellationToken, folder),
_ => folder => contextViewModel.CUE4Parse.ExtractFolder(cancellationToken, folder, bulktype | EBulkType.Auto),
};
FLogger.Append(ELog.Information, () =>
{
FLogger.Text("Successfully saved audio from ", Constants.WHITE);
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.AudioDirectory, true);
});
}
break;
case "Folders_Save_Code":
foreach (var folder in folders)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.CodeFolder(cancellationToken, folder);
foreach (var folder in folders)
{
cancellationToken.ThrowIfCancellationRequested();
folderAction(folder);
FLogger.Append(ELog.Information, () =>
{
FLogger.Text("Successfully saved decompiled blueprints from ", Constants.WHITE);
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.CodeDirectory, true);
});
}
break;
#endregion
var path = Path.Combine(dirType, UserSettings.Default.KeepDirectoryStructure ? folder.PathAtThisPoint : folder.PathAtThisPoint.SubstringAfterLast('/')).Replace('\\', '/');
LogExport(contextViewModel, folder.PathAtThisPoint, path, dirType, filetype);
}
Action<GameFile, EBulkType, bool> fileAction = bulktype switch
{
EBulkType.Raw => (entry, _, update) => contextViewModel.CUE4Parse.ExportData(entry, !update),
_ => (entry, bulk, update) => contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, bulk),
};
foreach (var group in assetsGroups)
{
var directory = group.Key;
var list = group.ToArray();
var update = list.Length > 1;
var bulk = bulktype | (update ? EBulkType.Auto : EBulkType.None);
foreach (var entry in list)
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
fileAction(entry, bulk, update);
}
if (update)
{
var path = Path.Combine(dirType, UserSettings.Default.KeepDirectoryStructure ? directory : directory.SubstringAfterLast('/')).Replace('\\', '/');
LogExport(contextViewModel, directory, path, dirType, filetype);
}
}
});
}
private void LogExport(ApplicationViewModel contextViewModel, string directory, string path, string basePath, string fileType)
{
if (contextViewModel.CUE4Parse.ExportedCount > 0)
{
FLogger.Append(ELog.Information, () =>
{
FLogger.Text($"Successfully exported {contextViewModel.CUE4Parse.ExportedCount} {fileType} from ", Constants.WHITE);
FLogger.Link(directory, Path.Exists(path) ? path : basePath, true);
});
}
else if (contextViewModel.CUE4Parse.FailedExportCount == 0)
{
// Not an error because folder simply might not contain type of asset user is trying to save
FLogger.Append(ELog.Warning, () =>
{
FLogger.Text($"Failed to find any {fileType} in {directory}", Constants.WHITE, true);
});
}
if (contextViewModel.CUE4Parse.FailedExportCount > 0)
{
FLogger.Append(ELog.Error, () =>
{
FLogger.Text($"Failed to export {contextViewModel.CUE4Parse.FailedExportCount} {fileType} from {directory}", Constants.WHITE, true);
});
}
Interlocked.Exchange(ref contextViewModel.CUE4Parse.ExportedCount, 0);
Interlocked.Exchange(ref contextViewModel.CUE4Parse.FailedExportCount, 0);
}
}

View File

@ -34,34 +34,34 @@ public class TabCommand : ViewModelCommand<TabItem>
case "Find_References":
_applicationView.CUE4Parse.FindReferences(tabViewModel.Entry);
break;
case "Asset_Export_Data":
case "Save_Data":
await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(tabViewModel.Entry));
break;
case "Asset_Save_Properties":
case "Save_Properties":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Properties);
});
break;
case "Asset_Save_Textures":
case "Save_Textures":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Textures);
});
break;
case "Asset_Save_Models":
case "Save_Models":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Meshes);
});
break;
case "Asset_Save_Animations":
case "Save_Animations":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Animations);
});
break;
case "Asset_Save_Audio":
case "Save_Audio":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Audio);

View File

@ -1,6 +1,17 @@
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;
using CUE4Parse.FileProvider.Objects;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.Utils;
using CUE4Parse_Conversion.Textures;
using FModel.Extensions;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;
using FModel.ViewModels.Commands;
using FModel.Views.Resources.Controls;
@ -8,15 +19,6 @@ using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using Serilog;
using SkiaSharp;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Media.Imaging;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.FileProvider.Objects;
using CUE4Parse.Utils;
namespace FModel.ViewModels;
@ -374,8 +376,7 @@ public class TabItem : ViewModel
public void SaveImage() => SaveImage(SelectedImage, true);
private void SaveImage(TabImage image, bool updateUi)
{
if (image == null)
return;
if (image is null) return;
var path = Path.Combine(UserSettings.Default.TextureDirectory, UserSettings.Default.KeepDirectoryStructure ? Entry.Directory : "", image.ExportName).Replace('\\', '/');
@ -392,6 +393,7 @@ public class TabItem : ViewModel
private void SaveImage(TabImage image, string path)
{
if (image.ImageBuffer is null) return;
using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
fs.Write(image.ImageBuffer, 0, image.ImageBuffer.Length);
}
@ -422,6 +424,7 @@ public class TabItem : ViewModel
{
if (File.Exists(path))
{
Interlocked.Increment(ref ApplicationService.ApplicationView.CUE4Parse.ExportedCount);
Log.Information("{FileName} successfully saved", fileName);
if (updateUi)
{
@ -434,6 +437,7 @@ public class TabItem : ViewModel
}
else
{
Interlocked.Increment(ref ApplicationService.ApplicationView.CUE4Parse.FailedExportCount);
Log.Error("{FileName} could not be saved", fileName);
if (updateUi)
FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{fileName}'", Constants.WHITE, true));

View File

@ -1,4 +1,4 @@
<adonisControls:AdonisWindow x:Class="FModel.Views.DirectorySelector"
<adonisControls:AdonisWindow x:Class="FModel.Views.DirectorySelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:FModel.Views.Resources.Controls"
@ -8,7 +8,8 @@
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" IconVisibility="Collapsed" SizeToContent="Height"
MinHeight="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.20'}"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.25'}">
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.25'}"
AllowDrop="True">
<adonisControls:AdonisWindow.Style>
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
<Setter Property="Title" Value="Directory Selector" />
@ -83,9 +84,12 @@
</Button>
</Grid>
<Separator Style="{StaticResource CustomSeparator}" Tag="ADD UNDETECTED GAME" />
<Expander ExpandDirection="Down" IsExpanded="False">
<Separator Style="{StaticResource CustomSeparator}" Tag="ADD UNDETECTED GAME" Margin="0,0,0,0"/>
<TextBlock Text="Drag &amp; drop folder to quickly configure new game"
HorizontalAlignment="Center"
Foreground="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}"
Margin="0,0,0,5" />
<Expander x:Name="ManualGameExpander" ExpandDirection="Down" IsExpanded="False">
<Grid MaxWidth="{Binding ActualWidth, ElementName=Hello}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@ -137,5 +141,8 @@
HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Cancel" />
</Grid>
</Border>
<controls:DropOverlay Grid.RowSpan="99"
Grid.ColumnSpan="99"
Panel.ZIndex="1000" />
</Grid>
</adonisControls:AdonisWindow>

View File

@ -1,10 +1,6 @@
using System;
using System.IO;
using System.Linq;
using FModel.ViewModels;
using Ookii.Dialogs.Wpf;
using System.Windows;
using CUE4Parse.Utils;
namespace FModel.Views;
@ -43,28 +39,7 @@ public partial class DirectorySelector
if (folderBrowser.ShowDialog() == true)
{
HelloGameMyNameIsDirectory.Text = folderBrowser.SelectedPath;
// install_folder/
// ├─ Engine/
// ├─ GameName/
// │ ├─ Binaries/
// │ ├─ Content/
// │ │ ├─ Paks/
// our goal is to get the GameName folder
var currentFolder = folderBrowser.SelectedPath.SubstringAfterLast('\\');
if (currentFolder.Equals("Paks", StringComparison.InvariantCulture))
{
var dir = new DirectoryInfo(folderBrowser.SelectedPath);
if (dir.Parent is { Parent: not null } &&
dir.Parent.Name.Equals("Content", StringComparison.InvariantCulture) &&
dir.Parent.Parent.GetDirectories().Any(x => x.Name == "Binaries"))
{
HelloMyNameIsGame.Text = dir.Parent.Parent.Name;
return;
}
}
HelloMyNameIsGame.Text = folderBrowser.SelectedPath.SubstringAfterLast('\\');
HelloMyNameIsGame.Text = Helper.GetGameName(folderBrowser.SelectedPath);
}
}
@ -87,4 +62,11 @@ public partial class DirectorySelector
gameLauncherViewModel.DeleteSelectedGame();
}
public void AddManualGame(string directory)
{
ManualGameExpander.IsExpanded = true;
HelloMyNameIsGame.Text = Helper.GetGameName(directory);
HelloGameMyNameIsDirectory.Text = directory;
}
}

View File

@ -120,7 +120,7 @@
</MenuItem.Header>
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Export_Data" />
<Binding Source="Save_Data" />
<Binding Path="PlacementTarget.SelectedItems" RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -135,7 +135,7 @@
<MenuItem Header="Save Properties (.json)" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Properties" />
<Binding Source="Save_Properties" />
<Binding Path="PlacementTarget.SelectedItems" RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -150,7 +150,7 @@
<MenuItem Header="Save Texture" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Textures" />
<Binding Source="Save_Textures" />
<Binding Path="PlacementTarget.SelectedItems" RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -176,7 +176,7 @@
<MenuItem Header="Save Model" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Models" />
<Binding Source="Save_Models" />
<Binding Path="PlacementTarget.SelectedItems" RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -202,7 +202,7 @@
<MenuItem Header="Save Animation" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Animations" />
<Binding Source="Save_Animations" />
<Binding Path="PlacementTarget.SelectedItems" RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -228,7 +228,7 @@
<MenuItem Header="Save Audio" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Audio" />
<Binding Source="Save_Audio" />
<Binding Path="PlacementTarget.SelectedItems" RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
</MenuItem.CommandParameter>

View File

@ -9,7 +9,7 @@
Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Folders_Export_Data" />
<Binding Source="Save_Data" />
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
@ -29,7 +29,7 @@
Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Folders_Save_Properties" />
<Binding Source="Save_Properties" />
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
@ -49,7 +49,7 @@
Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Folders_Save_Textures" />
<Binding Source="Save_Textures" />
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
@ -89,7 +89,7 @@
Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Folders_Save_Models" />
<Binding Source="Save_Models" />
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
@ -109,7 +109,7 @@
Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Folders_Save_Animations" />
<Binding Source="Save_Animations" />
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>
@ -129,7 +129,7 @@
Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Folders_Save_Audio" />
<Binding Source="Save_Audio" />
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
</MultiBinding>

View File

@ -0,0 +1,58 @@
<UserControl x:Class="FModel.Views.Resources.Controls.DropOverlay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FModel.Views.Resources.Controls"
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
Visibility="Collapsed"
Background="Transparent"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid Background="#DD000000"
IsHitTestVisible="False"
Grid.Row="0"
Grid.RowSpan="99">
<Border BorderThickness="2"
CornerRadius="10"
Margin="60"
Background="#1FFFFFFF">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentBrush}}"
StrokeThickness="2"
StrokeDashArray="4 2"
StrokeDashCap="Round"
Width="{Binding RelativeSource={RelativeSource AncestorType=Border}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource AncestorType=Border}, Path=ActualHeight}"
RadiusX="10"
RadiusY="10" />
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
<StackPanel VerticalAlignment="Center">
<Viewbox Width="80"
Height="80"
Margin="0,0,0,24"
HorizontalAlignment="Center">
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}"
Data="{StaticResource ImportIcon}" />
</Viewbox>
<TextBlock x:Name="TitleText" Text="Drop .usmap to import"
Foreground="White"
FontSize="24"
FontWeight="Bold"
HorizontalAlignment="Center" />
<TextBlock x:Name="DescriptionText" Text="Mapping file will be applied immediately"
Foreground="LightGray"
FontSize="14"
HorizontalAlignment="Center"
Margin="0,10,0,0" />
</StackPanel>
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,169 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using FModel.Services;
using FModel.Settings;
using FModel.ViewModels;
namespace FModel.Views.Resources.Controls;
public partial class DropOverlay : UserControl
{
enum DragStatus
{
None,
File,
Folder,
}
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
private DragStatus _dragStatus = DragStatus.None;
private string _path = null;
public DropOverlay()
{
InitializeComponent();
Loaded += OnLoaded;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetState()
{
_dragStatus = DragStatus.None;
_path = null;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var window = Window.GetWindow(this);
if (window is null)
return;
window.PreviewDragEnter += OnPreviewDragEnter;
window.PreviewDragOver += OnPreviewDragOver;
window.PreviewDragLeave += OnPreviewDragLeave;
window.Drop += OnDrop;
}
private void OnPreviewDragEnter(object sender, DragEventArgs e)
{
GetValidTarget(sender, e);
if (_dragStatus is DragStatus.None)
{
e.Effects = DragDropEffects.None;
}
else
{
Visibility = Visibility.Visible;
e.Effects = DragDropEffects.Copy;
if (_dragStatus is DragStatus.Folder)
{
TitleText.Text = "Drop folder to add new game";
DescriptionText.Text = "Folder will be added to the directory selector";
}
else if (_dragStatus is DragStatus.File)
{
TitleText.Text = "Drop .usmap to import";
DescriptionText.Text = "Mapping file will be applied immediately";
}
}
e.Handled = true;
}
private void OnPreviewDragOver(object sender, DragEventArgs e)
{
e.Effects = _dragStatus is DragStatus.None ? DragDropEffects.None : DragDropEffects.Copy;
e.Handled = true;
}
private void OnPreviewDragLeave(object sender, DragEventArgs e)
{
Visibility = Visibility.Collapsed;
ResetState();
}
private async void OnDrop(object sender, DragEventArgs e)
{
Visibility = Visibility.Collapsed;
e.Handled = true;
switch (_dragStatus)
{
case DragStatus.Folder:
await Dispatcher.InvokeAsync(() => _applicationView.AddGameDirectory(_path));
break;
case DragStatus.File:
UserSettings.IsEndpointValid(EEndpointType.Mapping, out var oldMappingsEndpoint);
try
{
var newMappingsEndpoint = new EndpointSettings() { Overwrite = true, FilePath = _path };
UserSettings.Default.CurrentDir.Endpoints[(int) EEndpointType.Mapping] = newMappingsEndpoint;
await _applicationView.CUE4Parse.InitMappings();
_applicationView.SettingsView.MappingEndpoint = newMappingsEndpoint;
}
catch (Exception ex)
{
UserSettings.Default.CurrentDir.Endpoints[(int) EEndpointType.Mapping] = oldMappingsEndpoint;
FLogger.Append(ELog.Error, () =>
{
FLogger.Text($"Failed to load mapping file: {ex.Message}", Constants.WHITE, true);
});
}
break;
default:
break;
}
ResetState();
}
private void GetValidTarget(object sender, DragEventArgs e)
{
if (!_applicationView.Status.IsReady || !e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetData(DataFormats.FileDrop) is not string[] files)
return;
bool directorySelectorIsVisible = _applicationView.Status.Kind is EStatusKind.Configuring;
if (!directorySelectorIsVisible && (Helper.IsWindowOpen<DictionaryEditor>() || Helper.IsWindowOpen<EndpointEditor>()))
{
_applicationView.Status.SetStatus(EStatusKind.Configuring);
ResetState();
return;
}
switch (sender)
{
case MainWindow or SettingsView when !directorySelectorIsVisible:
foreach (var path in files)
{
if (Directory.Exists(path))
{
_path = path;
_dragStatus = DragStatus.Folder;
return;
}
else if (File.Exists(path) && Path.GetExtension(path).Equals(".usmap", StringComparison.OrdinalIgnoreCase))
{
_path = path;
_dragStatus = DragStatus.File;
return;
}
}
break;
case DirectorySelector:
if (files.FirstOrDefault(f => Directory.Exists(f)) is { } folder)
{
_path = folder;
_dragStatus = DragStatus.Folder;
return;
}
break;
default:
break;
}
ResetState();
}
}

View File

@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FModel.Extensions;
using FModel.Services;
using FModel.Settings;
@ -23,8 +24,9 @@ public partial class EndpointEditor
InitializeComponent();
Title = title;
TargetResponse.SyntaxHighlighting =
EndpointResponse.SyntaxHighlighting = AvalonExtensions.HighlighterSelector("json");
TargetResponse.SyntaxHighlighting = EndpointResponse.SyntaxHighlighting = AvalonExtensions.HighlighterSelector("json");
TargetResponse.TextArea.TextView.LinkTextForegroundBrush = Brushes.DodgerBlue;
EndpointResponse.TextArea.TextView.LinkTextForegroundBrush = Brushes.DodgerBlue;
InstructionBox.Text = type switch
{

View File

@ -98,6 +98,7 @@
<Geometry x:Key="CssIcon">M5,3L4.35,6.34H17.94L17.5,8.5H3.92L3.26,11.83H16.85L16.09,15.64L10.61,17.45L5.86,15.64L6.19,14H2.85L2.06,18L9.91,21L18.96,18L20.16,11.97L20.4,10.76L21.94,3H5Z</Geometry>
<Geometry x:Key="CsvIcon">M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2M15 16L13 20H10L12 16H9V11H15V16M13 9V3.5L18.5 9H13Z</Geometry>
<Geometry x:Key="AIIcon">M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z</Geometry>
<Geometry x:Key="ImportIcon">M14,12L10,8V11H2V13H10V16M20,18V6C20,4.89 19.1,4 18,4H6A2,2 0 0,0 4,6V9H6V6H18V18H6V15H4V18A2,2 0 0,0 6,20H18A2,2 0 0,0 20,18Z</Geometry>
<!-- For specific games-->
<Geometry x:Key="BorderlandsIcon">M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6 ZM12,11A3,3 0 1,0 12,17A3,3 0 0,0 12,11 ZM12,12.5L14,16H13L12,14.5L11,16H10L12,12.5Z</Geometry>

View File

@ -932,7 +932,7 @@
</MenuItem.IsEnabled>
</MenuItem>
<Separator />
<MenuItem Command="{Binding TabCommand}" CommandParameter="Asset_Export_Data">
<MenuItem Command="{Binding TabCommand}" CommandParameter="Save_Data">
<MenuItem.Header>
<TextBlock Text="{Binding Entry.Extension, FallbackValue='Export Raw Data', StringFormat='Export Raw Data (.{0})'}" />
</MenuItem.Header>
@ -944,7 +944,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Properties (.json)" Command="{Binding TabCommand}" CommandParameter="Asset_Save_Properties">
<MenuItem Header="Save Properties (.json)" Command="{Binding TabCommand}" CommandParameter="Save_Properties">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
@ -953,7 +953,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Texture" Command="{Binding TabCommand}" CommandParameter="Asset_Save_Textures">
<MenuItem Header="Save Texture" Command="{Binding TabCommand}" CommandParameter="Save_Textures">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
@ -962,7 +962,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Model" Command="{Binding TabCommand}" CommandParameter="Asset_Save_Models">
<MenuItem Header="Save Model" Command="{Binding TabCommand}" CommandParameter="Save_Models">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
@ -971,7 +971,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Animation" Command="{Binding TabCommand}" CommandParameter="Asset_Save_Animations">
<MenuItem Header="Save Animation" Command="{Binding TabCommand}" CommandParameter="Save_Animations">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
@ -980,7 +980,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Audio" Command="{Binding TabCommand}" CommandParameter="Asset_Save_Audio">
<MenuItem Header="Save Audio" Command="{Binding TabCommand}" CommandParameter="Save_Audio">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">

View File

@ -216,7 +216,7 @@
</MenuItem.Header>
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Export_Data" />
<Binding Source="Save_Data" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -231,7 +231,7 @@
<MenuItem Header="Save Properties (.json)" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Properties" />
<Binding Source="Save_Properties" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -246,7 +246,7 @@
<MenuItem Header="Save Texture" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Textures" />
<Binding Source="Save_Textures" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -261,7 +261,7 @@
<MenuItem Header="Save Model" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Models" />
<Binding Source="Save_Models" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -276,7 +276,7 @@
<MenuItem Header="Save Animation" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Animations" />
<Binding Source="Save_Animations" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -291,7 +291,7 @@
<MenuItem Header="Save Audio" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Audio" />
<Binding Source="Save_Audio" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -561,7 +561,7 @@
</MenuItem.Header>
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Export_Data" />
<Binding Source="Save_Data" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -576,7 +576,7 @@
<MenuItem Header="Save Properties (.json)" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Properties" />
<Binding Source="Save_Properties" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -591,7 +591,7 @@
<MenuItem Header="Save Texture" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Textures" />
<Binding Source="Save_Textures" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -606,7 +606,7 @@
<MenuItem Header="Save Model" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Models" />
<Binding Source="Save_Models" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -621,7 +621,7 @@
<MenuItem Header="Save Animation" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Animations" />
<Binding Source="Save_Animations" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -636,7 +636,7 @@
<MenuItem Header="Save Audio" Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Audio" />
<Binding Source="Save_Audio" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>

View File

@ -10,7 +10,8 @@
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" IconVisibility="Collapsed" SizeToContent="Height"
MinHeight="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.10'}"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.45'}">
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.45'}"
AllowDrop="True">
<adonisControls:AdonisWindow.Style>
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
<Setter Property="Title" Value="Settings" />
@ -43,7 +44,6 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@ -161,7 +161,7 @@
IsChecked="{Binding KeepDirectoryStructure, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<TextBlock Grid.Row="10" Grid.Column="0" Text="Local Mapping File" VerticalAlignment="Center" Margin="0 5 0 0" />
<TextBlock Grid.Row="10" Grid.Column="0" Text="Local Mapping File (drag &amp; drop)" VerticalAlignment="Center" Margin="0 5 0 0" />
<CheckBox Grid.Row="10" Grid.Column="2" Margin="0 5 0 0"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
@ -249,19 +249,14 @@
Margin="0 5 0 10"
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<TextBlock Grid.Row="20" Grid.Column="0" Text="Max Wwise Bank (.BNK) Prefetch" VerticalAlignment="Center" Margin="0 0 0 5" />
<Slider Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="5" TickPlacement="None" Minimum="0" Maximum="2048" Ticks="0,8,32,128,256,512,1024,2048"
AutoToolTipPlacement="BottomRight" IsMoveToPointEnabled="True" IsSnapToTickEnabled="True" Margin="0 5 0 5"
Value="{Binding WwiseMaxBnkPrefetch, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"/>
<TextBlock Grid.Row="21"
<TextBlock Grid.Row="20"
Grid.Column="0"
Text="CRIWARE Decryption Key"
VerticalAlignment="Center"
Margin="0 0 0 10" />
<TextBox x:Name="CriwareKeyBox"
Grid.Row="21"
Grid.Row="20"
Grid.Column="2"
Grid.ColumnSpan="5"
Margin="0 5 0 10"
@ -707,5 +702,8 @@
HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="OK" Click="OnClick" />
</Grid>
</Border>
<controls:DropOverlay Grid.RowSpan="99"
Grid.ColumnSpan="99"
Panel.ZIndex="1000" />
</Grid>
</adonisControls:AdonisWindow>

View File

@ -158,7 +158,11 @@ public partial class SettingsView
private void OpenCustomVersions(object sender, RoutedEventArgs e)
{
var editor = new DictionaryEditor(_applicationView.SettingsView.SelectedCustomVersions, "Versioning Configuration (Custom Versions)");
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Configuring);
var result = editor.ShowDialog();
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Ready);
if (!result.HasValue || !result.Value)
return;
@ -168,7 +172,11 @@ public partial class SettingsView
private void OpenOptions(object sender, RoutedEventArgs e)
{
var editor = new DictionaryEditor(_applicationView.SettingsView.SelectedOptions, "Versioning Configuration (Options)");
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Configuring);
var result = editor.ShowDialog();
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Ready);
if (!result.HasValue || !result.Value)
return;
@ -178,7 +186,11 @@ public partial class SettingsView
private void OpenMapStructTypes(object sender, RoutedEventArgs e)
{
var editor = new DictionaryEditor(_applicationView.SettingsView.SelectedMapStructTypes, "Versioning Configuration (MapStructTypes)");
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Configuring);
var result = editor.ShowDialog();
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Ready);
if (!result.HasValue || !result.Value)
return;
@ -189,14 +201,22 @@ public partial class SettingsView
{
var editor = new EndpointEditor(
_applicationView.SettingsView.AesEndpoint, "Endpoint Configuration (AES)", EEndpointType.Aes);
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Configuring);
editor.ShowDialog();
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Ready);
}
private void OpenMappingEndpoint(object sender, RoutedEventArgs e)
{
var editor = new EndpointEditor(
_applicationView.SettingsView.MappingEndpoint, "Endpoint Configuration (Mapping)", EEndpointType.Mapping);
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Configuring);
editor.ShowDialog();
if (_applicationView.Status.IsReady)
_applicationView.Status.SetStatus(EStatusKind.Ready);
}
private void CriwareKeyBox_Loaded(object sender, RoutedEventArgs e)