Fixes & Cleanup

This commit is contained in:
VaDiM 2024-01-26 03:22:16 +03:00
parent 0d2aebc1f4
commit 2f6d9ec77f
10 changed files with 343 additions and 287 deletions

View File

@ -53,21 +53,21 @@ namespace AssetStudio
public void LoadFilesAndFolders(params string[] path) public void LoadFilesAndFolders(params string[] path)
{ {
List<string> pathList = new List<string>(); var pathList = new List<string>();
pathList.AddRange(path); pathList.AddRange(path);
LoadFilesAndFolders(out _, pathList); LoadFilesAndFolders(out _, pathList);
} }
public void LoadFilesAndFolders(out string parentPath, params string[] path) public void LoadFilesAndFolders(out string parentPath, params string[] path)
{ {
List<string> pathList = new List<string>(); var pathList = new List<string>();
pathList.AddRange(path); pathList.AddRange(path);
LoadFilesAndFolders(out parentPath, pathList); LoadFilesAndFolders(out parentPath, pathList);
} }
public void LoadFilesAndFolders(out string parentPath, List<string> pathList) public void LoadFilesAndFolders(out string parentPath, List<string> pathList)
{ {
List<string> fileList = new List<string>(); var fileList = new List<string>();
bool filesInPath = false; bool filesInPath = false;
parentPath = ""; parentPath = "";
foreach (var path in pathList) foreach (var path in pathList)
@ -113,9 +113,15 @@ namespace AssetStudio
//use a for loop because list size can change //use a for loop because list size can change
for (var i = 0; i < importFiles.Count; i++) for (var i = 0; i < importFiles.Count; i++)
{ {
LoadFile(importFiles[i]); if (LoadFile(importFiles[i]))
{
Progress.Report(i + 1, importFiles.Count); Progress.Report(i + 1, importFiles.Count);
} }
else
{
break;
}
}
importFiles.Clear(); importFiles.Clear();
importFilesHash.Clear(); importFilesHash.Clear();
@ -126,13 +132,13 @@ namespace AssetStudio
ProcessAssets(); ProcessAssets();
} }
private void LoadFile(string fullName) private bool LoadFile(string fullName)
{ {
var reader = new FileReader(fullName); var reader = new FileReader(fullName);
LoadFile(reader); return LoadFile(reader);
} }
private void LoadFile(FileReader reader) private bool LoadFile(FileReader reader)
{ {
switch (reader?.FileType) switch (reader?.FileType)
{ {
@ -140,8 +146,7 @@ namespace AssetStudio
LoadAssetsFile(reader); LoadAssetsFile(reader);
break; break;
case FileType.BundleFile: case FileType.BundleFile:
LoadBundleFile(reader); return LoadBundleFile(reader);
break;
case FileType.WebFile: case FileType.WebFile:
LoadWebFile(reader); LoadWebFile(reader);
break; break;
@ -154,7 +159,10 @@ namespace AssetStudio
case FileType.ZipFile: case FileType.ZipFile:
LoadZipFile(reader); LoadZipFile(reader);
break; break;
default:
return false;
} }
return true;
} }
private void LoadAssetsFile(FileReader reader) private void LoadAssetsFile(FileReader reader)
@ -248,7 +256,7 @@ namespace AssetStudio
Logger.Info($"Skipping {originalPath} ({reader.FileName})"); Logger.Info($"Skipping {originalPath} ({reader.FileName})");
} }
private void LoadBundleFile(FileReader reader, string originalPath = null) private bool LoadBundleFile(FileReader reader, string originalPath = null)
{ {
Logger.Info("Loading " + reader.FullPath); Logger.Info("Loading " + reader.FullPath);
try try
@ -267,10 +275,12 @@ namespace AssetStudio
resourceFileReaders.Add(file.fileName, subReader); resourceFileReaders.Add(file.fileName, subReader);
} }
} }
return true;
} }
catch (NotSupportedException e) catch (NotSupportedException e)
{ {
Logger.Error(e.Message); Logger.Error(e.Message);
return false;
} }
catch (Exception e) catch (Exception e)
{ {
@ -280,6 +290,7 @@ namespace AssetStudio
str += $" from {Path.GetFileName(originalPath)}"; str += $" from {Path.GetFileName(originalPath)}";
} }
Logger.Warning($"{str}\r\n{e}"); Logger.Warning($"{str}\r\n{e}");
return true;
} }
finally finally
{ {

View File

@ -4,7 +4,11 @@ namespace AssetStudio
{ {
public static class BigArrayPool<T> public static class BigArrayPool<T>
{ {
private static readonly ArrayPool<T> s_shared = ArrayPool<T>.Create(64 * 1024 * 1024, 3); public static ArrayPool<T> Shared { get; }
public static ArrayPool<T> Shared => s_shared;
static BigArrayPool()
{
Shared = ArrayPool<T>.Create(256 * 1024 * 1024, 5);
}
} }
} }

View File

@ -2,6 +2,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
namespace AssetStudio namespace AssetStudio
{ {
@ -100,16 +101,16 @@ namespace AssetStudio
case "UnityFS": case "UnityFS":
ReadHeader(reader); ReadHeader(reader);
bool isUnityCnEnc = false; var isUnityCnEnc = false;
string unityVer = string.IsNullOrEmpty(specUnityVer) ? m_Header.unityRevision : specUnityVer; var unityVerStr = string.IsNullOrEmpty(specUnityVer) ? m_Header.unityRevision : specUnityVer;
int[] ver = new string(unityVer.SkipWhile(x => !char.IsDigit(x)).TakeWhile(x => char.IsDigit(x) || x == '.').ToArray()).Split('.').Select(x => int.Parse(x)).ToArray(); int[] ver = Regex.Matches(unityVerStr, @"\d+").Cast<Match>().Select(x => int.Parse(x.Value)).ToArray();
if (ver[0] != 0) if (ver.Length > 0 && ver[0] != 0)
{ {
// https://issuetracker.unity3d.com/issues/files-within-assetbundles-do-not-start-on-aligned-boundaries-breaking-patching-on-nintendo-switch // https://issuetracker.unity3d.com/issues/files-within-assetbundles-do-not-start-on-aligned-boundaries-breaking-patching-on-nintendo-switch
if (ver[0] < 2020 || if (ver[0] < 2020
(ver[0] == 2020 && ver[1] <= 3 && ver[2] < 34) || || (ver[0] == 2020 && ver[1] <= 3 && ver[2] < 34)
(ver[0] == 2021 && ver[1] <= 3 && ver[2] < 2) || || (ver[0] == 2021 && ver[1] <= 3 && ver[2] < 2)
(ver[0] == 2022 && ver[1] <= 1 && ver[2] < 1)) || (ver[0] == 2022 && ver[1] <= 1 && ver[2] < 1))
{ {
isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.OldFlag) != 0; isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.OldFlag) != 0;
} }
@ -133,7 +134,7 @@ namespace AssetStudio
} }
} }
private void ReadHeaderAndBlocksInfo(EndianBinaryReader reader) private void ReadHeaderAndBlocksInfo(FileReader reader)
{ {
if (m_Header.version >= 4) if (m_Header.version >= 4)
{ {
@ -185,7 +186,7 @@ namespace AssetStudio
return blocksStream; return blocksStream;
} }
private void ReadBlocksAndDirectory(EndianBinaryReader reader, Stream blocksStream) private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream)
{ {
var isCompressed = m_Header.signature == "UnityWeb"; var isCompressed = m_Header.signature == "UnityWeb";
foreach (var blockInfo in m_BlocksInfo) foreach (var blockInfo in m_BlocksInfo)
@ -246,7 +247,7 @@ namespace AssetStudio
} }
} }
private void ReadHeader(EndianBinaryReader reader) private void ReadHeader(FileReader reader)
{ {
m_Header.size = reader.ReadInt64(); m_Header.size = reader.ReadInt64();
m_Header.compressedBlocksInfoSize = reader.ReadUInt32(); m_Header.compressedBlocksInfoSize = reader.ReadUInt32();
@ -258,7 +259,7 @@ namespace AssetStudio
} }
} }
private void ReadBlocksInfoAndDirectory(EndianBinaryReader reader, int[] unityVer) private void ReadBlocksInfoAndDirectory(FileReader reader, int[] unityVer)
{ {
byte[] blocksInfoBytes; byte[] blocksInfoBytes;
@ -290,24 +291,25 @@ namespace AssetStudio
{ {
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize); blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
} }
MemoryStream blocksInfoUncompresseddStream; MemoryStream blocksInfoUncompressedStream;
var uncompressedSize = m_Header.uncompressedBlocksInfoSize; var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask); var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
switch (compressionType) switch (compressionType)
{ {
case CompressionType.None: case CompressionType.None:
{ {
blocksInfoUncompresseddStream = new MemoryStream(blocksInfoBytes); blocksInfoUncompressedStream = new MemoryStream(blocksInfoBytes);
break; break;
} }
case CompressionType.Lzma: case CompressionType.Lzma:
{ {
blocksInfoUncompresseddStream = new MemoryStream((int)(uncompressedSize)); blocksInfoUncompressedStream = new MemoryStream((int) (uncompressedSize));
using (var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes)) using (var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes))
{ {
SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompresseddStream, m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize); SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompressedStream,
m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize);
} }
blocksInfoUncompresseddStream.Position = 0; blocksInfoUncompressedStream.Position = 0;
break; break;
} }
case CompressionType.Lz4: case CompressionType.Lz4:
@ -319,13 +321,14 @@ namespace AssetStudio
{ {
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
} }
blocksInfoUncompresseddStream = new MemoryStream(uncompressedBytes); blocksInfoUncompressedStream = new MemoryStream(uncompressedBytes);
break; break;
} }
default: default:
throw new IOException($"Unsupported compression type {compressionType}"); throw new IOException($"Unsupported compression type {compressionType}");
} }
using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompresseddStream))
using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompressedStream))
{ {
var uncompressedDataHash = blocksInfoReader.ReadBytes(16); var uncompressedDataHash = blocksInfoReader.ReadBytes(16);
var blocksInfoCount = blocksInfoReader.ReadInt32(); var blocksInfoCount = blocksInfoReader.ReadInt32();
@ -359,7 +362,7 @@ namespace AssetStudio
} }
} }
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream) private void ReadBlocks(FileReader reader, Stream blocksStream)
{ {
foreach (var blockInfo in m_BlocksInfo) foreach (var blockInfo in m_BlocksInfo)
{ {
@ -381,17 +384,23 @@ namespace AssetStudio
{ {
var compressedSize = (int)blockInfo.compressedSize; var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize); var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
reader.Read(compressedBytes, 0, compressedSize); _ = reader.Read(compressedBytes, 0, compressedSize);
var uncompressedSize = (int)blockInfo.uncompressedSize; var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize); var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
try
{
var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
if (numWrite != uncompressedSize) if (numWrite != uncompressedSize)
{ {
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
} }
blocksStream.Write(uncompressedBytes, 0, uncompressedSize); blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes); }
BigArrayPool<byte>.Shared.Return(uncompressedBytes); finally
{
BigArrayPool<byte>.Shared.Return(compressedBytes, clearArray: true);
BigArrayPool<byte>.Shared.Return(uncompressedBytes, clearArray: true);
}
break; break;
} }
default: default:

View File

@ -111,7 +111,6 @@ namespace AssetStudio
{ {
reader.AlignStream(); reader.AlignStream();
var m_MipmapLimitGroupName = reader.ReadAlignedString(); var m_MipmapLimitGroupName = reader.ReadAlignedString();
reader.AlignStream();
} }
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{ {

View File

@ -0,0 +1,22 @@
using System;
using System.Buffers.Binary;
namespace AssetStudio
{
public static class EndianSpanReader
{
public static uint SpanToUint32(Span<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start))
: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start));
}
public static long SpanToInt64(Span<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start))
: BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start));
}
}
}

View File

@ -40,7 +40,7 @@ namespace AssetStudio
if (encoding?.CodePage == 1200) //Unicode (UTF-16LE) if (encoding?.CodePage == 1200) //Unicode (UTF-16LE)
return reader.ReadUnicodeStringToNull(maxLength * 2); return reader.ReadUnicodeStringToNull(maxLength * 2);
var bytes = new List<byte>(); Span<byte> bytes = stackalloc byte[maxLength];
var count = 0; var count = 0;
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength) while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
{ {
@ -49,9 +49,10 @@ namespace AssetStudio
{ {
break; break;
} }
bytes.Add(b); bytes[count] = b;
count++; count++;
} }
bytes = bytes.Slice(0, count);
return encoding?.GetString(bytes.ToArray()) ?? Encoding.UTF8.GetString(bytes.ToArray()); return encoding?.GetString(bytes.ToArray()) ?? Encoding.UTF8.GetString(bytes.ToArray());
} }

View File

@ -1,5 +1,6 @@
using System.IO; using System;
using System.Linq; using System.IO;
using static AssetStudio.EndianSpanReader;
namespace AssetStudio namespace AssetStudio
{ {
@ -38,65 +39,68 @@ namespace AssetStudio
return FileType.WebFile; return FileType.WebFile;
default: default:
{ {
byte[] magic = ReadBytes(2); var buff = ReadBytes(40).AsSpan();
var magic = Span<byte>.Empty;
Position = 0; Position = 0;
if (gzipMagic.SequenceEqual(magic))
magic = buff.Length > 2 ? buff.Slice(0, 2) : magic;
if (magic.SequenceEqual(gzipMagic))
{ {
return FileType.GZipFile; return FileType.GZipFile;
} }
Position = 0x20;
magic = ReadBytes(6); magic = buff.Length > 38 ? buff.Slice(32, 6) : magic;
Position = 0; if (magic.SequenceEqual(brotliMagic))
if (brotliMagic.SequenceEqual(magic))
{ {
return FileType.BrotliFile; return FileType.BrotliFile;
} }
if (IsSerializedFile())
if (IsSerializedFile(buff))
{ {
return FileType.AssetsFile; return FileType.AssetsFile;
} }
magic = ReadBytes(4);
Position = 0; magic = buff.Length > 4 ? buff.Slice(0, 4): magic;
if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic)) if (magic.SequenceEqual(zipMagic) || magic.SequenceEqual(zipSpannedMagic))
{
return FileType.ZipFile; return FileType.ZipFile;
}
return FileType.ResourceFile; return FileType.ResourceFile;
} }
} }
} }
private bool IsSerializedFile() private bool IsSerializedFile(Span<byte> buff)
{ {
var fileSize = BaseStream.Length; var fileSize = BaseStream.Length;
if (fileSize < 20) if (fileSize < 20)
{ {
return false; return false;
} }
var m_MetadataSize = ReadUInt32(); var isBigEndian = Endian == EndianType.BigEndian;
long m_FileSize = ReadUInt32();
var m_Version = ReadUInt32(); //var m_MetadataSize = SpanToUint32(buff, 0, isBigEndian);
long m_DataOffset = ReadUInt32(); long m_FileSize = SpanToUint32(buff, 4, isBigEndian);
var m_Endianess = ReadByte(); var m_Version = SpanToUint32(buff, 8, isBigEndian);
var m_Reserved = ReadBytes(3); long m_DataOffset = SpanToUint32(buff, 12, isBigEndian);
//var m_Endianess = buff[16];
//var m_Reserved = buff.Slice(17, 3);
if (m_Version >= 22) if (m_Version >= 22)
{ {
if (fileSize < 48) if (fileSize < 48)
{ {
Position = 0;
return false; return false;
} }
m_MetadataSize = ReadUInt32(); //m_MetadataSize = SpanToUint32(buff, 20, isBigEndian);
m_FileSize = ReadInt64(); m_FileSize = SpanToInt64(buff, 24, isBigEndian);
m_DataOffset = ReadInt64(); m_DataOffset = SpanToInt64(buff, 32, isBigEndian);
} }
Position = 0; if (m_FileSize != fileSize || m_DataOffset > fileSize)
if (m_FileSize != fileSize)
{
return false;
}
if (m_DataOffset > fileSize)
{ {
return false; return false;
} }
return true; return true;
} }
} }

View File

@ -43,6 +43,6 @@ namespace AssetStudioGUI
public Bitmap Bitmap => m_bitmap; public Bitmap Bitmap => m_bitmap;
private Bitmap m_bitmap; private Bitmap m_bitmap;
private readonly GCHandle m_handle; private GCHandle m_handle;
} }
} }

View File

@ -12,7 +12,7 @@ namespace AssetStudio
private TextureFormat m_TextureFormat; private TextureFormat m_TextureFormat;
private int[] version; private int[] version;
private BuildTarget platform; private BuildTarget platform;
private int outPutSize; public int outPutSize;
public Texture2DConverter(Texture2D m_Texture2D) public Texture2DConverter(Texture2D m_Texture2D)
{ {
@ -33,6 +33,8 @@ namespace AssetStudio
} }
var flag = false; var flag = false;
var buff = BigArrayPool<byte>.Shared.Rent(reader.Size); var buff = BigArrayPool<byte>.Shared.Rent(reader.Size);
try
{
reader.GetData(buff); reader.GetData(buff);
switch (m_TextureFormat) switch (m_TextureFormat)
{ {
@ -209,7 +211,11 @@ namespace AssetStudio
flag = DecodeRGBA64(buff, bytes); flag = DecodeRGBA64(buff, bytes);
break; break;
} }
}
finally
{
BigArrayPool<byte>.Shared.Return(buff); BigArrayPool<byte>.Shared.Return(buff);
}
return flag; return flag;
} }

View File

@ -10,7 +10,7 @@ namespace AssetStudio
public static Image<Bgra32> ConvertToImage(this Texture2D m_Texture2D, bool flip) public static Image<Bgra32> ConvertToImage(this Texture2D m_Texture2D, bool flip)
{ {
var converter = new Texture2DConverter(m_Texture2D); var converter = new Texture2DConverter(m_Texture2D);
var buff = BigArrayPool<byte>.Shared.Rent(m_Texture2D.m_Width * m_Texture2D.m_Height * 4); var buff = BigArrayPool<byte>.Shared.Rent(converter.outPutSize);
try try
{ {
if (converter.DecodeTexture2D(buff)) if (converter.DecodeTexture2D(buff))
@ -26,7 +26,7 @@ namespace AssetStudio
} }
finally finally
{ {
BigArrayPool<byte>.Shared.Return(buff); BigArrayPool<byte>.Shared.Return(buff, clearArray: true);
} }
} }