From 7ab2cda120ec08d34a7a3c52cd31ab97272a2815 Mon Sep 17 00:00:00 2001 From: Perfare Date: Fri, 2 Jul 2021 02:17:59 +0800 Subject: [PATCH] refactor the file reading part --- AssetStudio/AssetsManager.cs | 77 ++++++++++++++------------- AssetStudio/BundleFile.cs | 14 ++--- AssetStudio/FileReader.cs | 98 +++++++++++++++++++++++++++++++++++ AssetStudio/FileType.cs | 16 ++++++ AssetStudio/ImportHelper.cs | 60 --------------------- AssetStudio/SerializedFile.cs | 45 ++-------------- AssetStudioGUI/Studio.cs | 24 ++++----- 7 files changed, 175 insertions(+), 159 deletions(-) create mode 100644 AssetStudio/FileReader.cs create mode 100644 AssetStudio/FileType.cs diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index 8f08d90..f12b788 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -11,6 +11,7 @@ namespace AssetStudio { public string SpecifyUnityVersion; public List assetsFileList = new List(); + internal Dictionary assetsFileIndexCache = new Dictionary(StringComparer.OrdinalIgnoreCase); internal Dictionary resourceFileReaders = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -60,43 +61,43 @@ namespace AssetStudio private void LoadFile(string fullName) { - switch (CheckFileType(fullName, out var reader)) + var reader = new FileReader(fullName); + switch (reader.FileType) { case FileType.AssetsFile: - LoadAssetsFile(fullName, reader); + LoadAssetsFile(reader); break; case FileType.BundleFile: - LoadBundleFile(fullName, reader); + LoadBundleFile(reader); break; case FileType.WebFile: - LoadWebFile(fullName, reader); + LoadWebFile(reader); break; } } - private void LoadAssetsFile(string fullName, EndianBinaryReader reader) + private void LoadAssetsFile(FileReader reader) { - var fileName = Path.GetFileName(fullName); - if (!assetsFileListHash.Contains(fileName)) + if (!assetsFileListHash.Contains(reader.FileName)) { - Logger.Info($"Loading {fileName}"); + Logger.Info($"Loading {reader.FileName}"); try { - var assetsFile = new SerializedFile(this, fullName, reader); + var assetsFile = new SerializedFile(reader, this); CheckStrippedVersion(assetsFile); assetsFileList.Add(assetsFile); assetsFileListHash.Add(assetsFile.fileName); foreach (var sharedFile in assetsFile.m_Externals) { - var sharedFilePath = Path.Combine(Path.GetDirectoryName(fullName), sharedFile.fileName); var sharedFileName = sharedFile.fileName; if (!importFilesHash.Contains(sharedFileName)) { + var sharedFilePath = Path.Combine(Path.GetDirectoryName(reader.FullPath), sharedFileName); if (!File.Exists(sharedFilePath)) { - var findFiles = Directory.GetFiles(Path.GetDirectoryName(fullName), sharedFileName, SearchOption.AllDirectories); + var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories); if (findFiles.Length > 0) { sharedFilePath = findFiles[0]; @@ -113,7 +114,7 @@ namespace AssetStudio } catch (Exception e) { - Logger.Error($"Error while reading assets file {fileName}", e); + Logger.Error($"Error while reading assets file {reader.FileName}", e); reader.Dispose(); } } @@ -123,14 +124,13 @@ namespace AssetStudio } } - private void LoadAssetsFromMemory(string fullName, EndianBinaryReader reader, string originalPath, string unityVersion = null) + private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null) { - var fileName = Path.GetFileName(fullName); - if (!assetsFileListHash.Contains(fileName)) + if (!assetsFileListHash.Contains(reader.FileName)) { try { - var assetsFile = new SerializedFile(this, fullName, reader); + var assetsFile = new SerializedFile(reader, this); assetsFile.originalPath = originalPath; if (!string.IsNullOrEmpty(unityVersion) && assetsFile.header.m_Version < SerializedFileFormatVersion.kUnknown_7) { @@ -142,26 +142,25 @@ namespace AssetStudio } catch (Exception e) { - Logger.Error($"Error while reading assets file {fileName} from {Path.GetFileName(originalPath)}", e); - resourceFileReaders.Add(fileName, reader); + Logger.Error($"Error while reading assets file {reader.FileName} from {Path.GetFileName(originalPath)}", e); + resourceFileReaders.Add(reader.FileName, reader); } } } - private void LoadBundleFile(string fullName, EndianBinaryReader reader, string parentPath = null) + private void LoadBundleFile(FileReader reader, string originalPath = null) { - var fileName = Path.GetFileName(fullName); - Logger.Info("Loading " + fileName); + Logger.Info("Loading " + reader.FileName); try { - var bundleFile = new BundleFile(reader, fullName); + var bundleFile = new BundleFile(reader); foreach (var file in bundleFile.fileList) { - var subReader = new EndianBinaryReader(file.stream); - if (SerializedFile.IsSerializedFile(subReader)) + var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName); + var subReader = new FileReader(dummyPath, file.stream); + if (subReader.FileType == FileType.AssetsFile) { - var dummyPath = Path.GetDirectoryName(fullName) + Path.DirectorySeparatorChar + file.fileName; - LoadAssetsFromMemory(dummyPath, subReader, parentPath ?? fullName, bundleFile.m_Header.unityRevision); + LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision); } else { @@ -171,10 +170,10 @@ namespace AssetStudio } catch (Exception e) { - var str = $"Error while reading bundle file {fileName}"; - if (parentPath != null) + var str = $"Error while reading bundle file {reader.FileName}"; + if (originalPath != null) { - str += $" from {Path.GetFileName(parentPath)}"; + str += $" from {Path.GetFileName(originalPath)}"; } Logger.Error(str, e); } @@ -184,36 +183,36 @@ namespace AssetStudio } } - private void LoadWebFile(string fullName, EndianBinaryReader reader) + private void LoadWebFile(FileReader reader) { - var fileName = Path.GetFileName(fullName); - Logger.Info("Loading " + fileName); + Logger.Info("Loading " + reader.FileName); try { var webFile = new WebFile(reader); foreach (var file in webFile.fileList) { - var dummyPath = Path.Combine(Path.GetDirectoryName(fullName), file.fileName); - switch (CheckFileType(file.stream, out var fileReader)) + var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName); + var subReader = new FileReader(dummyPath, file.stream); + switch (subReader.FileType) { case FileType.AssetsFile: - LoadAssetsFromMemory(dummyPath, fileReader, fullName); + LoadAssetsFromMemory(subReader, reader.FullPath); break; case FileType.BundleFile: - LoadBundleFile(dummyPath, fileReader, fullName); + LoadBundleFile(subReader, reader.FullPath); break; case FileType.WebFile: - LoadWebFile(dummyPath, fileReader); + LoadWebFile(subReader); break; case FileType.ResourceFile: - resourceFileReaders[file.fileName] = fileReader; //TODO + resourceFileReaders[file.fileName] = subReader; //TODO break; } } } catch (Exception e) { - Logger.Error($"Error while reading web file {fileName}", e); + Logger.Error($"Error while reading web file {reader.FileName}", e); } finally { diff --git a/AssetStudio/BundleFile.cs b/AssetStudio/BundleFile.cs index 704ae57..1e6ac8f 100644 --- a/AssetStudio/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -41,7 +41,7 @@ namespace AssetStudio public StreamFile[] fileList; - public BundleFile(EndianBinaryReader reader, string path) + public BundleFile(FileReader reader) { m_Header = new Header(); m_Header.signature = reader.ReadStringToNull(); @@ -59,19 +59,19 @@ namespace AssetStudio goto case "UnityFS"; } ReadHeaderAndBlocksInfo(reader); - using (var blocksStream = CreateBlocksStream(path)) + using (var blocksStream = CreateBlocksStream(reader.FullPath)) { ReadBlocksAndDirectory(reader, blocksStream); - ReadFiles(blocksStream, path); + ReadFiles(blocksStream, reader.FullPath); } break; case "UnityFS": ReadHeader(reader); ReadBlocksInfoAndDirectory(reader); - using (var blocksStream = CreateBlocksStream(path)) + using (var blocksStream = CreateBlocksStream(reader.FullPath)) { ReadBlocks(reader, blocksStream); - ReadFiles(blocksStream, path); + ReadFiles(blocksStream, reader.FullPath); } break; } @@ -120,7 +120,7 @@ namespace AssetStudio var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize); if (uncompressedSizeSum >= int.MaxValue) { - /*var memoryMappedFile = MemoryMappedFile.CreateNew(Path.GetFileName(path), uncompressedSizeSum); + /*var memoryMappedFile = MemoryMappedFile.CreateNew(null, uncompressedSizeSum); assetsDataStream = memoryMappedFile.CreateViewStream();*/ blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose); } @@ -175,7 +175,7 @@ namespace AssetStudio file.fileName = Path.GetFileName(node.path); if (node.size >= int.MaxValue) { - /*var memoryMappedFile = MemoryMappedFile.CreateNew(file.fileName, entryinfo_size); + /*var memoryMappedFile = MemoryMappedFile.CreateNew(null, entryinfo_size); file.stream = memoryMappedFile.CreateViewStream();*/ var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar; Directory.CreateDirectory(extractPath); diff --git a/AssetStudio/FileReader.cs b/AssetStudio/FileReader.cs new file mode 100644 index 0000000..5b8f034 --- /dev/null +++ b/AssetStudio/FileReader.cs @@ -0,0 +1,98 @@ +using System.IO; +using System.Linq; + +namespace AssetStudio +{ + public class FileReader : EndianBinaryReader + { + public string FullPath; + public string FileName; + public FileType FileType; + + public FileReader(string path) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { } + + public FileReader(string path, Stream stream) : base(stream, EndianType.BigEndian) + { + FullPath = Path.GetFullPath(path); + FileName = Path.GetFileName(path); + CheckFileType(); + } + + private void CheckFileType() + { + var signature = this.ReadStringToNull(20); + Position = 0; + switch (signature) + { + case "UnityWeb": + case "UnityRaw": + case "UnityArchive": + case "UnityFS": + FileType = FileType.BundleFile; + break; + case "UnityWebData1.0": + FileType = FileType.WebFile; + break; + default: + var magic = ReadBytes(2); + Position = 0; + if (WebFile.gzipMagic.SequenceEqual(magic)) + { + FileType = FileType.WebFile; + } + Position = 0x20; + magic = ReadBytes(6); + Position = 0; + if (WebFile.brotliMagic.SequenceEqual(magic)) + { + FileType = FileType.WebFile; + } + if (IsSerializedFile()) + { + FileType = FileType.AssetsFile; + } + else + { + FileType = FileType.ResourceFile; + } + break; + } + } + + private bool IsSerializedFile() + { + var fileSize = BaseStream.Length; + if (fileSize < 20) + { + return false; + } + var m_MetadataSize = ReadUInt32(); + long m_FileSize = ReadUInt32(); + var m_Version = ReadUInt32(); + long m_DataOffset = ReadUInt32(); + var m_Endianess = ReadByte(); + var m_Reserved = ReadBytes(3); + if (m_Version >= 22) + { + if (fileSize < 48) + { + Position = 0; + return false; + } + m_MetadataSize = ReadUInt32(); + m_FileSize = ReadInt64(); + m_DataOffset = ReadInt64(); + } + Position = 0; + if (m_FileSize != fileSize) + { + return false; + } + if (m_DataOffset > fileSize) + { + return false; + } + return true; + } + } +} diff --git a/AssetStudio/FileType.cs b/AssetStudio/FileType.cs new file mode 100644 index 0000000..4b22f1f --- /dev/null +++ b/AssetStudio/FileType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AssetStudio +{ + public enum FileType + { + AssetsFile, + BundleFile, + WebFile, + ResourceFile + } +} diff --git a/AssetStudio/ImportHelper.cs b/AssetStudio/ImportHelper.cs index fea5d53..1a59063 100644 --- a/AssetStudio/ImportHelper.cs +++ b/AssetStudio/ImportHelper.cs @@ -4,14 +4,6 @@ using System.Linq; namespace AssetStudio { - public enum FileType - { - AssetsFile, - BundleFile, - WebFile, - ResourceFile - } - public static class ImportHelper { public static void MergeSplitAssets(string path, bool allDirectories = false) @@ -56,57 +48,5 @@ namespace AssetStudio } return selectFile.Distinct().ToArray(); } - - public static FileType CheckFileType(Stream stream, out EndianBinaryReader reader) - { - reader = new EndianBinaryReader(stream); - return CheckFileType(reader); - } - - public static FileType CheckFileType(string fileName, out EndianBinaryReader reader) - { - reader = new EndianBinaryReader(File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - return CheckFileType(reader); - } - - private static FileType CheckFileType(EndianBinaryReader reader) - { - var signature = reader.ReadStringToNull(20); - reader.Position = 0; - switch (signature) - { - case "UnityWeb": - case "UnityRaw": - case "UnityArchive": - case "UnityFS": - return FileType.BundleFile; - case "UnityWebData1.0": - return FileType.WebFile; - default: - { - var magic = reader.ReadBytes(2); - reader.Position = 0; - if (WebFile.gzipMagic.SequenceEqual(magic)) - { - return FileType.WebFile; - } - reader.Position = 0x20; - magic = reader.ReadBytes(6); - reader.Position = 0; - if (WebFile.brotliMagic.SequenceEqual(magic)) - { - return FileType.WebFile; - } - if (SerializedFile.IsSerializedFile(reader)) - { - return FileType.AssetsFile; - } - else - { - return FileType.ResourceFile; - } - } - } - } } } diff --git a/AssetStudio/SerializedFile.cs b/AssetStudio/SerializedFile.cs index 4b48d0a..d10210d 100644 --- a/AssetStudio/SerializedFile.cs +++ b/AssetStudio/SerializedFile.cs @@ -9,7 +9,7 @@ namespace AssetStudio public class SerializedFile { public AssetsManager assetsManager; - public EndianBinaryReader reader; + public FileReader reader; public string fullName; public string originalPath; public string fileName; @@ -31,12 +31,12 @@ namespace AssetStudio public List m_RefTypes; public string userInformation; - public SerializedFile(AssetsManager assetsManager, string fullName, EndianBinaryReader reader) + public SerializedFile(FileReader reader, AssetsManager assetsManager) { this.assetsManager = assetsManager; this.reader = reader; - this.fullName = fullName; - fileName = Path.GetFileName(fullName); + fullName = reader.FullPath; + fileName = reader.FileName; // ReadHeader header = new SerializedFileHeader(); @@ -377,42 +377,5 @@ namespace AssetStudio public bool IsVersionStripped => unityVersion == strippedVersion; private const string strippedVersion = "0.0.0"; - - public static bool IsSerializedFile(EndianBinaryReader reader) - { - var fileSize = reader.BaseStream.Length; - if (fileSize < 20) - { - return false; - } - var m_MetadataSize = reader.ReadUInt32(); - long m_FileSize = reader.ReadUInt32(); - var m_Version = reader.ReadUInt32(); - long m_DataOffset = reader.ReadUInt32(); - var m_Endianess = reader.ReadByte(); - var m_Reserved = reader.ReadBytes(3); - if (m_Version >= 22) - { - if (fileSize < 48) - { - return false; - } - m_MetadataSize = reader.ReadUInt32(); - m_FileSize = reader.ReadInt64(); - m_DataOffset = reader.ReadInt64(); - } - if (m_FileSize != fileSize) - { - reader.Position = 0; - return false; - } - if (m_DataOffset > fileSize) - { - reader.Position = 0; - return false; - } - reader.Position = 0; - return true; - } } } diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index 2651590..7243398 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -72,37 +72,37 @@ namespace AssetStudioGUI public static int ExtractFile(string fileName, string savePath) { int extractedCount = 0; - var type = ImportHelper.CheckFileType(fileName, out var reader); - if (type == FileType.BundleFile) - extractedCount += ExtractBundleFile(fileName, reader, savePath); - else if (type == FileType.WebFile) - extractedCount += ExtractWebDataFile(fileName, reader, savePath); + var reader = new FileReader(fileName); + if (reader.FileType == FileType.BundleFile) + extractedCount += ExtractBundleFile(reader, savePath); + else if (reader.FileType == FileType.WebFile) + extractedCount += ExtractWebDataFile(reader, savePath); else reader.Dispose(); return extractedCount; } - private static int ExtractBundleFile(string bundleFilePath, EndianBinaryReader reader, string savePath) + private static int ExtractBundleFile(FileReader reader, string savePath) { - StatusStripUpdate($"Decompressing {Path.GetFileName(bundleFilePath)} ..."); - var bundleFile = new BundleFile(reader, bundleFilePath); + StatusStripUpdate($"Decompressing {reader.FileName} ..."); + var bundleFile = new BundleFile(reader); reader.Dispose(); if (bundleFile.fileList.Length > 0) { - var extractPath = Path.Combine(savePath, Path.GetFileName(bundleFilePath) + "_unpacked"); + var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked"); return ExtractStreamFile(extractPath, bundleFile.fileList); } return 0; } - private static int ExtractWebDataFile(string webFilePath, EndianBinaryReader reader, string savePath) + private static int ExtractWebDataFile(FileReader reader, string savePath) { - StatusStripUpdate($"Decompressing {Path.GetFileName(webFilePath)} ..."); + StatusStripUpdate($"Decompressing {reader.FileName} ..."); var webFile = new WebFile(reader); reader.Dispose(); if (webFile.fileList.Length > 0) { - var extractPath = Path.Combine(savePath, Path.GetFileName(webFilePath) + "_unpacked"); + var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked"); return ExtractStreamFile(extractPath, webFile.fileList); } return 0;