Skip to content

Commit b32bd1f

Browse files
committed
FLAC: Fix writing in files with no metadata
1 parent 368f048 commit b32bd1f

File tree

7 files changed

+417
-153
lines changed

7 files changed

+417
-153
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5858
- Don't error on empty `SYLT` strings ([issue](https://github.com/Serial-ATA/lofty-rs/issues/563)) ([PR](https://github.com/Serial-ATA/lofty-rs/pull/564))
5959
- **Vorbis Comments**: Parse `TRACKNUMBER` with respect to `ParseOptions::implicit_conversions` ([issue](https://github.com/Serial-ATA/lofty-rs/issues/540)) ([PR](https://github.com/Serial-ATA/lofty-rs/issues/542))
6060
- **APE**: Fix disc number removal/writing ([issue](https://github.com/Serial-ATA/lofty-rs/issues/545)) ([PR](https://github.com/Serial-ATA/lofty-rs/pull/546))
61+
- **FLAC**: Fix corruption of files with no metadata blocks ([issue](https://github.com/Serial-ATA/lofty-rs/issues/549)) ([PR](https://github.com/Serial-ATA/lofty-rs/pull/583))
6162

6263
### Removed
6364

lofty/src/flac/block.rs

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#![allow(dead_code)]
22

33
use crate::error::Result;
4-
use crate::macros::try_vec;
4+
use crate::macros::{err, try_vec};
5+
use crate::picture::{Picture, PictureInformation};
56

6-
use std::io::{Read, Seek, SeekFrom};
7+
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
78

8-
use byteorder::{BigEndian, ReadBytesExt};
9+
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
910

1011
pub(in crate::flac) const BLOCK_ID_STREAMINFO: u8 = 0;
1112
pub(in crate::flac) const BLOCK_ID_PADDING: u8 = 1;
@@ -16,7 +17,6 @@ pub(in crate::flac) const BLOCK_ID_PICTURE: u8 = 6;
1617
const BLOCK_HEADER_SIZE: u64 = 4;
1718

1819
pub(crate) struct Block {
19-
pub(super) byte: u8,
2020
pub(super) ty: u8,
2121
pub(super) last: bool,
2222
pub(crate) content: Vec<u8>,
@@ -25,6 +25,9 @@ pub(crate) struct Block {
2525
}
2626

2727
impl Block {
28+
pub(super) const BLOCK_HEADER_SIZE: usize = 4;
29+
pub(super) const MAX_CONTENT_SIZE: u32 = 16_777_215;
30+
2831
pub(crate) fn read<R, P>(data: &mut R, mut predicate: P) -> Result<Self>
2932
where
3033
R: Read + Seek,
@@ -51,12 +54,88 @@ impl Block {
5154
let end = start + u64::from(size) + BLOCK_HEADER_SIZE;
5255

5356
Ok(Self {
54-
byte,
5557
ty,
5658
last,
5759
content,
5860
start,
5961
end,
6062
})
6163
}
64+
65+
pub fn len(&self) -> u32 {
66+
(Self::BLOCK_HEADER_SIZE as u32) + self.content.len() as u32
67+
}
68+
69+
pub(super) fn new_padding(size: usize) -> Result<Self> {
70+
let block_size = core::cmp::min(size, Self::MAX_CONTENT_SIZE as usize);
71+
let content = try_vec![0; block_size];
72+
Ok(Self {
73+
ty: BLOCK_ID_PADDING,
74+
last: false,
75+
content,
76+
start: 0,
77+
end: 0,
78+
})
79+
}
80+
81+
pub(super) fn new_picture(picture: &Picture, info: PictureInformation) -> Result<Self> {
82+
let picture_data = picture.as_flac_bytes(info, false);
83+
if picture_data.len() > Self::MAX_CONTENT_SIZE as usize {
84+
err!(TooMuchData);
85+
}
86+
87+
Ok(Self {
88+
ty: BLOCK_ID_PICTURE,
89+
last: false,
90+
content: picture_data,
91+
start: 0,
92+
end: 0,
93+
})
94+
}
95+
96+
pub(super) fn new_comments<'a>(
97+
vendor: &str,
98+
items: &mut impl Iterator<Item = (&'a str, &'a str)>,
99+
) -> Result<Self> {
100+
let mut comments = Cursor::new(Vec::new());
101+
102+
comments.write_u32::<LittleEndian>(vendor.len() as u32)?;
103+
comments.write_all(vendor.as_bytes())?;
104+
105+
let item_count_pos = comments.stream_position()?;
106+
let mut count = 0;
107+
108+
comments.write_u32::<LittleEndian>(count)?;
109+
110+
crate::ogg::write::create_comments(&mut comments, &mut count, items)?;
111+
112+
if comments.get_ref().len() > Block::MAX_CONTENT_SIZE as usize {
113+
err!(TooMuchData);
114+
}
115+
116+
comments.seek(SeekFrom::Start(item_count_pos))?;
117+
comments.write_u32::<LittleEndian>(count)?;
118+
119+
Ok(Self {
120+
ty: BLOCK_ID_VORBIS_COMMENTS,
121+
last: false,
122+
content: comments.into_inner(),
123+
start: 0,
124+
end: 0,
125+
})
126+
}
127+
128+
pub(super) fn write_to<W>(&self, writer: &mut W) -> Result<usize>
129+
where
130+
W: Write,
131+
{
132+
let block_content_size =
133+
core::cmp::min(self.content.len(), Self::MAX_CONTENT_SIZE as usize);
134+
135+
writer.write_u8((self.ty & 0x7F) | u8::from(self.last) << 7)?;
136+
writer.write_u24::<BigEndian>(block_content_size as u32)?;
137+
writer.write_all(&self.content)?;
138+
139+
Ok(Self::BLOCK_HEADER_SIZE + self.content.len())
140+
}
62141
}

0 commit comments

Comments
 (0)