@@ -11,7 +11,7 @@ use crate::picture::{MimeType, Picture};
1111use crate :: probe:: ParseOptions ;
1212
1313use std:: fs:: File ;
14- use std:: io:: { Seek , SeekFrom , Write } ;
14+ use std:: io:: { Cursor , Seek , SeekFrom , Write } ;
1515
1616use byteorder:: { BigEndian , ReadBytesExt , WriteBytesExt } ;
1717
@@ -208,7 +208,7 @@ fn save_to_existing(
208208 ilst : Vec < u8 > ,
209209 remove_tag : bool ,
210210) -> Result < ( ) > {
211- let replacement;
211+ let mut replacement;
212212 let range;
213213
214214 let mut write_handle = writer. start_write ( ) ;
@@ -293,9 +293,24 @@ fn save_to_existing(
293293 }
294294 }
295295
296- let new_meta_size = ( meta. len - range. len ( ) as u64 ) + replacement. len ( ) as u64 ;
297-
298296 drop ( write_handle) ;
297+
298+ let mut new_meta_size = ( meta. len - range. len ( ) as u64 ) + replacement. len ( ) as u64 ;
299+
300+ // Pad the `ilst` in the event of a shrink
301+ let mut difference = ( new_meta_size as i64 ) - ( meta. len as i64 ) ;
302+ if !replacement. is_empty ( ) && difference != 0 {
303+ log:: trace!( "Tag size changed, attempting to avoid offset update" ) ;
304+
305+ let mut ilst_writer = Cursor :: new ( replacement) ;
306+ let ( atom_size_difference, padding_size) = pad_atom ( & mut ilst_writer, difference) ?;
307+
308+ replacement = ilst_writer. into_inner ( ) ;
309+ new_meta_size += padding_size;
310+ difference = atom_size_difference;
311+ }
312+
313+ // Update the parent atom sizes
299314 if new_meta_size != meta. len {
300315 // We need to change the `meta` and `udta` atom sizes
301316 let mut write_handle = writer. start_write ( ) ;
@@ -307,40 +322,13 @@ fn save_to_existing(
307322
308323 write_handle. seek ( SeekFrom :: Start ( udta. start ) ) ?;
309324 write_handle. write_atom_size ( udta. start , * new_udta_size, udta. extended ) ?;
310-
311- // Update offset atoms
312-
313- let mut difference = ( new_meta_size as i64 ) - ( meta. len as i64 ) ;
314- if difference. is_negative ( ) {
315- let diff_abs = difference. abs ( ) ;
316- if diff_abs >= 8 {
317- log:: trace!(
318- "Avoiding offset update, padding tag with {} bytes" ,
319- diff_abs
320- ) ;
321-
322- // If our difference is >= 8, we can make up the difference with
323- // a `free` atom and skip updating the offsets.
324- write_free_atom ( & mut write_handle, diff_abs as u32 ) ?;
325- difference = 0 ;
326- } else {
327- log:: trace!(
328- "Cannot avoid offset update, padding tag with {} bytes" ,
329- DEFAULT_PADDING_SIZE
330- ) ;
331-
332- // Otherwise, we'll have to just pad the default amount,
333- // and update the offsets.
334- write_free_atom ( & mut write_handle, DEFAULT_PADDING_SIZE ) ?;
335- difference += i64:: from ( DEFAULT_PADDING_SIZE ) ;
336- }
337- }
338-
339325 drop ( write_handle) ;
340- if difference != 0 {
341- let offset = range. start as u64 ;
342- update_offsets ( writer, moov, difference, offset) ?;
343- }
326+ }
327+
328+ // Update offset atoms
329+ if difference != 0 {
330+ let offset = range. start as u64 ;
331+ update_offsets ( writer, moov, difference, offset) ?;
344332 }
345333
346334 // Replace the `ilst` atom
@@ -351,6 +339,48 @@ fn save_to_existing(
351339 Ok ( ( ) )
352340}
353341
342+ fn pad_atom < W > ( writer : & mut W , mut atom_size_difference : i64 ) -> Result < ( i64 , u64 ) >
343+ where
344+ W : Write + Seek ,
345+ {
346+ if atom_size_difference. is_positive ( ) {
347+ log:: trace!( "Atom has grown, cannot avoid offset update" ) ;
348+ return Ok ( ( atom_size_difference, 0 ) ) ;
349+ }
350+
351+ // When the tag shrinks, we need to try and pad it out to avoid updating
352+ // the offsets.
353+ writer. seek ( SeekFrom :: End ( 0 ) ) ?;
354+
355+ let padding_size: u64 ;
356+ let diff_abs = atom_size_difference. abs ( ) ;
357+ if diff_abs >= 8 {
358+ log:: trace!(
359+ "Avoiding offset update, padding atom with {} bytes" ,
360+ diff_abs
361+ ) ;
362+
363+ // If our difference is >= 8, we can make up the difference with
364+ // a `free` atom and skip updating the offsets.
365+ write_free_atom ( writer, diff_abs as u32 ) ?;
366+ atom_size_difference = 0 ;
367+ padding_size = diff_abs as u64 ;
368+ } else {
369+ log:: trace!(
370+ "Cannot avoid offset update, padding atom with {} bytes" ,
371+ DEFAULT_PADDING_SIZE
372+ ) ;
373+
374+ // Otherwise, we'll have to just pad the default amount,
375+ // and update the offsets.
376+ write_free_atom ( writer, DEFAULT_PADDING_SIZE ) ?;
377+ atom_size_difference += i64:: from ( DEFAULT_PADDING_SIZE ) ;
378+ padding_size = u64:: from ( DEFAULT_PADDING_SIZE ) ;
379+ }
380+
381+ Ok ( ( atom_size_difference, padding_size) )
382+ }
383+
354384fn write_free_atom < W > ( writer : & mut W , size : u32 ) -> Result < ( ) >
355385where
356386 W : Write ,
@@ -365,7 +395,7 @@ fn update_offsets(
365395 writer : & AtomWriter ,
366396 moov : & ContextualAtom ,
367397 difference : i64 ,
368- offset : u64 ,
398+ ilst_offset : u64 ,
369399) -> Result < ( ) > {
370400 log:: debug!( "Checking for offset atoms to update" ) ;
371401
@@ -385,15 +415,15 @@ fn update_offsets(
385415 let count = write_handle. read_u32 :: < BigEndian > ( ) ?;
386416 for _ in 0 ..count {
387417 let read_offset = write_handle. read_u32 :: < BigEndian > ( ) ?;
388- if u64:: from ( read_offset) < offset {
418+ if u64:: from ( read_offset) < ilst_offset {
389419 continue ;
390420 }
391421 write_handle. seek ( SeekFrom :: Current ( -4 ) ) ?;
392422 write_handle. write_u32 :: < BigEndian > ( ( i64:: from ( read_offset) + difference) as u32 ) ?;
393423
394424 log:: trace!(
395425 "Updated offset from {} to {}" ,
396- offset ,
426+ read_offset ,
397427 ( i64 :: from( read_offset) + difference) as u32
398428 ) ;
399429 }
@@ -413,7 +443,7 @@ fn update_offsets(
413443 let count = write_handle. read_u32 :: < BigEndian > ( ) ?;
414444 for _ in 0 ..count {
415445 let read_offset = write_handle. read_u64 :: < BigEndian > ( ) ?;
416- if read_offset < offset {
446+ if read_offset < ilst_offset {
417447 continue ;
418448 }
419449
@@ -422,7 +452,7 @@ fn update_offsets(
422452
423453 log:: trace!(
424454 "Updated offset from {} to {}" ,
425- offset ,
455+ read_offset ,
426456 ( ( read_offset as i64 ) + difference) as u64
427457 ) ;
428458 }
@@ -451,7 +481,7 @@ fn update_offsets(
451481
452482 if base_data_offset {
453483 let read_offset = write_handle. read_u64 :: < BigEndian > ( ) ?;
454- if read_offset < offset {
484+ if read_offset < ilst_offset {
455485 continue ;
456486 }
457487
@@ -460,8 +490,8 @@ fn update_offsets(
460490
461491 log:: trace!(
462492 "Updated offset from {} to {}" ,
463- offset ,
464- ( ( offset as i64 ) + difference) as u64
493+ read_offset ,
494+ ( ( read_offset as i64 ) + difference) as u64
465495 ) ;
466496 }
467497 }
0 commit comments