77using System . Collections . Generic ;
88using System . Diagnostics . CodeAnalysis ;
99using System . IO ;
10+ using System . IO . Compression ;
1011using System . Linq ;
1112using System . Runtime . InteropServices ;
1213using System . Runtime . InteropServices . Marshalling ;
1314using System . Security . Cryptography ;
15+ using System . Text ;
1416using System . Text . Encodings . Web ;
1517using System . Text . Json ;
1618using System . Threading ;
1719using System . Threading . Tasks ;
20+ // ReSharper disable AccessToDisposedClosure
1821
1922namespace Indexer ;
2023
@@ -31,6 +34,7 @@ public class Program
3134{
3235 private static readonly string [ ] AllowedPluginExt = [ ".dll" , ".exe" , ".so" , ".dylib" ] ;
3336 private static readonly SearchValues < string > AllowedPluginExtSearchValues = SearchValues . Create ( AllowedPluginExt , StringComparison . OrdinalIgnoreCase ) ;
37+ private static readonly string PackageExtension = ".zip" ;
3438
3539 public static int Main ( params string [ ] args )
3640 {
@@ -57,7 +61,8 @@ public static int Main(params string[] args)
5761 }
5862
5963 string referenceFilePath = Path . Combine ( path , "manifest.json" ) ;
60- return WriteToJson ( reference , referenceFilePath , assetInfo ) ;
64+ int retCode = WriteToJson ( reference , referenceFilePath , assetInfo ) ;
65+ return retCode != 0 ? retCode : PackFiles ( path , reference , assetInfo ) ;
6166 }
6267 catch ( Exception ex )
6368 {
@@ -96,7 +101,7 @@ private static int WriteToJson(PluginManifest reference, string referenceFilePat
96101 writer . WriteString ( nameof ( PluginManifest . PluginStandardVersion ) , reference . PluginStandardVersion . ToString ( ) ) ;
97102 writer . WriteString ( nameof ( PluginManifest . PluginVersion ) , reference . PluginVersion . ToString ( ) ) ;
98103 writer . WriteString ( nameof ( PluginManifest . PluginCreationDate ) , creationDate ) ;
99- writer . WriteString ( nameof ( PluginManifest . ManifestDate ) , DateTimeOffset . Now ) ;
104+ writer . WriteString ( nameof ( PluginManifest . ManifestDate ) , reference . ManifestDate ) ;
100105 if ( reference . PluginAlternativeIcon ? . Length != 0 )
101106 {
102107 writer . WriteString ( nameof ( PluginManifest . PluginAlternativeIcon ) , reference . PluginAlternativeIcon ) ;
@@ -122,6 +127,75 @@ private static int WriteToJson(PluginManifest reference, string referenceFilePat
122127 return 0 ;
123128 }
124129
130+ private static int PackFiles ( string outputPath , PluginManifest referenceInfo , List < SelfUpdateAssetInfo > fileList )
131+ {
132+ string packageName = $ "{ Path . GetFileNameWithoutExtension ( referenceInfo . MainLibraryName ) } _{ referenceInfo . PluginVersion } _API-{ referenceInfo . PluginStandardVersion } _{ referenceInfo . ManifestDate . ToString ( "yyyyMMdd" ) } ";
133+ string packageFilePath = Path . Combine ( outputPath , packageName + PackageExtension ) ;
134+
135+ int threads = Environment . ProcessorCount ;
136+
137+ Console . WriteLine ( $ "Writing output package in parallel using { threads } threads at: { packageFilePath } ...") ;
138+
139+ try
140+ {
141+ using FileStream packageFileStream = File . Create ( packageFilePath ) ;
142+ using ZipArchive packageWriter = new ZipArchive ( packageFileStream , ZipArchiveMode . Create , false , Encoding . UTF8 ) ;
143+
144+ fileList . Add ( new SelfUpdateAssetInfo
145+ {
146+ FileHash = [ ] ,
147+ FilePath = "manifest.json"
148+ } ) ;
149+
150+ int length = fileList . Count ;
151+ int count = 0 ;
152+
153+ Lock thisLock = new Lock ( ) ;
154+ Parallel . ForEach ( fileList , CompressBrotliAndCreate ) ;
155+
156+ return 0 ;
157+
158+ void CompressBrotliAndCreate ( SelfUpdateAssetInfo asset )
159+ {
160+ Interlocked . Increment ( ref count ) ;
161+ int currentCount = count ;
162+
163+ string filePath = Path . Combine ( outputPath , asset . FilePath ) ;
164+ DateTime lastWriteTime = File . GetLastWriteTimeUtc ( filePath ) ;
165+
166+ if ( lastWriteTime . Year is < 1980 or > 2107 )
167+ lastWriteTime = new DateTime ( 1980 , 1 , 1 , 0 , 0 , 0 ) ;
168+
169+ Console . WriteLine ( $ " [{ currentCount } /{ length } ] Compressing asset to buffer: { asset . FilePath } ") ;
170+ using MemoryStream compressedStream = new MemoryStream ( ) ;
171+ using BrotliStream brotliStream = new BrotliStream ( compressedStream , CompressionLevel . SmallestSize ) ;
172+ using FileStream fileStream = File . OpenRead ( filePath ) ;
173+
174+ fileStream . CopyTo ( brotliStream ) ;
175+ brotliStream . Flush ( ) ;
176+
177+ compressedStream . Position = 0 ;
178+
179+ using ( thisLock . EnterScope ( ) )
180+ {
181+ Console . WriteLine ( $ " [{ currentCount } /{ length } ] Compress done. Now locking and writing buffer to package for: { asset . FilePath } ") ;
182+
183+ string entryBrExt = asset . FilePath + ".br" ;
184+ ZipArchiveEntry entry = packageWriter . CreateEntry ( entryBrExt , CompressionLevel . NoCompression ) ;
185+ entry . LastWriteTime = lastWriteTime ;
186+
187+ using Stream entryStream = entry . Open ( ) ;
188+ compressedStream . CopyTo ( entryStream ) ;
189+ }
190+ }
191+ }
192+ catch ( Exception e )
193+ {
194+ Console . WriteLine ( e ) ;
195+ return Marshal . GetHRForException ( e ) ;
196+ }
197+ }
198+
125199 private static FileInfo ? FindPluginLibraryAndGetAssets ( string dirPath , out List < SelfUpdateAssetInfo > fileList , out PluginManifest ? referenceInfo )
126200 {
127201 DirectoryInfo directoryInfo = new DirectoryInfo ( dirPath ) ;
@@ -140,6 +214,10 @@ private static int WriteToJson(PluginManifest reference, string referenceFilePat
140214 void Impl ( FileInfo fileInfo )
141215 {
142216 string fileName = fileInfo . FullName . AsSpan ( directoryInfo . FullName . Length ) . TrimStart ( "\\ /" ) . ToString ( ) ;
217+ if ( fileName . EndsWith ( PackageExtension , StringComparison . OrdinalIgnoreCase ) )
218+ {
219+ return ;
220+ }
143221
144222 if ( mainLibraryFileInfo == null &&
145223 IsPluginLibrary ( fileInfo , fileName , out PluginManifest ? referenceInfoInner ) )
@@ -265,7 +343,8 @@ private static unsafe bool IsPluginLibrary(FileInfo fileInfo, string fileName, [
265343 PluginVersion = pluginVersion ,
266344 PluginStandardVersion = pluginStandardVersion ,
267345 PluginAlternativeIcon = TryGetAlternateIconData ( plugin ) ,
268- MainLibraryName = fileName
346+ MainLibraryName = fileName ,
347+ ManifestDate = DateTimeOffset . UtcNow
269348 } ;
270349 return true ;
271350 }
0 commit comments