From 01c19f9a84e4853dbbe0f96da8fb85a00d3a3908 Mon Sep 17 00:00:00 2001 From: Krowe Moh <27891447+Krowe-moh@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:48:59 +1100 Subject: [PATCH] Bulk Blueprint Decompilation on folders --- FModel/App.xaml.cs | 6 +++++ FModel/Settings/UserSettings.cs | 7 ++++++ FModel/ViewModels/CUE4ParseViewModel.cs | 22 +++++++++++++++---- .../Commands/RightClickMenuCommand.cs | 14 ++++++++++++ FModel/ViewModels/SettingsViewModel.cs | 2 ++ FModel/ViewModels/TabControlViewModel.cs | 10 +++++++++ .../ContextMenus/FolderContextMenu.xaml | 20 +++++++++++++++++ FModel/Views/SettingsView.xaml.cs | 1 + 8 files changed, 78 insertions(+), 4 deletions(-) diff --git a/FModel/App.xaml.cs b/FModel/App.xaml.cs index c434def6..31da19b9 100644 --- a/FModel/App.xaml.cs +++ b/FModel/App.xaml.cs @@ -92,6 +92,12 @@ public partial class App UserSettings.Default.AudioDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports"); } + if (!Directory.Exists(UserSettings.Default.CodeDirectory)) + { + createMe = true; + UserSettings.Default.CodeDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports"); + } + if (!Directory.Exists(UserSettings.Default.ModelDirectory)) { createMe = true; diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index f6a77b19..66acbeb8 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -119,6 +119,13 @@ namespace FModel.Settings set => SetProperty(ref _audioDirectory, value); } + private string _codeDirectory; + public string CodeDirectory + { + get => _codeDirectory; + set => SetProperty(ref _codeDirectory, value); + } + private string _modelDirectory; public string ModelDirectory { diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index ec595740..b482f692 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -628,6 +628,9 @@ public class CUE4ParseViewModel : ViewModel public void AudioFolder(CancellationToken cancellationToken, TreeItem folder) => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Audio | EBulkType.Auto)); + public void CodeFolder(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Code | EBulkType.Auto)); + public void Extract(CancellationToken cancellationToken, GameFile entry, bool addNewTab = false, EBulkType bulk = EBulkType.None) { ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; @@ -641,6 +644,7 @@ public class CUE4ParseViewModel : ViewModel var saveProperties = HasFlag(bulk, EBulkType.Properties); var saveTextures = HasFlag(bulk, EBulkType.Textures); var saveAudio = HasFlag(bulk, EBulkType.Audio); + var saveDecompiled = HasFlag(bulk, EBulkType.Code); switch (entry.Extension) { case "uasset": @@ -655,6 +659,13 @@ public class CUE4ParseViewModel : ViewModel if (saveProperties) break; // do not search for viewable exports if we are dealing with jsons } + if (saveDecompiled) + { + if (Decompile(entry, false)) + TabControl.SelectedTab.SaveDecompiled(updateUi); + break; + } + for (var i = result.InclusiveStart; i < result.ExclusiveEnd; i++) { if (CheckExport(cancellationToken, result.Package, i, bulk)) @@ -1364,11 +1375,13 @@ public class CUE4ParseViewModel : ViewModel } - public void Decompile(GameFile entry) + public bool Decompile(GameFile entry, bool AddTab = true) { - ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; - - if (TabControl.CanAddTabs) TabControl.AddTab(entry); + if (TabControl.CanAddTabs && AddTab) + { + ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; + TabControl.AddTab(entry); + } else TabControl.SelectedTab.SoftReset(entry); TabControl.SelectedTab.TitleExtra = "Decompiled"; @@ -1410,6 +1423,7 @@ public class CUE4ParseViewModel : ViewModel cpp = Regex.Replace(cpp, @"K2Node_([A-Za-z0-9_]+)", "$1"); TabControl.SelectedTab.SetDocumentText(cpp, false, false); + return cpp.Length > 0; } private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool isBulk, bool updateUi) diff --git a/FModel/ViewModels/Commands/RightClickMenuCommand.cs b/FModel/ViewModels/Commands/RightClickMenuCommand.cs index f4456ed9..2f2f4f17 100644 --- a/FModel/ViewModels/Commands/RightClickMenuCommand.cs +++ b/FModel/ViewModels/Commands/RightClickMenuCommand.cs @@ -207,6 +207,20 @@ public class RightClickMenuCommand : ViewModelCommand }); } break; + case "Folders_Save_Code": + foreach (var folder in folders) + { + Thread.Yield(); + cancellationToken.ThrowIfCancellationRequested(); + contextViewModel.CUE4Parse.CodeFolder(cancellationToken, folder); + + FLogger.Append(ELog.Information, () => + { + FLogger.Text("Successfully saved decompiled blueprints from ", Constants.WHITE); + FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.CodeDirectory, true); + }); + } + break; #endregion } }); diff --git a/FModel/ViewModels/SettingsViewModel.cs b/FModel/ViewModels/SettingsViewModel.cs index 2a27f836..9e0c5362 100644 --- a/FModel/ViewModels/SettingsViewModel.cs +++ b/FModel/ViewModels/SettingsViewModel.cs @@ -195,6 +195,7 @@ public class SettingsViewModel : ViewModel private string _propertiesSnapshot; private string _textureSnapshot; private string _audioSnapshot; + private string _codeSnapshot; private string _modelSnapshot; private string _gameSnapshot; private ETexturePlatform _uePlatformSnapshot; @@ -227,6 +228,7 @@ public class SettingsViewModel : ViewModel _propertiesSnapshot = UserSettings.Default.PropertiesDirectory; _textureSnapshot = UserSettings.Default.TextureDirectory; _audioSnapshot = UserSettings.Default.AudioDirectory; + _codeSnapshot = UserSettings.Default.CodeDirectory; _modelSnapshot = UserSettings.Default.ModelDirectory; _gameSnapshot = UserSettings.Default.GameDirectory; _uePlatformSnapshot = UserSettings.Default.CurrentDir.TexturePlatform; 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 class TabItem : ViewModel 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)) diff --git a/FModel/Views/Resources/Controls/ContextMenus/FolderContextMenu.xaml b/FModel/Views/Resources/Controls/ContextMenus/FolderContextMenu.xaml index 7ad5f5a8..106195b6 100644 --- a/FModel/Views/Resources/Controls/ContextMenus/FolderContextMenu.xaml +++ b/FModel/Views/Resources/Controls/ContextMenus/FolderContextMenu.xaml @@ -65,6 +65,26 @@ + + + + + + + + + + + + + + + diff --git a/FModel/Views/SettingsView.xaml.cs b/FModel/Views/SettingsView.xaml.cs index 3b5f946d..8b069da2 100644 --- a/FModel/Views/SettingsView.xaml.cs +++ b/FModel/Views/SettingsView.xaml.cs @@ -76,6 +76,7 @@ public partial class SettingsView UserSettings.Default.PropertiesDirectory = path; UserSettings.Default.TextureDirectory = path; UserSettings.Default.AudioDirectory = path; + UserSettings.Default.CodeDirectory = path; } private void OnBrowseDirectories(object sender, RoutedEventArgs e)