Skip to content

Commit bc0e32e

Browse files
committed
Add multiBundle support
including fake headers
1 parent db4eb30 commit bc0e32e

File tree

8 files changed

+317
-69
lines changed

8 files changed

+317
-69
lines changed

AssetStudio/AssetsManager.cs

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ private bool LoadAssetsFile(FileReader reader)
201201
{
202202
if (!assetsFileListHash.Contains(reader.FileName))
203203
{
204-
Logger.Info($"Loading {reader.FullPath}");
204+
Logger.Info($"Loading \"{reader.FullPath}\"");
205205
try
206206
{
207207
var assetsFile = new SerializedFile(reader, this);
@@ -248,13 +248,13 @@ private bool LoadAssetsFile(FileReader reader)
248248
}
249249
catch (Exception e)
250250
{
251-
Logger.Warning($"Failed to read assets file {reader.FullPath}\r\n{e}");
251+
Logger.Warning($"Failed to read assets file \"{reader.FullPath}\"\n{e}");
252252
reader.Dispose();
253253
}
254254
}
255255
else
256256
{
257-
Logger.Info($"Skipping {reader.FullPath}");
257+
Logger.Info($"Skipping \"{reader.FullPath}\"");
258258
reader.Dispose();
259259
}
260260
return true;
@@ -284,38 +284,46 @@ private bool LoadAssetsFromMemory(FileReader reader, string originalPath, UnityV
284284
}
285285
catch (Exception e)
286286
{
287-
Logger.Warning($"Failed to read assets file {reader.FullPath} from {Path.GetFileName(originalPath)}\r\n{e}");
287+
Logger.Warning($"Failed to read assets file \"{reader.FullPath}\" from {Path.GetFileName(originalPath)}\n{e}");
288288
resourceFileReaders.TryAdd(reader.FileName, reader);
289289
}
290290
}
291291
else
292292
{
293-
Logger.Info($"Skipping {originalPath} ({reader.FileName})");
293+
Logger.Info($"Skipping \"{originalPath}\" ({reader.FileName})");
294294
}
295295
return true;
296296
}
297297

298298
private bool LoadBundleFile(FileReader reader, string originalPath = null)
299299
{
300-
Logger.Info("Loading " + reader.FullPath);
300+
Logger.Info($"Loading \"{reader.FullPath}\"");
301+
Logger.Debug($"Bundle offset: {reader.Position}");
302+
var bundleStream = new OffsetStream(reader);
303+
var bundleReader = new FileReader(reader.FullPath, bundleStream);
304+
301305
try
302306
{
303-
var bundleFile = new BundleFile(reader, ZstdEnabled, specifiedUnityVersion);
304-
foreach (var file in bundleFile.fileList)
307+
var bundleFile = new BundleFile(bundleReader, ZstdEnabled, specifiedUnityVersion);
308+
var isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath);
309+
if (!isLoaded)
310+
return false;
311+
312+
while (bundleFile.IsMultiBundle && isLoaded)
305313
{
306-
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
307-
var subReader = new FileReader(dummyPath, file.stream);
308-
if (subReader.FileType == FileType.AssetsFile)
309-
{
310-
if (!LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision))
311-
return false;
312-
}
313-
else
314+
bundleStream.Offset = reader.Position;
315+
bundleReader = new FileReader($"{reader.FullPath}_0x{bundleStream.Offset:X}", bundleStream);
316+
if (bundleReader.Position > 0)
314317
{
315-
resourceFileReaders.TryAdd(file.fileName, subReader);
318+
bundleStream.Offset += bundleReader.Position;
319+
bundleReader.FullPath = $"{reader.FullPath}_0x{bundleStream.Offset:X}";
320+
bundleReader.FileName = $"{reader.FileName}_0x{bundleStream.Offset:X}";
316321
}
322+
Logger.Info($"[MultiBundle] Loading \"{reader.FileName}\" from offset: 0x{bundleStream.Offset:X}");
323+
bundleFile = new BundleFile(bundleReader, ZstdEnabled, specifiedUnityVersion);
324+
isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath ?? reader.FullPath);
317325
}
318-
return true;
326+
return isLoaded;
319327
}
320328
catch (NotSupportedException e)
321329
{
@@ -324,23 +332,42 @@ private bool LoadBundleFile(FileReader reader, string originalPath = null)
324332
}
325333
catch (Exception e)
326334
{
327-
var str = $"Error while reading bundle file {reader.FullPath}";
335+
var str = $"Error while reading bundle file \"{bundleReader.FullPath}\"";
328336
if (originalPath != null)
329337
{
330338
str += $" from {Path.GetFileName(originalPath)}";
331339
}
332-
Logger.Warning($"{str}\r\n{e}");
340+
Logger.Warning($"{str}\n{e}");
333341
return true;
334342
}
335343
finally
336344
{
337-
reader.Dispose();
345+
bundleReader.Dispose();
338346
}
339347
}
340348

349+
private bool LoadBundleFiles(FileReader reader, BundleFile bundleFile, string originalPath = null)
350+
{
351+
foreach (var file in bundleFile.fileList)
352+
{
353+
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
354+
var subReader = new FileReader(dummyPath, file.stream);
355+
if (subReader.FileType == FileType.AssetsFile)
356+
{
357+
if (!LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision))
358+
return false;
359+
}
360+
else
361+
{
362+
resourceFileReaders.TryAdd(file.fileName, subReader);
363+
}
364+
}
365+
return true;
366+
}
367+
341368
private void LoadWebFile(FileReader reader)
342369
{
343-
Logger.Info("Loading " + reader.FullPath);
370+
Logger.Info($"Loading \"{reader.FullPath}\"");
344371
try
345372
{
346373
var webFile = new WebFile(reader);
@@ -367,7 +394,7 @@ private void LoadWebFile(FileReader reader)
367394
}
368395
catch (Exception e)
369396
{
370-
Logger.Error($"Error while reading web file {reader.FullPath}", e);
397+
Logger.Error($"Error while reading web file \"{reader.FullPath}\"", e);
371398
}
372399
finally
373400
{
@@ -427,7 +454,7 @@ private void LoadZipFile(FileReader reader)
427454
}
428455
catch (Exception e)
429456
{
430-
Logger.Warning($"Error while reading zip split file {basePath}\r\n{e}");
457+
Logger.Warning($"Error while reading zip split file \"{basePath}\"\n{e}");
431458
}
432459
}
433460

@@ -461,7 +488,7 @@ private void LoadZipFile(FileReader reader)
461488
}
462489
catch (Exception e)
463490
{
464-
Logger.Warning($"Error while reading zip entry {entry.FullName}\r\n{e}");
491+
Logger.Warning($"Error while reading zip entry \"{entry.FullName}\"\n{e}");
465492
}
466493
}
467494
}

AssetStudio/BundleFile.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public enum CompressionType
4242

4343
public class BundleFile
4444
{
45+
public readonly bool IsMultiBundle;
46+
4547
public class Header
4648
{
4749
public string signature;
@@ -102,6 +104,17 @@ public BundleFile(FileReader reader, bool useZstd, UnityVersion specUnityVer = n
102104
case "UnityFS":
103105
ReadHeader(reader);
104106

107+
var bundleSize = m_Header.size;
108+
var streamSize = reader.BaseStream.Length;
109+
if (bundleSize > streamSize)
110+
{
111+
Logger.Warning("Bundle size is incorrect.");
112+
}
113+
else if (streamSize - bundleSize > 200)
114+
{
115+
IsMultiBundle = true;
116+
}
117+
105118
var isUnityCnEnc = false;
106119
var unityVer = m_Header.unityRevision;
107120
if (specUnityVer != null)

AssetStudio/CubismMoc.cs

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4-
using System.Text;
5-
using static AssetStudio.EndianSpanReader;
64

75
namespace AssetStudio
86
{
@@ -59,24 +57,25 @@ public CubismMoc(MonoBehaviour moc)
5957
}
6058
isBigEndian = BitConverter.ToBoolean(modelData, 5);
6159

60+
var modelDataSpan = modelData.AsSpan();
6261
//offsets
63-
var countInfoTableOffset = (int)SpanToUint32(modelData, 64, isBigEndian);
64-
var canvasInfoOffset = (int)SpanToUint32(modelData, 68, isBigEndian);
65-
var partIdsOffset = SpanToUint32(modelData, 76, isBigEndian);
66-
var parameterIdsOffset = SpanToUint32(modelData, 264, isBigEndian);
62+
var countInfoTableOffset = (int)modelDataSpan.ReadUInt32(64, isBigEndian);
63+
var canvasInfoOffset = (int)modelDataSpan.ReadUInt32(68, isBigEndian);
64+
var partIdsOffset = modelDataSpan.ReadUInt32(76, isBigEndian);
65+
var parameterIdsOffset = modelDataSpan.ReadUInt32(264, isBigEndian);
6766

6867
//canvas
69-
PixelPerUnit = ToSingle(modelData, canvasInfoOffset, isBigEndian);
70-
CentralPosX = ToSingle(modelData, canvasInfoOffset + 4, isBigEndian);
71-
CentralPosY = ToSingle(modelData, canvasInfoOffset + 8, isBigEndian);
72-
CanvasWidth = ToSingle(modelData, canvasInfoOffset + 12, isBigEndian);
73-
CanvasHeight = ToSingle(modelData, canvasInfoOffset + 16, isBigEndian);
68+
PixelPerUnit = modelDataSpan.ReadSingle(canvasInfoOffset, isBigEndian);
69+
CentralPosX = modelDataSpan.ReadSingle(canvasInfoOffset + 4, isBigEndian);
70+
CentralPosY = modelDataSpan.ReadSingle(canvasInfoOffset + 8, isBigEndian);
71+
CanvasWidth = modelDataSpan.ReadSingle(canvasInfoOffset + 12, isBigEndian);
72+
CanvasHeight = modelDataSpan.ReadSingle(canvasInfoOffset + 16, isBigEndian);
7473

7574
//model
76-
PartCount = SpanToUint32(modelData, countInfoTableOffset, isBigEndian);
77-
ParamCount = SpanToUint32(modelData, countInfoTableOffset + 20, isBigEndian);
78-
PartNames = ReadMocStringHashSet(modelData, (int)partIdsOffset, (int)PartCount);
79-
ParamNames = ReadMocStringHashSet(modelData, (int)parameterIdsOffset, (int)ParamCount);
75+
PartCount = modelDataSpan.ReadUInt32(countInfoTableOffset, isBigEndian);
76+
ParamCount = modelDataSpan.ReadUInt32(countInfoTableOffset + 20, isBigEndian);
77+
PartNames = ReadMocStrings(modelData, (int)partIdsOffset, (int)PartCount);
78+
ParamNames = ReadMocStrings(modelData, (int)parameterIdsOffset, (int)ParamCount);
8079
}
8180

8281
public void SaveMoc3(string savePath)
@@ -103,25 +102,16 @@ private string ParseVersion()
103102
}
104103
}
105104

106-
private static float ToSingle(ReadOnlySpan<byte> data, int index, bool isBigEndian) //net framework ver
107-
{
108-
var bytes = data.Slice(index, index + 4).ToArray();
109-
if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian))
110-
(bytes[0], bytes[1], bytes[2], bytes[3]) = (bytes[3], bytes[2], bytes[1], bytes[0]);
111-
112-
return BitConverter.ToSingle(bytes, 0);
113-
}
114-
115-
private static HashSet<string> ReadMocStringHashSet(ReadOnlySpan<byte> data, int index, int count)
105+
private static HashSet<string> ReadMocStrings(Span<byte> data, int index, int count)
116106
{
117107
const int strLen = 64;
118108
var strHashSet = new HashSet<string>();
119109
for (var i = 0; i < count; i++)
120110
{
121111
if (index + i * strLen <= data.Length)
122112
{
123-
var buff = data.Slice(index + i * strLen, strLen);
124-
strHashSet.Add(Encoding.UTF8.GetString(buff.ToArray()).TrimEnd('\0'));
113+
var str = data.Slice(index + i * strLen, strLen).ReadStringToNull();
114+
strHashSet.Add(str);
125115
}
126116
}
127117
return strHashSet;

AssetStudio/EndianBinaryReader.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public override ulong ReadUInt64()
8282
return base.ReadUInt64();
8383
}
8484

85+
#if NETFRAMEWORK
8586
public override float ReadSingle()
8687
{
8788
if (Endian == EndianType.BigEndian)
@@ -103,5 +104,26 @@ public override double ReadDouble()
103104
}
104105
return base.ReadDouble();
105106
}
107+
#else
108+
public override float ReadSingle()
109+
{
110+
if (Endian == EndianType.BigEndian)
111+
{
112+
Read(buffer, 0, 4);
113+
return BinaryPrimitives.ReadSingleBigEndian(buffer);
114+
}
115+
return base.ReadSingle();
116+
}
117+
118+
public override double ReadDouble()
119+
{
120+
if (Endian == EndianType.BigEndian)
121+
{
122+
Read(buffer, 0, 8);
123+
return BinaryPrimitives.ReadDoubleBigEndian(buffer);
124+
}
125+
return base.ReadDouble();
126+
}
127+
#endif
106128
}
107129
}

AssetStudio/EndianSpanReader.cs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,90 @@
11
using System;
22
using System.Buffers.Binary;
3+
using System.Text;
34

45
namespace AssetStudio
56
{
67
public static class EndianSpanReader
78
{
8-
public static uint SpanToUint32(Span<byte> data, int start, bool isBigEndian)
9+
public static uint ReadUInt32(this Span<byte> data, int start, bool isBigEndian)
10+
{
11+
return SpanToUInt32(data, start, isBigEndian);
12+
}
13+
14+
public static uint SpanToUInt32(Span<byte> data, int start, bool isBigEndian)
915
{
1016
return isBigEndian
1117
? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start))
1218
: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start));
1319
}
1420

15-
public static uint SpanToUint16(Span<byte> data, int start, bool isBigEndian)
21+
public static long ReadUInt16(this Span<byte> data, int start, bool isBigEndian)
22+
{
23+
return SpanToUInt16(data, start, isBigEndian);
24+
}
25+
26+
public static uint SpanToUInt16(Span<byte> data, int start, bool isBigEndian)
1627
{
1728
return isBigEndian
1829
? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start))
1930
: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start));
2031
}
2132

33+
public static long ReadInt64(this Span<byte> data, int start, bool isBigEndian)
34+
{
35+
return SpanToInt64(data, start, isBigEndian);
36+
}
37+
2238
public static long SpanToInt64(Span<byte> data, int start, bool isBigEndian)
2339
{
2440
return isBigEndian
2541
? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start))
2642
: BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start));
2743
}
44+
45+
public static float ReadSingle(this Span<byte> data, int start, bool isBigEndian)
46+
{
47+
return SpanToSingle(data, start, isBigEndian);
48+
}
49+
50+
#if NETFRAMEWORK
51+
public static float SpanToSingle(Span<byte> data, int start, bool isBigEndian)
52+
{
53+
var bytes = data.Slice(start, 4);
54+
if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian))
55+
bytes.Reverse();
56+
57+
return BitConverter.ToSingle(bytes.ToArray(), 0);
58+
}
59+
#else
60+
public static float SpanToSingle(Span<byte> data, int start, bool isBigEndian)
61+
{
62+
return isBigEndian
63+
? BinaryPrimitives.ReadSingleBigEndian(data[start..])
64+
: BinaryPrimitives.ReadSingleLittleEndian(data[start..]);
65+
}
66+
#endif
67+
68+
public static string ReadStringToNull(this Span<byte> data, int maxLength = 32767)
69+
{
70+
Span<byte> bytes = stackalloc byte[maxLength];
71+
var count = 0;
72+
while (count != data.Length && count < maxLength)
73+
{
74+
var b = data[count];
75+
if (b == 0)
76+
{
77+
break;
78+
}
79+
bytes[count] = b;
80+
count++;
81+
}
82+
bytes = bytes.Slice(0, count);
83+
#if NETFRAMEWORK
84+
return Encoding.UTF8.GetString(bytes.ToArray());
85+
#else
86+
return Encoding.UTF8.GetString(bytes);
87+
#endif
88+
}
2889
}
2990
}

0 commit comments

Comments
 (0)