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