1 /*
2   This file is part of Deadbeef Player source code
3   http://deadbeef.sourceforge.net
4 
5   library for reading tags from various audio files
6 
7   Copyright (C) 2009-2016 Alexey Yakovenko
8 
9   This software is provided 'as-is', without any express or implied
10   warranty.  In no event will the authors be held liable for any damages
11   arising from the use of this software.
12 
13   Permission is granted to anyone to use this software for any purpose,
14   including commercial applications, and to alter it and redistribute it
15   freely, subject to the following restrictions:
16 
17   1. The origin of this software must not be misrepresented; you must not
18      claim that you wrote the original software. If you use this software
19      in a product, an acknowledgment in the product documentation would be
20      appreciated but is not required.
21   2. Altered source versions must be plainly marked as such, and must not be
22      misrepresented as being the original software.
23   3. This notice may not be removed or altered from any source distribution.
24 
25   Alexey Yakovenko waker@users.sourceforge.net
26 */
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 #include "junklib.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #undef HAVE_ICI
35 #if HAVE_ICONV
36   #include <iconv.h>
37 #elif HAVE_ICU
38   #warning icu
39   #include <unicode/utypes.h>
40   #include <unicode/ucnv.h>
41 #else
42   #define DDB_RECODE
43   #include "ConvertUTF/ConvertUTF.h"
44 uint16_t sj_to_unicode[] = {
45   #include "sj_to_unicode.h"
46 };
47 #endif
48 #include <limits.h>
49 #include <errno.h>
50 #include <ctype.h>
51 #include <unistd.h>
52 #include <assert.h>
53 #include <fcntl.h>
54 #ifndef __linux__
55 #define O_LARGEFILE 0
56 #endif
57 #include <sys/stat.h>
58 #include "playlist.h"
59 #include "utf8.h"
60 #include "plugins.h"
61 #include "conf.h"
62 
63 int enable_cp1251_detection = 1;
64 int enable_cp936_detection = 0;
65 int enable_shift_jis_detection = 0;
66 
67 #define MAX_TEXT_FRAME_SIZE 10000
68 #define MAX_CUESHEET_FRAME_SIZE 10000
69 #define MAX_APEV2_FRAME_SIZE 2000000
70 #define MAX_ID3V2_FRAME_SIZE 100000
71 #define MAX_ID3V2_APIC_FRAME_SIZE 2000000
72 
73 #define UTF8_STR "utf-8"
74 
75 //#define trace(...) { fprintf(stderr, __VA_ARGS__); }
76 #define trace(fmt,...)
77 
78 #define min(x,y) ((x)<(y)?(x):(y))
79 #define max(x,y) ((x)>(y)?(x):(y))
80 
81 
82 // mapping between ddb metadata names and id3v2/apev2 names
83 #define FRAME_MAPPINGS 5
84 enum {
85     MAP_DDB = 0,
86     MAP_ID3V23 = 1,
87     MAP_ID3V24 = 2,
88     MAP_ID3V22 = 3,
89     MAP_APEV2 = 4
90 };
91 
92 // map of known id3v2 and apev2 text tags
93 // order: ddb, id3-2.3, 2.4, 2.2, apev2
94 static const char *frame_mapping[] = {
95 // these tags will be displayed and edited uniformly for all tag types
96     "artist", "TPE1", "TPE1", "TP1", "Artist",
97     "disc", "TPOS", "TPOS", "TPA", "Disc", // NOTE: this is special case when writing id3v2
98     "title", "TIT2", "TIT2", "TT2", "Title",
99     "album", "TALB", "TALB", "TAL", "Album",
100     "copyright", "TCOP", "TCOP", "TCO", "Copyright",
101     "genre", "TCON", "TCON", "TCO", "Genre",
102     "composer", "TCOM", "TCOM", "TCM", "Composer",
103     "year", "TYER", "TDRC", "TYE", "Year", // NOTE: TDRC and TYER are slightly different, and are converted on read/write
104     "track", "TRCK", "TRCK", "TRK", "Track", // NOTE: this is special case when writing id3v2
105 // misc id3v2 fields
106 // these might or might not have appropriate fields in every tag type
107     "BAND", "TPE2", "TPE2", "TP2", NULL,
108     "ENCODER", "TENC", "TENC", "TEN", NULL,
109     "BEATS_PER_MINUTE", "TBPM", "TBPM", "TBP", NULL,
110     "PLAYLIST_DELAY", "TDLY", "TDLY", "TDY", NULL,
111     "TEXT_WRITERS", "TEXT", "TEXT", "TXT", NULL,
112     "FILE_TYPE", "TFLT", "TFLT", "TFT", NULL,
113     "CONTENT_GROUP_DESCRIPTION", "TIT1", "TIT1", "TT1", NULL,
114     "SUBTITLE", "TIT3", "TIT3", "TT3", NULL,
115     "INITIAL_KEY", "TKEY", "TKEY", "TKE", NULL,
116     "LANGUAGES", "TLAN", "TLAN", "TLA", NULL,
117     "LENGTH", "TLEN", "TLEN", "TLE", NULL,
118     "MEDIA_TYPE", "TMED", "TMED", "TMT", NULL,
119     "ORIGINAL_ALBUM_TITLE", "TOAL", "TOAL", "TOT", NULL,
120     "ORIGINAL_FILENAME", "TOFN", "TOFN", "TOF", NULL,
121     "ORIGINAL_TEXT_WRITERS", "TOLY", "TOLY", "TOL", NULL,
122     "ORIGINAL_ARTISTS", "TOPE", "TOPE", "TOA", NULL,
123     "FILE_OWNER", "TOWN", "TOWN", NULL, NULL,
124     "PERFORMER_REFINEMENT", "TPE3", "TPE3", "TP3", NULL,
125     "MODIFIED_BY", "TPE4", "TPE4", "TP4", NULL,
126     "PUBLISHER", "TPUB", "TPUB", "TPB", NULL,
127     "INTERNET_RADIO_STATION_NAME", "TRSN", "TRSN", NULL, NULL,
128     "INTERNET_RADIO_STATION_OWNER", "TRSO", "TRSO", NULL, NULL,
129     "ISRC", "TSRC", "TSRC", NULL, NULL,
130     "ENCODING_SOFTWARE_HARDWARE", "TSSE", "TSSE", "TSS", NULL,
131     "RECORDING_TIME", NULL, "TDRC", NULL, NULL,
132     "RELEASE_TIME", NULL, "TDRL", NULL, NULL,
133     "TAGGING_TIME", NULL, "TDTG", NULL, NULL,
134     "ALBUM_SORT_ORDER", NULL, "TSOA", NULL, NULL,
135     "PERFORMER_SORT_ORDER", NULL, "TSOP", NULL, NULL,
136     "TITLE_SORT_ORDER", NULL, "TSOT", NULL, NULL,
137     "SIZE", "TSIZ", NULL, "TSI", NULL,
138     "RECORDING_DATES", "TRDA", NULL, "TRD", NULL,
139     "INVOLVED_PEOPLE_LIST", NULL, "TIPL", NULL, NULL,
140     "MUSICIAN_CREDITS_LIST", NULL, "TMCL", NULL, NULL,
141     "ENCODING_TIME", NULL, "TDEN", NULL, NULL,
142     "ORIGINAL_RELEASE_TIME", NULL, "TDOR", NULL, NULL,
143     "ORIGINAL_RELEASE_YEAR", NULL, NULL, NULL, "ORIGINALYEAR",
144     "MOOD", NULL, "TMOO", NULL, NULL,
145     "PRODUCED_NOTICE", NULL, "TPRO", NULL, NULL,
146     "musicbrainz_trackid", NULL, NULL, NULL, NULL,
147     NULL
148 };
149 
150 // replaygain key names in both id3v2.3+ TXX and APEv2
151 static const char *tag_rg_names[] = {
152     "replaygain_album_gain",
153     "replaygain_album_peak",
154     "replaygain_track_gain",
155     "replaygain_track_peak",
156     NULL
157 };
158 
159 // replaygain key names in deadbeef internal metadata
160 const char *ddb_internal_rg_keys[] = {
161     ":REPLAYGAIN_ALBUMGAIN",
162     ":REPLAYGAIN_ALBUMPEAK",
163     ":REPLAYGAIN_TRACKGAIN",
164     ":REPLAYGAIN_TRACKPEAK",
165     NULL
166 };
167 
168 static uint32_t
extract_i32(const uint8_t * buf)169 extract_i32 (const uint8_t *buf)
170 {
171     uint32_t x;
172     // big endian extract
173 
174     x = buf[0];
175     x <<= 8;
176     x |= buf[1];
177     x <<= 8;
178     x |= buf[2];
179     x <<= 8;
180     x |= buf[3];
181 
182     return x;
183 }
184 
185 static inline uint32_t
extract_i32_le(unsigned char * buf)186 extract_i32_le (unsigned char *buf)
187 {
188     uint32_t x;
189     // little endian extract
190 
191     x = buf[3];
192     x <<= 8;
193     x |= buf[2];
194     x <<= 8;
195     x |= buf[1];
196     x <<= 8;
197     x |= buf[0];
198 
199     return x;
200 }
201 
202 static inline uint16_t
extract_i16(const uint8_t * buf)203 extract_i16 (const uint8_t *buf)
204 {
205     uint16_t x;
206     // big endian extract
207 
208     x = buf[0];
209     x <<= 8;
210     x |= buf[1];
211 
212     return x;
213 }
214 
215 static inline float
extract_f32(unsigned char * buf)216 extract_f32 (unsigned char *buf) {
217     float f;
218     uint32_t *x = (uint32_t *)&f;
219     *x = buf[0];
220     *x <<= 8;
221     *x |= buf[1];
222     *x <<= 8;
223     *x |= buf[2];
224     *x <<= 8;
225     *x |= buf[3];
226     return f;
227 }
228 
229 #ifdef DDB_RECODE
230 
231 static int
cp1251_to_utf8(const uint8_t * in,int inlen,uint8_t * out,int outlen)232 cp1251_to_utf8(const uint8_t *in, int inlen, uint8_t *out, int outlen) {
233     static const long utf[256] = {
234 		0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,
235 		31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,
236 		59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,
237 		87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,
238 		111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,1026,1027,8218,
239 		1107,8222,8230,8224,8225,8364,8240,1033,8249,1034,1036,1035,1039,1106,8216,8217,
240 		8220,8221,8226,8211,8212,8250,8482,1113,8250,1114,1116,1115,1119,160,1038,1118,1032,
241 		164,1168,166,167,1025,169,1028,171,172,173,174,1031,176,177,1030,1110,1169,181,182,
242 		183,1105,8470,1108,187,1112,1029,1109,1111,1040,1041,1042,1043,1044,1045,1046,1047,
243 		1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,
244 		1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,
245 		1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,
246 		1096,1097,1098,1099,1100,1101,1102,1103
247 	};
248 
249     uint8_t *out_start = out;
250     uint8_t *end = out + outlen;
251 
252 	for(int i = 0; i < inlen && out < end - 4; i++) {
253 		long c = utf[*in++];
254 		if (c < 0x80) {
255 			*out++ = c;
256 		}
257 		else if (c < 0x800) {
258 			*out++ = (c >> 6) | 0xc0;
259 			*out++ = (c & 0x3f) | 0x80;
260 		}
261 		else if( c < 0x10000 ) {
262 			*out++ = (c >> 12) | 0xe0;
263 			*out++ = ((c >> 6) & 0x3f) | 0x80;
264 			*out++ = (c & 0x3f) | 0x80;
265 		}
266 	}
267 	*out++ = 0;
268     return (int)(out - out_start);
269 }
270 
271 int
junk_cp1252_to_utf8(const uint8_t * in,int inlen,uint8_t * out,int outlen)272 junk_cp1252_to_utf8(const uint8_t *in, int inlen, uint8_t *out, int outlen) {
273     int len = 0;
274     while (inlen > 0 && outlen-len > 2) {
275         uint8_t c=*in;
276 
277         switch (c) {
278         case 192 ... 255:
279             *out++ = 195;
280             *out++ = c - 64;
281             len += 2;
282             break;
283         case 160 ... 191:
284             *out++ = 0xc2;
285             *out++ = c;
286             len += 2;
287             break;
288 #define CONV2(x,y,z) case x:\
289             *out++ = y;\
290             *out++ = z;\
291             len += 2;\
292             break;
293 #define CONV3(x,y,z,w) case x:\
294             *out++ = y;\
295             *out++ = z;\
296             *out++ = w;\
297             len += 3;\
298             break;
299         CONV2(0x9f,0xc5,0xb8);
300         CONV2(0x9e,0xc5,0xbe);
301         CONV3(0x9d,0xef,0xbf,0xbd);
302         CONV3(0x80,0xe2,0x82,0xac);
303         CONV3(0x81,0xef,0xbf,0xbd);
304         CONV3(0x82,0xe2,0x80,0x9a);
305         CONV2(0x83,0xc6,0x92);
306         CONV3(0x84,0xe2,0x80,0x9e);
307         CONV3(0x85,0xe2,0x80,0xa6);
308         CONV3(0x86,0xe2,0x80,0xa0);
309         CONV3(0x87,0xe2,0x80,0xa1);
310         CONV2(0x88,0xcb,0x86);
311         CONV3(0x89,0xe2,0x80,0xb0);
312         CONV2(0x8a,0xc5,0xa0);
313         CONV3(0x8b,0xe2,0x80,0xb9);
314         CONV2(0x8c,0xc5,0x92);
315         CONV3(0x8d,0xef,0xbf,0xbd);
316         CONV2(0x8e,0xc5,0xbd);
317         CONV3(0x8f,0xef,0xbf,0xbd);
318         CONV3(0x90,0xef,0xbf,0xbd);
319         CONV3(0x91,0xe2,0x80,0x98);
320         CONV3(0x92,0xe2,0x80,0x99);
321         CONV3(0x93,0xe2,0x80,0x9c);
322         CONV3(0x94,0xe2,0x80,0x9d);
323         CONV3(0x95,0xe2,0x80,0xa2);
324         CONV3(0x96,0xe2,0x80,0x93);
325         CONV3(0x97,0xe2,0x80,0x94);
326         CONV2(0x98,0xcb,0x9c);
327         CONV3(0x99,0xe2,0x84,0xa2);
328         CONV2(0x9a,0xc5,0xa1);
329         CONV3(0x9b,0xe2,0x80,0xba);
330         CONV2(0x9c,0xc5,0x93);
331 #undef CONV2
332 #undef CONV3
333         default:
334             if (c >= 127) {
335                 trace ("iso8859 char: %d\n", c);
336             }
337             *out++ = c;
338             len++;
339             break;
340         }
341 
342         in++;
343         inlen--;
344     }
345     *out = 0;
346     return len;
347 }
348 
349 int
junk_utf8_to_cp1252(const uint8_t * in,int inlen,uint8_t * out,int outlen)350 junk_utf8_to_cp1252(const uint8_t *in, int inlen, uint8_t *out, int outlen) {
351     uint8_t *outptr = out;
352     const char *cp1252_charset[] = {"€", "", "‚", "ƒ", "„", "…", "†", "‡", "ˆ", "‰", "Š", "‹", "Œ", "", "Ž", "", "", "‘", "’", "“", "”", "•", "–", "—", "˜", "™", "š", "›", "œ", "", "ž", "Ÿ", " ", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "­", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", NULL};
353     while (inlen && outlen > 0) {
354         if (*in < 0x80) {
355             *out++ = *in++;
356             outlen--;
357             inlen--;
358         }
359         else {
360             int idx = 0;
361             u8_inc((char *)in, &idx);
362             int i;
363             for (i = 0; cp1252_charset[i]; i++) {
364                 if (strlen (cp1252_charset[i]) == idx && !memcmp (in, cp1252_charset[i], idx)) {
365                     *out++ = i + 0x80;
366                     outlen--;
367                     break;
368                 }
369             }
370             if (!cp1252_charset[i]) {
371                 return -1;
372             }
373             in += idx;
374             inlen -= idx;
375         }
376     }
377     *out = 0;
378     return (int)(out-outptr);
379 }
380 
381 int
junk_utf8_to_ascii(const uint8_t * in,int inlen,uint8_t * out,int outlen)382 junk_utf8_to_ascii(const uint8_t *in, int inlen, uint8_t *out, int outlen) {
383     uint8_t *outptr = out;
384     while (inlen && outlen > 0) {
385         if (*in < 0x80) {
386             *out++ = *in++;
387             outlen--;
388             inlen--;
389         }
390         else {
391             int idx = 0;
392             u8_inc((char *)in, &idx);
393             in += idx;
394             inlen -= idx;
395         }
396     }
397     *out = 0;
398     return (int)(out-outptr);
399 }
400 
401 ConversionResult
ConvertUTF16BEtoUTF8(const UTF16 ** sourceStart,const UTF16 * sourceEnd,UTF8 ** targetStart,UTF8 * targetEnd,ConversionFlags flags)402 ConvertUTF16BEtoUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
403     // swap to make it little endian
404     size_t sourceLESize = (size_t)(sourceEnd - *sourceStart) * sizeof (UTF16);
405     UTF16 sourceLE[sourceLESize];
406     UTF16 *pLE = sourceLE;
407     for (const UTF16 *p = *sourceStart; p != sourceEnd; p++) {
408         *pLE++ = extract_i16((const uint8_t *)p);
409     }
410     const UTF16 *leStart = sourceLE;
411     ConversionResult res = ConvertUTF16toUTF8(&leStart, pLE, targetStart, targetEnd, flags);
412     *sourceStart += pLE - sourceLE;
413     return res;
414 }
415 
416 ConversionResult
ConvertUTF8toUTF16BE(const UTF8 ** sourceStart,const UTF8 * sourceEnd,UTF16 ** targetStart,UTF16 * targetEnd,ConversionFlags flags)417 ConvertUTF8toUTF16BE (const UTF8** sourceStart, const UTF8* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
418     UTF16 *output = *targetStart;
419     ConversionResult res = ConvertUTF8toUTF16(sourceStart, sourceEnd, targetStart, targetEnd, flags);
420     if (res == conversionOK) {
421         for (UTF16 *p = output; p != *targetStart; p++) {
422             *p = extract_i16((const uint8_t *)p);
423         }
424     }
425     return res;
426 }
427 
ddb_iconv(const char * cs_out,const char * cs_in,char * out,int outlen,const char * in,int inlen)428 int ddb_iconv (const char *cs_out, const char *cs_in, char *out, int outlen, const char *in, int inlen) {
429     int len = -1;
430     *out = 0;
431     if (inlen==0) {
432         return 0;
433     }
434 
435 
436     if (!strcmp (cs_in, "UTF-16")) {
437         if (in[0] == 0xfe && in[1] == 0xff) {
438             cs_in = "UTF-16BE";
439         }
440         else {
441             cs_in = "UTF-16LE";
442         }
443     }
444 
445     // to utf8 branch
446     if (!strcasecmp (cs_out, UTF8_STR)) {
447         if (!strcasecmp (cs_in, UTF8_STR)) {
448             memcpy (out, in, inlen);
449             out[inlen] = 0;
450             int valid = u8_valid (out, inlen, NULL);
451             if (valid) {
452                 len = inlen;
453             }
454         }
455         else if (!strcasecmp (cs_in, "cp1251")) {
456             len = cp1251_to_utf8 (in, inlen, out, outlen);
457         }
458         else if (!strcasecmp (cs_in, "iso8859-1") || !strcasecmp (cs_in, "cp1252")) {
459             len = junk_cp1252_to_utf8 (in, inlen, out, outlen);
460         }
461         else if (!strcasecmp (cs_in, "UTF-16LE") || !strcasecmp (cs_in, "UCS-2LE")) {
462             char *target = out;
463             ConversionResult result = ConvertUTF16toUTF8 ((const UTF16**)&in, (const UTF16*)(in + inlen), (UTF8**)&target, (UTF8*)(out + outlen), strictConversion);
464             if (result == conversionOK) {
465                 *target = 0;
466                 len = target - out;
467             }
468         }
469         else if (!strcasecmp (cs_in, "UTF-16BE") || !strcasecmp (cs_in, "UCS-2BE")) {
470             // convert to big endian
471             char temp[inlen];
472             for (int i = 0; i < inlen; i += 2) {
473                 temp[i] = in[i+1];
474                 temp[i+1] = in[i];
475             }
476             char *target = out;
477             char *src = temp;
478             ConversionResult result = ConvertUTF16BEtoUTF8 ((const UTF16**)&src, (const UTF16*)(temp + inlen), (UTF8**)&target, (UTF8*)(out + outlen), strictConversion);
479             if (result == conversionOK) {
480                 *target = 0;
481                 len = target - out;
482             }
483         }
484         else if (!strcasecmp (cs_in, "SHIFT-JIS")) {
485             int len = 0;
486             while (inlen > 0 && len < outlen) {
487                 if (*in > 0) {
488                     *out++ = *in++;
489                     inlen--;
490                     len++;
491                 }
492                 else if (inlen < 2) {
493                     return -1;
494                 }
495                 else {
496                     // find character in table
497                     uint16_t c = (((uint8_t*)in)[0] << 8) | ((uint8_t*)in)[1];
498                     int i;
499                     for (i = 0; sj_to_unicode[i]; i += 2) {
500                         if (c == sj_to_unicode[i]) {
501                             break;
502                         }
503                     }
504                     if (sj_to_unicode[i]) {
505                         // slow conversion!
506                         char unicode_val[2] = { (sj_to_unicode[i+1] & 0xff00) >> 8, sj_to_unicode[i+1] & 0xff };
507                         char utf8_val[5];
508                         char *src = unicode_val, *dst = utf8_val;
509 
510                         ConversionResult res = ConvertUTF16toUTF8 ((const UTF16**)&src, (const UTF16 *)(src+2), (UTF8**)&dst, dst+5, strictConversion);
511                         if (res == conversionOK) {
512                             if (src - utf8_val < outlen-len) {
513                                 memcpy (out, utf8_val, src - utf8_val);
514                                 out += src - utf8_val;
515                                 len += src - utf8_val;
516                                 inlen -= 2;
517                                 in += 2;
518                             }
519                             else {
520                                 return -1;
521                             }
522                         }
523                         else {
524                             return -1;
525                         }
526                     }
527                     else {
528                         return -1; // error
529                     }
530                 }
531             }
532         }
533         else {
534             fprintf (stderr, "invalid conversion request: %s -> %s\n", cs_in, cs_out);
535         }
536     }
537     else if (!strcasecmp (cs_in, UTF8_STR)) {
538         if (!strcasecmp (cs_out, "UTF-16LE") || !strcasecmp (cs_out, "UCS-2LE")) {
539             char *target = out;
540             ConversionResult result = ConvertUTF8toUTF16 ((const UTF8**)&in, (const UTF8*)(in + inlen), (UTF16**)&target, (UTF16*)(out + outlen), strictConversion);
541             if (result == conversionOK) {
542                 *target = 0;
543                 *(target+1) = 0;
544                 len = target - out;
545             }
546         }
547         else if (!strcasecmp (cs_out, "UTF-16BE") || !strcasecmp (cs_out, "UCS-2BE")) {
548             char *target = out;
549             ConversionResult result = ConvertUTF8toUTF16BE ((const UTF8**)&in, (const UTF8*)(in + inlen), (UTF16**)&target, (UTF16*)(out + outlen), strictConversion);
550             if (result == conversionOK) {
551                 *target = 0;
552                 *(target+1) = 0;
553                 len = target - out;
554             }
555         }
556         else if (!strcasecmp (cs_out, "cp1252") || !strcasecmp (cs_out, "iso8859-1")) {
557             int res = junk_utf8_to_cp1252((uint8_t *)in, inlen, (uint8_t *)out, outlen);
558             if (res >= 0) {
559                 len = res;
560             }
561         }
562         else if (!strcasecmp (cs_out, "ascii")) {
563             int res = junk_utf8_to_ascii((uint8_t *)in, inlen, (uint8_t *)out, outlen);
564             if (res >= 0) {
565                 len = res;
566             }
567         }
568         else {
569             fprintf (stderr, "invalid conversion request: %s -> %s\n", cs_in, cs_out);
570         }
571     }
572     else {
573         fprintf (stderr, "invalid conversion request: %s -> %s\n", cs_in, cs_out);
574     }
575     trace ("\033[0;31mddb_iconv: %s -> %s, in: %s, out: %s\033[37;0m\n", cs_in, cs_out, in, out);
576     return len;
577 }
578 #endif
579 
580 int
junk_iconv(const char * in,int inlen,char * out,int outlen,const char * cs_in,const char * cs_out)581 junk_iconv (const char *in, int inlen, char *out, int outlen, const char *cs_in, const char *cs_out) {
582 // NOTE: this function must support utf8->utf8 conversion, used for validation
583 #if HAVE_ICONV
584     iconv_t cd = iconv_open (cs_out, cs_in);
585     if (cd == (iconv_t)-1) {
586         return -1;
587     }
588 #ifdef __linux__
589     char *pin = (char*)in;
590 #else
591     const char *pin = in;
592 #endif
593 
594     size_t inbytesleft = inlen;
595     size_t outbytesleft = outlen;
596 
597     char *pout = out;
598 
599     size_t res = iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft);
600     int err = errno;
601     iconv_close (cd);
602 
603     //trace ("iconv -f %s -t %s '%s': returned %d, inbytes %d/%d, outbytes %d/%d, errno=%d\n", cs_in, cs_out, in, (int)res, inlen, (int)inbytesleft, outlen, (int)outbytesleft, err);
604     if (res == -1) {
605         return -1;
606     }
607     out[pout-out] = 0;
608     //trace ("iconv out: %s (len=%d)\n", out, pout - out);
609     return pout - out;
610 #elif defined(HAVE_ICU)
611     int status = 0;
612     trace ("ICU convert from %s to %s input %s\n", cs_in, cs_out, in);
613     int32_t len = ucnv_convert (cs_out, cs_in, out, outlen, in, inlen, &status);
614     out[len] = 0;
615     trace ("ICU out: %s\n", out);
616     return len;
617 #else
618     int len = ddb_iconv (cs_out, cs_in, out, outlen, in, inlen);
619     return len;
620 #endif
621 }
622 
623 #define ID3V1_GENRE_COUNT (sizeof(junk_genretbl) / sizeof (char *) - 1)
624 static const char *junk_genretbl[] = {
625     "Blues",
626     "Classic Rock",
627     "Country",
628     "Dance",
629     "Disco",
630     "Funk",
631     "Grunge",
632     "Hip-Hop",
633     "Jazz",
634     "Metal",
635     "New Age",
636     "Oldies",
637     "Other",
638     "Pop",
639     "R&B",
640     "Rap",
641     "Reggae",
642     "Rock",
643     "Techno",
644     "Industrial",
645     "Alternative",
646     "Ska",
647     "Death Metal",
648     "Pranks",
649     "Soundtrack",
650     "Euro-Techno",
651     "Ambient",
652     "Trip-Hop",
653     "Vocal",
654     "Jazz+Funk",
655     "Fusion",
656     "Trance",
657     "Classical",
658     "Instrumental",
659     "Acid",
660     "House",
661     "Game",
662     "Sound Clip",
663     "Gospel",
664     "Noise",
665     "AlternRock",
666     "Bass",
667     "Soul",
668     "Punk",
669     "Space",
670     "Meditative",
671     "Instrumental Pop",
672     "Instrumental Rock",
673     "Ethnic",
674     "Gothic",
675     "Darkwave",
676     "Techno-Industrial",
677     "Electronic",
678     "Pop-Folk",
679     "Eurodance",
680     "Dream",
681     "Southern Rock",
682     "Comedy",
683     "Cult",
684     "Gangsta",
685     "Top 40",
686     "Christian Rap",
687     "Pop/Funk",
688     "Jungle",
689     "Native American",
690     "Cabaret",
691     "New Wave",
692     "Psychedelic",
693     "Rave",
694     "Showtunes",
695     "Trailer",
696     "Lo-Fi",
697     "Tribal",
698     "Acid Punk",
699     "Acid Jazz",
700     "Polka",
701     "Retro",
702     "Musical",
703     "Rock & Roll",
704     "Hard Rock",
705     "Folk",
706     "Folk-Rock",
707     "National Folk",
708     "Swing",
709     "Fast Fusion",
710     "Bebob",
711     "Latin",
712     "Revival",
713     "Celtic",
714     "Bluegrass",
715     "Avantgarde",
716     "Gothic Rock",
717     "Progressive Rock",
718     "Psychedelic Rock",
719     "Symphonic Rock",
720     "Slow Rock",
721     "Big Band",
722     "Chorus",
723     "Easy Listening",
724     "Acoustic",
725     "Humour",
726     "Speech",
727     "Chanson",
728     "Opera",
729     "Chamber Music",
730     "Sonata",
731     "Symphony",
732     "Booty Bass",
733     "Primus",
734     "Porn Groove",
735     "Satire",
736     "Slow Jam",
737     "Club",
738     "Tango",
739     "Samba",
740     "Folklore",
741     "Ballad",
742     "Power Ballad",
743     "Rhythmic Soul",
744     "Freestyle",
745     "Duet",
746     "Punk Rock",
747     "Drum Solo",
748     "Acapella",
749     "Euro-House",
750     "Dance Hall",
751     "Goa",
752     "Drum & Bass",
753     "Club-House",
754     "Hardcore",
755     "Terror",
756     "Indie",
757     "BritPop",
758     "Negerpunk",
759     "Polsk Punk",
760     "Beat",
761     "Christian Gangsta",
762     "Heavy Metal",
763     "Black Metal",
764     "Crossover",
765     "Contemporary C",
766     "Christian Rock",
767     "Merengue",
768     "Salsa",
769     "Thrash Metal",
770     "Anime",
771     "JPop",
772     "SynthPop",
773     "Abstract",
774     "Art Rock",
775     "Baroque",
776     "Bhangra",
777     "Big Beat",
778     "Breakbeat",
779     "Chillout",
780     "Downtempo",
781     "Dub",
782     "EBM",
783     "Eclectic",
784     "Electro",
785     "Electroclash",
786     "Emo",
787     "Experimental",
788     "Garage",
789     "Global",
790     "IDM",
791     "Illbient",
792     "Industro-Goth",
793     "Jam Band",
794     "Krautrock",
795     "Leftfield",
796     "Lounge",
797     "Math Rock",
798     "New Romantic",
799     "Nu-Breakz",
800     "Post-Punk",
801     "Post-Rock",
802     "Psytrance",
803     "Shoegaze",
804     "Space Rock",
805     "Trop Rock",
806     "World Music",
807     "Neoclassical",
808     "Audiobook",
809     "Audio Theatre",
810     "Neue Deutsche Welle",
811     "Podcast",
812     "Indie Rock",
813     "G-Funk",
814     "Dubstep",
815     "Garage Rock",
816     "Psybient",
817     NULL
818 };
819 
820 static int
can_be_russian(const signed char * str,int size)821 can_be_russian (const signed char *str, int size) {
822     if (!enable_cp1251_detection) {
823         return 0;
824     }
825     int latin = 0;
826     int rus = 0;
827     int rus_in_row = 0;
828     int max_rus_row = 0;
829     int n = 0;
830     for (; n < size; str++, n++) {
831         if ((*str >= 'A' && *str <= 'Z')
832                 || *str >= 'a' && *str <= 'z') {
833             if (rus_in_row > max_rus_row) {
834                 max_rus_row = rus_in_row;
835             }
836             rus_in_row = 0;
837             latin++;
838         }
839         else if (*str < 0) {
840             rus_in_row++;
841             rus++;
842         }
843     }
844     if (rus > latin/2 || (max_rus_row > 4)) {
845         return 1;
846     }
847     return 0;
848 }
849 
850 static int
can_be_chinese(const uint8_t * str,int sz)851 can_be_chinese (const uint8_t *str, int sz) {
852     if (!enable_cp936_detection) {
853         return 0;
854     }
855     int len = strlen (str);
856     for (int i = 0; i < sz; str++, i++) {
857         if (i < len-3
858                 && (*str >= 0x81 && *str <= 0xFE )
859                 && (*(str+1) >= 0x30 && *(str+1) <= 0x39)
860                 && (*(str+2) >= 0x81 && *(str+2) <= 0xFE)
861                 && (*(str+3) >= 0x30 && *(str+3) <= 0x39)) {
862             return 1;
863         }
864         if (i < len - 1
865                 && (*str >= 0x81 && *str <= 0xFE )
866                 && ((*(str+1) >= 0x40 && *(str+1) <= 0x7E)
867                     || (*(str+1) >= 0x80 && *(str+1) <= 0xFE))) {
868             return 1;
869         }
870     }
871     return 0;
872 }
873 
874 static int
can_be_shift_jis(const unsigned char * str,int size)875 can_be_shift_jis (const unsigned char *str, int size) {
876     unsigned char out[size*4];
877 
878     if (size < 2) {
879         return 0;
880     }
881 
882     const unsigned char *p = str;
883     int s = size;
884     while (s >= 2) {
885         if ((((p[0] >= 0x81 && p[0] <= 0x84) || (p[0] >= 0x87 && p[0] <= 0x9f))
886                     && ((p[1] >= 0x40 && p[1] <= 0x9e) || (p[1] >= 0x9f && p[1] <= 0xfc)))
887                 || ((p[0] >= 0xe0 && p[0] <= 0xef)
888                     && ((p[1] >= 0x40 && p[1] <= 0x9e) || (p[1] >= 0x9f && p[1] <= 0xfc)))) {
889             break;
890         }
891         s--;
892         p++;
893     }
894 
895     if (s >= 2) {
896         if (junk_iconv (str, size, out, sizeof (out), "shift-jis", UTF8_STR) >= 0) {
897             return 1;
898         }
899     }
900     return 0;
901 
902 }
903 
904 
905 static char *
convstr_id3v2(int version,uint8_t encoding,const uint8_t * str,int sz)906 convstr_id3v2 (int version, uint8_t encoding, const uint8_t *str, int sz) {
907     const char *enc = NULL;
908 
909     // detect encoding
910     if (version == 4 && encoding == 2) {
911         enc = "UTF-16BE";
912     }
913     else if (version == 4 && encoding == 3) {
914         enc = UTF8_STR;
915     }
916     else if (encoding == 0) {
917         if (can_be_chinese (str, sz)) {
918             // hack to add cp936 support
919             enc = "cp936";
920         }
921         else if (can_be_russian (str, sz)) {
922             // hack to add limited cp1251 recoding support
923             enc = "cp1251";
924         }
925         else {
926             enc = "cp1252";
927         }
928     }
929     else if (encoding != 1 && !(version == 4 && encoding == 3)){
930         return NULL; // invalid encoding
931     }
932 
933     if (encoding == 1) { // detect kind of unicode used
934         if (sz < 2) {
935             return NULL;
936         }
937         if (version < 4) {
938             if (str[0] == 0xff && str[1] == 0xfe) {
939                 enc = "UCS-2LE";
940                 str += 2;
941                 sz -= 2;
942             }
943             else if (str[1] == 0xff && str[0] == 0xfe) {
944                 enc = "UCS-2BE";
945                 str += 2;
946                 sz -= 2;
947             }
948             else {
949                 trace ("invalid ucs-2 signature %x %x\n", (int)str[0], (int)str[1]);
950                 enc = "UCS-2LE";
951                 // NOTE: this is an assumption, might break in the future.
952             }
953         }
954         else {
955             enc = "UTF-16";
956         }
957     }
958 
959     trace ("encoding: %s\n", enc);
960 
961     int converted_sz = 0;
962 
963     int outlen = sz*4+1;
964     char *out = malloc (outlen);
965     if ((converted_sz = junk_iconv (str, sz, out, outlen, enc, UTF8_STR)) < 0) {
966         free (out);
967         return NULL;
968     }
969 //    trace ("%s -> %s\n", str, out);
970     int n;
971     for (n = 0; n < converted_sz; n++) {
972         if (out[n] == 0 && n != converted_sz-1) {
973             out[n] = '\n';
974         }
975     }
976     // trim trailing linebreaks
977     for (n = converted_sz-1; n >= 0; n--) {
978         if ((uint8_t)out[n] <= 32) {
979             out[n] = 0;
980         }
981         else {
982             break;
983         }
984     }
985     return out;
986 }
987 
988 static const char *
convstr_id3v1(const char * str,int sz,const char * charset,char * out,int outsize)989 convstr_id3v1 (const char* str, int sz, const char *charset, char *out, int outsize) {
990     if (!charset) {
991         return str;
992     }
993     int i;
994     for (i = 0; i < sz; i++) {
995         if (str[i] != ' ') {
996             break;
997         }
998     }
999     if (i == sz) {
1000         out[0] = 0;
1001         return out;
1002     }
1003 
1004     int len = junk_iconv (str, sz, out, outsize, charset, UTF8_STR);
1005     if (len >= 0) {
1006         return out;
1007     }
1008     return NULL;
1009 }
1010 
1011 static void
str_trim_right(uint8_t * str,int len)1012 str_trim_right (uint8_t *str, int len) {
1013     uint8_t *p = str + len - 1;
1014     while (p >= str && *p <= 0x20) {
1015         p--;
1016     }
1017     p++;
1018     *p = 0;
1019 }
1020 
1021 int
junk_id3v1_read_int(playItem_t * it,char * buffer,const char ** charset)1022 junk_id3v1_read_int (playItem_t *it, char *buffer, const char **charset) {
1023     if (!buffer) {
1024         return -1;
1025     }
1026 
1027     if (it) {
1028         if (memcmp (buffer, "TAG", 3)) {
1029             return -1; // no tag
1030         }
1031         const char *cs = NULL;
1032         charset = &cs;
1033         int res = junk_id3v1_read_int (NULL, buffer, charset);
1034         if (res) {
1035             return res;
1036         }
1037     }
1038 
1039     if (!charset) {
1040         return -1;
1041     }
1042 
1043     char title[31];
1044     char artist[31];
1045     char album[31];
1046     char year[5];
1047     char comment[31];
1048     uint8_t genreid;
1049     uint8_t tracknum;
1050     const char *genre = NULL;
1051     memset (title, 0, 31);
1052     memset (artist, 0, 31);
1053     memset (album, 0, 31);
1054     memset (year, 0, 5);
1055     memset (comment, 0, 31);
1056     memcpy (title, &buffer[3], 30);
1057     str_trim_right (title, 30);
1058     memcpy (artist, &buffer[3+30], 30);
1059     str_trim_right (artist, 30);
1060     memcpy (album, &buffer[3+60], 30);
1061     str_trim_right (album, 30);
1062     memcpy (year, &buffer[3+90], 4);
1063     str_trim_right (year, 4);
1064     memcpy (comment, &buffer[3+94], 30);
1065     str_trim_right (comment, 30);
1066     genreid = buffer[3+124];
1067     tracknum = 0;
1068     if (it) {
1069         if (comment[28] == 0 && comment[29] != 0) {
1070             tracknum = comment[29];
1071         }
1072 
1073         // 255 = "None",
1074         // "CR" = "Cover" (id3v2)
1075         // "RX" = "Remix" (id3v2)
1076         if (genreid == 0xff) {
1077             //genre = "None";
1078         }
1079         else if (genreid < ID3V1_GENRE_COUNT) {
1080             genre = junk_genretbl[genreid];
1081         }
1082     }
1083 
1084     // add meta
1085 //    trace ("%s - %s - %s - %s - %s - %s\n", title, artist, album, year, comment, genre);
1086     if (!it) {
1087         char buf[129];
1088         char *p = buf;
1089         strcpy (p, title);
1090         p += strlen (title);
1091         strcpy (p, artist);
1092         p += strlen (artist);
1093         strcpy (p, album);
1094         p += strlen (album);
1095         strcpy (p, year);
1096         p += strlen (year);
1097         strcpy (p, comment);
1098         *charset = junk_detect_charset (buf);
1099         return 0;
1100     }
1101 
1102     char utf8_value[150];
1103 
1104     if (*title) {
1105         pl_add_meta (it, "title", convstr_id3v1 (title, (int)strlen (title), *charset, utf8_value, sizeof (utf8_value)));
1106     }
1107     if (*artist) {
1108         pl_add_meta (it, "artist", convstr_id3v1 (artist, (int)strlen (artist), *charset, utf8_value, sizeof (utf8_value)));
1109     }
1110     if (*album) {
1111         pl_add_meta (it, "album", convstr_id3v1 (album, (int)strlen (album), *charset, utf8_value, sizeof (utf8_value)));
1112     }
1113     if (*year) {
1114         pl_add_meta (it, "year", convstr_id3v1 (year, (int)strlen (year), *charset, utf8_value, sizeof (utf8_value)));
1115     }
1116     if (*comment) {
1117         pl_add_meta (it, "comment", convstr_id3v1 (comment, (int)strlen (comment), *charset, utf8_value, sizeof (utf8_value)));
1118     }
1119     if (genre && *genre) {
1120         pl_add_meta (it, "genre", convstr_id3v1 (genre, (int)strlen (genre), *charset, utf8_value, sizeof (utf8_value)));
1121     }
1122     if (tracknum != 0) {
1123         char s[4];
1124         snprintf (s, 4, "%d", tracknum);
1125         pl_add_meta (it, "track", s);
1126     }
1127 
1128     uint32_t f = pl_get_item_flags (it);
1129     f |= DDB_TAG_ID3V1;
1130     pl_set_item_flags (it, f);
1131 
1132     return 0;
1133 }
1134 
1135 // should read both id3v1 and id3v1.1
1136 int
junk_id3v1_read(playItem_t * it,DB_FILE * fp)1137 junk_id3v1_read (playItem_t *it, DB_FILE *fp) {
1138     uint8_t id3[128];
1139 
1140     if (deadbeef->fseek (fp, -128, SEEK_END) == -1) {
1141         return -1;
1142     }
1143     if (deadbeef->fread (id3, 1, 128, fp) != 128) {
1144         return -1;
1145     }
1146 
1147     return junk_id3v1_read_int (it, id3, NULL);
1148 }
1149 
1150 int
junk_id3v1_write2(int fd,playItem_t * it,const char * enc)1151 junk_id3v1_write2 (int fd, playItem_t *it, const char *enc) {
1152     char title[30] = "";
1153     char artist[30] = "";
1154     char album[30] = "";
1155     char year[4] = "";
1156     char comment[28] = "";
1157     uint8_t genreid = 0xff;
1158     uint8_t tracknum = 0;
1159 
1160     const char *meta;
1161 
1162     pl_lock ();
1163 
1164 #define conv(name, store) {\
1165     memset (store, 0x20, sizeof (store));\
1166     meta = pl_find_meta (it, name);\
1167     if (meta) {\
1168         char temp[1000];\
1169         int l = junk_iconv (meta, strlen (meta), temp, sizeof (temp), UTF8_STR, enc);\
1170         if (l == -1) {\
1171             memset (store, 0, sizeof (store));\
1172         }\
1173         else {\
1174             strncpy (store, temp, sizeof (store));\
1175         }\
1176         char *cr = strchr (store, '\n');\
1177         if (cr) {\
1178             *cr = 0;\
1179         }\
1180     }\
1181 }
1182 
1183     conv ("title", title);
1184     conv ("artist", artist);
1185     conv ("album", album);
1186     conv ("year", year);
1187     conv ("comment", comment);
1188 
1189 #undef conv
1190 
1191     // tracknum
1192     meta = pl_find_meta (it, "track");
1193     if (meta) {
1194         tracknum = atoi (meta);
1195     }
1196 
1197     // find genre
1198     meta = pl_find_meta (it, "genre");
1199     if (meta) {
1200         for (int i = 0; junk_genretbl[i]; i++) {
1201             if (!strcasecmp (meta, junk_genretbl[i])) {
1202                 genreid = i;
1203                 break;
1204             }
1205         }
1206         // workaround for the id3v1 std spelling error
1207         if (genreid == 0xff && !strcasecmp (meta, "Psychadelic")) {
1208             genreid = 67;
1209         }
1210     }
1211 
1212     pl_unlock ();
1213 
1214     if (write (fd, "TAG", 3) != 3) {
1215         trace ("junk_id3v1_write: failed to write signature\n");
1216         return -1;
1217     }
1218     if (write (fd, title, sizeof (title)) != sizeof (title)) {
1219         trace ("junk_id3v1_write: failed to write title\n");
1220         return -1;
1221     }
1222     if (write (fd, artist, sizeof (artist)) != sizeof (artist)) {
1223         trace ("junk_id3v1_write: failed to write artist\n");
1224         return -1;
1225     }
1226     if (write (fd, album, sizeof (album)) != sizeof (album)) {
1227         trace ("junk_id3v1_write: failed to write album\n");
1228         return -1;
1229     }
1230     if (write (fd, year, sizeof (year)) != sizeof (year)) {
1231         trace ("junk_id3v1_write: failed to write year\n");
1232         return -1;
1233     }
1234     if (write (fd, comment, sizeof (comment)) != sizeof (comment)) {
1235         trace ("junk_id3v1_write: failed to write comment\n");
1236         return -1;
1237     }
1238     uint8_t zero = 0;
1239     if (write (fd, &zero, 1) != 1) {
1240         trace ("junk_id3v1_write: failed to write id3v1.1 marker\n");
1241         return -1;
1242     }
1243     if (write (fd, &tracknum, 1) != 1) {
1244         trace ("junk_id3v1_write: failed to write track\n");
1245         return -1;
1246     }
1247     if (write (fd, &genreid, 1) != 1) {
1248         trace ("junk_id3v1_write: failed to write genre\n");
1249         return -1;
1250     }
1251     return 0;
1252 }
1253 
1254 int
junk_id3v1_write(FILE * fp,playItem_t * it,const char * enc)1255 junk_id3v1_write (FILE *fp, playItem_t *it, const char *enc) {
1256     char title[30] = "";
1257     char artist[30] = "";
1258     char album[30] = "";
1259     char year[4] = "";
1260     char comment[28] = "";
1261     uint8_t genreid = 0xff;
1262     uint8_t tracknum = 0;
1263 
1264     const char *meta;
1265 
1266     pl_lock ();
1267 
1268 #define conv(name, store) {\
1269     memset (store, 0x20, sizeof (store));\
1270     meta = pl_find_meta (it, name);\
1271     if (meta) {\
1272         char temp[1000];\
1273         int l = junk_iconv (meta, strlen (meta), temp, sizeof (temp), UTF8_STR, enc);\
1274         if (l == -1) {\
1275             memset (store, 0, sizeof (store));\
1276         }\
1277         else {\
1278             strncpy (store, temp, sizeof (store));\
1279         }\
1280         char *cr = strchr (store, '\n');\
1281         if (cr) {\
1282             *cr = 0;\
1283         }\
1284     }\
1285 }
1286 
1287     conv ("title", title);
1288     conv ("artist", artist);
1289     conv ("album", album);
1290     conv ("year", year);
1291     conv ("comment", comment);
1292 
1293 #undef conv
1294 
1295     // tracknum
1296     meta = pl_find_meta (it, "track");
1297     if (meta) {
1298         tracknum = atoi (meta);
1299     }
1300 
1301     // find genre
1302     meta = pl_find_meta (it, "genre");
1303     if (meta) {
1304         for (int i = 0; junk_genretbl[i]; i++) {
1305             if (!strcasecmp (meta, junk_genretbl[i])) {
1306                 genreid = i;
1307                 break;
1308             }
1309         }
1310         // workaround for the id3v1 std spelling error
1311         if (genreid == 0xff && !strcasecmp (meta, "Psychadelic")) {
1312             genreid = 67;
1313         }
1314     }
1315 
1316     pl_unlock ();
1317 
1318     if (fwrite ("TAG", 1, 3, fp) != 3) {
1319         trace ("junk_id3v1_write: failed to write signature\n");
1320         return -1;
1321     }
1322     if (fwrite (title, 1, sizeof (title), fp) != sizeof (title)) {
1323         trace ("junk_id3v1_write: failed to write title\n");
1324         return -1;
1325     }
1326     if (fwrite (artist, 1, sizeof (artist), fp) != sizeof (artist)) {
1327         trace ("junk_id3v1_write: failed to write artist\n");
1328         return -1;
1329     }
1330     if (fwrite (album, 1, sizeof (album), fp) != sizeof (album)) {
1331         trace ("junk_id3v1_write: failed to write album\n");
1332         return -1;
1333     }
1334     if (fwrite (year, 1, sizeof (year), fp) != sizeof (year)) {
1335         trace ("junk_id3v1_write: failed to write year\n");
1336         return -1;
1337     }
1338     if (fwrite (comment, 1, sizeof (comment), fp) != sizeof (comment)) {
1339         trace ("junk_id3v1_write: failed to write comment\n");
1340         return -1;
1341     }
1342     uint8_t zero = 0;
1343     if (fwrite (&zero, 1, 1, fp) != 1) {
1344         trace ("junk_id3v1_write: failed to write id3v1.1 marker\n");
1345         return -1;
1346     }
1347     if (fwrite (&tracknum, 1, 1, fp) != 1) {
1348         trace ("junk_id3v1_write: failed to write track\n");
1349         return -1;
1350     }
1351     if (fwrite (&genreid, 1, 1, fp) != 1) {
1352         trace ("junk_id3v1_write: failed to write genre\n");
1353         return -1;
1354     }
1355     return 0;
1356 }
1357 
1358 int64_t
junk_id3v1_find2(DB_FILE * fp)1359 junk_id3v1_find2 (DB_FILE *fp) {
1360     uint8_t buffer[3];
1361     if (deadbeef->fseek (fp, -128, SEEK_END) == -1) {
1362         return -1;
1363     }
1364     if (deadbeef->fread (buffer, 1, 3, fp) != 3) {
1365         return -1;
1366     }
1367     if (memcmp (buffer, "TAG", 3)) {
1368         return -1; // no tag
1369     }
1370     return deadbeef->ftell (fp) - 3;
1371 }
1372 
1373 int
junk_id3v1_find(DB_FILE * fp)1374 junk_id3v1_find (DB_FILE *fp) {
1375     int64_t pos = junk_id3v1_find2 (fp);
1376     // 32 bit overflow protection
1377     if (pos > 0x7fffffff) {
1378         return -1;
1379     }
1380     return pos;
1381 }
1382 
1383 int64_t
junk_apev2_find2(DB_FILE * fp,int32_t * psize,uint32_t * pflags,uint32_t * pnumitems)1384 junk_apev2_find2 (DB_FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems) {
1385     uint8_t header[32];
1386     if (deadbeef->fseek (fp, -32, SEEK_END) == -1) {
1387         return -1; // something bad happened
1388     }
1389 
1390     if (deadbeef->fread (header, 1, 32, fp) != 32) {
1391         return -1; // something bad happened
1392     }
1393     if (strncmp (header, "APETAGEX", 8)) {
1394         // try to skip 128 bytes backwards (id3v1)
1395         if (deadbeef->fseek (fp, -128-32, SEEK_END) == -1) {
1396             return -1; // something bad happened
1397         }
1398         if (deadbeef->fread (header, 1, 32, fp) != 32) {
1399             return -1; // something bad happened
1400         }
1401         if (strncmp (header, "APETAGEX", 8)) {
1402             return -1; // no ape tag here
1403         }
1404     }
1405 
1406     uint32_t version = extract_i32_le (&header[8]);
1407     int32_t size = extract_i32_le (&header[12]);
1408     uint32_t numitems = extract_i32_le (&header[16]);
1409     uint32_t flags = extract_i32_le (&header[20]);
1410 
1411     trace ("APEv%d, size=%d, items=%d, flags=%x\n", version, size, numitems, flags);
1412 
1413     // size contains footer, but not header, so add header size
1414     if (flags & (1<<31)) {
1415         size += 32;
1416     }
1417 
1418     // seek to beginning of the tag/header
1419     if (deadbeef->fseek (fp, -size, SEEK_CUR) == -1) {
1420         trace ("failed to seek to tag start (-%d)\n", size);
1421         return -1;
1422     }
1423     *psize = size;
1424     *pflags = flags;
1425     *pnumitems = numitems;
1426     return deadbeef->ftell (fp);
1427 }
1428 
1429 int
junk_apev2_find(DB_FILE * fp,int32_t * psize,uint32_t * pflags,uint32_t * pnumitems)1430 junk_apev2_find (DB_FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems) {
1431     int64_t pos = junk_apev2_find2 (fp, psize, pflags, pnumitems);
1432     // 32 bit overflow protection
1433     if (pos > 0x7fffffff) {
1434         return -1;
1435     }
1436     return pos;
1437 }
1438 
1439 int
junk_find_id3v1(DB_FILE * fp)1440 junk_find_id3v1 (DB_FILE *fp) {
1441     if (deadbeef->fseek (fp, -128, SEEK_END) == -1) {
1442         return -1;
1443     }
1444     char buffer[3];
1445     if (deadbeef->fread (buffer, 1, 3, fp) != 3) {
1446         return -1;
1447     }
1448     if (memcmp (buffer, "TAG", 3)) {
1449         return -1; // no tag
1450     }
1451     return deadbeef->ftell (fp) - 3;
1452 }
1453 
1454 int
junk_add_track_meta(playItem_t * it,const char * track)1455 junk_add_track_meta (playItem_t *it, const char *track) {
1456     char *slash = strchr (track, '/');
1457     if (slash) {
1458         // split into track/number
1459         *slash = 0;
1460         slash++;
1461         pl_add_meta (it, "numtracks", slash);
1462     }
1463     pl_add_meta (it, "track", track);
1464     return 0;
1465 }
1466 
1467 int
junk_add_disc_meta(playItem_t * it,const char * disc)1468 junk_add_disc_meta (playItem_t *it, const char *disc) {
1469     char *slash = strchr (disc, '/');
1470     if (slash) {
1471         // split into track/number
1472         *slash = 0;
1473         slash++;
1474         pl_add_meta (it, "numdiscs", slash);
1475     }
1476     pl_add_meta (it, "disc", disc);
1477     return 0;
1478 }
1479 
1480 int
junk_apev2_add_frame(playItem_t * it,DB_apev2_tag_t * tag_store,DB_apev2_frame_t ** tail,uint32_t itemsize,uint32_t itemflags,const char * key,const uint8_t * value)1481 junk_apev2_add_frame (playItem_t *it, DB_apev2_tag_t *tag_store, DB_apev2_frame_t **tail, uint32_t itemsize, uint32_t itemflags, const char *key, const uint8_t *value) {
1482     if (tag_store) {
1483         DB_apev2_frame_t *frm = malloc (sizeof (DB_apev2_frame_t) + itemsize);
1484         memset (frm, 0, sizeof (DB_apev2_tag_t));
1485         frm->flags = itemflags;
1486         strcpy (frm->key, key);
1487         trace ("*** stored frame %s flags %X\n", key, itemflags);
1488         frm->size = itemsize;
1489         memcpy (frm->data, value, itemsize);
1490         if (*tail) {
1491             (*tail)->next = frm;
1492         }
1493         else {
1494             tag_store->frames = frm;
1495         }
1496         *tail = frm;
1497     }
1498 
1499     if (it) {
1500         int valuetype = ((itemflags >> 1) & 3);
1501         // add metainfo only if it's textual
1502         if (valuetype == 0 && (itemsize < MAX_TEXT_FRAME_SIZE || (!strcasecmp (key, "cuesheet") && itemsize < MAX_CUESHEET_FRAME_SIZE))) {
1503             if (!u8_valid (value, itemsize, NULL)) {
1504                 trace ("junk_read_ape_full: bad encoding in text frame %s\n", key);
1505                 return -1;
1506             }
1507 
1508             int m;
1509             for (m = 0; frame_mapping[m]; m += FRAME_MAPPINGS) {
1510                 if (frame_mapping[m + MAP_APEV2] && !strcasecmp (key, frame_mapping[m + MAP_APEV2])) {
1511                     if (!strcmp (frame_mapping[m+MAP_DDB], "track")) {
1512                         junk_add_track_meta (it, value);
1513                     }
1514                     else {
1515                         trace ("pl_append_meta %s %s\n", frame_mapping[m+MAP_DDB], value);
1516                         pl_append_meta (it, frame_mapping[m+MAP_DDB], value);
1517                     }
1518                     break;
1519                 }
1520             }
1521 
1522             trace ("apev2 %s=%s\n", key, value);
1523 
1524             if (!frame_mapping[m]) {
1525                 if (!strncasecmp (key, "replaygain_album_gain", 21)) {
1526                     pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, atof (value));
1527                     trace ("album_gain=%s\n", value);
1528                 }
1529                 else if (!strncasecmp (key, "replaygain_album_peak", 21)) {
1530                     pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, atof (value));
1531                     trace ("album_peak=%s\n", value);
1532                 }
1533                 else if (!strncasecmp (key, "replaygain_track_gain", 21)) {
1534                     pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, atof (value));
1535                     trace ("track_gain=%s\n", value);
1536                 }
1537                 else if (!strncasecmp (key, "replaygain_track_peak", 21)) {
1538                     pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (value));
1539                     trace ("track_peak=%s\n", value);
1540                 }
1541                 else {
1542                     trace ("%s=%s\n", key, value);
1543                     pl_append_meta (it, key, value);;
1544                 }
1545             }
1546         }
1547     }
1548     return 0;
1549 }
1550 
1551 int
junk_apev2_read_full_mem(playItem_t * it,DB_apev2_tag_t * tag_store,char * mem,int memsize)1552 junk_apev2_read_full_mem (playItem_t *it, DB_apev2_tag_t *tag_store, char *mem, int memsize) {
1553     char *end = mem+memsize;
1554 #define STEP(x,y) {mem+=(x);if(mem+(y)>end) {trace ("fail %d\n", (x));return -1;}}
1555 
1556     char *header = mem;
1557 
1558     DB_apev2_frame_t *tail = NULL;
1559 
1560     uint32_t version = extract_i32_le (&header[0]);
1561     int32_t size = extract_i32_le (&header[4]);
1562     uint32_t numitems = extract_i32_le (&header[8]);
1563     uint32_t flags = extract_i32_le (&header[12]);
1564 
1565     trace ("APEv%d, size=%d, items=%d, flags=%x\n", version, size, numitems, flags);
1566     if (it) {
1567         uint32_t f = pl_get_item_flags (it);
1568         f |= DDB_TAG_APEV2;
1569         pl_set_item_flags (it, f);
1570     }
1571 
1572     STEP(24, 8);
1573 
1574     int i;
1575     for (i = 0; i < numitems; i++) {
1576         trace ("reading item %d\n", i);
1577         uint8_t *buffer = mem;
1578 
1579         uint32_t itemsize = extract_i32_le (&buffer[0]);
1580         uint32_t itemflags = extract_i32_le (&buffer[4]);
1581 
1582         STEP(8, 1);
1583         trace ("size=%d, flags=%x\n", itemsize, itemflags);
1584 
1585         // read key until 0 (stupid and slow)
1586         char key[256];
1587         int keysize = 0;
1588         while (keysize <= 255 && mem < end) {
1589             key[keysize] = *mem;
1590             mem++;
1591             if (key[keysize] == 0) {
1592                 break;
1593             }
1594             if (key[keysize] < 0x20) {
1595                 trace ("nonascii chars\n");
1596                 return -1; // non-ascii chars and chars with codes 0..0x1f not allowed in ape item keys
1597             }
1598             keysize++;
1599         }
1600         key[255] = 0;
1601         trace ("item %d, size %d, flags %08x, keysize %d, key %s\n", i, itemsize, itemflags, keysize, key);
1602         // read value
1603         if (itemsize <= MAX_APEV2_FRAME_SIZE) // just a sanity check
1604         {
1605             STEP(0,itemsize);
1606             uint8_t *value = malloc (itemsize+1);
1607             if (!value) {
1608                 trace ("junk_read_ape_full: failed to allocate %d bytes\n", itemsize+1);
1609                 return -1;
1610             }
1611             memcpy (value, mem, itemsize);
1612             value[itemsize] = 0;
1613 
1614             // replace 0s with \n
1615             uint8_t *p = value;
1616             while (p < value + itemsize - 1) {
1617                 if (*p == 0) {
1618                     *p = '\n';
1619                 }
1620                 p++;
1621             }
1622 
1623             junk_apev2_add_frame (it, tag_store, &tail, itemsize, itemflags, key, value);
1624 
1625             free (value);
1626             STEP(itemsize, 8);
1627         }
1628         else {
1629             STEP(itemsize,8);
1630         }
1631     }
1632     return 0;
1633 }
1634 
1635 int
junk_apev2_read_full(playItem_t * it,DB_apev2_tag_t * tag_store,DB_FILE * fp)1636 junk_apev2_read_full (playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp) {
1637     // try to read footer, position must be already at the EOF right before
1638     // id3v1 (if present)
1639 
1640     DB_apev2_frame_t *tail = NULL;
1641 
1642     uint8_t header[32];
1643     if (deadbeef->fseek (fp, -32, SEEK_END) == -1) {
1644         return -1; // something bad happened
1645     }
1646 
1647     if (deadbeef->fread (header, 1, 32, fp) != 32) {
1648         return -1; // something bad happened
1649     }
1650     if (strncmp (header, "APETAGEX", 8)) {
1651         // try to skip 128 bytes backwards (id3v1)
1652         if (deadbeef->fseek (fp, -128-32, SEEK_END) == -1) {
1653             return -1; // something bad happened
1654         }
1655         if (deadbeef->fread (header, 1, 32, fp) != 32) {
1656             return -1; // something bad happened
1657         }
1658         if (strncmp (header, "APETAGEX", 8)) {
1659             return -1; // no ape tag here
1660         }
1661     }
1662 
1663     // end of footer must be 0
1664 //    if (memcmp (&header[24], "\0\0\0\0\0\0\0\0", 8)) {
1665 //        trace ("bad footer\n");
1666 //        return -1;
1667 //    }
1668 
1669     uint32_t version = extract_i32_le (&header[8]);
1670     int32_t size = extract_i32_le (&header[12]);
1671     uint32_t numitems = extract_i32_le (&header[16]);
1672     uint32_t flags = extract_i32_le (&header[20]);
1673 
1674     trace ("APEv%d, size=%d, items=%d, flags=%x\n", version, size, numitems, flags);
1675     if (it) {
1676         uint32_t f = pl_get_item_flags (it);
1677         f |= DDB_TAG_APEV2;
1678         pl_set_item_flags (it, f);
1679     }
1680 
1681     // now seek to beginning of the tag (exluding header)
1682     if (deadbeef->fseek (fp, -size, SEEK_CUR) == -1) {
1683         trace ("failed to seek to tag start (-%d)\n", size);
1684         return -1;
1685     }
1686 
1687     int i;
1688     for (i = 0; i < numitems; i++) {
1689         uint8_t buffer[8];
1690         if (deadbeef->fread (buffer, 1, 8, fp) != 8) {
1691             return -1;
1692         }
1693         uint32_t itemsize = extract_i32_le (&buffer[0]);
1694         uint32_t itemflags = extract_i32_le (&buffer[4]);
1695 
1696         // read key until 0 (stupid and slow)
1697         char key[256];
1698         int keysize = 0;
1699         while (keysize <= 255) {
1700             if (deadbeef->fread (&key[keysize], 1, 1, fp) != 1) {
1701                 return -1;
1702             }
1703             if (key[keysize] == 0) {
1704                 break;
1705             }
1706             if (key[keysize] < 0x20) {
1707                 return -1; // non-ascii chars and chars with codes 0..0x1f not allowed in ape item keys
1708             }
1709             keysize++;
1710         }
1711         key[255] = 0;
1712         trace ("item %d, size %d, flags %08x, keysize %d, key %s\n", i, itemsize, itemflags, keysize, key);
1713         // read value
1714         if (itemsize <= MAX_APEV2_FRAME_SIZE) // just a sanity check
1715         {
1716             uint8_t *value = malloc (itemsize+1);
1717             if (!value) {
1718                 trace ("junk_read_ape_full: failed to allocate %d bytes\n", itemsize+1);
1719                 return -1;
1720             }
1721             if (deadbeef->fread (value, 1, itemsize, fp) != itemsize) {
1722                 trace ("junk_read_ape_full: failed to read %d bytes from file\n", itemsize);
1723                 free (value);
1724                 return -1;
1725             }
1726             value[itemsize] = 0;
1727 
1728             if ((flags&6) == 0 && strncasecmp (key, "cover art ", 10)) {
1729                 // replace 0s with \n
1730                 uint8_t *p = value;
1731                 while (p < value + itemsize - 1) {
1732                     if (*p == 0) {
1733                         *p = '\n';
1734                     }
1735                     p++;
1736                 }
1737             }
1738 
1739             junk_apev2_add_frame (it, tag_store, &tail, itemsize, itemflags, key, value);
1740             free (value);
1741         }
1742         else {
1743             // try to skip
1744             int err = deadbeef->fseek (fp, itemsize, SEEK_CUR);
1745             if (0 != err) {
1746                 perror ("junklib: corrupted APEv2 tag\n");
1747                 return -1;
1748             }
1749         }
1750     }
1751 
1752     return 0;
1753 }
1754 
1755 int
junk_apev2_read(playItem_t * it,DB_FILE * fp)1756 junk_apev2_read (playItem_t *it, DB_FILE *fp) {
1757     return junk_apev2_read_full (it, NULL, fp);
1758 }
1759 
1760 int
junk_apev2_read_mem(playItem_t * it,char * mem,int size)1761 junk_apev2_read_mem (playItem_t *it, char *mem, int size) {
1762     return junk_apev2_read_full_mem (it, NULL, mem, size);
1763 }
1764 
1765 int
junk_id3v2_find(DB_FILE * fp,int * psize)1766 junk_id3v2_find (DB_FILE *fp, int *psize) {
1767     if (deadbeef->fseek (fp, 0, SEEK_SET) == -1) {
1768         trace ("junk_id3v2_find: seek error\n");
1769         return -1;
1770     }
1771     uint8_t header[10];
1772     int pos = deadbeef->ftell (fp);
1773     if (pos == -1) {
1774         trace ("junk_id3v2_find: ftell error\n");
1775         return -1;
1776     }
1777     if (deadbeef->fread (header, 1, 10, fp) != 10) {
1778         trace ("junk_id3v2_find: read error\n");
1779         return -1; // too short
1780     }
1781     if (strncmp (header, "ID3", 3)) {
1782         return -1; // no tag
1783     }
1784     uint8_t flags = header[5];
1785     if (flags & 15) {
1786         return -1; // unsupported
1787     }
1788     int footerpresent = (flags & (1<<4)) ? 1 : 0;
1789     // check for bad size
1790     if ((header[9] & 0x80) || (header[8] & 0x80) || (header[7] & 0x80) || (header[6] & 0x80)) {
1791         return -1; // bad header
1792     }
1793     uint32_t size = (header[9] << 0) | (header[8] << 7) | (header[7] << 14) | (header[6] << 21);
1794     //trace ("junklib: leading junk size %d\n", size);
1795     *psize = size + 10 + 10 * footerpresent;
1796     return pos;
1797 }
1798 
1799 int
junk_get_leading_size_stdio(FILE * fp)1800 junk_get_leading_size_stdio (FILE *fp) {
1801     uint8_t header[10];
1802     int pos = ftell (fp);
1803     if (fread (header, 1, 10, fp) != 10) {
1804         fseek (fp, pos, SEEK_SET);
1805         return -1; // too short
1806     }
1807     fseek (fp, pos, SEEK_SET);
1808     if (strncmp (header, "ID3", 3)) {
1809         return -1; // no tag
1810     }
1811     uint8_t flags = header[5];
1812     if (flags & 15) {
1813         return -1; // unsupported
1814     }
1815     int footerpresent = (flags & (1<<4)) ? 1 : 0;
1816     // check for bad size
1817     if ((header[9] & 0x80) || (header[8] & 0x80) || (header[7] & 0x80) || (header[6] & 0x80)) {
1818         return -1; // bad header
1819     }
1820     uint32_t size = (header[9] << 0) | (header[8] << 7) | (header[7] << 14) | (header[6] << 21);
1821     //trace ("junklib: leading junk size %d\n", size);
1822     return size + 10 + 10 * footerpresent;
1823 }
1824 
1825 int
junk_get_leading_size(DB_FILE * fp)1826 junk_get_leading_size (DB_FILE *fp) {
1827     uint8_t header[10];
1828     int pos = deadbeef->ftell (fp);
1829     if (deadbeef->fread (header, 1, 10, fp) != 10) {
1830         deadbeef->fseek (fp, pos, SEEK_SET);
1831         trace ("junk_get_leading_size: file is too short\n");
1832         return -1; // too short
1833     }
1834     deadbeef->fseek (fp, pos, SEEK_SET);
1835     if (strncmp (header, "ID3", 3)) {
1836         trace ("junk_get_leading_size: no id3v2 found\n");
1837         return -1; // no tag
1838     }
1839     uint8_t flags = header[5];
1840     if (flags & 15) {
1841         trace ("unsupported flags in id3v2\n");
1842         return -1; // unsupported
1843     }
1844     int footerpresent = (flags & (1<<4)) ? 1 : 0;
1845     // check for bad size
1846     if ((header[9] & 0x80) || (header[8] & 0x80) || (header[7] & 0x80) || (header[6] & 0x80)) {
1847         trace ("bad header in id3v2\n");
1848         return -1; // bad header
1849     }
1850     uint32_t size = (header[9] << 0) | (header[8] << 7) | (header[7] << 14) | (header[6] << 21);
1851     //trace ("junklib: leading junk size %d\n", size);
1852     return size + 10 + 10 * footerpresent;
1853 }
1854 
1855 int
junk_id3v2_unsync(uint8_t * out,int len,int maxlen)1856 junk_id3v2_unsync (uint8_t *out, int len, int maxlen) {
1857     uint8_t buf [maxlen];
1858     uint8_t *p = buf;
1859     int res = -1;
1860     for (int i = 0; i < len; i++) {
1861         *p++ = out[i];
1862         if (i < len - 1 && out[i] == 0xff && (out[i+1] & 0xe0) == 0xe0) {
1863             *p++ = 0;
1864             res = 0;
1865         }
1866     }
1867     if (!res) {
1868         res = p-buf;
1869         memcpy (out, buf, res);
1870     }
1871     return res;
1872 }
1873 
1874 int
junk_id3v2_remove_frames(DB_id3v2_tag_t * tag,const char * frame_id)1875 junk_id3v2_remove_frames (DB_id3v2_tag_t *tag, const char *frame_id) {
1876     DB_id3v2_frame_t *prev = NULL;
1877     for (DB_id3v2_frame_t *f = tag->frames; f; ) {
1878         DB_id3v2_frame_t *next = f->next;
1879         if (!strcmp (f->id, frame_id)) {
1880             if (prev) {
1881                 prev->next = f->next;
1882             }
1883             else {
1884                 tag->frames = f->next;
1885             }
1886             free (f);
1887         }
1888         else {
1889             prev = f;
1890         }
1891         f = next;
1892     }
1893     return 0;
1894 }
1895 
1896 // this function will split multiline values into separate frames
1897 DB_id3v2_frame_t *
junk_id3v2_add_text_frame(DB_id3v2_tag_t * tag,const char * frame_id,const char * value)1898 junk_id3v2_add_text_frame (DB_id3v2_tag_t *tag, const char *frame_id, const char *value) {
1899     // copy value to handle multiline strings
1900     size_t inlen = strlen (value);
1901     char buffer[inlen];
1902     char *pp = buffer;
1903     for (const char *p = value; *p; p++) {
1904         if (*p == '\n') {
1905             *pp++ = 0;
1906             if (tag->version[0] == 3) {
1907                 inlen = p - value;
1908                 break;
1909             }
1910         }
1911         else {
1912             *pp++ = *p;
1913         }
1914     }
1915 
1916     if (!inlen) {
1917         return NULL;
1918     }
1919     uint8_t *out = NULL;
1920 
1921     trace ("junklib: setting id3v2.%d text frame '%s' = '%s'\n", tag->version[0], frame_id, value);
1922 
1923     int encoding = 0;
1924 
1925     size_t outlen = -1;
1926     if (tag->version[0] == 4) {
1927         outlen = inlen;
1928         out = (uint8_t *)value;
1929         encoding = 3;
1930     }
1931     else {
1932         int bufsize = inlen * 4 + 1;
1933         out = malloc (bufsize);
1934         outlen = junk_iconv (value, inlen, out, bufsize, UTF8_STR, "cp1252");
1935         if (outlen == -1) {
1936             outlen = junk_iconv (value, inlen, out+2, bufsize - 2, UTF8_STR, "UCS-2LE");
1937             if (outlen <= 0) {
1938                 return NULL;
1939             }
1940             out[0] = 0xff;
1941             out[1] = 0xfe;
1942             outlen += 2;
1943             trace ("successfully converted to ucs-2le (size=%d, bom: %x %x)\n", (int)outlen, out[0], out[1]);
1944             encoding = 1;
1945         }
1946         else {
1947             trace ("successfully converted to cp1252 (size=%d)\n", (int)outlen);
1948         }
1949     }
1950 
1951     // make a frame
1952     int size = outlen + 1;
1953     trace ("calculated frame size = %d\n", size);
1954     DB_id3v2_frame_t *f = malloc (size + sizeof (DB_id3v2_frame_t));
1955     memset (f, 0, sizeof (DB_id3v2_frame_t));
1956     strcpy (f->id, frame_id);
1957     f->size = size;
1958     f->data[0] = encoding;
1959     memcpy (f->data + 1, out, outlen);
1960 
1961     if (tag->version[0] != 4) {
1962         free (out);
1963     }
1964 
1965     // append to tag
1966     DB_id3v2_frame_t *tail = NULL;
1967 
1968     for (tail = tag->frames; tail && tail->next; tail = tail->next);
1969 
1970     if (tail) {
1971         tail->next = f;
1972     }
1973     else {
1974         tag->frames = f;
1975     }
1976     tail = f;
1977 
1978     return tail;
1979 }
1980 
1981 DB_id3v2_frame_t *
junk_id3v2_add_comment_frame(DB_id3v2_tag_t * tag,const char * lang,const char * descr,const char * value)1982 junk_id3v2_add_comment_frame (DB_id3v2_tag_t *tag, const char *lang, const char *descr, const char *value) {
1983     trace ("junklib: setting 2.3 COMM frame lang=%s, descr='%s', data='%s'\n", lang, descr, value);
1984 
1985     // make a frame
1986     size_t descrlen = strlen (descr);
1987     size_t outlen = strlen (value);
1988 
1989     size_t inputsize = descrlen+outlen+1;
1990     char *input = malloc (inputsize);
1991 
1992     memcpy (input, descr, descrlen);
1993     input[descrlen] = 0;
1994     memcpy (input+descrlen+1, value, outlen);
1995 
1996     size_t buffersize = inputsize * 4;
1997     char *buffer = malloc (buffersize);
1998 
1999     int enc = 0;
2000     int l;
2001 
2002     if (tag->version[0] == 4) {
2003         // utf8
2004         enc = 3;
2005         memcpy (buffer, input, inputsize);
2006         l = inputsize;
2007     }
2008     else {
2009         l = junk_iconv (input, (int)inputsize, buffer, (int)buffersize, UTF8_STR, "cp1252");
2010         if (l <= 0) {
2011             l = junk_iconv (input, (int)inputsize, buffer+2, (int)buffersize - 2, UTF8_STR, "UCS-2LE");
2012             if (l <= 0) {
2013                 trace ("failed to encode to ucs2 or cp1252\n");
2014                 free (input);
2015                 free (buffer);
2016                 return NULL;
2017             }
2018             else {
2019                 enc = 1;
2020                 buffer[0] = 0xff;
2021                 buffer[1] = 0xfe;
2022                 l += 2;
2023             }
2024         }
2025     }
2026 
2027     free (input);
2028 
2029     trace ("calculated frame size = %d\n", l + 4);
2030     DB_id3v2_frame_t *f = malloc (l + 4 + sizeof (DB_id3v2_frame_t));
2031     memset (f, 0, sizeof (DB_id3v2_frame_t));
2032     strcpy (f->id, "COMM");
2033     // flags are all zero
2034     f->size = l + 4;
2035     f->data[0] = enc; // encoding=utf8
2036     memcpy (&f->data[1], lang, 3);
2037     memcpy (&f->data[4], buffer, l);
2038 
2039     free (buffer);
2040 
2041     // append to tag
2042     DB_id3v2_frame_t *tail;
2043     for (tail = tag->frames; tail && tail->next; tail = tail->next);
2044     if (tail) {
2045         tail->next = f;
2046     }
2047     else {
2048         tag->frames = f;
2049     }
2050 
2051     return f;
2052 }
2053 
2054 int
junk_id3v2_remove_txxx_frame(DB_id3v2_tag_t * tag,const char * key)2055 junk_id3v2_remove_txxx_frame (DB_id3v2_tag_t *tag, const char *key) {
2056     DB_id3v2_frame_t *prev = NULL;
2057     for (DB_id3v2_frame_t *f = tag->frames; f; ) {
2058         DB_id3v2_frame_t *next = f->next;
2059         if (!strcmp (f->id, "TXXX")) {
2060             char *txx = convstr_id3v2 (tag->version[0], f->data[0], f->data+1, f->size-1);
2061             if (txx && !strncasecmp (txx, key, strlen (key))) {
2062                 if (prev) {
2063                     prev->next = f->next;
2064                 }
2065                 else {
2066                     tag->frames = f->next;
2067                 }
2068                 free (f);
2069             }
2070             if (txx) {
2071                 free (txx);
2072             }
2073         }
2074         else {
2075             prev = f;
2076         }
2077         f = next;
2078     }
2079     return 0;
2080 }
2081 
2082 int
junk_id3v2_remove_all_txxx_frames(DB_id3v2_tag_t * tag)2083 junk_id3v2_remove_all_txxx_frames (DB_id3v2_tag_t *tag) {
2084     DB_id3v2_frame_t *prev = NULL;
2085     for (DB_id3v2_frame_t *f = tag->frames; f; ) {
2086         DB_id3v2_frame_t *next = f->next;
2087         if (!strcmp (f->id, "TXXX")) {
2088             if (prev) {
2089                 prev->next = f->next;
2090             }
2091             else {
2092                 tag->frames = f->next;
2093             }
2094             free (f);
2095         }
2096         else {
2097             prev = f;
2098         }
2099         f = next;
2100     }
2101     return 0;
2102 }
2103 
2104 DB_id3v2_frame_t *
junk_id3v2_add_txxx_frame(DB_id3v2_tag_t * tag,const char * key,const char * value)2105 junk_id3v2_add_txxx_frame (DB_id3v2_tag_t *tag, const char *key, const char *value) {
2106     size_t keylen = strlen (key);
2107     size_t valuelen = strlen (value);
2108     size_t len = keylen + valuelen + 1;
2109     uint8_t buffer[len];
2110     memcpy (buffer, key, keylen);
2111     buffer[keylen] = 0;
2112     memcpy (buffer+keylen+1, value, valuelen);
2113 
2114     size_t outsize = (keylen + valuelen) * 4 + 1;
2115     uint8_t *out = malloc (outsize);
2116     int encoding = 0;
2117 
2118 
2119     size_t res;
2120 
2121     if (tag->version[0] == 4) {
2122         res = len;
2123         encoding = 3;
2124         memcpy (out, key, keylen);
2125         out[keylen] = 0;
2126         memcpy (out + keylen + 1, value, valuelen);
2127     }
2128     else { // version 3
2129         res = junk_iconv (buffer, len, out, outsize, UTF8_STR, "iso8859-1");
2130         if (res == -1) {
2131             res = junk_iconv (buffer, len, out+2, outsize - 2, UTF8_STR, "UCS-2LE");
2132             if (res == -1) {
2133                 return NULL;
2134             }
2135             out[0] = 0xff;
2136             out[1] = 0xfe;
2137             res += 2;
2138             trace ("successfully converted to ucs-2le (size=%d, bom: %x %x)\n", res, out[0], out[1]);
2139             encoding = 1;
2140         }
2141         else {
2142             trace ("successfully converted to cp1252 (size=%d)\n", res);
2143         }
2144     }
2145 
2146     // make a frame
2147     int size = res + 1;
2148     trace ("calculated frame size = %d\n", size);
2149     DB_id3v2_frame_t *f = malloc (size + sizeof (DB_id3v2_frame_t));
2150     memset (f, 0, sizeof (DB_id3v2_frame_t));
2151     strcpy (f->id, "TXXX");
2152     f->size = size;
2153     f->data[0] = encoding;
2154     memcpy (&f->data[1], out, res);
2155     // append to tag
2156     DB_id3v2_frame_t *tail;
2157     for (tail = tag->frames; tail && tail->next; tail = tail->next);
2158     if (tail) {
2159         tail->next = f;
2160     }
2161     else {
2162         tag->frames = f;
2163     }
2164 
2165     free (out);
2166 
2167     return f;
2168 }
2169 
2170 // TODO: some non-Txxx frames might still need charset conversion
2171 // TODO: 2.4 TDTG frame (tagging time) should not be converted, but might be useful to create it
2172 int
junk_id3v2_convert_24_to_23(DB_id3v2_tag_t * tag24,DB_id3v2_tag_t * tag23)2173 junk_id3v2_convert_24_to_23 (DB_id3v2_tag_t *tag24, DB_id3v2_tag_t *tag23) {
2174     DB_id3v2_frame_t *f24;
2175     DB_id3v2_frame_t *tail = tag23->frames;
2176     assert (tag24->version[0] == 4);
2177     tag23->version[0] = 3;
2178     tag23->version[1] = 0;
2179 
2180     while (tail && tail->next) {
2181         tail = tail->next;
2182     }
2183 
2184     const char *copy_frames[] = {
2185         "AENC", "APIC",
2186         "COMR", "ENCR",
2187         "ETCO", "GEOB", "GRID",
2188         "LINK", "MCDI", "MLLT", "OWNE", "PRIV",
2189         "POPM", "POSS", "RBUF",
2190         "RVRB",
2191         "SYLT", "SYTC",
2192         "UFID", "USER", "USLT",
2193         NULL
2194     };
2195 
2196     // NOTE: 2.4 ASPI, EQU2, RVA2, SEEK, SIGN are discarded for 2.3
2197     // NOTE: 2.4 PCNT is discarded because it is useless
2198     // NOTE: all Wxxx frames are copy_frames, handled as special case
2199 
2200 
2201     // "TDRC" TDAT with conversion from ID3v2-strct timestamp to DDMM format
2202     // "TDOR" TORY with conversion from ID3v2-strct timestamp to year
2203     // TODO: "TIPL" IPLS with conversion to non-text format
2204 
2205     const char *text_frames[] = {
2206         "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT", "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED", "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE", "TXXX", "TDRC", NULL
2207     };
2208 
2209     // NOTE: 2.4 TMCL (musician credits list) is discarded for 2.3
2210     // NOTE: 2.4 TMOO (mood) is discarded for 2.3
2211     // NOTE: 2.4 TPRO (produced notice) is discarded for 2.3
2212     // NOTE: 2.4 TSOA (album sort order) is discarded for 2.3
2213     // NOTE: 2.4 TSOP (performer sort order) is discarded for 2.3
2214     // NOTE: 2.4 TSOT (title sort order) is discarded for 2.3
2215     // NOTE: 2.4 TSST (set subtitle) is discarded for 2.3
2216 
2217     for (f24 = tag24->frames; f24; f24 = f24->next) {
2218         DB_id3v2_frame_t *f23 = NULL;
2219         // we are altering the tag, so check for tag alter preservation
2220         if (tag24->flags & (1<<6)) {
2221             continue; // discard the frame
2222         }
2223 
2224         int simplecopy = 0; // means format is the same in 2.3 and 2.4
2225         int text = 0; // means this is a text frame
2226 
2227         int i;
2228 
2229         if (f24->id[0] == 'W') { // covers all W000..WZZZ tags
2230             simplecopy = 1;
2231         }
2232 
2233         if (!simplecopy) {
2234             for (i = 0; copy_frames[i]; i++) {
2235                 if (!strcmp (f24->id, copy_frames[i])) {
2236                     simplecopy = 1;
2237                     break;
2238                 }
2239             }
2240         }
2241 
2242         if (!simplecopy) {
2243             // check if this is a text frame
2244             for (i = 0; text_frames[i]; i++) {
2245                 if (!strcmp (f24->id, text_frames[i])) {
2246                     text = 1;
2247                     break;
2248                 }
2249             }
2250         }
2251 
2252 
2253         if (!simplecopy && !text) {
2254             if (!strcmp (f24->id, "COMM")) {
2255                 uint8_t enc = f24->data[0];
2256                 char lang[4] = {f24->data[1], f24->data[2], f24->data[3], 0};
2257                 trace ("COMM enc is: %d\n", (int)enc);
2258                 trace ("COMM language is: %s\n", lang);
2259 
2260                 char *descr = convstr_id3v2 (4, enc, f24->data+4, f24->size-4);
2261                 if (!descr) {
2262                     trace ("failed to decode COMM frame, probably wrong encoding (%d)\n", enc);
2263                 }
2264                 else {
2265                     // find value
2266                     char *value = descr;
2267                     while (*value && *value != '\n') {
2268                         value++;
2269                     }
2270                     if (*value != '\n') {
2271                         trace ("failed to parse COMM frame, descr was \"%s\"\n", descr);
2272                     }
2273                     else {
2274                         *value = 0;
2275                         value++;
2276                         f23 = junk_id3v2_add_comment_frame (tag23, lang, descr, value);
2277                         if (f23) {
2278                             tail = f23;
2279                             f23 = NULL;
2280                         }
2281                     }
2282                     free (descr);
2283                 }
2284             }
2285             continue; // unknown frame
2286         }
2287 
2288         // convert flags
2289         uint8_t flags[2];
2290         // 1st byte (status flags) is the same, but shifted by 1 bit to the left
2291         flags[0] = f24->flags[0] << 1;
2292 
2293         // 2nd byte (format flags) is quite different
2294         // 2.4 format is %0h00kmnp (6:grouping, 3:compression, 2:encryption, 1:unsync, 0:datalen)
2295         // 2.3 format is %ijk00000 (7:compression, 6:encryption, 5:grouping)
2296         flags[1] = 0;
2297         if (f24->flags[1] & (1 << 6)) {
2298             flags[1] |= (1 << 5);
2299         }
2300         if (f24->flags[1] & (1 << 3)) {
2301             flags[1] |= (1 << 7);
2302         }
2303         if (f24->flags[1] & (1 << 2)) {
2304             flags[1] |= (1 << 6);
2305         }
2306         if (f24->flags[1] & (1 << 1)) {
2307             // 2.3 doesn't support per-frame unsyncronyzation
2308         }
2309         if (f24->flags[1] & (1 << 0)) {
2310             // 2.3 doesn't support data length, but remember to skip 4 bytes of
2311             // the frame
2312         }
2313 
2314         if (simplecopy) {
2315             f23 = malloc (sizeof (DB_id3v2_frame_t) + f24->size);
2316             memset (f23, 0, sizeof (DB_id3v2_frame_t) + f24->size);
2317             strcpy (f23->id, f24->id);
2318             if (f24->flags[1] & (1<<0)) {
2319                 // skip 1st 4 bytes (2.4 data length indicator)
2320                 memcpy (f23->data, f24->data+4, f24->size-4);
2321                 f23->size = f24->size-4;
2322             }
2323             else {
2324                 f23->size = f24->size;
2325                 memcpy (f23->data, f24->data, f24->size);
2326             }
2327             f23->flags[0] = flags[0];
2328             f23->flags[1] = flags[1];
2329         }
2330         else if (text) {
2331 
2332             char *decoded = convstr_id3v2 (4, f24->data[0], f24->data+1, f24->size-1);
2333             if (!decoded) {
2334                 trace ("junk_id3v2_convert_24_to_23: failed to decode text frame %s\n", f24->id);
2335                 continue; // failed, discard it
2336             }
2337             if (!strcmp (f24->id, "TDRC")) {
2338                 trace ("junk_id3v2_convert_24_to_23: TDRC text: %s\n", decoded);
2339                 int year, month, day;
2340                 int c = sscanf (decoded, "%4d-%2d-%2d", &year, &month, &day);
2341                 if (c >= 1) {
2342                     char s[5];
2343                     snprintf (s, sizeof (s), "%04d", year);
2344                     f23 = junk_id3v2_add_text_frame (tag23, "TYER", s);
2345                     if (f23) {
2346                         tail = f23;
2347                         f23 = NULL;
2348                     }
2349                 }
2350                 if (c == 3) {
2351                     char s[5];
2352                     snprintf (s, sizeof (s), "%02d%02d", month, day);
2353                     f23 = junk_id3v2_add_text_frame (tag23, "TDAT", s);
2354                     if (f23) {
2355                         tail = f23;
2356                         f23 = NULL;
2357                     }
2358                 }
2359                 else {
2360                     trace ("junk_id3v2_add_text_frame: 2.4 TDRC doesn't have month/day info; discarded\n");
2361                 }
2362             }
2363             else if (!strcmp (f24->id, "TDOR")) {
2364                 trace ("junk_id3v2_convert_24_to_23: TDOR text: %s\n", decoded);
2365                 int year;
2366                 int c = sscanf (decoded, "%4d", &year);
2367                 if (c == 1) {
2368                     char s[5];
2369                     snprintf (s, sizeof (s), "%04d", year);
2370                     f23 = junk_id3v2_add_text_frame (tag23, "TORY", s);
2371                     if (f23) {
2372                         tail = f23;
2373                         f23 = NULL;
2374                     }
2375                 }
2376                 else {
2377                     trace ("junk_id3v2_add_text_frame: 2.4 TDOR doesn't have month/day info; discarded\n");
2378                 }
2379             }
2380             else {
2381                 // encode for 2.3
2382                 f23 = junk_id3v2_add_text_frame (tag23, f24->id, decoded);
2383                 if (f23) {
2384                     tail = f23;
2385                     f23 = NULL;
2386                 }
2387             }
2388             free (decoded);
2389         }
2390         if (f23) {
2391             if (tail) {
2392                 tail->next = f23;
2393             }
2394             else {
2395                 tag23->frames = f23;
2396             }
2397             tail = f23;
2398         }
2399     }
2400 
2401     // convert tag header
2402     tag23->flags = tag24->flags;
2403     tag23->flags &= ~(1<<4); // no footer (unsupported in 2.3)
2404     tag23->flags &= ~(1<<7); // no unsync
2405 
2406     return 0;
2407 }
2408 
2409 int
junk_id3v2_convert_23_to_24(DB_id3v2_tag_t * tag23,DB_id3v2_tag_t * tag24)2410 junk_id3v2_convert_23_to_24 (DB_id3v2_tag_t *tag23, DB_id3v2_tag_t *tag24) {
2411     DB_id3v2_frame_t *f23;
2412     DB_id3v2_frame_t *tail = tag24->frames;
2413 
2414     while (tail && tail->next) {
2415         tail = tail->next;
2416     }
2417 
2418     const char *copy_frames[] = {
2419         "AENC", "APIC",
2420         "COMM", "COMR", "ENCR",
2421         "ETCO", "GEOB", "GRID",
2422         "LINK", "MCDI", "MLLT", "OWNE", "PRIV",
2423         "POPM", "POSS", "RBUF",
2424         "RVRB",
2425         "SYLT", "SYTC",
2426         "UFID", "USER", "USLT",
2427         NULL
2428     };
2429 
2430     // NOTE: all Wxxx frames are copy_frames, handled as special case
2431 
2432     // "TDRC" TDAT with conversion from ID3v2-strct timestamp to DDMM format
2433     // "TDOR" TORY with conversion from ID3v2-strct timestamp to year
2434     // TODO: "TIPL" IPLS with conversion to non-text format
2435 
2436     const char *text_frames[] = {
2437         "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT", "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED", "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE", "TXXX", "TDRC", NULL
2438     };
2439 
2440     for (f23 = tag23->frames; f23; f23 = f23->next) {
2441         // we are altering the tag, so check for tag alter preservation
2442         if (tag23->flags & (1<<7)) {
2443             continue; // discard the frame
2444         }
2445 
2446         int simplecopy = 0; // means format is the same in 2.3 and 2.4
2447         int text = 0; // means this is a text frame
2448 
2449         int i;
2450 
2451         if (f23->id[0] == 'W') { // covers all W000..WZZZ tags
2452             simplecopy = 1;
2453         }
2454 
2455         if (!simplecopy) {
2456             for (i = 0; copy_frames[i]; i++) {
2457                 if (!strcmp (f23->id, copy_frames[i])) {
2458                     simplecopy = 1;
2459                     break;
2460                 }
2461             }
2462         }
2463 
2464         if (!simplecopy) {
2465             // check if this is a text frame
2466             for (i = 0; text_frames[i]; i++) {
2467                 if (!strcmp (f23->id, text_frames[i])) {
2468                     text = 1;
2469                     break;
2470                 }
2471             }
2472         }
2473 
2474 
2475         if (!simplecopy && !text) {
2476             continue; // unknown frame
2477         }
2478 
2479         // convert flags
2480         uint8_t flags[2];
2481         // 1st byte (status flags) is the same, but shifted by 1 bit to the
2482         // right
2483         flags[0] = f23->flags[0] >> 1;
2484 
2485         // 2nd byte (format flags) is quite different
2486         // 2.4 format is %0h00kmnp (6:grouping, 3:compression, 2:encryption, 1:unsync, 0:datalen)
2487         // 2.3 format is %ijk00000 (7:compression, 6:encryption, 5:grouping)
2488         flags[1] = 0;
2489         if (f23->flags[1] & (1 << 7)) {
2490             flags[1] |= (1 << 3);
2491         }
2492         if (f23->flags[1] & (1 << 6)) {
2493             flags[1] |= (1 << 2);
2494         }
2495         if (f23->flags[1] & (1 << 5)) {
2496             flags[1] |= (1 << 6);
2497         }
2498 
2499         DB_id3v2_frame_t *f24 = NULL;
2500         if (simplecopy) {
2501             f24 = malloc (sizeof (DB_id3v2_frame_t) + f23->size);
2502             memset (f24, 0, sizeof (DB_id3v2_frame_t) + f23->size);
2503             strcpy (f24->id, f23->id);
2504             f24->size = f23->size;
2505             memcpy (f24->data, f23->data, f23->size);
2506             f24->flags[0] = flags[0];
2507             f24->flags[1] = flags[1];
2508         }
2509         else if (text) {
2510             // decode text into utf8
2511             char *decoded = convstr_id3v2 (3, f23->data[0], f23->data+1, f23->size-1);
2512             if (!decoded) {
2513                 trace ("junk_id3v2_convert_23_to_24: failed to decode text frame %s\n", f23->id);
2514                 continue; // failed, discard it
2515             }
2516             if (!strcmp (f23->id, "TDRC")) {
2517                 trace ("junk_id3v2_convert_23_to_24: TDRC text: %s\n", decoded);
2518                 int year, month, day;
2519                 int c = sscanf (decoded, "%4d-%2d-%2d", &year, &month, &day);
2520                 if (c >= 1) {
2521                     char s[5];
2522                     snprintf (s, sizeof (s), "%04d", year);
2523                     f24 = junk_id3v2_add_text_frame (tag24, "TYER", s);
2524                     if (f24) {
2525                         tail = f24;
2526                         f24 = NULL;
2527                     }
2528                 }
2529                 if (c == 3) {
2530                     char s[5];
2531                     snprintf (s, sizeof (s), "%02d%02d", month, day);
2532                     f24 = junk_id3v2_add_text_frame (tag24, "TDAT", s);
2533                     if (f24) {
2534                         tail = f24;
2535                         f24 = NULL;
2536                     }
2537                 }
2538                 else {
2539                     trace ("junk_id3v2_add_text_frame: 2.4 TDRC doesn't have month/day info; discarded\n");
2540                 }
2541             }
2542             else if (!strcmp (f23->id, "TORY")) {
2543                 trace ("junk_id3v2_convert_23_to_24: TDOR text: %s\n", decoded);
2544                 int year;
2545                 int c = sscanf (decoded, "%4d", &year);
2546                 if (c == 1) {
2547                     char s[5];
2548                     snprintf (s, sizeof (s), "%04d", year);
2549                     f24 = junk_id3v2_add_text_frame (tag24, "TDOR", s);
2550                     if (f24) {
2551                         tail = f24;
2552                         f24 = NULL;
2553                     }
2554                 }
2555                 else {
2556                     trace ("junk_id3v2_add_text_frame: 2.4 TDOR doesn't have month/day info; discarded\n");
2557                 }
2558             }
2559             else {
2560                 // encode for 2.4
2561                 f24 = junk_id3v2_add_text_frame (tag24, f23->id, decoded);
2562                 if (f24) {
2563                     tail = f24;
2564                     f24 = NULL;
2565                 }
2566             }
2567             free (decoded);
2568         }
2569         if (f24) {
2570             if (tail) {
2571                 tail->next = f24;
2572             }
2573             else {
2574                 tag24->frames = f24;
2575             }
2576             tail = f24;
2577         }
2578     }
2579 
2580     // convert tag header
2581     tag24->version[0] = 4;
2582     tag24->version[1] = 0;
2583     tag24->flags = tag23->flags;
2584     tag24->flags &= ~(1<<4); // no footer (unsupported in 2.3)
2585     tag24->flags &= ~(1<<7); // no unsync
2586 
2587     return 0;
2588 }
2589 
2590 int
junk_id3v2_convert_22_to_24(DB_id3v2_tag_t * tag22,DB_id3v2_tag_t * tag24)2591 junk_id3v2_convert_22_to_24 (DB_id3v2_tag_t *tag22, DB_id3v2_tag_t *tag24) {
2592     DB_id3v2_frame_t *f22;
2593     DB_id3v2_frame_t *tail = tag24->frames;
2594 
2595     while (tail && tail->next) {
2596         tail = tail->next;
2597     }
2598 
2599     const char *copy_frames[] = {
2600         "BUF", "COM", "CRA", "ETC", "GEO", "IPL", "MCI", "MLL", "POP", "REV", "SLT", "STC", "UFI", "ULT",
2601         NULL
2602     };
2603 
2604     const char *copy_frames_24[] = {
2605         "RBUF", "COMM", "AENC", "ETCO", "GEOB", "TIPL", "MCDI", "MLLT", "POPM", "RVRB", "SYLT", "SYTC", "UFID", "USLT",
2606         NULL
2607     };
2608 
2609     // NOTE: BUF is discarded (no match in 2.4)
2610     // NOTE: CNT is discarded (useless)
2611     // NOTE: CRM is discarded (no match in 2.4)
2612     // NOTE: EQU is discarded (difficult to convert to EQU2)
2613     // NOTE: LNK is discarded (maybe later)
2614     // NOTE: PIC is discarded (needs conversion from custom image-format field to mime-type)
2615     // NOTE: RVA is discarded (subjective, and difficult to convert to RVA2)
2616 
2617     const char *text_frames[] = {
2618         "TAL", "TBP", "TCM", "TCO", "TCR", "TDY", "TEN", "TFT", "TKE", "TLA", "TLE", "TMT", "TOA", "TOF", "TOL", "TOT", "TP1", "TP2", "TP3", "TP4", "TPA", "TPB", "TRC", "TRK", "TSS", "TT1", "TT2", "TT3", "TXT", "TXX", "TOR", NULL
2619     };
2620 
2621     const char *text_frames_24[] = {
2622         "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TFLT", "TKEY", "TLAN", "TLEN", "TMED", "TOPE", "TOFN", "TOLY", "TOAL", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPUB", "TSRC", "TRCK", "TSSE", "TIT1", "TIT2", "TIT3", "TEXT", "TXXX", "TDOR"
2623     };
2624 
2625     // NOTE: TRD is discarded (no match in 2.4)
2626     // NOTE: TSI is discarded (no match in 2.4)
2627 
2628     int year = 0;
2629     int month = 0;
2630     int day = 0;
2631     int hour = 0;
2632     int minute = 0;
2633 
2634     for (f22 = tag22->frames; f22; f22 = f22->next) {
2635         int simplecopy = -1; // means format is the same in 2.2 and 2.4
2636         int text = -1; // means this is a text frame
2637 
2638         int i;
2639 
2640         if (f22->id[0] == 'W') { // covers all W00..WZZ tags
2641             simplecopy = 0;
2642         }
2643 
2644         if (simplecopy == -1) {
2645             for (i = 0; copy_frames[i]; i++) {
2646                 if (!strcmp (f22->id, copy_frames[i])) {
2647                     simplecopy = i;
2648                     break;
2649                 }
2650             }
2651         }
2652 
2653         if (simplecopy == -1) {
2654             // check if this is a text frame
2655             for (i = 0; text_frames[i]; i++) {
2656                 if (!strcmp (f22->id, text_frames[i])) {
2657                     text = i;
2658                     break;
2659                 }
2660             }
2661         }
2662 
2663 
2664         if (simplecopy == -1 && text == -1) {
2665             continue; // unknown frame
2666         }
2667 
2668         // convert flags
2669         uint8_t flags[2];
2670         // 1st byte (status flags) is the same, but shifted by 1 bit to the
2671         // right
2672         flags[0] = f22->flags[0] >> 1;
2673 
2674         // 2nd byte (format flags) is quite different
2675         // 2.4 format is %0h00kmnp (grouping, compression, encryption, unsync)
2676         // 2.3 format is %ijk00000 (compression, encryption, grouping)
2677         flags[1] = 0;
2678         if (f22->flags[1] & (1 << 4)) {
2679             flags[1] |= (1 << 6);
2680         }
2681         if (f22->flags[1] & (1 << 7)) {
2682             flags[1] |= (1 << 3);
2683         }
2684         if (f22->flags[1] & (1 << 6)) {
2685             flags[1] |= (1 << 2);
2686         }
2687         if (f22->flags[1] & (1 << 5)) {
2688             flags[1] |= (1 << 1);
2689         }
2690 
2691         DB_id3v2_frame_t *f24 = NULL;
2692         if (simplecopy != -1) {
2693             f24 = malloc (sizeof (DB_id3v2_frame_t) + f22->size);
2694             memset (f24, 0, sizeof (DB_id3v2_frame_t) + f22->size);
2695             if (f22->id[0] == 'W') { // duplicate last letter of W00-WZZ frames
2696                 strcpy (f24->id, f22->id);
2697                 f24->id[3] = f24->id[2];
2698                 f24->id[4] = 0;
2699             }
2700             else {
2701                 strcpy (f24->id, copy_frames_24[simplecopy]);
2702             }
2703             f24->size = f22->size;
2704             memcpy (f24->data, f22->data, f22->size);
2705             f24->flags[0] = flags[0];
2706             f24->flags[1] = flags[1];
2707         }
2708         else if (text != -1) {
2709             // decode text into utf8
2710             char *decoded = convstr_id3v2 (2, f22->data[0], f22->data+1, f22->size-1);
2711             if (!decoded) {
2712                 trace ("junk_id3v2_convert_23_to_24: failed to decode text frame %s\n", f22->id);
2713                 continue; // failed, discard it
2714             }
2715             // encode for 2.4
2716             f24 = junk_id3v2_add_text_frame (tag24, text_frames_24[text], decoded);
2717             if (f24) {
2718                 tail = f24;
2719                 f24 = NULL;
2720             }
2721             free (decoded);
2722         }
2723         else if (!strcmp (f22->id, "TYE")) {
2724             char *decoded = convstr_id3v2 (2, f22->data[0], f22->data+1, f22->size-1);
2725             if (decoded) {
2726                 year = atoi (decoded);
2727                 free (decoded);
2728             }
2729         }
2730         else if (!strcmp (f22->id, "TDA")) {
2731             char *decoded = convstr_id3v2 (2, f22->data[0], f22->data+1, f22->size-1);
2732             if (decoded) {
2733                 sscanf (decoded, "%02d%02d", &month, &day);
2734                 free (decoded);
2735             }
2736         }
2737         else if (!strcmp (f22->id, "TIM")) {
2738             char *decoded = convstr_id3v2 (2, f22->data[0], f22->data+1, f22->size-1);
2739             if (decoded) {
2740                 sscanf (decoded, "%02d%02d", &hour, &minute);
2741                 free (decoded);
2742             }
2743         }
2744         if (f24) {
2745             if (tail) {
2746                 tail->next = f24;
2747             }
2748             else {
2749                 tag24->frames = f24;
2750             }
2751             tail = f24;
2752         }
2753     }
2754 
2755     char tdrc[100];
2756     char *p = tdrc;
2757     if (year > 0) {
2758         int n = sprintf (p, "%04d", year);
2759         p += n;
2760         if (month) {
2761             n = sprintf (p, "-%02d", month);
2762             p += n;
2763             if (day) {
2764                 n = sprintf (p, "-%02d", day);
2765                 p += n;
2766                 if (hour && minute) {
2767                     n = sprintf (p, "-T%02d:%02d", hour, minute);
2768                     p += n;
2769                 }
2770             }
2771         }
2772         DB_id3v2_frame_t *f24 = junk_id3v2_add_text_frame (tag24, "TDRC", tdrc);
2773         if (f24) {
2774             tail = f24;
2775         }
2776     }
2777 
2778     // convert tag header
2779     tag24->version[0] = 4;
2780     tag24->version[1] = 0;
2781     tag24->flags = tag22->flags;
2782     tag24->flags &= ~(1<<4); // no footer (unsupported in 2.3)
2783     tag24->flags &= ~(1<<7); // no unsync
2784 
2785     return 0;
2786 }
2787 
2788 int
junk_apev2_remove_frames(DB_apev2_tag_t * tag,const char * frame_id)2789 junk_apev2_remove_frames (DB_apev2_tag_t *tag, const char *frame_id) {
2790     DB_apev2_frame_t *prev = NULL;
2791     for (DB_apev2_frame_t *f = tag->frames; f; ) {
2792         DB_apev2_frame_t *next = f->next;
2793         if (!strcasecmp (f->key, frame_id)) {
2794             if (prev) {
2795                 prev->next = f->next;
2796             }
2797             else {
2798                 tag->frames = f->next;
2799             }
2800             free (f);
2801         }
2802         else {
2803             prev = f;
2804         }
2805         f = next;
2806     }
2807     return 0;
2808 }
2809 
2810 int
junk_apev2_remove_all_text_frames(DB_apev2_tag_t * tag)2811 junk_apev2_remove_all_text_frames (DB_apev2_tag_t *tag) {
2812     DB_apev2_frame_t *prev = NULL;
2813     for (DB_apev2_frame_t *f = tag->frames; f; ) {
2814         DB_apev2_frame_t *next = f->next;
2815         int valuetype = ((f->flags >> 1) & 3);
2816         if (valuetype == 0) {
2817             if (prev) {
2818                 prev->next = f->next;
2819             }
2820             else {
2821                 tag->frames = f->next;
2822             }
2823             free (f);
2824         }
2825         else {
2826             prev = f;
2827         }
2828         f = next;
2829     }
2830     return 0;
2831 }
2832 DB_apev2_frame_t *
junk_apev2_add_text_frame(DB_apev2_tag_t * tag,const char * frame_id,const char * value)2833 junk_apev2_add_text_frame (DB_apev2_tag_t *tag, const char *frame_id, const char *value) {
2834     trace ("adding apev2 frame %s %s\n", frame_id, value);
2835     if (!*value) {
2836         return NULL;
2837     }
2838     DB_apev2_frame_t *tail = tag->frames;
2839     while (tail && tail->next) {
2840         tail = tail->next;
2841     }
2842 
2843 #if 0 // this will split every line into separate field
2844     const char *next = value;
2845     while (*value) {
2846         while (*next && *next != '\n') {
2847             next++;
2848         }
2849 
2850         //int size = strlen (value);
2851         int size = next - value;
2852         if (*next) {
2853             next++;
2854         }
2855 
2856         if (!size) {
2857             continue;
2858         }
2859 
2860         trace ("adding apev2 subframe %s len %d\n", value, size);
2861         DB_apev2_frame_t *f = malloc (sizeof (DB_apev2_frame_t) + size);
2862         if (!f) {
2863             trace ("junk_apev2_add_text_frame: failed to allocate %d bytes\n", size);
2864             return NULL;
2865         }
2866         memset (f, 0, sizeof (DB_apev2_frame_t));
2867         f->flags = 0;
2868         strcpy (f->key, frame_id);
2869         f->size = size;
2870         memcpy (f->data, value, size);
2871 
2872         if (tail) {
2873             tail->next = f;
2874         }
2875         else {
2876             tag->frames = f;
2877         }
2878         tail = f;
2879 
2880         value = next;
2881     }
2882 #endif
2883     size_t size = strlen (value);
2884     DB_apev2_frame_t *f = malloc (sizeof (DB_apev2_frame_t) + size);
2885     if (!f) {
2886         trace ("junk_apev2_add_text_frame: failed to allocate %d bytes\n", size);
2887         return NULL;
2888     }
2889     memset (f, 0, sizeof (DB_apev2_frame_t));
2890     f->flags = 0;
2891     strcpy (f->key, frame_id);
2892     f->size = size;
2893     memcpy (f->data, value, size);
2894 
2895     if (tail) {
2896         tail->next = f;
2897     }
2898     else {
2899         tag->frames = f;
2900     }
2901     tail = f;
2902     return tail;
2903 }
2904 
2905 int
junk_id3v2_convert_apev2_to_24(DB_apev2_tag_t * ape,DB_id3v2_tag_t * tag24)2906 junk_id3v2_convert_apev2_to_24 (DB_apev2_tag_t *ape, DB_id3v2_tag_t *tag24) {
2907     DB_apev2_frame_t *f_ape;
2908     DB_id3v2_frame_t *tail = tag24->frames;
2909 
2910     while (tail && tail->next) {
2911         tail = tail->next;
2912     }
2913 
2914     const char *text_keys[] = {
2915         "Title", "Subtitle", "Artist", "Album", "Publisher", "Conductor", "Track", "Composer", "Copyright", "Genre", "Disc", "ISRC", "Language", "Year", NULL
2916     };
2917 
2918     const char *text_keys_24[] = {
2919         "TIT2", "TIT3", "TPE1", "TALB", "TPUB", "TPE3", "TRCK", "TCOM", "TCOP", "TCON", "TPOS", "TSRC", "TLAN", "TDRC"
2920     };
2921 
2922     const char *comm_frames[] = {
2923         "Comment", "EAN/UPC", "ISBN", "Catalog", "LC", "Publicationright", "Record Location", "Related", "Abstract", "Bibliography", NULL
2924     };
2925 
2926     // FIXME: additional frames: File->WOAF
2927     // converted to COMM: Comment, EAN/UPC, ISBN, Catalog, LC, Publicationright, Record Location, Related, Abstract, Bibliography
2928     // "Debut album" is discarded
2929     // "Index" is discarded
2930     // "Introplay" is discarded
2931 
2932     for (f_ape = ape->frames; f_ape; f_ape = f_ape->next) {
2933         int i;
2934 
2935         for (i = 0; text_keys[i]; i++) {
2936             if (!strcasecmp (text_keys[i], f_ape->key)) {
2937                 break;
2938             }
2939         }
2940 
2941         DB_id3v2_frame_t *f24 = NULL;
2942 
2943         if (text_keys[i]) {
2944             char str[f_ape->size+1];
2945             memcpy (str, f_ape->data, f_ape->size);
2946             str[f_ape->size] = 0;
2947             f24 = junk_id3v2_add_text_frame (tag24, text_keys_24[i], str);
2948             if (f24) {
2949                 tail = f24;
2950                 f24 = NULL;
2951             }
2952         }
2953         else {
2954             for (i = 0; comm_frames[i]; i++) {
2955                 if (!strcasecmp (f_ape->key, comm_frames[i])) {
2956                     char str[f_ape->size+1];
2957                     memcpy (str, f_ape->data, f_ape->size);
2958                     str[f_ape->size] = 0;
2959                     if (!strcasecmp (f_ape->key, "Comment")) {
2960                         junk_id3v2_add_comment_frame (tag24, "eng", "", str);
2961                     }
2962                     else {
2963                         junk_id3v2_add_comment_frame (tag24, "eng", comm_frames[i], str);
2964                     }
2965                     break;
2966                 }
2967             }
2968         }
2969 
2970         if (f24) {
2971             if (tail) {
2972                 tail->next = f24;
2973             }
2974             else {
2975                 tag24->frames = f24;
2976             }
2977             tail = f24;
2978         }
2979     }
2980 
2981     // convert tag header
2982     tag24->version[0] = 4;
2983     tag24->version[1] = 0;
2984     tag24->flags = 0;
2985 
2986     return 0;
2987 }
2988 
2989 int
junk_apev2_write_i32_le2(int fd,uint32_t data)2990 junk_apev2_write_i32_le2 (int fd, uint32_t data) {
2991     int shift = 0;
2992     for (int i = 0; i < 4; i++) {
2993         uint8_t d = (data >> shift) & 0xff;
2994         if (write (fd, &d, 1) != 1) {
2995             return -1;
2996         }
2997         shift += 8;
2998     }
2999 
3000     return 0;
3001 }
3002 
3003 int
junk_apev2_write_i32_le(FILE * fp,uint32_t data)3004 junk_apev2_write_i32_le (FILE *fp, uint32_t data) {
3005     int shift = 0;
3006     for (int i = 0; i < 4; i++) {
3007         uint8_t d = (data >> shift) & 0xff;
3008         if (fwrite (&d, 1, 1, fp) != 1) {
3009             return -1;
3010         }
3011         shift += 8;
3012     }
3013 
3014     return 0;
3015 }
3016 
3017 int
junk_apev2_write2(int fd,DB_apev2_tag_t * tag,int write_header,int write_footer)3018 junk_apev2_write2 (int fd, DB_apev2_tag_t *tag, int write_header, int write_footer) {
3019     // calc size and numitems
3020     uint32_t numframes = 0;
3021     uint32_t size = 0;
3022     DB_apev2_frame_t *f = tag->frames;
3023     while (f) {
3024         size += 8 + strlen (f->key) + 1 + f->size;
3025         numframes++;
3026         f = f->next;
3027     }
3028     size += 32;
3029 
3030     trace ("junk_apev2_write2: writing apev2 tag, size=%d, numframes=%d\n", size, numframes);
3031 
3032 
3033     if (write_header) {
3034         if (write (fd, "APETAGEX", 8) != 8) {
3035             trace ("junk_apev2_write2: failed to write apev2 header signature\n");
3036             goto error;
3037         }
3038         uint32_t flags = (1 << 31) | (1 << 29); // contains header, this is header
3039         if (!write_footer) {
3040             flags |= 1 << 30; // contains no footer
3041         }
3042         uint32_t header[4] = {
3043             2000, // version
3044             size,
3045             numframes,
3046             flags
3047         };
3048         for (int i = 0; i < 4; i++) {
3049             if (junk_apev2_write_i32_le2 (fd, header[i]) != 0) {
3050                 trace ("junk_apev2_write_i32_le2: failed to write apev2 header\n");
3051                 goto error;
3052             }
3053         }
3054         // write 8 bytes of padding
3055         header[0] = header[1] = 0;
3056         if (write (fd, header, 8) != 8) {
3057             trace ("junk_apev2_write2: failed to write apev2 header padding\n");
3058             goto error;
3059         }
3060     }
3061 
3062     // write items
3063     f = tag->frames;
3064     while (f) {
3065         if (junk_apev2_write_i32_le2 (fd, f->size) != 0) {
3066             trace ("junk_apev2_write_i32_le2: failed to write apev2 item size\n");
3067             goto error;
3068         }
3069         if (junk_apev2_write_i32_le2 (fd, f->flags) != 0) {
3070             trace ("junk_apev2_write_i32_le2: failed to write apev2 item flags\n");
3071             goto error;
3072         }
3073         int l = strlen (f->key) + 1;
3074         if (write (fd, f->key, l) != l) {
3075             trace ("junk_apev2_write2: failed to write apev2 item key\n");
3076             goto error;
3077         }
3078         if (write (fd, f->data, f->size) != f->size) {
3079             trace ("junk_apev2_write2: failed to write apev2 item value\n");
3080             goto error;
3081         }
3082         f = f->next;
3083     }
3084 
3085     if (write_footer) {
3086         if (write (fd, "APETAGEX", 8) != 8) {
3087             trace ("junk_apev2_write: failed to write apev2 footer signature\n");
3088             goto error;
3089         }
3090         uint32_t flags = 0;
3091         if (write_header) {
3092             flags |= 1 << 31;
3093         }
3094         uint32_t header[4] = {
3095             2000, // version
3096             size,
3097             numframes,
3098             flags
3099         };
3100         for (int i = 0; i < 4; i++) {
3101             if (junk_apev2_write_i32_le2 (fd, header[i]) != 0) {
3102                 trace ("junk_apev2_write_i32_le2: failed to write apev2 footer\n");
3103                 goto error;
3104             }
3105         }
3106         // write 8 bytes of padding
3107         header[0] = header[1] = 0;
3108         if (write (fd, header, 8) != 8) {
3109             trace ("junk_apev2_write2: failed to write apev2 footer padding\n");
3110             goto error;
3111         }
3112     }
3113     return 0;
3114 error:
3115     return -1;
3116 }
3117 
3118 
3119 int
junk_apev2_write(FILE * fp,DB_apev2_tag_t * tag,int write_header,int write_footer)3120 junk_apev2_write (FILE *fp, DB_apev2_tag_t *tag, int write_header, int write_footer) {
3121     // calc size and numitems
3122     uint32_t numframes = 0;
3123     uint32_t size = 0;
3124     DB_apev2_frame_t *f = tag->frames;
3125     while (f) {
3126         size += 8 + strlen (f->key) + 1 + f->size;
3127         numframes++;
3128         f = f->next;
3129     }
3130     size += 32;
3131 
3132     trace ("junk_apev2_write: writing apev2 tag, size=%d, numframes=%d\n", size, numframes);
3133 
3134 
3135     if (write_header) {
3136         if (fwrite ("APETAGEX", 1, 8, fp) != 8) {
3137             trace ("junk_apev2_write: failed to write apev2 header signature\n");
3138             goto error;
3139         }
3140         uint32_t flags = (1 << 31) | (1 << 29); // contains header, this is header
3141         if (!write_footer) {
3142             flags |= 1 << 30; // contains no footer
3143         }
3144         uint32_t header[4] = {
3145             2000, // version
3146             size,
3147             numframes,
3148             flags
3149         };
3150         for (int i = 0; i < 4; i++) {
3151             if (junk_apev2_write_i32_le (fp, header[i]) != 0) {
3152                 trace ("junk_apev2_write_i32_le: failed to write apev2 header\n");
3153                 goto error;
3154             }
3155         }
3156         // write 8 bytes of padding
3157         header[0] = header[1] = 0;
3158         if (fwrite (header, 1, 8, fp) != 8) {
3159             trace ("junk_apev2_write_i32_le: failed to write apev2 header padding\n");
3160             goto error;
3161         }
3162     }
3163 
3164     // write items
3165     f = tag->frames;
3166     while (f) {
3167         if (junk_apev2_write_i32_le (fp, f->size) != 0) {
3168             trace ("junk_apev2_write_i32_le: failed to write apev2 item size\n");
3169             goto error;
3170         }
3171         if (junk_apev2_write_i32_le (fp, f->flags) != 0) {
3172             trace ("junk_apev2_write_i32_le: failed to write apev2 item flags\n");
3173             goto error;
3174         }
3175         int l = strlen (f->key) + 1;
3176         if (fwrite (f->key, 1, l, fp) != l) {
3177             trace ("junk_apev2_write_i32_le: failed to write apev2 item key\n");
3178             goto error;
3179         }
3180         if (fwrite (f->data, 1, f->size, fp) != f->size) {
3181             trace ("junk_apev2_write_i32_le: failed to write apev2 item value\n");
3182             goto error;
3183         }
3184         f = f->next;
3185     }
3186 
3187     if (write_footer) {
3188         if (fwrite ("APETAGEX", 1, 8, fp) != 8) {
3189             trace ("junk_apev2_write: failed to write apev2 footer signature\n");
3190             goto error;
3191         }
3192         uint32_t flags = 0;
3193         if (write_header) {
3194             flags |= 1 << 31;
3195         }
3196         uint32_t header[4] = {
3197             2000, // version
3198             size,
3199             numframes,
3200             flags
3201         };
3202         for (int i = 0; i < 4; i++) {
3203             if (junk_apev2_write_i32_le (fp, header[i]) != 0) {
3204                 trace ("junk_apev2_write_i32_le: failed to write apev2 footer\n");
3205                 goto error;
3206             }
3207         }
3208         // write 8 bytes of padding
3209         header[0] = header[1] = 0;
3210         if (fwrite (header, 1, 8, fp) != 8) {
3211             trace ("junk_apev2_write_i32_le: failed to write apev2 footer padding\n");
3212             goto error;
3213         }
3214     }
3215     return 0;
3216 error:
3217     return -1;
3218 }
3219 
3220 int
junk_id3v2_write2(int out,DB_id3v2_tag_t * tag)3221 junk_id3v2_write2 (int out, DB_id3v2_tag_t *tag) {
3222     if (tag->version[0] < 3) {
3223         fprintf (stderr, "junk_write_id3v2: writing id3v2.2 is not supported\n");
3224         return -1;
3225     }
3226 
3227     FILE *fp = NULL;
3228     char *buffer = NULL;
3229     int err = -1;
3230 
3231     // write tag header
3232     if (write (out, "ID3", 3) != 3) {
3233         fprintf (stderr, "junk_write_id3v2: failed to write ID3 signature\n");
3234         goto error;
3235     }
3236 
3237     if (write (out, tag->version, 2) != 2) {
3238         fprintf (stderr, "junk_write_id3v2: failed to write tag version\n");
3239         goto error;
3240     }
3241     uint8_t flags = tag->flags;
3242     flags &= ~(1<<6); // we don't (yet?) write ext header
3243     flags &= ~(1<<4); // we don't write footer
3244 
3245     if (write (out, &flags, 1) != 1) {
3246         fprintf (stderr, "junk_write_id3v2: failed to write tag flags\n");
3247         goto error;
3248     }
3249     // run through list of frames, and calculate size
3250     uint32_t sz = 0;
3251     for (DB_id3v2_frame_t *f = tag->frames; f; f = f->next) {
3252         // each tag has 10 bytes header
3253         if (tag->version[0] > 2) {
3254             sz += 10;
3255         }
3256         else {
3257             sz += 6;
3258         }
3259         sz += f->size;
3260     }
3261 
3262     trace ("calculated tag size: %d bytes\n", sz);
3263     uint8_t tagsize[4];
3264     tagsize[0] = (sz >> 21) & 0x7f;
3265     tagsize[1] = (sz >> 14) & 0x7f;
3266     tagsize[2] = (sz >> 7) & 0x7f;
3267     tagsize[3] = sz & 0x7f;
3268     if (write (out, tagsize, 4) != 4) {
3269         fprintf (stderr, "junk_write_id3v2: failed to write tag size\n");
3270         goto error;
3271     }
3272 
3273     trace ("writing frames\n");
3274     // write frames
3275     for (DB_id3v2_frame_t *f = tag->frames; f; f = f->next) {
3276         trace ("writing frame %s size %d\n", f->id, f->size);
3277         int id_size = 3;
3278         uint8_t frame_size[4];
3279         if (tag->version[0] > 2) {
3280             id_size = 4;
3281         }
3282         if (tag->version[0] == 3) {
3283             frame_size[0] = (f->size >> 24) & 0xff;
3284             frame_size[1] = (f->size >> 16) & 0xff;
3285             frame_size[2] = (f->size >> 8) & 0xff;
3286             frame_size[3] = f->size & 0xff;
3287         }
3288         else if (tag->version[0] == 4) {
3289             frame_size[0] = (f->size >> 21) & 0x7f;
3290             frame_size[1] = (f->size >> 14) & 0x7f;
3291             frame_size[2] = (f->size >> 7) & 0x7f;
3292             frame_size[3] = f->size & 0x7f;
3293         }
3294         if (write (out, f->id, 4) != 4) {
3295             fprintf (stderr, "junk_write_id3v2: failed to write frame id %s\n", f->id);
3296             goto error;
3297         }
3298         if (write (out, frame_size, 4) != 4) {
3299             fprintf (stderr, "junk_write_id3v2: failed to write frame size, id %s, size %d\n", f->id, f->size);
3300             goto error;
3301         }
3302         if (write (out, f->flags, 2) != 2) {
3303             fprintf (stderr, "junk_write_id3v2: failed to write frame header flags, id %s, size %d\n", f->id, f->size);
3304             goto error;
3305         }
3306         if (write (out, f->data, f->size) != f->size) {
3307             fprintf (stderr, "junk_write_id3v2: failed to write frame data, id %s, size %d\n", f->id, f->size);
3308             goto error;
3309         }
3310         sz += f->size;
3311     }
3312 
3313     return 0;
3314 
3315 error:
3316     if (buffer) {
3317         free (buffer);
3318     }
3319     return err;
3320 }
3321 
3322 int
junk_id3v2_write(FILE * out,DB_id3v2_tag_t * tag)3323 junk_id3v2_write (FILE *out, DB_id3v2_tag_t *tag) {
3324     if (tag->version[0] < 3) {
3325         fprintf (stderr, "junk_write_id3v2: writing id3v2.2 is not supported\n");
3326         return -1;
3327     }
3328 
3329     FILE *fp = NULL;
3330     char *buffer = NULL;
3331     int err = -1;
3332 
3333     // write tag header
3334     if (fwrite ("ID3", 1, 3, out) != 3) {
3335         fprintf (stderr, "junk_write_id3v2: failed to write ID3 signature\n");
3336         goto error;
3337     }
3338 
3339     if (fwrite (tag->version, 1, 2, out) != 2) {
3340         fprintf (stderr, "junk_write_id3v2: failed to write tag version\n");
3341         goto error;
3342     }
3343     uint8_t flags = tag->flags;
3344     flags &= ~(1<<6); // we don't (yet?) write ext header
3345     flags &= ~(1<<4); // we don't write footer
3346 
3347     if (fwrite (&flags, 1, 1, out) != 1) {
3348         fprintf (stderr, "junk_write_id3v2: failed to write tag flags\n");
3349         goto error;
3350     }
3351     // run through list of frames, and calculate size
3352     uint32_t sz = 0;
3353     for (DB_id3v2_frame_t *f = tag->frames; f; f = f->next) {
3354         // each tag has 10 bytes header
3355         if (tag->version[0] > 2) {
3356             sz += 10;
3357         }
3358         else {
3359             sz += 6;
3360         }
3361         sz += f->size;
3362     }
3363 
3364     trace ("calculated tag size: %d bytes\n", sz);
3365     uint8_t tagsize[4];
3366     tagsize[0] = (sz >> 21) & 0x7f;
3367     tagsize[1] = (sz >> 14) & 0x7f;
3368     tagsize[2] = (sz >> 7) & 0x7f;
3369     tagsize[3] = sz & 0x7f;
3370     if (fwrite (tagsize, 1, 4, out) != 4) {
3371         fprintf (stderr, "junk_write_id3v2: failed to write tag size\n");
3372         goto error;
3373     }
3374 
3375     trace ("writing frames\n");
3376     // write frames
3377     for (DB_id3v2_frame_t *f = tag->frames; f; f = f->next) {
3378         trace ("writing frame %s size %d\n", f->id, f->size);
3379         int id_size = 3;
3380         uint8_t frame_size[4];
3381         if (tag->version[0] > 2) {
3382             id_size = 4;
3383         }
3384         if (tag->version[0] == 3) {
3385             frame_size[0] = (f->size >> 24) & 0xff;
3386             frame_size[1] = (f->size >> 16) & 0xff;
3387             frame_size[2] = (f->size >> 8) & 0xff;
3388             frame_size[3] = f->size & 0xff;
3389         }
3390         else if (tag->version[0] == 4) {
3391             frame_size[0] = (f->size >> 21) & 0x7f;
3392             frame_size[1] = (f->size >> 14) & 0x7f;
3393             frame_size[2] = (f->size >> 7) & 0x7f;
3394             frame_size[3] = f->size & 0x7f;
3395         }
3396         if (fwrite (f->id, 1, 4, out) != 4) {
3397             fprintf (stderr, "junk_write_id3v2: failed to write frame id %s\n", f->id);
3398             goto error;
3399         }
3400         if (fwrite (frame_size, 1, 4, out) != 4) {
3401             fprintf (stderr, "junk_write_id3v2: failed to write frame size, id %s, size %d\n", f->id, f->size);
3402             goto error;
3403         }
3404         if (fwrite (f->flags, 1, 2, out) != 2) {
3405             fprintf (stderr, "junk_write_id3v2: failed to write frame header flags, id %s, size %d\n", f->id, f->size);
3406             goto error;
3407         }
3408         if (fwrite (f->data, 1, f->size, out) != f->size) {
3409             fprintf (stderr, "junk_write_id3v2: failed to write frame data, id %s, size %d\n", f->id, f->size);
3410             goto error;
3411         }
3412         sz += f->size;
3413     }
3414 
3415     return 0;
3416 
3417 error:
3418     if (buffer) {
3419         free (buffer);
3420     }
3421     return err;
3422 }
3423 
3424 void
junk_id3v2_free(DB_id3v2_tag_t * tag)3425 junk_id3v2_free (DB_id3v2_tag_t *tag) {
3426     while (tag->frames) {
3427         DB_id3v2_frame_t *next = tag->frames->next;
3428         free (tag->frames);
3429         tag->frames = next;
3430     }
3431 }
3432 
3433 void
junk_apev2_free(DB_apev2_tag_t * tag)3434 junk_apev2_free (DB_apev2_tag_t *tag) {
3435     while (tag->frames) {
3436         DB_apev2_frame_t *next = tag->frames->next;
3437         free (tag->frames);
3438         tag->frames = next;
3439     }
3440 }
3441 
3442 int
junklib_id3v2_sync_frame(uint8_t * data,int size)3443 junklib_id3v2_sync_frame (uint8_t *data, int size) {
3444     char *writeptr = data;
3445     int skipnext = 0;
3446     int written = 0;
3447     while (size > 0) {
3448         *writeptr = *data;
3449         if (data[0] == 0xff && size >= 2 && data[1] == 0) {
3450             data++;
3451             size--;
3452         }
3453         writeptr++;
3454         data++;
3455         size--;
3456         written++;
3457     }
3458     return written;
3459 }
3460 
3461 char *
junk_append_meta(const char * old,const char * new)3462 junk_append_meta (const char *old, const char *new) {
3463     int sz = strlen (old) + strlen (new) + 2;
3464     char *appended = malloc (sz);
3465     if (!appended) {
3466         trace ("junk_append_meta: failed to allocate %d bytes\n");
3467         return NULL;
3468     }
3469     snprintf (appended, sz, "%s\n%s", old, new);
3470     return appended;
3471 }
3472 
3473 int
junk_load_comm_frame(int version_major,playItem_t * it,uint8_t * readptr,int synched_size)3474 junk_load_comm_frame (int version_major, playItem_t *it, uint8_t *readptr, int synched_size) {
3475     uint8_t enc = readptr[0];
3476     char lang[4] = {readptr[1], readptr[2], readptr[3], 0};
3477     trace ("COMM enc: %d\n", (int)enc);
3478     trace ("COMM language: %s\n", lang);
3479     trace ("COMM data size: %d\n", synched_size);
3480 
3481     char *descr = convstr_id3v2 (version_major, enc, readptr+4, synched_size-4);
3482     if (!descr) {
3483         trace ("failed to decode COMM frame, probably wrong encoding (%d)\n", enc);
3484         return -1;
3485     }
3486 
3487     trace ("COMM raw data: %s\n", descr);
3488     // find value
3489     char *value = descr;
3490     while (*value && *value != '\n') {
3491         value++;
3492     }
3493     if (*value != '\n') {
3494         trace ("failed to parse COMM frame, descr was \"%s\"\n", descr);
3495         free (descr);
3496         return -1;
3497     }
3498 
3499     *value = 0;
3500     value++;
3501 
3502     int len = strlen (descr) + strlen (value) + 3;
3503     char comment[len];
3504 
3505     if (*descr) {
3506         snprintf (comment, len, "%s: %s", descr, value);
3507     }
3508     else {
3509         strcpy (comment, value);
3510     }
3511 
3512     trace ("COMM combined: %s\n", comment);
3513     // skip utf8 BOM (can be produced by iconv FEFF/FFFE)
3514     int l = strlen (comment);
3515     uint8_t bom[] = { 0xEF, 0xBB, 0xBF };
3516     if (l >= 3 && !memcmp (comment, bom, 3)) {
3517         pl_append_meta (it, "comment", comment+3);
3518     }
3519     else {
3520         pl_append_meta (it, "comment", comment);
3521     }
3522 
3523     free (descr);
3524     return 0;
3525 }
3526 
3527 /* Parse RVA2 tag */
3528 /* Currently only supports tags wich set master volume and are labeled "track"
3529  * or "album". Also only supports peak value if stored as 16 bits. */
junk_id3v2_load_rva2(int version_major,playItem_t * it,uint8_t * readptr,int synched_size)3530 static int junk_id3v2_load_rva2 (int version_major, playItem_t *it, uint8_t *readptr, int synched_size) {
3531     uint8_t *rva_desc = readptr;
3532     unsigned rva_desc_len = 0;
3533     const uint8_t *p = rva_desc;
3534     while (*p++ && rva_desc_len < synched_size) {
3535         rva_desc_len++;
3536     }
3537 
3538     if(rva_desc_len == synched_size) { /* tag too short */
3539         return -1;
3540     }
3541     if(rva_desc_len != 5) { /* only support track or album labeled ones */
3542         return 0;
3543     }
3544 
3545     if(synched_size < rva_desc_len + 1 + 4) return -1; /* at least 4 bytes after zero-terminated label */
3546 
3547     uint8_t *rva_data = rva_desc + rva_desc_len + 1;
3548 
3549     uint8_t vol_type = rva_data[0];
3550 
3551     if(vol_type != 1) return 0;
3552 
3553     int16_t volume_adjust = (int16_t)(((int16_t)rva_data[1] << 8) | rva_data[2]); /* this is little-endian safe :) */
3554     uint8_t peak_bits = rva_data[3];
3555     uint16_t peak_val = 0;
3556 
3557     if(peak_bits == 16 && synched_size >= rva_desc_len + 1 + 6) {
3558         peak_val = (uint16_t)((rva_data[4] << 8) | rva_data[5]);
3559     }
3560 
3561     if (!strcasecmp (rva_desc, "album")) {
3562         if (!pl_find_meta (it, ddb_internal_rg_keys[DDB_REPLAYGAIN_ALBUMGAIN])) {
3563             pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, (float)volume_adjust / 512.0);
3564         }
3565         if (!pl_find_meta (it, ddb_internal_rg_keys[DDB_REPLAYGAIN_ALBUMPEAK]) && peak_val) {
3566             pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, (float)peak_val / 32767.0); /* NOTE: this is a guess based on mp3gain 1.5.2 written tags */
3567         }
3568     }
3569     else if (!strcasecmp (rva_desc, "track")) {
3570         if (!pl_find_meta (it, ddb_internal_rg_keys[DDB_REPLAYGAIN_TRACKGAIN])) {
3571             pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, (float)volume_adjust / 512.0);
3572         }
3573         if (!pl_find_meta (it, ddb_internal_rg_keys[DDB_REPLAYGAIN_TRACKPEAK]) && peak_val) {
3574             pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, (float)peak_val / 32767.0);
3575         }
3576     }
3577 
3578     return 0;
3579 }
3580 
3581 int
junk_id3v2_load_ufid(int version_major,playItem_t * it,uint8_t * readptr,int synched_size)3582 junk_id3v2_load_ufid (int version_major, playItem_t *it, uint8_t *readptr, int synched_size) {
3583     char *owner = readptr;
3584     while (*readptr && synched_size > 0) {
3585         readptr++;
3586         synched_size--;
3587     }
3588     if (!synched_size) {
3589         trace ("UFID owner is not null-terminated\n");
3590         return -1;
3591     }
3592     readptr++;
3593     synched_size--;
3594     char id[synched_size+1];
3595     memcpy (id, readptr, synched_size);
3596     id[synched_size] = 0;
3597 
3598     // verify that owner is musicbrainz and that content is ascii
3599     if (strcmp (owner, "http://musicbrainz.org")) {
3600         return -1;
3601     }
3602     for (int i = 0; i < synched_size; i++) {
3603         if (!isascii (id[i])) {
3604             return -1;
3605         }
3606     }
3607 
3608     pl_replace_meta (it, "musicbrainz_trackid", id);
3609     return 0;
3610 }
3611 
3612 int
junk_id3v2_remove_ufid_frames(DB_id3v2_tag_t * tag,const char * frame_id,const char * owner)3613 junk_id3v2_remove_ufid_frames (DB_id3v2_tag_t *tag, const char *frame_id, const char *owner) {
3614     DB_id3v2_frame_t *prev = NULL;
3615     for (DB_id3v2_frame_t *f = tag->frames; f; ) {
3616         DB_id3v2_frame_t *next = f->next;
3617         if (!strcmp (f->id, frame_id) && f->size >= strlen(owner) && !strcmp (f->data, owner)) {
3618             if (prev) {
3619                 prev->next = f->next;
3620             }
3621             else {
3622                 tag->frames = f->next;
3623             }
3624             free (f);
3625         }
3626         else {
3627             prev = f;
3628         }
3629         f = next;
3630     }
3631     return 0;
3632 }
3633 
3634 DB_id3v2_frame_t *
junk_id3v2_add_ufid_frame(DB_id3v2_tag_t * tag,const char * owner,const char * id,int id_len)3635 junk_id3v2_add_ufid_frame (DB_id3v2_tag_t *tag, const char *owner, const char *id, int id_len) {
3636     int ownerlen = strlen (owner);
3637     int len = ownerlen + 1 + id_len;
3638 
3639     // make a frame
3640     trace ("calculated frame size = %d\n", len);
3641     DB_id3v2_frame_t *f = malloc (len + sizeof (DB_id3v2_frame_t));
3642     memset (f, 0, sizeof (DB_id3v2_frame_t));
3643     strcpy (f->id, "UFID");
3644     f->size = len;
3645     memcpy (f->data, owner, ownerlen+1);
3646     memcpy (f->data+ownerlen+1, id, id_len);
3647     // append to tag
3648     DB_id3v2_frame_t *tail;
3649     for (tail = tag->frames; tail && tail->next; tail = tail->next);
3650     if (tail) {
3651         tail->next = f;
3652     }
3653     else {
3654         tag->frames = f;
3655     }
3656 
3657     return f;
3658 }
3659 
3660 int
junk_id3v2_load_txx(int version_major,playItem_t * it,uint8_t * readptr,int synched_size)3661 junk_id3v2_load_txx (int version_major, playItem_t *it, uint8_t *readptr, int synched_size) {
3662     char *txx = convstr_id3v2 (version_major, *readptr, readptr+1, synched_size-1);
3663     if (!txx) {
3664         return -1;
3665     }
3666 
3667     char *val = NULL;
3668     if (txx) {
3669         char *p;
3670         for (p = txx; *p; p++) {
3671             if (*p == '\n') {
3672                 *p = 0;
3673                 val = p+1;
3674                 break;
3675             }
3676         }
3677     }
3678 
3679     if (val) {
3680         // skip utf8 BOM (can be produced by iconv FEFF/FFFE)
3681         int l = strlen (val);
3682         uint8_t bom[] = { 0xEF, 0xBB, 0xBF };
3683         if (l >= 3 && !memcmp (val, bom, 3)) {
3684             val += 3;
3685         }
3686 
3687         if (!strcasecmp (txx, "replaygain_album_gain")) {
3688             pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, atof (val));
3689         }
3690         else if (!strcasecmp (txx, "replaygain_album_peak")) {
3691             pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, atof (val));
3692         }
3693         else if (!strcasecmp (txx, "replaygain_track_gain")) {
3694             pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, atof (val));
3695         }
3696         else if (!strcasecmp (txx, "replaygain_track_peak")) {
3697             pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (val));
3698         }
3699         else if (!strcasecmp (txx, "date")) { // HACK: fb2k date support
3700             pl_append_meta (it, "year", val);
3701         }
3702         else {
3703             pl_append_meta (it, txx, val);
3704         }
3705     }
3706 
3707     free (txx);
3708 
3709     return 0;
3710 }
3711 
3712 int
junk_id3v2_add_genre(playItem_t * it,char * genre)3713 junk_id3v2_add_genre (playItem_t *it, char *genre) {
3714     int numeric = 0;
3715     if (genre[0] == '(') {
3716         // find matching parenthesis
3717         char *p = &genre[1];
3718         while (*p && *p != ')') {
3719             if (!isdigit (*p)) {
3720                 break;
3721             }
3722             p++;
3723         }
3724         if (*p == ')') {
3725             *p = 0;
3726             memmove (genre, genre+1, p-genre);
3727             numeric = 1;
3728         }
3729     }
3730     if (!numeric) {
3731         // check if it is numeric
3732         const char *p = genre;
3733         while (*p) {
3734             if (!isdigit (*p)) {
3735                 break;
3736             }
3737             p++;
3738         }
3739         if (*p == 0 && p > genre) {
3740             numeric = 1;
3741         }
3742     }
3743 
3744     if (numeric) {
3745         int genre_id = atoi (genre);
3746         if (genre_id >= 0) {
3747             const char *genre_str = NULL;
3748             if (genre_id < ID3V1_GENRE_COUNT) {
3749                 genre_str = junk_genretbl[genre_id];
3750             }
3751             else if (genre_id == 0xff) {
3752                 // genre_str = "None";
3753             }
3754             if (genre_str) {
3755                 pl_add_meta (it, "genre", genre_str);
3756                 return 0;
3757             }
3758         }
3759     }
3760     else if (!strcmp (genre, "CR")) {
3761         pl_add_meta (it, "genre", "Cover");
3762     }
3763     else if (!strcmp (genre, "RX")) {
3764         pl_add_meta (it, "genre", "Remix");
3765     }
3766     else {
3767         pl_add_meta (it, "genre", genre);
3768     }
3769 
3770     return 0;
3771 }
3772 
3773 int
junk_id3v2_read_full(playItem_t * it,DB_id3v2_tag_t * tag_store,DB_FILE * fp)3774 junk_id3v2_read_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
3775     DB_id3v2_frame_t *tail = NULL;
3776     int title_added = 0;
3777     if (!fp) {
3778         trace ("bad call to junk_id3v2_read!\n");
3779         return -1;
3780     }
3781     deadbeef->rewind (fp);
3782     uint8_t header[10];
3783     if (deadbeef->fread (header, 1, 10, fp) != 10) {
3784         return -1; // too short
3785     }
3786     if (strncmp (header, "ID3", 3)) {
3787         return -1; // no tag
3788     }
3789     uint8_t version_major = header[3];
3790     uint8_t version_minor = header[4];
3791     if (version_major > 4 || version_major < 2) {
3792         trace ("id3v2.%d.%d is unsupported\n", version_major, version_minor);
3793         return -1; // unsupported
3794     }
3795     uint8_t flags = header[5];
3796     if (flags & 15) {
3797         trace ("unrecognized flags: one of low 15 bits is set, value=0x%x\n", (int)flags);
3798         return -1; // unsupported
3799     }
3800     int unsync = (flags & (1<<7)) ? 1 : 0;
3801     int extheader = (flags & (1<<6)) ? 1 : 0;
3802     int expindicator = (flags & (1<<5)) ? 1 : 0;
3803     int footerpresent = (flags & (1<<4)) ? 1 : 0;
3804     // check for bad size
3805     if ((header[9] & 0x80) || (header[8] & 0x80) || (header[7] & 0x80) || (header[6] & 0x80)) {
3806         trace ("bad header size\n");
3807         return -1; // bad header
3808     }
3809     uint32_t size = (header[9] << 0) | (header[8] << 7) | (header[7] << 14) | (header[6] << 21);
3810 
3811     trace ("tag size: %d\n", size);
3812     if (size == 0) {
3813         return -1;
3814     }
3815     if (tag_store) {
3816         tag_store->version[0] = version_major;
3817         tag_store->version[1] = version_minor;
3818         tag_store->flags = flags;
3819         // remove unsync flag
3820         tag_store->flags &= ~ (1<<7);
3821     }
3822 
3823     uint8_t *tag = malloc (size);
3824     if (!tag) {
3825         fprintf (stderr, "junklib: out of memory while reading id3v2, tried to alloc %d bytes\n", size);
3826         goto error;
3827     }
3828     if (deadbeef->fread (tag, 1, size, fp) != size) {
3829         goto error; // bad size
3830     }
3831     uint8_t *readptr = tag;
3832     int crcpresent = 0;
3833     trace ("version: 2.%d.%d, unsync: %d, extheader: %d, experimental: %d\n", version_major, version_minor, unsync, extheader, expindicator);
3834 
3835     if (extheader) {
3836         uint32_t sz = (readptr[3] << 0) | (readptr[2] << 7) | (readptr[1] << 14) | (readptr[0] << 21);
3837         if (size < sz) {
3838             trace ("error: size of ext header (%d) is greater than tag size\n", sz);
3839             goto error; // bad size
3840         }
3841         readptr += sz;
3842     }
3843     int err = -1;
3844     while (readptr - tag <= size - 4 && *readptr) {
3845         if (version_major == 3 || version_major == 4) {
3846             trace ("pos %d of %d\n", readptr - tag, size);
3847             char frameid[5];
3848             memcpy (frameid, readptr, 4);
3849             frameid[4] = 0;
3850             readptr += 4;
3851             if (readptr - tag >= size - 4) {
3852                 trace ("reached the end of tag\n");
3853                 break;
3854             }
3855             uint32_t sz;
3856             if (version_major == 4) {
3857                 sz = (readptr[3] << 0) | (readptr[2] << 7) | (readptr[1] << 14) | (readptr[0] << 21);
3858             }
3859             else if (version_major == 3) {
3860                 sz = (readptr[3] << 0) | (readptr[2] << 8) | (readptr[1] << 16) | (readptr[0] << 24);
3861             }
3862             else {
3863                 trace ("unknown id3v2 version (2.%d.%d)\n", version_major, version_minor);
3864                 goto error;
3865             }
3866             readptr += 4;
3867             trace ("got frame %s, size %d, pos %d, tagsize %d\n", frameid, sz, readptr-tag, size);
3868             if (readptr - tag >= size - sz) {
3869                 trace ("frame is out of tag bounds\n");
3870                 err = -1;
3871                 goto error; // size of frame is more than size of tag
3872             }
3873             if (sz < 1) {
3874 //                err = 1;
3875                 break; // frame must be at least 1 byte long
3876             }
3877             uint8_t flags1 = readptr[0];
3878             uint8_t flags2 = readptr[1];
3879             readptr += 2;
3880 
3881             if (!strcmp (frameid, "APIC")) {
3882                 if (sz > MAX_ID3V2_APIC_FRAME_SIZE) {
3883                     trace ("junk_id3v2_read_full: frame %s size is too big (%d), discarded\n", frameid, sz);
3884                     readptr += sz;
3885                     continue;
3886                 }
3887             }
3888             else if (sz > MAX_ID3V2_FRAME_SIZE || readptr - tag + sz > size) {
3889                 trace ("junk_id3v2_read_full: frame %s size is too big (%d), discarded\n", frameid, sz);
3890                 readptr += sz;
3891                 continue;
3892             }
3893             int synched_size = sz;
3894             if (unsync) {
3895                 synched_size = junklib_id3v2_sync_frame (readptr, sz);
3896                 trace ("size: %d/%d\n", synched_size, sz);
3897             }
3898 
3899             if (tag_store) {
3900                 DB_id3v2_frame_t *frm = malloc (sizeof (DB_id3v2_frame_t) + sz);
3901                 if (!frm) {
3902                     fprintf (stderr, "junklib: failed to alloc %d bytes for id3v2 frame %s\n", (int)(sizeof (DB_id3v2_frame_t) + sz), frameid);
3903                     goto error;
3904                 }
3905                 memset (frm, 0, sizeof (DB_id3v2_frame_t));
3906                 if (tail) {
3907                     tail->next = frm;
3908                 }
3909                 tail = frm;
3910                 if (!tag_store->frames) {
3911                     tag_store->frames = frm;
3912                 }
3913                 strcpy (frm->id, frameid);
3914                 memcpy (frm->data, readptr, sz);
3915                 frm->size = synched_size;
3916 
3917                 frm->flags[0] = flags1;
3918                 frm->flags[1] = flags2;
3919             }
3920             if (version_major == 4) {
3921                 if (flags1 & 0x8f) {
3922                     // unknown flags
3923                     trace ("unknown status flags: %02x\n", flags1);
3924                     readptr += sz;
3925                     continue;
3926                 }
3927                 if (flags2 & 0xb0) {
3928                     // unknown flags
3929                     trace ("unknown format flags: %02x\n", flags2);
3930                     readptr += sz;
3931                     continue;
3932                 }
3933 
3934                 if (flags2 & 0x40) { // group id
3935                     trace ("frame has group id\n");
3936                     readptr++; // skip id
3937                     sz--;
3938                 }
3939                 if (flags2 & 0x08) { // compressed frame, ignore
3940                     trace ("frame is compressed, skipping\n");
3941                     readptr += sz;
3942                     continue;
3943                 }
3944                 if (flags2 & 0x04) { // encrypted frame, skip
3945                     trace ("frame is encrypted, skipping\n");
3946                     readptr += sz;
3947                     continue;
3948                 }
3949                 if (flags2 & 0x02) { // unsync, just do nothing
3950                 }
3951                 if (flags2 & 0x01) { // data size
3952                     synched_size = (readptr[3] << 0) | (readptr[2] << 7) | (readptr[1] << 14) | (readptr[0] << 21);
3953                     trace ("frame has extra size field = %d\n", synched_size);
3954                     readptr += 4;
3955                     sz -= 4;
3956                 }
3957             }
3958             else if (version_major == 3) {
3959                 if (flags1 & 0x1F) {
3960                     trace ("unknown status flags: %02x\n", flags1);
3961                     readptr += sz;
3962                     continue;
3963                 }
3964                 if (flags2 & 0x1F) {
3965                     trace ("unknown format flags: %02x\n", flags2);
3966                     readptr += sz;
3967                     continue;
3968                 }
3969                 if (flags2 & 0x80) {
3970                     trace ("frame is compressed, skipping\n");
3971                     readptr += sz;
3972                     continue;
3973                 }
3974                 if (flags2 & 0x40) {
3975                     trace ("frame is encrypted, skipping\n");
3976                     readptr += sz;
3977                     continue;
3978                 }
3979                 if (flags2 & 0x20) {
3980                     trace ("frame has group id\n");
3981                     readptr++; // skip id
3982                     sz--;
3983                 }
3984             }
3985 
3986             // parse basic 2.3/2.4 text frames
3987             //const char *text_frames[] = { "TPE1", "TPE2", "TPOS", "TIT2", "TALB", "TCOP", "TCON", "TENC", "TPE3", "TCOM", "TRCK", "TYER", "TDRC", NULL };
3988             //char **text_holders[] = { &artist, &band, &disc, &title, &album, &copyright, &genre, &vendor, &performer, &composer, &track, version_major == 3 ? &year : NULL,  version_major == 4 ? &year : NULL, };
3989             if (it) {
3990                 int added = 0;
3991                 if (strcmp (frameid, "TXXX")) {
3992                     for (int f = 0; frame_mapping[f]; f += FRAME_MAPPINGS) {
3993                         const char *frm_name = version_major == 3 ? frame_mapping[f+MAP_ID3V23] : frame_mapping[f+MAP_ID3V24];
3994                         if (frm_name && !strcmp (frameid, frm_name)) {
3995                             added = 1;
3996                             if (synched_size > MAX_TEXT_FRAME_SIZE) {
3997                                 trace ("frame %s is too big, discard\n", frameid);
3998                                 break;
3999                             }
4000 
4001                             char *text = convstr_id3v2 (version_major, readptr[0], readptr+1, synched_size-1);
4002 
4003                             // couple of simple tests
4004                             //char *text = convstr_id3v2 (4, 3, "текст1\0текст2", strlen ("текст1")*2+2);
4005                             //const char ucstext[] = { 0x42, 0x04, 0x35, 0x04, 0x3a, 0x04, 0x41, 0x04, 0x42, 0x04, 0x31, 0x00, 0x00, 0x00, 0x42, 0x04, 0x35, 0x04, 0x3a, 0x04, 0x41, 0x04, 0x42, 0x04, 0x32, 0x00 };
4006                             //char *text = convstr_id3v2 (4, 1, ucstext, sizeof (ucstext));
4007 
4008                             if (text && *text) {
4009                                 if (!strcmp (frameid, "TRCK")) { // special case for track/totaltracks
4010                                     junk_add_track_meta (it, text);
4011                                 }
4012                                 if (!strcmp (frameid, "TPOS")) { // special case for disc/totaldiscs
4013                                     junk_add_disc_meta (it, text);
4014                                 }
4015                                 else if (!strcmp (frameid, "TCON")) {
4016                                     junk_id3v2_add_genre (it, text);
4017                                 }
4018                                 else {
4019                                     pl_append_meta (it, frame_mapping[f+MAP_DDB], text);
4020                                 }
4021 //                                if (text) {
4022 //                                    trace ("%s = %s\n", frameid, text);
4023 //                                }
4024                             }
4025                             if (text) {
4026                                 free (text);
4027                             }
4028                             break;
4029                         }
4030                     }
4031                 }
4032 
4033                 if (added) {
4034                     readptr += sz;
4035                     continue;
4036                 }
4037 
4038                 if (!strcmp (frameid, "COMM")) {
4039                     if (sz < 4) {
4040                         trace ("COMM frame is too short, skipped\n");
4041                         readptr += sz; // bad tag
4042                         continue;
4043                     }
4044 
4045                     /*int res = */junk_load_comm_frame (version_major, it, readptr, synched_size);
4046                 }
4047                 else if (it && !strcmp (frameid, "RVA2")) {
4048                     if (synched_size < 5) {
4049                         trace ("RVA2 frame is too short, skipped\n");
4050                         readptr += sz; // bad tag
4051                         continue;
4052                     }
4053 
4054                     /*int res = */junk_id3v2_load_rva2(version_major, it, readptr, synched_size);
4055                 }
4056                 else if (it && !strcmp (frameid, "UFID")) {
4057                     if (synched_size < 2) {
4058                         trace ("UFID frame is too short, skipped\n");
4059                         readptr += sz; // bad tag
4060                         continue;
4061                     }
4062                     junk_id3v2_load_ufid (version_major, it, readptr, synched_size);
4063                 }
4064                 else if (it && !strcmp (frameid, "TXXX")) {
4065                     if (synched_size < 2) {
4066                         trace ("TXXX frame is too short, skipped\n");
4067                         readptr += sz; // bad tag
4068                         continue;
4069                     }
4070                     int res = junk_id3v2_load_txx (version_major, it, readptr, synched_size);
4071                 }
4072             }
4073             readptr += sz;
4074         }
4075         else if (version_major == 2) {
4076             char frameid[4];
4077             memcpy (frameid, readptr, 3);
4078             frameid[3] = 0;
4079             readptr += 3;
4080             if (readptr - tag >= size - 3) {
4081                 break;
4082             }
4083             uint32_t sz = (readptr[2] << 0) | (readptr[1] << 8) | (readptr[0] << 16);
4084             readptr += 3;
4085             if (readptr - tag >= size - sz) {
4086                 break; // size of frame is less than size of tag
4087             }
4088             if (sz < 1) {
4089                 break; // frame must be at least 1 byte long
4090             }
4091             if (sz > MAX_ID3V2_FRAME_SIZE) {
4092                 trace ("junk_id3v2_read_full: frame %s size is too big, discarded\n", frameid);
4093                 readptr += sz;
4094                 continue;
4095             }
4096             int synched_size = sz;
4097             if (unsync) {
4098                 synched_size = junklib_id3v2_sync_frame (readptr, sz);
4099             }
4100 
4101             if (tag_store) {
4102                 DB_id3v2_frame_t *frm = malloc (sizeof (DB_id3v2_frame_t) + sz);
4103                 if (!frm) {
4104                     fprintf (stderr, "junklib: failed to alloc %d bytes for id3v2.2 frame %s\n", (int)(sizeof (DB_id3v2_frame_t) + sz), frameid);
4105                     goto error;
4106                 }
4107                 memset (frm, 0, sizeof (DB_id3v2_frame_t));
4108                 if (tail) {
4109                     tail->next = frm;
4110                 }
4111                 tail = frm;
4112                 if (!tag_store->frames) {
4113                     tag_store->frames = frm;
4114                 }
4115                 strcpy (frm->id, frameid);
4116                 memcpy (frm->data, readptr, synched_size);
4117                 frm->size = sz;
4118             }
4119 //            trace ("found id3v2.2 frame: %s, size=%d\n", frameid, sz);
4120 
4121             // parse basic 2.2 text frames
4122             if (it) {
4123                 int added = 0;
4124                 if (strcmp (frameid, "TXX")) {
4125                     for (int f = 0; frame_mapping[f]; f++) {
4126                         if (frame_mapping[f+MAP_ID3V22] && !strcmp (frameid, frame_mapping[f+MAP_ID3V22])) {
4127                             added = 1;
4128                             if (synched_size > MAX_TEXT_FRAME_SIZE) {
4129                                 trace ("frame %s is too big, discard\n", frameid);
4130                                 break;
4131                             }
4132                             char *text = convstr_id3v2 (version_major, readptr[0], readptr+1, synched_size-1);
4133                             if (text && *text) {
4134                                 if (!strcmp (frameid, "TRK")) { // special case for track/totaltracks
4135                                     junk_add_track_meta (it, text);
4136                                 }
4137                                 if (!strcmp (frameid, "TPA")) { // special case for disc/totaldiscs
4138                                     junk_add_disc_meta (it, text);
4139                                 }
4140                                 else if (!strcmp (frameid, "TCO")) {
4141                                     junk_id3v2_add_genre (it, text);
4142                                 }
4143                                 else {
4144                                     pl_append_meta (it, frame_mapping[f+MAP_DDB], text);
4145                                 }
4146                                 free (text);
4147                             }
4148                             break;
4149                         }
4150                     }
4151                 }
4152 
4153                 if (added) {
4154                     readptr += sz;
4155                     continue;
4156                 }
4157 
4158                 if (!strcmp (frameid, "COM")) {
4159                     if (synched_size < 6) {
4160                         readptr += sz;
4161                         continue;
4162                     }
4163                     /*int res = */junk_load_comm_frame (version_major, it, readptr, synched_size);
4164                 }
4165                 else if (it && !strcmp (frameid, "TXX")) {
4166                     if (synched_size < 2) {
4167                         trace ("TXX frame is too short, skipped\n");
4168                         readptr += sz; // bad tag
4169                         continue;
4170                     }
4171                     int res = junk_id3v2_load_txx (version_major, it, readptr, synched_size);
4172                 }
4173             }
4174             readptr += sz;
4175         }
4176         else {
4177             trace ("id3v2.%d (unsupported!)\n", version_minor);
4178         }
4179     }
4180     if (it) {
4181         if (version_major == 2) {
4182             uint32_t f = pl_get_item_flags (it);
4183             f |= DDB_TAG_ID3V22;
4184             pl_set_item_flags (it, f);
4185         }
4186         else if (version_major == 3) {
4187             uint32_t f = pl_get_item_flags (it);
4188             f |= DDB_TAG_ID3V23;
4189             pl_set_item_flags (it, f);
4190         }
4191         else if (version_major == 4) {
4192             uint32_t f = pl_get_item_flags (it);
4193             f |= DDB_TAG_ID3V24;
4194             pl_set_item_flags (it, f);
4195         }
4196     }
4197     err = 0;
4198 error:
4199     if (err != 0) {
4200         trace ("error parsing id3v2\n");
4201     }
4202 
4203     if (tag) {
4204         free (tag);
4205     }
4206     if (tag_store && err != 0) {
4207         while (tag_store->frames) {
4208             DB_id3v2_frame_t *next = tag_store->frames->next;
4209             free (tag_store->frames);
4210             tag_store->frames = next;
4211         }
4212     }
4213     return err;
4214 }
4215 
4216 int
junk_id3v2_read(playItem_t * it,DB_FILE * fp)4217 junk_id3v2_read (playItem_t *it, DB_FILE *fp) {
4218     return junk_id3v2_read_full (it, NULL, fp);
4219 }
4220 
4221 const char *
junk_detect_charset_len(const char * s,int len)4222 junk_detect_charset_len (const char *s, int len) {
4223     // check if that's already utf8
4224     if (u8_valid (s, len, NULL)) {
4225         return NULL; // means no recoding required
4226     }
4227     // try shift-jis
4228     if (enable_shift_jis_detection && can_be_shift_jis (s, len)) {
4229         return "shift-jis";
4230     }
4231     // hack to add cp936 support
4232     if (can_be_chinese (s, len)) {
4233        return "cp936";
4234     }
4235     // check if that could be non-latin1 (too many nonascii chars)
4236     if (can_be_russian (s, len)) {
4237         return "cp1251";
4238     }
4239 
4240     return "cp1252";
4241 }
4242 
4243 const char *
junk_detect_charset(const char * s)4244 junk_detect_charset (const char *s) {
4245     size_t len = strlen (s);
4246     return junk_detect_charset_len (s, len);
4247 }
4248 
4249 int
junk_recode(const char * in,int inlen,char * out,int outlen,const char * cs)4250 junk_recode (const char *in, int inlen, char *out, int outlen, const char *cs) {
4251     return junk_iconv (in, inlen, out, outlen, cs, UTF8_STR);
4252 }
4253 
4254 int
junk_rewrite_tags(playItem_t * it,uint32_t junk_flags,int id3v2_version,const char * id3v1_encoding)4255 junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const char *id3v1_encoding) {
4256     trace ("junk_rewrite_tags %X\n", junk_flags);
4257     int err = -1;
4258     char *buffer = NULL;
4259     DB_FILE *fp = NULL;
4260     int out = -1;
4261 
4262     uint32_t item_flags = pl_get_item_flags (it);
4263 
4264     // get options
4265     int strip_id3v2 = junk_flags & JUNK_STRIP_ID3V2;
4266     int strip_id3v1 = junk_flags & JUNK_STRIP_ID3V1;
4267     int strip_apev2 = junk_flags & JUNK_STRIP_APEV2;
4268     int write_id3v2 = junk_flags & JUNK_WRITE_ID3V2;
4269     int write_id3v1 = junk_flags & JUNK_WRITE_ID3V1;
4270     int write_apev2 = junk_flags & JUNK_WRITE_APEV2;
4271 
4272     char tmppath[PATH_MAX];
4273     // find the beginning and the end of audio data
4274     char fname[PATH_MAX];
4275     pl_get_meta (it, ":URI", fname, sizeof (fname));
4276     snprintf (tmppath, sizeof (tmppath), "%s.temp", fname);
4277     fp = deadbeef->fopen (fname);
4278     if (!fp) {
4279         trace ("file not found %s\n", fname);
4280         return -1;
4281     }
4282 
4283     int64_t fsize = deadbeef->fgetlength (fp);
4284     int id3v2_size = 0;
4285     int id3v2_start = deadbeef->junk_id3v2_find (fp, &id3v2_size);
4286     if (id3v2_start == -1) {
4287         id3v2_size = -1;
4288     }
4289 
4290     int32_t apev2_size;
4291     uint32_t flags, numitems;
4292     int64_t apev2_start = junk_apev2_find2 (fp, &apev2_size, &flags, &numitems);
4293     if (apev2_start == -1) {
4294         apev2_start = 0;
4295     }
4296 
4297     if (!strip_apev2 && !write_apev2) {
4298         apev2_start = 0;
4299     }
4300 
4301     int64_t id3v1_start = junk_id3v1_find2 (fp);
4302     if (id3v1_start == -1) {
4303         id3v1_start = 0;
4304     }
4305 
4306     int64_t header = 0;
4307     if (id3v2_size > 0) {
4308         header = id3v2_start + id3v2_size;
4309     }
4310 
4311     int64_t footer = fsize;
4312 
4313     if (id3v1_start > 0) {
4314         footer = id3v1_start;
4315     }
4316     if (apev2_start > 0) {
4317         footer = min (footer, apev2_start);
4318     }
4319 
4320     trace ("header size: %lld, footer size: %lld\n", header, fsize-footer);
4321 
4322     // "TRCK" -- special case
4323     // "TYER"/"TDRC" -- special case
4324 
4325     // open output file
4326     struct stat stat_struct;
4327     if (stat(fname, &stat_struct) != 0) {
4328         stat_struct.st_mode = 00640;
4329     }
4330     out = open (tmppath, O_CREAT | O_LARGEFILE | O_WRONLY, stat_struct.st_mode);
4331     trace ("will write tags into %s\n", tmppath);
4332     if (out < 0) {
4333         fprintf (stderr, "cmp3_write_metadata: failed to open temp file %s\n", tmppath);
4334         goto error;
4335     }
4336 
4337     DB_id3v2_tag_t id3v2;
4338     DB_apev2_tag_t apev2;
4339 
4340     memset (&id3v2, 0, sizeof (id3v2));
4341     memset (&apev2, 0, sizeof (apev2));
4342 
4343     if (!strip_id3v2 && !write_id3v2 && id3v2_size > 0) {
4344         if (deadbeef->fseek (fp, id3v2_start, SEEK_SET) == -1) {
4345             trace ("cmp3_write_metadata: failed to seek to original id3v2 tag position in %s\n", pl_find_meta (it, ":URI"));
4346             goto error;
4347         }
4348         uint8_t *buf = malloc (id3v2_size);
4349         if (!buf) {
4350             trace ("cmp3_write_metadata: failed to alloc %d bytes for id3v2 tag\n", id3v2_size);
4351             goto error;
4352         }
4353         if (deadbeef->fread (buf, 1, id3v2_size, fp) != id3v2_size) {
4354             trace ("cmp3_write_metadata: failed to read original id3v2 tag from %s\n", pl_find_meta (it, ":URI"));
4355             free (buf);
4356             goto error;
4357         }
4358         if (write (out, buf, id3v2_size) != id3v2_size) {
4359             trace ("cmp3_write_metadata: failed to copy original id3v2 tag from %s to temp file\n", pl_find_meta (it, ":URI"));
4360             free (buf);
4361             goto error;
4362         }
4363         free (buf);
4364     }
4365     else if (write_id3v2) {
4366         trace ("writing id3v2\n");
4367         if (id3v2_size <= 0 || strip_id3v2 || deadbeef->junk_id3v2_read_full (NULL, &id3v2, fp) != 0) {
4368             deadbeef->junk_id3v2_free (&id3v2);
4369             memset (&id3v2, 0, sizeof (id3v2));
4370             id3v2.version[0] = id3v2_version;
4371         }
4372         // convert to required version
4373         while (id3v2.version[0] != id3v2_version) {
4374             DB_id3v2_tag_t converted;
4375             memset (&converted, 0, sizeof (converted));
4376             if (id3v2.version[0] == 2) {
4377                 if (deadbeef->junk_id3v2_convert_22_to_24 (&id3v2, &converted) != 0) {
4378                     goto error;
4379                 }
4380                 deadbeef->junk_id3v2_free (&id3v2);
4381                 memcpy (&id3v2, &converted, sizeof (DB_id3v2_tag_t));
4382                 continue;
4383             }
4384             else if (id3v2.version[0] == 3) {
4385                 if (deadbeef->junk_id3v2_convert_23_to_24 (&id3v2, &converted) != 0) {
4386                     goto error;
4387                 }
4388                 deadbeef->junk_id3v2_free (&id3v2);
4389                 memcpy (&id3v2, &converted, sizeof (DB_id3v2_tag_t));
4390                 continue;
4391             }
4392             else if (id3v2.version[0] == 4) {
4393                 if (deadbeef->junk_id3v2_convert_24_to_23 (&id3v2, &converted) != 0) {
4394                     goto error;
4395                 }
4396                 deadbeef->junk_id3v2_free (&id3v2);
4397                 memcpy (&id3v2, &converted, sizeof (DB_id3v2_tag_t));
4398                 continue;
4399             }
4400         }
4401 
4402         junk_id3v2_remove_all_txxx_frames (&id3v2);
4403 
4404         pl_lock ();
4405         {
4406             // COMM
4407             junk_id3v2_remove_frames (&id3v2, "COMM");
4408             const char *val = pl_find_meta (it, "comment");
4409             if (val && *val) {
4410                 junk_id3v2_add_comment_frame (&id3v2, "eng", "", val);
4411             }
4412             // UFID
4413             junk_id3v2_remove_ufid_frames (&id3v2, "UFID", "http://musicbrainz.org");
4414             val = pl_find_meta (it, "musicbrainz_trackid");
4415             if (val && *val) {
4416                 junk_id3v2_add_ufid_frame (&id3v2, "http://musicbrainz.org", val, strlen (val));
4417             }
4418         }
4419         pl_unlock ();
4420 
4421         // remove all known normal frames (they will be refilled from track metadata)
4422         int idx = id3v2.version[0] == 3 ? MAP_ID3V23 : MAP_ID3V24;
4423         for (int i = 0; frame_mapping[i]; i += FRAME_MAPPINGS) {
4424             if (frame_mapping[i+idx]) {
4425                 junk_id3v2_remove_frames (&id3v2, frame_mapping[i+idx]);
4426                 trace ("removed frame %s\n", frame_mapping[i+idx]);
4427             }
4428         }
4429 
4430         DB_metaInfo_t *meta = pl_get_metadata_head (it);
4431         while (meta) {
4432             if (meta->value && *meta->value) {
4433                 int i;
4434                 for (i = 0; frame_mapping[i]; i += FRAME_MAPPINGS) {
4435                     if (!strcasecmp (meta->key, frame_mapping[i+MAP_DDB])) {
4436                         const char *frm_name = id3v2_version == 3 ? frame_mapping[i+MAP_ID3V23] : frame_mapping[i+MAP_ID3V24];
4437                         if (frm_name) {
4438                             // field is known and supported for this tag version
4439                             trace ("add_frame %s %s\n", frm_name, meta->value);
4440                             junk_id3v2_add_text_frame (&id3v2, frm_name, meta->value);
4441                         }
4442                         break;
4443                     }
4444                 }
4445                 if (!frame_mapping[i]
4446                         && meta->key[0] != ':'
4447                         && strcasecmp (meta->key, "comment")
4448                         && strcasecmp (meta->key, "track")
4449                         && strcasecmp (meta->key, "numtracks")
4450                         && strcasecmp (meta->key, "disc")
4451                         && strcasecmp (meta->key, "numdiscs")
4452                    ) {
4453                     // add as txxx
4454                     trace ("adding unknown frame as TXX %s=%s\n", meta->key, meta->value);
4455                     junk_id3v2_remove_txxx_frame (&id3v2, meta->key);
4456                     junk_id3v2_add_txxx_frame (&id3v2, meta->key, meta->value);
4457                 }
4458             }
4459             meta = meta->next;
4460         }
4461 
4462         pl_lock ();
4463         {
4464             // add tracknumber/totaltracks
4465             const char *track = pl_find_meta (it, "track");
4466             const char *totaltracks = pl_find_meta (it, "numtracks");
4467             if (track && totaltracks) {
4468                 char s[100];
4469                 snprintf (s, sizeof (s), "%s/%s", track, totaltracks);
4470                 junk_id3v2_remove_frames (&id3v2, "TRCK");
4471                 junk_id3v2_add_text_frame (&id3v2, "TRCK", s);
4472             }
4473             else if (track) {
4474                 junk_id3v2_remove_frames (&id3v2, "TRCK");
4475                 junk_id3v2_add_text_frame (&id3v2, "TRCK", track);
4476             }
4477             // add discnumber/totaldiscs
4478             const char *disc = pl_find_meta (it, "disc");
4479             const char *totaldiscs = pl_find_meta (it, "numdiscs");
4480             if (disc && totaldiscs) {
4481                 char s[100];
4482                 snprintf (s, sizeof (s), "%s/%s", disc, totaldiscs);
4483                 junk_id3v2_remove_frames (&id3v2, "TPOS");
4484                 junk_id3v2_add_text_frame (&id3v2, "TPOS", s);
4485             }
4486             else if (disc) {
4487                 junk_id3v2_remove_frames (&id3v2, "TPOS");
4488                 junk_id3v2_add_text_frame (&id3v2, "TPOS", disc);
4489             }
4490         }
4491         pl_unlock ();
4492 
4493         // remove and re-add replaygain id3v2 frames
4494         for (int n = 0; ddb_internal_rg_keys[n]; n++) {
4495             junk_id3v2_remove_txxx_frame (&id3v2, tag_rg_names[n]);
4496             if (pl_find_meta (it, ddb_internal_rg_keys[n])) {
4497                 float value = pl_get_item_replaygain (it, n);
4498                 char s[100];
4499                 snprintf (s, sizeof (s), "%f", value);
4500                 junk_id3v2_add_txxx_frame (&id3v2, tag_rg_names[n], s);
4501             }
4502         }
4503 
4504         // write tag
4505         if (junk_id3v2_write2 (out, &id3v2) != 0) {
4506             trace ("cmp3_write_metadata: failed to write id3v2 tag to %s\n", pl_find_meta (it, ":URI"))
4507             goto error;
4508         }
4509     }
4510 
4511     // now write audio data
4512     buffer = malloc (8192);
4513     deadbeef->fseek (fp, header, SEEK_SET);
4514     int64_t writesize = fsize;
4515     if (footer > 0) {
4516         writesize -= (fsize - footer);
4517     }
4518     writesize -= header;
4519     trace ("writesize: %d, id3v1_start: %d(%d), apev2_start: %d, footer: %d\n", writesize, id3v1_start, fsize-id3v1_start, apev2_start, footer);
4520 
4521     while (writesize > 0) {
4522         int rb = min (8192, writesize);
4523         rb = deadbeef->fread (buffer, 1, rb, fp);
4524         if (rb < 0) {
4525             fprintf (stderr, "junk_write_id3v2: error reading input data\n");
4526             goto error;
4527         }
4528         if (write (out, buffer, rb) != rb) {
4529             fprintf (stderr, "junk_write_id3v2: error writing output file\n");
4530             goto error;
4531         }
4532         if (rb == 0) {
4533             break; // eof
4534         }
4535         writesize -= rb;
4536     }
4537 
4538     if (!write_apev2 && !strip_apev2 && apev2_start != 0) {
4539         trace ("copying original apev2 tag\n");
4540         if (deadbeef->fseek (fp, apev2_start, SEEK_SET) == -1) {
4541             trace ("cmp3_write_metadata: failed to seek to original apev2 tag position in %s\n", pl_find_meta (it, ":URI"));
4542             goto error;
4543         }
4544         uint8_t *buf = malloc (apev2_size);
4545         if (!buf) {
4546             trace ("cmp3_write_metadata: failed to alloc %d bytes for apev2 tag\n", apev2_size);
4547             goto error;
4548         }
4549         if (deadbeef->fread (buf, 1, apev2_size, fp) != apev2_size) {
4550             trace ("cmp3_write_metadata: failed to read original apev2 tag from %s\n", pl_find_meta (it, ":URI"));
4551             free (buf);
4552             goto error;
4553         }
4554         if (write (out, buf, apev2_size) != apev2_size) {
4555             trace ("cmp3_write_metadata: failed to copy original apev2 tag from %s to temp file\n", pl_find_meta (it, ":URI"));
4556             free (buf);
4557             goto error;
4558         }
4559         free (buf);
4560     }
4561     else if (write_apev2) {
4562         trace ("writing new apev2 tag (strip=%d)\n", strip_apev2);
4563         if (strip_apev2 || junk_apev2_read_full (NULL, &apev2, fp) != 0) {
4564             deadbeef->junk_apev2_free (&apev2);
4565             memset (&apev2, 0, sizeof (apev2));
4566         }
4567 
4568         // remove all text frames
4569         junk_apev2_remove_all_text_frames (&apev2);
4570 
4571         // add all basic frames
4572         DB_metaInfo_t *meta = pl_get_metadata_head (it);
4573         while (meta) {
4574             if (meta->value && *meta->value) {
4575                 int i;
4576                 for (i = 0; frame_mapping[i]; i += FRAME_MAPPINGS) {
4577                     if (!strcasecmp (meta->key, frame_mapping[i+MAP_DDB]) && frame_mapping[i+MAP_APEV2]) {
4578                         trace ("apev2 writing known field: %s=%s\n", meta->key, meta->value);
4579                         junk_apev2_add_text_frame (&apev2, frame_mapping[i+MAP_APEV2], meta->value);
4580                         break;
4581                     }
4582                 }
4583                 if (!frame_mapping[i]
4584                         && meta->key[0] != ':'
4585                         && strcasecmp (meta->key, "track")
4586                         && strcasecmp (meta->key, "numtracks")
4587                         && strcasecmp (meta->key, "disc")
4588                         && strcasecmp (meta->key, "numdiscs")
4589                    ) {
4590                     trace ("apev2 writing unknown field: %s=%s\n", meta->key, meta->value);
4591                     junk_apev2_add_text_frame (&apev2, meta->key, meta->value);
4592                 }
4593             }
4594             meta = meta->next;
4595         }
4596 
4597         {
4598             pl_lock ();
4599             // add tracknumber/totaltracks
4600             const char *track = pl_find_meta (it, "track");
4601             const char *totaltracks = pl_find_meta (it, "numtracks");
4602             if (track && totaltracks) {
4603                 char s[100];
4604                 snprintf (s, sizeof (s), "%s/%s", track, totaltracks);
4605                 junk_apev2_remove_frames (&apev2, "Track");
4606                 junk_apev2_add_text_frame (&apev2, "Track", s);
4607             }
4608             else if (track) {
4609                 junk_apev2_remove_frames (&apev2, "Track");
4610                 junk_apev2_add_text_frame (&apev2, "Track", track);
4611             }
4612             // add discnumber/totaldiscs
4613             const char *disc = pl_find_meta (it, "disc");
4614             const char *totaldiscs = pl_find_meta (it, "numdiscs");
4615             if (disc && totaldiscs) {
4616                 char s[100];
4617                 snprintf (s, sizeof (s), "%s/%s", disc, totaldiscs);
4618                 junk_apev2_remove_frames (&apev2, "disc");
4619                 junk_apev2_add_text_frame (&apev2, "disc", s);
4620             }
4621             else if (disc) {
4622                 junk_apev2_remove_frames (&apev2, "disc");
4623                 junk_apev2_add_text_frame (&apev2, "disc", disc);
4624             }
4625             pl_unlock ();
4626         }
4627 
4628         // remove and re-add replaygain apev2 frames
4629         for (int n = 0; ddb_internal_rg_keys[n]; n++) {
4630             junk_apev2_remove_frames (&apev2, tag_rg_names[n]);
4631             if (pl_find_meta (it, ddb_internal_rg_keys[0])) {
4632                 float value = pl_get_item_replaygain (it, n);
4633                 char s[100];
4634                 snprintf (s, sizeof (s), "%f", value);
4635                 junk_apev2_add_text_frame (&apev2, tag_rg_names[n], s);
4636             }
4637         }
4638 
4639         // write tag
4640         if (junk_apev2_write2 (out, &apev2, 0, 1) != 0) {
4641             trace ("cmp3_write_metadata: failed to write apev2 tag to %s\n", pl_find_meta (it, ":URI"))
4642             goto error;
4643         }
4644     }
4645 
4646     if (!write_id3v1 && !strip_id3v1 && id3v1_start != 0) {
4647         trace ("copying original id3v1 tag %d %d %d\n", write_id3v1, strip_id3v1, id3v1_start);
4648         if (deadbeef->fseek (fp, id3v1_start, SEEK_SET) == -1) {
4649             trace ("cmp3_write_metadata: failed to seek to original id3v1 tag position in %s\n", pl_find_meta (it, ":URI"));
4650             goto error;
4651         }
4652         char buf[128];
4653         if (deadbeef->fread (buf, 1, 128, fp) != 128) {
4654             trace ("cmp3_write_metadata: failed to read original id3v1 tag from %s\n", pl_find_meta (it, ":URI"));
4655             goto error;
4656         }
4657         if (write (out, buf, 128) != 128) {
4658             trace ("cmp3_write_metadata: failed to copy id3v1 tag from %s to temp file\n", pl_find_meta (it, ":URI"));
4659             goto error;
4660         }
4661     }
4662     else if (write_id3v1) {
4663         trace ("writing new id3v1 tag\n");
4664         if (junk_id3v1_write2 (out, it, id3v1_encoding) != 0) {
4665             trace ("cmp3_write_metadata: failed to write id3v1 tag to %s\n", pl_find_meta (it, ":URI"))
4666             goto error;
4667         }
4668     }
4669 
4670     if (strip_id3v1 && !write_id3v1) {
4671         item_flags &= ~DDB_TAG_ID3V1;
4672     }
4673     if (strip_id3v2 && !write_id3v2) {
4674         item_flags &= ~(DDB_TAG_ID3V22|DDB_TAG_ID3V23|DDB_TAG_ID3V24);
4675     }
4676     if (strip_apev2 && !write_apev2) {
4677         item_flags &= ~DDB_TAG_APEV2;
4678     }
4679 
4680     if (write_id3v1) {
4681         item_flags |= DDB_TAG_ID3V1;
4682     }
4683     if (write_id3v2) {
4684         item_flags &= ~(DDB_TAG_ID3V22|DDB_TAG_ID3V23|DDB_TAG_ID3V24);
4685         item_flags |= id3v2_version == 3 ? DDB_TAG_ID3V23 : DDB_TAG_ID3V24;
4686     }
4687     if (write_apev2) {
4688         item_flags |= DDB_TAG_APEV2;
4689     }
4690 
4691     pl_set_item_flags (it, item_flags);
4692     err = 0;
4693 error:
4694     if (fp) {
4695         deadbeef->fclose (fp);
4696     }
4697     if (out) {
4698         close (out);
4699         out = -1;
4700     }
4701     if (buffer) {
4702         free (buffer);
4703     }
4704     if (!err) {
4705         pl_lock ();
4706         rename (tmppath, fname);
4707         pl_unlock ();
4708     }
4709     else {
4710         unlink (tmppath);
4711     }
4712     return err;
4713 }
4714 
4715 void
junk_enable_cp1251_detection(int enable)4716 junk_enable_cp1251_detection (int enable) {
4717     enable_cp1251_detection = enable;
4718 }
4719 
4720 void
junk_enable_cp936_detection(int enable)4721 junk_enable_cp936_detection (int enable) {
4722     enable_cp936_detection = enable;
4723 }
4724 
4725 void
junk_enable_shift_jis_detection(int enable)4726 junk_enable_shift_jis_detection (int enable) {
4727     enable_shift_jis_detection = enable;
4728 }
4729 
4730 void
junk_configchanged(void)4731 junk_configchanged (void) {
4732     int cp1251 = conf_get_int ("junk.enable_cp1251_detection", 1);
4733     int cp936 = conf_get_int ("junk.enable_cp936_detection", 0);
4734     int shift_jis = conf_get_int ("junk.enable_shift_jis_detection", 0);
4735     junk_enable_cp1251_detection (cp1251);
4736     junk_enable_cp936_detection (cp936);
4737     junk_enable_shift_jis_detection (shift_jis);
4738 }
4739