Skip to content

Commit 9ce618c

Browse files
committed
Some adjustments
1 parent 6fb3a8b commit 9ce618c

23 files changed

Lines changed: 303 additions & 207 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Only support Unity 2017.x or later.
1616
### BundleFile
1717

1818
- [x] Parse/Serialize
19-
- [ ] Patch
19+
- [x] Patch
2020
- [x] Calculate/Patch crc32
2121

2222
### SerializedFile
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Runtime.InteropServices;
2+
using UnityAsset.NET.Files;
3+
using UnityAsset.NET.Files.BundleFiles;
4+
using UnityAsset.NET.Files.SerializedFiles;
5+
using UnityAsset.NET.IO;
6+
7+
namespace UnityAsset.NET.Extensions;
8+
9+
public static class BundleFileExtensions
10+
{
11+
public static void ParseFilesWithTypeConversion(this BundleFile bf)
12+
{
13+
if (bf.Files == null) throw new NullReferenceException();
14+
var filesSpan = bf.Files.AsSpan();
15+
for (int i = 0; i < filesSpan.Length; i++)
16+
{
17+
ref var file = ref filesSpan[i];
18+
if (file is { File: DataBuffer db, CanBeSerializedFile: true })
19+
file = new FileWrapper(SerializedFile.Parse(db), file.Info);
20+
}
21+
}
22+
23+
public static List<Asset> Assets(this BundleFile bf) =>
24+
bf.Files
25+
.Where(file => file.File is SerializedFile)
26+
.SelectMany(file => ((SerializedFile)file.File).Assets)
27+
.ToList();
28+
29+
public static void PatchCrc32(this BundleFile bf, uint newCrc32)
30+
{
31+
if (bf.Crc32 != newCrc32)
32+
{
33+
var patchBytes = CRC32.rCRC(newCrc32, bf.Crc32);
34+
bf.Files.Add(new FileWrapper(new DataBuffer(patchBytes), new FileEntry(0, 4, 0, "crc32-patch-data")));
35+
}
36+
}
37+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace UnityAsset.NET.Extensions;
4+
5+
public static class ListExtensions
6+
{
7+
public static Span<T> AsSpan<T>(this List<T> list)
8+
{
9+
return CollectionsMarshal.AsSpan(list);
10+
}
11+
12+
public static ReadOnlySpan<T> AsReadOnlySpan<T>(this List<T> list)
13+
{
14+
return list.AsSpan();
15+
}
16+
}

UnityAsset.NET/Files/BundleFileExtension.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

UnityAsset.NET/Files/BundleFiles/BlocksAndDirectoryInfo.cs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Text;
1+
using System.Runtime.InteropServices;
2+
using System.Text;
3+
using UnityAsset.NET.Extensions;
24
using UnityAsset.NET.IO;
35

46
namespace UnityAsset.NET.Files.BundleFiles;
@@ -24,10 +26,13 @@ public BlocksAndDirectoryInfo(byte[] uncompressedDataHash,
2426
db.ReadList(db.ReadInt32(), FileEntry.Parse)
2527
);
2628

27-
public void Serialize(DataBuffer db) {
28-
db.WriteBytes(UncompressedDataHash);
29-
db.WriteListWithCount(BlocksInfo, (DataBuffer d, StorageBlockInfo info) => info.Serialize(d));
30-
db.WriteListWithCount(DirectoryInfo, (DataBuffer d, FileEntry info) => info.Serialize(d));
29+
public int Serialize(DataBuffer db)
30+
{
31+
int size = 0;
32+
size += db.WriteBytes(UncompressedDataHash);
33+
size += db.WriteListWithCount(BlocksInfo, (d, info) => info.Serialize(d));
34+
size += db.WriteListWithCount(DirectoryInfo, (d, info) => info.Serialize(d));
35+
return size;
3136
}
3237

3338
public long SerializeSize => UncompressedDataHash.Length + 8 +
@@ -38,12 +43,12 @@ public override string ToString()
3843
{
3944
StringBuilder sb = new StringBuilder();
4045
sb.AppendLine($"UncompressedDataHash: {BitConverter.ToString(UncompressedDataHash)}");
41-
for (int i = 0; i < BlocksInfo.Count; ++i)
42-
sb.Append($"Block {i}: {BlocksInfo[i]}");
43-
sb.AppendLine();
44-
for (int i = 0; i < DirectoryInfo.Count; ++i)
45-
sb.Append($"Directory {i}: {DirectoryInfo[i]}");
46-
sb.AppendLine();
46+
var blockInfoSpan = BlocksInfo.AsSpan();
47+
for (int i = 0; i < blockInfoSpan.Length; ++i)
48+
sb.AppendLine($"Block {i}: {blockInfoSpan[i]}");
49+
var directoryInfoSpan = DirectoryInfo.AsSpan();
50+
for (int i = 0; i < directoryInfoSpan.Length; ++i)
51+
sb.AppendLine($"Directory {i}: {directoryInfoSpan[i]}");
4752
return sb.ToString();
4853
}
4954
}

UnityAsset.NET/Files/BundleFiles/BundleFile.cs

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System.Text.RegularExpressions;
1+
using System.Text;
2+
using System.Text.RegularExpressions;
23
using UnityAsset.NET.Enums;
4+
using UnityAsset.NET.Extensions;
35
using UnityAsset.NET.Files.BundleFiles;
46
using UnityAsset.NET.Files.SerializedFiles;
57
using UnityAsset.NET.IO;
@@ -77,16 +79,17 @@ public static (List<FileWrapper>, uint) ParseFiles(DataBuffer db, BlocksAndDirec
7779
{
7880
DataBuffer blocksBuffer = new DataBuffer(dataInfo.BlocksInfo.Sum(block => (int)block.UncompressedSize));
7981
if (unityCnInfo == null)
80-
foreach (var blockInfo in dataInfo.BlocksInfo)
82+
foreach (var blockInfo in dataInfo.BlocksInfo.AsReadOnlySpan())
8183
Compression.DecompressToBytes(
8284
db.ReadSpanBytes((int)blockInfo.CompressedSize),
8385
blocksBuffer.ReadSpanBytes((int)blockInfo.UncompressedSize),
8486
(CompressionType)(blockInfo.Flags & StorageBlockFlags.CompressionTypeMask));
8587
else
8688
{
87-
for (int i = 0; i < dataInfo.BlocksInfo.Count; i++)
89+
var blocksInfoSpan = dataInfo.BlocksInfo.AsReadOnlySpan();
90+
for (int i = 0; i < blocksInfoSpan.Length; i++)
8891
{
89-
var blockInfo = dataInfo.BlocksInfo[i];
92+
var blockInfo = blocksInfoSpan[i];
9093
var compressionType = (CompressionType)(blockInfo.Flags & StorageBlockFlags.CompressionTypeMask);
9194
if (compressionType == CompressionType.Lz4 || compressionType == CompressionType.Lz4HC)
9295
unityCnInfo.DecryptAndDecompress(
@@ -102,19 +105,8 @@ public static (List<FileWrapper>, uint) ParseFiles(DataBuffer db, BlocksAndDirec
102105
var crc32 = CRC32.CalculateCRC32(blocksBuffer.AsSpan());
103106

104107
var files = new List<FileWrapper>();
105-
foreach (var dir in dataInfo.DirectoryInfo)
106-
{
107-
if (dir.Path.StartsWith("CAB-") && !dir.Path.EndsWith(".resS"))
108-
{
109-
var cabBuffer = blocksBuffer.SliceBuffer((int)dir.Size);
110-
files.Add(new FileWrapper(SerializedFile.Parse(cabBuffer), dir));
111-
blocksBuffer.Advance((int)dir.Size);
112-
}
113-
else
114-
{
115-
files.Add(new FileWrapper(new DataBuffer(blocksBuffer.ReadSpanBytes((int)dir.Size).ToArray()), dir));
116-
}
117-
}
108+
foreach (var dir in dataInfo.DirectoryInfo.AsReadOnlySpan())
109+
files.Add(new FileWrapper(new DataBuffer(blocksBuffer.ReadSpanBytes((int)dir.Size).ToArray()), dir));
118110

119111
return (files, crc32);
120112
}
@@ -137,9 +129,18 @@ public BundleFile(DataBuffer db, string? key = null)
137129
(Files, Crc32) = ParseFiles(db, DataInfo, UnityCnInfo);
138130
}
139131

132+
/// <summary>
133+
/// Parses only the BundleFile container structure without further parsing the contained SerializedFile format.
134+
/// </summary>
135+
/// <remarks>
136+
/// This method stops at the BundleFile level and does not process the internal SerializedFile structures.
137+
/// If you need full parsing including SerializedFile format conversion, use <see cref="BundleFileExtensions.ParseFilesWithTypeConversion"/> instead.
138+
/// </remarks>
139+
/// <param name="path">The file path to the BundleFile</param>
140+
/// <param name="key">Optional decryption key</param>
140141
public BundleFile(string path, string? key = null) : this(DataBuffer.FromFile(path), key) {}
141142

142-
public void Serialize(DataBuffer db, CompressionType infoPacker = CompressionType.None, CompressionType dataPacker = CompressionType.None, string? unityCnKey = null)
143+
public int Serialize(DataBuffer db, CompressionType infoPacker = CompressionType.None, CompressionType dataPacker = CompressionType.None, string? unityCnKey = null)
143144
{
144145
if (Header == null || DataInfo == null || Files == null)
145146
throw new NullReferenceException("BundleFile has not read completely");
@@ -152,20 +153,17 @@ public void Serialize(DataBuffer db, CompressionType infoPacker = CompressionTyp
152153
});
153154
DataBuffer filesBuffer = new DataBuffer((int)filesCapacity);
154155
Int64 offset = 0;
155-
foreach (var file in Files)
156+
foreach (var file in Files.AsReadOnlySpan())
156157
{
157-
Int64 cabSize;
158+
int cabSize;
158159
switch (file.File)
159160
{
160161
case DataBuffer dataBuffer:
161-
filesBuffer.WriteBytes(dataBuffer.AsSpan());
162-
cabSize = dataBuffer.Length;
162+
cabSize = filesBuffer.WriteBytes(dataBuffer.AsSpan());
163163
break;
164164
case SerializedFile sf:
165-
var subBuffer = filesBuffer.SliceBufferToEnd();
166-
sf.Serialize(subBuffer);
167-
filesBuffer.Advance(subBuffer.Position);
168-
cabSize = subBuffer.Position;
165+
cabSize = sf.Serialize(filesBuffer.SliceBufferToEnd());
166+
filesBuffer.Advance(cabSize);
169167
break;
170168
default:
171169
throw new Exception($"Unexpected type: {file.File.GetType().Name}");
@@ -199,9 +197,10 @@ public void Serialize(DataBuffer db, CompressionType infoPacker = CompressionTyp
199197
unityCnInfo = new UnityCN(unityCnKey);
200198
if (dataPacker == CompressionType.Lz4 || dataPacker == CompressionType.Lz4HC)
201199
{
202-
for (int i = 0; i < blocksInfo.Count; i++)
200+
var blocksInfoSpan = blocksInfo.AsSpan();
201+
for (int i = 0; i < blocksInfoSpan.Length; i++)
203202
{
204-
var blockInfo = blocksInfo[i];
203+
var blockInfo = blocksInfoSpan[i];
205204
blockInfo.Flags |= (StorageBlockFlags)0x100;
206205
unityCnInfo.EncryptBlock(compressedBlocksDataBuffer.ReadSpanBytes((int)blockInfo.CompressedSize), (int)blockInfo.CompressedSize, i);
207206
}
@@ -232,35 +231,46 @@ public void Serialize(DataBuffer db, CompressionType infoPacker = CompressionTyp
232231
0, (uint)compressedBlocksInfoSize, (uint)uncompressedBlocksInfoSize,
233232
(Header.Flags & ~ArchiveFlags.CompressionTypeMask) | (ArchiveFlags)infoPacker);
234233
db.EnsureCapacity((int)(header.SerializeSize + 16 + compressedBlocksInfoSize + compressedBlocksDataBuffer.Length + unityCnInfo?.SerializeSize ?? 0));
235-
header.Serialize(db);
234+
int size = 0;
235+
size += header.Serialize(db);
236236
if (unityCnInfo != null)
237-
unityCnInfo.Serialize(db);
237+
size += unityCnInfo.Serialize(db);
238238
if (header.Version >= 7)
239-
db.Align(16);
239+
size += db.Align(16);
240240
else // temp fix for 2019.4.x
241-
{
242241
if (version[0] == 2019 && version[1] == 4 && version[2] >= 30)
243-
db.Align(16);
244-
}
242+
size += db.Align(16);
245243
if ((header.Flags & ArchiveFlags.BlocksInfoAtTheEnd) == 0) //0x40 BlocksAndDirectoryInfoCombined
246-
db.WriteBytes(compressedDataInfoBuffer.SliceForward((int)compressedBlocksInfoSize));
244+
size += db.WriteBytes(compressedDataInfoBuffer.SliceForward((int)compressedBlocksInfoSize));
247245
if (!VersionJudge1(version) &&(header.Flags & ArchiveFlags.BlockInfoNeedPaddingAtStart) != 0)
248-
db.Align(16);
249-
db.WriteBytes(compressedBlocksDataBuffer.SliceForward(compressedBlocksDataBuffer.Length));
246+
size += db.Align(16);
247+
size += db.WriteBytes(compressedBlocksDataBuffer.SliceForward(compressedBlocksDataBuffer.Length));
250248
if ((header.Flags & ArchiveFlags.BlocksInfoAtTheEnd) != 0) //kArchiveBlocksInfoAtTheEnd
251-
db.WriteBytes(compressedDataInfoBuffer.SliceForward((int)compressedBlocksInfoSize));
252-
var size = db.Position;
249+
size += db.WriteBytes(compressedDataInfoBuffer.SliceForward((int)compressedBlocksInfoSize));
253250
header.Size = size;
254251
db.Seek(0);
255252
header.Serialize(db);
253+
return size;
256254
}
257255

258-
public void Serialize(string path, CompressionType infoPacker = CompressionType.None,
256+
public int Serialize(string path, CompressionType infoPacker = CompressionType.None,
259257
CompressionType dataPacker = CompressionType.None, string? unityCnKey = null)
260258
{
261259
DataBuffer db = new DataBuffer(0);
262-
Serialize(db, infoPacker, dataPacker, unityCnKey);
263-
db.WriteToFile(path);
260+
int size = Serialize(db, infoPacker, dataPacker, unityCnKey);
261+
db.WriteToFile(path, size);
262+
return size;
263+
}
264+
265+
public override string ToString()
266+
{
267+
StringBuilder sb = new StringBuilder();
268+
sb.AppendLine($"Header: {Header}");
269+
sb.AppendLine($"DataInfo: {DataInfo}");
270+
var filesSpan = Files.AsSpan();
271+
for (int i = 0; i < filesSpan.Length; ++i)
272+
sb.AppendLine($"File {i}: {filesSpan[i]}");
273+
return sb.ToString();
264274
}
265275

266276
public static int[] ParseVersion(Header header)

UnityAsset.NET/Files/BundleFiles/FileEntry.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ public FileEntry(Int64 offset, Int64 size, UInt32 flags, string path)
2525
db.ReadNullTerminatedString()
2626
);
2727

28-
public void Serialize(DataBuffer db) {
29-
db.WriteInt64(Offset);
30-
db.WriteInt64(Size);
31-
db.WriteUInt32(Flags);
32-
db.WriteNullTerminatedString(Path);
28+
public int Serialize(DataBuffer db)
29+
{
30+
int size = 0;
31+
size += db.WriteInt64(Offset);
32+
size += db.WriteInt64(Size);
33+
size += db.WriteUInt32(Flags);
34+
size += db.WriteNullTerminatedString(Path);
35+
return size;
3336
}
3437

3538
public long SerializeSize => 21 + Path.Length;

UnityAsset.NET/Files/BundleFiles/Header.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,18 @@ public Header(string signature, UInt32 version, string unityVersion, string unit
4040
(ArchiveFlags)db.ReadUInt32()
4141
);
4242

43-
public void Serialize(DataBuffer db)
43+
public int Serialize(DataBuffer db)
4444
{
45-
db.WriteNullTerminatedString(Signature);
46-
db.WriteUInt32(Version);
47-
db.WriteNullTerminatedString(UnityVersion);
48-
db.WriteNullTerminatedString(UnityRevision);
49-
db.WriteInt64(Size);
50-
db.WriteUInt32(CompressedBlocksInfoSize);
51-
db.WriteUInt32(UncompressedBlocksInfoSize);
52-
db.WriteUInt32((UInt32)Flags);
45+
int size = 0;
46+
size += db.WriteNullTerminatedString(Signature);
47+
size += db.WriteUInt32(Version);
48+
size += db.WriteNullTerminatedString(UnityVersion);
49+
size += db.WriteNullTerminatedString(UnityRevision);
50+
size += db.WriteInt64(Size);
51+
size += db.WriteUInt32(CompressedBlocksInfoSize);
52+
size += db.WriteUInt32(UncompressedBlocksInfoSize);
53+
size += db.WriteUInt32((UInt32)Flags);
54+
return size;
5355
}
5456

5557
public long SerializeSize => 27 + Signature.Length + UnityVersion.Length + UnityRevision.Length;

UnityAsset.NET/Files/BundleFiles/StorageBlockInfo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ public StorageBlockInfo(UInt32 uncompressedSize, UInt32 compressedSize, StorageB
2323
(StorageBlockFlags)db.ReadUInt16()
2424
);
2525

26-
public void Serialize(DataBuffer db) {
26+
public int Serialize(DataBuffer db) {
2727
db.WriteUInt32(UncompressedSize);
2828
db.WriteUInt32(CompressedSize);
2929
db.WriteUInt16((UInt16)Flags);
30+
return 10;
3031
}
3132

3233
public long SerializeSize => 10;

UnityAsset.NET/Files/BundleFiles/UnityCN.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public void Reset()
119119
}
120120
}
121121

122-
public void Serialize(DataBuffer db)
122+
public int Serialize(DataBuffer db)
123123
{
124124
db.WriteUInt32(Value);
125125
db.WriteBytes(InfoBytes);
@@ -128,6 +128,7 @@ public void Serialize(DataBuffer db)
128128
db.WriteBytes(SignatureBytes);
129129
db.WriteBytes(SignatureKey);
130130
db.WriteByte(0);
131+
return 70;
131132
}
132133

133134
public long SerializeSize => 70;

0 commit comments

Comments
 (0)