Files
id3_editor/metadata/frame.v
T
2026-10-07 23:03:27 +03:00

154 lines
4.7 KiB
V
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}