aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/libapetag/apetaglib.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libapetag/apetaglib.c')
-rwxr-xr-xsrc/libapetag/apetaglib.c1065
1 files changed, 1065 insertions, 0 deletions
diff --git a/src/libapetag/apetaglib.c b/src/libapetag/apetaglib.c
new file mode 100755
index 0000000..1f5012e
--- /dev/null
+++ b/src/libapetag/apetaglib.c
@@ -0,0 +1,1065 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: apetaglib.c,v 1.44 2003/04/16 21:06:27 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#ifndef __BORLANDC__
+# include <unistd.h>
+#endif
+#include "apetaglib.h"
+#include "../genres.h"
+
+#include "is_tag.h"
+#ifdef ID3V2_READ
+# include "id3v2_read.h"
+#endif
+
+/* LOCAL STRUCTURES */
+
+/**
+ \struct _apetag_footer
+ \brief structure of APETAGEXT footer or/and header tag
+*/
+struct _apetag_footer
+{
+ unsigned char id[8]; /**< magic should equal 'APETAGEX' */
+ unsigned char version[4]; /**< version 1000 (v1.0) or 2000 (v 2.0) */
+ unsigned char length[4]; /**< the complete size of the tag, including footer, but no header for v2.0 */
+ unsigned char tagCount[4]; /**< the number of fields in the tag */
+ unsigned char flags[4]; /**< the tag flags (none currently defined for v 1.0) */
+ unsigned char reserved[8]; /**< reserved for later use */
+};
+
+/**
+ \struct _ape_mem_cnt
+ \brief internal structure for apetag
+*/
+struct _ape_mem_cnt
+{
+ struct tag **tag;
+ int countTag;
+ int memTagAlloc; // for mem container;
+ char *filename; // for info
+ struct _apetag_footer ape_header;
+ struct _apetag_footer ape_footer;
+ int currentPosition;
+};
+
+/* *
+ \struct _id3v1Tag
+ \brief for id3v1 tag
+*/
+struct _id3v1Tag
+{
+ char magic[3]; // `TAG`
+ char title[30];
+ char artist[30];
+ char album[30];
+ char year[4];
+ char comment[30]; // if ([28]==0 and [29]!=0) track = [29]
+ unsigned char genre;
+};
+
+/* LOCAL FUNCTION prototypes */
+unsigned long
+ape2long (unsigned char *p);
+void
+long2ape (unsigned char *p, const unsigned long value);
+struct tag *
+libapetag_maloc_cont_int (apetag *mem_cnt, struct tag *mTag);
+int
+libapetag_maloc_cont_text (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue, char *value);
+int
+libapetag_maloc_cont (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue, char *value);
+static int
+libapetag_qsort (struct tag **a, struct tag **b);
+int
+make_id3v1_tag(apetag *mem_cnt, struct _id3v1Tag *m);
+
+
+unsigned long
+ape2long (unsigned char *p)
+{
+ return (((unsigned long) p[0] << 0) |
+ ((unsigned long) p[1] << 8) |
+ ((unsigned long) p[2] << 16) |
+ ((unsigned long) p[3] << 24) );
+}
+
+void
+long2ape (unsigned char *p, const unsigned long value)
+{
+ p[0] = (unsigned char) (value >> 0);
+ p[1] = (unsigned char) (value >> 8);
+ p[2] = (unsigned char) (value >> 16);
+ p[3] = (unsigned char) (value >> 24);
+}
+
+
+/*
+ PL: funkcja troszczaca sie o odpowiednią ilosc zalokowanej pamieci dla tablicy
+ PL: %mTag% przy okazji alokuje z wyprzedzeniem troche wiecej pamieci [mniej %realoc%]
+ PL: zwraca %mTag[]%
+ :NON_USER:!!!
+ */
+#define LIBAPETAG_MEM_ALLOC_AHEAD 16 /* 15 it's good for normal #of tag, Aver 4-8 */
+struct tag *
+libapetag_maloc_cont_int (apetag *mem_cnt, struct tag *mTag)
+{
+ struct tag **tag_tmp = mem_cnt->tag;
+
+ if (mem_cnt->memTagAlloc == 0) { /* init */
+ mem_cnt->tag = (struct tag **)
+ malloc (((sizeof (struct tag **)) * (LIBAPETAG_MEM_ALLOC_AHEAD)));
+ mem_cnt->memTagAlloc = LIBAPETAG_MEM_ALLOC_AHEAD;
+ mem_cnt->countTag = 0;
+ if (mem_cnt->tag == NULL) {
+ mem_cnt->memTagAlloc = mem_cnt->countTag = 0;
+ PRINT_ERR ( "ERROR->libapetag->libapetag_maloc_cont_int:malloc\n");
+ return NULL;
+ }
+ }
+
+ if ((mem_cnt->memTagAlloc) <= (mem_cnt->countTag + 1)) {
+ mem_cnt->tag = (struct tag **) realloc (mem_cnt->tag, ((sizeof (struct tag **)) *
+ (mem_cnt->memTagAlloc + LIBAPETAG_MEM_ALLOC_AHEAD)));
+ mem_cnt->memTagAlloc += LIBAPETAG_MEM_ALLOC_AHEAD;
+ }
+
+ if (mem_cnt->tag == NULL) {
+ int n;
+
+ PRINT_ERR ( "ERROR->libapetag->libapetag_maloc_cont_int:malloc\n");
+ /* free old all */
+ for (n = mem_cnt->countTag-1; n >= 0; n--) {
+ free (tag_tmp[n]->value);
+ free (tag_tmp[n]->name);
+ free (tag_tmp[n]);
+ }
+ free (tag_tmp);
+ mem_cnt->memTagAlloc = mem_cnt->countTag = 0;
+ return NULL;
+ }
+
+ mem_cnt->tag[mem_cnt->countTag] = mTag;
+ mem_cnt->countTag++;
+ return mTag;
+
+}
+#undef LIBAPETAG_MEM_ALLOC_AHEAD
+
+
+/*
+ PL: alocuje pamiec dla %mTag% przypisuje odpowiednio wartosci
+ PL: dodaje %\0% do stringów [na wszelki wypadek]
+ PL: nie dopisuje takich samych
+ PL: wszystkie sizy maja byc bez \0 (jak bedzie to doliczy jeszcze jeden)
+ :NON_USER:!!!
+ */
+int
+libapetag_maloc_cont (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue, char *value)
+{
+ struct tag *mTag;
+ // TODO:: zadbac o to zeby tu czyscilo istniejace tagi jesli value=NULL
+ if (!sizeName || !sizeValue)
+ return ATL_BADARG;
+
+ if (apefrm_getstr (mem_cnt, name) == NULL) {
+ mTag = (struct tag *) malloc (sizeof (struct tag));
+
+ if (mTag == NULL)
+ return ATL_MALOC;
+
+ mTag->value = (char *) malloc (sizeValue + 1);
+ if (mTag->value==NULL) {
+ free (mTag);
+ return ATL_MALOC;
+ }
+
+ mTag->name = (char *) malloc (sizeName + 1);
+ if (mTag->name==NULL) {
+ free (mTag->value);
+ free (mTag);
+ return ATL_MALOC;
+ }
+
+ memcpy (mTag->value, value, sizeValue);
+ memcpy (mTag->name, name, sizeName);
+ mTag->value[sizeValue] = '\0';
+ mTag->name[sizeName] = '\0';
+ mTag->sizeName = sizeName;
+ mTag->sizeValue = sizeValue;
+ mTag->flags = flags;
+
+ if (libapetag_maloc_cont_int (mem_cnt, mTag)==NULL) {
+ PRINT_ERR(">apetaglib>libapetag_maloc_cont>> int==NULL");
+ return ATL_MALOC;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ PL: jezeli nie istnieje to dodaje taga, pomija ostatnie biale znaki
+ PL: pomija jesli pusty
+ PL: ! zmienia tekst wejściowy
+ :NON_USER:!!!
+*/
+int
+libapetag_maloc_cont_text (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue,
+ char *value)
+{
+ int n = sizeValue;
+
+ if (value != NULL && value[0] != '\0' && apefrm_getstr (mem_cnt, name) == NULL) {
+ while (value[--n] == ' ' || value[n] == '\0' || value[n] == '\n') {
+ value[n] = '\0';
+ }
+ return libapetag_maloc_cont (mem_cnt, flags, sizeName, name, n + 1, value);
+ }
+
+ return 0;
+}
+
+
+/*
+ PL: dodaje taga do istniejeacych o ustawionych wartosciach %flag% %name% i %value%
+ PL: wylicza odpowiednio rozmiary przy pomocy strlen!!
+ PL: wraper na %libapetag_maloc_cont%
+ PL: wszystko kopiuje sobie do pamieci
+ PL: musi byc juz w UTF-8 dla v2
+ PL: Nadpisuje istniejace
+ */
+/**
+ \brief Add text frame
+
+ add text frame/field to object apetag (if exist then overwrite)
+
+ \param mem_cnt object #apetag
+ \param flags flags stored in frame
+ \param name name of frame
+ \param value value of frame
+ \return 0 - OK else check #atl_return
+*/
+int
+apefrm_add (apetag *mem_cnt, unsigned long flags, char *name,
+ char *value)
+{
+ apefrm_remove_real (mem_cnt, name);
+ return libapetag_maloc_cont (mem_cnt, flags, strlen (name), name, strlen (value), value);
+}
+
+/*
+ PL: Prosty wraperek na maloc_cont - do zapisu binarnych
+*/
+/**
+ \brief add binary frame
+
+ add binary frame/field to object apetag (if exist then overwrite)
+
+ \param mem_cnt object #apetag
+ \param flags flags stored in frame
+ \param sizeName size of name
+ \param name name of frame
+ \param sizeValue size of value
+ \param value value of frame
+ \return 0 - OK else check #atl_return
+*/
+int
+apefrm_add_bin (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name,
+ long sizeValue, char *value)
+{
+ apefrm_remove_real (mem_cnt, name);
+ return libapetag_maloc_cont (mem_cnt, flags, sizeName, name, sizeValue, value);
+}
+
+/*
+ PL: jak %apefrm_add ()% z tym ze nie nadpisuje istniejacych
+*/
+/**
+ \brief add frame if other (the same name) no exist
+
+ if exist "name" in ape_mem then do nothing else add frame/field to ape_mem
+
+ \param mem_cnt object #apetag
+ \param flags flags stored in frame
+ \param name name of frame
+ \param value value of frame
+ \return 0 - OK else check #atl_return
+*/
+int
+apefrm_add_noreplace (apetag *mem_cnt, unsigned long flags,
+ char *name, char *value)
+{
+ if ( apefrm_getstr (mem_cnt, name) == NULL )
+ return apefrm_add (mem_cnt, flags, name, value);
+
+ return 0;
+}
+
+/*
+ PL: wyszukuje taga o nazwie %name% i zwraca structure %struct tag%
+ PL: %APE_TAG_LIB_FIRST% i %APE_TAG_LIB_NEXT% to ulatwienie dla
+ PL: przesukiwania wszystkich istniejacych tagów
+ PL: %APE_TAG_LIB_FIRST% ustawia znacznik na pierwszy tag [0] i zwraca jego wartość
+ PL: %APE_TAG_LIB_NEXT% podaje nastepny tag i zwieksza znacznik, po ostatnim funkcja zwraca %NULL%
+ PL: UWAGA!!! zwraca pointer do wewnetrznej struktury
+ PL: niczego nie zmieniac i nie free()-jowac skopiowac i dopiero
+ PL: zwraca teksty w UTF-8
+ */
+/**
+ \brief search in apetag for name and return tag
+
+ 2 special names \a APE_TAG_LIB_FIRST and \a APE_TAG_LIB_NEXT.
+ FIRST return first frame and set counter to 1
+ NEXT return ++counter frame
+\code
+for ((framka = apefrm_get(ape, APE_TAG_LIB_FIRST)); framka!=NULL;) {
+ do_something();
+ framka = apefrm_get(ape, APE_TAG_LIB_NEXT);
+}
+\endcode
+ return NULL if no more frame exist
+
+ \param mem_cnt object #apetag
+ \param name frame name for search
+ \return pointer to struct tag if name exist or NULL if don't
+ \warning don't change anything in this struct make copy and work
+*/
+struct tag *
+apefrm_get (apetag *mem_cnt, char *name)
+{
+ int n;
+ struct tag **mTag;
+
+ mTag = (mem_cnt->tag);
+
+ if (mem_cnt->countTag == 0)
+ return NULL;
+
+ if (strcmp (name, APE_TAG_LIB_FIRST) == 0) {
+ mem_cnt->currentPosition = 0;
+ return (mTag[mem_cnt->currentPosition++]);
+ }
+
+ if (strcmp (name, APE_TAG_LIB_NEXT) == 0) {
+ if (mem_cnt->currentPosition >= mem_cnt->countTag)
+ return NULL;
+ return (mTag[mem_cnt->currentPosition++]);
+ }
+
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (strcasecmp (mTag[n]->name, name) == 0) {
+ return (mTag[n]);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ PL:zwraca %mem_cnt->tag[x]->value% o ile znajdzie nazwe %name% taga
+ PL: prosty wraper na %apefrm_get %
+ PL: UWAGA zwraca pointer z wewnetrznych struktur niczego bezposrednio nie zmieniac
+ PL: i nie free()-jowac bo sie rozsypie
+ PL: zwraca tekst w UTF-8
+ */
+/**
+ \brief search in apetag for name and return string
+
+ \param mem_cnt object #apetag
+ \param name frame name for search
+ \return pointer to value of frame if name exist or NULL if don't
+ \warning don't change that string make copy before any action
+ \todo check if frame type isn't binary
+*/
+char *
+apefrm_getstr (apetag *mem_cnt, char *name)
+{
+ struct tag *mTag;
+
+ mTag = apefrm_get (mem_cnt, name);
+
+ if (mTag == NULL)
+ return NULL;
+
+ return (mTag->value);
+}
+
+/*
+ PL: usuwanie taga o nazwie zdefiniowanej w %name%
+ PL:lub wszystkich jezeli %name%=%APE_TAG_LIB_DEL_ALL%
+ PL:UWAGA mozna to napisac inaczej (sprawdzanie czy %name% OR %%special%) ale to w v1.0
+ */
+/**
+ \brief remove frame from memory
+
+ (real) remove frame from ape_mem.
+ Check #apefrm_remove for more info
+
+ \param mem_cnt object #apetag
+ \param name frame name for search and remove
+*/
+void
+apefrm_remove_real (apetag *mem_cnt, char *name)
+{
+ int n;
+ struct tag **mTag;
+
+ mTag = (mem_cnt->tag);
+
+ /* Delete all */
+ if (strcmp (name, APE_TAG_LIB_DEL_ALL) == 0) {
+ for (n = mem_cnt->countTag-1; n >= 0; n--) {
+ free (mTag[n]->name);
+ free (mTag[n]->value);
+ free (mTag[n]);
+ --mem_cnt->countTag;
+ }
+ return;
+ }
+ /* Delete only one */
+ for (n = mem_cnt->countTag-1; n >= 0; n--) {
+ if (strcasecmp (mTag[n]->name, name) == 0) {
+ free (mTag[n]->name);
+ free (mTag[n]->value);
+ free (mTag[n]);
+ mTag[n] = mTag[mem_cnt->countTag];
+ --mem_cnt->countTag;
+ /* !no return; search for all */
+ }
+ }
+
+ return;
+}
+/*
+ PL: tak jakby frejuje framke oznacza do kasacji jednak tego nie robi
+ PL: mechanizm ten głownie jest wykorzystywany do wczytania innych tagów
+ PL: poza wczesniej zkasowanymi aby to usunąc uzyj apefrm_remove_real
+*/
+/**
+ \brief set frame to remove
+
+ Create fake name and empty value (and set don't save flag).
+ If you use apefrm_add_norepleace then you don't change
+ this not_save_flag.
+ Only apefrm_add overwrite this.
+ [it's for id3v1 but you may using this for remove frames]
+
+ \param mem_cnt object #apetag
+ \param name frame name for search and remove
+*/
+void
+apefrm_remove (apetag *mem_cnt, char *name)
+{
+ int n;
+ struct tag **mTag;
+
+ apefrm_add (mem_cnt, 0 , name, "delete me");
+
+ mTag = (mem_cnt->tag);
+
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (strcasecmp (mTag[n]->name, name) == 0) {
+ mTag[n]->sizeValue=0;
+ return;
+ }
+ }
+
+ return;
+}
+
+/*
+ PL:Wypisuje na ekran wszystko to co potrzebne do debugu
+ :NON_USER:!!!
+*/
+/**
+ debug function print all tags exclude bin (print only size for bin)
+*/
+void
+libapetag_print_mem_cnt (apetag *mem_cnt)
+{
+ int n;
+ struct tag **mTag;
+
+ mTag = (mem_cnt->tag);
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if ( (mTag[n]->flags & ~ITEM_TEXT) == 0 ||
+ (mTag[n]->flags & ~ITEM_LINK) == 0 ) {
+ printf (">apetaglib>PRINT>>F=%li SN=%li SV=%li N[%s] V[%s]\n",
+ mTag[n]->flags,
+ (long) mTag[n]->sizeName, (long) mTag[n]->sizeValue,
+ mTag[n]->name, mTag[n]->value);
+ } else {
+ printf (">apetaglib>PRINT>>F=%li SN=%li SV=%li N[%s] V=BINARY\n",
+ mTag[n]->flags,
+ (long) mTag[n]->sizeName, (long) mTag[n]->sizeValue,
+ mTag[n]->name);
+ }
+ }
+
+ return;
+}
+
+/*
+ PL: alokuje pamiec dla glównej struktury %struct ape_mem_cnt%
+ PL: i zeruje wszystko co trzeba
+ PL: z jakiegos powodu (mojej niewiedzy) memset nie dziala
+ PL: a w sumie dziala czyszczac troche za duzo
+*/
+/**
+ \brief initialise new object #apetag and return
+ \return new initialised object #apetag
+*/
+apetag *
+apetag_init (void)
+{
+ apetag * mem_cnt;
+
+ mem_cnt = (apetag *) malloc (sizeof (apetag));
+ if (mem_cnt == NULL) {
+ PRINT_ERR ("ERROR->libapetag->apetag_init:malloc\n");
+ return NULL;
+ }
+ mem_cnt->memTagAlloc = 0;
+ mem_cnt->countTag = 0;
+ mem_cnt->filename = NULL;
+ mem_cnt->currentPosition = 0;
+ mem_cnt->tag = NULL;
+
+ return mem_cnt;
+}
+
+/*
+ PL: Czysci z sila wodospadu wszystko co zostalo do czyszczenia
+ PL: z %struct ape_mem_cnt% wlacznie, wcześniej to nie było jasne
+*/
+/**
+ \brief free all work
+ \param mem_cnt object #apetag
+*/
+void
+apetag_free (apetag *mem_cnt)
+{
+ int n;
+
+ for (n = mem_cnt->countTag-1; n >= 0; n--)
+ {
+ free (mem_cnt->tag[n]->value);
+ free (mem_cnt->tag[n]->name);
+ free (mem_cnt->tag[n]);
+ }
+ free (mem_cnt->tag);
+ free (mem_cnt);
+ mem_cnt = NULL;
+
+ return;
+
+}
+
+
+/**
+ \brief read id3v1 and add frames
+
+ read id3v1 and add frames to ape_mem.
+ Using #apefrm_add_norepleace
+
+ \param mem_cnt object #apetag
+ \param fp file pointer
+ \return 0 - OK else check #atl_return
+*/
+int
+readtag_id3v1_fp (apetag *mem_cnt, FILE * fp)
+{
+ struct _id3v1Tag m;
+
+ if (!is_id3v1(fp))
+ return 0; /* TODO:: 0 or no_id3v1*/
+
+ fseek(fp, -128, SEEK_END);
+ if (sizeof (struct _id3v1Tag)!=fread(&m, 1, sizeof (struct _id3v1Tag), fp)){
+ PRINT_ERR( "ERROR->libapetag->readtag_id3v1_fp:fread\n");
+ return ATL_FREAD;
+ }
+
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Title", 30, m.title);
+ libapetag_maloc_cont_text(mem_cnt, 0, 6, "Artist", 30, m.artist);
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Album", 30, m.album);
+ libapetag_maloc_cont_text(mem_cnt, 0, 4, "Year", 4, m.year);
+ if (m.comment[28] == 0 && m.comment[29] != 0) {
+ char track[20];
+ snprintf(track, 19, "%i", m.comment[29]);
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Track", strlen(track), track);
+ libapetag_maloc_cont_text(mem_cnt, 0, 7, "Comment", 28, m.comment);
+ } else {
+ libapetag_maloc_cont_text(mem_cnt, 0, 7, "Comment", 30, m.comment);
+ }
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Genre",
+ strlen(genre_no(m.genre)), genre_no(m.genre));
+
+ return 0;
+}
+
+/*
+ PL: wczytuje odpowiednie fra(mk)gi do pamieci w razie koniecznosci przyciecia
+ PL: dodaje "..." na koniec
+ PL: TODO genre
+
+ PL: macro COMPUTE_ID3V1_TAG
+*/
+#define COMPUTE_ID3V1_TAG(FramkA, TagNamE, SizE, TagValuE) \
+ FramkA = apefrm_get(mem_cnt, TagNamE); \
+ if (FramkA != NULL) { \
+ memcpy (TagValuE, FramkA->value, \
+ ((FramkA->sizeValue) > SizE) ? SizE : FramkA->sizeValue ); \
+ if ((FramkA->sizeValue) > SizE) { \
+ TagValuE[SizE-1]='.'; TagValuE[SizE-2]='.'; TagValuE[SizE-3]='.'; \
+ } \
+ }
+
+int
+make_id3v1_tag(apetag *mem_cnt, struct _id3v1Tag *m)
+{
+ struct tag * framka;
+
+ if (m == NULL)
+ return ATL_BADARG;
+
+ memset(m, '\0', sizeof(struct _id3v1Tag));
+
+ memcpy (m->magic,"TAG",3);
+ COMPUTE_ID3V1_TAG(framka, "Title", 30, m->title);
+ COMPUTE_ID3V1_TAG(framka, "Artist", 30, m->artist);
+ COMPUTE_ID3V1_TAG(framka, "Album", 30, m->album);
+ COMPUTE_ID3V1_TAG(framka, "Year", 4, m->year);
+
+ if ((framka=apefrm_get(mem_cnt, "Track"))!=NULL) {
+ m->comment[29]=(unsigned char) atoi(framka->value);
+ m->comment[28]='\0';
+ COMPUTE_ID3V1_TAG(framka, "Comment", 28, m->comment);
+ } else {
+ COMPUTE_ID3V1_TAG(framka, "Comment", 30, m->comment);
+ }
+
+ return 0;
+}
+
+/*
+ PL: silnik tego liba
+ PL: %filename% jest w tej chwili tylko dla id3v2 f..k
+ PL: %ape_mem_cnt% moze byc nie zainicjalizowany ale wtedy musi byc = NULL
+*/
+/**
+ \brief read file and add frames
+
+ \param mem_cnt object #apetag
+ \param filename
+ \param fp
+ \param flag
+ \return 0 - OK else check #atl_return
+*/
+int
+apetag_read_fp(apetag *mem_cnt, FILE * fp, char *filename, int flag)
+{
+ int id3v1 = 0;
+ int apeTag2 = 0;
+ unsigned char *buff;
+ struct _apetag_footer ape_footer;
+ size_t savedFilePosition, buffLength;
+
+ unsigned char *end;
+ unsigned long tagCount;
+ unsigned char *p;
+
+ savedFilePosition = ftell(fp);
+
+ id3v1 = is_id3v1(fp);
+
+ if (mem_cnt == NULL) {
+ PRINT_ERR( ">apetaglib>READ_FP>FATAL>apetag_init()\n");
+ fseek(fp, savedFilePosition, SEEK_SET);
+ return ATL_NOINIT;
+ }
+
+ fseek(fp, id3v1 ? -128 - sizeof (ape_footer) : -sizeof (ape_footer), SEEK_END);
+ if (sizeof (ape_footer) != fread(&ape_footer, 1, sizeof (ape_footer), fp)){
+ PRINT_ERR( "ERROR->libapetag->apetag_read_fp:fread1\n");
+ fseek(fp, savedFilePosition, SEEK_SET);
+ return ATL_FREAD;
+ }
+
+ if (!(flag & DONT_READ_TAG_APE) &&
+ (memcmp(ape_footer.id, "APETAGEX", sizeof (ape_footer.id)) == 0))
+ {
+ PRINT_D9(">apetaglib>READ_FP>>%s: ver %li len %li # %li fl %lx v1=%i v2=%i ape=%i[v%i]\n",
+ filename, ape2long(ape_footer.version),
+ ape2long(ape_footer.length),
+ ape2long(ape_footer.tagCount),
+ ape2long(ape_footer.flags),
+ is_id3v1 (fp), is_id3v2 (fp), is_ape (fp), is_ape_ver (fp));
+
+ apeTag2 = ape2long(ape_footer.version);
+ buffLength = is_ape(fp) + 128;
+ buff = (unsigned char *) malloc(buffLength);
+ if (buff == NULL) {
+ PRINT_ERR( "ERROR->libapetag->apetag_read_fp:malloc\n");
+ return ATL_MALOC;
+ }
+
+ fseek(fp, id3v1 ? -ape2long(ape_footer.length) -
+ 128 : -ape2long(ape_footer.length), SEEK_END);
+ memset(buff, 0, buffLength);
+ if (ape2long(ape_footer.length) != fread(buff, 1, ape2long(ape_footer.length), fp)) {
+ PRINT_ERR( "ERROR->libapetag->apetag_read_fp:fread2\n");
+ fseek(fp, savedFilePosition, SEEK_SET);
+ free(buff);
+ return ATL_FREAD;
+ }
+
+ tagCount = ape2long(ape_footer.tagCount);
+
+ end = buff + ape2long(ape_footer.length) - sizeof (ape_footer);
+
+ for (p = buff; p < end && tagCount--;) {
+ /* 8 = sizeof( sizeValue+flags ) */
+ unsigned long flag = ape2long(p + 4);
+ unsigned long sizeValue = ape2long(p);
+ unsigned long sizeName;
+ char *name = p + 8;
+ char *value;
+
+ sizeName = strlen(p + 8);
+ value = p + sizeName + 8 + 1;
+ if (apeTag2 == 1000 && value[sizeValue - 1] == '\0') {
+ libapetag_maloc_cont(mem_cnt, flag,
+ sizeName, name,
+ sizeValue - 1, value);
+ } else {
+ libapetag_maloc_cont(mem_cnt, flag,
+ sizeName, name,
+ sizeValue, value);
+ }
+ p += (sizeName + sizeValue + 8 + 1);
+ }
+
+ free(buff);
+ } else { /* if no ape tag */
+ PRINT_D5(">apetaglib>READ_FP>>%s: v1=%i v2=%i ape=%i[v%i]\n",
+ filename, is_id3v1 (fp), is_id3v2 (fp), is_ape (fp), is_ape_ver (fp));
+ }
+
+#ifdef ID3V2_READ
+ if (!(flag & DONT_READ_TAG_ID3V2) && filename!=NULL && is_id3v2(fp)!=0) {
+ readtag_id3v2(mem_cnt, filename);
+ }
+#endif
+ if (!(flag & DONT_READ_TAG_ID3V1) && (id3v1)) {
+ readtag_id3v1_fp(mem_cnt, fp);
+ }
+
+ fseek(fp, savedFilePosition, SEEK_SET);
+ return 0;
+}
+
+/*
+ PL: wraper na apetag_read_fp
+ PL: otwiera plik wczytuje co trzeba i zamyka
+ PL: dobre do wczytywania informacji ktore sa potrzebne pozniej bez fatygi otwierania pliku
+*/
+/**
+ \brief read file and add frames
+
+ \param mem_cnt object #apetag
+ \param filename file name
+ \param flag
+ \return 0 - OK else check #atl_return
+*/
+int
+apetag_read (apetag *mem_cnt, char *filename,int flag)
+{
+ FILE *fp;
+
+ if (mem_cnt==NULL) {
+ PRINT_ERR(">apetaglib>READ>FATAL>apetag_init()\n");
+ return ATL_NOINIT;
+ }
+
+ fp = fopen (filename, "rb");
+ if (fp == NULL)
+ return ATL_FOPEN;
+
+ apetag_read_fp (mem_cnt, fp, filename,flag);
+
+ fclose (fp);
+
+ return 0;
+}
+
+/*
+ PL: Funkcja dla qsorta ze specjalnymi wyjatkami
+ PL: uzywana w apetag_save
+ :NON_USER:!!!
+*/
+static int
+libapetag_qsort (struct tag **a, struct tag **b)
+{
+ char *sorting[] = { "Artist", "Year", "Album", "Track", "Title", "Genre", NULL, NULL };
+ int n, m;
+
+ if (!a || !b || !*a || !*b) {
+ PRINT_ERR ("ERROR->libapetag->apetag_qsort:*a ||*b = NULL : FATAL PLEASE REPORT!!!\n");
+ return 0;
+ }
+ for (n = 0; sorting[n] != NULL; n++) {
+ if (strcasecmp ((*a)->name, sorting[n]) == 0)
+ break;
+ }
+ if (sorting[n] == NULL)
+ n += (*a)->sizeValue + 1; /* n = max entries of sorting + size of tag */
+
+ for (m = 0; sorting[m] != NULL; m++) {
+ if (strcasecmp ((*b)->name, sorting[m]) == 0)
+ break;
+ }
+ if (sorting[m] == NULL)
+ m += (*b)->sizeValue + 1; /* m = max entries */
+
+ if (n == m)
+ return 0;
+ if (n > m)
+ return 1;
+ else
+ return -1;
+}
+
+#ifdef USE_CHSIZE
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <io.h>
+/* on winblows we don't have truncate (and ftruncate) but have chsize() */
+void
+truncate (char *filename, size_t fileSize)
+{
+ int handle;
+
+ handle = open (filename, O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (handle != -1) {
+ if (chsize (handle, fileSize) != 0) {
+ PRINT_ERR ("Error truncatng file\n");
+ }
+ close (handle);
+ }
+
+}
+
+#endif
+
+/*
+ PL: domyslne %flag% = APE_TAG_V2 + SAVE_NEW_OLD_APE_TAG
+*/
+/**
+ \brief save apetag to file
+
+ \param filename file name
+ \param mem_cnt object #apetag
+ \param flag flags for read/save
+ \return 0 - OK else check #atl_return
+ \warning for ape tag v 1 you must add frames in iso-1
+ for v 2 this must be in utf-8
+ \todo PL: v9 sprawdzac flagi w footer i na tej podstawie zmieniac skipBytes
+ bez domniemywania ze v2 ma zawsze oba
+
+*/
+int
+apetag_save (char *filename, apetag *mem_cnt, int flag)
+{
+ FILE *fp;
+ struct _id3v1Tag id3v1_tag;
+ int id3v1;
+ int apeTag, saveApe2;
+ int tagCount = 0;
+ int realCountTag = 0;
+ struct _apetag_footer ape_footer;
+ long skipBytes;
+ unsigned char *buff, *p;
+ struct tag **mTag;
+ size_t tagSSize = 32;
+ int n;
+ char temp[4];
+
+ if (mem_cnt==NULL) {
+ PRINT_ERR("ERROR->apetaglib>apetag_save::apetag_init()\n");
+ return ATL_NOINIT;
+ }
+
+ fp = fopen (filename, "rb+");
+ if (fp == NULL) {
+ PRINT_ERR ( "ERROR->apetaglib->apetag_save::fopen (r+)\n");
+ return ATL_FOPEN;
+ }
+
+ skipBytes = 0;
+ id3v1 = is_id3v1 (fp);
+ apeTag = is_ape (fp);
+ saveApe2 = !(flag & APE_TAG_V1); // (flag & APE_TAG_V2) ? 1 : (flag & APE_TAG_V1);
+
+ if (id3v1) {
+ fseek (fp, -128, SEEK_END);
+ fread (&id3v1_tag, 1, sizeof (struct _id3v1Tag), fp);
+ skipBytes += id3v1;
+ }
+ skipBytes += apeTag;
+
+ if (!(flag & SAVE_NEW_APE_TAG)) {
+ apetag_read_fp (mem_cnt, fp, filename, flag);
+ }
+
+ mTag = (mem_cnt->tag);
+ qsort( mTag , mem_cnt->countTag , sizeof(struct tag *),
+ (int (*)(const void *,const void *))libapetag_qsort);
+
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (mTag[n]->sizeValue != 0) {
+ tagSSize += ((long) mTag[n]->sizeName + (long) mTag[n]->sizeValue);
+ tagSSize += 4 + 4 + 1 + (saveApe2 ? 0 : 1); // flag & sizeValue & \0
+ realCountTag++; // count not deleted tag (exl. not real)
+ }
+ }
+ if (!!(flag & SAVE_CREATE_ID3V1_TAG )) {
+ make_id3v1_tag(mem_cnt, &id3v1_tag);
+ tagSSize += 128;
+ }
+ //PRINT_D4 (">apetaglib>SAVE>>: size %li %i %i %i\n", tagSSize,
+ // mem_cnt->countTag, flag, saveApe2);
+ buff = (unsigned char *) malloc (tagSSize + (saveApe2 ? 32 : 0));
+ p = buff;
+
+ if (buff == NULL) {
+ PRINT_ERR ("ERROR->libapetag->apetag_save::malloc");
+ return ATL_MALOC;
+ }
+ memset (ape_footer.id, 0, sizeof (ape_footer));
+ memcpy (ape_footer.id, "APETAGEX", sizeof (ape_footer.id));
+ long2ape (ape_footer.flags, 0l);
+ if (!!(flag & SAVE_CREATE_ID3V1_TAG ))
+ long2ape (ape_footer.length, tagSSize-128);
+ else
+ long2ape (ape_footer.length, tagSSize);
+ //long2ape(ape_footer.tagCount, mem_cnt->countTag);
+ long2ape(ape_footer.tagCount, realCountTag);
+ long2ape (ape_footer.version, (saveApe2 ? 2000 : 1000));
+ if (saveApe2) {
+ long2ape (ape_footer.flags, HEADER_THIS_IS + HEADER_IS + FOOTER_IS);
+ memcpy (p, ape_footer.id, sizeof (ape_footer));
+ p += sizeof (ape_footer);
+ }
+
+ mTag = (mem_cnt->tag);
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (saveApe2) {
+ long2ape (temp, mTag[n]->sizeValue);
+ } else {
+ /* TODO:convert UTF8 to ASCII mTag[n]->value */
+ long2ape (temp, (mTag[n]->sizeValue) + 1);
+ }
+
+ if (mTag[n]->sizeValue != 0) {
+ memcpy (p, temp, 4);
+ p += 4;
+ long2ape (temp, (saveApe2!=0) ? mTag[n]->flags : 0l );
+ memcpy (p, temp, 4);
+ p += 4;
+
+ memcpy (p, mTag[n]->name, mTag[n]->sizeName);
+ p += mTag[n]->sizeName;
+ memcpy (p, "\0", 1);
+ p++;
+ memcpy (p, mTag[n]->value, mTag[n]->sizeValue);
+ p += mTag[n]->sizeValue;
+
+ if (!saveApe2) {
+ memcpy (p, "\0", 1);
+ p++;
+ }
+ tagCount++;
+ }
+ } /* for */
+
+ if (saveApe2)
+ long2ape (ape_footer.flags, FOOTER_THIS_IS + FOOTER_IS + HEADER_IS);
+
+ memcpy (p, ape_footer.id, sizeof (ape_footer));
+ p += sizeof (ape_footer);
+
+ if (!!(flag & SAVE_CREATE_ID3V1_TAG )) {
+ memcpy (p, &id3v1_tag , sizeof (struct _id3v1Tag));
+ }
+
+ /* write tag to file and truncate */
+ if (!(flag & SAVE_FAKE_SAVE)) {
+ size_t fileSize;
+ size_t newFileSize;
+ size_t writedBytes;
+
+ fseek (fp, 0, SEEK_END);
+ fileSize = ftell (fp);
+ fseek (fp, fileSize - skipBytes, SEEK_SET);
+ if (tagCount != 0) {
+ newFileSize = (fileSize - skipBytes + tagSSize + (saveApe2 ? 32 : 0));
+ writedBytes = fwrite (buff, 1, tagSSize + (saveApe2 ? 32 : 0), fp);
+ if (writedBytes != tagSSize + (saveApe2 ? 32 : 0)) {
+ PRINT_ERR ("FATAL_ERROR->libapetag->apetag_save::fwrite [data lost]");
+ fclose (fp);
+ free (buff);
+ return ATL_FWRITE;
+ }
+ fseek (fp, newFileSize, SEEK_SET);
+ PRINT_D4 (">apetaglib>SAVE>> write:%i == tag:%i file: %i->%i\n",
+ writedBytes, tagSSize + (saveApe2 ? 32 : 0), fileSize, newFileSize);
+ } else {
+ newFileSize = (fileSize - skipBytes);
+ }
+ fflush (fp);
+ fclose (fp);
+ /* ftruncate don't work */
+ truncate (filename, newFileSize);
+ } else { /* !!SAVE_FAKE_SAVE */
+ libapetag_print_mem_cnt (mem_cnt);
+ }
+ free (buff);
+
+ return 0;
+}