1- using UnityAsset . NET . Enums ;
1+ using System . Collections . Concurrent ;
2+ using UnityAsset . NET . Enums ;
23using UnityAsset . NET . Files . BundleFiles ;
4+ using UnityAsset . NET . Files . SerializedFiles ;
5+ using UnityAsset . NET . IO . Reader ;
36
47namespace UnityAsset . NET . IO . Stream ;
58
69public class BlockStreamProvider : IStreamProvider
710{
8- private readonly BlockStream . BlockInfo [ ] _blocks ;
9- private readonly IReaderProvider _baseReaderProvider ;
11+ public readonly BlockStream . BlockInfo [ ] Blocks ;
12+ public readonly IReaderProvider BaseReaderProvider ;
1013 private readonly ulong _length ;
1114 private readonly UnityCN ? _unityCnInfo ;
1215
1316 public BlockStreamProvider ( List < StorageBlockInfo > blocks , IReaderProvider readerProvider ,
1417 UnityCN ? unityCnInfo = null )
1518 {
16- _blocks = new BlockStream . BlockInfo [ blocks . Count ] ;
19+ Blocks = new BlockStream . BlockInfo [ blocks . Count ] ;
1720 ulong currentOffset = 0 ;
1821 ulong uncompressedOffset = 0 ;
1922 for ( int i = 0 ; i < blocks . Count ; i ++ )
2023 {
2124 var block = blocks [ i ] ;
2225 var compressionType = ( CompressionType ) ( block . Flags & StorageBlockFlags . CompressionTypeMask ) ;
23- _blocks [ i ] = new BlockStream . BlockInfo (
26+ Blocks [ i ] = new BlockStream . BlockInfo (
2427 block . UncompressedSize ,
2528 block . CompressedSize ,
2629 currentOffset ,
@@ -31,12 +34,12 @@ public BlockStreamProvider(List<StorageBlockInfo> blocks, IReaderProvider reader
3134 uncompressedOffset += block . UncompressedSize ;
3235 }
3336
34- _baseReaderProvider = readerProvider ;
37+ BaseReaderProvider = readerProvider ;
3538 _length = uncompressedOffset ;
3639 _unityCnInfo = unityCnInfo ;
3740 }
3841
39- public System . IO . Stream OpenStream ( ) => new BlockStream ( _blocks , _baseReaderProvider , _length , _unityCnInfo ) ;
42+ public System . IO . Stream OpenStream ( ) => new BlockStream ( Blocks , BaseReaderProvider , _length , _unityCnInfo ) ;
4043}
4144
4245public class BlockStream : System . IO . Stream
@@ -60,6 +63,59 @@ public readonly record struct BlockInfo(
6063 public readonly record struct CacheKey ( IReaderProvider Provider , int BlockIndex ) ;
6164
6265 public static CustomMemoryCache < CacheKey , byte [ ] > Cache = new ( maxSize : Setting . DefaultBlockCacheSize ) ;
66+
67+ public static ConcurrentDictionary < CacheKey , ( int parsed , int total ) > AssetToBlockCache = new ( ) ;
68+
69+ public static void RegisterAssetToBlockMap ( FileEntryStreamProvider fesp , BlockStreamProvider bsp , SerializedFile sf )
70+ {
71+ var offset = fesp . FileEntry . Offset ;
72+ var blocks = bsp . Blocks ;
73+ foreach ( var info in sf . Metadata . AssetInfos )
74+ {
75+ var pos = offset + sf . Header . DataOffset + info . ByteOffset ;
76+ var index = FindBlockIndex ( blocks , pos ) ;
77+ while ( index < blocks . Length && pos + info . ByteSize > blocks [ index ] . UncompressedOffset )
78+ {
79+ var key = new CacheKey ( bsp . BaseReaderProvider , index ) ;
80+ AssetToBlockCache . AddOrUpdate ( key ,
81+ addValue : ( 0 , 1 ) ,
82+ updateValueFactory : ( k , existing ) =>
83+ ( existing . parsed , existing . total + 1 ) ) ;
84+ index ++ ;
85+ }
86+ }
87+ }
88+
89+ public static void OnAssetParsed ( Asset asset )
90+ {
91+ var sf = asset . SourceFile ;
92+ if ( sf . ReaderProvider is CustomStreamReaderProvider csrp )
93+ {
94+ if ( csrp . StreamProvider is FileEntryStreamProvider fesp )
95+ {
96+ if ( fesp . StreamProvider is BlockStreamProvider bsp )
97+ {
98+ var offset = fesp . FileEntry . Offset ;
99+ var blocks = bsp . Blocks ;
100+ var pos = offset + sf . Header . DataOffset + asset . Info . ByteOffset ;
101+ var index = FindBlockIndex ( blocks , pos ) ;
102+ while ( index < blocks . Length && pos + asset . Info . ByteSize > blocks [ index ] . UncompressedOffset )
103+ {
104+ var key = new CacheKey ( bsp . BaseReaderProvider , index ) ;
105+ var newStats = AssetToBlockCache . AddOrUpdate ( key ,
106+ addValue : ( 0 , 1 ) ,
107+ updateValueFactory : ( _ , existing ) =>
108+ ( existing . parsed + 1 , existing . total ) ) ;
109+ if ( newStats . parsed == newStats . total )
110+ {
111+ Cache . Remove ( key ) ;
112+ }
113+ index ++ ;
114+ }
115+ }
116+ }
117+ }
118+ }
63119
64120 public BlockStream ( BlockInfo [ ] blocks , IReaderProvider baseReaderProvider , ulong length , UnityCN ? unityCnInfo = null )
65121 {
@@ -80,18 +136,16 @@ public override long Position
80136 set => Seek ( value , SeekOrigin . Begin ) ;
81137 }
82138
83- private void EnsureBlockLoaded ( ulong position )
139+ private static int FindBlockIndex ( BlockInfo [ ] blocks , ulong position )
84140 {
85- if ( _disposed ) throw new ObjectDisposedException ( nameof ( BlockStream ) ) ;
86-
87141 int low = 0 ;
88- int high = Blocks . Length - 1 ;
142+ int high = blocks . Length - 1 ;
89143 int blockIndex = - 1 ;
90-
144+
91145 while ( low <= high )
92146 {
93147 int mid = low + ( high - low ) / 2 ;
94- var block = Blocks [ mid ] ;
148+ var block = blocks [ mid ] ;
95149 if ( position >= block . UncompressedOffset && position < block . UncompressedOffset + block . UncompressedSize )
96150 {
97151 blockIndex = mid ;
@@ -107,6 +161,30 @@ private void EnsureBlockLoaded(ulong position)
107161 }
108162 }
109163
164+ return blockIndex ;
165+ }
166+
167+ private byte [ ] DecompressBlock ( int index )
168+ {
169+ var block = Blocks [ index ] ;
170+ using var reader = _baseReaderProvider . CreateReader ( ) ;
171+ reader . Position = ( long ) block . CompressedOffset ;
172+ var compressedData = reader . ReadBytes ( ( int ) block . CompressedSize ) ;
173+ if ( _unityCnInfo != null && ( block . CompressionType == CompressionType . Lz4 || block . CompressionType == CompressionType . Lz4HC ) )
174+ {
175+ _unityCnInfo . DecryptBlock ( compressedData , compressedData . Length , index ) ;
176+ }
177+
178+ var uncompressedBuffer = new byte [ block . UncompressedSize ] ;
179+ Compression . DecompressToBytes ( compressedData , uncompressedBuffer . AsSpan ( 0 , ( int ) block . UncompressedSize ) , block . CompressionType ) ;
180+
181+ return uncompressedBuffer ;
182+ }
183+
184+ private void EnsureBlockLoaded ( ulong position )
185+ {
186+ var blockIndex = FindBlockIndex ( Blocks , position ) ;
187+
110188 if ( blockIndex == - 1 )
111189 {
112190 throw new ArgumentOutOfRangeException ( nameof ( position ) , "Position is beyond the end of the data" ) ;
@@ -115,26 +193,15 @@ private void EnsureBlockLoaded(ulong position)
115193 if ( blockIndex != CurrentBlockIndex )
116194 {
117195 var block = Blocks [ blockIndex ] ;
196+ var key = new CacheKey ( _baseReaderProvider , blockIndex ) ;
118197
119- var blockBuffer = Cache . GetOrCreate (
120- key : new ( _baseReaderProvider , blockIndex ) ,
121- factory : ( ) =>
122- {
123- using var reader = _baseReaderProvider . CreateReader ( ) ;
124- reader . Position = ( long ) block . CompressedOffset ;
125- var compressedData = reader . ReadBytes ( ( int ) block . CompressedSize ) ;
126- if ( _unityCnInfo != null && ( block . CompressionType == CompressionType . Lz4 || block . CompressionType == CompressionType . Lz4HC ) )
127- {
128- _unityCnInfo . DecryptBlock ( compressedData , compressedData . Length , blockIndex ) ;
129- }
130-
131- var uncompressedBuffer = new byte [ block . UncompressedSize ] ;
132- Compression . DecompressToBytes ( compressedData , uncompressedBuffer . AsSpan ( 0 , ( int ) block . UncompressedSize ) , block . CompressionType ) ;
133-
134- return uncompressedBuffer ;
135- } ,
136- size : block . UncompressedSize
137- ) ;
198+ var blockBuffer = AssetToBlockCache . ContainsKey ( key )
199+ ? Cache . GetOrCreate (
200+ key : new ( _baseReaderProvider , blockIndex ) ,
201+ factory : ( ) => DecompressBlock ( blockIndex ) ,
202+ size : block . UncompressedSize
203+ )
204+ : DecompressBlock ( blockIndex ) ;
138205
139206 _currentBlockData = new MemoryStream ( blockBuffer ) ;
140207
0 commit comments