diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index f7c729a..5a60c70 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -7,6 +7,8 @@ using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Text.Json; +using AssetStudio.CustomOptions; +using AssetStudio.CustomOptions.Asmo; using static AssetStudio.ImportHelper; namespace AssetStudio @@ -14,46 +16,22 @@ namespace AssetStudio public class AssetsManager { public bool LoadingViaTypeTreeEnabled = true; - public CompressionType CustomBlockCompression = CompressionType.Auto; - public CompressionType CustomBlockInfoCompression = CompressionType.Auto; - public List assetsFileList = new List(); + public ImportOptions Options = new ImportOptions(); + public readonly List> OptionLoaders = new List>(); + public readonly List AssetsFileList = new List(); internal Dictionary assetsFileIndexCache = new Dictionary(StringComparer.OrdinalIgnoreCase); internal ConcurrentDictionary resourceFileReaders = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private UnityVersion specifiedUnityVersion; - private List importFiles = new List(); - private HashSet filteredAssetTypesList = new HashSet(); - private HashSet importFilesHash = new HashSet(StringComparer.OrdinalIgnoreCase); - private HashSet noexistFiles = new HashSet(StringComparer.OrdinalIgnoreCase); - private HashSet assetsFileListHash = new HashSet(StringComparer.OrdinalIgnoreCase); + private readonly List importFiles = new List(); + private readonly HashSet filteredAssetTypesList = new HashSet(); + private readonly HashSet importFilesHash = new HashSet(StringComparer.OrdinalIgnoreCase); + private readonly HashSet noexistFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + private readonly HashSet assetsFileListHash = new HashSet(StringComparer.OrdinalIgnoreCase); - public UnityVersion SpecifyUnityVersion + public AssetsManager() { - get => specifiedUnityVersion; - set - { - if (specifiedUnityVersion == value) - { - return; - } - if (value == null) - { - specifiedUnityVersion = null; - Logger.Info("Specified Unity version: None"); - return; - } - - if (string.IsNullOrEmpty(value.BuildType)) - { - throw new NotSupportedException("Specified Unity version is not in a correct format.\n" + - "Specify full Unity version, including letters at the end.\n" + - "Example: 2017.4.39f1"); - } - - specifiedUnityVersion = value; - Logger.Info($"Specified Unity version: {specifiedUnityVersion}"); - } + OptionLoaders.Add(LoadImportOptions); } public void SetAssetFilter(params ClassIDType[] classIDTypes) @@ -102,16 +80,14 @@ namespace AssetStudio SetAssetFilter(classIDTypeList.ToArray()); } - public void LoadFilesAndFolders(params string[] path) + public void LoadFilesAndFolders(params string[] paths) { - LoadFilesAndFolders(out _, path); + LoadFilesAndFolders(out _, paths.ToList()); } - public void LoadFilesAndFolders(out string parentPath, params string[] path) + public void LoadFilesAndFolders(out string parentPath, params string[] paths) { - var pathList = new List(); - pathList.AddRange(path); - LoadFilesAndFolders(out parentPath, pathList); + LoadFilesAndFolders(out parentPath, paths.ToList()); } public void LoadFilesAndFolders(out string parentPath, List pathList) @@ -143,6 +119,8 @@ namespace AssetStudio { MergeSplitAssets(parentPath); } + LoadOptionFiles(fileList); + var toReadFile = ProcessingSplitFiles(fileList); fileList.Clear(); pathList.Clear(); @@ -224,7 +202,7 @@ namespace AssetStudio var assetsFile = new SerializedFile(reader, this); var dirName = Path.GetDirectoryName(reader.FullPath); CheckStrippedVersion(assetsFile); - assetsFileList.Add(assetsFile); + AssetsFileList.Add(assetsFile); assetsFileListHash.Add(assetsFile.fileName); if (fromZip) return true; @@ -293,7 +271,7 @@ namespace AssetStudio assetsFile.version = assetBundleUnityVer; } CheckStrippedVersion(assetsFile, assetBundleUnityVer); - assetsFileList.Add(assetsFile); + AssetsFileList.Add(assetsFile); assetsFileListHash.Add(assetsFile.fileName); } catch (NotSupportedException e) @@ -325,7 +303,7 @@ namespace AssetStudio try { - var bundleFile = new BundleFile(bundleReader, CustomBlockInfoCompression, CustomBlockCompression, specifiedUnityVersion); + var bundleFile = new BundleFile(bundleReader, Options.BundleOptions); isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath); if (!isLoaded) return false; @@ -347,7 +325,7 @@ namespace AssetStudio bundleReader.FileName = $"{reader.FileName}_0x{bundleStream.Offset:X}"; } Logger.Info($"[MultiBundle] Loading \"{reader.FileName}\" from offset: 0x{bundleStream.Offset:X}"); - bundleFile = new BundleFile(bundleReader, CustomBlockInfoCompression, CustomBlockCompression, specifiedUnityVersion, isMultiBundle: true); + bundleFile = new BundleFile(bundleReader, Options.BundleOptions, isMultiBundle: true); isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath ?? reader.FullPath); } return isLoaded; @@ -540,29 +518,92 @@ namespace AssetStudio } } + public void LoadOptionFiles(List pathList) + { + if (pathList.Count == 0) + return; + + var optionFileIndexes = new List(); + for (var i = 0; i < pathList.Count; i++) + { + var path = pathList[i]; + if (!path.EndsWith(OptionsFile.Extension, StringComparison.OrdinalIgnoreCase)) + continue; + + optionFileIndexes.Add(i); + var optionsFile = LoadOptionsFile(new FileReader(path)); + if (optionsFile == null) + continue; + + foreach (var optionsLoader in OptionLoaders) + { + optionsLoader(optionsFile); + } + } + + for (var i = 0; i < optionFileIndexes.Count; i++) + { + pathList.RemoveAt(optionFileIndexes[i] - i); + } + } + + private static OptionsFile LoadOptionsFile(FileReader reader) + { + Logger.Info($"Loading options file \"{reader.FullPath}\""); + try + { + return new OptionsFile(reader); + } + catch (Exception e) + { + Logger.Warning($"Error while loading options file \"{reader.FullPath}\"\n{e}"); + return null; + } + finally + { + reader.Dispose(); + } + } + + private void LoadImportOptions(OptionsFile optionsFile) + { + try + { + var importOptions = ImportOptions.FromOptionsFile(optionsFile); + if (importOptions == null) + return; + Options = importOptions; + Logger.Info("Import options successfully loaded."); + } + catch (Exception e) + { + Logger.Warning($"Error while reading import options\n{e}"); + } + } + public void CheckStrippedVersion(SerializedFile assetsFile, UnityVersion bundleUnityVer = null) { - if (assetsFile.version.IsStripped && specifiedUnityVersion == null) + if (assetsFile.version.IsStripped && Options.CustomUnityVersion == null) { var msg = "The asset's Unity version has been stripped, please set the version in the options."; if (bundleUnityVer != null && !bundleUnityVer.IsStripped) msg += $"\n\nAssumed Unity version based on asset bundle: {bundleUnityVer}"; throw new NotSupportedException(msg); } - if (specifiedUnityVersion != null) + if (Options.CustomUnityVersion != null) { - assetsFile.version = SpecifyUnityVersion; + assetsFile.version = Options.CustomUnityVersion; } } public void Clear() { - foreach (var assetsFile in assetsFileList) + foreach (var assetsFile in AssetsFileList) { assetsFile.Objects.Clear(); assetsFile.reader.Close(); } - assetsFileList.Clear(); + AssetsFileList.Clear(); foreach (var resourceFileReader in resourceFileReaders) { @@ -585,10 +626,10 @@ namespace AssetStudio IncludeFields = true, }; - var progressCount = assetsFileList.Sum(x => x.m_Objects.Count); + var progressCount = AssetsFileList.Sum(x => x.m_Objects.Count); var i = 0; Progress.Reset(); - foreach (var assetsFile in assetsFileList) + foreach (var assetsFile in AssetsFileList) { JsonConverterHelper.AssetsFile = assetsFile; foreach (var objectInfo in assetsFile.m_Objects) @@ -735,7 +776,7 @@ namespace AssetStudio { Logger.Info("Process assets..."); - foreach (var assetsFile in assetsFileList) + foreach (var assetsFile in AssetsFileList) { foreach (var obj in assetsFile.Objects) { diff --git a/AssetStudio/BundleFile.cs b/AssetStudio/BundleFile.cs index d1462b5..7162821 100644 --- a/AssetStudio/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Collections.Generic; +using AssetStudio.CustomOptions; namespace AssetStudio { @@ -44,6 +45,7 @@ namespace AssetStudio public class BundleFile { public readonly bool IsDataAfterBundle; + private readonly CustomBundleOptions _bundleOptions; public class Header { @@ -78,9 +80,10 @@ namespace AssetStudio public List fileList; - public BundleFile(FileReader reader, CompressionType customBlockInfoCompression, CompressionType customBlockCompression, UnityVersion specUnityVer = null, bool isMultiBundle = false) + public BundleFile(FileReader reader, CustomBundleOptions bundleOptions, bool isMultiBundle = false) { Stream blocksStream; + _bundleOptions = bundleOptions; m_Header = new Header(); m_Header.signature = reader.ReadStringToNull(); m_Header.version = reader.ReadUInt32(); @@ -121,20 +124,21 @@ namespace AssetStudio } var unityVer = m_Header.unityRevision; - if (specUnityVer != null) + var customUnityVer = _bundleOptions.Options.CustomUnityVersion; + if (customUnityVer != null) { - if (!unityVer.IsStripped && specUnityVer != unityVer) + if (!unityVer.IsStripped && customUnityVer != unityVer) { - Logger.Warning($"Detected Unity version is different from the specified one ({specUnityVer.FullVersion.Color(ColorConsole.BrightCyan)}).\n" + + Logger.Warning($"Detected Unity version is different from the specified one ({customUnityVer.FullVersion.Color(ColorConsole.BrightCyan)}).\n" + $"Assets may load with errors.\n" + $"It is recommended to specify the detected Unity version: {unityVer.FullVersion.Color(ColorConsole.BrightCyan)}"); } - unityVer = specUnityVer; + unityVer = customUnityVer; } - UnityCnCheck(reader, customBlockInfoCompression, unityVer); + UnityCnCheck(reader, unityVer); - ReadBlocksInfoAndDirectory(reader, customBlockInfoCompression, unityVer); + ReadBlocksInfoAndDirectory(reader, unityVer); if (!isMultiBundle && IsUncompressedBundle) { @@ -142,7 +146,7 @@ namespace AssetStudio break; } - blocksStream = ReadBlocks(reader, customBlockCompression); + blocksStream = ReadBlocks(reader); ReadFiles(blocksStream); if (!IsDataAfterBundle) @@ -190,7 +194,7 @@ namespace AssetStudio private Stream CreateBlocksStream(string path) { var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize); - return uncompressedSizeSum >= int.MaxValue + return uncompressedSizeSum >= int.MaxValue || _bundleOptions.DecompressToDisk ? new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose) : (Stream) new MemoryStream((int)uncompressedSizeSum); } @@ -264,7 +268,7 @@ namespace AssetStudio } } - private void ReadBlocksInfoAndDirectory(FileReader reader, CompressionType customBlockInfoCompression, UnityVersion unityVer, bool silent = false) + private void ReadBlocksInfoAndDirectory(FileReader reader, UnityVersion unityVer, bool silent = false) { byte[] blocksInfoBytes; @@ -304,6 +308,7 @@ namespace AssetStudio blocksInfoBytes = reader.ReadBytes(compressedSize); } + var customBlockInfoCompression = _bundleOptions.CustomBlockInfoCompression; var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask); if (customBlockInfoCompression == CompressionType.Auto) { @@ -402,8 +407,9 @@ namespace AssetStudio } } - private Stream ReadBlocks(FileReader reader, CompressionType customBlockCompression) + private Stream ReadBlocks(FileReader reader) { + var customBlockCompression = _bundleOptions.CustomBlockCompression; var blocksStream = CreateBlocksStream(reader.FullPath); var blocksCompression = m_BlocksInfo.Max(x => (CompressionType)(x.flags & StorageBlockFlags.CompressionTypeMask)); Logger.Debug($"BlockData compression: {blocksCompression}"); @@ -503,7 +509,7 @@ namespace AssetStudio return blocksStream; } - private void UnityCnCheck(FileReader reader, CompressionType customBlockInfoCompression, UnityVersion unityVer) + private void UnityCnCheck(FileReader reader, UnityVersion unityVer) { var hasUnityCnFlag = false; if (!unityVer.IsStripped) @@ -528,7 +534,7 @@ namespace AssetStudio reader.Position += 70; try { - ReadBlocksInfoAndDirectory(reader, customBlockInfoCompression, unityVer, silent: true); + ReadBlocksInfoAndDirectory(reader, unityVer, silent: true); } catch (Exception) { diff --git a/AssetStudio/Classes/PPtr.cs b/AssetStudio/Classes/PPtr.cs index bd973e5..3d13840 100644 --- a/AssetStudio/Classes/PPtr.cs +++ b/AssetStudio/Classes/PPtr.cs @@ -39,7 +39,7 @@ namespace AssetStudio if (m_FileID > 0 && m_FileID - 1 < _assetsFile.m_Externals.Count) { var assetsManager = _assetsFile.assetsManager; - var assetsFileList = assetsManager.assetsFileList; + var assetsFileList = assetsManager.AssetsFileList; var assetsFileIndexCache = assetsManager.assetsFileIndexCache; if (_index == -2) @@ -126,7 +126,7 @@ namespace AssetStudio } var assetsManager = _assetsFile.assetsManager; - var assetsFileList = assetsManager.assetsFileList; + var assetsFileList = assetsManager.AssetsFileList; var assetsFileIndexCache = assetsManager.assetsFileIndexCache; if (!assetsFileIndexCache.TryGetValue(name, out _index)) diff --git a/AssetStudio/CustomOptions/Asmo/OptionsFile.cs b/AssetStudio/CustomOptions/Asmo/OptionsFile.cs new file mode 100644 index 0000000..e143598 --- /dev/null +++ b/AssetStudio/CustomOptions/Asmo/OptionsFile.cs @@ -0,0 +1,56 @@ +using System; +using System.Buffers.Binary; + +namespace AssetStudio.CustomOptions.Asmo +{ + public class OptionsFile + { + private static readonly byte[] OptionsFileSign = new byte[] { 0x41, 0x53, 0x4D, 0x4F }; //ASMO + public static readonly string Extension = ".asmo"; + + public byte[] Signature { get; private set; } = OptionsFileSign; + public short Version { get; private set; } = 1; + public short Reserved { get; set; } + public uint DataCrc { get; set; } + public int DataSize { get; set; } + public byte[] Data { get; set; } + + public OptionsFile() { } + + public OptionsFile(EndianBinaryReader reader) + { + Signature = reader.ReadBytes(4); + Version = reader.ReadInt16(); + Reserved = reader.ReadInt16(); + DataCrc = reader.ReadUInt32(); + DataSize = reader.ReadInt32(); + CheckHeader(reader.BaseStream.Length); + Data = reader.ReadBytes(DataSize); + } + + public void CheckHeader(long fileLength) + { + if (!Signature.AsSpan().SequenceEqual(OptionsFileSign)) + throw new NotSupportedException("Incorrect options file signature."); + + if (Version != 1) + throw new NotSupportedException("Incorrect options file version."); + + if (DataSize <= 0 || DataSize > fileLength) + throw new NotSupportedException("Incorrect data size."); + } + + public byte[] ToByteArray() + { + var buffer = new byte[16 + DataSize]; + OptionsFileSign.AsSpan().CopyTo(buffer); + BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(4), Version); + BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(6), Reserved); + BinaryPrimitives.WriteUInt32BigEndian(buffer.AsSpan(8), DataCrc); + BinaryPrimitives.WriteInt32BigEndian(buffer.AsSpan(12), DataSize); + Data.AsSpan().CopyTo(buffer.AsSpan(16)); + + return buffer; + } + } +} diff --git a/AssetStudio/CustomOptions/CustomBundleOptions.cs b/AssetStudio/CustomOptions/CustomBundleOptions.cs new file mode 100644 index 0000000..8c974a5 --- /dev/null +++ b/AssetStudio/CustomOptions/CustomBundleOptions.cs @@ -0,0 +1,40 @@ +namespace AssetStudio.CustomOptions +{ + public class CustomBundleOptions + { + private CompressionType _customBlockCompression = CompressionType.Auto; + private CompressionType _customBlockInfoCompression = CompressionType.Auto; + private bool _decompressToDisk; + + public ImportOptions Options; + + public CompressionType CustomBlockCompression + { + get => _customBlockCompression; + set => _customBlockCompression = SetOption(nameof(CustomBlockCompression), value); + } + public CompressionType CustomBlockInfoCompression + { + get => _customBlockInfoCompression; + set => _customBlockInfoCompression = SetOption(nameof(CustomBlockInfoCompression), value); + } + public bool DecompressToDisk + { + get => _decompressToDisk; + set => _decompressToDisk = SetOption(nameof(DecompressToDisk), value); + } + + public CustomBundleOptions() { } + + public CustomBundleOptions(ImportOptions importOptions) + { + Options = importOptions; + } + + private static T SetOption(string option, T value) + { + Logger.Info($"- {option}: {value}"); + return value; + } + } +} diff --git a/AssetStudio/CustomOptions/ImportOptions.cs b/AssetStudio/CustomOptions/ImportOptions.cs new file mode 100644 index 0000000..452904f --- /dev/null +++ b/AssetStudio/CustomOptions/ImportOptions.cs @@ -0,0 +1,94 @@ +using AssetStudio.CustomOptions.Asmo; +using SevenZip; +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace AssetStudio.CustomOptions +{ + public class ImportOptions + { + private static JsonSerializerOptions jsonOptions; + private static string fileName = "ImportOptions"; + private UnityVersion _customUnityVer; + + public CustomBundleOptions BundleOptions { get; set; } + public UnityVersion CustomUnityVersion { get => _customUnityVer; set => SetUnityVersion(value); } + + static ImportOptions() + { + jsonOptions = new JsonSerializerOptions + { + ReferenceHandler = ReferenceHandler.Preserve, + IncludeFields = true, + }; + } + + public ImportOptions() + { + BundleOptions = new CustomBundleOptions(this); + CustomUnityVersion = null; + } + + public static ImportOptions FromOptionsFile(OptionsFile optionsFile) + { + if (optionsFile.Reserved != 0) + { + Logger.Debug("Skipped. Not an import options file."); + return null; + } + + var utf8Bytes = Convert.FromBase64String(Encoding.ASCII.GetString(optionsFile.Data)); + var dataCrc = CRC.CalculateDigest(utf8Bytes, 0, (uint)utf8Bytes.Length); + if (optionsFile.DataCrc != dataCrc) + throw new IOException("Options file data is corrupted."); + + return JsonSerializer.Deserialize(utf8Bytes, jsonOptions); + } + + public void SaveToFile(string outputFolder) + { + var utf8Bytes = JsonSerializer.SerializeToUtf8Bytes(this, jsonOptions); + var dataCrc = CRC.CalculateDigest(utf8Bytes, 0, (uint)utf8Bytes.Length); + var base64String = Convert.ToBase64String(utf8Bytes); + + var optionsFile = new OptionsFile + { + DataCrc = dataCrc, + DataSize = base64String.Length, + Data = Encoding.ASCII.GetBytes(base64String), + }; + + var unityVer = _customUnityVer != null + ? $"_{_customUnityVer}" + : ""; + var path = Path.Combine(outputFolder, $"{fileName}{unityVer}{OptionsFile.Extension}"); + File.WriteAllBytes(path, optionsFile.ToByteArray()); + + Logger.Info($"Options file saved to \"{path}\""); + } + + private void SetUnityVersion(UnityVersion value) + { + if (_customUnityVer == value) + return; + if (value == null) + { + _customUnityVer = null; + Logger.Info("- Specified Unity version: None"); + return; + } + if (string.IsNullOrEmpty(value.BuildType)) + { + throw new NotSupportedException("Specified Unity version is not in a correct format.\n" + + "Specify full Unity version, including letters at the end.\n" + + "Example: 2017.4.39f1"); + } + _customUnityVer = value; + + Logger.Info($"- Specified Unity version: {_customUnityVer}"); + } + } +} diff --git a/AssetStudio/FileType.cs b/AssetStudio/FileType.cs index f803e97..b73b4ff 100644 --- a/AssetStudio/FileType.cs +++ b/AssetStudio/FileType.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AssetStudio +namespace AssetStudio { public enum FileType { diff --git a/AssetStudio/SerializedFile.cs b/AssetStudio/SerializedFile.cs index 9c07940..0b19732 100644 --- a/AssetStudio/SerializedFile.cs +++ b/AssetStudio/SerializedFile.cs @@ -74,13 +74,13 @@ namespace AssetStudio var verStr = reader.ReadStringToNull(); if (!UnityVersion.TryParse(verStr, out version)) { - if (assetsManager.SpecifyUnityVersion == null) + if (assetsManager.Options.CustomUnityVersion == null) { Logger.Warning($"Failed to parse Unity version: \"{verStr}\""); version = new UnityVersion(); return; } - version = assetsManager.SpecifyUnityVersion; + version = assetsManager.Options.CustomUnityVersion; reader.Position = versionPos; reader.ReadBytes(version.ToString().Length + 1); } diff --git a/AssetStudio/UnityVersion.cs b/AssetStudio/UnityVersion.cs index 535c830..2ba0ed1 100644 --- a/AssetStudio/UnityVersion.cs +++ b/AssetStudio/UnityVersion.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace AssetStudio @@ -54,6 +55,7 @@ namespace AssetStudio } } + [JsonConstructor] public UnityVersion(int major = 0, int minor = 0, int patch = 0) { (Major, Minor, Patch) = (major, minor, patch); diff --git a/AssetStudioCLI/Studio.cs b/AssetStudioCLI/Studio.cs index 8c9013f..2c8b9b6 100644 --- a/AssetStudioCLI/Studio.cs +++ b/AssetStudioCLI/Studio.cs @@ -30,6 +30,11 @@ namespace AssetStudioCLI { Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US"); Progress.Default = new Progress(ShowCurProgressValue); + assetsManager.LoadingViaTypeTreeEnabled = !CLIOptions.f_avoidLoadingViaTypetree.Value; + assetsManager.Options.CustomUnityVersion = CLIOptions.o_unityVersion.Value; + assetsManager.Options.BundleOptions.CustomBlockInfoCompression = CLIOptions.o_bundleBlockInfoCompression.Value; + assetsManager.Options.BundleOptions.CustomBlockCompression = CLIOptions.o_bundleBlockCompression.Value; + assetsManager.OptionLoaders.Clear(); } private static void ShowCurProgressValue(int value) @@ -94,7 +99,7 @@ namespace AssetStudioCLI var count = 0; var bundleStream = new OffsetStream(reader); var bundleReader = new FileReader(reader.FullPath, bundleStream); - var bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion); + var bundleFile = new BundleFile(bundleReader, assetsManager.Options.BundleOptions); var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked"); if (bundleFile.fileList.Count > 0) { @@ -113,7 +118,7 @@ namespace AssetStudioCLI bundleReader.FileName = $"{reader.FileName}_0x{bundleStream.Offset:X}"; } Logger.Info($"[MultiBundle] Decompressing \"{reader.FileName}\" from offset: 0x{bundleStream.Offset:X}.."); - bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion, isMultiBundle: true); + bundleFile = new BundleFile(bundleReader, assetsManager.Options.BundleOptions, isMultiBundle: true); if (bundleFile.fileList.Count > 0) { count += ExtractStreamFile(extractPath, bundleFile.fileList); @@ -170,16 +175,13 @@ namespace AssetStudioCLI public static bool LoadAssets() { var isLoaded = false; - assetsManager.SpecifyUnityVersion = CLIOptions.o_unityVersion.Value; - assetsManager.CustomBlockInfoCompression = CLIOptions.o_bundleBlockInfoCompression.Value; - assetsManager.CustomBlockCompression = CLIOptions.o_bundleBlockCompression.Value; - assetsManager.LoadingViaTypeTreeEnabled = !CLIOptions.f_avoidLoadingViaTypetree.Value; + if (!CLIOptions.f_loadAllAssets.Value) { assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value); } assetsManager.LoadFilesAndFolders(out _, CLIOptions.inputPathList); - if (assetsManager.assetsFileList.Count == 0) + if (assetsManager.AssetsFileList.Count == 0) { Logger.Warning("No Unity file can be loaded."); } @@ -197,13 +199,13 @@ namespace AssetStudioCLI var fileAssetsList = new List(); var tex2dArrayAssetList = new List(); - var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count); + var objectCount = assetsManager.AssetsFileList.Sum(x => x.Objects.Count); var objectAssetItemDic = new Dictionary(objectCount); var isL2dMode = CLIOptions.o_workMode.Value == WorkMode.Live2D; Progress.Reset(); var i = 0; - foreach (var assetsFile in assetsManager.assetsFileList) + foreach (var assetsFile in assetsManager.AssetsFileList) { var preloadTable = new List>(); foreach (var asset in assetsFile.Objects) @@ -370,21 +372,21 @@ namespace AssetStudioCLI BuildTreeStructure(objectAssetItemDic); } - var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {parsedAssetsList.Count} exportable assets"; - var unityVer = assetsManager.assetsFileList[0].version; + var log = $"Finished loading {assetsManager.AssetsFileList.Count} files with {parsedAssetsList.Count} exportable assets"; + var unityVer = assetsManager.AssetsFileList[0].version; long m_ObjectsCount; if (unityVer > 2020) { - m_ObjectsCount = assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y => + m_ObjectsCount = assetsManager.AssetsFileList.Sum(x => x.m_Objects.LongCount(y => y.classID != (int)ClassIDType.Shader && CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID)) ); } else { - m_ObjectsCount = assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID))); + m_ObjectsCount = assetsManager.AssetsFileList.Sum(x => x.m_Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID))); } - var objectsCount = assetsManager.assetsFileList.Sum(x => x.Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => k == y.type))); + var objectsCount = assetsManager.AssetsFileList.Sum(x => x.Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => k == y.type))); if (m_ObjectsCount != objectsCount) { log += $" and {m_ObjectsCount - objectsCount} assets failed to read"; @@ -397,10 +399,10 @@ namespace AssetStudioCLI Logger.Info("Building tree structure..."); var treeNodeDictionary = new Dictionary(); - var assetsFileCount = assetsManager.assetsFileList.Count; + var assetsFileCount = assetsManager.AssetsFileList.Count; int j = 0; Progress.Reset(); - foreach (var assetsFile in assetsManager.assetsFileList) + foreach (var assetsFile in assetsManager.AssetsFileList) { var fileNode = new BaseNode(assetsFile.fileName); //RootNode diff --git a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs index d1f7a31..43388da 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs @@ -46,13 +46,17 @@ this.autoPlayAudioAssetsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.useDumpTreeViewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.buildTreeStructureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.customCompressionTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.blockInfoCompressionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.customBlockInfoCompressionComboBoxToolStripMenuItem = new System.Windows.Forms.ToolStripComboBox(); - this.blockCompressionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.customBlockCompressionComboBoxToolStripMenuItem = new System.Windows.Forms.ToolStripComboBox(); - this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem(); - this.specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox(); + this.importOptionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.specifyUnityVersionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.specifyUnityVersionTextBox = new System.Windows.Forms.ToolStripTextBox(); + this.bundleDecompressionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.alwaysDecompressToDiskToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.blockInfoCompressionTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.customBlockInfoCompressionComboBox = new System.Windows.Forms.ToolStripComboBox(); + this.blockCompressionTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.customBlockCompressionComboBox = new System.Windows.Forms.ToolStripComboBox(); + this.importOptionsToolStripSeparator = new System.Windows.Forms.ToolStripSeparator(); + this.saveOptionsToDiskToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.showExpOpt = new System.Windows.Forms.ToolStripMenuItem(); this.modelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportAllObjectssplitToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); @@ -273,8 +277,7 @@ this.autoPlayAudioAssetsToolStripMenuItem, this.useDumpTreeViewToolStripMenuItem, this.buildTreeStructureToolStripMenuItem, - this.customCompressionTypeToolStripMenuItem, - this.toolStripMenuItem14, + this.importOptionsToolStripMenuItem, this.showExpOpt}); this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; this.optionsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); @@ -359,79 +362,118 @@ 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 + // importOptionsToolStripMenuItem // - this.customCompressionTypeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.blockInfoCompressionToolStripMenuItem, - this.customBlockInfoCompressionComboBoxToolStripMenuItem, - this.blockCompressionToolStripMenuItem, - this.customBlockCompressionComboBoxToolStripMenuItem}); - this.customCompressionTypeToolStripMenuItem.Name = "customCompressionTypeToolStripMenuItem"; - this.customCompressionTypeToolStripMenuItem.Size = new System.Drawing.Size(241, 22); - this.customCompressionTypeToolStripMenuItem.Text = "Bundle compression type"; + this.importOptionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.specifyUnityVersionToolStripMenuItem, + this.specifyUnityVersionTextBox, + this.bundleDecompressionToolStripMenuItem, + this.alwaysDecompressToDiskToolStripMenuItem, + this.blockInfoCompressionTypeToolStripMenuItem, + this.customBlockInfoCompressionComboBox, + this.blockCompressionTypeToolStripMenuItem, + this.customBlockCompressionComboBox, + this.importOptionsToolStripSeparator, + this.saveOptionsToDiskToolStripMenuItem}); + this.importOptionsToolStripMenuItem.Name = "importOptionsToolStripMenuItem"; + this.importOptionsToolStripMenuItem.Size = new System.Drawing.Size(241, 22); + this.importOptionsToolStripMenuItem.Text = "Import options"; + this.importOptionsToolStripMenuItem.DropDownClosed += new System.EventHandler(this.importOptions_DropDownClose); + this.importOptionsToolStripMenuItem.DropDownOpened += new System.EventHandler(this.importOptions_DropDownOpened); // - // blockInfoCompressionToolStripMenuItem + // specifyUnityVersionToolStripMenuItem // - this.blockInfoCompressionToolStripMenuItem.Enabled = false; - this.blockInfoCompressionToolStripMenuItem.Name = "blockInfoCompressionToolStripMenuItem"; - this.blockInfoCompressionToolStripMenuItem.Size = new System.Drawing.Size(197, 22); - this.blockInfoCompressionToolStripMenuItem.Text = "BlockInfo Compression"; + this.specifyUnityVersionToolStripMenuItem.Enabled = false; + this.specifyUnityVersionToolStripMenuItem.Name = "specifyUnityVersionToolStripMenuItem"; + this.specifyUnityVersionToolStripMenuItem.ShowShortcutKeys = false; + this.specifyUnityVersionToolStripMenuItem.Size = new System.Drawing.Size(217, 22); + this.specifyUnityVersionToolStripMenuItem.Text = "Specify Unity version"; + this.specifyUnityVersionToolStripMenuItem.ToolTipText = "Specify full Unity version, including letters at the end\r\nExample: 2017.4.39f1"; // - // customBlockInfoCompressionComboBoxToolStripMenuItem + // specifyUnityVersionTextBox // - this.customBlockInfoCompressionComboBoxToolStripMenuItem.DropDownHeight = 80; - this.customBlockInfoCompressionComboBoxToolStripMenuItem.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.customBlockInfoCompressionComboBoxToolStripMenuItem.IntegralHeight = false; - this.customBlockInfoCompressionComboBoxToolStripMenuItem.Items.AddRange(new object[] { + this.specifyUnityVersionTextBox.Font = new System.Drawing.Font("Segoe UI", 9F); + this.specifyUnityVersionTextBox.Name = "specifyUnityVersionTextBox"; + this.specifyUnityVersionTextBox.Size = new System.Drawing.Size(100, 23); + // + // bundleDecompressionToolStripMenuItem + // + this.bundleDecompressionToolStripMenuItem.Enabled = false; + this.bundleDecompressionToolStripMenuItem.Name = "bundleDecompressionToolStripMenuItem"; + this.bundleDecompressionToolStripMenuItem.ShowShortcutKeys = false; + this.bundleDecompressionToolStripMenuItem.Size = new System.Drawing.Size(217, 22); + this.bundleDecompressionToolStripMenuItem.Text = "Bundle Decompression"; + // + // alwaysDecompressToDiskToolStripMenuItem + // + this.alwaysDecompressToDiskToolStripMenuItem.CheckOnClick = true; + this.alwaysDecompressToDiskToolStripMenuItem.Name = "alwaysDecompressToDiskToolStripMenuItem"; + this.alwaysDecompressToDiskToolStripMenuItem.ShowShortcutKeys = false; + this.alwaysDecompressToDiskToolStripMenuItem.Size = new System.Drawing.Size(217, 22); + this.alwaysDecompressToDiskToolStripMenuItem.Text = "Always decompress to disk"; + this.alwaysDecompressToDiskToolStripMenuItem.ToolTipText = "If not selected, any bundles less than 2GB will be decompressed to memory"; + this.alwaysDecompressToDiskToolStripMenuItem.Click += new System.EventHandler(this.alwaysDecompressToDiskToolStripMenuItem_Click); + // + // blockInfoCompressionTypeToolStripMenuItem + // + this.blockInfoCompressionTypeToolStripMenuItem.Enabled = false; + this.blockInfoCompressionTypeToolStripMenuItem.Name = "blockInfoCompressionTypeToolStripMenuItem"; + this.blockInfoCompressionTypeToolStripMenuItem.ShowShortcutKeys = false; + this.blockInfoCompressionTypeToolStripMenuItem.Size = new System.Drawing.Size(217, 22); + this.blockInfoCompressionTypeToolStripMenuItem.Text = "BlockInfo Compression Type"; + // + // customBlockInfoCompressionComboBox + // + this.customBlockInfoCompressionComboBox.DropDownHeight = 80; + this.customBlockInfoCompressionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.customBlockInfoCompressionComboBox.IntegralHeight = false; + this.customBlockInfoCompressionComboBox.Items.AddRange(new object[] { "Auto", "Zstd", "Oodle", "Lz4/Lz4HC", "Lzma"}); - this.customBlockInfoCompressionComboBoxToolStripMenuItem.Name = "customBlockInfoCompressionComboBoxToolStripMenuItem"; - this.customBlockInfoCompressionComboBoxToolStripMenuItem.Size = new System.Drawing.Size(100, 23); - this.customBlockInfoCompressionComboBoxToolStripMenuItem.ToolTipText = "Selected compression type will override detected type from asset bundle"; - this.customBlockInfoCompressionComboBoxToolStripMenuItem.SelectedIndexChanged += new System.EventHandler(this.customBlockInfoCompressionComboBoxToolStripMenuItem_SelectedIndexChanged); + this.customBlockInfoCompressionComboBox.Name = "customBlockInfoCompressionComboBox"; + this.customBlockInfoCompressionComboBox.Size = new System.Drawing.Size(121, 23); + this.customBlockInfoCompressionComboBox.ToolTipText = "Selected compression type will override detected type from asset bundle"; + this.customBlockInfoCompressionComboBox.SelectedIndexChanged += new System.EventHandler(this.customBlockInfoCompressionComboBox_SelectedIndexChanged); // - // blockCompressionToolStripMenuItem + // blockCompressionTypeToolStripMenuItem // - this.blockCompressionToolStripMenuItem.Enabled = false; - this.blockCompressionToolStripMenuItem.Name = "blockCompressionToolStripMenuItem"; - this.blockCompressionToolStripMenuItem.Size = new System.Drawing.Size(197, 22); - this.blockCompressionToolStripMenuItem.Text = "Block Compression"; + this.blockCompressionTypeToolStripMenuItem.Enabled = false; + this.blockCompressionTypeToolStripMenuItem.Name = "blockCompressionTypeToolStripMenuItem"; + this.blockCompressionTypeToolStripMenuItem.ShowShortcutKeys = false; + this.blockCompressionTypeToolStripMenuItem.Size = new System.Drawing.Size(217, 22); + this.blockCompressionTypeToolStripMenuItem.Text = "Block Compression Type"; // - // customBlockCompressionComboBoxToolStripMenuItem + // customBlockCompressionComboBox // - this.customBlockCompressionComboBoxToolStripMenuItem.DropDownHeight = 80; - this.customBlockCompressionComboBoxToolStripMenuItem.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.customBlockCompressionComboBoxToolStripMenuItem.IntegralHeight = false; - this.customBlockCompressionComboBoxToolStripMenuItem.Items.AddRange(new object[] { + this.customBlockCompressionComboBox.DropDownHeight = 80; + this.customBlockCompressionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.customBlockCompressionComboBox.IntegralHeight = false; + this.customBlockCompressionComboBox.Items.AddRange(new object[] { "Auto", "Zstd", "Oodle", "Lz4/Lz4HC", "Lzma"}); - this.customBlockCompressionComboBoxToolStripMenuItem.Name = "customBlockCompressionComboBoxToolStripMenuItem"; - this.customBlockCompressionComboBoxToolStripMenuItem.Size = new System.Drawing.Size(100, 23); - this.customBlockCompressionComboBoxToolStripMenuItem.ToolTipText = "Selected compression type will override detected type from asset bundle"; - this.customBlockCompressionComboBoxToolStripMenuItem.SelectedIndexChanged += new System.EventHandler(this.customBlockCompressionComboBoxToolStripMenuItem_SelectedIndexChanged); + this.customBlockCompressionComboBox.Name = "customBlockCompressionComboBox"; + this.customBlockCompressionComboBox.Size = new System.Drawing.Size(121, 23); + this.customBlockCompressionComboBox.ToolTipText = "Selected compression type will override detected type from asset bundle"; + this.customBlockCompressionComboBox.SelectedIndexChanged += new System.EventHandler(this.customBlockCompressionComboBox_SelectedIndexChanged); // - // toolStripMenuItem14 + // importOptionsToolStripSeparator // - this.toolStripMenuItem14.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.specifyUnityVersion}); - this.toolStripMenuItem14.Name = "toolStripMenuItem14"; - this.toolStripMenuItem14.Size = new System.Drawing.Size(241, 22); - this.toolStripMenuItem14.Text = "Specify Unity version"; - this.toolStripMenuItem14.DropDownClosed += new System.EventHandler(this.specifyUnityVersion_Close); + this.importOptionsToolStripSeparator.Name = "importOptionsToolStripSeparator"; + this.importOptionsToolStripSeparator.Size = new System.Drawing.Size(214, 6); // - // specifyUnityVersion + // saveOptionsToDiskToolStripMenuItem // - this.specifyUnityVersion.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.specifyUnityVersion.Font = new System.Drawing.Font("Microsoft YaHei UI", 9F); - this.specifyUnityVersion.Name = "specifyUnityVersion"; - this.specifyUnityVersion.Size = new System.Drawing.Size(100, 23); - this.specifyUnityVersion.ToolTipText = "Specify full Unity version, including letters at the end\r\nExample: 2017.4.39f1"; + this.saveOptionsToDiskToolStripMenuItem.Name = "saveOptionsToDiskToolStripMenuItem"; + this.saveOptionsToDiskToolStripMenuItem.ShowShortcutKeys = false; + this.saveOptionsToDiskToolStripMenuItem.Size = new System.Drawing.Size(217, 22); + this.saveOptionsToDiskToolStripMenuItem.Text = "Save options to disk"; + this.saveOptionsToDiskToolStripMenuItem.Click += new System.EventHandler(this.saveOptionsToDiskToolStripMenuItem_Click); // // showExpOpt // @@ -1758,8 +1800,6 @@ private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem11; private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem12; private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem13; - private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem14; - private System.Windows.Forms.ToolStripTextBox specifyUnityVersion; private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem15; private System.Windows.Forms.ToolStripMenuItem dumpSelectedAssetsToolStripMenuItem; private System.Windows.Forms.ContextMenuStrip sceneContextMenuStrip; @@ -1790,7 +1830,6 @@ 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 useAssetLoadingViaTypetreeToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator assetLoadingToolStripSeparator; private System.Windows.Forms.TreeView dumpTreeView; @@ -1806,11 +1845,18 @@ private System.Windows.Forms.ToolStripMenuItem colorThemeDarkToolStripMenuItem; private System.Windows.Forms.Label FMODaudioChannelsLabel; private System.Windows.Forms.ToolStripMenuItem autoPlayAudioAssetsToolStripMenuItem; - private System.Windows.Forms.ToolStripComboBox customBlockCompressionComboBoxToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem blockCompressionToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem blockInfoCompressionToolStripMenuItem; - private System.Windows.Forms.ToolStripComboBox customBlockInfoCompressionComboBoxToolStripMenuItem; private System.Windows.Forms.CheckBox sceneExactSearchCheckBox; + private System.Windows.Forms.ToolStripMenuItem importOptionsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem specifyUnityVersionToolStripMenuItem; + private System.Windows.Forms.ToolStripTextBox specifyUnityVersionTextBox; + private System.Windows.Forms.ToolStripMenuItem bundleDecompressionToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem alwaysDecompressToDiskToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem blockInfoCompressionTypeToolStripMenuItem; + private System.Windows.Forms.ToolStripComboBox customBlockInfoCompressionComboBox; + private System.Windows.Forms.ToolStripMenuItem blockCompressionTypeToolStripMenuItem; + private System.Windows.Forms.ToolStripComboBox customBlockCompressionComboBox; + private System.Windows.Forms.ToolStripMenuItem saveOptionsToDiskToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator importOptionsToolStripSeparator; } } diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index c26d27d..6708c19 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -145,8 +145,9 @@ namespace AssetStudioGUI useAssetLoadingViaTypetreeToolStripMenuItem.Checked = Properties.Settings.Default.useTypetreeLoading; useDumpTreeViewToolStripMenuItem.Checked = Properties.Settings.Default.useDumpTreeView; autoPlayAudioAssetsToolStripMenuItem.Checked = Properties.Settings.Default.autoplayAudio; - customBlockCompressionComboBoxToolStripMenuItem.SelectedIndex = 0; - customBlockInfoCompressionComboBoxToolStripMenuItem.SelectedIndex = 0; + customBlockCompressionComboBox.SelectedIndex = 0; + customBlockInfoCompressionComboBox.SelectedIndex = 0; + assetsManager.Options.BundleOptions.DecompressToDisk = Properties.Settings.Default.decompressToDisk; FMODinit(); listSearchFilterMode.SelectedIndex = 0; if (string.IsNullOrEmpty(Properties.Settings.Default.fbxSettings)) @@ -172,23 +173,28 @@ namespace AssetStudioGUI private async void AssetStudioGUIForm_DragDrop(object sender, DragEventArgs e) { - var paths = (string[])e.Data.GetData(DataFormats.FileDrop); - if (paths.Length == 0) + var pathArray = (string[])e.Data?.GetData(DataFormats.FileDrop); + if (pathArray == null) + return; + + var pathList = pathArray.ToList(); + assetsManager.LoadOptionFiles(pathList); + if (pathList.Count == 0) return; ResetForm(); - for (var i = 0; i < paths.Length; i++) + for (var i = 0; i < pathList.Count; i++) { - if (paths[i].ToLower().EndsWith(".lnk")) + if (pathList[i].ToLower().EndsWith(".lnk")) { - var targetPath = LnkReader.GetLnkTarget(paths[i]); + var targetPath = LnkReader.GetLnkTarget(pathList[i]); if (!string.IsNullOrEmpty(targetPath)) { - paths[i] = targetPath; + pathList[i] = targetPath; } } } - await Task.Run(() => assetsManager.LoadFilesAndFolders(out openDirectoryBackup, paths)); + await Task.Run(() => assetsManager.LoadFilesAndFolders(out openDirectoryBackup, pathList)); saveDirectoryBackup = openDirectoryBackup; BuildAssetStructures(); } @@ -198,8 +204,12 @@ namespace AssetStudioGUI openFileDialog1.InitialDirectory = openDirectoryBackup; if (openFileDialog1.ShowDialog(this) == DialogResult.OK) { + var pathList = openFileDialog1.FileNames.ToList(); + assetsManager.LoadOptionFiles(pathList); + if (pathList.Count == 0) + return; ResetForm(); - await Task.Run(() => assetsManager.LoadFilesAndFolders(out openDirectoryBackup, openFileDialog1.FileNames)); + await Task.Run(() => assetsManager.LoadFilesAndFolders(out openDirectoryBackup, pathList)); BuildAssetStructures(); } } @@ -216,24 +226,6 @@ namespace AssetStudioGUI } } - private void specifyUnityVersion_Close(object sender, EventArgs e) - { - if (string.IsNullOrEmpty(specifyUnityVersion.Text)) - { - assetsManager.SpecifyUnityVersion = null; - return; - } - - try - { - assetsManager.SpecifyUnityVersion = new UnityVersion(specifyUnityVersion.Text); - } - catch (Exception ex) - { - Logger.Error(ex.Message); - } - } - private async void extractFileToolStripMenuItem_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog(this) == DialogResult.OK) @@ -269,7 +261,7 @@ namespace AssetStudioGUI private async void BuildAssetStructures() { - if (assetsManager.assetsFileList.Count == 0) + if (assetsManager.AssetsFileList.Count == 0) { Logger.Info("No Unity file can be loaded."); return; @@ -281,7 +273,7 @@ namespace AssetStudioGUI if (isDarkMode) Progress.Reset(); - var serializedFile = assetsManager.assetsFileList[0]; + var serializedFile = assetsManager.AssetsFileList[0]; var tuanjieString = serializedFile.version.IsTuanjie ? " - Tuanjie Engine" : ""; Text = $"{guiTitle} - {productName} - {serializedFile.version} - {serializedFile.targetPlatformString}{tuanjieString}"; @@ -326,12 +318,12 @@ namespace AssetStudioGUI filterTypeToolStripMenuItem.DropDownItems.Add(typeItem); } allToolStripMenuItem.Checked = true; - var log = $"Finished loading {assetsManager.assetsFileList.Count} file(s) with {assetListView.Items.Count} exportable assets"; - var unityVer = assetsManager.assetsFileList[0].version; + var log = $"Finished loading {assetsManager.AssetsFileList.Count} file(s) with {assetListView.Items.Count} exportable assets"; + var unityVer = assetsManager.AssetsFileList[0].version; var m_ObjectsCount = unityVer > 2020 ? - assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y => y.classID != (int)ClassIDType.Shader)) : - assetsManager.assetsFileList.Sum(x => x.m_Objects.Count); - var objectsCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count); + assetsManager.AssetsFileList.Sum(x => x.m_Objects.LongCount(y => y.classID != (int)ClassIDType.Shader)) : + assetsManager.AssetsFileList.Sum(x => x.m_Objects.Count); + var objectsCount = assetsManager.AssetsFileList.Sum(x => x.Objects.Count); if (m_ObjectsCount != objectsCount) { log += $" and {m_ObjectsCount - objectsCount} assets failed to read"; @@ -1551,7 +1543,7 @@ namespace AssetStudioGUI private void ResetForm() { - if (Studio.assetsManager.assetsFileList.Count > 0) + if (Studio.assetsManager.AssetsFileList.Count > 0) Logger.Info("Resetting program..."); Text = guiTitle; @@ -2438,49 +2430,90 @@ namespace AssetStudioGUI } } - private void customBlockCompressionComboBoxToolStripMenuItem_SelectedIndexChanged(object sender, EventArgs e) + private void importOptions_DropDownClose(object sender, EventArgs e) { - var selectedTypeIndex = customBlockCompressionComboBoxToolStripMenuItem.SelectedIndex; - switch (selectedTypeIndex) + if (string.IsNullOrEmpty(specifyUnityVersionTextBox.Text)) { - case 0: - assetsManager.CustomBlockCompression = CompressionType.Auto; - break; - case 1: - assetsManager.CustomBlockCompression = CompressionType.Zstd; - break; - case 2: - assetsManager.CustomBlockCompression = CompressionType.Oodle; - break; - case 3: - assetsManager.CustomBlockCompression = CompressionType.Lz4HC; - break; - case 4: - assetsManager.CustomBlockCompression = CompressionType.Lzma; - break; + assetsManager.Options.CustomUnityVersion = null; + return; + } + + try + { + assetsManager.Options.CustomUnityVersion = new UnityVersion(specifyUnityVersionTextBox.Text); + } + catch (Exception ex) + { + Logger.Error(ex.Message); } } - private void customBlockInfoCompressionComboBoxToolStripMenuItem_SelectedIndexChanged(object sender, EventArgs e) + private void importOptions_DropDownOpened(object sender, EventArgs e) { - var selectedTypeIndex = customBlockInfoCompressionComboBoxToolStripMenuItem.SelectedIndex; - switch (selectedTypeIndex) + if (assetsManager.Options.CustomUnityVersion != null) { - case 0: - assetsManager.CustomBlockInfoCompression = CompressionType.Auto; - break; - case 1: - assetsManager.CustomBlockInfoCompression = CompressionType.Zstd; - break; - case 2: - assetsManager.CustomBlockInfoCompression = CompressionType.Oodle; - break; - case 3: - assetsManager.CustomBlockInfoCompression = CompressionType.Lz4HC; - break; - case 4: - assetsManager.CustomBlockInfoCompression = CompressionType.Lzma; - break; + specifyUnityVersionTextBox.Text = assetsManager.Options.CustomUnityVersion.FullVersion; + } + alwaysDecompressToDiskToolStripMenuItem.Checked = assetsManager.Options.BundleOptions.DecompressToDisk; + customBlockInfoCompressionComboBox.SelectedIndex = SetComboBoxIndex(assetsManager.Options.BundleOptions.CustomBlockInfoCompression); + customBlockCompressionComboBox.SelectedIndex = SetComboBoxIndex(assetsManager.Options.BundleOptions.CustomBlockCompression); + } + + private static int SetComboBoxIndex(CompressionType compressionType) + { + switch (compressionType) + { + case CompressionType.Auto: return 0; + case CompressionType.Lzma: return 4; + case CompressionType.Lz4: + case CompressionType.Lz4HC: return 3; + case CompressionType.Zstd: return 1; + case CompressionType.Oodle: return 2; + default: throw new NotSupportedException(); + } + } + + private void customBlockCompressionComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + var selectedTypeIndex = customBlockCompressionComboBox.SelectedIndex; + assetsManager.Options.BundleOptions.CustomBlockCompression = GetCustomCompressionTypes(selectedTypeIndex); + } + + private void customBlockInfoCompressionComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + var selectedTypeIndex = customBlockInfoCompressionComboBox.SelectedIndex; + assetsManager.Options.BundleOptions.CustomBlockInfoCompression = GetCustomCompressionTypes(selectedTypeIndex); + } + + private static CompressionType GetCustomCompressionTypes(int index) + { + switch (index) + { + case 0: return CompressionType.Auto; + case 1: return CompressionType.Zstd; + case 2: return CompressionType.Oodle; + case 3: return CompressionType.Lz4HC; + case 4: return CompressionType.Lzma; + default: throw new NotSupportedException(); + } + } + + private void alwaysDecompressToDiskToolStripMenuItem_Click(object sender, EventArgs e) + { + var isEnabled = alwaysDecompressToDiskToolStripMenuItem.Checked; + assetsManager.Options.BundleOptions.DecompressToDisk = isEnabled; + Properties.Settings.Default.decompressToDisk = isEnabled; + Properties.Settings.Default.Save(); + } + + private void saveOptionsToDiskToolStripMenuItem_Click(object sender, EventArgs e) + { + var saveFolderDialog = new OpenFolderDialog(); + saveFolderDialog.Title = "Select the save folder"; + if (saveFolderDialog.ShowDialog(this) == DialogResult.OK) + { + var savePath = saveFolderDialog.Folder; + assetsManager.Options.SaveToFile(savePath); } } diff --git a/AssetStudioGUI/Properties/Settings.Designer.cs b/AssetStudioGUI/Properties/Settings.Designer.cs index d68db64..7e015d7 100644 --- a/AssetStudioGUI/Properties/Settings.Designer.cs +++ b/AssetStudioGUI/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace AssetStudioGUI.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -322,5 +322,17 @@ namespace AssetStudioGUI.Properties { this["fbxSettings"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool decompressToDisk { + get { + return ((bool)(this["decompressToDisk"])); + } + set { + this["decompressToDisk"] = value; + } + } } } diff --git a/AssetStudioGUI/Properties/Settings.settings b/AssetStudioGUI/Properties/Settings.settings index 1dbb002..67f11d6 100644 --- a/AssetStudioGUI/Properties/Settings.settings +++ b/AssetStudioGUI/Properties/Settings.settings @@ -77,5 +77,8 @@ + + False + \ No newline at end of file diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index abbf44d..5d850ab 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -141,7 +141,7 @@ namespace AssetStudioGUI var count = 0; var bundleStream = new OffsetStream(reader); var bundleReader = new FileReader(reader.FullPath, bundleStream); - var bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion); + var bundleFile = new BundleFile(bundleReader, assetsManager.Options.BundleOptions); var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked"); if (bundleFile.fileList.Count > 0) { @@ -160,7 +160,7 @@ namespace AssetStudioGUI bundleReader.FileName = $"{reader.FileName}_0x{bundleStream.Offset:X}"; } Logger.Info($"[MultiBundle] Decompressing \"{reader.FileName}\" from offset: 0x{bundleStream.Offset:X}.."); - bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion, isMultiBundle: true); + bundleFile = new BundleFile(bundleReader, assetsManager.Options.BundleOptions, isMultiBundle: true); if (bundleFile.fileList.Count > 0) { count += ExtractStreamFile(extractPath, bundleFile.fileList); @@ -219,14 +219,14 @@ namespace AssetStudioGUI Logger.Info("Building asset list..."); string productName = null; - var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count); + var objectCount = assetsManager.AssetsFileList.Sum(x => x.Objects.Count); var objectAssetItemDic = new Dictionary(objectCount); var containers = new List<(PPtr, string)>(); var tex2dArrayAssetList = new List(); l2dAssetContainers.Clear(); var i = 0; Progress.Reset(); - foreach (var assetsFile in assetsManager.assetsFileList) + foreach (var assetsFile in assetsManager.AssetsFileList) { var preloadTable = new List>(); @@ -428,10 +428,10 @@ namespace AssetStudioGUI var treeNodeCollection = new List(); var treeNodeDictionary = new Dictionary(); - var assetsFileCount = assetsManager.assetsFileList.Count; + var assetsFileCount = assetsManager.AssetsFileList.Count; var j = 0; Progress.Reset(); - foreach (var assetsFile in assetsManager.assetsFileList) + foreach (var assetsFile in assetsManager.AssetsFileList) { var fileNode = new TreeNode(assetsFile.fileName); //RootNode @@ -504,7 +504,7 @@ namespace AssetStudioGUI public static Dictionary> BuildClassStructure() { var typeMap = new Dictionary>(); - foreach (var assetsFile in assetsManager.assetsFileList) + foreach (var assetsFile in assetsManager.AssetsFileList) { if (typeMap.TryGetValue(assetsFile.version, out var curVer)) { diff --git a/AssetStudioUtility/ModelConverter.cs b/AssetStudioUtility/ModelConverter.cs index ffe192d..9c31353 100644 --- a/AssetStudioUtility/ModelConverter.cs +++ b/AssetStudioUtility/ModelConverter.cs @@ -586,7 +586,7 @@ namespace AssetStudio { Logger.Debug("Mesh Renderer had no Mesh attached, trying to find Mesh by name.."); var meshR_originalName = m_GameObject.m_Name; - foreach (var serializedFile in m_GameObject.assetsFile.assetsManager.assetsFileList) + foreach (var serializedFile in m_GameObject.assetsFile.assetsManager.AssetsFileList) { var nameRelatedMesh = (Mesh)serializedFile.Objects.Find(x => x is Mesh m_Mesh && m_Mesh.m_Name == meshR_originalName); if (nameRelatedMesh != null)