mirror of
https://github.com/storytold/soundcraft.git
synced 2026-11-01 06:26:51 -06:00
This commit is contained in:
@@ -50,13 +50,25 @@ fn parse_fmt(c: &[u8]) -> Result<Fmt> {
|
||||
fmt.valid_bits = fmt.bits;
|
||||
if fmt.tag == WAVE_FORMAT_EXTENSIBLE {
|
||||
// cbSize(2) validBits(2) channelMask(4) subFormat GUID(16)
|
||||
if let (Some(valid), Some(sub)) = (le_u16(c, 18), le_u16(c, 24)) {
|
||||
if valid != 0 {
|
||||
fmt.valid_bits = valid;
|
||||
}
|
||||
fmt.tag = sub;
|
||||
} else {
|
||||
let (Some(cb_size), Some(valid), Some(sub)) = (le_u16(c, 16), le_u16(c, 18), le_u16(c, 24)) else {
|
||||
return Err(AudioError::Malformed("WAVE_FORMAT_EXTENSIBLE fmt chunk too short".into()));
|
||||
};
|
||||
let Some(tail) = c.get(26..40) else {
|
||||
return Err(AudioError::Malformed("WAVE_FORMAT_EXTENSIBLE fmt chunk too short".into()));
|
||||
};
|
||||
if cb_size < 22 {
|
||||
return Err(AudioError::Malformed("WAVE_FORMAT_EXTENSIBLE cbSize is below 22".into()));
|
||||
}
|
||||
if valid > fmt.bits {
|
||||
return Err(AudioError::Malformed("WAVE_FORMAT_EXTENSIBLE valid bits exceed the container size".into()));
|
||||
}
|
||||
if valid != 0 {
|
||||
fmt.valid_bits = valid;
|
||||
}
|
||||
// A subtype outside the KSDATAFORMAT_SUBTYPE_* family is not PCM/float, whatever its first two bytes say:
|
||||
// keep the extensible tag so the file goes to the foreign decoder.
|
||||
if tail == GUID_TAIL {
|
||||
fmt.tag = sub;
|
||||
}
|
||||
}
|
||||
Ok(fmt)
|
||||
|
||||
@@ -511,3 +511,37 @@ proptest! {
|
||||
prop_assert_eq!(flac, wav);
|
||||
}
|
||||
}
|
||||
|
||||
/// An extensible `fmt ` chunk: cbSize, valid bits, mask, then the 16-byte subtype GUID.
|
||||
fn extensible_fmt(cb_size: u16, valid_bits: u16, guid: &[u8]) -> Vec<u8> {
|
||||
let mut f = fmt(0xFFFE, 1, 44_100, 16);
|
||||
f.extend_from_slice(&cb_size.to_le_bytes());
|
||||
f.extend_from_slice(&valid_bits.to_le_bytes());
|
||||
f.extend_from_slice(&4u32.to_le_bytes());
|
||||
f.extend_from_slice(guid);
|
||||
f
|
||||
}
|
||||
|
||||
const PCM_GUID: [u8; 16] = [1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xAA, 0, 0x38, 0x9B, 0x71];
|
||||
|
||||
#[test]
|
||||
fn extensible_fmt_is_checked_in_full() {
|
||||
let data = vec![0u8; 8];
|
||||
let good = riff(&[(b"fmt ", extensible_fmt(22, 16, &PCM_GUID)), (b"data", data.clone())]);
|
||||
assert_eq!(probe(&good, None).unwrap().sample_format, SampleFormat::Int16);
|
||||
// Subtype GUID cut short after the format code.
|
||||
let cut = riff(&[(b"fmt ", extensible_fmt(22, 16, &PCM_GUID[..4])), (b"data", data.clone())]);
|
||||
assert!(matches!(probe(&cut, None), Err(AudioError::Malformed(_))));
|
||||
assert!(matches!(decode(&cut, None), Err(AudioError::Malformed(_))));
|
||||
// cbSize too small for the extension.
|
||||
let small_cb = riff(&[(b"fmt ", extensible_fmt(0, 16, &PCM_GUID)), (b"data", data.clone())]);
|
||||
assert!(matches!(probe(&small_cb, None), Err(AudioError::Malformed(_))));
|
||||
// More valid bits than the 16-bit container holds.
|
||||
let wide = riff(&[(b"fmt ", extensible_fmt(22, 24, &PCM_GUID)), (b"data", data.clone())]);
|
||||
assert!(matches!(probe(&wide, None), Err(AudioError::Malformed(_))));
|
||||
// PCM-like prefix but a foreign GUID tail is not read as native PCM.
|
||||
let mut foreign = PCM_GUID;
|
||||
foreign[15] ^= 0xFF;
|
||||
let foreign = riff(&[(b"fmt ", extensible_fmt(22, 16, &foreign)), (b"data", data)]);
|
||||
assert!(!matches!(probe(&foreign, None), Ok(i) if i.sample_format == SampleFormat::Int16));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user