module metadata import encoding.iconv @[heap] pub struct Frame { pub mut: data string mut: prev_size u64 cur_size u64 start u64 end u64 encoding_name string name string encoding_name_idx u8 addition []u8 encoded_data []u8 apic ?[]u8 encoded_size []u8 modified bool } // Во фрейме первый байт указывает кодировку 0 - ISO-8859-1 (Latin-1), 1 - UTF-16LE @[inline] fn (mut frame Frame) fill(bytes []u8, start_position u64, idv3_version u8) { // Начало фрейма frame.start = start_position // Название фрейма frame.name = bytes[frame.start..frame.start + 4].bytestr() // Размер фрейма frame.cur_size = if idv3_version == 3 { get_size_v23(bytes[frame.start + 4..frame.start + 8]) } else { get_size_v24(bytes[frame.start + 4..frame.start + 8]) } // Конец фрейма frame.end = frame.start + frame.cur_size + 10 // Содержимое фрейма frame.encoding_name = match bytes[frame.start + 10] { 0 { "ISO-8859-1" } 1 { "UTF-16LE" } 2 { "UTF-16BE" } else { "UTF-8" } } frame.encoding_name_idx = u8(bytes[frame.start + 10]) if frame.name.to_lower() == "apic" { // Дополнительные байты с информацией (MIME) frame.addition = bytes[frame.start + 10..frame.start + 24] // Фрейм целиком вместе с заголовком, чтобы записать его обратно без изменений frame.apic = bytes[frame.start..frame.end] } else { // Данные начинаются после 10 байт заголовка и байта кодировки mut data_start := frame.start + 11 // UTF-16 с BOM (01): 2 байта маркера, по ним определяется порядок байт if frame.encoding_name_idx == 1 && data_start + 2 <= frame.end { if bytes[data_start] == 0xFE && bytes[data_start + 1] == 0xFF { frame.encoding_name = "UTF-16BE" } data_start += 2 } // Замыкающие нули не относятся к данным frame.data = (iconv.encoding_to_vstring(bytes[data_start..frame.end], frame.encoding_name) or { "" }).trim_right('\x00') frame.addition = bytes[frame.start..frame.start + 10] } } pub fn (mut frame Frame) init(tag_name string) { frame.name = tag_name.to_upper() frame.encoding_name = "UTF-8" frame.encoding_name_idx = 3 } // Кодируем длину фрейма с нужным сдвигом @[inline] pub fn (mut frame Frame) encode_size(idv3_version u8) { stride := if idv3_version == 3 { [u64(24), u64(16), u64(8), u64(0xFF)] } else { [u64(21), u64(14), u64(7), u64(0x7F)] } mut encoded_size := []u8{len: 4} encoded_size[0] = u8((frame.cur_size >> stride[0]) & stride[3]) encoded_size[1] = u8((frame.cur_size >> stride[1]) & stride[3]) encoded_size[2] = u8((frame.cur_size >> stride[2]) & stride[3]) encoded_size[3] = u8(frame.cur_size & stride[3]) frame.encoded_size = encoded_size } // Кодируем кадр для дальнейшей записи pub fn (mut frame Frame) encode_frame(idv3_version u8) { encoded_data := iconv.vstring_to_encoding(frame.data, frame.encoding_name) or { []u8{} } frame.encoded_data = encoded_data // Размер тела фрейма: байт кодировки + данные + замыкающие нули frame.cur_size = u64(1 + frame.encoded_data.len + frame.terminator_len()) // число -> [байт, байт, байт, байт] frame.encode_size(idv3_version) } // Для UTF-16 строка завершается двумя нулями, для остальных кодировок одним @[inline] fn (frame Frame) terminator_len() int { return if frame.encoding_name_idx == 1 || frame.encoding_name_idx == 2 { 2 } else { 1 } } // Получить закодированый тэг pub fn (mut frame Frame) get_encoded_frame_tag(idv3_version u8) []u8 { frame.encode_frame(idv3_version) // Название тэга mut encoded_tag := frame.name.bytes() // Четыре числа на закодированный размер encoded_tag << frame.encoded_size // Флаги, оставляем пустыми encoded_tag << []u8{len: 2} // Идекс кодировщика текста encoded_tag << frame.encoding_name_idx // Уже сами данные encoded_tag << frame.encoded_data // Замыкающие нули encoded_tag << []u8{len: frame.terminator_len()} return encoded_tag } // Запись данных в тэг @[inline] pub fn (mut frame Frame) set_data(data string) { if frame.data.len != 0 { frame.prev_size = u64(frame.data.len) } else { mut b_data := data.bytes() b_data << []u8{len: 1} frame.prev_size = u64(b_data.len) frame.cur_size = u64(b_data.len) } frame.data = data frame.modified = true }