Skip to content

Commit bdfe1a8

Browse files
committed
ID3v2: Move all header related items to v2::header module
1 parent 553d16f commit bdfe1a8

File tree

18 files changed

+175
-172
lines changed

18 files changed

+175
-172
lines changed

src/aac/read.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::header::{ADTSHeader, HEADER_MASK};
22
use super::AacFile;
33
use crate::error::Result;
4+
use crate::id3::v2::header::Id3v2Header;
45
use crate::id3::v2::read::parse_id3v2;
5-
use crate::id3::v2::read_id3v2_header;
66
use crate::id3::{find_id3v1, ID3FindResults};
77
use crate::macros::{decode_err, parse_mode_choice};
88
use crate::mpeg::header::{cmp_header, search_for_frame_sync, HeaderCmpResult};
@@ -43,7 +43,7 @@ where
4343
// Seek back to read the tag in full
4444
reader.seek(SeekFrom::Current(-4))?;
4545

46-
let header = read_id3v2_header(reader)?;
46+
let header = Id3v2Header::parse(reader)?;
4747
let skip_footer = header.flags.footer;
4848

4949
stream_len -= u64::from(header.size);

src/id3/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub mod v2;
88

99
use crate::error::{ErrorKind, LoftyError, Result};
1010
use crate::macros::try_vec;
11-
use v2::{read_id3v2_header, Id3v2Header};
11+
use v2::header::Id3v2Header;
1212

1313
use std::io::{Read, Seek, SeekFrom};
1414
use std::ops::Neg;
@@ -92,7 +92,7 @@ where
9292
let mut header = None;
9393
let mut id3v2 = None;
9494

95-
if let Ok(id3v2_header) = read_id3v2_header(data) {
95+
if let Ok(id3v2_header) = Id3v2Header::parse(data) {
9696
if read {
9797
let mut tag = try_vec![0; id3v2_header.size as usize];
9898
data.read_exact(&mut tag)?;

src/id3/v2/flags.rs

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/id3/v2/frame/content.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::error::{Id3v2Error, Id3v2ErrorKind, Result};
22
use crate::id3::v2::frame::FrameValue;
3+
use crate::id3::v2::header::Id3v2Version;
34
use crate::id3::v2::items::{
45
AttachedPictureFrame, CommentFrame, EventTimingCodesFrame, ExtendedTextFrame, ExtendedUrlFrame,
56
KeyValueFrame, OwnershipFrame, Popularimeter, PrivateFrame, RelativeVolumeAdjustmentFrame,
67
TextInformationFrame, UniqueFileIdentifierFrame, UnsynchronizedTextFrame, UrlLinkFrame,
78
};
8-
use crate::id3::v2::Id3v2Version;
99
use crate::macros::err;
1010
use crate::probe::ParsingMode;
1111
use crate::util::text::TextEncoding;

src/id3/v2/frame/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ mod header;
33
pub(super) mod id;
44
pub(super) mod read;
55

6+
use super::header::Id3v2Version;
67
use super::items::{
78
AttachedPictureFrame, CommentFrame, EventTimingCodesFrame, ExtendedTextFrame, ExtendedUrlFrame,
89
KeyValueFrame, OwnershipFrame, Popularimeter, PrivateFrame, RelativeVolumeAdjustmentFrame,
910
TextInformationFrame, UniqueFileIdentifierFrame, UnsynchronizedTextFrame, UrlLinkFrame,
1011
};
1112
use super::util::upgrade::{upgrade_v2, upgrade_v3};
12-
use super::Id3v2Version;
1313
use crate::error::{ErrorKind, Id3v2Error, Id3v2ErrorKind, LoftyError, Result};
1414
use crate::tag::item::{ItemKey, ItemValue, TagItem};
1515
use crate::tag::TagType;

src/id3/v2/frame/read.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use super::header::{parse_header, parse_v2_header};
22
use super::Frame;
33
use crate::error::{Id3v2Error, Id3v2ErrorKind, Result};
44
use crate::id3::v2::frame::content::parse_content;
5+
use crate::id3::v2::header::Id3v2Version;
56
use crate::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream};
6-
use crate::id3::v2::{FrameFlags, FrameId, FrameValue, Id3v2Version};
7+
use crate::id3::v2::{FrameFlags, FrameId, FrameValue};
78
use crate::macros::try_vec;
89
use crate::probe::ParsingMode;
910

src/id3/v2/header.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use crate::error::{Id3v2Error, Id3v2ErrorKind, Result};
2+
use crate::id3::v2::restrictions::TagRestrictions;
3+
use crate::id3::v2::util::synchsafe::SynchsafeInteger;
4+
use crate::macros::err;
5+
6+
use std::io::Read;
7+
8+
use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
9+
10+
/// The ID3v2 version
11+
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
12+
pub enum Id3v2Version {
13+
/// ID3v2.2
14+
V2,
15+
/// ID3v2.3
16+
V3,
17+
/// ID3v2.4
18+
V4,
19+
}
20+
21+
/// Flags that apply to the entire tag
22+
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
23+
#[allow(clippy::struct_excessive_bools)]
24+
pub struct Id3v2TagFlags {
25+
/// Whether or not all frames are unsynchronised. See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation)
26+
pub unsynchronisation: bool,
27+
/// Indicates if the tag is in an experimental stage
28+
pub experimental: bool,
29+
/// Indicates that the tag includes a footer
30+
///
31+
/// A footer will be created if the tag is written
32+
pub footer: bool,
33+
/// Whether or not to include a CRC-32 in the extended header
34+
///
35+
/// This is calculated if the tag is written
36+
pub crc: bool,
37+
/// Restrictions on the tag, written in the extended header
38+
///
39+
/// In addition to being setting this flag, all restrictions must be provided. See [`TagRestrictions`]
40+
pub restrictions: Option<TagRestrictions>,
41+
}
42+
43+
#[derive(Copy, Clone, Debug)]
44+
pub(crate) struct Id3v2Header {
45+
pub version: Id3v2Version,
46+
pub flags: Id3v2TagFlags,
47+
pub size: u32,
48+
pub extended_size: u32,
49+
}
50+
51+
impl Id3v2Header {
52+
pub(crate) fn parse<R>(bytes: &mut R) -> Result<Self>
53+
where
54+
R: Read,
55+
{
56+
let mut header = [0; 10];
57+
bytes.read_exact(&mut header)?;
58+
59+
if &header[..3] != b"ID3" {
60+
err!(FakeTag);
61+
}
62+
63+
// Version is stored as [major, minor], but here we don't care about minor revisions unless there's an error.
64+
let version = match header[3] {
65+
2 => Id3v2Version::V2,
66+
3 => Id3v2Version::V3,
67+
4 => Id3v2Version::V4,
68+
major => {
69+
return Err(
70+
Id3v2Error::new(Id3v2ErrorKind::BadId3v2Version(major, header[4])).into(),
71+
)
72+
},
73+
};
74+
75+
let flags = header[5];
76+
77+
// Compression was a flag only used in ID3v2.2 (bit 2).
78+
// At the time the ID3v2.2 specification was written, a compression scheme wasn't decided.
79+
// The spec recommends just ignoring the tag in this case.
80+
if version == Id3v2Version::V2 && flags & 0x40 == 0x40 {
81+
return Err(Id3v2Error::new(Id3v2ErrorKind::V2Compression).into());
82+
}
83+
84+
let mut flags_parsed = Id3v2TagFlags {
85+
unsynchronisation: flags & 0x80 == 0x80,
86+
experimental: (version == Id3v2Version::V4 || version == Id3v2Version::V3)
87+
&& flags & 0x20 == 0x20,
88+
footer: (version == Id3v2Version::V4 || version == Id3v2Version::V3)
89+
&& flags & 0x10 == 0x10,
90+
crc: false, // Retrieved later if applicable
91+
restrictions: None, // Retrieved later if applicable
92+
};
93+
94+
let size = BigEndian::read_u32(&header[6..]).unsynch();
95+
let mut extended_size = 0;
96+
97+
let extended_header =
98+
(version == Id3v2Version::V4 || version == Id3v2Version::V3) && flags & 0x40 == 0x40;
99+
100+
if extended_header {
101+
extended_size = bytes.read_u32::<BigEndian>()?.unsynch();
102+
103+
if extended_size < 6 {
104+
return Err(Id3v2Error::new(Id3v2ErrorKind::BadExtendedHeaderSize).into());
105+
}
106+
107+
// Useless byte since there's only 1 byte for flags
108+
let _num_flag_bytes = bytes.read_u8()?;
109+
110+
let extended_flags = bytes.read_u8()?;
111+
112+
// The only flags we care about here are the CRC and restrictions
113+
114+
if extended_flags & 0x20 == 0x20 {
115+
flags_parsed.crc = true;
116+
117+
// We don't care about the existing CRC (5) or its length byte (1)
118+
let mut crc = [0; 6];
119+
bytes.read_exact(&mut crc)?;
120+
}
121+
122+
if extended_flags & 0x10 == 0x10 {
123+
// We don't care about the length byte, it is always 1
124+
let _data_length = bytes.read_u8()?;
125+
126+
flags_parsed.restrictions = Some(TagRestrictions::from_byte(bytes.read_u8()?));
127+
}
128+
}
129+
130+
if extended_size > 0 && extended_size >= size {
131+
return Err(Id3v2Error::new(Id3v2ErrorKind::BadExtendedHeaderSize).into());
132+
}
133+
134+
Ok(Id3v2Header {
135+
version,
136+
flags: flags_parsed,
137+
size,
138+
extended_size,
139+
})
140+
}
141+
}

src/id3/v2/items/attached_picture_frame.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::error::{Id3v2Error, Id3v2ErrorKind, Result};
2-
use crate::id3::v2::Id3v2Version;
2+
use crate::id3::v2::header::Id3v2Version;
33
use crate::macros::err;
44
use crate::picture::{MimeType, Picture, PictureType};
55
use crate::util::text::{encode_text, TextEncoding};

src/id3/v2/items/extended_text_frame.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::error::{Id3v2Error, Id3v2ErrorKind, LoftyError, Result};
22
use crate::id3::v2::frame::content::verify_encoding;
3-
use crate::id3::v2::Id3v2Version;
3+
use crate::id3::v2::header::Id3v2Version;
44
use crate::util::text::{decode_text, encode_text, read_to_terminator, utf16_decode, TextEncoding};
55

66
use std::hash::{Hash, Hasher};

src/id3/v2/items/extended_url_frame.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::error::Result;
22
use crate::id3::v2::frame::content::verify_encoding;
3-
use crate::id3::v2::Id3v2Version;
3+
use crate::id3::v2::header::Id3v2Version;
44
use crate::util::text::{decode_text, encode_text, TextEncoding};
55

66
use std::hash::{Hash, Hasher};

0 commit comments

Comments
 (0)