Mercurial > audlegacy
comparison src/libaudtag/id3/id3v2.c @ 4887:0ddbd0025174 default tip
added libaudtag. (not used yet.)
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Wed, 05 May 2010 18:26:06 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
4886:54b4f7aaca24 | 4887:0ddbd0025174 |
---|---|
1 /* | |
2 * Copyright 2009 Paula Stanciu | |
3 * | |
4 * This file is part of Audacious. | |
5 * | |
6 * Audacious is free software: you can redistribute it and/or modify it under | |
7 * the terms of the GNU General Public License as published by the Free Software | |
8 * Foundation, version 3 of the License. | |
9 * | |
10 * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY | |
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |
12 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License along with | |
15 * Audacious. If not, see <http://www.gnu.org/licenses/>. | |
16 * | |
17 * The Audacious team does not consider modular code linking to Audacious or | |
18 * using our public API to be a derived work. | |
19 */ | |
20 | |
21 #include <glib.h> | |
22 #include <glib/gstdio.h> | |
23 | |
24 #include "id3v2.h" | |
25 #include "../util.h" | |
26 #include <inttypes.h> | |
27 #include "../tag_module.h" | |
28 #include "frame.h" | |
29 | |
30 #define TAG_SIZE 1 | |
31 | |
32 guint32 read_syncsafe_int32(VFSFile * fd) | |
33 { | |
34 guint32 val = read_BEuint32(fd); | |
35 guint32 mask = 0x7f; | |
36 guint32 intVal = 0; | |
37 intVal = ((intVal) | (val & mask)); | |
38 int i; | |
39 for (i = 0; i < 3; i++) | |
40 { | |
41 mask = mask << 8; | |
42 guint32 tmp = (val & mask); | |
43 tmp = tmp >> 1; | |
44 intVal = intVal | tmp; | |
45 }; | |
46 return intVal; | |
47 } | |
48 | |
49 ID3v2Header *readHeader(VFSFile * fd) | |
50 { | |
51 ID3v2Header *header = g_new0(ID3v2Header, 1); | |
52 header->id3 = read_char_data(fd, 3); | |
53 header->version = read_LEuint16(fd); | |
54 header->flags = *read_char_data(fd, 1); | |
55 header->size = read_syncsafe_int32(fd); | |
56 return header; | |
57 } | |
58 | |
59 ExtendedHeader *readExtendedHeader(VFSFile * fd) | |
60 { | |
61 ExtendedHeader *header = g_new0(ExtendedHeader, 1); | |
62 header->header_size = read_syncsafe_int32(fd); | |
63 header->flags = read_LEuint16(fd); | |
64 header->padding_size = read_BEuint32(fd); | |
65 return header; | |
66 } | |
67 | |
68 ID3v2FrameHeader *readID3v2FrameHeader(VFSFile * fd) | |
69 { | |
70 ID3v2FrameHeader *frameheader = g_new0(ID3v2FrameHeader, 1); | |
71 frameheader->frame_id = read_char_data(fd, 4); | |
72 frameheader->size = read_syncsafe_int32(fd); | |
73 frameheader->flags = read_LEuint16(fd); | |
74 if ((frameheader->flags & 0x100) == 0x100) | |
75 frameheader->size = read_syncsafe_int32(fd); | |
76 return frameheader; | |
77 } | |
78 | |
79 static gint unsyncsafe (gchar * data, gint size) | |
80 { | |
81 gchar * get = data, * set = data; | |
82 | |
83 while (size --) | |
84 { | |
85 gchar c = * set ++ = * get ++; | |
86 | |
87 if (c == (gchar) 0xff && size) | |
88 { | |
89 size --; | |
90 get ++; | |
91 } | |
92 } | |
93 | |
94 return set - data; | |
95 } | |
96 | |
97 static gchar * read_text_frame (VFSFile * handle, ID3v2FrameHeader * header) | |
98 { | |
99 gint size = header->size; | |
100 gchar data[size]; | |
101 | |
102 if (vfs_fread (data, 1, size, handle) != size) | |
103 return NULL; | |
104 | |
105 if (header->flags & 0x200) | |
106 size = unsyncsafe (data, size); | |
107 | |
108 switch (data[0]) | |
109 { | |
110 case 0: | |
111 return g_convert (data + 1, size - 1, "UTF-8", "ISO-8859-1", NULL, NULL, | |
112 NULL); | |
113 case 1: | |
114 if (data[1] == (gchar) 0xff) | |
115 return g_convert (data + 3, size - 3, "UTF-8", "UTF-16LE", NULL, | |
116 NULL, NULL); | |
117 else | |
118 return g_convert (data + 3, size - 3, "UTF-8", "UTF-16BE", NULL, | |
119 NULL, NULL); | |
120 case 2: | |
121 return g_convert (data + 1, size - 1, "UTF-8", "UTF-16BE", NULL, NULL, | |
122 NULL); | |
123 case 3: | |
124 return g_strndup (data + 1, size - 1); | |
125 default: | |
126 AUDDBG ("Throwing away %i bytes of text due to invalid encoding %d\n", | |
127 size - 1, (gint) data[0]); | |
128 return NULL; | |
129 } | |
130 } | |
131 | |
132 static gboolean read_comment_frame (VFSFile * handle, ID3v2FrameHeader * header, | |
133 gchar * * lang, gchar * * type, gchar * * value) | |
134 { | |
135 gint size = header->size; | |
136 gchar data[size]; | |
137 gchar * pair, * sep; | |
138 gsize converted; | |
139 | |
140 if (vfs_fread (data, 1, size, handle) != size) | |
141 return FALSE; | |
142 | |
143 if (header->flags & 0x200) | |
144 size = unsyncsafe (data, size); | |
145 | |
146 switch (data[0]) | |
147 { | |
148 case 0: | |
149 pair = g_convert (data + 4, size - 4, "UTF-8", "ISO-8859-1", NULL, | |
150 & converted, NULL); | |
151 break; | |
152 case 1: | |
153 if (data[4] == (gchar) 0xff) | |
154 pair = g_convert (data + 6, size - 6, "UTF-8", "UTF-16LE", NULL, | |
155 & converted, NULL); | |
156 else | |
157 pair = g_convert (data + 6, size - 6, "UTF-8", "UTF-16BE", NULL, | |
158 & converted, NULL); | |
159 break; | |
160 case 2: | |
161 pair = g_convert (data + 4, size - 4, "UTF-8", "UTF-16BE", NULL, | |
162 & converted, NULL); | |
163 break; | |
164 case 3: | |
165 pair = g_malloc (size - 3); | |
166 memcpy (pair, data + 4, size - 4); | |
167 pair[size - 4] = 0; | |
168 converted = size - 4; | |
169 break; | |
170 default: | |
171 AUDDBG ("Throwing away %i bytes of text due to invalid encoding %d\n", | |
172 size - 4, (gint) data[0]); | |
173 pair = NULL; | |
174 break; | |
175 } | |
176 | |
177 if (pair == NULL || (sep = memchr (pair, 0, converted)) == NULL) | |
178 return FALSE; | |
179 | |
180 * lang = g_strndup (data + 1, 3); | |
181 * type = g_strdup (pair); | |
182 * value = g_strdup (sep + 1); | |
183 | |
184 g_free (pair); | |
185 return TRUE; | |
186 } | |
187 | |
188 GenericFrame *readGenericFrame(VFSFile * fd, GenericFrame * gf) | |
189 { | |
190 gf->header = readID3v2FrameHeader(fd); | |
191 gf->frame_body = read_char_data(fd, gf->header->size); | |
192 | |
193 return gf; | |
194 } | |
195 | |
196 | |
197 void readAllFrames(VFSFile * fd, int framesSize) | |
198 { | |
199 int pos = 0; | |
200 int i = 0; | |
201 while (pos < framesSize) | |
202 { | |
203 GenericFrame *gframe = g_new0(GenericFrame, 1); | |
204 gframe = readGenericFrame(fd, gframe); | |
205 if (isValidFrame(gframe)) | |
206 { | |
207 mowgli_dictionary_add(frames, gframe->header->frame_id, gframe); | |
208 mowgli_node_add(gframe->header->frame_id, mowgli_node_create(), frameIDs); | |
209 pos += gframe->header->size; | |
210 i++; | |
211 } | |
212 else | |
213 break; | |
214 } | |
215 | |
216 } | |
217 | |
218 void write_int32(VFSFile * fd, guint32 val) | |
219 { | |
220 guint32 be_val = GUINT32_TO_BE(val); | |
221 vfs_fwrite(&be_val, 4, 1, fd); | |
222 } | |
223 | |
224 void write_syncsafe_int32(VFSFile * fd, guint32 val) | |
225 { | |
226 //TODO write the correct function - this is just for testing | |
227 int i = 0; | |
228 guint32 tmp = 0x0; | |
229 guint32 mask = 0x7f; | |
230 guint32 syncVal = 0; | |
231 tmp = val & mask; | |
232 syncVal = tmp; | |
233 for (i = 0; i < 3; i++) | |
234 { | |
235 tmp = 0; | |
236 mask <<= 7; | |
237 tmp = val & mask; | |
238 tmp <<= 1; | |
239 syncVal |= tmp; | |
240 } | |
241 guint32 be_val = GUINT32_TO_BE(syncVal); | |
242 vfs_fwrite(&be_val, 4, 1, fd); | |
243 } | |
244 | |
245 | |
246 void write_ASCII(VFSFile * fd, int size, gchar * value) | |
247 { | |
248 vfs_fwrite(value, size, 1, fd); | |
249 } | |
250 | |
251 | |
252 void write_utf8(VFSFile * fd, int size, gchar * value) | |
253 { | |
254 GError *error = NULL; | |
255 gsize bytes_read = 0, bytes_write = 0; | |
256 gchar *isoVal = g_convert(value, size, "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); | |
257 vfs_fwrite(isoVal, size, 1, fd); | |
258 } | |
259 | |
260 guint32 writeAllFramesToFile(VFSFile * fd) | |
261 { | |
262 guint32 size = 0; | |
263 mowgli_node_t *n, *tn; | |
264 MOWGLI_LIST_FOREACH_SAFE(n, tn, frameIDs->head) | |
265 { | |
266 GenericFrame *frame = (GenericFrame *) mowgli_dictionary_retrieve(frames, (gchar *) (n->data)); | |
267 if (frame) | |
268 { | |
269 writeGenericFrame(fd, frame); | |
270 size += frame->header->size + 10; | |
271 } | |
272 } | |
273 return size; | |
274 } | |
275 | |
276 void writeID3HeaderToFile(VFSFile * fd, ID3v2Header * header) | |
277 { | |
278 vfs_fwrite(header->id3, 3, 1, fd); | |
279 vfs_fwrite(&header->version, 2, 1, fd); | |
280 vfs_fwrite(&header->flags, 1, 1, fd); | |
281 write_syncsafe_int32(fd, header->size); | |
282 } | |
283 | |
284 void writePaddingToFile(VFSFile * fd, int ksize) | |
285 { | |
286 gchar padding = 0; | |
287 int i = 0; | |
288 for (i = 0; i < ksize; i++) | |
289 vfs_fwrite(&padding, 1, 1, fd); | |
290 } | |
291 | |
292 | |
293 void writeID3FrameHeaderToFile(VFSFile * fd, ID3v2FrameHeader * header) | |
294 { | |
295 vfs_fwrite(header->frame_id, 4, 1, fd); | |
296 write_int32(fd, header->size); | |
297 vfs_fwrite(&header->flags, 2, 1, fd); | |
298 } | |
299 | |
300 void writeGenericFrame(VFSFile * fd, GenericFrame * frame) | |
301 { | |
302 writeID3FrameHeaderToFile(fd, frame->header); | |
303 vfs_fwrite(frame->frame_body, frame->header->size, 1, fd); | |
304 } | |
305 | |
306 gboolean isExtendedHeader(ID3v2Header * header) | |
307 { | |
308 if ((header->flags & 0x40) == (0x40)) | |
309 return TRUE; | |
310 else | |
311 return FALSE; | |
312 } | |
313 | |
314 int getFrameID(ID3v2FrameHeader * header) | |
315 { | |
316 int i = 0; | |
317 for (i = 0; i < ID3_TAGS_NO; i++) | |
318 { | |
319 if (!strcmp(header->frame_id, id3_frames[i])) | |
320 return i; | |
321 } | |
322 return -1; | |
323 } | |
324 | |
325 | |
326 void skipFrame(VFSFile * fd, guint32 size) | |
327 { | |
328 vfs_fseek(fd, size, SEEK_CUR); | |
329 } | |
330 | |
331 static void associate_string (Tuple * tuple, VFSFile * handle, gint field, | |
332 const gchar * customfield, ID3v2FrameHeader * header) | |
333 { | |
334 gchar * text = read_text_frame (handle, header); | |
335 | |
336 if (text == NULL) | |
337 return; | |
338 | |
339 if (customfield != NULL) | |
340 AUDDBG ("custom field %s = %s\n", customfield, text); | |
341 else | |
342 AUDDBG ("field %i = %s\n", field, text); | |
343 | |
344 tuple_associate_string (tuple, field, customfield, text); | |
345 g_free (text); | |
346 } | |
347 | |
348 static void associate_int (Tuple * tuple, VFSFile * handle, gint field, | |
349 const gchar * customfield, ID3v2FrameHeader * header) | |
350 { | |
351 gchar * text = read_text_frame (handle, header); | |
352 | |
353 if (text == NULL) | |
354 return; | |
355 | |
356 if (customfield != NULL) | |
357 AUDDBG ("custom field %s = %s\n", customfield, text); | |
358 else | |
359 AUDDBG ("field %i = %s\n", field, text); | |
360 | |
361 tuple_associate_int (tuple, field, customfield, atoi (text)); | |
362 g_free (text); | |
363 } | |
364 | |
365 static void decode_private_info(Tuple * tuple, VFSFile * fd, ID3v2FrameHeader * header) | |
366 { | |
367 gchar *value = read_char_data(fd, header->size); | |
368 if (!strncmp(value, "WM/", 3)) | |
369 { | |
370 AUDDBG("Windows Media tag: %s\n", value); | |
371 } else { | |
372 AUDDBG("Unable to decode private data, skipping: %s\n", value); | |
373 } | |
374 } | |
375 | |
376 static void decode_comment (Tuple * tuple, VFSFile * handle, ID3v2FrameHeader * | |
377 header) | |
378 { | |
379 gchar * lang, * type, * value; | |
380 | |
381 if (! read_comment_frame (handle, header, & lang, & type, & value)) | |
382 return; | |
383 | |
384 AUDDBG ("comment: lang = %s, type = %s, value = %s\n", lang, type, value); | |
385 | |
386 if (! type[0]) /* blank type == actual comment */ | |
387 tuple_associate_string (tuple, FIELD_COMMENT, NULL, value); | |
388 | |
389 g_free (lang); | |
390 g_free (type); | |
391 g_free (value); | |
392 } | |
393 | |
394 static void decode_txxx (Tuple * tuple, VFSFile * handle, ID3v2FrameHeader * header) | |
395 { | |
396 gchar * text = read_text_frame (handle, header); | |
397 | |
398 if (text == NULL) | |
399 return; | |
400 | |
401 gchar * separator = strchr(text, 0); | |
402 | |
403 if (separator == NULL) | |
404 return; | |
405 | |
406 gchar * value = separator + 1; | |
407 AUDDBG ("Field '%s' has value '%s'\n", text, value); | |
408 tuple_associate_string (tuple, -1, text, value); | |
409 | |
410 g_free (text); | |
411 } | |
412 | |
413 Tuple *decodeGenre(Tuple * tuple, VFSFile * fd, ID3v2FrameHeader header) | |
414 { | |
415 gint numericgenre; | |
416 gchar * text = read_text_frame (fd, & header); | |
417 | |
418 if (text == NULL) | |
419 return tuple; | |
420 | |
421 if (text[0] == '(') | |
422 numericgenre = atoi (text + 1); | |
423 else | |
424 numericgenre = atoi (text); | |
425 | |
426 if (numericgenre > 0) | |
427 { | |
428 tuple_associate_string(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(numericgenre)); | |
429 return tuple; | |
430 } | |
431 tuple_associate_string(tuple, FIELD_GENRE, NULL, text); | |
432 g_free (text); | |
433 return tuple; | |
434 } | |
435 | |
436 gboolean isValidFrame(GenericFrame * frame) | |
437 { | |
438 if (strlen(frame->header->frame_id) != 0) | |
439 return TRUE; | |
440 else | |
441 return FALSE; | |
442 } | |
443 | |
444 | |
445 | |
446 void add_newISO8859_1FrameFromString(const gchar * value, int id3_field) | |
447 { | |
448 GError *error = NULL; | |
449 gsize bytes_read = 0, bytes_write = 0; | |
450 gchar *retVal = g_convert(value, strlen(value), "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); | |
451 ID3v2FrameHeader *header = g_new0(ID3v2FrameHeader, 1); | |
452 header->frame_id = id3_frames[id3_field]; | |
453 header->flags = 0; | |
454 header->size = strlen(retVal) + 1; | |
455 gchar *buf = g_new0(gchar, header->size + 1); | |
456 memcpy(buf + 1, retVal, header->size); | |
457 GenericFrame *frame = g_new0(GenericFrame, 1); | |
458 frame->header = header; | |
459 frame->frame_body = buf; | |
460 mowgli_dictionary_add(frames, header->frame_id, frame); | |
461 mowgli_node_add(frame->header->frame_id, mowgli_node_create(), frameIDs); | |
462 | |
463 } | |
464 | |
465 | |
466 void add_newFrameFromTupleStr(Tuple * tuple, int field, int id3_field) | |
467 { | |
468 const gchar *value = tuple_get_string(tuple, field, NULL); | |
469 add_newISO8859_1FrameFromString(value, id3_field); | |
470 } | |
471 | |
472 | |
473 void add_newFrameFromTupleInt(Tuple * tuple, int field, int id3_field) | |
474 { | |
475 int intvalue = tuple_get_int(tuple, field, NULL); | |
476 gchar *value = g_strdup_printf("%d", intvalue); | |
477 add_newISO8859_1FrameFromString(value, id3_field); | |
478 | |
479 } | |
480 | |
481 | |
482 | |
483 void add_frameFromTupleStr(Tuple * tuple, int field, int id3_field) | |
484 { | |
485 const gchar *value = tuple_get_string(tuple, field, NULL); | |
486 GError *error = NULL; | |
487 gsize bytes_read = 0, bytes_write = 0; | |
488 gchar *retVal = g_convert(value, strlen(value), "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); | |
489 | |
490 GenericFrame *frame = mowgli_dictionary_retrieve(frames, id3_frames[id3_field]); | |
491 if (frame != NULL) | |
492 { | |
493 frame->header->size = strlen(retVal) + 1; | |
494 gchar *buf = g_new0(gchar, frame->header->size + 1); | |
495 memcpy(buf + 1, retVal, frame->header->size); | |
496 frame->frame_body = buf; | |
497 } | |
498 else | |
499 add_newFrameFromTupleStr(tuple, field, id3_field); | |
500 | |
501 } | |
502 | |
503 void add_frameFromTupleInt(Tuple * tuple, int field, int id3_field) | |
504 { | |
505 int intvalue = tuple_get_int(tuple, field, NULL); | |
506 gchar *value = g_strdup_printf("%d", intvalue); | |
507 GError *error = NULL; | |
508 gsize bytes_read = 0, bytes_write = 0; | |
509 gchar *retVal = g_convert(value, strlen(value), "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); | |
510 | |
511 GenericFrame *frame = mowgli_dictionary_retrieve(frames, id3_frames[id3_field]); | |
512 if (frame != NULL) | |
513 { | |
514 frame->header->size = strlen(retVal) + 1; | |
515 gchar *buf = g_new0(gchar, frame->header->size + 1); | |
516 memcpy(buf + 1, retVal, frame->header->size); | |
517 frame->frame_body = buf; | |
518 } | |
519 else | |
520 add_newFrameFromTupleStr(tuple, field, id3_field); | |
521 | |
522 } | |
523 | |
524 gboolean id3v2_can_handle_file(VFSFile * f) | |
525 { | |
526 ID3v2Header *header = readHeader(f); | |
527 if (!strcmp(header->id3, "ID3")) | |
528 return TRUE; | |
529 return FALSE; | |
530 } | |
531 | |
532 | |
533 | |
534 Tuple *id3v2_populate_tuple_from_file(Tuple * tuple, VFSFile * f) | |
535 { | |
536 vfs_fseek(f, 0, SEEK_SET); | |
537 ExtendedHeader *extHeader; | |
538 ID3v2Header *header = readHeader(f); | |
539 int pos = 0; | |
540 if (isExtendedHeader(header)) | |
541 { | |
542 extHeader = readExtendedHeader(f); | |
543 vfs_fseek(f, 10 + extHeader->header_size, SEEK_SET); | |
544 } | |
545 | |
546 while (pos < header->size) | |
547 { | |
548 ID3v2FrameHeader *frame = readID3v2FrameHeader(f); | |
549 if (frame->size == 0) | |
550 break; | |
551 int id = getFrameID(frame); | |
552 pos = pos + frame->size + 10; | |
553 if (pos > header->size) | |
554 break; | |
555 switch (id) | |
556 { | |
557 case ID3_ALBUM: | |
558 associate_string (tuple, f, FIELD_ALBUM, NULL, frame); | |
559 break; | |
560 case ID3_TITLE: | |
561 associate_string (tuple, f, FIELD_TITLE, NULL, frame); | |
562 break; | |
563 case ID3_COMPOSER: | |
564 associate_string (tuple, f, FIELD_ARTIST, NULL, frame); | |
565 break; | |
566 case ID3_COPYRIGHT: | |
567 associate_string (tuple, f, FIELD_COPYRIGHT, NULL, frame); | |
568 break; | |
569 case ID3_DATE: | |
570 associate_string (tuple, f, FIELD_DATE, NULL, frame); | |
571 break; | |
572 case ID3_TIME: | |
573 associate_int (tuple, f, FIELD_LENGTH, NULL, frame); | |
574 break; | |
575 case ID3_LENGTH: | |
576 associate_int (tuple, f, FIELD_LENGTH, NULL, frame); | |
577 break; | |
578 case ID3_ARTIST: | |
579 associate_string (tuple, f, FIELD_ARTIST, NULL, frame); | |
580 break; | |
581 case ID3_TRACKNR: | |
582 associate_int (tuple, f, FIELD_TRACK_NUMBER, NULL, frame); | |
583 break; | |
584 case ID3_YEAR: | |
585 case ID3_RECORDING_TIME: | |
586 associate_int (tuple, f, FIELD_YEAR, NULL, frame); | |
587 break; | |
588 case ID3_GENRE: | |
589 tuple = decodeGenre(tuple, f, *frame); | |
590 break; | |
591 case ID3_COMMENT: | |
592 decode_comment (tuple, f, frame); | |
593 break; | |
594 case ID3_PRIVATE: | |
595 decode_private_info (tuple, f, frame); | |
596 break; | |
597 case ID3_ENCODER: | |
598 associate_string (tuple, f, -1, "encoder", frame); | |
599 break; | |
600 case ID3_TXXX: | |
601 decode_txxx (tuple, f, frame); | |
602 break; | |
603 default: | |
604 AUDDBG("Skipping %i bytes over unsupported ID3 frame %s\n", frame->size, frame->frame_id); | |
605 skipFrame(f, frame->size); | |
606 } | |
607 } | |
608 return tuple; | |
609 } | |
610 | |
611 | |
612 gboolean id3v2_write_tuple_to_file(Tuple * tuple, VFSFile * f) | |
613 { | |
614 VFSFile *tmp; | |
615 vfs_fseek(f, 0, SEEK_SET); | |
616 | |
617 ExtendedHeader *extHeader; | |
618 if (frameIDs != NULL) | |
619 { | |
620 mowgli_node_t *n, *tn; | |
621 MOWGLI_LIST_FOREACH_SAFE(n, tn, frameIDs->head) | |
622 { | |
623 mowgli_node_delete(n, frameIDs); | |
624 } | |
625 } | |
626 frameIDs = mowgli_list_create(); | |
627 ID3v2Header *header = readHeader(f); | |
628 int framesSize = header->size; | |
629 | |
630 if (isExtendedHeader(header)) | |
631 { | |
632 extHeader = readExtendedHeader(f); | |
633 framesSize -= 10; | |
634 framesSize -= extHeader->padding_size; | |
635 } | |
636 | |
637 //read all frames into generic frames; | |
638 frames = mowgli_dictionary_create(strcasecmp); | |
639 readAllFrames(f, header->size); | |
640 | |
641 //make the new frames from tuple and replace in the dictionary the old frames with the new ones | |
642 if (tuple_get_string(tuple, FIELD_ARTIST, NULL)) | |
643 add_frameFromTupleStr(tuple, FIELD_ARTIST, ID3_ARTIST); | |
644 | |
645 if (tuple_get_string(tuple, FIELD_TITLE, NULL)) | |
646 add_frameFromTupleStr(tuple, FIELD_TITLE, ID3_TITLE); | |
647 | |
648 if (tuple_get_string(tuple, FIELD_ALBUM, NULL)) | |
649 add_frameFromTupleStr(tuple, FIELD_ALBUM, ID3_ALBUM); | |
650 | |
651 if (tuple_get_string(tuple, FIELD_COMMENT, NULL)) | |
652 add_frameFromTupleStr(tuple, FIELD_COMMENT, ID3_COMMENT); | |
653 | |
654 if (tuple_get_string(tuple, FIELD_GENRE, NULL)) | |
655 add_frameFromTupleStr(tuple, FIELD_GENRE, ID3_GENRE); | |
656 | |
657 if (tuple_get_int(tuple, FIELD_YEAR, NULL) != 0) | |
658 add_frameFromTupleInt(tuple, FIELD_YEAR, ID3_YEAR); | |
659 | |
660 if (tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL) != 0) | |
661 add_frameFromTupleInt(tuple, FIELD_TRACK_NUMBER, ID3_TRACKNR); | |
662 | |
663 const gchar *tmpdir = g_get_tmp_dir(); | |
664 gchar *tmp_path = g_strdup_printf("file://%s/%s", tmpdir, "tmp.mpc"); | |
665 tmp = vfs_fopen(tmp_path, "w+"); | |
666 | |
667 int oldSize = header->size; | |
668 header->size = TAG_SIZE * 1024; | |
669 | |
670 writeID3HeaderToFile(tmp, header); | |
671 | |
672 int size = writeAllFramesToFile(tmp); | |
673 writePaddingToFile(tmp, TAG_SIZE * 1024 - size - 10); | |
674 | |
675 copyAudioToFile(f, tmp, oldSize); | |
676 | |
677 | |
678 gchar *uri = g_strdup(f->uri); | |
679 vfs_fclose(tmp); | |
680 gchar *f1 = g_filename_from_uri(tmp_path, NULL, NULL); | |
681 gchar *f2 = g_filename_from_uri(uri, NULL, NULL); | |
682 if (g_rename(f1, f2) == 0) | |
683 { | |
684 AUDDBG("the tag was updated successfully\n"); | |
685 } | |
686 else | |
687 { | |
688 AUDDBG("an error has occured\n"); | |
689 } | |
690 return TRUE; | |
691 } |