static gboolean decode_rva_block (const guchar * * _data, gint * _size, gint * channel, gint * adjustment, gint * adjustment_unit, gint * peak, gint * peak_unit) { const guchar * data = * _data; gint size = * _size; gint peak_bits; if (size < 4) return FALSE; * channel = data[0]; * adjustment = (gchar) data[1]; /* first byte is signed */ * adjustment = (* adjustment << 8) | data[2]; * adjustment_unit = 512; peak_bits = data[3]; data += 4; size -= 4; TAGDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n", * channel, * adjustment, * adjustment_unit, peak_bits); if (peak_bits > 0 && peak_bits < sizeof (gint) * 8) { gint bytes = (peak_bits + 7) / 8; gint count; if (bytes > size) return FALSE; * peak = 0; * peak_unit = 1 << peak_bits; for (count = 0; count < bytes; count ++) * peak = (* peak << 8) | data[count]; data += bytes; size -= count; TAGDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit); } else { * peak = 0; * peak_unit = 0; } * _data = data; * _size = size; return TRUE; }
static gboolean read_header (VFSFile * handle, gint * version, gboolean * syncsafe, gsize * offset, gint * header_size, gint * data_size) { ID3v2Header header; if (vfs_fseek (handle, 0, SEEK_SET)) return FALSE; if (vfs_fread (& header, 1, sizeof (ID3v2Header), handle) != sizeof (ID3v2Header)) return FALSE; if (validate_header (& header)) { * offset = 0; * version = header.version; * header_size = sizeof (ID3v2Header); * data_size = header.size; } else return FALSE; * syncsafe = (header.flags & ID3_HEADER_SYNCSAFE) ? TRUE : FALSE; TAGDBG ("Offset = %d, header size = %d, data size = %d\n", (gint) * offset, * header_size, * data_size); return TRUE; }
static void read_all_frames (VFSFile * handle, gint version, gboolean syncsafe, gint data_size, mowgli_dictionary_t * dict) { gint pos; for (pos = 0; pos < data_size; ) { gint frame_size, size; gchar key[5]; guchar * data; GenericFrame * frame; if (! read_frame (handle, data_size - pos, version, syncsafe, & frame_size, key, & data, & size)) break; pos += frame_size; if (mowgli_dictionary_retrieve (dict, key) != NULL) { TAGDBG ("Discarding duplicate frame %s.\n", key); g_free (data); continue; } frame = g_malloc (sizeof (GenericFrame)); strcpy (frame->key, key); frame->data = data; frame->size = size; mowgli_dictionary_add (dict, key, frame); } }
static void decode_rva (Tuple * tuple, const guchar * data, gint size) { const gchar * domain; gint channel, adjustment, adjustment_unit, peak, peak_unit; if (memchr (data, 0, size) == NULL) return; domain = (const gchar *) data; TAGDBG ("RVA domain: %s\n", domain); size -= strlen (domain) + 1; data += strlen (domain) + 1; while (size > 0) { if (! decode_rva_block (& data, & size, & channel, & adjustment, & adjustment_unit, & peak, & peak_unit)) break; if (channel != 1) /* specific channel? */ continue; if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT, NULL) == TUPLE_INT) adjustment = adjustment * (gint64) tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL) / adjustment_unit; else tuple_associate_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL, adjustment_unit); if (peak_unit) { if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT, NULL) == TUPLE_INT) peak = peak * (gint64) tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL) / peak_unit; else tuple_associate_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL, peak_unit); } if (! strcasecmp (domain, "album")) { tuple_associate_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL, adjustment); if (peak_unit) tuple_associate_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL, peak); } else if (! strcasecmp (domain, "track")) { tuple_associate_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL, adjustment); if (peak_unit) tuple_associate_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL, peak); } } }
static gint writeAllFramesToFile (VFSFile * fd, mowgli_dictionary_t * dict) { WriteState state = {fd, 0}; mowgli_dictionary_foreach (dict, write_frame_cb, & state); TAGDBG ("Total frame bytes written = %d.\n", state.written_size); return state.written_size; }
static int write_all_frames (VFSFile * handle, GHashTable * dict) { WriteState state = {handle, 0}; g_hash_table_foreach (dict, write_frame_list, & state); TAGDBG ("Total frame bytes written = %d.\n", state.written_size); return state.written_size; }
static gboolean read_frame (VFSFile * handle, gint max_size, gint version, gboolean syncsafe, gint * frame_size, gchar * key, guchar * * data, gint * size) { ID3v2FrameHeader header; gint i; guint32 hdrsz = 0; if ((max_size -= sizeof (ID3v2FrameHeader)) < 0) return FALSE; if (vfs_fread (& header, 1, sizeof (ID3v2FrameHeader), handle) != sizeof (ID3v2FrameHeader)) return FALSE; if (! header.key[0]) /* padding */ return FALSE; for (i = 0; i < 3; i++) { hdrsz |= (guint32) header.size[i] << ((2 - i) * 8); TAGDBG("header.size[%d] = %d hdrsz %d slot %d\n", i, header.size[i], hdrsz, 2 - i); } // hdrsz = GUINT32_TO_BE(hdrsz); if (hdrsz > max_size || hdrsz == 0) return FALSE; TAGDBG ("Found frame:\n"); TAGDBG (" key = %.3s\n", header.key); TAGDBG (" size = %d\n", (gint) hdrsz); * frame_size = sizeof (ID3v2FrameHeader) + hdrsz; sprintf (key, "%.3s", header.key); * size = hdrsz; * data = g_malloc (* size); if (vfs_fread (* data, 1, * size, handle) != * size) return FALSE; TAGDBG ("Data size = %d.\n", * size); return TRUE; }
static void associate_int (Tuple * tuple, gint field, const gchar * customfield, const guchar * data, gint size) { gchar * text = decode_text_frame (data, size); if (text == NULL || atoi (text) < 1) { g_free (text); return; } if (customfield != NULL) TAGDBG ("Custom field %s = %s.\n", customfield, text); else TAGDBG ("Field %i = %s.\n", field, text); tuple_associate_int (tuple, field, customfield, atoi (text)); g_free (text); }
static void remove_frame (gint id, mowgli_dictionary_t * dict) { GenericFrame * frame = mowgli_dictionary_retrieve (dict, id3_frames[id]); if (frame == NULL) return; TAGDBG ("Deleting frame %s.\n", id3_frames[id]); mowgli_dictionary_delete (dict, id3_frames[id]); free_generic_frame (frame); }
static void associate_string (Tuple * tuple, int field, const char * customfield, const unsigned char * data, int size) { char * text = decode_text_frame (data, size); if (text == NULL || ! text[0]) { g_free (text); return; } if (customfield != NULL) TAGDBG ("Custom field %s = %s.\n", customfield, text); else TAGDBG ("Field %i = %s.\n", field, text); tuple_set_str (tuple, field, customfield, text); g_free (text); }
static void decode_private_info (Tuple * tuple, const unsigned char * data, int size) { char * text = g_strndup ((const char *) data, size); if (!strncmp(text, "WM/", 3)) { char *separator = strchr(text, 0); if (separator == NULL) goto DONE; char * value = separator + 1; if (!strncmp(text, "WM/MediaClassPrimaryID", 22)) { if (!memcmp(value, PRIMARY_CLASS_MUSIC, 16)) tuple_set_str (tuple, -1, "media-class", "Music"); if (!memcmp(value, PRIMARY_CLASS_AUDIO, 16)) tuple_set_str (tuple, -1, "media-class", "Audio (non-music)"); } else if (!strncmp(text, "WM/MediaClassSecondaryID", 24)) { if (!memcmp(value, SECONDARY_CLASS_AUDIOBOOK, 16)) tuple_set_str (tuple, -1, "media-class", "Audio Book"); if (!memcmp(value, SECONDARY_CLASS_SPOKENWORD, 16)) tuple_set_str (tuple, -1, "media-class", "Spoken Word"); if (!memcmp(value, SECONDARY_CLASS_NEWS, 16)) tuple_set_str (tuple, -1, "media-class", "News"); if (!memcmp(value, SECONDARY_CLASS_TALKSHOW, 16)) tuple_set_str (tuple, -1, "media-class", "Talk Show"); if (!memcmp(value, SECONDARY_CLASS_GAMES_CLIP, 16)) tuple_set_str (tuple, -1, "media-class", "Game Audio (clip)"); if (!memcmp(value, SECONDARY_CLASS_GAMES_SONG, 16)) tuple_set_str (tuple, -1, "media-class", "Game Soundtrack"); } else { TAGDBG("Unrecognised tag %s (Windows Media) ignored\n", text); } } else { TAGDBG("Unable to decode private data, skipping: %s\n", text); } DONE: g_free (text); }
static bool_t read_frame (VFSFile * handle, int max_size, int version, bool_t syncsafe, int * frame_size, char * key, char * * data, int * size) { ID3v2FrameHeader header; int skip = 0; if ((max_size -= sizeof (ID3v2FrameHeader)) < 0) return FALSE; if (vfs_fread (& header, 1, sizeof (ID3v2FrameHeader), handle) != sizeof (ID3v2FrameHeader)) return FALSE; if (! header.key[0]) /* padding */ return FALSE; header.size = (version == 3) ? GUINT32_FROM_BE (header.size) : unsyncsafe32 (GUINT32_FROM_BE (header.size)); header.flags = GUINT16_FROM_BE (header.flags); if (header.size > max_size || header.size == 0) return FALSE; TAGDBG ("Found frame:\n"); TAGDBG (" key = %.4s\n", header.key); TAGDBG (" size = %d\n", (int) header.size); TAGDBG (" flags = %x\n", (int) header.flags); * frame_size = sizeof (ID3v2FrameHeader) + header.size; g_strlcpy (key, header.key, 5); if (header.flags & (ID3_FRAME_COMPRESSED | ID3_FRAME_ENCRYPTED)) { TAGDBG ("Hit compressed/encrypted frame %s.\n", key); return FALSE; } if (header.flags & ID3_FRAME_HAS_GROUP) skip ++; if (header.flags & ID3_FRAME_HAS_LENGTH) skip += 4; if ((skip > 0 && vfs_fseek (handle, skip, SEEK_CUR)) || skip >= header.size) return FALSE; * size = header.size - skip; * data = g_malloc (* size); if (vfs_fread (* data, 1, * size, handle) != * size) return FALSE; if (syncsafe || (header.flags & ID3_FRAME_SYNCSAFE)) * size = unsyncsafe (* data, * size); TAGDBG ("Data size = %d.\n", * size); return TRUE; }
static void add_text_frame (int id, const char * text, GHashTable * dict) { if (text == NULL) { remove_frame (id, dict); return; } TAGDBG ("Adding text frame %s = %s.\n", id3_frames[id], text); int length = strlen (text); GenericFrame * frame = add_generic_frame (id, length + 1, dict); frame->data[0] = 3; /* UTF-8 encoding */ memcpy (frame->data + 1, text, length); }
static void decode_comment (Tuple * tuple, const guchar * data, gint size) { gchar * lang, * type, * value; if (! decode_comment_frame (data, size, & lang, & type, & value)) return; TAGDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value); if (! type[0]) /* blank type == actual comment */ tuple_associate_string (tuple, FIELD_COMMENT, NULL, value); g_free (lang); g_free (type); g_free (value); }
static void add_comment_frame (const char * text, GHashTable * dict) { if (text == NULL) { remove_frame (ID3_COMMENT, dict); return; } TAGDBG ("Adding comment frame = %s.\n", text); int length = strlen (text); GenericFrame * frame = add_generic_frame (ID3_COMMENT, length + 5, dict); frame->data[0] = 3; /* UTF-8 encoding */ strcpy ((char *) frame->data + 1, "eng"); /* well, it *might* be English */ memcpy (frame->data + 5, text, length); }
static bool_t skip_extended_header_4 (VFSFile * handle, int * _size) { uint32_t size; if (vfs_fread (& size, 1, 4, handle) != 4) return FALSE; size = unsyncsafe32 (GUINT32_FROM_BE (size)); TAGDBG ("Found v2.4 extended header, size = %d.\n", (int) size); if (vfs_fseek (handle, size - 4, SEEK_CUR)) return FALSE; * _size = size; return TRUE; }
static gboolean skip_extended_header_3 (VFSFile * handle, gint * _size) { guint32 size; if (vfs_fread (& size, 1, 4, handle) != 4) return FALSE; size = GUINT32_FROM_BE (size); TAGDBG ("Found v2.3 extended header, size = %d.\n", (gint) size); if (vfs_fseek (handle, size, SEEK_CUR)) return FALSE; * _size = 4 + size; return TRUE; }
static void decode_txx (Tuple * tuple, const guchar * data, gint size) { gchar * text = decode_text_frame (data, size); if (text == NULL) return; gchar *separator = strchr(text, 0); if (separator == NULL) return; gchar * value = separator + 1; TAGDBG ("TXX: %s = %s.\n", text, value); tuple_associate_string (tuple, -1, text, value); g_free (text); }
static gboolean parse_pic (const guchar * data, gint size, gchar * * mime, gint * type, void * * image_data, gint * image_size) { const guchar * sep; const guchar * after; if (size < 2 || (sep = memchr (data + 1, 0, size - 2)) == NULL) return FALSE; after = sep + 2; * mime = g_strdup ((const gchar *) data + 1); * type = sep[1]; * image_data = g_memdup (after, data + size - after); * image_size = data + size - after; TAGDBG ("PIC: mime = %s, type = %d, size = %d.\n", * mime, * type, * image_size); return TRUE; }
static gboolean parse_apic (const guchar * _data, gint size, gchar * * mime, gint * type, gchar * * desc, void * * image_data, gint * image_size) { const gchar * data = (const gchar *) _data; const gchar * sep, * after; if (size < 2 || (sep = memchr (data + 1, 0, size - 2)) == NULL) return FALSE; if ((* desc = convert_text (sep + 2, data + size - sep - 2, data[0], TRUE, NULL, & after)) == NULL) return FALSE; * mime = g_strdup (data + 1); * type = sep[1]; * image_data = g_memdup (after, data + size - after); * image_size = data + size - after; TAGDBG ("APIC: mime = %s, type = %d, desc = %s, size = %d.\n", * mime, * type, * desc, * image_size); return TRUE; }
static bool_t write_frame (VFSFile * handle, GenericFrame * frame, int * frame_size) { TAGDBG ("Writing frame %s, size %d\n", frame->key, frame->size); ID3v2FrameHeader header; memcpy (header.key, frame->key, 4); header.size = syncsafe32 (frame->size); header.size = GUINT32_TO_BE (header.size); header.flags = 0; if (vfs_fwrite (& header, 1, sizeof (ID3v2FrameHeader), handle) != sizeof (ID3v2FrameHeader)) return FALSE; if (vfs_fwrite (frame->data, 1, frame->size, handle) != frame->size) return FALSE; * frame_size = sizeof (ID3v2FrameHeader) + frame->size; return TRUE; }
static bool_t validate_header (ID3v2Header * header, bool_t is_footer) { if (memcmp (header->magic, is_footer ? "3DI" : "ID3", 3)) return FALSE; if ((header->version != 3 && header->version != 4) || header->revision != 0) return FALSE; header->size = unsyncsafe32 (GUINT32_FROM_BE (header->size)); TAGDBG ("Found ID3v2 %s:\n", is_footer ? "footer" : "header"); TAGDBG (" magic = %.3s\n", header->magic); TAGDBG (" version = %d\n", (int) header->version); TAGDBG (" revision = %d\n", (int) header->revision); TAGDBG (" flags = %x\n", (int) header->flags); TAGDBG (" size = %d\n", (int) header->size); return TRUE; }
static gboolean validate_header (ID3v2Header * header) { if (memcmp (header->magic, "ID3", 3)) return FALSE; if ((header->version != 2)) return FALSE; header->size = unsyncsafe32(GUINT32_FROM_BE(header->size)); TAGDBG ("Found ID3v2 header:\n"); TAGDBG (" magic = %.3s\n", header->magic); TAGDBG (" version = %d\n", (gint) header->version); TAGDBG (" revision = %d\n", (gint) header->revision); TAGDBG (" flags = %x\n", (gint) header->flags); TAGDBG (" size = %d\n", (gint) header->size); return TRUE; }
gboolean id3v22_read_tag (Tuple * tuple, VFSFile * handle) { gint version, header_size, data_size; gboolean syncsafe; gsize offset; gint pos; if (! read_header (handle, & version, & syncsafe, & offset, & header_size, & data_size)) return FALSE; TAGDBG("Reading tags from %i bytes of ID3 data in %s\n", data_size, handle->uri); for (pos = 0; pos < data_size; ) { gint frame_size, size, id; gchar key[5]; guchar * data; if (! read_frame (handle, data_size - pos, version, syncsafe, & frame_size, key, & data, & size)) { TAGDBG("read_frame failed at pos %i\n", pos); break; } id = get_frame_id (key); switch (id) { case ID3_ALBUM: associate_string (tuple, FIELD_ALBUM, NULL, data, size); break; case ID3_TITLE: associate_string (tuple, FIELD_TITLE, NULL, data, size); break; case ID3_COMPOSER: associate_string (tuple, FIELD_COMPOSER, NULL, data, size); break; case ID3_COPYRIGHT: associate_string (tuple, FIELD_COPYRIGHT, NULL, data, size); break; case ID3_DATE: associate_string (tuple, FIELD_DATE, NULL, data, size); break; case ID3_LENGTH: associate_int (tuple, FIELD_LENGTH, NULL, data, size); break; case ID3_FUCKO_ARTIST: case ID3_ARTIST: associate_string (tuple, FIELD_ARTIST, NULL, data, size); break; case ID3_TRACKNR: associate_int (tuple, FIELD_TRACK_NUMBER, NULL, data, size); break; case ID3_YEAR: associate_int (tuple, FIELD_YEAR, NULL, data, size); break; case ID3_GENRE: decode_genre (tuple, data, size); break; case ID3_COMMENT: decode_comment (tuple, data, size); break; case ID3_ENCODER: associate_string (tuple, -1, "encoder", data, size); break; case ID3_TXX: decode_txx (tuple, data, size); break; case ID3_RVA: decode_rva (tuple, data, size); break; default: TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key); break; } g_free (data); pos += frame_size; } return TRUE; }
static gboolean id3v24_read_tag (Tuple * tuple, VFSFile * handle) { gint version, header_size, data_size, footer_size; gboolean syncsafe; gint64 offset; gint pos; if (! read_header (handle, & version, & syncsafe, & offset, & header_size, & data_size, & footer_size)) return FALSE; for (pos = 0; pos < data_size; ) { gint frame_size, size, id; gchar key[5]; guchar * data; if (! read_frame (handle, data_size - pos, version, syncsafe, & frame_size, key, & data, & size)) break; id = get_frame_id (key); switch (id) { case ID3_ALBUM: associate_string (tuple, FIELD_ALBUM, NULL, data, size); break; case ID3_TITLE: associate_string (tuple, FIELD_TITLE, NULL, data, size); break; case ID3_COMPOSER: associate_string (tuple, FIELD_COMPOSER, NULL, data, size); break; case ID3_COPYRIGHT: associate_string (tuple, FIELD_COPYRIGHT, NULL, data, size); break; case ID3_DATE: associate_string (tuple, FIELD_DATE, NULL, data, size); break; case ID3_TIME: associate_int (tuple, FIELD_LENGTH, NULL, data, size); break; case ID3_LENGTH: associate_int (tuple, FIELD_LENGTH, NULL, data, size); break; case ID3_ARTIST: associate_string (tuple, FIELD_ARTIST, NULL, data, size); break; case ID3_TRACKNR: associate_int (tuple, FIELD_TRACK_NUMBER, NULL, data, size); break; case ID3_YEAR: case ID3_RECORDING_TIME: associate_int (tuple, FIELD_YEAR, NULL, data, size); break; case ID3_GENRE: decode_genre (tuple, data, size); break; case ID3_COMMENT: decode_comment (tuple, data, size); break; case ID3_PRIVATE: decode_private_info (tuple, data, size); break; case ID3_ENCODER: associate_string (tuple, -1, "encoder", data, size); break; case ID3_TXXX: decode_txxx (tuple, data, size); break; case ID3_RVA2: decode_rva2 (tuple, data, size); break; default: TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key); break; } g_free (data); pos += frame_size; } return TRUE; }
static bool_t read_header (VFSFile * handle, int * version, bool_t * syncsafe, int64_t * offset, int * header_size, int * data_size, int * footer_size) { ID3v2Header header, footer; if (vfs_fseek (handle, 0, SEEK_SET)) return FALSE; if (vfs_fread (& header, 1, sizeof (ID3v2Header), handle) != sizeof (ID3v2Header)) return FALSE; if (validate_header (& header, FALSE)) { * offset = 0; * version = header.version; * header_size = sizeof (ID3v2Header); * data_size = header.size; if (header.flags & ID3_HEADER_HAS_FOOTER) { if (vfs_fseek (handle, header.size, SEEK_CUR)) return FALSE; if (vfs_fread (& footer, 1, sizeof (ID3v2Header), handle) != sizeof (ID3v2Header)) return FALSE; if (! validate_header (& footer, TRUE)) return FALSE; * footer_size = sizeof (ID3v2Header); } else * footer_size = 0; } else { int64_t end = vfs_fsize (handle); if (end < 0) return FALSE; if (vfs_fseek (handle, end - sizeof (ID3v2Header), SEEK_SET)) return FALSE; if (vfs_fread (& footer, 1, sizeof (ID3v2Header), handle) != sizeof (ID3v2Header)) return FALSE; if (! validate_header (& footer, TRUE)) return FALSE; * offset = end - 2 * sizeof (ID3v2Header) - footer.size; * version = footer.version; * header_size = sizeof (ID3v2Header); * data_size = footer.size; * footer_size = sizeof (ID3v2Header); if (vfs_fseek (handle, * offset, SEEK_SET)) return FALSE; if (vfs_fread (& header, 1, sizeof (ID3v2Header), handle) != sizeof (ID3v2Header)) return FALSE; if (! validate_header (& header, FALSE)) return FALSE; } * syncsafe = (header.flags & ID3_HEADER_SYNCSAFE) ? TRUE : FALSE; if (header.flags & ID3_HEADER_HAS_EXTENDED_HEADER) { int extended_size = 0; if (header.version == 3) { if (! skip_extended_header_3 (handle, & extended_size)) return FALSE; } else if (header.version == 4) { if (! skip_extended_header_4 (handle, & extended_size)) return FALSE; } * header_size += extended_size; * data_size -= extended_size; } TAGDBG ("Offset = %d, header size = %d, data size = %d, footer size = " "%d.\n", (int) * offset, * header_size, * data_size, * footer_size); return TRUE; }
static bool_t id3v24_read_tag (Tuple * tuple, VFSFile * handle) { int version, header_size, data_size, footer_size; bool_t syncsafe; int64_t offset; int pos; if (! read_header (handle, & version, & syncsafe, & offset, & header_size, & data_size, & footer_size)) return FALSE; for (pos = 0; pos < data_size; ) { int frame_size, size, id; char key[5]; char * data; if (! read_frame (handle, data_size - pos, version, syncsafe, & frame_size, key, & data, & size)) break; id = get_frame_id (key); switch (id) { case ID3_ALBUM: id3_associate_string (tuple, FIELD_ALBUM, data, size); break; case ID3_TITLE: id3_associate_string (tuple, FIELD_TITLE, data, size); break; case ID3_COMPOSER: id3_associate_string (tuple, FIELD_COMPOSER, data, size); break; case ID3_COPYRIGHT: id3_associate_string (tuple, FIELD_COPYRIGHT, data, size); break; case ID3_DATE: id3_associate_string (tuple, FIELD_DATE, data, size); break; case ID3_LENGTH: id3_associate_int (tuple, FIELD_LENGTH, data, size); break; case ID3_ARTIST: id3_associate_string (tuple, FIELD_ARTIST, data, size); break; case ID3_TRACKNR: id3_associate_int (tuple, FIELD_TRACK_NUMBER, data, size); break; case ID3_YEAR: case ID3_RECORDING_TIME: id3_associate_int (tuple, FIELD_YEAR, data, size); break; case ID3_GENRE: id3_decode_genre (tuple, data, size); break; case ID3_COMMENT: id3_decode_comment (tuple, data, size); break; #if 0 case ID3_PRIVATE: decode_private_info (tuple, data, size); break; #endif case ID3_RVA2: id3_decode_rva (tuple, data, size); break; default: TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key); break; } g_free (data); pos += frame_size; } return TRUE; }
gchar * convert_text (const gchar * text, gint length, gint encoding, gboolean nulled, gint * _converted, const gchar * * after) { gchar * buffer = NULL; gsize converted = 0; TAGDBG ("length = %d, encoding = %d, nulled = %d\n", length, encoding, nulled); if (nulled) { const guchar null16[] = {0, 0}; const gchar * null; switch (encoding) { case 0: case 3: if ((null = memchr (text, 0, length)) == NULL) return NULL; length = null - text; TAGDBG ("length before null = %d\n", length); if (after != NULL) * after = null + 1; break; case 1: case 2: if ((null = memfind (text, length, null16, 2)) == NULL) return NULL; length = null - text; TAGDBG ("length before null = %d\n", length); if (after != NULL) * after = null + 2; break; } } switch (encoding) { case 0: case 3: buffer = str_to_utf8_full (text, length, NULL, & converted, NULL); break; case 1: if (text[0] == (gchar) 0xff) buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16LE", NULL, & converted, NULL); else buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16BE", NULL, & converted, NULL); break; case 2: buffer = g_convert (text, length, "UTF-8", "UTF-16BE", NULL, & converted, NULL); break; } TAGDBG ("length converted: %d\n", (gint) converted); TAGDBG ("string: %s\n", buffer); if (_converted != NULL) * _converted = converted; return buffer; }
static void remove_frame (int id, GHashTable * dict) { TAGDBG ("Deleting frame %s.\n", id3_frames[id]); g_hash_table_remove (dict, id3_frames[id]); }