Skip to content

Commit 293571c

Browse files
committed
Improve handling of mip levels
1 parent c6a5667 commit 293571c

File tree

1 file changed

+54
-25
lines changed

1 file changed

+54
-25
lines changed

AssetRipper.Conversions.UnityCrunch/UnityCrunch.cs

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using AssetRipper.Conversions.UnityCrunch.Structures;
22
using System;
3+
using System.Diagnostics;
34
using System.Diagnostics.CodeAnalysis;
45

56
namespace AssetRipper.Conversions.UnityCrunch;
@@ -16,12 +17,7 @@ public static partial class UnityCrunch
1617
/// </summary>
1718
public const int MaxLevels = 16;
1819

19-
public static bool TryDecompress(ReadOnlySpan<byte> input, [NotNullWhen(true)] out byte[]? output)
20-
{
21-
return TryDecompress(input, 0, out output);
22-
}
23-
24-
public static unsafe bool TryDecompress(ReadOnlySpan<byte> input, int levelIndex, [NotNullWhen(true)] out byte[]? output)
20+
public static unsafe bool TryDecompress(ReadOnlySpan<byte> input, [NotNullWhen(true)] out byte[]? output)
2521
{
2622
if (input.Length == 0)
2723
{
@@ -46,35 +42,54 @@ public static unsafe bool TryDecompress(ReadOnlySpan<byte> input, int levelIndex
4642
}
4743

4844
// m_struct_size
49-
int width = int.Max(1, textureInfo.field_1 >> levelIndex);
50-
int height = int.Max(1, textureInfo.field_2 >> levelIndex);
51-
// m_levels
45+
int fullWidth = textureInfo.field_1;
46+
int fullHeight = textureInfo.field_2;
47+
int levelCount = textureInfo.field_3;
5248
int faceCount = textureInfo.field_4;
53-
int bytesPerBlock = textureInfo.field_5;
49+
int bytesPerDxtBlock = textureInfo.field_5;
5450
// m_userdata0
5551
// m_userdata1
5652
// m_format
5753

58-
int blocksX = (width + 3) >> 2;
59-
int blocksY = (height + 3) >> 2;
60-
int rowPitch = blocksX * crnd_get_bytes_per_dxt_block(textureInfo.field_8);
61-
int faceSize = rowPitch * blocksY;
62-
int totalFaceSize = faceCount * faceSize;
54+
Debug.Assert(bytesPerDxtBlock == crnd_get_bytes_per_dxt_block(textureInfo.field_8));
55+
56+
if (levelCount < 1 || levelCount > MaxLevels || faceCount < 1 || faceCount > MaxFaces)
57+
{
58+
crnd_unpack_end(context);
59+
return False(out output);
60+
}
61+
62+
int completeImageSize = CalculateCompleteImageSize(fullWidth, fullHeight, bytesPerDxtBlock, levelCount);
63+
64+
byte[] result = new byte[completeImageSize * faceCount];
6365

64-
byte[] result = new byte[totalFaceSize];
66+
byte** pResultArray = stackalloc byte*[MaxFaces];
6567

6668
fixed (byte* pResult = result)
6769
{
68-
byte** pResultArray = stackalloc byte*[MaxFaces] { null, null, null, null, null, null };
69-
for (int i = 0; i < faceCount; i++)
70+
int offset = 0;
71+
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++)
7072
{
71-
pResultArray[i] = pResult + i * faceSize;
72-
}
73+
int width = int.Max(1, fullWidth >> levelIndex);
74+
int height = int.Max(1, fullHeight >> levelIndex);
75+
int blocksX = (width + 3) >> 2;
76+
int blocksY = (height + 3) >> 2;
77+
int rowPitch = blocksX * bytesPerDxtBlock;
78+
int faceSize = rowPitch * blocksY;
7379

74-
if (!crnd_unpack_level(context, pResultArray, faceSize, rowPitch, levelIndex))
75-
{
76-
crnd_unpack_end(context);
77-
return False(out output);
80+
new Span<nint>(pResultArray, MaxFaces).Clear();
81+
for (int i = 0; i < faceCount; i++)
82+
{
83+
pResultArray[i] = pResult + offset + i * completeImageSize;
84+
}
85+
86+
if (!crnd_unpack_level(context, pResultArray, faceSize, rowPitch, levelIndex))
87+
{
88+
crnd_unpack_end(context);
89+
return False(out output);
90+
}
91+
92+
offset += faceSize;
7893
}
7994
}
8095
crnd_unpack_end(context);
@@ -83,10 +98,24 @@ public static unsafe bool TryDecompress(ReadOnlySpan<byte> input, int levelIndex
8398

8499
return true;
85100

86-
static unsafe bool False([NotNullWhen(true)] out byte[]? output)
101+
static bool False([NotNullWhen(true)] out byte[]? output)
87102
{
88103
output = null;
89104
return false;
90105
}
106+
107+
static int CalculateCompleteImageSize(int width, int height, int bytesPerDxtBlock, int levelCount)
108+
{
109+
int totalSize = 0;
110+
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++)
111+
{
112+
int levelWidth = int.Max(1, width >> levelIndex);
113+
int levelHeight = int.Max(1, height >> levelIndex);
114+
int blocksX = (levelWidth + 3) >> 2;
115+
int blocksY = (levelHeight + 3) >> 2;
116+
totalSize += blocksX * blocksY * bytesPerDxtBlock;
117+
}
118+
return totalSize;
119+
}
91120
}
92121
}

0 commit comments

Comments
 (0)