11using System ;
22using System . Buffers ;
33using System . Collections . Generic ;
4+ using System . IO ;
5+ using System . IO . Compression ;
46using System . Linq ;
7+ using System . Reflection ;
58using BTD_Mod_Helper . Api . UI ;
69using Il2CppAssets . Scripts . Unity . Audio ;
710using Il2CppNinjaKiwi . Common . ResourceUtils ;
8- using Il2CppSystem . IO ;
911using NAudio . Vorbis ;
1012using NAudio . Wave ;
1113using UnityEngine ;
1214using UnityEngine . AddressableAssets ;
13- using Stream = System . IO . Stream ;
1415namespace BTD_Mod_Helper . Api . Internal ;
1516
1617/// <summary>
@@ -67,22 +68,64 @@ public static class ResourceHandler
6768
6869 internal static void LoadEmbeddedResources ( BloonsMod mod )
6970 {
70- LoadEmbeddedTextures ( mod ) ;
71- LoadEmbeddedAudio ( mod ) ;
72- LoadEmbeddedBundles ( mod ) ;
71+ var zipArchives = LoadEmbeddedZipArchives ( mod ) ;
72+ try
73+ {
74+ var zipEntries = zipArchives
75+ . SelectMany ( archive => archive . Entries )
76+ . Where ( entry => ! string . IsNullOrEmpty ( entry . Name ) )
77+ . ToList ( ) ;
78+
79+ LoadEmbeddedTextures ( mod , zipEntries ) ;
80+ LoadEmbeddedAudio ( mod , zipEntries ) ;
81+ LoadEmbeddedBundles ( mod , zipEntries ) ;
82+ }
83+ finally
84+ {
85+ foreach ( var zipArchive in zipArchives )
86+ {
87+ zipArchive . Dispose ( ) ;
88+ }
89+ }
7390 }
7491
75- internal static void LoadEmbeddedTextures ( BloonsMod mod )
92+ private static List < ZipArchive > LoadEmbeddedZipArchives ( BloonsMod mod )
93+ {
94+ var assembly = mod . GetAssembly ( ) ;
95+ var archives = new List < ZipArchive > ( ) ;
96+
97+ foreach ( var zipName in assembly . GetManifestResourceNames ( ) . Where ( s => s . EndsWith ( ".zip" ) ) )
98+ {
99+ Stream zipStream = null ;
100+ try
101+ {
102+ zipStream = assembly . GetManifestResourceStream ( zipName ) ;
103+ if ( zipStream == null ) continue ;
104+
105+ archives . Add ( new ZipArchive ( zipStream , ZipArchiveMode . Read ) ) ;
106+ zipStream = null ;
107+ }
108+ catch ( Exception e )
109+ {
110+ zipStream ? . Dispose ( ) ;
111+ ModHelper . Warning ( "Failed to load embedded resource zip " + zipName ) ;
112+ ModHelper . Warning ( e ) ;
113+ }
114+ }
115+
116+ return archives ;
117+ }
118+
119+ internal static void LoadEmbeddedTextures ( BloonsMod mod , IEnumerable < ZipArchiveEntry > zipEntries = null )
76120 {
77121 mod . Resources = new Dictionary < string , byte [ ] > ( ) ;
78- foreach ( var fileName in mod . GetAssembly ( ) . GetManifestResourceNames ( ) . Where ( s => ImageExtensions . Any ( s . EndsWith ) ) )
122+
123+ foreach ( var ( name , ext , stream ) in GetEmbeddedResourceStreams (
124+ mod . GetAssembly ( ) , zipEntries , ImageExtensions ) )
79125 {
80- var resource = mod . GetAssembly ( ) . GetManifestResourceStream ( fileName ) . GetByteArray ( ) ;
126+ var resource = stream . GetByteArray ( ) ;
81127 if ( resource == null ) continue ;
82128
83- var split = fileName . Split ( '.' ) ;
84- var name = split [ ^ 2 ] ;
85- var ext = split [ ^ 1 ] ;
86129 var id = ModContent . GetId ( mod , name ) ;
87130 Resources [ id ] = resource ;
88131 mod . Resources [ name ] = resource ;
@@ -91,21 +134,18 @@ internal static void LoadEmbeddedTextures(BloonsMod mod)
91134 }
92135 }
93136
94- internal static void LoadEmbeddedAudio ( BloonsMod mod )
137+ internal static void LoadEmbeddedAudio ( BloonsMod mod , IEnumerable < ZipArchiveEntry > zipEntries = null )
95138 {
96139 mod . AudioClips = new Dictionary < string , AudioClip > ( ) ;
97140
98- foreach ( var fileName in mod . GetAssembly ( ) . GetManifestResourceNames ( ) . Where ( s => AudioExtensions . Any ( s . EndsWith ) ) )
141+ foreach ( var ( name , ext , stream ) in GetEmbeddedResourceStreams (
142+ mod . GetAssembly ( ) , zipEntries , AudioExtensions ) )
99143 {
100- var split = fileName . Split ( '.' ) ;
101- var extension = split [ ^ 1 ] ;
102- var name = split [ ^ 2 ] ;
103144 var id = ModContent . GetId ( mod , name ) ;
104145
105146 try
106147 {
107- using var stream = mod . GetAssembly ( ) . GetManifestResourceStream ( fileName ) ! ;
108- using var waveStream = GetWaveStream ( stream , extension ) ;
148+ using var waveStream = GetWaveStream ( stream , ext ) ;
109149
110150 if ( mod . NormalizeAllAudioVolume )
111151 {
@@ -120,49 +160,85 @@ internal static void LoadEmbeddedAudio(BloonsMod mod)
120160 }
121161 catch ( Exception e )
122162 {
123- ModHelper . Warning ( "Failed to load audio clip " + fileName ) ;
163+ ModHelper . Warning ( $ "Failed to load audio clip { name } . { ext } " ) ;
124164 ModHelper . Warning ( e ) ;
125165 }
126166 }
127167 }
128168
129- internal static void LoadEmbeddedBundles ( BloonsMod mod )
169+ internal static void LoadEmbeddedBundles ( BloonsMod mod , IEnumerable < ZipArchiveEntry > zipEntries = null )
130170 {
131- foreach ( var name in mod . GetAssembly ( ) . GetManifestResourceNames ( ) . Where ( s => s . EndsWith ( " bundle") ) )
171+ foreach ( var ( name , _ , stream ) in GetEmbeddedResourceStreams ( mod . GetAssembly ( ) , zipEntries , [ ". bundle"] ) )
132172 {
133- var bytes = mod . GetAssembly ( ) . GetManifestResourceStream ( name ) . GetByteArray ( ) ;
173+ var bytes = stream . GetByteArray ( ) ;
134174 if ( bytes == null ) continue ;
135175
136- var stream = new MemoryStream ( bytes ) ;
137- var bundle = AssetBundle . LoadFromStream ( stream ) ;
138- stream . Dispose ( ) ;
139- var guid = mod . IDPrefix ;
140- if ( bundle == null )
141- {
142- ModHelper . Log ( $ "The bundle { name } is null!") ;
143- continue ;
144- }
176+ LoadBundle ( mod , name , bytes ) ;
177+ }
178+ }
145179
146- if ( string . IsNullOrEmpty ( bundle . name ) )
147- {
148- ModHelper . Log ( $ "The bundle { name } has no name!") ;
149- continue ;
150- }
180+ private static void LoadBundle ( BloonsMod mod , string name , byte [ ] bytes )
181+ {
182+ var stream = new Il2CppSystem . IO . MemoryStream ( bytes ) ;
183+ var bundle = AssetBundle . LoadFromStream ( stream ) ;
184+ stream . Dispose ( ) ;
185+ var guid = mod . IDPrefix ;
186+ if ( bundle == null )
187+ {
188+ ModHelper . Log ( $ "The bundle { name } is null!") ;
189+ return ;
190+ }
151191
152- if ( bundle . name . EndsWith ( ".bundle" ) )
153- {
154- guid += bundle . name . Substring ( 0 , bundle . name . LastIndexOf ( "." , StringComparison . Ordinal ) ) ;
155- }
156- else
157- {
158- guid += bundle . name ;
159- }
192+ if ( string . IsNullOrEmpty ( bundle . name ) )
193+ {
194+ ModHelper . Log ( $ "The bundle { name } has no name!") ;
195+ return ;
196+ }
197+
198+ if ( bundle . name . EndsWith ( ".bundle" ) )
199+ {
200+ guid += bundle . name . Substring ( 0 , bundle . name . LastIndexOf ( "." , StringComparison . Ordinal ) ) ;
201+ }
202+ else
203+ {
204+ guid += bundle . name ;
205+ }
206+
207+ Bundles [ guid ] = bundle ;
208+ // ModHelper.Msg("Successfully loaded bundle " + guid);
209+ }
210+
211+ private static IEnumerable < ( string Name , string Ext , Stream Stream ) > GetEmbeddedResourceStreams (
212+ Assembly assembly , IEnumerable < ZipArchiveEntry > zipEntries , string [ ] extensions )
213+ {
214+ foreach ( var fileName in assembly . GetManifestResourceNames ( ) . Where ( s => extensions . Any ( s . EndsWith ) ) )
215+ {
216+ using var stream = assembly . GetManifestResourceStream ( fileName ) ;
217+ if ( stream == null ) continue ;
160218
161- Bundles [ guid ] = bundle ;
162- // ModHelper.Msg("Successfully loaded bundle " + guid);
219+ var split = fileName . Split ( '.' ) ;
220+ yield return ( split [ ^ 2 ] , split [ ^ 1 ] , stream ) ;
221+ }
222+
223+ if ( zipEntries == null ) yield break ;
224+
225+ foreach ( var entry in zipEntries . Where ( entry => extensions . Any ( entry . Name . EndsWith ) ) )
226+ {
227+ using var stream = entry . Open ( ) ;
228+ var fileName = GetZipEntryFileName ( entry ) ;
229+ yield return (
230+ Path . GetFileNameWithoutExtension ( fileName ) ,
231+ Path . GetExtension ( fileName ) . TrimStart ( '.' ) ,
232+ stream ) ;
163233 }
164234 }
165235
236+ private static string GetZipEntryFileName ( ZipArchiveEntry entry )
237+ {
238+ var normalized = entry . FullName . Replace ( '\\ ' , '/' ) ;
239+ var index = normalized . LastIndexOf ( '/' ) ;
240+ return index >= 0 ? normalized [ ( index + 1 ) ..] : normalized ;
241+ }
166242
167243 /// <summary>
168244 /// Turns a stream into a WaveStream based on file extension
0 commit comments