From 9e30dfcf297217d14249f28a52ffe8f7238e4946 Mon Sep 17 00:00:00 2001 From: Imre Fekete Date: Mon, 15 Dec 2025 17:15:06 +0100 Subject: [PATCH] Added the functionality to save folder's packages decompiled blueprints to cpp files. To this end: Created functions for handling the gui commands, plus a MenuItem to call these. Added a Code enum for EBulkType. Modified CUE4ParseViewModel.Decompile to optionally create new tabs. Original behavior should be unchanged. Modified CUE4ParseViewModel.Extract to decompile if needed. --- FModel/Enums.cs | 3 ++- FModel/MainWindow.xaml | 9 +++++++++ FModel/MainWindow.xaml.cs | 12 ++++++++++++ FModel/ViewModels/CUE4ParseViewModel.cs | 23 +++++++++++++++-------- FModel/ViewModels/TabControlViewModel.cs | 10 ++++++++++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/FModel/Enums.cs b/FModel/Enums.cs index f59efb9d..c60c30b2 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -115,5 +115,6 @@ public enum EBulkType Meshes = 1 << 3, Skeletons = 1 << 4, Animations = 1 << 5, - Audio = 1 << 6 + Audio = 1 << 6, + Code = 1 << 7 } diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index f70fc5af..73255040 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -346,6 +346,15 @@ + + + + + + + + + diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index b6a957fa..0797e8e5 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -198,6 +198,18 @@ private async void OnFolderSaveClick(object sender, RoutedEventArgs e) }); } } + private async void OnFolderSaveDecompiled(object sender, RoutedEventArgs e) + { + if (AssetsFolderName.SelectedItem is TreeItem folder) + { + await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.SaveDecompiled(cancellationToken, folder); }); + FLogger.Append(ELog.Information, () => + { + FLogger.Text("Successfully saved ", Constants.WHITE); + FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.PropertiesDirectory, true); + }); + } + } private async void OnFolderTextureClick(object sender, RoutedEventArgs e) { diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index e5a9393b..bc81de62 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -580,8 +580,9 @@ public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder) => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs)); public void SaveFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto)); - + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto)); + public void SaveDecompiled(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Code | EBulkType.Auto)); public void TextureFolder(CancellationToken cancellationToken, TreeItem folder) => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Textures | EBulkType.Auto)); @@ -605,7 +606,8 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad var updateUi = !HasFlag(bulk, EBulkType.Auto); var saveProperties = HasFlag(bulk, EBulkType.Properties); var saveTextures = HasFlag(bulk, EBulkType.Textures); - var saveAudio = HasFlag(bulk, EBulkType.Audio); + var saveAudio = HasFlag(bulk, EBulkType.Audio); + var saveDecompiled = HasFlag(bulk, EBulkType.Code); switch (entry.Extension) { case "uasset": @@ -618,9 +620,14 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad { TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(result.GetDisplayData(saveProperties), Formatting.Indented), saveProperties, updateUi); if (saveProperties) break; // do not search for viewable exports if we are dealing with jsons - } - - for (var i = result.InclusiveStart; i < result.ExclusiveEnd; i++) + } + if (saveDecompiled || updateUi) + { + Decompile(entry, false); + TabControl.SelectedTab.SaveDecompiled(updateUi); + } + + for (var i = result.InclusiveStart; i < result.ExclusiveEnd; i++) { if (CheckExport(cancellationToken, result.Package, i, bulk)) break; @@ -1178,9 +1185,9 @@ public void ShowMetadata(GameFile entry) TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(package, Formatting.Indented), false, false); } - public void Decompile(GameFile entry) + public void Decompile(GameFile entry, bool AddTab = true) { - if (TabControl.CanAddTabs) TabControl.AddTab(entry); + if (TabControl.CanAddTabs && AddTab) TabControl.AddTab(entry); else TabControl.SelectedTab.SoftReset(entry); TabControl.SelectedTab.TitleExtra = "Decompiled"; diff --git a/FModel/ViewModels/TabControlViewModel.cs b/FModel/ViewModels/TabControlViewModel.cs index 9131169a..31e579e7 100644 --- a/FModel/ViewModels/TabControlViewModel.cs +++ b/FModel/ViewModels/TabControlViewModel.cs @@ -407,7 +407,17 @@ public void SaveProperty(bool updateUi) Application.Current.Dispatcher.Invoke(() => File.WriteAllText(directory, Document.Text)); SaveCheck(directory, fileName, updateUi); } + public void SaveDecompiled(bool updateUi) + { + var fileName = Path.ChangeExtension(Entry.Name, ".cpp"); + var directory = Path.Combine(UserSettings.Default.PropertiesDirectory, + UserSettings.Default.KeepDirectoryStructure ? Entry.Directory : "", fileName).Replace('\\', '/'); + + Directory.CreateDirectory(directory.SubstringBeforeLast('/')); + Application.Current.Dispatcher.Invoke(() => File.WriteAllText(directory, Document.Text)); + SaveCheck(directory, fileName, updateUi); + } private void SaveCheck(string path, string fileName, bool updateUi) { if (File.Exists(path))