diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index aa1d671d..ffca4ec1 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -275,6 +275,13 @@ namespace FModel.Settings set => SetProperty(ref _convertAudioOnBulkExport, value); } + private bool _mergeEditorOnlyDataExports = true; + public bool MergeEditorOnlyDataExports + { + get => _mergeEditorOnlyDataExports; + set => SetProperty(ref _mergeEditorOnlyDataExports, value); + } + private bool _decompileLua; public bool DecompileLua { diff --git a/FModel/ViewModels/AssetsFolderViewModel.cs b/FModel/ViewModels/AssetsFolderViewModel.cs index d5af8e98..c76b0ad3 100644 --- a/FModel/ViewModels/AssetsFolderViewModel.cs +++ b/FModel/ViewModels/AssetsFolderViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -229,11 +230,17 @@ public class AssetsFolderViewModel return null; } + var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); foreach (var entry in entries) { TreeItem lastNode = null; TreeItem parentItem = null; - var folders = entry.Path.Split('/', StringSplitOptions.RemoveEmptyEntries); + + var path = entry.Path; + if (path.StartsWith(localAppData, StringComparison.OrdinalIgnoreCase)) + path = path[localAppData.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + var folders = path.Split('/', StringSplitOptions.RemoveEmptyEntries); var builder = new StringBuilder(64); var parentNode = treeItems; diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 0422c690..2d4923d7 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -48,6 +48,7 @@ using CUE4Parse.UE4.BinaryConfig; using CUE4Parse.UE4.CriWare; using CUE4Parse.UE4.CriWare.Readers; using CUE4Parse.UE4.FMod; +using CUE4Parse.UE4.GameFeatures; using CUE4Parse.UE4.IO; using CUE4Parse.UE4.Localization; using CUE4Parse.UE4.Lua.unluac; @@ -199,6 +200,10 @@ public class CUE4ParseViewModel : ViewModel [ new(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\KONAMI\\eFootball\\ST\\Download") ], SearchOption.AllDirectories, versionContainer, pathComparer), + "DeadByDaylight" => new DefaultFileProvider(new DirectoryInfo(gameDirectory), + [ + new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\DeadByDaylight\\Saved\\PersistentDownloadDir\\DynamicContent") + ], SearchOption.AllDirectories, versionContainer, pathComparer), _ when versionContainer.Game is EGame.GAME_AshEchoes => new AEDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer), _ when versionContainer.Game is EGame.GAME_BlackStigma => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, StringComparer.Ordinal), _ when versionContainer.Game is EGame.GAME_HonorofKingsWorld => new HoKWDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer), @@ -650,7 +655,34 @@ public class CUE4ParseViewModel : ViewModel if (saveProperties || updateUi) { - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(result.GetDisplayData(saveProperties), Formatting.Indented), saveProperties, updateUi); + var displayData = result.GetDisplayData(saveProperties); + + if (UserSettings.Default.MergeEditorOnlyDataExports && Provider.TryLoadPackage(entry.Path.SubstringBefore('.') + ".o.uasset", out var editorAsset)) + { + var pkg = Provider.LoadPackage(entry.Path); + var exports = pkg.GetExports().ToArray(); + var finalExports = new List(exports); + var editorOnlyDataExports = new HashSet(); + + foreach (var export in exports) + { + var editorData = editorAsset.GetExportOrNull(export.Name + "EditorOnlyData"); + if (editorData == null) + continue; + + export.Properties.AddRange(editorData.Properties); + editorOnlyDataExports.Add(editorData); + } + + if (editorOnlyDataExports.Count > 0) + { + finalExports.AddRange(editorAsset.GetExports().Where(editorExport => !editorOnlyDataExports.Contains(editorExport))); + } + + displayData = finalExports; + } + + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(displayData, Formatting.Indented), saveProperties, updateUi); if (saveProperties) break; // do not search for viewable exports if we are dealing with jsons } @@ -831,6 +863,13 @@ public class CUE4ParseViewModel : ViewModel break; } + case "bin" when entry.Name.Contains("GameFeatureVersePaths", StringComparison.OrdinalIgnoreCase): + { + var archive = entry.CreateReader(); + var versePathLookup = new FGameFeatureVersePathLookup(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(versePathLookup, Formatting.Indented), saveProperties, updateUi); + break; + } case "bank": { var archive = entry.CreateReader(); @@ -1076,6 +1115,11 @@ public class CUE4ParseViewModel : ViewModel var l10nData = new FAion2L10NFile(entry, Provider); TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(l10nData, Formatting.Indented), saveProperties, updateUi); } + else if (entry.NameWithoutExtension.Equals("key_manifest")) + { + var keymanifest = new FAion2KeyManifestFile(entry, Provider); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(keymanifest, Formatting.Indented), saveProperties, updateUi); + } else { FAion2DataFile datfile = entry.NameWithoutExtension switch diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml index d62e7752..e0ed13e8 100644 --- a/FModel/Views/SettingsView.xaml +++ b/FModel/Views/SettingsView.xaml @@ -46,6 +46,7 @@ + @@ -258,13 +259,25 @@ Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" /> + + + - - + Loaded="CriwareKeyBox_Loaded" />