1 /* EasyTAG - tag editor for audio files
2  * Copyright (C) 2014 David King <amigadave@amigadave.com>
3  * Copyright (C) 2002 Artur Polaczynski (Ar't) <artii@o2.pl>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <limits.h>
27 #include <assert.h>
28 #include <math.h>
29 #ifndef __BORLANDC__
30 #    include <unistd.h>
31 #endif
32 #include <glib/gstdio.h>
33 #include "apetaglib.h"
34 #include "../id3_tag.h"
35 #include "../genres.h"
36 #include "../win32/win32dep.h"
37 
38 #include "is_tag.h"
39 #ifdef ID3V2_READ
40 #    include "id3v2_read.h"
41 #endif
42 
43 /* LOCAL STRUCTURES */
44 
45 /**
46     \struct _apetag_footer
47     \brief structure of APETAGEXT footer or/and header tag
48 */
49 struct _apetag_footer
50 {
51     unsigned char id[8];        /**< magic should equal 'APETAGEX' */
52     unsigned char version[4];   /**< version 1000 (v1.0) or 2000 (v 2.0) */
53     unsigned char length[4];    /**< the complete size of the tag, including footer, but no header for v2.0 */
54     unsigned char tagCount[4];  /**< the number of fields in the tag */
55     unsigned char flags[4];     /**< the tag flags (none currently defined for v 1.0) */
56     unsigned char reserved[8];  /**< reserved for later use */
57 };
58 
59 /**
60     \struct _ape_mem_cnt
61     \brief internal structure for apetag
62 */
63 struct _ape_mem_cnt
64 {
65     struct tag **tag;
66     int countTag;
67     int memTagAlloc;    // for mem container;
68     char *filename;     // for info
69     struct _apetag_footer ape_header;
70     struct _apetag_footer ape_footer;
71     int currentPosition;
72 };
73 
74 /* *
75     \struct _id3v1Tag
76     \brief for id3v1 tag
77 */
78 struct _id3v1Tag
79 {
80     char magic[3];    // `TAG`
81     char title[30];
82     char artist[30];
83     char album[30];
84     char year[4];
85     char comment[30]; // if ([28]==0 and [29]!=0) track = [29]
86     unsigned char genre;
87 };
88 
89 /* LOCAL FUNCTION prototypes */
90 unsigned long
91 ape2long (unsigned char *p);
92 void
93 long2ape (unsigned char *p, const unsigned long value);
94 struct tag *
95 libapetag_maloc_cont_int (apetag *mem_cnt, struct tag *mTag);
96 int
97 libapetag_maloc_cont_text (apetag *mem_cnt, unsigned long flags,
98              long sizeName, const char *name, long sizeValue, char *value);
99 int
100 libapetag_maloc_cont (apetag *mem_cnt, unsigned long flags,
101         long sizeName, const char *name, long sizeValue, const char *value);
102 static int
103 libapetag_qsort (struct tag **a, struct tag **b);
104 int
105 make_id3v1_tag(apetag *mem_cnt, struct _id3v1Tag *m);
106 
107 
108 unsigned long
ape2long(unsigned char * p)109 ape2long (unsigned char *p)
110 {
111     return (((unsigned long) p[0] << 0)  |
112             ((unsigned long) p[1] << 8)  |
113             ((unsigned long) p[2] << 16) |
114             ((unsigned long) p[3] << 24) );
115 }
116 
117 void
long2ape(unsigned char * p,const unsigned long value)118 long2ape (unsigned char *p, const unsigned long value)
119 {
120     p[0] = (unsigned char) (value >> 0);
121     p[1] = (unsigned char) (value >> 8);
122     p[2] = (unsigned char) (value >> 16);
123     p[3] = (unsigned char) (value >> 24);
124 }
125 
126 
127 /*
128     PL: funkcja troszczaca sie o odpowiednią ilosc zalokowanej pamieci dla tablicy
129     PL: %mTag% przy okazji alokuje z wyprzedzeniem troche wiecej pamieci [mniej %realoc%]
130     PL: zwraca %mTag[]%
131     :NON_USER:!!!
132  */
133 #define LIBAPETAG_MEM_ALLOC_AHEAD 16    /* 15 it's good for normal #of tag, Aver 4-8 */
134 struct tag *
libapetag_maloc_cont_int(apetag * mem_cnt,struct tag * mTag)135 libapetag_maloc_cont_int (apetag *mem_cnt, struct tag *mTag)
136 {
137     struct tag **tag_tmp = mem_cnt->tag;
138 
139     if (mem_cnt->memTagAlloc == 0) {    /* init */
140         mem_cnt->tag = (struct tag **) malloc ((sizeof (struct tag *) * LIBAPETAG_MEM_ALLOC_AHEAD));
141         mem_cnt->memTagAlloc = LIBAPETAG_MEM_ALLOC_AHEAD;
142         mem_cnt->countTag = 0;
143         if (mem_cnt->tag == NULL) {
144             mem_cnt->memTagAlloc = mem_cnt->countTag = 0;
145             PRINT_ERR ( "ERROR->libapetag->libapetag_maloc_cont_int:malloc\n");
146             return NULL;
147         }
148     }
149 
150     if ((mem_cnt->memTagAlloc) <= (mem_cnt->countTag + 1)) {
151         mem_cnt->tag = (struct tag **) realloc (mem_cnt->tag, (sizeof (struct tag *) *
152                         (mem_cnt->memTagAlloc + LIBAPETAG_MEM_ALLOC_AHEAD)));
153         mem_cnt->memTagAlloc += LIBAPETAG_MEM_ALLOC_AHEAD;
154     }
155 
156     if (mem_cnt->tag == NULL) {
157         int n;
158 
159         PRINT_ERR ( "ERROR->libapetag->libapetag_maloc_cont_int:malloc\n");
160         /* free old all */
161         for (n = mem_cnt->countTag-1; n >= 0; n--) {
162             free (tag_tmp[n]->value);
163             free (tag_tmp[n]->name);
164             free (tag_tmp[n]);
165         }
166         free (tag_tmp);
167         mem_cnt->memTagAlloc = mem_cnt->countTag = 0;
168         return NULL;
169     }
170 
171     mem_cnt->tag[mem_cnt->countTag] = mTag;
172     mem_cnt->countTag++;
173     return mTag;
174 
175 }
176 #undef LIBAPETAG_MEM_ALLOC_AHEAD
177 
178 
179 /*
180     PL: alocuje pamiec dla %mTag% przypisuje odpowiednio wartosci
181     PL: dodaje %\0% do stringów [na wszelki wypadek]
182     PL: nie dopisuje takich samych
183     PL: wszystkie sizy maja byc bez \0 (jak bedzie to doliczy jeszcze jeden)
184     :NON_USER:!!!
185  */
186 int
libapetag_maloc_cont(apetag * mem_cnt,unsigned long flags,long sizeName,const char * name,long sizeValue,const char * value)187 libapetag_maloc_cont (apetag *mem_cnt, unsigned long flags,
188         long sizeName, const char *name, long sizeValue, const char *value)
189 {
190     struct tag *mTag;
191         // TODO:: zadbac o to zeby tu czyscilo istniejace tagi jesli value=NULL
192     if (!sizeName || !sizeValue)
193             return ATL_BADARG;
194 
195     if (apefrm_getstr (mem_cnt, name) == NULL) {
196         mTag = (struct tag *) malloc (sizeof (struct tag));
197 
198         if (mTag == NULL)
199                 return ATL_MALOC;
200 
201         mTag->value = (char *) malloc (sizeValue + 1);
202         if (mTag->value==NULL) {
203             free (mTag);
204             return ATL_MALOC;
205         }
206 
207         mTag->name = (char *) malloc (sizeName + 1);
208         if (mTag->name==NULL) {
209             free (mTag->value);
210             free (mTag);
211             return ATL_MALOC;
212         }
213 
214         memcpy (mTag->value, value, sizeValue);
215         memcpy (mTag->name, name, sizeName);
216         mTag->value[sizeValue] = '\0';
217         mTag->name[sizeName] = '\0';
218         mTag->sizeName = sizeName;
219         mTag->sizeValue = sizeValue;
220         mTag->flags = flags;
221 
222         if (libapetag_maloc_cont_int (mem_cnt, mTag)==NULL) {
223             PRINT_ERR(">apetaglib>libapetag_maloc_cont>> int==NULL");
224             return ATL_MALOC;
225         }
226     }
227 
228     return 0;
229 }
230 
231 /*
232     PL: jezeli nie istnieje to dodaje taga, pomija ostatnie biale znaki
233     PL: pomija jesli pusty
234     PL: ! zmienia tekst wejściowy
235     :NON_USER:!!!
236 */
237 int
libapetag_maloc_cont_text(apetag * mem_cnt,unsigned long flags,long sizeName,const char * name,long sizeValue,char * value)238 libapetag_maloc_cont_text (apetag *mem_cnt, unsigned long flags,
239                long sizeName, const char *name, long sizeValue,
240                char *value)
241 {
242     int n = sizeValue;
243 
244     if (value != NULL && value[0] != '\0' && apefrm_getstr (mem_cnt, name) == NULL) {
245         while (value[--n] == ' ' || value[n] == '\0' || value[n] == '\n') {
246             value[n] = '\0';
247         }
248         return libapetag_maloc_cont (mem_cnt, flags, sizeName, name, n + 1, value);
249     }
250 
251     return 0;
252 }
253 
254 
255 /*
256     PL: dodaje taga do istniejeacych o ustawionych wartosciach %flag% %name% i %value%
257     PL: wylicza odpowiednio rozmiary przy pomocy strlen!!
258     PL: wraper na %libapetag_maloc_cont%
259     PL: wszystko kopiuje sobie do pamieci
260     PL: musi byc juz w UTF-8 dla v2
261     PL: Nadpisuje istniejace
262  */
263 /**
264     \brief Add text frame
265 
266     add text frame/field to object apetag (if exist then overwrite)
267 
268     \param mem_cnt     object #apetag
269     \param flags     flags stored in frame
270     \param name     name of frame
271     \param value     value of frame
272     \return 0 - OK else check #atl_return
273 */
274 int
apefrm_add(apetag * mem_cnt,unsigned long flags,const char * name,const char * value)275 apefrm_add (apetag *mem_cnt, unsigned long flags, const char *name,
276      const char *value)
277 {
278     apefrm_remove_real (mem_cnt, name);
279     return libapetag_maloc_cont (mem_cnt, flags, strlen (name), name, strlen (value), value);
280 }
281 
282 /*
283     PL: Prosty wraperek na maloc_cont - do zapisu binarnych
284 */
285 /**
286     \brief add binary frame
287 
288     add binary frame/field to object apetag (if exist then overwrite)
289 
290     \param mem_cnt    object #apetag
291     \param flags     flags stored in frame
292     \param sizeName     size of name
293     \param name     name of frame
294     \param sizeValue     size of value
295     \param value    value of frame
296     \return 0 - OK else check #atl_return
297 */
298 int
apefrm_add_bin(apetag * mem_cnt,unsigned long flags,long sizeName,char * name,long sizeValue,char * value)299 apefrm_add_bin (apetag *mem_cnt, unsigned long flags,
300                 long sizeName, char *name,
301                 long sizeValue, char *value)
302 {
303     apefrm_remove_real (mem_cnt, name);
304     return libapetag_maloc_cont (mem_cnt, flags, sizeName, name, sizeValue, value);
305 }
306 
307 /*
308     PL: jak %apefrm_add ()%  z tym ze nie nadpisuje istniejacych
309 */
310 /**
311     \brief add frame if other (the same name) no exist
312 
313     if exist "name" in ape_mem then do nothing else add frame/field to ape_mem
314 
315     \param mem_cnt    object #apetag
316     \param flags     flags stored in frame
317     \param name     name of frame
318     \param value    value of frame
319     \return 0 - OK else check #atl_return
320 */
321 int
apefrm_add_noreplace(apetag * mem_cnt,unsigned long flags,char * name,char * value)322 apefrm_add_noreplace (apetag *mem_cnt, unsigned long flags,
323            char *name, char *value)
324 {
325     if ( apefrm_getstr (mem_cnt, name) == NULL )
326         return apefrm_add (mem_cnt, flags, name, value);
327 
328     return 0;
329 }
330 
331 /*
332     PL: wyszukuje taga o nazwie %name% i zwraca structure %struct tag%
333     PL: %APE_TAG_LIB_FIRST% i %APE_TAG_LIB_NEXT% to ulatwienie dla
334     PL: przesukiwania wszystkich istniejacych tagów
335     PL: %APE_TAG_LIB_FIRST% ustawia znacznik na pierwszy tag [0] i zwraca jego wartość
336     PL: %APE_TAG_LIB_NEXT% podaje nastepny tag i zwieksza znacznik, po ostatnim funkcja zwraca %NULL%
337     PL: UWAGA!!! zwraca pointer do wewnetrznej struktury
338     PL: niczego nie zmieniac i nie free()-jowac skopiowac i dopiero
339     PL: zwraca teksty w UTF-8
340  */
341 /**
342     \brief search in apetag for name and return tag
343 
344     2 special names \a APE_TAG_LIB_FIRST and \a APE_TAG_LIB_NEXT.
345     FIRST return first frame and set counter to 1
346     NEXT return ++counter frame
347 \code
348 for ((framka = apefrm_get(ape, APE_TAG_LIB_FIRST)); framka!=NULL;) {
349     do_something();
350     framka = apefrm_get(ape, APE_TAG_LIB_NEXT);
351 }
352 \endcode
353     return NULL if no more frame exist
354 
355     \param mem_cnt     object #apetag
356     \param name     frame name for search
357     \return pointer to struct tag if name exist or NULL if don't
358     \warning don't change anything in this struct make copy and work
359 */
360 struct tag *
apefrm_get(apetag * mem_cnt,const char * name)361 apefrm_get (apetag *mem_cnt, const char *name)
362 {
363     int n;
364     struct tag **mTag;
365 
366     mTag = (mem_cnt->tag);
367 
368     if (mem_cnt->countTag == 0)
369         return NULL;
370 
371     if (strcmp (name, APE_TAG_LIB_FIRST) == 0) {
372         mem_cnt->currentPosition = 0;
373         return (mTag[mem_cnt->currentPosition++]);
374     }
375 
376     if (strcmp (name, APE_TAG_LIB_NEXT) == 0) {
377         if (mem_cnt->currentPosition >= mem_cnt->countTag)
378             return NULL;
379         return (mTag[mem_cnt->currentPosition++]);
380     }
381 
382     for (n = 0; (mem_cnt->countTag) > n; n++) {
383         if (strcasecmp (mTag[n]->name, name) == 0) {
384             return (mTag[n]);
385         }
386     }
387 
388     return NULL;
389 }
390 
391 /*
392     PL:zwraca %mem_cnt->tag[x]->value% o ile znajdzie nazwe %name% taga
393     PL: prosty wraper na %apefrm_get %
394     PL: UWAGA zwraca pointer z wewnetrznych struktur niczego bezposrednio nie zmieniac
395     PL: i nie free()-jowac bo sie rozsypie
396     PL: zwraca tekst w UTF-8
397  */
398 /**
399     \brief search in apetag for name and return string
400 
401     \param mem_cnt     object #apetag
402     \param name     frame name for search
403     \return pointer to value of frame if name exist or NULL if don't
404     \warning don't change that string make copy before any action
405     \todo check if frame type isn't binary
406 */
407 char *
apefrm_getstr(apetag * mem_cnt,const char * name)408 apefrm_getstr (apetag *mem_cnt, const char *name)
409 {
410     struct tag *mTag;
411 
412     mTag = apefrm_get (mem_cnt, name);
413 
414     if (mTag == NULL)
415         return NULL;
416 
417     return (mTag->value);
418 }
419 
420 /*
421     PL: usuwanie taga o nazwie zdefiniowanej w %name%
422     PL:lub wszystkich jezeli %name%=%APE_TAG_LIB_DEL_ALL%
423     PL:UWAGA mozna to napisac inaczej (sprawdzanie czy %name% OR %%special%) ale to w v1.0
424  */
425 /**
426     \brief remove frame from memory
427 
428     (real) remove frame from ape_mem.
429     Check #apefrm_remove for more info
430 
431     \param mem_cnt     object #apetag
432     \param name     frame name for search and remove
433 */
434 void
apefrm_remove_real(apetag * mem_cnt,const char * name)435 apefrm_remove_real (apetag *mem_cnt, const char *name)
436 {
437     int n;
438     struct tag **mTag;
439 
440     mTag = (mem_cnt->tag);
441 
442     /* Delete all */
443     if (strcmp (name, APE_TAG_LIB_DEL_ALL) == 0) {
444         for (n = mem_cnt->countTag-1; n >= 0; n--) {
445             free (mTag[n]->name);
446             free (mTag[n]->value);
447             free (mTag[n]);
448             --mem_cnt->countTag;
449         }
450         return;
451     }
452     /* Delete only one */
453     for (n = mem_cnt->countTag-1; n >= 0; n--) {
454         if (strcasecmp (mTag[n]->name, name) == 0) {
455             free (mTag[n]->name);
456             free (mTag[n]->value);
457             free (mTag[n]);
458             mTag[n] = mTag[mem_cnt->countTag];
459             --mem_cnt->countTag;
460             /* !no return; search for all */
461         }
462     }
463 
464     return;
465 }
466 /*
467     PL: tak jakby frejuje framke oznacza do kasacji jednak tego nie robi
468     PL: mechanizm ten głownie jest wykorzystywany do wczytania innych tagów
469     PL: poza wczesniej zkasowanymi aby to usunąc uzyj apefrm_remove_real
470 */
471 /**
472     \brief set frame to remove
473 
474     Create fake name and empty value (and set don't save flag).
475     If you use apefrm_add_norepleace then you don't change
476     this not_save_flag.
477     Only apefrm_add overwrite this.
478     [it's for id3v1 but you may using this for remove frames]
479 
480     \param mem_cnt     object #apetag
481     \param name     frame name for search and remove
482 */
483 void
apefrm_remove(apetag * mem_cnt,const char * name)484 apefrm_remove (apetag *mem_cnt, const char *name)
485 {
486     int n;
487     struct tag **mTag;
488 
489     apefrm_add (mem_cnt, 0 , name, "delete me");
490 
491     mTag = (mem_cnt->tag);
492 
493     for (n = 0; (mem_cnt->countTag) > n; n++) {
494         if (strcasecmp (mTag[n]->name, name) == 0) {
495             mTag[n]->sizeValue=0;
496             return;
497         }
498     }
499 
500     return;
501 }
502 
503 /*
504     PL:Wypisuje na ekran wszystko to co potrzebne do debugu
505     :NON_USER:!!!
506 */
507 /**
508     debug function print all tags exclude bin (print only size for bin)
509 */
510 void
libapetag_print_mem_cnt(apetag * mem_cnt)511 libapetag_print_mem_cnt (apetag *mem_cnt)
512 {
513     int n;
514     struct tag **mTag;
515 
516     mTag = (mem_cnt->tag);
517     for (n = 0; (mem_cnt->countTag) > n; n++) {
518         if ( (mTag[n]->flags & ~ITEM_TEXT) == 0 ||
519             (mTag[n]->flags & ~ITEM_LINK) == 0 ) {
520         printf (">apetaglib>PRINT>>F=%lu SN=%li SV=%li N[%s] V[%s]\n",
521             mTag[n]->flags,
522             (long) mTag[n]->sizeName, (long) mTag[n]->sizeValue,
523             mTag[n]->name, mTag[n]->value);
524         } else {
525         printf (">apetaglib>PRINT>>F=%lu SN=%li SV=%li N[%s] V=BINARY\n",
526             mTag[n]->flags,
527             (long) mTag[n]->sizeName, (long) mTag[n]->sizeValue,
528             mTag[n]->name);
529         }
530     }
531 
532     return;
533 }
534 
535 /*
536     PL: alokuje pamiec dla glównej struktury %struct ape_mem_cnt%
537     PL: i zeruje wszystko co trzeba
538     PL: z jakiegos powodu (mojej niewiedzy) memset nie dziala
539     PL: a w sumie dziala czyszczac troche za duzo
540 */
541 /**
542     \brief initialise new object #apetag and return
543     \return new initialised object #apetag
544 */
545 apetag *
apetag_init(void)546 apetag_init (void)
547 {
548     apetag * mem_cnt;
549 
550     mem_cnt = (apetag *) malloc (sizeof (apetag));
551     if (mem_cnt == NULL) {
552         PRINT_ERR ("ERROR->libapetag->apetag_init:malloc\n");
553         return NULL;
554     }
555     mem_cnt->memTagAlloc = 0;
556     mem_cnt->countTag = 0;
557     mem_cnt->filename = NULL;
558     mem_cnt->currentPosition = 0;
559     mem_cnt->tag = NULL;
560 
561     return mem_cnt;
562 }
563 
564 /*
565     PL: Czysci z sila wodospadu wszystko co zostalo do czyszczenia
566     PL: z %struct ape_mem_cnt% wlacznie, wcześniej to nie było jasne
567 */
568 /**
569     \brief free all work
570     \param mem_cnt object #apetag
571 */
572 void
apetag_free(apetag * mem_cnt)573 apetag_free (apetag *mem_cnt)
574 {
575     int n;
576 
577     for (n = mem_cnt->countTag-1; n >= 0; n--)
578     {
579         free (mem_cnt->tag[n]->value);
580         free (mem_cnt->tag[n]->name);
581         free (mem_cnt->tag[n]);
582     }
583     free (mem_cnt->tag);
584     free (mem_cnt);
585     mem_cnt = NULL;
586 
587     return;
588 
589 }
590 
591 
592 /**
593     \brief read id3v1 and add frames
594 
595     read id3v1 and add frames to ape_mem.
596     Using #apefrm_add_norepleace
597 
598     \param mem_cnt     object #apetag
599     \param fp         file pointer
600     \return 0 - OK else check #atl_return
601 */
602 int
readtag_id3v1_fp(apetag * mem_cnt,FILE * fp)603 readtag_id3v1_fp (apetag *mem_cnt, FILE * fp)
604 {
605     struct _id3v1Tag m;
606 
607     if (!is_id3v1(fp))
608             return 0;  /* TODO:: 0 or no_id3v1*/
609 
610     fseek (fp, -ID3V1_TAG_SIZE, SEEK_END);
611     if (sizeof (struct _id3v1Tag)!=fread(&m, 1, sizeof (struct _id3v1Tag), fp)){
612         PRINT_ERR( "ERROR->libapetag->readtag_id3v1_fp:fread\n");
613         return ATL_FREAD;
614     }
615 
616     libapetag_maloc_cont_text(mem_cnt, 0, 5, "Title", 30, m.title);
617     libapetag_maloc_cont_text(mem_cnt, 0, 6, "Artist", 30, m.artist);
618     libapetag_maloc_cont_text(mem_cnt, 0, 5, "Album", 30, m.album);
619     libapetag_maloc_cont_text(mem_cnt, 0, 4, "Year", 4, m.year);
620     if (m.comment[28] == 0 && m.comment[29] != 0) {
621         char track[20];
622         snprintf(track, 19, "%i", m.comment[29]);
623         libapetag_maloc_cont_text(mem_cnt, 0, 5, "Track", strlen(track), track);
624         libapetag_maloc_cont_text(mem_cnt, 0, 7, "Comment", 28, m.comment);
625     } else {
626         libapetag_maloc_cont_text(mem_cnt, 0, 7, "Comment", 30, m.comment);
627     }
628     /* Special-case the genre. As the text never has a trailing space, which
629      * libapetag_maloc_cont_text() strips, simply call libapetag_maloc_cont()
630      * directly. */
631     libapetag_maloc_cont(mem_cnt, 0, 5, "Genre",
632                 strlen(genre_no(m.genre)), genre_no(m.genre));
633 
634     return 0;
635 }
636 
637 /*
638     PL: wczytuje odpowiednie fra(mk)gi do pamieci w razie koniecznosci przyciecia
639     PL: dodaje "..." na koniec
640     PL: TODO genre
641 
642     PL: macro COMPUTE_ID3V1_TAG
643 */
644 #define COMPUTE_ID3V1_TAG(FramkA, TagNamE, SizE, TagValuE) \
645     FramkA = apefrm_get(mem_cnt, TagNamE);    \
646     if (FramkA != NULL) {             \
647         memcpy (TagValuE, FramkA->value,    \
648             ((FramkA->sizeValue) > SizE) ? SizE : FramkA->sizeValue );    \
649         if ((FramkA->sizeValue) > SizE) {    \
650         TagValuE[SizE-1]='.'; TagValuE[SizE-2]='.'; TagValuE[SizE-3]='.';    \
651         }                    \
652     }
653 
654 int
make_id3v1_tag(apetag * mem_cnt,struct _id3v1Tag * m)655 make_id3v1_tag(apetag *mem_cnt, struct _id3v1Tag *m)
656 {
657     struct tag * framka;
658 
659     if (m == NULL)
660         return ATL_BADARG;
661 
662     memset(m, '\0', sizeof(struct _id3v1Tag));
663 
664     memcpy (m->magic,"TAG",3);
665     COMPUTE_ID3V1_TAG(framka, "Title",  30, m->title);
666     COMPUTE_ID3V1_TAG(framka, "Artist", 30, m->artist);
667     COMPUTE_ID3V1_TAG(framka, "Album",  30, m->album);
668     COMPUTE_ID3V1_TAG(framka, "Year",    4, m->year);
669 
670     if ((framka=apefrm_get(mem_cnt, "Track"))!=NULL) {
671         m->comment[29]=(unsigned char) atoi(framka->value);
672         m->comment[28]='\0';
673             COMPUTE_ID3V1_TAG(framka, "Comment", 28, m->comment);
674     } else {
675             COMPUTE_ID3V1_TAG(framka, "Comment", 30, m->comment);
676     }
677 
678     return 0;
679 }
680 
681 /*
682     PL: silnik tego liba
683     PL: %filename% jest w tej chwili tylko dla id3v2 f..k
684     PL: %ape_mem_cnt% moze byc nie zainicjalizowany ale wtedy musi byc = NULL
685 */
686 /**
687     \brief read file and add frames
688 
689     \param mem_cnt      object #apetag
690     \param filename
691     \param fp
692     \param flag
693     \return 0 - OK else check #atl_return
694 */
695 int
apetag_read_fp(apetag * mem_cnt,FILE * fp,const char * filename,int flag)696 apetag_read_fp(apetag *mem_cnt, FILE * fp, const char *filename, int flag)
697 {
698     int id3v1 = 0;
699     int apeTag2 = 0;
700     unsigned char *buff;
701     struct _apetag_footer ape_footer;
702     size_t savedFilePosition, buffLength;
703 
704     unsigned char *end;
705     unsigned long tagCount;
706     unsigned char *p;
707 
708     savedFilePosition = ftell(fp);
709 
710     id3v1 = is_id3v1(fp);
711 
712     if (mem_cnt == NULL) {
713         PRINT_ERR( ">apetaglib>READ_FP>FATAL>apetag_init()\n");
714         fseek(fp, savedFilePosition, SEEK_SET);
715         return ATL_NOINIT;
716     }
717 
718     fseek (fp, id3v1 ? -ID3V1_TAG_SIZE - sizeof (ape_footer)
719                      : -sizeof (ape_footer), SEEK_END);
720     if (sizeof (ape_footer) != fread(&ape_footer, 1, sizeof (ape_footer), fp)){
721         PRINT_ERR( "ERROR->libapetag->apetag_read_fp:fread1\n");
722         fseek(fp, savedFilePosition, SEEK_SET);
723         return ATL_FREAD;
724     }
725 
726     if (!(flag & DONT_READ_TAG_APE) &&
727         (memcmp(ape_footer.id, "APETAGEX", sizeof (ape_footer.id)) == 0))
728     {
729         PRINT_D9(">apetaglib>READ_FP>>%s: ver %li len %li # %li fl %lx v1=%i v2=%i ape=%i[v%i]\n",
730              filename, ape2long(ape_footer.version),
731              ape2long(ape_footer.length),
732              ape2long(ape_footer.tagCount),
733              ape2long(ape_footer.flags),
734              is_id3v1 (fp), is_id3v2 (fp), is_ape (fp), is_ape_ver (fp));
735 
736         apeTag2 = ape2long(ape_footer.version);
737         buffLength = is_ape(fp) + ID3V1_TAG_SIZE;
738         buff = (unsigned char *) malloc(buffLength);
739         if (buff == NULL) {
740             PRINT_ERR( "ERROR->libapetag->apetag_read_fp:malloc\n");
741             return ATL_MALOC;
742         }
743 
744         fseek (fp, id3v1 ? -ape2long (ape_footer.length) -
745                ID3V1_TAG_SIZE : -ape2long (ape_footer.length), SEEK_END);
746         memset(buff, 0, buffLength);
747         if (ape2long(ape_footer.length) != fread(buff, 1, ape2long(ape_footer.length), fp)) {
748             PRINT_ERR( "ERROR->libapetag->apetag_read_fp:fread2\n");
749             fseek(fp, savedFilePosition, SEEK_SET);
750             free(buff);
751             return ATL_FREAD;
752         }
753 
754         tagCount = ape2long(ape_footer.tagCount);
755 
756         end = buff + ape2long(ape_footer.length) - sizeof (ape_footer);
757 
758         for (p = buff; p < end && tagCount--;) {
759             /* 8 = sizeof( sizeValue+flags ) */
760             unsigned long flags = ape2long (p + 4);
761             unsigned long sizeValue = ape2long(p);
762             unsigned long sizeName;
763             char *name = (char *)p + 8;
764             char *value;
765 
766             sizeName = strlen((char *)p + 8);
767             value = (char *)p + sizeName + 8 + 1;
768             if (apeTag2 == 1000 && value[sizeValue - 1] == '\0') {
769                 libapetag_maloc_cont(mem_cnt, flags,
770                              sizeName, name,
771                              sizeValue - 1, value);
772             } else {
773                 libapetag_maloc_cont(mem_cnt, flags,
774                              sizeName, name,
775                              sizeValue, value);
776             }
777             p += (sizeName + sizeValue + 8 + 1);
778         }
779 
780         free(buff);
781     } else { /* if no ape tag */
782         PRINT_D5(">apetaglib>READ_FP>>%s: v1=%i v2=%i ape=%i[v%i]\n",
783              filename, is_id3v1 (fp), is_id3v2 (fp), is_ape (fp), is_ape_ver (fp));
784     }
785 
786 #ifdef ID3V2_READ
787     if (!(flag & DONT_READ_TAG_ID3V2) && filename!=NULL && is_id3v2(fp)!=0) {
788         readtag_id3v2(mem_cnt, filename);
789     }
790 #endif
791     if (!(flag & DONT_READ_TAG_ID3V1) && (id3v1)) {
792         readtag_id3v1_fp(mem_cnt, fp);
793     }
794 
795     fseek(fp, savedFilePosition, SEEK_SET);
796     return 0;
797 }
798 
799 /*
800     PL: wraper na apetag_read_fp
801     PL: otwiera plik wczytuje co trzeba i zamyka
802     PL: dobre do wczytywania informacji ktore sa potrzebne pozniej bez fatygi otwierania pliku
803 */
804 /**
805     \brief read file and add frames
806 
807     \param mem_cnt      object #apetag
808     \param filename    file name
809     \param flag
810     \return 0 - OK else check #atl_return
811 */
812 int
apetag_read(apetag * mem_cnt,const char * filename,int flag)813 apetag_read (apetag *mem_cnt, const char *filename, int flag)
814 {
815     FILE *fp;
816 
817     if (mem_cnt==NULL) {
818         PRINT_ERR(">apetaglib>READ>FATAL>apetag_init()\n");
819         return ATL_NOINIT;
820     }
821 
822     fp = g_fopen (filename, "rb");
823     if (fp == NULL)
824         return ATL_FOPEN;
825 
826     apetag_read_fp (mem_cnt, fp, filename,flag);
827 
828     fclose (fp);
829 
830     return 0;
831 }
832 
833 /*
834     PL: Funkcja dla qsorta ze specjalnymi wyjatkami
835     PL: uzywana w apetag_save
836     :NON_USER:!!!
837 */
838 static int
libapetag_qsort(struct tag ** a,struct tag ** b)839 libapetag_qsort (struct tag **a, struct tag **b)
840 {
841     const char * const sorting[] = { "Artist", "Year", "Album", "Track", "Title", "Genre", NULL, NULL };
842     int n, m;
843 
844     if (!a || !b || !*a || !*b) {
845         PRINT_ERR ("ERROR->libapetag->apetag_qsort:*a ||*b = NULL : FATAL PLEASE REPORT!!!\n");
846         return 0;
847     }
848     for (n = 0; sorting[n] != NULL; n++) {
849         if (strcasecmp ((*a)->name, sorting[n]) == 0)
850             break;
851     }
852     if (sorting[n] == NULL)
853         n += (*a)->sizeValue + 1;    /* n = max entries of sorting + size of tag */
854 
855     for (m = 0; sorting[m] != NULL; m++) {
856         if (strcasecmp ((*b)->name, sorting[m]) == 0)
857             break;
858     }
859     if (sorting[m] == NULL)
860         m += (*b)->sizeValue + 1;    /* m = max entries */
861 
862     if (n == m)
863         return 0;
864     if (n > m)
865         return 1;
866     else
867         return -1;
868 }
869 
870 /*
871     PL: domyslne %flag% = APE_TAG_V2 + SAVE_NEW_OLD_APE_TAG
872 */
873 /**
874     \brief save apetag to file
875 
876     \param filename file name
877     \param mem_cnt  object #apetag
878     \param flag     flags for read/save
879     \return 0 - OK else check #atl_return
880     \warning for ape tag v 1 you must add frames in iso-1
881     for v 2 this must be in utf-8
882     \todo PL: v9 sprawdzac flagi w footer i na tej podstawie zmieniac skipBytes
883     bez domniemywania ze v2 ma zawsze oba
884 
885 */
886 int
apetag_save(const char * filename,apetag * mem_cnt,int flag)887 apetag_save (const char *filename, apetag *mem_cnt, int flag)
888 {
889     FILE *fp;
890     struct _id3v1Tag id3v1_tag;
891     int id3v1;
892     int apeTag, saveApe2;
893     int tagCount = 0;
894     int realCountTag = 0;
895     struct _apetag_footer ape_footer;
896     long skipBytes;
897     unsigned char *buff, *p;
898     struct tag **mTag;
899     size_t tagSSize = 32;
900     int n;
901     unsigned char temp[4];
902 
903     if (mem_cnt==NULL) {
904         PRINT_ERR("ERROR->apetaglib>apetag_save::apetag_init()\n");
905         return ATL_NOINIT;
906     }
907 
908     fp = g_fopen (filename, "rb+");
909     if (fp == NULL) {
910         PRINT_ERR ( "ERROR->apetaglib->apetag_save::fopen (r+)\n");
911         return ATL_FOPEN;
912     }
913 
914     skipBytes = 0;
915     id3v1 = is_id3v1 (fp);
916     apeTag = is_ape (fp);
917     saveApe2 = !(flag & APE_TAG_V1); // (flag & APE_TAG_V2) ? 1 : (flag & APE_TAG_V1);
918 
919     if (id3v1) {
920         fseek (fp, -ID3V1_TAG_SIZE, SEEK_END);
921         if (fread (&id3v1_tag, 1, sizeof (struct _id3v1Tag), fp)
922             != sizeof (struct _id3v1Tag))
923         {
924             PRINT_ERR ("ERROR->apetaglib->apetag_save::fread");
925             fclose (fp);
926             return ATL_FREAD;
927         }
928         skipBytes += id3v1;
929     }
930     skipBytes += apeTag;
931 
932     if (!(flag & SAVE_NEW_APE_TAG)) {
933         apetag_read_fp (mem_cnt, fp, filename, flag);
934     }
935 
936     mTag = (mem_cnt->tag);
937     qsort( mTag , mem_cnt->countTag , sizeof(struct tag *),
938         (int (*)(const void *,const void *))libapetag_qsort);
939 
940     for (n = 0; (mem_cnt->countTag) > n; n++) {
941         if (mTag[n]->sizeValue != 0) {
942             tagSSize += ((long) mTag[n]->sizeName + (long) mTag[n]->sizeValue);
943             tagSSize += 4 + 4 + 1 + (saveApe2 ? 0 : 1);    // flag & sizeValue & \0
944             realCountTag++; // count not deleted tag (exl. not real)
945         }
946     }
947     if (!!(flag & SAVE_CREATE_ID3V1_TAG )) {
948         make_id3v1_tag(mem_cnt, &id3v1_tag);
949         tagSSize += ID3V1_TAG_SIZE;
950     }
951     //PRINT_D4 (">apetaglib>SAVE>>: size %li %i %i %i\n", tagSSize,
952     //    mem_cnt->countTag, flag, saveApe2);
953     buff = (unsigned char *) malloc (tagSSize + (saveApe2 ? 32 : 0));
954     p = buff;
955 
956     if (buff == NULL) {
957         PRINT_ERR ("ERROR->libapetag->apetag_save::malloc");
958         return ATL_MALOC;
959     }
960     memset (ape_footer.id, 0, sizeof (ape_footer));
961     memcpy (ape_footer.id, "APETAGEX", sizeof (ape_footer.id));
962     long2ape (ape_footer.flags, 0l);
963     if (!!(flag & SAVE_CREATE_ID3V1_TAG ))
964         long2ape (ape_footer.length, tagSSize - ID3V1_TAG_SIZE);
965     else
966         long2ape (ape_footer.length, tagSSize);
967     //long2ape(ape_footer.tagCount, mem_cnt->countTag);
968     long2ape(ape_footer.tagCount, realCountTag);
969     long2ape (ape_footer.version, (saveApe2 ? 2000 : 1000));
970     if (saveApe2) {
971         long2ape (ape_footer.flags, HEADER_THIS_IS + HEADER_IS + FOOTER_IS);
972         memcpy (p, ape_footer.id, sizeof (ape_footer));
973         p += sizeof (ape_footer);
974     }
975 
976     mTag = (mem_cnt->tag);
977     for (n = 0; (mem_cnt->countTag) > n; n++) {
978         if (saveApe2) {
979             long2ape (temp, mTag[n]->sizeValue);
980         } else {
981             /* TODO:convert UTF8 to ASCII mTag[n]->value */
982             long2ape (temp, (mTag[n]->sizeValue) + 1);
983         }
984 
985         if (mTag[n]->sizeValue != 0) {
986             memcpy (p, temp, 4);
987             p += 4;
988             long2ape (temp, (saveApe2!=0) ? mTag[n]->flags : 0l );
989             memcpy (p, temp, 4);
990             p += 4;
991 
992             memcpy (p, mTag[n]->name, mTag[n]->sizeName);
993             p += mTag[n]->sizeName;
994             memcpy (p, "\0", 1);
995             p++;
996             memcpy (p, mTag[n]->value, mTag[n]->sizeValue);
997             p += mTag[n]->sizeValue;
998 
999             if (!saveApe2) {
1000                 memcpy (p, "\0", 1);
1001                 p++;
1002             }
1003             tagCount++;
1004         }
1005     } /* for */
1006 
1007     if (saveApe2)
1008         long2ape (ape_footer.flags, FOOTER_THIS_IS + FOOTER_IS + HEADER_IS);
1009 
1010     memcpy (p, ape_footer.id, sizeof (ape_footer));
1011     p += sizeof (ape_footer);
1012 
1013     if (!!(flag & SAVE_CREATE_ID3V1_TAG )) {
1014          memcpy (p, &id3v1_tag , sizeof (struct _id3v1Tag));
1015     }
1016 
1017     /* write tag to file and truncate */
1018     if (!(flag & SAVE_FAKE_SAVE)) {
1019         size_t fileSize;
1020         size_t newFileSize;
1021         size_t writedBytes;
1022         int fd;
1023 
1024         fseek (fp, 0, SEEK_END);
1025         fileSize = ftell (fp);
1026         fseek (fp, fileSize - skipBytes, SEEK_SET);
1027         if (tagCount != 0) {
1028             newFileSize = (fileSize - skipBytes + tagSSize + (saveApe2 ? 32 : 0));
1029             writedBytes = fwrite (buff, 1, tagSSize + (saveApe2 ? 32 : 0), fp);
1030             if (writedBytes != tagSSize + (saveApe2 ? 32 : 0)) {
1031                 PRINT_ERR ("FATAL_ERROR->libapetag->apetag_save::fwrite [data lost]");
1032                 fclose (fp);
1033                 free (buff);
1034                 return ATL_FWRITE;
1035             }
1036             fseek (fp, newFileSize, SEEK_SET);
1037             PRINT_D4 (">apetaglib>SAVE>> write:%zu == tag:%zu file: %zu->%zu\n",
1038                 writedBytes, tagSSize + (saveApe2 ? 32 : 0), fileSize, newFileSize);
1039         } else {
1040             newFileSize = (fileSize - skipBytes);
1041         }
1042         fflush (fp);
1043 
1044         if ((fd = fileno (fp)) == -1)
1045         {
1046             PRINT_ERR ("FATAL_ERROR->libapetag->apetag_save::fileno [file not truncated]");
1047             fclose (fp);
1048             return ATL_FWRITE;
1049         }
1050 
1051 #ifdef G_OS_WIN32
1052         if (_chsize (fd, newFileSize) == -1)
1053 #else
1054         if (ftruncate (fd, newFileSize) == -1)
1055 #endif
1056         {
1057             PRINT_ERR ("FATAL_ERROR->libapetag->apetag_save::fwrite [file not truncated]");
1058         }
1059 
1060         fclose (fp);
1061     } else { /* !!SAVE_FAKE_SAVE */
1062         libapetag_print_mem_cnt (mem_cnt);
1063     }
1064     free (buff);
1065 
1066     return 0;
1067 }
1068