///|
fn validate_picture(picture : Picture) -> Unit raise FlacError {
guard picture.kind >= 0 && picture.kind <= 20 else {
raise Invalid(0, "reserved picture type")
}
guard !picture.media_type.is_empty() else {
raise Invalid(0, "empty picture media type")
}
for c in picture.media_type.code_units() {
guard c.to_int() >= 0x20 && c.to_int() <= 0x7e else {
raise Invalid(0, "picture media type must be printable ASCII")
}
}
if picture.kind == 1 {
guard picture.media_type == "image/png" &&
picture.width == 32U &&
picture.height == 32U else {
raise Invalid(0, "type 1 icon requires 32x32 image/png")
}
}
}
///|
pub fn decode_picture(data : Bytes) -> Picture raise FlacError {
check_block_size(data.length())
let r = { data, pos: 0, }
let kind = r.u32()
guard kind <= 20U else { raise Invalid(0, "reserved picture type") }
let media_type = text(r.sized())
let description = text(r.sized())
let width = r.u32()
let height = r.u32()
let depth = r.u32()
let colors = r.u32()
let image = r.sized()
guard r.pos == data.length() else {
raise Invalid(r.pos, "trailing bytes in picture")
}
let picture = {
kind: kind.reinterpret_as_int(),
media_type,
description,
width,
height,
depth,
colors,
data: image,
}
validate_picture(picture)
picture
}
///|
pub fn encode_picture(picture : Picture) -> Bytes raise FlacError {
validate_picture(picture)
let mime = text_bytes(picture.media_type)
let description = text_bytes(picture.description)
guard mime.length() <= MaxBlock - 32 &&
description.length() <= MaxBlock - 32 - mime.length() &&
picture.data.length() <=
MaxBlock - 32 - mime.length() - description.length() else {
raise Limit("picture exceeds metadata block limit")
}
let out = @buffer.Buffer()
out.write_uint_be(picture.kind.reinterpret_as_uint())
put_sized(out, mime)
put_sized(out, description)
out.write_uint_be(picture.width)
out.write_uint_be(picture.height)
out.write_uint_be(picture.depth)
out.write_uint_be(picture.colors)
put_sized(out, picture.data)
out.to_bytes()
}