mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-11-12 23:32:42 -05:00
Update bundle reader
- Replaced creation of a duplicated file/memory stream with OffsetStream. - Added separate processing of uncompressed bundles (including streamed bundles). They will be read directly from disk. - Added progress report on LZMA decompression process.
This commit is contained in:
@ -321,16 +321,18 @@ namespace AssetStudio
|
||||
Logger.Debug($"Bundle offset: {reader.Position}");
|
||||
var bundleStream = new OffsetStream(reader);
|
||||
var bundleReader = new FileReader(reader.FullPath, bundleStream);
|
||||
var isLoaded = false;
|
||||
|
||||
try
|
||||
{
|
||||
var bundleFile = new BundleFile(bundleReader, CustomBlockInfoCompression, CustomBlockCompression, specifiedUnityVersion);
|
||||
var isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath);
|
||||
isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath);
|
||||
if (!isLoaded)
|
||||
return false;
|
||||
|
||||
while (bundleFile.IsMultiBundle && isLoaded)
|
||||
while (bundleFile.IsDataAfterBundle && isLoaded)
|
||||
{
|
||||
isLoaded = false;
|
||||
bundleStream.Offset = reader.Position;
|
||||
bundleReader = new FileReader($"{reader.FullPath}_0x{bundleStream.Offset:X}", bundleStream);
|
||||
if (bundleReader.FileType != FileType.BundleFile)
|
||||
@ -345,7 +347,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);
|
||||
bundleFile = new BundleFile(bundleReader, CustomBlockInfoCompression, CustomBlockCompression, specifiedUnityVersion, isMultiBundle: true);
|
||||
isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath ?? reader.FullPath);
|
||||
}
|
||||
return isLoaded;
|
||||
@ -367,6 +369,7 @@ namespace AssetStudio
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!isLoaded)
|
||||
bundleReader.Dispose();
|
||||
}
|
||||
}
|
||||
@ -375,6 +378,9 @@ namespace AssetStudio
|
||||
{
|
||||
foreach (var file in bundleFile.fileList)
|
||||
{
|
||||
if (file.stream == null)
|
||||
continue;
|
||||
file.stream.Position = 0; //go to file offset
|
||||
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
|
||||
var subReader = new FileReader(dummyPath, file.stream);
|
||||
if (subReader.FileType == FileType.AssetsFile)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// LzmaDecoder.cs
|
||||
|
||||
using System;
|
||||
using AssetStudio;
|
||||
|
||||
namespace SevenZip.Compression.LZMA
|
||||
{
|
||||
@ -247,6 +248,8 @@ namespace SevenZip.Compression.LZMA
|
||||
m_OutWindow.PutByte(b);
|
||||
nowPos64++;
|
||||
}
|
||||
|
||||
Progress.Reset();
|
||||
while (nowPos64 < outSize64)
|
||||
{
|
||||
// UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64);
|
||||
@ -338,6 +341,8 @@ namespace SevenZip.Compression.LZMA
|
||||
}
|
||||
m_OutWindow.CopyBlock(rep0, len);
|
||||
nowPos64 += len;
|
||||
|
||||
Progress.Report((int)(nowPos64 * 100f / outSize64), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@ -42,7 +43,7 @@ namespace AssetStudio
|
||||
|
||||
public class BundleFile
|
||||
{
|
||||
public readonly bool IsMultiBundle;
|
||||
public readonly bool IsDataAfterBundle;
|
||||
|
||||
public class Header
|
||||
{
|
||||
@ -75,10 +76,11 @@ namespace AssetStudio
|
||||
private StorageBlock[] m_BlocksInfo;
|
||||
private Node[] m_DirectoryInfo;
|
||||
|
||||
public StreamFile[] fileList;
|
||||
public List<StreamFile> fileList;
|
||||
|
||||
public BundleFile(FileReader reader, CompressionType customBlockInfoCompression, CompressionType customBlockCompression, UnityVersion specUnityVer = null)
|
||||
public BundleFile(FileReader reader, CompressionType customBlockInfoCompression, CompressionType customBlockCompression, UnityVersion specUnityVer = null, bool isMultiBundle = false)
|
||||
{
|
||||
Stream blocksStream;
|
||||
m_Header = new Header();
|
||||
m_Header.signature = reader.ReadStringToNull();
|
||||
m_Header.version = reader.ReadUInt32();
|
||||
@ -98,11 +100,11 @@ namespace AssetStudio
|
||||
goto case "UnityFS";
|
||||
}
|
||||
ReadHeaderAndBlocksInfo(reader);
|
||||
using (var blocksStream = CreateBlocksStream(reader.FullPath))
|
||||
using (reader)
|
||||
{
|
||||
ReadBlocksAndDirectory(reader, blocksStream);
|
||||
ReadFiles(blocksStream, reader.FullPath);
|
||||
blocksStream = ReadBlocksAndDirectory(reader);
|
||||
}
|
||||
ReadFiles(blocksStream);
|
||||
break;
|
||||
case "UnityFS":
|
||||
ReadHeader(reader);
|
||||
@ -115,7 +117,7 @@ namespace AssetStudio
|
||||
}
|
||||
else if (streamSize - bundleSize > 200)
|
||||
{
|
||||
IsMultiBundle = true;
|
||||
IsDataAfterBundle = true;
|
||||
}
|
||||
|
||||
var unityVer = m_Header.unityRevision;
|
||||
@ -133,11 +135,19 @@ namespace AssetStudio
|
||||
UnityCnCheck(reader, customBlockInfoCompression, unityVer);
|
||||
|
||||
ReadBlocksInfoAndDirectory(reader, customBlockInfoCompression, unityVer);
|
||||
using (var blocksStream = CreateBlocksStream(reader.FullPath))
|
||||
|
||||
if (!isMultiBundle && IsUncompressedBundle)
|
||||
{
|
||||
ReadBlocks(reader, customBlockCompression, blocksStream);
|
||||
ReadFiles(blocksStream, reader.FullPath);
|
||||
ReadFiles(reader.BaseStream, reader.Position);
|
||||
break;
|
||||
}
|
||||
|
||||
blocksStream = ReadBlocks(reader, customBlockCompression);
|
||||
ReadFiles(blocksStream);
|
||||
|
||||
if (!IsDataAfterBundle)
|
||||
reader.Close();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -156,7 +166,7 @@ namespace AssetStudio
|
||||
m_BlocksInfo = new StorageBlock[1];
|
||||
for (int i = 0; i < levelCount; i++)
|
||||
{
|
||||
var storageBlock = new StorageBlock()
|
||||
var storageBlock = new StorageBlock
|
||||
{
|
||||
compressedSize = reader.ReadUInt32(),
|
||||
uncompressedSize = reader.ReadUInt32(),
|
||||
@ -179,23 +189,15 @@ namespace AssetStudio
|
||||
|
||||
private Stream CreateBlocksStream(string path)
|
||||
{
|
||||
Stream blocksStream;
|
||||
var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize);
|
||||
if (uncompressedSizeSum >= int.MaxValue)
|
||||
{
|
||||
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, uncompressedSizeSum);
|
||||
assetsDataStream = memoryMappedFile.CreateViewStream();*/
|
||||
blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
|
||||
}
|
||||
else
|
||||
{
|
||||
blocksStream = new MemoryStream((int)uncompressedSizeSum);
|
||||
}
|
||||
return blocksStream;
|
||||
return uncompressedSizeSum >= int.MaxValue
|
||||
? new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose)
|
||||
: (Stream) new MemoryStream((int)uncompressedSizeSum);
|
||||
}
|
||||
|
||||
private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream)
|
||||
private Stream ReadBlocksAndDirectory(FileReader reader)
|
||||
{
|
||||
var blocksStream = CreateBlocksStream(reader.FullPath);
|
||||
var isCompressed = m_Header.signature == "UnityWeb";
|
||||
foreach (var blockInfo in m_BlocksInfo)
|
||||
{
|
||||
@ -213,10 +215,11 @@ namespace AssetStudio
|
||||
blocksStream.Write(uncompressedBytes, 0, uncompressedBytes.Length);
|
||||
}
|
||||
blocksStream.Position = 0;
|
||||
|
||||
var blocksReader = new EndianBinaryReader(blocksStream);
|
||||
var nodesCount = blocksReader.ReadInt32();
|
||||
m_DirectoryInfo = new Node[nodesCount];
|
||||
for (int i = 0; i < nodesCount; i++)
|
||||
for (var i = 0; i < nodesCount; i++)
|
||||
{
|
||||
m_DirectoryInfo[i] = new Node
|
||||
{
|
||||
@ -225,33 +228,27 @@ namespace AssetStudio
|
||||
size = blocksReader.ReadUInt32()
|
||||
};
|
||||
}
|
||||
|
||||
return blocksStream;
|
||||
}
|
||||
|
||||
public void ReadFiles(Stream blocksStream, string path)
|
||||
private void ReadFiles(Stream inputStream, long blocksOffset = 0)
|
||||
{
|
||||
fileList = new StreamFile[m_DirectoryInfo.Length];
|
||||
for (int i = 0; i < m_DirectoryInfo.Length; i++)
|
||||
fileList = new List<StreamFile>(m_DirectoryInfo.Length);
|
||||
foreach (var node in m_DirectoryInfo)
|
||||
{
|
||||
var node = m_DirectoryInfo[i];
|
||||
var file = new StreamFile();
|
||||
fileList[i] = file;
|
||||
fileList.Add(file);
|
||||
file.path = node.path;
|
||||
file.fileName = Path.GetFileName(node.path);
|
||||
if (node.size >= int.MaxValue)
|
||||
try
|
||||
{
|
||||
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, entryinfo_size);
|
||||
file.stream = memoryMappedFile.CreateViewStream();*/
|
||||
var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar;
|
||||
Directory.CreateDirectory(extractPath);
|
||||
file.stream = new FileStream(extractPath + file.fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
file.stream = new OffsetStream(inputStream, node.offset + blocksOffset, node.size);
|
||||
}
|
||||
else
|
||||
catch (IOException e)
|
||||
{
|
||||
file.stream = new MemoryStream((int)node.size);
|
||||
Logger.Warning($"Failed to access {file.fileName} file.\n{e}");
|
||||
}
|
||||
blocksStream.Position = node.offset;
|
||||
blocksStream.CopyTo(file.stream, node.size);
|
||||
file.stream.Position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,7 +362,7 @@ namespace AssetStudio
|
||||
if (numWrite != uncompressedSize)
|
||||
{
|
||||
var msg = $"{compressionType} blockInfo decompression error. {errorMsg}\nWrite {numWrite} bytes but expected {uncompressedSize} bytes.";
|
||||
var exMsg = compressionType > CompressionType.Lz4HC
|
||||
var exMsg = compressionType > CompressionType.Lz4HC || customBlockInfoCompression != CompressionType.Auto
|
||||
? "Wrong compression type or blockInfo data might be encrypted."
|
||||
: "BlockInfo data might be encrypted.";
|
||||
throw new IOException($"{msg}\n{exMsg}\n");
|
||||
@ -376,7 +373,7 @@ namespace AssetStudio
|
||||
var uncompressedDataHash = blocksInfoReader.ReadBytes(16);
|
||||
var blocksInfoCount = blocksInfoReader.ReadInt32();
|
||||
m_BlocksInfo = new StorageBlock[blocksInfoCount];
|
||||
for (int i = 0; i < blocksInfoCount; i++)
|
||||
for (var i = 0; i < blocksInfoCount; i++)
|
||||
{
|
||||
m_BlocksInfo[i] = new StorageBlock
|
||||
{
|
||||
@ -388,7 +385,7 @@ namespace AssetStudio
|
||||
|
||||
var nodesCount = blocksInfoReader.ReadInt32();
|
||||
m_DirectoryInfo = new Node[nodesCount];
|
||||
for (int i = 0; i < nodesCount; i++)
|
||||
for (var i = 0; i < nodesCount; i++)
|
||||
{
|
||||
m_DirectoryInfo[i] = new Node
|
||||
{
|
||||
@ -405,30 +402,46 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadBlocks(FileReader reader, CompressionType customBlockCompression, Stream blocksStream)
|
||||
private Stream ReadBlocks(FileReader reader, CompressionType customBlockCompression)
|
||||
{
|
||||
Logger.Debug($"Block compression: {(CompressionType)m_BlocksInfo.Max(x => x.flags)}");
|
||||
var blocksStream = CreateBlocksStream(reader.FullPath);
|
||||
var blocksCompression = m_BlocksInfo.Max(x => (CompressionType)(x.flags & StorageBlockFlags.CompressionTypeMask));
|
||||
Logger.Debug($"BlockData compression: {blocksCompression}");
|
||||
Logger.Debug($"BlockData count: {m_BlocksInfo.Length}");
|
||||
|
||||
var showCustomTypeWarning = true;
|
||||
if (customBlockCompression == CompressionType.Auto)
|
||||
{
|
||||
if (blocksCompression > CompressionType.Lzham && Enum.IsDefined(typeof(CompressionType), blocksCompression))
|
||||
{
|
||||
Logger.Warning($"Non-standard block compression type: {(int)blocksCompression}. Trying to decompress as {blocksCompression} archive..");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info($"Custom block compression type: {customBlockCompression}");
|
||||
blocksCompression = customBlockCompression;
|
||||
}
|
||||
|
||||
byte[] sharedCompressedBuff = null;
|
||||
byte[] sharedUncompressedBuff = null;
|
||||
if (blocksCompression != CompressionType.Lzma && blocksCompression != CompressionType.Lzham)
|
||||
{
|
||||
var blockSize = (int)m_BlocksInfo.Max(x => x.uncompressedSize);
|
||||
Logger.Debug($"BlockSize: {blockSize}");
|
||||
|
||||
sharedCompressedBuff = BigArrayPool<byte>.Shared.Rent(blockSize);
|
||||
sharedUncompressedBuff = BigArrayPool<byte>.Shared.Rent(blockSize);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var blockInfo in m_BlocksInfo)
|
||||
{
|
||||
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
|
||||
if (customBlockCompression == CompressionType.Auto)
|
||||
{
|
||||
if (showCustomTypeWarning && compressionType > CompressionType.Lzham && Enum.IsDefined(typeof(CompressionType), compressionType))
|
||||
{
|
||||
Logger.Warning($"Non-standard block compression type: {(int)compressionType}. Trying to decompress as {compressionType} archive..");
|
||||
showCustomTypeWarning = false;
|
||||
}
|
||||
}
|
||||
else if (compressionType != CompressionType.None)
|
||||
|
||||
if (customBlockCompression != CompressionType.Auto && compressionType > 0)
|
||||
{
|
||||
compressionType = customBlockCompression;
|
||||
if (showCustomTypeWarning)
|
||||
{
|
||||
Logger.Info($"Custom block compression type: {customBlockCompression}");
|
||||
showCustomTypeWarning = false;
|
||||
}
|
||||
}
|
||||
|
||||
long numWrite;
|
||||
@ -436,43 +449,32 @@ namespace AssetStudio
|
||||
switch (compressionType)
|
||||
{
|
||||
case CompressionType.None:
|
||||
{
|
||||
reader.BaseStream.CopyTo(blocksStream, blockInfo.compressedSize);
|
||||
numWrite = blockInfo.compressedSize;
|
||||
break;
|
||||
}
|
||||
case CompressionType.Lzma:
|
||||
{
|
||||
Logger.Info("Decompressing LZMA stream...");
|
||||
numWrite = BundleDecompressionHelper.DecompressLzmaStream(reader.BaseStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize, ref errorMsg);
|
||||
break;
|
||||
}
|
||||
case CompressionType.Lz4:
|
||||
case CompressionType.Lz4HC:
|
||||
case CompressionType.Zstd:
|
||||
case CompressionType.Oodle:
|
||||
{
|
||||
var compressedSize = (int)blockInfo.compressedSize;
|
||||
var uncompressedSize = (int)blockInfo.uncompressedSize;
|
||||
|
||||
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
|
||||
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
|
||||
try
|
||||
{
|
||||
_ = reader.Read(compressedBytes, 0, compressedSize);
|
||||
var compressedSpan = new ReadOnlySpan<byte>(compressedBytes, 0, compressedSize);
|
||||
var uncompressedSpan = new Span<byte>(uncompressedBytes, 0, uncompressedSize);
|
||||
sharedCompressedBuff.AsSpan().Clear();
|
||||
sharedUncompressedBuff.AsSpan().Clear();
|
||||
|
||||
_ = reader.Read(sharedCompressedBuff, 0, compressedSize);
|
||||
var compressedSpan = new ReadOnlySpan<byte>(sharedCompressedBuff, 0, compressedSize);
|
||||
var uncompressedSpan = new Span<byte>(sharedUncompressedBuff, 0, uncompressedSize);
|
||||
|
||||
numWrite = BundleDecompressionHelper.DecompressBlock(compressionType, compressedSpan, uncompressedSpan, ref errorMsg);
|
||||
if (numWrite == uncompressedSize)
|
||||
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
BigArrayPool<byte>.Shared.Return(compressedBytes, clearArray: true);
|
||||
BigArrayPool<byte>.Shared.Return(uncompressedBytes, clearArray: true);
|
||||
}
|
||||
blocksStream.Write(sharedUncompressedBuff, 0, uncompressedSize);
|
||||
|
||||
break;
|
||||
}
|
||||
case CompressionType.Lzham:
|
||||
throw new IOException($"Unsupported block compression type: {compressionType}.\n");
|
||||
default:
|
||||
@ -482,13 +484,23 @@ namespace AssetStudio
|
||||
if (numWrite != blockInfo.uncompressedSize)
|
||||
{
|
||||
var msg = $"{compressionType} block decompression error. {errorMsg}\nWrite {numWrite} bytes but expected {blockInfo.uncompressedSize} bytes.";
|
||||
var exMsg = compressionType > CompressionType.Lz4HC
|
||||
var exMsg = compressionType > CompressionType.Lz4HC || customBlockCompression != CompressionType.Auto
|
||||
? "Wrong compression type or block data might be encrypted."
|
||||
: "Block data might be encrypted.";
|
||||
throw new IOException($"{msg}\n{exMsg}\n");
|
||||
}
|
||||
}
|
||||
blocksStream.Position = 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (sharedCompressedBuff != null)
|
||||
BigArrayPool<byte>.Shared.Return(sharedCompressedBuff, clearArray: true);
|
||||
|
||||
if (sharedUncompressedBuff != null)
|
||||
BigArrayPool<byte>.Shared.Return(sharedUncompressedBuff, clearArray: true);
|
||||
}
|
||||
|
||||
return blocksStream;
|
||||
}
|
||||
|
||||
private void UnityCnCheck(FileReader reader, CompressionType customBlockInfoCompression, UnityVersion unityVer)
|
||||
@ -525,5 +537,7 @@ namespace AssetStudio
|
||||
}
|
||||
throw new NotSupportedException("Unsupported bundle file. UnityCN encryption was detected.");
|
||||
}
|
||||
|
||||
private bool IsUncompressedBundle => m_BlocksInfo.All(x => (CompressionType)(x.flags & StorageBlockFlags.CompressionTypeMask) == CompressionType.None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,12 +5,15 @@ namespace AssetStudio
|
||||
public class OffsetStream : Stream
|
||||
{
|
||||
private readonly Stream _baseStream;
|
||||
private readonly long _length;
|
||||
private long _offset;
|
||||
|
||||
public override bool CanRead => _baseStream.CanRead;
|
||||
public override bool CanSeek => _baseStream.CanSeek;
|
||||
public override bool CanWrite => false;
|
||||
public override long Length => _baseStream.Length - _offset;
|
||||
public override long Length => _length > 0
|
||||
? _length
|
||||
: _baseStream.Length - _offset;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
@ -40,6 +43,13 @@ namespace AssetStudio
|
||||
Offset = reader.Position;
|
||||
}
|
||||
|
||||
public OffsetStream(Stream stream, long offset, long length)
|
||||
{
|
||||
_baseStream = stream;
|
||||
_length = length;
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
|
||||
@ -6,7 +6,7 @@ namespace AssetStudio
|
||||
{
|
||||
public class WebFile
|
||||
{
|
||||
public StreamFile[] fileList;
|
||||
public List<StreamFile> fileList;
|
||||
|
||||
private class WebData
|
||||
{
|
||||
@ -30,16 +30,15 @@ namespace AssetStudio
|
||||
data.path = Encoding.UTF8.GetString(reader.ReadBytes(pathLength));
|
||||
dataList.Add(data);
|
||||
}
|
||||
fileList = new StreamFile[dataList.Count];
|
||||
for (int i = 0; i < dataList.Count; i++)
|
||||
fileList = new List<StreamFile>(dataList.Count);
|
||||
foreach (var data in dataList)
|
||||
{
|
||||
var data = dataList[i];
|
||||
var file = new StreamFile();
|
||||
file.path = data.path;
|
||||
file.fileName = Path.GetFileName(data.path);
|
||||
reader.BaseStream.Position = data.dataOffset;
|
||||
file.stream = new MemoryStream(reader.ReadBytes(data.dataLength));
|
||||
fileList[i] = file;
|
||||
fileList.Add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,11 +96,11 @@ namespace AssetStudioCLI
|
||||
var bundleReader = new FileReader(reader.FullPath, bundleStream);
|
||||
var bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion);
|
||||
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
if (bundleFile.fileList.Length > 0)
|
||||
if (bundleFile.fileList.Count > 0)
|
||||
{
|
||||
count += ExtractStreamFile(extractPath, bundleFile.fileList);
|
||||
}
|
||||
while (bundleFile.IsMultiBundle)
|
||||
while (bundleFile.IsDataAfterBundle)
|
||||
{
|
||||
bundleStream.Offset = reader.Position;
|
||||
bundleReader = new FileReader($"{reader.FullPath}_0x{bundleStream.Offset:X}", bundleStream);
|
||||
@ -113,8 +113,8 @@ 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);
|
||||
if (bundleFile.fileList.Length > 0)
|
||||
bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion, isMultiBundle: true);
|
||||
if (bundleFile.fileList.Count > 0)
|
||||
{
|
||||
count += ExtractStreamFile(extractPath, bundleFile.fileList);
|
||||
}
|
||||
@ -128,19 +128,21 @@ namespace AssetStudioCLI
|
||||
Logger.Info($"Decompressing {reader.FileName} ...");
|
||||
var webFile = new WebFile(reader);
|
||||
reader.Dispose();
|
||||
if (webFile.fileList.Length > 0)
|
||||
if (webFile.fileList.Count > 0)
|
||||
{
|
||||
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
return ExtractStreamFile(extractPath, webFile.fileList);
|
||||
return ExtractStreamFile(extractPath, webFile.fileList, isOffsetStream: false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int ExtractStreamFile(string extractPath, StreamFile[] fileList)
|
||||
private static int ExtractStreamFile(string extractPath, List<StreamFile> fileList, bool isOffsetStream = true)
|
||||
{
|
||||
var extractedCount = 0;
|
||||
foreach (var file in fileList)
|
||||
{
|
||||
if (file.stream == null)
|
||||
continue;
|
||||
var filePath = Path.Combine(extractPath, file.path);
|
||||
var fileDirectory = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(fileDirectory))
|
||||
@ -151,11 +153,16 @@ namespace AssetStudioCLI
|
||||
{
|
||||
using (var fileStream = File.Create(filePath))
|
||||
{
|
||||
file.stream.CopyTo(fileStream);
|
||||
file.stream.Position = 0;
|
||||
file.stream.CopyTo(fileStream, file.stream.Length);
|
||||
}
|
||||
extractedCount += 1;
|
||||
extractedCount++;
|
||||
}
|
||||
file.stream.Dispose();
|
||||
if (!isOffsetStream) file.stream.Dispose();
|
||||
}
|
||||
if (isOffsetStream && fileList.Count > 0)
|
||||
{
|
||||
fileList[0].stream?.Dispose();
|
||||
}
|
||||
return extractedCount;
|
||||
}
|
||||
|
||||
@ -143,11 +143,11 @@ namespace AssetStudioGUI
|
||||
var bundleReader = new FileReader(reader.FullPath, bundleStream);
|
||||
var bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion);
|
||||
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
if (bundleFile.fileList.Length > 0)
|
||||
if (bundleFile.fileList.Count > 0)
|
||||
{
|
||||
count += ExtractStreamFile(extractPath, bundleFile.fileList);
|
||||
}
|
||||
while (bundleFile.IsMultiBundle)
|
||||
while (bundleFile.IsDataAfterBundle)
|
||||
{
|
||||
bundleStream.Offset = reader.Position;
|
||||
bundleReader = new FileReader($"{reader.FullPath}_0x{bundleStream.Offset:X}", bundleStream);
|
||||
@ -160,8 +160,8 @@ 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);
|
||||
if (bundleFile.fileList.Length > 0)
|
||||
bundleFile = new BundleFile(bundleReader, assetsManager.CustomBlockInfoCompression, assetsManager.CustomBlockCompression, assetsManager.SpecifyUnityVersion, isMultiBundle: true);
|
||||
if (bundleFile.fileList.Count > 0)
|
||||
{
|
||||
count += ExtractStreamFile(extractPath, bundleFile.fileList);
|
||||
}
|
||||
@ -175,19 +175,21 @@ namespace AssetStudioGUI
|
||||
Logger.Info($"Decompressing {reader.FileName} ...");
|
||||
var webFile = new WebFile(reader);
|
||||
reader.Dispose();
|
||||
if (webFile.fileList.Length > 0)
|
||||
if (webFile.fileList.Count > 0)
|
||||
{
|
||||
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
return ExtractStreamFile(extractPath, webFile.fileList);
|
||||
return ExtractStreamFile(extractPath, webFile.fileList, isOffsetStream: false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int ExtractStreamFile(string extractPath, StreamFile[] fileList)
|
||||
private static int ExtractStreamFile(string extractPath, List<StreamFile> fileList, bool isOffsetStream = true)
|
||||
{
|
||||
int extractedCount = 0;
|
||||
var extractedCount = 0;
|
||||
foreach (var file in fileList)
|
||||
{
|
||||
if (file.stream == null)
|
||||
continue;
|
||||
var filePath = Path.Combine(extractPath, file.path);
|
||||
var fileDirectory = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(fileDirectory))
|
||||
@ -198,11 +200,16 @@ namespace AssetStudioGUI
|
||||
{
|
||||
using (var fileStream = File.Create(filePath))
|
||||
{
|
||||
file.stream.CopyTo(fileStream);
|
||||
file.stream.Position = 0;
|
||||
file.stream.CopyTo(fileStream, file.stream.Length);
|
||||
}
|
||||
extractedCount += 1;
|
||||
extractedCount++;
|
||||
}
|
||||
file.stream.Dispose();
|
||||
if (!isOffsetStream) file.stream.Dispose();
|
||||
}
|
||||
if (isOffsetStream && fileList.Count > 0)
|
||||
{
|
||||
fileList[0].stream?.Dispose();
|
||||
}
|
||||
return extractedCount;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user