From b32843860e4304db70fcce2a3b74d27a24215612 Mon Sep 17 00:00:00 2001 From: Razmoth <32140579+Razmoth@users.noreply.github.com> Date: Fri, 2 Feb 2024 22:36:50 +0400 Subject: [PATCH] - [GUI] Add option to export selected assets in `AssetBrowser`. --- AssetStudio.CLI/Program.cs | 8 +- AssetStudio.GUI/AssetBrowser.Designer.cs | 201 ++++++++++++----- AssetStudio.GUI/AssetBrowser.cs | 268 ++++++++++++++++++++--- AssetStudio.GUI/MainForm.cs | 6 +- AssetStudio.GUI/Studio.cs | 31 +-- AssetStudio/AssetsHelper.cs | 13 +- AssetStudio/AssetsManager.cs | 17 -- 7 files changed, 408 insertions(+), 136 deletions(-) diff --git a/AssetStudio.CLI/Program.cs b/AssetStudio.CLI/Program.cs index 26dbca1..e7b0da9 100644 --- a/AssetStudio.CLI/Program.cs +++ b/AssetStudio.CLI/Program.cs @@ -115,15 +115,11 @@ namespace AssetStudio.CLI { throw new Exception("Unable to build AssetMap with input_path as a file !!"); } - var resetEvent = new ManualResetEvent(false); - AssetsHelper.BuildAssetMap(files, o.MapName, game, o.Output.FullName, o.MapType, resetEvent, o.TypeFilter, o.NameFilter, o.ContainerFilter); - resetEvent.WaitOne(); + AssetsHelper.BuildAssetMap(files, o.MapName, game, o.Output.FullName, o.MapType, o.TypeFilter, o.NameFilter, o.ContainerFilter); } if (o.MapOp.HasFlag(MapOpType.Both)) { - var resetEvent = new ManualResetEvent(false); - AssetsHelper.BuildBoth(files, o.MapName, o.Input.FullName, game, o.Output.FullName, o.MapType, resetEvent, o.TypeFilter, o.NameFilter, o.ContainerFilter); - resetEvent.WaitOne(); + AssetsHelper.BuildBoth(files, o.MapName, o.Input.FullName, game, o.Output.FullName, o.MapType, o.TypeFilter, o.NameFilter, o.ContainerFilter); } if (o.MapOp.Equals(MapOpType.None) || o.MapOp.HasFlag(MapOpType.Load)) { diff --git a/AssetStudio.GUI/AssetBrowser.Designer.cs b/AssetStudio.GUI/AssetBrowser.Designer.cs index 780e284..cacac0c 100644 --- a/AssetStudio.GUI/AssetBrowser.Designer.cs +++ b/AssetStudio.GUI/AssetBrowser.Designer.cs @@ -31,63 +31,37 @@ namespace AssetStudio.GUI /// private void InitializeComponent() { - assetDataGridView = new DataGridView(); - tableLayoutPanel1 = new TableLayoutPanel(); tableLayoutPanel2 = new TableLayoutPanel(); loadAssetMap = new Button(); clear = new Button(); loadSelected = new Button(); - searchTextBox = new TextBox(); + exportSelected = new Button(); + assetDataGridView = new DataGridView(); + tableLayoutPanel1 = new TableLayoutPanel(); + tableLayoutPanel3 = new TableLayoutPanel(); + sourceTextBox = new TextBox(); + pathTextBox = new TextBox(); + nameTextBox = new TextBox(); + containerTextBox = new TextBox(); + typeTextBox = new TextBox(); + tableLayoutPanel2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)assetDataGridView).BeginInit(); tableLayoutPanel1.SuspendLayout(); - tableLayoutPanel2.SuspendLayout(); - FormClosing += AssetBrowser_FormClosing; + tableLayoutPanel3.SuspendLayout(); SuspendLayout(); // - // assetDataGridView - // - assetDataGridView.AllowUserToAddRows = false; - assetDataGridView.AllowUserToDeleteRows = false; - assetDataGridView.AllowUserToResizeRows = false; - assetDataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; - assetDataGridView.Dock = DockStyle.Fill; - assetDataGridView.Location = new System.Drawing.Point(3, 38); - assetDataGridView.Name = "assetDataGridView"; - assetDataGridView.ReadOnly = true; - assetDataGridView.RowTemplate.Height = 25; - assetDataGridView.Size = new System.Drawing.Size(518, 250); - assetDataGridView.TabIndex = 2; - assetDataGridView.VirtualMode = true; - assetDataGridView.CellValueNeeded += AssetDataGridView_CellValueNeeded; - assetDataGridView.ColumnHeaderMouseClick += AssetListView_ColumnHeaderMouseClick; - // - // tableLayoutPanel1 - // - tableLayoutPanel1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; - tableLayoutPanel1.ColumnCount = 1; - tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); - tableLayoutPanel1.Controls.Add(assetDataGridView, 0, 1); - tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 0); - tableLayoutPanel1.Location = new System.Drawing.Point(12, 12); - tableLayoutPanel1.Name = "tableLayoutPanel1"; - tableLayoutPanel1.RowCount = 2; - tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F)); - tableLayoutPanel1.RowStyles.Add(new RowStyle()); - tableLayoutPanel1.Size = new System.Drawing.Size(524, 283); - tableLayoutPanel1.TabIndex = 3; - // // tableLayoutPanel2 // tableLayoutPanel2.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; tableLayoutPanel2.ColumnCount = 4; - tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 120F)); - tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 60F)); - tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 100F)); + tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40F)); + tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F)); + tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40F)); tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle()); tableLayoutPanel2.Controls.Add(loadAssetMap, 0, 0); tableLayoutPanel2.Controls.Add(clear, 1, 0); tableLayoutPanel2.Controls.Add(loadSelected, 2, 0); - tableLayoutPanel2.Controls.Add(searchTextBox, 3, 0); + tableLayoutPanel2.Controls.Add(exportSelected, 3, 0); tableLayoutPanel2.Location = new System.Drawing.Point(3, 3); tableLayoutPanel2.Name = "tableLayoutPanel2"; tableLayoutPanel2.RowCount = 1; @@ -122,48 +96,163 @@ namespace AssetStudio.GUI loadSelected.Dock = DockStyle.Fill; loadSelected.Location = new System.Drawing.Point(183, 3); loadSelected.Name = "loadSelected"; - loadSelected.Size = new System.Drawing.Size(94, 23); + loadSelected.Size = new System.Drawing.Size(114, 23); loadSelected.TabIndex = 2; loadSelected.Text = "Load Selected"; loadSelected.UseVisualStyleBackColor = true; loadSelected.Click += loadSelected_Click; // - // searchTextBox + // exportSelected // - searchTextBox.Dock = DockStyle.Fill; - searchTextBox.Location = new System.Drawing.Point(283, 3); - searchTextBox.Name = "searchTextBox"; - searchTextBox.PlaceholderText = "Column Name=Regex{space}...."; - searchTextBox.Size = new System.Drawing.Size(232, 23); - searchTextBox.TabIndex = 3; - searchTextBox.KeyPress += searchTextBox_KeyPress; + exportSelected.Dock = DockStyle.Fill; + exportSelected.Location = new System.Drawing.Point(303, 3); + exportSelected.Name = "exportSelected"; + exportSelected.Size = new System.Drawing.Size(212, 23); + exportSelected.TabIndex = 3; + exportSelected.Text = "Export Selected"; + exportSelected.UseVisualStyleBackColor = true; + exportSelected.Click += exportSelected_Click; + // + // assetDataGridView + // + assetDataGridView.AllowUserToAddRows = false; + assetDataGridView.AllowUserToDeleteRows = false; + assetDataGridView.AllowUserToResizeRows = false; + assetDataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; + assetDataGridView.Dock = DockStyle.Fill; + assetDataGridView.Location = new System.Drawing.Point(3, 73); + assetDataGridView.Name = "assetDataGridView"; + assetDataGridView.ReadOnly = true; + assetDataGridView.RowTemplate.Height = 25; + assetDataGridView.Size = new System.Drawing.Size(518, 263); + assetDataGridView.TabIndex = 2; + assetDataGridView.VirtualMode = true; + assetDataGridView.CellValueNeeded += AssetDataGridView_CellValueNeeded; + assetDataGridView.ColumnHeaderMouseClick += AssetListView_ColumnHeaderMouseClick; + // + // tableLayoutPanel1 + // + tableLayoutPanel1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + tableLayoutPanel1.ColumnCount = 1; + tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); + tableLayoutPanel1.Controls.Add(assetDataGridView, 0, 2); + tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 0); + tableLayoutPanel1.Controls.Add(tableLayoutPanel3, 0, 1); + tableLayoutPanel1.Location = new System.Drawing.Point(12, 12); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + tableLayoutPanel1.RowCount = 3; + tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F)); + tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F)); + tableLayoutPanel1.RowStyles.Add(new RowStyle()); + tableLayoutPanel1.Size = new System.Drawing.Size(524, 333); + tableLayoutPanel1.TabIndex = 3; + // + // tableLayoutPanel3 + // + tableLayoutPanel3.ColumnCount = 5; + tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F)); + tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F)); + tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F)); + tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F)); + tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F)); + tableLayoutPanel3.Controls.Add(sourceTextBox, 0, 0); + tableLayoutPanel3.Controls.Add(pathTextBox, 0, 0); + tableLayoutPanel3.Controls.Add(nameTextBox, 0, 0); + tableLayoutPanel3.Controls.Add(containerTextBox, 0, 0); + tableLayoutPanel3.Controls.Add(typeTextBox, 4, 0); + tableLayoutPanel3.Dock = DockStyle.Fill; + tableLayoutPanel3.Location = new System.Drawing.Point(3, 38); + tableLayoutPanel3.Name = "tableLayoutPanel3"; + tableLayoutPanel3.RowCount = 1; + tableLayoutPanel3.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + tableLayoutPanel3.Size = new System.Drawing.Size(518, 29); + tableLayoutPanel3.TabIndex = 4; + // + // sourceTextBox + // + sourceTextBox.Dock = DockStyle.Fill; + sourceTextBox.Location = new System.Drawing.Point(209, 3); + sourceTextBox.Name = "sourceTextBox"; + sourceTextBox.PlaceholderText = "Source"; + sourceTextBox.Size = new System.Drawing.Size(97, 23); + sourceTextBox.TabIndex = 6; + sourceTextBox.KeyPress += SourceTextBox_KeyPress; + // + // pathTextBox + // + pathTextBox.Dock = DockStyle.Fill; + pathTextBox.Location = new System.Drawing.Point(312, 3); + pathTextBox.Name = "pathTextBox"; + pathTextBox.PlaceholderText = "PathID"; + pathTextBox.Size = new System.Drawing.Size(97, 23); + pathTextBox.TabIndex = 7; + pathTextBox.KeyPress += PathTextBox_KeyPress; + // + // nameTextBox + // + nameTextBox.Dock = DockStyle.Fill; + nameTextBox.Location = new System.Drawing.Point(3, 3); + nameTextBox.Name = "nameTextBox"; + nameTextBox.PlaceholderText = "Name"; + nameTextBox.Size = new System.Drawing.Size(97, 23); + nameTextBox.TabIndex = 4; + nameTextBox.KeyPress += NameTextBox_KeyPress; + // + // containerTextBox + // + containerTextBox.Dock = DockStyle.Fill; + containerTextBox.Location = new System.Drawing.Point(106, 3); + containerTextBox.Name = "containerTextBox"; + containerTextBox.PlaceholderText = "Container"; + containerTextBox.Size = new System.Drawing.Size(97, 23); + containerTextBox.TabIndex = 5; + containerTextBox.KeyPress += ContainerTextBox_KeyPress; + // + // typeTextBox + // + typeTextBox.Dock = DockStyle.Fill; + typeTextBox.Location = new System.Drawing.Point(415, 3); + typeTextBox.Name = "typeTextBox"; + typeTextBox.PlaceholderText = "Type"; + typeTextBox.Size = new System.Drawing.Size(100, 23); + typeTextBox.TabIndex = 8; + typeTextBox.KeyPress += TypeTextBox_KeyPress; // // AssetBrowser // AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new System.Drawing.Size(548, 307); + ClientSize = new System.Drawing.Size(548, 357); Controls.Add(tableLayoutPanel1); Name = "AssetBrowser"; ShowIcon = false; StartPosition = FormStartPosition.CenterScreen; Text = "Asset Browser"; + FormClosing += AssetBrowser_FormClosing; + tableLayoutPanel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)assetDataGridView).EndInit(); tableLayoutPanel1.ResumeLayout(false); - tableLayoutPanel2.ResumeLayout(false); - tableLayoutPanel2.PerformLayout(); + tableLayoutPanel3.ResumeLayout(false); + tableLayoutPanel3.PerformLayout(); ResumeLayout(false); } #endregion - private System.Windows.Forms.DataGridView assetDataGridView; - private TableLayoutPanel tableLayoutPanel1; + private TableLayoutPanel tableLayoutPanel2; private Button loadAssetMap; private Button clear; private Button loadSelected; - private TextBox searchTextBox; + private Button exportSelected; + private DataGridView assetDataGridView; + private TableLayoutPanel tableLayoutPanel1; + private TableLayoutPanel tableLayoutPanel3; + private TextBox sourceTextBox; + private TextBox pathTextBox; + private TextBox nameTextBox; + private TextBox containerTextBox; + private TextBox typeTextBox; } } \ No newline at end of file diff --git a/AssetStudio.GUI/AssetBrowser.cs b/AssetStudio.GUI/AssetBrowser.cs index 1faa1e8..4db8a99 100644 --- a/AssetStudio.GUI/AssetBrowser.cs +++ b/AssetStudio.GUI/AssetBrowser.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; +using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; +using static AssetStudio.GUI.Studio; namespace AssetStudio.GUI { @@ -14,7 +16,7 @@ namespace AssetStudio.GUI { private readonly MainForm _parent; private readonly List _assetEntries; - private readonly List _columnNames; + private readonly Dictionary _filters; private SortOrder _sortOrder; private DataGridViewColumn _sortedColumn; @@ -23,7 +25,7 @@ namespace AssetStudio.GUI { InitializeComponent(); _parent = form; - _columnNames = new List(); + _filters = new Dictionary(); _assetEntries = new List(); } @@ -39,14 +41,20 @@ namespace AssetStudio.GUI await Task.Run(() => ResourceMap.FromFile(path)); _sortedColumn = null; - _columnNames.Clear(); - _columnNames.AddRange(typeof(AssetEntry).GetProperties().Select(x => x.Name).ToList()); + + var names = typeof(AssetEntry).GetProperties().Select(x => x.Name); + + _filters.Clear(); + foreach(var name in names) + { + _filters.Add(name, new Regex("")); + } _assetEntries.Clear(); _assetEntries.AddRange(ResourceMap.GetEntries()); assetDataGridView.Columns.Clear(); - assetDataGridView.Columns.AddRange(_columnNames.Select(x => new DataGridViewTextBoxColumn() { Name = x, HeaderText = x, SortMode = DataGridViewColumnSortMode.Programmatic }).ToArray()); + assetDataGridView.Columns.AddRange(names.Select(x => new DataGridViewTextBoxColumn() { Name = x, HeaderText = x, SortMode = DataGridViewColumnSortMode.Programmatic }).ToArray()); assetDataGridView.Columns.GetLastColumn(DataGridViewElementStates.None, DataGridViewElementStates.None).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; assetDataGridView.Rows.Clear(); @@ -75,48 +83,242 @@ namespace AssetStudio.GUI _parent.Invoke(() => _parent.LoadPaths(files.ToArray())); } } - private void searchTextBox_KeyPress(object sender, KeyPressEventArgs e) + private async void exportSelected_Click(object sender, EventArgs e) { - if (e.KeyChar == (char)Keys.Enter) + var saveFolderDialog = new OpenFolderDialog(); + if (saveFolderDialog.ShowDialog(this) == DialogResult.OK) { - var filters = new Dictionary(); + var entries = assetDataGridView.SelectedRows.Cast().Select(x => _assetEntries[x.Index]).ToArray(); - var value = searchTextBox.Text; - var options = value.Split(' '); - for (int i = 0; i < options.Length; i++) + _parent.Invoke(_parent.ResetForm); + + var statusStripUpdate = StatusStripUpdate; + assetsManager.Game = Studio.Game; + StatusStripUpdate = Logger.Info; + + var files = new List(entries.Select(x => x.Source).ToHashSet()); + await Task.Run(async () => { - var option = options[i]; - var arguments = option.Split('='); - if (arguments.Length != 2) + for (int i = 0; i < files.Count; i++) { - Logger.Error($"Invalid argument at index {i + 1}"); - continue; - } + var toExportAssets = new List(); - var (name, regex) = (arguments[0], arguments[1]); - if (!_columnNames.Contains(name, StringComparer.OrdinalIgnoreCase)) - { - Logger.Error($"Unknonw argument {name}"); - continue; + var file = files[i]; + assetsManager.LoadFiles(file); + if (assetsManager.assetsFileList.Count > 0) + { + BuildAssetData(toExportAssets, entries); + await ExportAssets(saveFolderDialog.Folder, toExportAssets, ExportType.Convert, i == files.Count - 1); + } + toExportAssets.Clear(); + assetsManager.Clear(); } - - try + }); + StatusStripUpdate = statusStripUpdate; + } + } + private void BuildAssetData(List exportableAssets, AssetEntry[] entries) + { + var objectAssetItemDic = new Dictionary(); + var mihoyoBinDataNames = new List<(PPtr, string)>(); + var containers = new List<(PPtr, string)>(); + foreach (var assetsFile in assetsManager.assetsFileList) + { + foreach (var asset in assetsFile.Objects) + { + ProcessAssetData(asset, exportableAssets, entries, objectAssetItemDic, mihoyoBinDataNames, containers); + } + } + foreach ((var pptr, var name) in mihoyoBinDataNames) + { + if (pptr.TryGet(out var obj)) + { + var assetItem = objectAssetItemDic[obj]; + if (int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hash)) { - filters[name] = new Regex(regex, RegexOptions.IgnoreCase); + assetItem.Text = name; + assetItem.Container = hash.ToString(); } - catch (Exception) + else assetItem.Text = $"BinFile #{assetItem.m_PathID}"; + } + } + foreach ((var pptr, var container) in containers) + { + if (pptr.TryGet(out var obj)) + { + var item = objectAssetItemDic[obj]; + if (entries.Any(x => x.Container == container)) { - Logger.Error($"Invalid regex {regex} at argument {name}"); - continue; + item.Container = container; + } + else + { + exportableAssets.Remove(item); } } + } + containers.Clear(); + } + private void ProcessAssetData(Object asset, List exportableAssets, AssetEntry[] entries, Dictionary objectAssetItemDic, List<(PPtr, string)> mihoyoBinDataNames, List<(PPtr, string)> containers) + { + var assetItem = new AssetItem(asset); + objectAssetItemDic.Add(asset, assetItem); + var exportable = false; + switch (asset) + { + case GameObject m_GameObject: + exportable = ClassIDType.GameObject.CanExport() && m_GameObject.HasModel(); + break; + case Texture2D m_Texture2D: + if (!string.IsNullOrEmpty(m_Texture2D.m_StreamData?.path)) + assetItem.FullSize = asset.byteSize + m_Texture2D.m_StreamData.size; + exportable = ClassIDType.Texture2D.CanExport(); + break; + case AudioClip m_AudioClip: + if (!string.IsNullOrEmpty(m_AudioClip.m_Source)) + assetItem.FullSize = asset.byteSize + m_AudioClip.m_Size; + exportable = ClassIDType.AudioClip.CanExport(); + break; + case VideoClip m_VideoClip: + if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath)) + assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size; + exportable = ClassIDType.VideoClip.CanExport(); + break; + case MonoBehaviour m_MonoBehaviour: + exportable = ClassIDType.MonoBehaviour.CanExport(); + break; + case AssetBundle m_AssetBundle: + foreach (var m_Container in m_AssetBundle.m_Container) + { + var preloadIndex = m_Container.Value.preloadIndex; + var preloadSize = m_Container.Value.preloadSize; + var preloadEnd = preloadIndex + preloadSize; + for (int k = preloadIndex; k < preloadEnd; k++) + { + containers.Add((m_AssetBundle.m_PreloadTable[k], m_Container.Key)); + } + } - _assetEntries.Clear(); - _assetEntries.AddRange(ResourceMap.GetEntries().FindAll(x => x.Matches(filters))); + exportable = ClassIDType.AssetBundle.CanExport(); + break; + case IndexObject m_IndexObject: + foreach (var index in m_IndexObject.AssetMap) + { + mihoyoBinDataNames.Add((index.Value.Object, index.Key)); + } - assetDataGridView.Rows.Clear(); - assetDataGridView.RowCount = _assetEntries.Count; - assetDataGridView.Refresh(); + exportable = ClassIDType.IndexObject.CanExport(); + break; + case ResourceManager m_ResourceManager: + foreach (var m_Container in m_ResourceManager.m_Container) + { + containers.Add((m_Container.Value, m_Container.Key)); + } + + exportable = ClassIDType.GameObject.CanExport(); + break; + case Mesh _ when ClassIDType.Mesh.CanExport(): + case TextAsset _ when ClassIDType.TextAsset.CanExport(): + case AnimationClip _ when ClassIDType.Font.CanExport(): + case Font _ when ClassIDType.GameObject.CanExport(): + case MovieTexture _ when ClassIDType.MovieTexture.CanExport(): + case Sprite _ when ClassIDType.Sprite.CanExport(): + case Material _ when ClassIDType.Material.CanExport(): + case MiHoYoBinData _ when ClassIDType.MiHoYoBinData.CanExport(): + case Shader _ when ClassIDType.Shader.CanExport(): + case Animator _ when ClassIDType.Animator.CanExport(): + exportable = true; + break; + } + if (assetItem.Text == "") + { + assetItem.Text = assetItem.TypeString + assetItem.UniqueID; + } + + if (entries.Any(x => x.Name == assetItem.Text && x.Type == assetItem.Type) && exportable) + { + exportableAssets.Add(assetItem); + } + } + private void FilterAssetDataGrid() + { + _assetEntries.Clear(); + _assetEntries.AddRange(ResourceMap.GetEntries().FindAll(x => x.Matches(_filters))); + + assetDataGridView.Rows.Clear(); + assetDataGridView.RowCount = _assetEntries.Count; + assetDataGridView.Refresh(); + } + private void TryAddFilter(string name, string value) + { + Regex regex; + try + { + regex = new Regex(value, RegexOptions.IgnoreCase); + } + catch (Exception) + { + Logger.Error($"Invalid regex {value}"); + return; + } + + if (!_filters.TryGetValue(name, out var filter)) + { + _filters.Add(name, regex); + } + else if (filter != regex) + { + _filters[name] = regex; + } + } + private void NameTextBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (sender is TextBox textBox && e.KeyChar == (char)Keys.Enter) + { + var value = textBox.Text; + + TryAddFilter("Name", value); + FilterAssetDataGrid(); + } + } + private void ContainerTextBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (sender is TextBox textBox && e.KeyChar == (char)Keys.Enter) + { + var value = textBox.Text; + + TryAddFilter("Container", value); + FilterAssetDataGrid(); + } + } + private void SourceTextBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (sender is TextBox textBox && e.KeyChar == (char)Keys.Enter) + { + var value = textBox.Text; + + TryAddFilter("Source", value); + FilterAssetDataGrid(); + } + } + private void PathTextBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (sender is TextBox textBox && e.KeyChar == (char)Keys.Enter) + { + var value = textBox.Text; + + TryAddFilter("PathID", value); + FilterAssetDataGrid(); + } + } + private void TypeTextBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (sender is TextBox textBox && e.KeyChar == (char)Keys.Enter) + { + var value = textBox.Text; + + TryAddFilter("Type", value); + FilterAssetDataGrid(); } } private void AssetDataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) diff --git a/AssetStudio.GUI/MainForm.cs b/AssetStudio.GUI/MainForm.cs index 34282ec..c25ab4b 100644 --- a/AssetStudio.GUI/MainForm.cs +++ b/AssetStudio.GUI/MainForm.cs @@ -1475,7 +1475,7 @@ namespace AssetStudio.GUI } } - private void ResetForm() + public void ResetForm() { Text = $"Studio v{Application.ProductVersion}"; assetsManager.Clear(); @@ -1883,7 +1883,7 @@ namespace AssetStudio.GUI assetListView.EndUpdate(); } - private void ExportAssets(ExportFilter type, ExportType exportType) + private async void ExportAssets(ExportFilter type, ExportType exportType) { if (exportableAssets.Count > 0) { @@ -1906,7 +1906,7 @@ namespace AssetStudio.GUI toExportAssets = visibleAssets; break; } - Studio.ExportAssets(saveFolderDialog.Folder, toExportAssets, exportType); + await Studio.ExportAssets(saveFolderDialog.Folder, toExportAssets, exportType, Properties.Settings.Default.openAfterExport); } } else diff --git a/AssetStudio.GUI/Studio.cs b/AssetStudio.GUI/Studio.cs index fe746cf..7027477 100644 --- a/AssetStudio.GUI/Studio.cs +++ b/AssetStudio.GUI/Studio.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; using System.Xml; using static AssetStudio.GUI.Exporter; @@ -526,9 +527,9 @@ namespace AssetStudio.GUI return typeMap; } - public static void ExportAssets(string savePath, List toExportAssets, ExportType exportType) + public static Task ExportAssets(string savePath, List toExportAssets, ExportType exportType, bool openAfterExport) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); @@ -617,16 +618,16 @@ namespace AssetStudio.GUI StatusStripUpdate(statusText); - if (Properties.Settings.Default.openAfterExport && exportedCount > 0) + if (openAfterExport && exportedCount > 0) { OpenFolderInExplorer(savePath); } }); } - public static void ExportAssetsList(string savePath, List toExportAssets, ExportListType exportListType) + public static Task ExportAssetsList(string savePath, List toExportAssets, ExportListType exportListType) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); @@ -674,9 +675,9 @@ namespace AssetStudio.GUI }); } - public static void ExportSplitObjects(string savePath, TreeNodeCollection nodes) + public static Task ExportSplitObjects(string savePath, TreeNodeCollection nodes) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { var exportNodes = GetNodes(nodes); var count = exportNodes.Cast().Sum(x => x.Nodes.Count); @@ -768,9 +769,9 @@ namespace AssetStudio.GUI } } - public static void ExportAnimatorWithAnimationClip(AssetItem animator, List animationList, string exportPath) + public static Task ExportAnimatorWithAnimationClip(AssetItem animator, List animationList, string exportPath) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { Progress.Reset(); StatusStripUpdate($"Exporting {animator.Text}"); @@ -792,9 +793,9 @@ namespace AssetStudio.GUI }); } - public static void ExportObjectsWithAnimationClip(string exportPath, TreeNodeCollection nodes, List animationList = null) + public static Task ExportObjectsWithAnimationClip(string exportPath, TreeNodeCollection nodes, List animationList = null) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { var gameObjects = new List(); GetSelectedParentNode(nodes, gameObjects); @@ -832,9 +833,9 @@ namespace AssetStudio.GUI }); } - public static void ExportObjectsMergeWithAnimationClip(string exportPath, List gameObjects, List animationList = null) + public static Task ExportObjectsMergeWithAnimationClip(string exportPath, List gameObjects, List animationList = null) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { var name = Path.GetFileName(exportPath); Progress.Reset(); @@ -857,9 +858,9 @@ namespace AssetStudio.GUI }); } - public static void ExportNodesWithAnimationClip(string exportPath, List nodes, List animationList = null) + public static Task ExportNodesWithAnimationClip(string exportPath, List nodes, List animationList = null) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { int i = 0; Progress.Reset(); diff --git a/AssetStudio/AssetsHelper.cs b/AssetStudio/AssetsHelper.cs index 0b365a4..844d48f 100644 --- a/AssetStudio/AssetsHelper.cs +++ b/AssetStudio/AssetsHelper.cs @@ -10,6 +10,7 @@ using System.Text.RegularExpressions; using System.Xml; using System.Text; using MessagePack; +using System.Threading.Tasks; namespace AssetStudio { @@ -312,7 +313,7 @@ namespace AssetStudio } } - public static void BuildAssetMap(string[] files, string mapName, Game game, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null) + public static async void BuildAssetMap(string[] files, string mapName, Game game, string savePath, ExportListType exportListType, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null) { Logger.Info("Building AssetMap..."); try @@ -327,7 +328,7 @@ namespace AssetStudio UpdateContainers(assets, game); - ExportAssetsMap(assets, game, mapName, savePath, exportListType, resetEvent); + await ExportAssetsMap(assets, game, mapName, savePath, exportListType); } catch(Exception e) { @@ -533,9 +534,9 @@ namespace AssetStudio } } - private static void ExportAssetsMap(List toExportAssets, Game game, string name, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null) + private static Task ExportAssetsMap(List toExportAssets, Game game, string name, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null) { - ThreadPool.QueueUserWorkItem(state => + return Task.Run(() => { Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); @@ -599,7 +600,7 @@ namespace AssetStudio resetEvent?.Set(); }); } - public static void BuildBoth(string[] files, string mapName, string baseFolder, Game game, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null) + public static async void BuildBoth(string[] files, string mapName, string baseFolder, Game game, string savePath, ExportListType exportListType, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null) { Logger.Info($"Building Both..."); CABMap.Clear(); @@ -618,7 +619,7 @@ namespace AssetStudio DumpCABMap(mapName); Logger.Info($"Map build successfully !! {collision} collisions found"); - ExportAssetsMap(assets, game, mapName, savePath, exportListType, resetEvent); + await ExportAssetsMap(assets, game, mapName, savePath, exportListType); } } } diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index 5c95268..ea279e4 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -28,23 +28,6 @@ namespace AssetStudio internal HashSet noexistFiles = new HashSet(StringComparer.OrdinalIgnoreCase); internal HashSet assetsFileListHash = new HashSet(StringComparer.OrdinalIgnoreCase); - public void LoadFiles(string file) - { - if (Silent) - { - Logger.Silent = true; - Progress.Silent = true; - } - - Load(new string[] { file }); - - if (Silent) - { - Logger.Silent = false; - Progress.Silent = false; - } - } - public void LoadFiles(params string[] files) { if (Silent)