Mercurial > audlegacy-plugins
annotate src/wavpack/tags.cxx @ 2057:cf4fa45ffd80
playlist API vtabling
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Sat, 13 Oct 2007 23:09:40 -0500 |
parents | aa8bd7b56cda |
children | 0de647993c2a |
rev | line source |
---|---|
109 | 1 #include <stdlib.h> |
2 #include <stdio.h> | |
3 #include <string.h> | |
4 #include <unistd.h> | |
5 #include <fcntl.h> | |
6 #include <wchar.h> | |
7 #include <audacious/util.h> | |
265 | 8 #include <audacious/vfs.h> |
2001
aa8bd7b56cda
make wavpack compile again.
Yoshiki Yazawa <yaz@cc.rim.or.jp>
parents:
1978
diff
changeset
|
9 #include <audacious/plugin.h> |
109 | 10 #include "tags.h" |
11 | |
12 struct APETagFooterStruct { | |
13 unsigned char ID[8]; | |
14 unsigned char Version[4]; | |
15 unsigned char Length[4]; | |
16 unsigned char TagCount[4]; | |
17 unsigned char Flags[4]; | |
18 unsigned char Reserved[8]; | |
19 }; | |
20 | |
21 typedef struct { | |
22 char *key; | |
23 size_t keylen; | |
24 unsigned char *value; | |
25 size_t valuelen; | |
26 unsigned int flags; | |
27 } TagItem; | |
28 | |
29 unsigned long | |
30 Read_LE_Uint32(const unsigned char *p) | |
31 { | |
32 return ((unsigned long) p[0] << 0) | | |
33 ((unsigned long) p[1] << 8) | | |
34 ((unsigned long) p[2] << 16) | ((unsigned long) p[3] << 24); | |
35 } | |
36 | |
37 // Convert UTF-8 coded string to UNICODE | |
38 // Return number of characters converted | |
39 int | |
40 utf8ToUnicode(const char *lpMultiByteStr, wchar_t * lpWideCharStr, | |
41 int cmbChars) | |
42 { | |
43 const unsigned char *pmb = (unsigned char *) lpMultiByteStr; | |
44 unsigned short *pwc = (unsigned short *) lpWideCharStr; | |
45 const unsigned char *pmbe; | |
46 size_t cwChars = 0; | |
47 | |
48 if (cmbChars >= 0) { | |
49 pmbe = pmb + cmbChars; | |
50 } | |
51 else { | |
52 pmbe = NULL; | |
53 } | |
54 | |
55 while ((pmbe == NULL) || (pmb < pmbe)) { | |
56 char mb = *pmb++; | |
57 unsigned int cc = 0; | |
58 unsigned int wc; | |
59 | |
60 while ((cc < 7) && (mb & (1 << (7 - cc)))) { | |
61 cc++; | |
62 } | |
63 | |
64 if (cc == 1 || cc > 6) // illegal character combination for UTF-8 | |
65 continue; | |
66 | |
67 if (cc == 0) { | |
68 wc = mb; | |
69 } | |
70 else { | |
71 wc = (mb & ((1 << (7 - cc)) - 1)) << ((cc - 1) * 6); | |
72 while (--cc > 0) { | |
73 if (pmb == pmbe) // reached end of the buffer | |
74 return cwChars; | |
75 mb = *pmb++; | |
76 if (((mb >> 6) & 0x03) != 2) // not part of multibyte character | |
77 return cwChars; | |
78 wc |= (mb & 0x3F) << ((cc - 1) * 6); | |
79 } | |
80 } | |
81 | |
82 if (wc & 0xFFFF0000) | |
83 wc = L'?'; | |
84 *pwc++ = wc; | |
85 cwChars++; | |
86 if (wc == L'\0') | |
87 return cwChars; | |
88 } | |
89 | |
90 return cwChars; | |
91 } | |
92 | |
93 void | |
94 tag_insert(char *buffer, const char *value, long unsigned int len, | |
95 long unsigned int maxlen, bool decode_utf8) | |
96 { | |
97 char *p; | |
98 wchar_t wValue[MAX_LEN]; | |
99 char temp[MAX_LEN]; | |
100 long unsigned int c; | |
101 const wchar_t *src = wValue; | |
102 | |
103 if (len >= maxlen) | |
104 len = maxlen - 1; | |
105 if (decode_utf8) { | |
106 if ((c = utf8ToUnicode(value, wValue, len)) <= 0) | |
107 return; | |
108 if (wValue[c] != L'\0') | |
109 wValue[c++] = L'\0'; | |
110 if ((c = wcsrtombs(temp, &src, MAX_LEN, NULL)) == 0) | |
111 return; | |
112 } | |
113 else { | |
114 c = len; | |
115 strncpy(temp, value, len); | |
116 while (temp[len - 1] == 0x20 || len < 1) { | |
117 len--; | |
118 } | |
119 temp[len] = '\0'; | |
120 } | |
121 | |
122 //if ( *buffer == '\0' ) { // new value | |
123 p = buffer; | |
124 //} else { // append to existing value | |
125 // p = strchr (buffer, '\0' ); | |
126 // p += sprintf ( p, ", " ); | |
127 //} | |
128 | |
129 if ((p - buffer) + c >= maxlen) | |
130 c = maxlen - (p - buffer) - 1; | |
131 strncpy(p, temp, c); | |
132 p[c] = '\0'; | |
133 } | |
134 | |
135 // Returns the Type of Tag (Ape or ID3) | |
136 int | |
265 | 137 GetTageType(VFSFile * fp) |
109 | 138 { |
139 struct APETagFooterStruct T; | |
140 unsigned char tagheader[3]; | |
141 int size; | |
142 | |
143 if (fp == NULL) { | |
144 return TAG_NONE; | |
145 } | |
146 | |
1978 | 147 if (aud_vfs_fseek(fp, 0, SEEK_END) != 0) |
109 | 148 return TAG_NONE; |
1978 | 149 size = aud_vfs_ftell(fp); |
150 if (aud_vfs_fseek(fp, size - sizeof T, SEEK_SET) != 0) | |
109 | 151 return TAG_NONE; |
1978 | 152 if (aud_vfs_fread(&T, 1, sizeof T, fp) != sizeof T) |
109 | 153 return TAG_NONE; |
154 if (memcmp(T.ID, "APETAGEX", sizeof T.ID) == 0) | |
155 return TAG_APE; | |
1978 | 156 if (aud_vfs_fseek(fp, -128L, SEEK_END) != 0) |
109 | 157 return TAG_NONE; |
1978 | 158 if (aud_vfs_fread(tagheader, 1, 3, fp) != 3) |
109 | 159 return TAG_NONE; |
160 if (0 == memcmp(tagheader, "TAG", 3)) | |
161 return TAG_ID3; | |
162 return TAG_NONE; | |
163 } | |
164 | |
165 | |
166 int | |
265 | 167 ReadID3Tag(VFSFile * fp, ape_tag * Tag) |
109 | 168 { |
169 char *tag; | |
170 char *buff; | |
171 unsigned int genre; | |
172 | |
173 buff = (char *) malloc(128); | |
174 | |
175 *(Tag->title) = '\0'; | |
176 *(Tag->artist) = '\0'; | |
177 *(Tag->album) = '\0'; | |
178 *(Tag->comment) = '\0'; | |
179 *(Tag->genre) = '\0'; | |
180 *(Tag->track) = '\0'; | |
181 *(Tag->year) = '\0'; | |
182 | |
1978 | 183 if (aud_vfs_fseek(fp, -128L, SEEK_END) != 0) |
109 | 184 return 0; |
1978 | 185 if (aud_vfs_fread(buff, 1, 128, fp) != 128) |
109 | 186 return 0; |
187 tag = buff; | |
188 tag_insert(Tag->title, (tag + 3), 30, 32, false); | |
189 tag_insert(Tag->artist, (tag + 33), 30, 32, false); | |
190 tag_insert(Tag->album, (tag + 63), 30, 32, false); | |
191 tag_insert(Tag->year, (tag + 93), 4, 32, false); | |
192 tag_insert(Tag->comment, (tag + 97), 30, 32, false); | |
193 genre = (unsigned char) tag[127]; | |
194 if (genre >= sizeof(GenreList) / sizeof(int)) | |
195 genre = 12; | |
196 tag_insert(Tag->genre, GenreList[genre], 30, 32, false); | |
197 sprintf(tag, "%u", tag[126]); | |
198 tag_insert(Tag->track, tag, 30, 32, false); | |
199 free(buff); | |
200 return 1; | |
201 } | |
202 | |
203 // Reads APE v2.0 tag | |
204 int | |
265 | 205 ReadAPE2Tag(VFSFile * fp, ape_tag * Tag) |
109 | 206 { |
207 unsigned long vsize; | |
208 unsigned long isize; | |
209 unsigned long flags; | |
210 unsigned char *buff; | |
211 unsigned char *p; | |
212 unsigned char *end; | |
213 struct APETagFooterStruct T; | |
214 unsigned long TagLen; | |
215 unsigned long TagCount; | |
216 long size; | |
217 | |
218 *(Tag->title) = '\0'; | |
219 *(Tag->artist) = '\0'; | |
220 *(Tag->album) = '\0'; | |
221 *(Tag->comment) = '\0'; | |
222 *(Tag->genre) = '\0'; | |
223 *(Tag->track) = '\0'; | |
224 *(Tag->year) = '\0'; | |
225 | |
1978 | 226 if (aud_vfs_fseek(fp, 0, SEEK_END) != 0) |
109 | 227 return 0; |
1978 | 228 size = aud_vfs_ftell(fp); |
229 if (aud_vfs_fseek(fp, size - sizeof T, SEEK_SET) != 0) | |
109 | 230 return 0; |
1978 | 231 if (aud_vfs_fread(&T, 1, sizeof T, fp) != sizeof T) |
109 | 232 return 0; |
233 if (memcmp(T.ID, "APETAGEX", sizeof T.ID) != 0) | |
234 return 0; | |
235 if (Read_LE_Uint32(T.Version) != 2000) | |
236 return 0; | |
237 TagLen = Read_LE_Uint32(T.Length); | |
238 if (TagLen < sizeof T) | |
239 return 0; | |
1978 | 240 if (aud_vfs_fseek(fp, size - TagLen, SEEK_SET) != 0) |
109 | 241 return 0; |
242 if ((buff = (unsigned char *) malloc(TagLen)) == NULL) | |
243 return 0; | |
1978 | 244 if (aud_vfs_fread(buff, 1, TagLen - sizeof T, fp) != TagLen - sizeof T) { |
109 | 245 free(buff); |
246 return 0; | |
247 } | |
248 | |
249 TagCount = Read_LE_Uint32(T.TagCount); | |
250 end = buff + TagLen - sizeof(T); | |
251 for (p = buff; p < end && TagCount--;) { | |
252 vsize = Read_LE_Uint32(p); | |
253 p += 4; | |
254 flags = Read_LE_Uint32(p); | |
255 p += 4; | |
256 isize = strlen((char *) p); | |
257 | |
258 if (isize > 0 && vsize > 0) { | |
259 if (!(flags & 1 << 1)) { // insert UTF-8 string (skip binary values) | |
260 if (!strcasecmp((char *) p, "Title")) { | |
261 tag_insert(Tag->title, (char *) (p + isize + 1), vsize, | |
262 MAX_LEN, false); | |
263 } | |
264 else if (!strcasecmp((char *) p, "Artist")) { | |
265 tag_insert(Tag->artist, (char *) (p + isize + 1), vsize, | |
266 MAX_LEN, false); | |
267 } | |
268 else if (!strcasecmp((char *) p, "Album")) { | |
269 tag_insert(Tag->album, (char *) (p + isize + 1), vsize, | |
270 MAX_LEN, false); | |
271 } | |
272 else if (!strcasecmp((char *) p, "Comment")) { | |
273 tag_insert(Tag->comment, (char *) (p + isize + 1), vsize, | |
274 MAX_LEN, false); | |
275 } | |
276 else if (!strcasecmp((char *) p, "Genre")) { | |
277 tag_insert(Tag->genre, (char *) (p + isize + 1), vsize, | |
278 MAX_LEN, false); | |
279 } | |
280 else if (!strcasecmp((char *) p, "Track")) { | |
281 tag_insert(Tag->track, (char *) (p + isize + 1), vsize, | |
282 128, false); | |
283 } | |
284 else if (!strcasecmp((char *) p, "Year")) { | |
285 tag_insert(Tag->year, (char *) (p + isize + 1), vsize, | |
286 128, false); | |
287 } | |
288 } | |
289 } | |
290 p += isize + 1 + vsize; | |
291 } | |
292 free(buff); | |
293 return 1; | |
294 } | |
295 | |
296 int | |
297 DeleteTag(char *filename) | |
298 { | |
299 | |
1978 | 300 VFSFile *fp = aud_vfs_fopen(filename, "rb+"); |
109 | 301 int tagtype; |
302 int fd; | |
303 long filelength = 0; | |
304 long dellength = -1; | |
305 char *tagheader; | |
306 unsigned long *apelength; | |
307 int res = -1; | |
308 | |
309 if (fp == NULL) { | |
310 char text[256]; | |
311 | |
312 sprintf(text, "File \"%s\" not found or is read protected!\n", | |
313 filename); | |
1677
f6f5603a0954
xmms_show_message() changed to audacious_info_dialog()
Matti Hamalainen <ccr@tnsp.org>
parents:
1159
diff
changeset
|
314 audacious_info_dialog("File-Error", (gchar *) text, "Ok", FALSE, NULL, |
109 | 315 NULL); |
316 return -1; | |
317 } | |
318 tagtype = GetTageType(fp); | |
319 | |
320 // get Length of File | |
1978 | 321 aud_vfs_fseek(fp, 0L, SEEK_END); |
322 filelength = aud_vfs_ftell(fp); | |
109 | 323 |
324 apelength = (unsigned long *) malloc(4); | |
325 tagheader = (char *) malloc(9); | |
326 | |
327 if (tagtype == TAG_ID3) { | |
328 dellength = 128L; | |
329 } | |
330 else if (tagtype == TAG_APE) { | |
1978 | 331 aud_vfs_fseek(fp, -32L, SEEK_END); |
332 aud_vfs_fread(tagheader, 8, 1, fp); | |
109 | 333 if (0 == memcmp(tagheader, "APETAGEX", 8)) { |
1978 | 334 aud_vfs_fseek(fp, -20L, SEEK_END); |
335 aud_vfs_fread(apelength, 4, 1, fp); | |
109 | 336 dellength = *apelength + 32; |
337 } | |
338 } | |
339 | |
340 | |
341 if (dellength > -1) //if TAG was found, delete it | |
342 { | |
343 fd = open(filename, O_RDWR); | |
344 res = ftruncate(fd, (off_t) (filelength - dellength)); | |
345 close(fd); | |
346 } | |
347 | |
348 free(tagheader); | |
349 free(apelength); | |
350 | |
351 //returns 0 if everything is ok | |
352 return res; | |
353 } | |
354 | |
355 // Returns bytes used in APE-Tag for this value | |
356 int | |
1044
b1128efde471
[svn] - get rid of all warnings gcc 4.2.0 emits with my build configuration.
yaz
parents:
265
diff
changeset
|
357 addValue(TagItem * item, const char *key, char *value) |
109 | 358 { |
359 item->keylen = strlen(key); | |
360 item->valuelen = strlen(value); | |
361 item->key = (char *) malloc(item->keylen + 1); | |
362 item->value = (unsigned char *) malloc(item->valuelen + 1); | |
363 strcpy((char *) item->value, value); | |
364 strcpy(item->key, key); | |
365 item->flags = 0; | |
366 return (9 + item->keylen + item->valuelen); | |
367 } | |
368 | |
369 int | |
370 WriteAPE2Tag(char *filename, ape_tag * Tag) | |
371 { | |
265 | 372 VFSFile *fp; |
109 | 373 unsigned char H[32] = "APETAGEX"; |
374 unsigned long Version = 2000; | |
375 unsigned char dw[8]; | |
376 unsigned long estimatedbytes = 32; // 32 byte footer + all items, these are the 32 bytes footer, the items are added later | |
377 long writtenbytes = -32; // actually writtenbytes-32, which should be equal to estimatedbytes (= footer + all items) | |
378 unsigned int TagCount = 0; | |
379 TagItem T[7]; | |
380 | |
381 | |
382 // Delete Tag if there is one | |
1978 | 383 fp = aud_vfs_fopen(filename, "rb+"); |
109 | 384 if (fp == NULL) { |
385 char text[256]; | |
386 | |
1159 | 387 snprintf(text, 256, "File \"%s\" not found or is read protected!\n", |
109 | 388 filename); |
1677
f6f5603a0954
xmms_show_message() changed to audacious_info_dialog()
Matti Hamalainen <ccr@tnsp.org>
parents:
1159
diff
changeset
|
389 audacious_info_dialog("File-Error", (gchar *) text, "Ok", FALSE, NULL, |
109 | 390 NULL); |
391 return -1; | |
392 } | |
393 | |
394 int tagtype = GetTageType(fp); | |
395 | |
396 if (tagtype != TAG_NONE) | |
397 if (DeleteTag(filename) != 0) | |
398 return 0; | |
399 | |
400 // Produce TagItem-Array | |
401 if (strlen(Tag->title) > 0) { | |
402 char *value = (char *) malloc(strlen(Tag->title) + 1); | |
403 | |
404 strcpy(value, Tag->title); | |
405 int res = addValue(&T[TagCount], "Title", value); | |
406 | |
407 estimatedbytes += res; | |
408 if (res > 0) | |
409 TagCount++; | |
410 free(value); | |
411 } | |
412 | |
413 if (strlen(Tag->artist) > 0) { | |
414 char *value = (char *) malloc(strlen(Tag->artist) + 1); | |
415 | |
416 strcpy(value, Tag->artist); | |
417 int res = addValue(&T[TagCount], "Artist", value); | |
418 | |
419 estimatedbytes += res; | |
420 if (res > 0) | |
421 TagCount++; | |
422 free(value); | |
423 } | |
424 | |
425 if (strlen(Tag->album) > 0) { | |
426 char *value = (char *) malloc(strlen(Tag->album) + 1); | |
427 | |
428 strcpy(value, Tag->album); | |
429 int res = addValue(&T[TagCount], "Album", value); | |
430 | |
431 estimatedbytes += res; | |
432 if (res > 0) | |
433 TagCount++; | |
434 free(value); | |
435 } | |
436 | |
437 if (strlen(Tag->comment) > 0) { | |
438 char *value = (char *) malloc(strlen(Tag->comment) + 1); | |
439 | |
440 strcpy(value, Tag->comment); | |
441 int res = addValue(&T[TagCount], "Comment", value); | |
442 | |
443 estimatedbytes += res; | |
444 if (res > 0) | |
445 TagCount++; | |
446 free(value); | |
447 } | |
448 | |
449 if (strlen(Tag->genre) > 0) { | |
450 char *value = (char *) malloc(strlen(Tag->genre) + 1); | |
451 | |
452 strcpy(value, Tag->genre); | |
453 int res = addValue(&T[TagCount], "Genre", value); | |
454 | |
455 estimatedbytes += res; | |
456 if (res > 0) | |
457 TagCount++; | |
458 free(value); | |
459 } | |
460 | |
461 if (strlen(Tag->track) > 0) { | |
462 char *value = (char *) malloc(strlen(Tag->track) + 1); | |
463 | |
464 strcpy(value, Tag->track); | |
465 int res = addValue(&T[TagCount], "Track", value); | |
466 | |
467 estimatedbytes += res; | |
468 if (res > 0) | |
469 TagCount++; | |
470 free(value); | |
471 } | |
472 | |
473 if (strlen(Tag->year) > 0) { | |
474 char *value = (char *) malloc(strlen(Tag->year) + 1); | |
475 | |
476 strcpy(value, Tag->year); | |
477 int res = addValue(&T[TagCount], "Year", value); | |
478 | |
479 estimatedbytes += res; | |
480 if (res > 0) | |
481 TagCount++; | |
482 free(value); | |
483 } | |
484 // Start writing the new Ape2 Tag | |
1978 | 485 aud_vfs_fseek(fp, 0L, SEEK_END); |
109 | 486 |
487 if (TagCount == 0) { | |
488 printf("no tag to write"); | |
489 return 0; | |
490 } | |
491 | |
492 if (estimatedbytes >= 8192 + 103) { | |
493 printf | |
494 ("\nTag is %.1f Kbyte long. This is longer than the maximum recommended 8 KByte.\n\a", | |
495 estimatedbytes / 1024.); | |
496 return 0; | |
497 } | |
498 | |
499 H[8] = Version >> 0; | |
500 H[9] = Version >> 8; | |
501 H[10] = Version >> 16; | |
502 H[11] = Version >> 24; | |
503 H[12] = estimatedbytes >> 0; | |
504 H[13] = estimatedbytes >> 8; | |
505 H[14] = estimatedbytes >> 16; | |
506 H[15] = estimatedbytes >> 24; | |
507 H[16] = TagCount >> 0; | |
508 H[17] = TagCount >> 8; | |
509 H[18] = TagCount >> 16; | |
510 H[19] = TagCount >> 24; | |
511 | |
512 H[23] = 0x80 | 0x20; | |
1978 | 513 writtenbytes += aud_vfs_fwrite(H, 1, 32, fp); |
109 | 514 |
515 for (unsigned int i = 0; i < TagCount; i++) { | |
516 dw[0] = T[i].valuelen >> 0; | |
517 dw[1] = T[i].valuelen >> 8; | |
518 dw[2] = T[i].valuelen >> 16; | |
519 dw[3] = T[i].valuelen >> 24; | |
520 dw[4] = T[i].flags >> 0; | |
521 dw[5] = T[i].flags >> 8; | |
522 dw[6] = T[i].flags >> 16; | |
523 dw[7] = T[i].flags >> 24; | |
1978 | 524 writtenbytes += aud_vfs_fwrite(dw, 1, 8, fp); |
525 writtenbytes += aud_vfs_fwrite(T[i].key, 1, T[i].keylen, fp); | |
526 writtenbytes += aud_vfs_fwrite("", 1, 1, fp); | |
109 | 527 if (T[i].valuelen > 0) |
1978 | 528 writtenbytes += aud_vfs_fwrite(T[i].value, 1, T[i].valuelen, fp); |
109 | 529 } |
530 | |
531 H[23] = 0x80; | |
1978 | 532 writtenbytes += aud_vfs_fwrite(H, 1, 32, fp); |
109 | 533 |
534 if (estimatedbytes != (unsigned long) writtenbytes) | |
535 printf("\nError writing APE tag.\n"); | |
1978 | 536 aud_vfs_fclose(fp); |
109 | 537 TagCount = 0; |
538 return 0; | |
539 } |