@@ -498,6 +498,107 @@ def test__ensure_little_endian_big_endian_input():
498498 assert result [0 ] == 0x12345678
499499
500500
501+ def test_load_zip64_with_extra_fields ():
502+ """Test loading ZIP64 files where both local and CD headers have extra fields.
503+
504+ Rust and other tools always write ZIP64 extended information (extra field
505+ ID 0x0001, 20 bytes: 4-byte tag+size + 8-byte orig + 8-byte comp) in both
506+ local file headers and central directory entries, even for small files.
507+ This ensures the data offset is computed correctly by reading the local
508+ header's extra_len rather than assuming a fixed layout.
509+ """
510+ positions = np .array ([[1.0 , 2.0 , 3.0 ], [4.0 , 5.0 , 6.0 ]], dtype = np .float32 )
511+ offsets = np .array ([0 , 2 ], dtype = np .uint64 )
512+ header = {
513+ "DIMENSIONS" : [10 , 10 , 10 ],
514+ "VOXEL_TO_RASMM" : np .eye (4 ).tolist (),
515+ "NB_VERTICES" : 2 ,
516+ "NB_STREAMLINES" : 1 ,
517+ }
518+
519+ def make_zip64_extra (orig_size , comp_size ):
520+ data = struct .pack ("<QQ" , orig_size , comp_size )
521+ return struct .pack ("<HH" , 0x0001 , len (data )) + data
522+
523+ with tempfile .TemporaryDirectory () as tmp_dir :
524+ trx_path = os .path .join (tmp_dir , "test_zip64.trx" )
525+
526+ with open (trx_path , "wb" ) as f :
527+ local_info = []
528+
529+ for name , data in [
530+ ("header.json" , json .dumps (header ).encode ()),
531+ ("positions.3.float32" , positions .astype ("<f4" ).tobytes ()),
532+ ("offsets.uint64" , offsets .astype ("<u8" ).tobytes ()),
533+ ]:
534+ offset = f .tell ()
535+ fname = name .encode ()
536+ crc = zipfile .crc32 (data )
537+ # ZIP64 extended info in local header (20 bytes)
538+ extra = make_zip64_extra (len (data ), len (data ))
539+ f .write (
540+ struct .pack (
541+ "<4sHHHHHIIIHH" ,
542+ b"PK\x03 \x04 " ,
543+ 45 , # version needed: ZIP64
544+ 0 , 0 , 0 , 0 ,
545+ crc ,
546+ len (data ),
547+ len (data ),
548+ len (fname ),
549+ len (extra ),
550+ )
551+ )
552+ f .write (fname )
553+ f .write (extra )
554+ f .write (data )
555+ local_info .append ((name , offset , crc , len (data )))
556+
557+ cd_start = f .tell ()
558+ for name , offset , crc , size in local_info :
559+ fname = name .encode ()
560+ # ZIP64 extended info in central directory entry (20 bytes)
561+ extra = make_zip64_extra (size , size )
562+ f .write (
563+ struct .pack (
564+ "<4sHHHHHHIIIHHHHHII" ,
565+ b"PK\x01 \x02 " ,
566+ 45 , # version made by: ZIP64
567+ 45 , # version needed: ZIP64
568+ 0 , 0 , 0 , 0 ,
569+ crc ,
570+ size ,
571+ size ,
572+ len (fname ),
573+ len (extra ),
574+ 0 , 0 , 0 , 0 ,
575+ offset ,
576+ )
577+ )
578+ f .write (fname )
579+ f .write (extra )
580+
581+ cd_size = f .tell () - cd_start
582+ f .write (
583+ struct .pack (
584+ "<4sHHHHIIH" ,
585+ b"PK\x05 \x06 " ,
586+ 0 , 0 ,
587+ len (local_info ),
588+ len (local_info ),
589+ cd_size ,
590+ cd_start ,
591+ 0 ,
592+ )
593+ )
594+
595+ trx = tmm .load_from_zip (trx_path )
596+ np .testing .assert_array_almost_equal (trx .streamlines ._data , positions )
597+ assert trx .header ["NB_VERTICES" ] == 2
598+ assert trx .header ["NB_STREAMLINES" ] == 1
599+ trx .close ()
600+
601+
501602def test_load_zip_with_local_header_extra_field ():
502603 """Test loading ZIP where local header has extra field not in central dir.
503604
0 commit comments