diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj index e7d3c21..d38d9ad 100644 --- a/AssetStudio/AssetStudio.csproj +++ b/AssetStudio/AssetStudio.csproj @@ -9,12 +9,14 @@ + + diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index cbb4b28..1834ec3 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -11,6 +11,7 @@ namespace AssetStudio public class AssetsManager { public string SpecifyUnityVersion; + public bool ZstdEnabled = true; public List assetsFileList = new List(); private HashSet filteredAssetTypesList = new HashSet(); @@ -267,7 +268,7 @@ namespace AssetStudio Logger.Info("Loading " + reader.FullPath); try { - var bundleFile = new BundleFile(reader, SpecifyUnityVersion); + var bundleFile = new BundleFile(reader, ZstdEnabled, SpecifyUnityVersion); foreach (var file in bundleFile.fileList) { var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName); diff --git a/AssetStudio/BundleFile.cs b/AssetStudio/BundleFile.cs index f82fb12..6b492fa 100644 --- a/AssetStudio/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -1,4 +1,5 @@ using K4os.Compression.LZ4; +using ZstdSharp; using System; using System.IO; using System.Linq; @@ -36,7 +37,8 @@ namespace AssetStudio Lzma, Lz4, Lz4HC, - Lzham + Lzham, + Custom, } public class BundleFile @@ -74,7 +76,7 @@ namespace AssetStudio public StreamFile[] fileList; - public BundleFile(FileReader reader, string specUnityVer = "") + public BundleFile(FileReader reader, bool useZstd, string specUnityVer = "") { m_Header = new Header(); m_Header.signature = reader.ReadStringToNull(); @@ -127,7 +129,7 @@ namespace AssetStudio ReadBlocksInfoAndDirectory(reader, ver); using (var blocksStream = CreateBlocksStream(reader.FullPath)) { - ReadBlocks(reader, blocksStream); + ReadBlocks(reader, blocksStream, useZstd); ReadFiles(blocksStream, reader.FullPath); } break; @@ -325,7 +327,7 @@ namespace AssetStudio break; } default: - throw new IOException($"Unsupported compression type {compressionType}"); + throw new IOException($"Unsupported block info compression type {compressionType}"); } using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompressedStream)) @@ -362,8 +364,10 @@ namespace AssetStudio } } - private void ReadBlocks(FileReader reader, Stream blocksStream) + private void ReadBlocks(FileReader reader, Stream blocksStream, bool useZstd) { + var zstdCodec = new Decompressor(); + var i = 0; foreach (var blockInfo in m_BlocksInfo) { var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask); @@ -381,6 +385,7 @@ namespace AssetStudio } case CompressionType.Lz4: case CompressionType.Lz4HC: + case CompressionType.Custom: { var compressedSize = (int)blockInfo.compressedSize; var compressedBytes = BigArrayPool.Shared.Rent(compressedSize); @@ -389,10 +394,30 @@ namespace AssetStudio var uncompressedBytes = BigArrayPool.Shared.Rent(uncompressedSize); try { - var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); + var compTypeStr = compressionType.ToString(); + if (compressionType == CompressionType.Custom) + { + compTypeStr = useZstd ? "Zstd" : "Lz4"; + if (i == 0) + { + Logger.Debug($"Custom block compression type was detected. Trying to decompress as {compTypeStr} archive.."); + i++; + } + } + + int numWrite; + if (compressionType == CompressionType.Custom && useZstd) + { + numWrite = zstdCodec.Unwrap(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); + } + else + { + numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); + } + if (numWrite != uncompressedSize) { - throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); + throw new IOException($"{compTypeStr} block decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); } blocksStream.Write(uncompressedBytes, 0, uncompressedSize); } @@ -404,7 +429,7 @@ namespace AssetStudio break; } default: - throw new IOException($"Unsupported compression type {compressionType}"); + throw new IOException($"Unsupported block compression type {compressionType}"); } } blocksStream.Position = 0; diff --git a/AssetStudioCLI/Options/CLIOptions.cs b/AssetStudioCLI/Options/CLIOptions.cs index 7ab20c8..d17a4de 100644 --- a/AssetStudioCLI/Options/CLIOptions.cs +++ b/AssetStudioCLI/Options/CLIOptions.cs @@ -67,6 +67,12 @@ namespace AssetStudioCLI.Options NameAndContainer, } + internal enum CustomCompressionType + { + Zstd, + Lz4, + } + internal static class CLIOptions { public static bool isParsed; @@ -105,6 +111,7 @@ namespace AssetStudioCLI.Options public static Option> o_filterByPathID; public static Option> o_filterByText; //advanced + public static Option o_customCompressionType; public static Option o_exportAssetList; public static Option o_assemblyPath; public static Option o_unityVersion; @@ -377,6 +384,18 @@ namespace AssetStudioCLI.Options #endregion #region Init Advanced Options + o_customCompressionType = new GroupedOption + ( + optionDefaultValue: CustomCompressionType.Zstd, + optionName: "--custom-compression ", + optionDescription: "Specify the compression type for assets that use custom compression\n" + + "\n" + + "Zstd - Try to decompress as zstd archive\n" + + "Lz4 - Try to decompress as lz4 archive\n", + optionExample: "Example: \"--custom-compression lz4\"\n", + optionHelpGroup: HelpGroups.Advanced + ); + o_exportAssetList = new GroupedOption ( optionDefaultValue: ExportListType.None, @@ -838,6 +857,22 @@ namespace AssetStudioCLI.Options return; } break; + case "--custom-compression": + switch (value.ToLower()) + { + case "zstd": + o_customCompressionType.Value = CustomCompressionType.Zstd; + break; + case "lz4": + case "lz4hc": + o_customCompressionType.Value = CustomCompressionType.Lz4; + break; + default: + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported compression type: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_customCompressionType.Description); + return; + } + break; case "--export-asset-list": switch (value.ToLower()) { @@ -1043,6 +1078,10 @@ namespace AssetStudioCLI.Options var sb = new StringBuilder(); sb.AppendLine("[Current Options]"); sb.AppendLine($"# Working Mode: {o_workMode}"); + if (o_customCompressionType.Value != o_customCompressionType.DefaultValue) + { + sb.AppendLine($"# Custom Compression Type: {o_customCompressionType}"); + } sb.AppendLine($"# Input Path: \"{inputPath}\""); switch (o_workMode.Value) { diff --git a/AssetStudioCLI/Studio.cs b/AssetStudioCLI/Studio.cs index ea5a797..1ff5b50 100644 --- a/AssetStudioCLI/Studio.cs +++ b/AssetStudioCLI/Studio.cs @@ -34,6 +34,7 @@ namespace AssetStudioCLI { var isLoaded = false; assetsManager.SpecifyUnityVersion = CLIOptions.o_unityVersion.Value; + assetsManager.ZstdEnabled = CLIOptions.o_customCompressionType.Value == CustomCompressionType.Zstd; if (!CLIOptions.f_loadAllAssets.Value) { assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value); diff --git a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs index d00f83a..5a3fb77 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs @@ -42,6 +42,9 @@ this.enablePreview = new System.Windows.Forms.ToolStripMenuItem(); this.displayInfo = new System.Windows.Forms.ToolStripMenuItem(); this.buildTreeStructureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.customCompressionTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.customCompressionZstdToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.customCompressionLZ4ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem(); this.specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox(); this.showExpOpt = new System.Windows.Forms.ToolStripMenuItem(); @@ -82,15 +85,14 @@ this.filterTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.allToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.debugMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripMenuItem(); this.showConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripMenuItem(); this.writeLogToFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportClassStructuresMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabPage1 = new System.Windows.Forms.TabPage(); - this.sceneTreeView = new AssetStudioGUI.GOHierarchy(); this.treeSearch = new System.Windows.Forms.TextBox(); this.tabPage2 = new System.Windows.Forms.TabPage(); this.assetListView = new System.Windows.Forms.ListView(); @@ -153,6 +155,7 @@ this.exportL2DWithClipsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.sceneTreeView = new AssetStudioGUI.GOHierarchy(); this.menuStrip1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); @@ -244,6 +247,7 @@ this.enablePreview, this.displayInfo, this.buildTreeStructureToolStripMenuItem, + this.customCompressionTypeToolStripMenuItem, this.toolStripMenuItem14, this.showExpOpt}); this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; @@ -254,7 +258,7 @@ // this.displayAll.CheckOnClick = true; this.displayAll.Name = "displayAll"; - this.displayAll.Size = new System.Drawing.Size(207, 22); + this.displayAll.Size = new System.Drawing.Size(213, 22); this.displayAll.Text = "Display all assets"; this.displayAll.ToolTipText = "Check this option will display all types assets. Not extractable assets can expor" + "t the RAW file."; @@ -266,7 +270,7 @@ this.enablePreview.CheckOnClick = true; this.enablePreview.CheckState = System.Windows.Forms.CheckState.Checked; this.enablePreview.Name = "enablePreview"; - this.enablePreview.Size = new System.Drawing.Size(207, 22); + this.enablePreview.Size = new System.Drawing.Size(213, 22); this.enablePreview.Text = "Enable preview"; this.enablePreview.ToolTipText = "Toggle the loading and preview of readable assets, such as images, sounds, text, " + "etc.\r\nDisable preview if you have performance or compatibility issues."; @@ -278,7 +282,7 @@ this.displayInfo.CheckOnClick = true; this.displayInfo.CheckState = System.Windows.Forms.CheckState.Checked; this.displayInfo.Name = "displayInfo"; - this.displayInfo.Size = new System.Drawing.Size(207, 22); + this.displayInfo.Size = new System.Drawing.Size(213, 22); this.displayInfo.Text = "Display asset information"; this.displayInfo.ToolTipText = "Toggle the overlay that shows information about each asset, eg. image size, forma" + "t, audio bitrate, etc."; @@ -290,17 +294,48 @@ this.buildTreeStructureToolStripMenuItem.CheckOnClick = true; this.buildTreeStructureToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; this.buildTreeStructureToolStripMenuItem.Name = "buildTreeStructureToolStripMenuItem"; - this.buildTreeStructureToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.buildTreeStructureToolStripMenuItem.Size = new System.Drawing.Size(213, 22); this.buildTreeStructureToolStripMenuItem.Text = "Build tree structure"; this.buildTreeStructureToolStripMenuItem.ToolTipText = "You can disable tree structure building if you don\'t use the Scene Hierarchy tab"; this.buildTreeStructureToolStripMenuItem.CheckedChanged += new System.EventHandler(this.buildTreeStructureToolStripMenuItem_CheckedChanged); // + // customCompressionTypeToolStripMenuItem + // + this.customCompressionTypeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.customCompressionZstdToolStripMenuItem, + this.customCompressionLZ4ToolStripMenuItem}); + this.customCompressionTypeToolStripMenuItem.Name = "customCompressionTypeToolStripMenuItem"; + this.customCompressionTypeToolStripMenuItem.Size = new System.Drawing.Size(213, 22); + this.customCompressionTypeToolStripMenuItem.Text = "Custom compression type"; + // + // customCompressionZstdToolStripMenuItem + // + this.customCompressionZstdToolStripMenuItem.Checked = true; + this.customCompressionZstdToolStripMenuItem.CheckOnClick = true; + this.customCompressionZstdToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; + this.customCompressionZstdToolStripMenuItem.Name = "customCompressionZstdToolStripMenuItem"; + this.customCompressionZstdToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.customCompressionZstdToolStripMenuItem.Text = "Zstd"; + this.customCompressionZstdToolStripMenuItem.ToolTipText = "If selected, Zstd-decompression will be used for assets with custom compression t" + + "ype"; + this.customCompressionZstdToolStripMenuItem.CheckedChanged += new System.EventHandler(this.customCompressionZstd_CheckedChanged); + // + // customCompressionLZ4ToolStripMenuItem + // + this.customCompressionLZ4ToolStripMenuItem.CheckOnClick = true; + this.customCompressionLZ4ToolStripMenuItem.Name = "customCompressionLZ4ToolStripMenuItem"; + this.customCompressionLZ4ToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.customCompressionLZ4ToolStripMenuItem.Text = "Lz4/Lz4HC"; + this.customCompressionLZ4ToolStripMenuItem.ToolTipText = "If selected, Lz4-decompression will be used for assets with custom compression ty" + + "pe"; + this.customCompressionLZ4ToolStripMenuItem.CheckedChanged += new System.EventHandler(this.customCompressionLZ4_CheckedChanged); + // // toolStripMenuItem14 // this.toolStripMenuItem14.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.specifyUnityVersion}); this.toolStripMenuItem14.Name = "toolStripMenuItem14"; - this.toolStripMenuItem14.Size = new System.Drawing.Size(207, 22); + this.toolStripMenuItem14.Size = new System.Drawing.Size(213, 22); this.toolStripMenuItem14.Text = "Specify Unity version"; // // specifyUnityVersion @@ -314,7 +349,7 @@ // showExpOpt // this.showExpOpt.Name = "showExpOpt"; - this.showExpOpt.Size = new System.Drawing.Size(207, 22); + this.showExpOpt.Size = new System.Drawing.Size(213, 22); this.showExpOpt.Text = "Export options"; this.showExpOpt.Click += new System.EventHandler(this.showExpOpt_Click); // @@ -607,14 +642,6 @@ this.debugMenuItem.Size = new System.Drawing.Size(54, 20); this.debugMenuItem.Text = "Debug"; // - // toolStripMenuItem15 - // - this.toolStripMenuItem15.CheckOnClick = true; - this.toolStripMenuItem15.Name = "toolStripMenuItem15"; - this.toolStripMenuItem15.Size = new System.Drawing.Size(288, 22); - this.toolStripMenuItem15.Text = "Show debug messages in console logger"; - this.toolStripMenuItem15.Click += new System.EventHandler(this.toolStripMenuItem15_Click); - // // showConsoleToolStripMenuItem // this.showConsoleToolStripMenuItem.Checked = true; @@ -625,6 +652,14 @@ this.showConsoleToolStripMenuItem.Text = "Show console logger"; this.showConsoleToolStripMenuItem.Click += new System.EventHandler(this.showConsoleToolStripMenuItem_Click); // + // toolStripMenuItem15 + // + this.toolStripMenuItem15.CheckOnClick = true; + this.toolStripMenuItem15.Name = "toolStripMenuItem15"; + this.toolStripMenuItem15.Size = new System.Drawing.Size(288, 22); + this.toolStripMenuItem15.Text = "Show debug messages in console logger"; + this.toolStripMenuItem15.Click += new System.EventHandler(this.toolStripMenuItem15_Click); + // // writeLogToFileToolStripMenuItem // this.writeLogToFileToolStripMenuItem.CheckOnClick = true; @@ -696,18 +731,6 @@ this.tabPage1.Text = "Scene Hierarchy"; this.tabPage1.UseVisualStyleBackColor = true; // - // sceneTreeView - // - this.sceneTreeView.CheckBoxes = true; - this.sceneTreeView.Dock = System.Windows.Forms.DockStyle.Fill; - this.sceneTreeView.HideSelection = false; - this.sceneTreeView.Location = new System.Drawing.Point(0, 20); - this.sceneTreeView.Name = "sceneTreeView"; - this.sceneTreeView.Size = new System.Drawing.Size(472, 587); - this.sceneTreeView.TabIndex = 1; - this.sceneTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.sceneTreeView_AfterCheck); - this.sceneTreeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.sceneTreeView_NodeMouseClick); - // // treeSearch // this.treeSearch.Dock = System.Windows.Forms.DockStyle.Top; @@ -1363,6 +1386,18 @@ this.showOriginalFileToolStripMenuItem.Visible = false; this.showOriginalFileToolStripMenuItem.Click += new System.EventHandler(this.showOriginalFileToolStripMenuItem_Click); // + // sceneTreeView + // + this.sceneTreeView.CheckBoxes = true; + this.sceneTreeView.Dock = System.Windows.Forms.DockStyle.Fill; + this.sceneTreeView.HideSelection = false; + this.sceneTreeView.Location = new System.Drawing.Point(0, 20); + this.sceneTreeView.Name = "sceneTreeView"; + this.sceneTreeView.Size = new System.Drawing.Size(472, 587); + this.sceneTreeView.TabIndex = 1; + this.sceneTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.sceneTreeView_AfterCheck); + this.sceneTreeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.sceneTreeView_NodeMouseClick); + // // AssetStudioGUIForm // this.AllowDrop = true; @@ -1538,6 +1573,9 @@ private System.Windows.Forms.ToolStripMenuItem exportL2DWithFadeToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem l2DModelWithFadeListToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem exportL2DWithFadeLstToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem customCompressionTypeToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem customCompressionZstdToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem customCompressionLZ4ToolStripMenuItem; } } diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 7428b3b..7951089 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -2267,6 +2267,18 @@ namespace AssetStudioGUI } } + private void customCompressionZstd_CheckedChanged(object sender, EventArgs e) + { + customCompressionLZ4ToolStripMenuItem.Checked = !customCompressionZstdToolStripMenuItem.Checked; + assetsManager.ZstdEnabled = customCompressionZstdToolStripMenuItem.Checked; + } + + private void customCompressionLZ4_CheckedChanged(object sender, EventArgs e) + { + customCompressionZstdToolStripMenuItem.Checked = !customCompressionLZ4ToolStripMenuItem.Checked; + assetsManager.ZstdEnabled = customCompressionZstdToolStripMenuItem.Checked; + } + #region FMOD private void FMODinit() { diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index 78c6fa5..6db4f92 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -124,7 +124,7 @@ namespace AssetStudioGUI private static int ExtractBundleFile(FileReader reader, string savePath) { Logger.Info($"Decompressing {reader.FileName} ..."); - var bundleFile = new BundleFile(reader, assetsManager.SpecifyUnityVersion); + var bundleFile = new BundleFile(reader, assetsManager.ZstdEnabled, assetsManager.SpecifyUnityVersion); reader.Dispose(); if (bundleFile.fileList.Length > 0) {