@@ -11,6 +11,9 @@ use lzma::{LzmaReader, LzmaWriter};
1111
1212use crate :: { util, Error , FileInfo } ;
1313
14+ // level index, file index, total files, file name
15+ pub type CompressionCallback = fn ( usize , usize , usize , String ) ;
16+
1417fn read_u32 < T : Read > ( reader : & mut T ) -> Result < u32 , Error > {
1518 let mut buf = [ 0 ; 4 ] ;
1619 reader. read_exact ( & mut buf) ?;
@@ -92,7 +95,12 @@ impl std::fmt::Display for AssetBundleHeader {
9295 i, level. compressed_end, level. uncompressed_end
9396 ) ?;
9497 }
95- write ! ( f, "Bundle size: {}" , self . bundle_size)
98+ write ! (
99+ f,
100+ "Bundle size: {} ({} bytes)" ,
101+ util:: bytes_to_human_readable( self . bundle_size) ,
102+ self . bundle_size
103+ )
96104 }
97105}
98106impl AssetBundleHeader {
@@ -292,15 +300,15 @@ impl std::fmt::Display for LevelFile {
292300 f,
293301 "{} - {} ({} bytes) - {}" ,
294302 self . name,
295- util:: bytes_to_human_readable( self . data. len( ) as u64 ) ,
303+ util:: bytes_to_human_readable( self . data. len( ) as u32 ) ,
296304 self . data. len( ) ,
297305 hash
298306 ) ,
299307 None => write ! (
300308 f,
301309 "{} - {} ({} bytes)" ,
302310 self . name,
303- util:: bytes_to_human_readable( self . data. len( ) as u64 ) ,
311+ util:: bytes_to_human_readable( self . data. len( ) as u32 ) ,
304312 self . data. len( )
305313 ) ,
306314 }
@@ -336,15 +344,29 @@ impl Level {
336344 Ok ( Self { files } )
337345 }
338346
339- fn write < W : Write > ( & self , writer : & mut W , compression : u32 ) -> Result < usize , Error > {
347+ fn write < W : Write > (
348+ & self ,
349+ writer : & mut W ,
350+ compression : u32 ,
351+ level_idx : usize ,
352+ callback : Option < CompressionCallback > ,
353+ ) -> Result < usize , Error > {
340354 let mut writer = Counter :: new ( LzmaWriter :: new_compressor ( writer, compression) ?) ;
341355 let header = self . gen_header ( ) ;
342356 header. write ( & mut writer) ?;
343357
358+ let num_files = header. files . len ( ) ;
344359 for ( idx, file) in header. files . iter ( ) . enumerate ( ) {
345360 let padding_size = file. offset as usize - writer. writer_bytes ( ) ;
346361 writer. write_all ( & vec ! [ 0 ; padding_size] ) ?;
347362 writer. write_all ( & self . files [ idx] . data ) ?;
363+ if let Some ( callback) = callback {
364+ callback ( level_idx, idx, num_files, file. name . clone ( ) ) ;
365+ }
366+ }
367+
368+ if let Some ( callback) = callback {
369+ callback ( level_idx, num_files, num_files, "Done" . to_string ( ) ) ;
348370 }
349371
350372 // pad to 4 bytes
@@ -442,14 +464,20 @@ impl AssetBundle {
442464 Ok ( ( header, Self { levels } ) )
443465 }
444466
445- fn write < W : Write > ( & self , writer : & mut W , compression : u32 ) -> Result < ( ) , Error > {
467+ fn write < W : Write > (
468+ & self ,
469+ writer : & mut W ,
470+ compression : u32 ,
471+ callback : Option < CompressionCallback > ,
472+ ) -> Result < ( ) , Error > {
446473 let mut buf = Vec :: new ( ) ;
447474 let mut buf_writer = Counter :: new ( & mut buf) ;
448475 let mut uncompressed_bytes_written = 0 ;
449476
450477 let mut level_ends = Vec :: new ( ) ;
451- for level in & self . levels {
452- uncompressed_bytes_written += level. write ( & mut buf_writer, compression) ?;
478+ for ( idx, level) in self . levels . iter ( ) . enumerate ( ) {
479+ uncompressed_bytes_written +=
480+ level. write ( & mut buf_writer, compression, idx, callback) ?;
453481 let uncompressed_end = uncompressed_bytes_written as u32 ;
454482 let compressed_end = buf_writer. writer_bytes ( ) as u32 ;
455483 level_ends. push ( LevelEnds {
@@ -492,11 +520,57 @@ impl AssetBundle {
492520 . map_err ( |e| format ! ( "Couldn't read bundle: {}" , e) )
493521 }
494522
495- pub fn to_file ( & self , path : & str , compression_level : u32 ) -> Result < ( ) , String > {
523+ pub fn from_directory ( path : & str ) -> Result < Self , String > {
524+ // each subdirectory with the name `levelX` contains the files for that level.
525+ // they must be in order-- starting from level0-- for their files to be included.
526+ // all loose files get put at the end of level0.
527+ let root_path = PathBuf :: from ( path) ;
528+ if !root_path. is_dir ( ) {
529+ return Err ( format ! ( "Invalid root directory: {}" , path) ) ;
530+ }
531+
532+ let mut levels = Vec :: new ( ) ;
533+ for i in 0 .. {
534+ let level_dir = root_path. join ( format ! ( "level{}" , i) ) ;
535+ if !level_dir. as_path ( ) . is_dir ( ) {
536+ break ;
537+ }
538+
539+ let Ok ( files) = Self :: get_level_files_from_dir ( & level_dir) else {
540+ return Err ( format ! (
541+ "Couldn't read files in dir: {}" ,
542+ level_dir. display( )
543+ ) ) ;
544+ } ;
545+
546+ levels. push ( Level { files } ) ;
547+ }
548+
549+ let Ok ( loose_files) = Self :: get_level_files_from_dir ( & root_path) else {
550+ return Err ( format ! (
551+ "Couldn't read files in dir: {}" ,
552+ root_path. display( )
553+ ) ) ;
554+ } ;
555+ if levels. is_empty ( ) {
556+ levels. push ( Level { files : loose_files } ) ;
557+ } else {
558+ levels[ 0 ] . files . extend ( loose_files) ;
559+ }
560+
561+ Ok ( Self { levels } )
562+ }
563+
564+ pub fn to_file (
565+ & self ,
566+ path : & str ,
567+ compression_level : u32 ,
568+ callback : Option < CompressionCallback > ,
569+ ) -> Result < ( ) , String > {
496570 let file =
497571 File :: create ( path) . map_err ( |e| format ! ( "Couldn't create file {}: {}" , path, e) ) ?;
498572 let mut writer = BufWriter :: new ( file) ;
499- self . write ( & mut writer, compression_level)
573+ self . write ( & mut writer, compression_level, callback )
500574 . map_err ( |e| format ! ( "Couldn't write bundle: {}" , e) ) ?;
501575 writer
502576 . flush ( )
@@ -553,4 +627,11 @@ impl AssetBundle {
553627
554628 Ok ( result)
555629 }
630+
631+ pub fn get_num_files ( & self , level : usize ) -> Result < usize , Error > {
632+ if level >= self . levels . len ( ) {
633+ return Err ( format ! ( "Level {} does not exist" , level) . into ( ) ) ;
634+ }
635+ Ok ( self . levels [ level] . files . len ( ) )
636+ }
556637}
0 commit comments