1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 //  The contents of this file are subject to the Mozilla Public License
4 //  Version 1.1 (the "License"); you may not use this file except in
5 //  compliance with the License. You may obtain a copy of the License at
6 //  http://www.mozilla.org/MPL/
7 //
8 //  Software distributed under the License is distributed on an "AS IS"
9 //  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
10 //  License for the specific language governing rights and limitations
11 //  under the License.
12 //
13 //  The Original Code is MP4v2.
14 //
15 //  The Initial Developer of the Original Code is Kona Blend.
16 //  Portions created by Kona Blend are Copyright (C) 2008.
17 //  Portions created by David Byron are Copyright (C) 2011.
18 //  All Rights Reserved.
19 //
20 //  Contributors:
21 //      Kona Blend, kona8lend@@gmail.com
22 //      Rouven Wessling, mp4v2@rouvenwessling.de
23 //      David Byron, dbyron@dbyron.com
24 //
25 ///////////////////////////////////////////////////////////////////////////////
26 
27 #include "impl.h"
28 
29 namespace mp4v2 { namespace impl { namespace itmf {
30 
31 ///////////////////////////////////////////////////////////////////////////////
32 
Tags()33 Tags::Tags()
34     : hasMetadata(false)
35 {
36 }
37 
38 ///////////////////////////////////////////////////////////////////////////////
39 
~Tags()40 Tags::~Tags()
41 {
42 }
43 
44 ///////////////////////////////////////////////////////////////////////////////
45 
46 void
c_addArtwork(MP4Tags * & tags,MP4TagArtwork & c_artwork)47 Tags::c_addArtwork( MP4Tags*& tags, MP4TagArtwork& c_artwork )
48 {
49     artwork.resize( artwork.size() + 1 );
50     c_setArtwork( tags, (uint32_t)artwork.size() - 1, c_artwork );
51     updateArtworkShadow( tags );
52 }
53 
54 ///////////////////////////////////////////////////////////////////////////////
55 
56 void
c_alloc(MP4Tags * & tags)57 Tags::c_alloc( MP4Tags*& tags )
58 {
59     tags = new MP4Tags();
60     memset( tags, 0, sizeof(MP4Tags) ); // safe: pure C-struct
61     tags->__handle = this;
62 }
63 
64 ///////////////////////////////////////////////////////////////////////////////
65 
66 void
c_fetch(MP4Tags * & tags,MP4FileHandle hFile)67 Tags::c_fetch( MP4Tags*& tags, MP4FileHandle hFile )
68 {
69     MP4Tags& c = *tags;
70     MP4File& file = *static_cast<MP4File*>(hFile);
71 
72     MP4ItmfItemList* itemList = genericGetItems( file ); // alloc
73 
74     hasMetadata = (itemList->size > 0);
75 
76     /* create code -> item map.
77      * map will only be used for items which do not repeat; we do not care if
78      * cover-art is inserted multiple times.
79      */
80     CodeItemMap cim;
81     for( uint32_t i = 0; i < itemList->size; i++ ) {
82         MP4ItmfItem& item = itemList->elements[i];
83         cim.insert( CodeItemMap::value_type( item.code, &item ));
84     }
85 
86     fetchString(  cim, CODE_NAME,              name,              c.name );
87     fetchString(  cim, CODE_ARTIST,            artist,            c.artist );
88     fetchString(  cim, CODE_ALBUMARTIST,       albumArtist,       c.albumArtist );
89     fetchString(  cim, CODE_ALBUM,             album,             c.album );
90     fetchString(  cim, CODE_GROUPING,          grouping,          c.grouping );
91     fetchString(  cim, CODE_COMPOSER,          composer,          c.composer );
92     fetchString(  cim, CODE_COMMENTS,          comments,          c.comments );
93 
94     fetchString(  cim, CODE_GENRE,             genre,             c.genre );
95     fetchGenre(   cim,                         genreType,         c.genreType );
96 
97     fetchString(  cim, CODE_RELEASEDATE,       releaseDate,       c.releaseDate );
98     fetchTrack(   cim,                         track,             c.track );
99     fetchDisk(    cim,                         disk,              c.disk );
100     fetchInteger( cim, CODE_TEMPO,             tempo,             c.tempo );
101     fetchInteger( cim, CODE_COMPILATION,       compilation,       c.compilation );
102 
103     fetchString(  cim, CODE_TVSHOW,            tvShow,            c.tvShow );
104     fetchString(  cim, CODE_TVNETWORK,         tvNetwork,         c.tvNetwork );
105     fetchString(  cim, CODE_TVEPISODEID,       tvEpisodeID,       c.tvEpisodeID );
106     fetchInteger( cim, CODE_TVSEASON,          tvSeason,          c.tvSeason );
107     fetchInteger( cim, CODE_TVEPISODE,         tvEpisode,         c.tvEpisode );
108 
109     fetchString(  cim, CODE_SORTNAME,          sortName,          c.sortName );
110     fetchString(  cim, CODE_SORTARTIST,        sortArtist,        c.sortArtist );
111     fetchString(  cim, CODE_SORTALBUMARTIST,   sortAlbumArtist,   c.sortAlbumArtist );
112     fetchString(  cim, CODE_SORTALBUM,         sortAlbum,         c.sortAlbum );
113     fetchString(  cim, CODE_SORTCOMPOSER,      sortComposer,      c.sortComposer );
114     fetchString(  cim, CODE_SORTTVSHOW,        sortTVShow,        c.sortTVShow );
115 
116     fetchString(  cim, CODE_DESCRIPTION,       description,       c.description );
117     fetchString(  cim, CODE_LONGDESCRIPTION,   longDescription,   c.longDescription );
118     fetchString(  cim, CODE_LYRICS,            lyrics,            c.lyrics );
119 
120     fetchString(  cim, CODE_COPYRIGHT,         copyright,         c.copyright );
121     fetchString(  cim, CODE_ENCODINGTOOL,      encodingTool,      c.encodingTool );
122     fetchString(  cim, CODE_ENCODEDBY,         encodedBy,         c.encodedBy );
123     fetchString(  cim, CODE_PURCHASEDATE,      purchaseDate,      c.purchaseDate );
124 
125     fetchInteger( cim, CODE_PODCAST,           podcast,           c.podcast );
126     fetchString(  cim, CODE_KEYWORDS,          keywords,          c.keywords );
127     fetchString(  cim, CODE_CATEGORY,          category,          c.category );
128 
129     fetchInteger( cim, CODE_HDVIDEO,           hdVideo,           c.hdVideo );
130     fetchInteger( cim, CODE_MEDIATYPE,         mediaType,         c.mediaType );
131     fetchInteger( cim, CODE_CONTENTRATING,     contentRating,     c.contentRating );
132     fetchInteger( cim, CODE_GAPLESS,           gapless,           c.gapless );
133 
134     fetchString(  cim, CODE_ITUNESACCOUNT,     iTunesAccount,     c.iTunesAccount );
135     fetchInteger( cim, CODE_ITUNESACCOUNTTYPE, iTunesAccountType, c.iTunesAccountType );
136     fetchInteger( cim, CODE_ITUNESCOUNTRY,     iTunesCountry,     c.iTunesCountry );
137 
138     fetchInteger( cim, CODE_CONTENTID,         contentID,         c.contentID );
139     fetchInteger( cim, CODE_ARTISTID,          artistID,          c.artistID );
140     fetchInteger( cim, CODE_PLAYLISTID,        playlistID,        c.playlistID );
141     fetchInteger( cim, CODE_GENREID,           genreID,           c.genreID );
142     fetchInteger( cim, CODE_COMPOSERID,        composerID,        c.composerID );
143     fetchString(  cim, CODE_XID,               xid,               c.xid );
144 
145     genericItemListFree( itemList ); // free
146 
147     // fetch full list and overwrite our copy, otherwise clear
148     {
149         CoverArtBox::ItemList items;
150         if( CoverArtBox::list( hFile, items ))
151             artwork.clear();
152         else
153             artwork = items;
154 
155         updateArtworkShadow( tags );
156     }
157 }
158 
159 ///////////////////////////////////////////////////////////////////////////////
160 
161 void
c_free(MP4Tags * & tags)162 Tags::c_free( MP4Tags*& tags )
163 {
164     MP4Tags* c = const_cast<MP4Tags*>(tags);
165 
166     delete[] c->artwork;
167     delete c;
168 
169     tags = NULL;
170 }
171 
172 ///////////////////////////////////////////////////////////////////////////////
173 
174 void
c_removeArtwork(MP4Tags * & tags,uint32_t index)175 Tags::c_removeArtwork( MP4Tags*& tags, uint32_t index )
176 {
177     if( !(index < artwork.size()) )
178         return;
179 
180     artwork.erase( artwork.begin() + index );
181     updateArtworkShadow( tags );
182 }
183 
184 ///////////////////////////////////////////////////////////////////////////////
185 
186 void
c_setArtwork(MP4Tags * & tags,uint32_t index,MP4TagArtwork & c_artwork)187 Tags::c_setArtwork( MP4Tags*& tags, uint32_t index, MP4TagArtwork& c_artwork )
188 {
189     if( !(index < artwork.size()) )
190         return;
191 
192     CoverArtBox::Item& item = artwork[index];
193 
194     switch( c_artwork.type ) {
195         case MP4_ART_BMP:
196             item.type = BT_BMP;
197             break;
198 
199         case MP4_ART_GIF:
200             item.type = BT_GIF;
201             break;
202 
203         case MP4_ART_JPEG:
204             item.type = BT_JPEG;
205             break;
206 
207         case MP4_ART_PNG:
208             item.type = BT_PNG;
209             break;
210 
211         case MP4_ART_UNDEFINED:
212         default:
213             item.type = computeBasicType( c_artwork.data, c_artwork.size );
214             break;
215     }
216 
217     item.buffer   = (uint8_t*)malloc( c_artwork.size );
218     item.size     = c_artwork.size;
219     item.autofree = true;
220 
221     memcpy( item.buffer, c_artwork.data, c_artwork.size );
222     updateArtworkShadow( tags );
223 }
224 
225 ///////////////////////////////////////////////////////////////////////////////
226 
227 void
c_setInteger(const uint8_t * value,uint8_t & cpp,const uint8_t * & c)228 Tags::c_setInteger( const uint8_t* value, uint8_t& cpp, const uint8_t*& c )
229 {
230     if( !value ) {
231         cpp = 0;
232         c = NULL;
233     }
234     else {
235         cpp = *value;
236         c = &cpp;
237     }
238 }
239 
240 ///////////////////////////////////////////////////////////////////////////////
241 
242 void
c_setInteger(const uint16_t * value,uint16_t & cpp,const uint16_t * & c)243 Tags::c_setInteger( const uint16_t* value, uint16_t& cpp, const uint16_t*& c )
244 {
245     if( !value ) {
246         cpp = 0;
247         c = NULL;
248     }
249     else {
250         cpp = *value;
251         c = &cpp;
252     }
253 }
254 
255 ///////////////////////////////////////////////////////////////////////////////
256 
257 void
c_setInteger(const uint32_t * value,uint32_t & cpp,const uint32_t * & c)258 Tags::c_setInteger( const uint32_t* value, uint32_t& cpp, const uint32_t*& c )
259 {
260     if( !value ) {
261         cpp = 0;
262         c = NULL;
263     }
264     else {
265         cpp = *value;
266         c = &cpp;
267     }
268 }
269 
270 ///////////////////////////////////////////////////////////////////////////////
271 
272 void
c_setInteger(const uint64_t * value,uint64_t & cpp,const uint64_t * & c)273 Tags::c_setInteger( const uint64_t* value, uint64_t& cpp, const uint64_t*& c )
274 {
275     if( !value ) {
276         cpp = 0;
277         c = NULL;
278     }
279     else {
280         cpp = *value;
281         c = &cpp;
282     }
283 }
284 
285 ///////////////////////////////////////////////////////////////////////////////
286 
287 void
c_setString(const char * value,string & cpp,const char * & c)288 Tags::c_setString( const char* value, string& cpp, const char*& c )
289 {
290     if( !value ) {
291         cpp.clear();
292         c = NULL;
293     }
294     else {
295         cpp = value;
296         c = cpp.c_str();
297     }
298 }
299 
300 ///////////////////////////////////////////////////////////////////////////////
301 
302 void
c_setTrack(const MP4TagTrack * value,MP4TagTrack & cpp,const MP4TagTrack * & c)303 Tags::c_setTrack( const MP4TagTrack* value, MP4TagTrack& cpp, const MP4TagTrack*& c )
304 {
305     if( !value ) {
306         cpp.index = 0;
307         cpp.total = 0;
308         c = NULL;
309     }
310     else {
311         cpp.index = value->index;
312         cpp.total = value->total;
313         c = &cpp;
314     }
315 }
316 
317 ///////////////////////////////////////////////////////////////////////////////
318 
319 void
c_setDisk(const MP4TagDisk * value,MP4TagDisk & cpp,const MP4TagDisk * & c)320 Tags::c_setDisk( const MP4TagDisk* value, MP4TagDisk& cpp, const MP4TagDisk*& c )
321 {
322     if( !value ) {
323         cpp.index = 0;
324         cpp.total = 0;
325         c = NULL;
326     }
327     else {
328         cpp.index = value->index;
329         cpp.total = value->total;
330         c = &cpp;
331     }
332 }
333 
334 ///////////////////////////////////////////////////////////////////////////////
335 
336 void
c_store(MP4Tags * & tags,MP4FileHandle hFile)337 Tags::c_store( MP4Tags*& tags, MP4FileHandle hFile )
338 {
339     MP4Tags& c = *tags;
340     MP4File& file = *static_cast<MP4File*>(hFile);
341 
342     storeString(  file, CODE_NAME,              name,              c.name );
343     storeString(  file, CODE_ARTIST,            artist,            c.artist );
344     storeString(  file, CODE_ALBUMARTIST,       albumArtist,       c.albumArtist );
345     storeString(  file, CODE_ALBUM,             album,             c.album );
346     storeString(  file, CODE_GROUPING,          grouping,          c.grouping );
347     storeString(  file, CODE_COMPOSER,          composer,          c.composer );
348     storeString(  file, CODE_COMMENTS,          comments,          c.comments );
349 
350     storeString(  file, CODE_GENRE,             genre,             c.genre );
351     storeGenre(   file,                         genreType,         c.genreType );
352 
353     storeString(  file, CODE_RELEASEDATE,       releaseDate,       c.releaseDate );
354     storeTrack(   file,                         track,             c.track );
355     storeDisk(    file,                         disk,              c.disk );
356     storeInteger( file, CODE_TEMPO,             tempo,             c.tempo );
357     storeInteger( file, CODE_COMPILATION,       compilation,       c.compilation );
358 
359     storeString(  file, CODE_TVSHOW,            tvShow,            c.tvShow );
360     storeString(  file, CODE_TVNETWORK,         tvNetwork,         c.tvNetwork );
361     storeString(  file, CODE_TVEPISODEID,       tvEpisodeID,       c.tvEpisodeID );
362     storeInteger( file, CODE_TVSEASON,          tvSeason,          c.tvSeason );
363     storeInteger( file, CODE_TVEPISODE,         tvEpisode,         c.tvEpisode );
364 
365     storeString(  file, CODE_SORTNAME,          sortName,          c.sortName );
366     storeString(  file, CODE_SORTARTIST,        sortArtist,        c.sortArtist );
367     storeString(  file, CODE_SORTALBUMARTIST,   sortAlbumArtist,   c.sortAlbumArtist );
368     storeString(  file, CODE_SORTALBUM,         sortAlbum,         c.sortAlbum );
369     storeString(  file, CODE_SORTCOMPOSER,      sortComposer,      c.sortComposer );
370     storeString(  file, CODE_SORTTVSHOW,        sortTVShow,        c.sortTVShow );
371 
372     storeString(  file, CODE_DESCRIPTION,       description,       c.description );
373     storeString(  file, CODE_LONGDESCRIPTION,   longDescription,   c.longDescription );
374     storeString(  file, CODE_LYRICS,            lyrics,            c.lyrics );
375 
376     storeString(  file, CODE_COPYRIGHT,         copyright,         c.copyright );
377     storeString(  file, CODE_ENCODINGTOOL,      encodingTool,      c.encodingTool );
378     storeString(  file, CODE_ENCODEDBY,         encodedBy,         c.encodedBy );
379     storeString(  file, CODE_PURCHASEDATE,      purchaseDate,      c.purchaseDate );
380 
381     storeInteger( file, CODE_PODCAST,           podcast,           c.podcast );
382     storeString(  file, CODE_KEYWORDS,          keywords,          c.keywords );
383     storeString(  file, CODE_CATEGORY,          category,          c.category );
384 
385     storeInteger( file, CODE_HDVIDEO,           hdVideo,           c.hdVideo );
386     storeInteger( file, CODE_MEDIATYPE,         mediaType,         c.mediaType );
387     storeInteger( file, CODE_CONTENTRATING,     contentRating,     c.contentRating );
388     storeInteger( file, CODE_GAPLESS,           gapless,           c.gapless );
389 
390     storeString(  file, CODE_ITUNESACCOUNT,     iTunesAccount,     c.iTunesAccount );
391     storeInteger( file, CODE_ITUNESACCOUNTTYPE, iTunesAccountType, c.iTunesAccountType );
392     storeInteger( file, CODE_ITUNESCOUNTRY,     iTunesCountry,     c.iTunesCountry );
393 
394     storeInteger( file, CODE_CONTENTID,         contentID,         c.contentID );
395     storeInteger( file, CODE_ARTISTID,          artistID,          c.artistID );
396     storeInteger( file, CODE_PLAYLISTID,        playlistID,        c.playlistID );
397     storeInteger( file, CODE_GENREID,           genreID,           c.genreID );
398     storeInteger( file, CODE_COMPOSERID,        composerID,        c.composerID );
399     storeString(  file, CODE_XID,               xid,               c.xid );
400 
401     // destroy all cover-art then add each
402     {
403         CoverArtBox::remove( hFile );
404         const CoverArtBox::ItemList::size_type max = artwork.size();
405         for( CoverArtBox::ItemList::size_type i = 0; i < max; i++ )
406             CoverArtBox::add( hFile, artwork[i] );
407     }
408 }
409 
410 ///////////////////////////////////////////////////////////////////////////////
411 
412 void
fetchGenre(const CodeItemMap & cim,uint16_t & cpp,const uint16_t * & c)413 Tags::fetchGenre( const CodeItemMap& cim, uint16_t& cpp, const uint16_t*& c )
414 {
415     cpp = 0;
416     c = NULL;
417 
418     CodeItemMap::const_iterator f = cim.find( CODE_GENRETYPE );
419     if( f == cim.end() || 0 == f->second->dataList.size )
420         return;
421 
422     MP4ItmfData& data = f->second->dataList.elements[0];
423     if( NULL == data.value )
424         return;
425 
426     cpp = (uint16_t(data.value[0]) <<  8)
427         | (uint16_t(data.value[1])      );
428 
429     c = &cpp;
430 }
431 
432 ///////////////////////////////////////////////////////////////////////////////
433 
434 void
fetchDisk(const CodeItemMap & cim,MP4TagDisk & cpp,const MP4TagDisk * & c)435 Tags::fetchDisk( const CodeItemMap& cim, MP4TagDisk& cpp, const MP4TagDisk*& c )
436 {
437     cpp.index = 0;
438     cpp.total = 0;
439     c = NULL;
440 
441     CodeItemMap::const_iterator f = cim.find( CODE_DISK );
442     if( f == cim.end() || 0 == f->second->dataList.size )
443         return;
444 
445     MP4ItmfData& data = f->second->dataList.elements[0];
446 
447     if( NULL == data.value )
448         return;
449 
450     cpp.index = (uint16_t(data.value[2]) <<  8)
451               | (uint16_t(data.value[3])      );
452 
453     cpp.total = (uint16_t(data.value[4]) <<  8)
454               | (uint16_t(data.value[5])      );
455 
456     c = &cpp;
457 }
458 
459 ///////////////////////////////////////////////////////////////////////////////
460 
461 void
fetchTrack(const CodeItemMap & cim,MP4TagTrack & cpp,const MP4TagTrack * & c)462 Tags::fetchTrack( const CodeItemMap& cim, MP4TagTrack& cpp, const MP4TagTrack*& c )
463 {
464     cpp.index = 0;
465     cpp.total = 0;
466     c = NULL;
467 
468     CodeItemMap::const_iterator f = cim.find( CODE_TRACK );
469     if( f == cim.end() || 0 == f->second->dataList.size )
470         return;
471 
472     MP4ItmfData& data = f->second->dataList.elements[0];
473 
474     if( NULL == data.value )
475         return;
476 
477     cpp.index = (uint16_t(data.value[2]) <<  8)
478               | (uint16_t(data.value[3])      );
479 
480     cpp.total = (uint16_t(data.value[4]) <<  8)
481               | (uint16_t(data.value[5])      );
482 
483     c = &cpp;
484 }
485 
486 ///////////////////////////////////////////////////////////////////////////////
487 
488 void
fetchInteger(const CodeItemMap & cim,const string & code,uint8_t & cpp,const uint8_t * & c)489 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint8_t& cpp, const uint8_t*& c )
490 {
491     cpp = 0;
492     c = NULL;
493 
494     CodeItemMap::const_iterator f = cim.find( code );
495     if( f == cim.end() || 0 == f->second->dataList.size )
496         return;
497 
498     MP4ItmfData& data = f->second->dataList.elements[0];
499     if( NULL == data.value )
500         return;
501 
502     cpp = data.value[0];
503     c = &cpp;
504 }
505 
506 ///////////////////////////////////////////////////////////////////////////////
507 
508 void
fetchInteger(const CodeItemMap & cim,const string & code,uint16_t & cpp,const uint16_t * & c)509 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint16_t& cpp, const uint16_t*& c )
510 {
511     cpp = 0;
512     c = NULL;
513 
514     CodeItemMap::const_iterator f = cim.find( code );
515     if( f == cim.end() || 0 == f->second->dataList.size )
516         return;
517 
518     MP4ItmfData& data = f->second->dataList.elements[0];
519 
520     if( NULL == data.value )
521         return;
522 
523     cpp = (uint16_t(data.value[0]) <<  8)
524         | (uint16_t(data.value[1])      );
525 
526     c = &cpp;
527 }
528 
529 ///////////////////////////////////////////////////////////////////////////////
530 
531 void
fetchInteger(const CodeItemMap & cim,const string & code,uint32_t & cpp,const uint32_t * & c)532 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint32_t& cpp, const uint32_t*& c )
533 {
534     cpp = 0;
535     c = NULL;
536 
537     CodeItemMap::const_iterator f = cim.find( code );
538     if( f == cim.end() || 0 == f->second->dataList.size )
539         return;
540 
541     MP4ItmfData& data = f->second->dataList.elements[0];
542 
543     if( NULL == data.value )
544         return;
545 
546     cpp = (uint32_t(data.value[0]) << 24)
547         | (uint32_t(data.value[1]) << 16)
548         | (uint32_t(data.value[2]) <<  8)
549         | (uint32_t(data.value[3])      );
550 
551     c = &cpp;
552 }
553 
554 ///////////////////////////////////////////////////////////////////////////////
555 
556 void
fetchInteger(const CodeItemMap & cim,const string & code,uint64_t & cpp,const uint64_t * & c)557 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint64_t& cpp, const uint64_t*& c )
558 {
559     cpp = 0;
560     c = NULL;
561 
562     CodeItemMap::const_iterator f = cim.find( code );
563     if( f == cim.end() || 0 == f->second->dataList.size )
564         return;
565 
566     MP4ItmfData& data = f->second->dataList.elements[0];
567 
568     if( NULL == data.value )
569         return;
570 
571     cpp = (uint64_t(data.value[0]) << 56)
572         | (uint64_t(data.value[1]) << 48)
573         | (uint64_t(data.value[2]) << 40)
574         | (uint64_t(data.value[3]) << 32)
575         | (uint64_t(data.value[4]) << 24)
576         | (uint64_t(data.value[5]) << 16)
577         | (uint64_t(data.value[6]) <<  8)
578         | (uint64_t(data.value[7])      );
579 
580     c = &cpp;
581 }
582 
583 ///////////////////////////////////////////////////////////////////////////////
584 
585 void
fetchString(const CodeItemMap & cim,const string & code,string & cpp,const char * & c)586 Tags::fetchString( const CodeItemMap& cim, const string& code, string& cpp, const char*& c )
587 {
588     cpp.clear();
589     c = NULL;
590 
591     CodeItemMap::const_iterator f = cim.find( code );
592     if( f == cim.end() || 0 == f->second->dataList.size )
593         return;
594 
595     MP4ItmfData& data = f->second->dataList.elements[0];
596 
597     if( NULL == data.value )
598         return;
599 
600     cpp.append( reinterpret_cast<char*>( data.value ), data.valueSize );
601     c = cpp.c_str();
602 }
603 
604 ///////////////////////////////////////////////////////////////////////////////
605 
606 void
remove(MP4File & file,const string & code)607 Tags::remove( MP4File& file, const string& code )
608 {
609     MP4ItmfItemList* itemList = genericGetItemsByCode( file, code ); // alloc
610 
611     if( itemList->size )
612         genericRemoveItem( file, &itemList->elements[0] );
613 
614     genericItemListFree( itemList ); // free
615 }
616 
617 ///////////////////////////////////////////////////////////////////////////////
618 
619 void
store(MP4File & file,const string & code,MP4ItmfBasicType basicType,const void * buffer,uint32_t size)620 Tags::store( MP4File& file, const string& code, MP4ItmfBasicType basicType, const void* buffer, uint32_t size )
621 {
622     // remove existing item
623     remove( file, code );
624 
625     // add item
626     MP4ItmfItem& item = *genericItemAlloc( code, 1 ); // alloc
627     MP4ItmfData& data = item.dataList.elements[0];
628 
629     data.typeCode = basicType;
630     data.valueSize = size;
631     data.value = (uint8_t*)malloc( data.valueSize );
632     memcpy( data.value, buffer, data.valueSize );
633 
634     genericAddItem( file, &item );
635     genericItemFree( &item ); // free
636 }
637 
638 ///////////////////////////////////////////////////////////////////////////////
639 
640 void
storeGenre(MP4File & file,uint16_t cpp,const uint16_t * c)641 Tags::storeGenre( MP4File& file, uint16_t cpp, const uint16_t* c )
642 {
643     if( c ) {
644         uint8_t buf[2];
645 
646         buf[0] = uint8_t((cpp & 0xff00) >> 8);
647         buf[1] = uint8_t((cpp & 0x00ff)     );
648 
649         // it's not clear if you must use implicit in these situations and iirc iTunes and other software are not consistent in this regard.
650         // many other tags must be integer type yet no issues there. Silly that iTunes insists it must be implict, which is then hardcoded
651         // to interpret as genres anyways.
652         store( file, CODE_GENRETYPE, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
653     }
654     else {
655         remove( file, CODE_GENRETYPE );
656     }
657 }
658 
659 ///////////////////////////////////////////////////////////////////////////////
660 
661 void
storeDisk(MP4File & file,const MP4TagDisk & cpp,const MP4TagDisk * c)662 Tags::storeDisk( MP4File& file, const MP4TagDisk& cpp, const MP4TagDisk* c )
663 {
664     if( c ) {
665         uint8_t buf[6];
666         memset( buf, 0, sizeof(buf) );
667 
668         buf[2] = uint8_t((cpp.index & 0xff00) >> 8);
669         buf[3] = uint8_t((cpp.index & 0x00ff)     );
670         buf[4] = uint8_t((cpp.total & 0xff00) >> 8);
671         buf[5] = uint8_t((cpp.total & 0x00ff)     );
672 
673         store( file, CODE_DISK, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
674     }
675     else {
676         remove( file, CODE_DISK );
677     }
678 }
679 
680 ///////////////////////////////////////////////////////////////////////////////
681 
682 void
storeTrack(MP4File & file,const MP4TagTrack & cpp,const MP4TagTrack * c)683 Tags::storeTrack( MP4File& file, const MP4TagTrack& cpp, const MP4TagTrack* c )
684 {
685     if( c ) {
686         uint8_t buf[8]; // iTMF spec says 7 but iTunes media is 8
687         memset( buf, 0, sizeof(buf) );
688 
689         buf[2] = uint8_t((cpp.index & 0xff00) >> 8);
690         buf[3] = uint8_t((cpp.index & 0x00ff)     );
691         buf[4] = uint8_t((cpp.total & 0xff00) >> 8);
692         buf[5] = uint8_t((cpp.total & 0x00ff)     );
693 
694         store( file, CODE_TRACK, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
695     }
696     else {
697         remove( file, CODE_TRACK );
698     }
699 }
700 
701 ///////////////////////////////////////////////////////////////////////////////
702 
703 void
storeInteger(MP4File & file,const string & code,uint8_t cpp,const uint8_t * c)704 Tags::storeInteger( MP4File& file, const string& code, uint8_t cpp, const uint8_t* c )
705 {
706     if( c )
707         store( file, code, MP4_ITMF_BT_INTEGER, &cpp, sizeof(cpp) );
708     else
709         remove( file, code );
710 }
711 
712 ///////////////////////////////////////////////////////////////////////////////
713 
714 void
storeInteger(MP4File & file,const string & code,uint16_t cpp,const uint16_t * c)715 Tags::storeInteger( MP4File& file, const string& code, uint16_t cpp, const uint16_t* c )
716 {
717     if( c ) {
718         uint8_t buf[2];
719 
720         buf[0] = uint8_t((cpp & 0xff00) >> 8);
721         buf[1] = uint8_t((cpp & 0x00ff)     );
722 
723         store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
724     }
725     else {
726         remove( file, code );
727     }
728 }
729 
730 
731 ///////////////////////////////////////////////////////////////////////////////
732 
733 void
storeInteger(MP4File & file,const string & code,uint32_t cpp,const uint32_t * c)734 Tags::storeInteger( MP4File& file, const string& code, uint32_t cpp, const uint32_t* c )
735 {
736     if( c ) {
737         uint8_t buf[4];
738 
739         buf[0] = uint8_t((cpp & 0xff000000) >> 24 );
740         buf[1] = uint8_t((cpp & 0x00ff0000) >> 16 );
741         buf[2] = uint8_t((cpp & 0x0000ff00) >>  8 );
742         buf[3] = uint8_t((cpp & 0x000000ff)       );
743 
744         store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
745     }
746     else {
747         remove( file, code );
748     }
749 }
750 
751 ///////////////////////////////////////////////////////////////////////////////
752 
753 void
storeInteger(MP4File & file,const string & code,uint64_t cpp,const uint64_t * c)754 Tags::storeInteger( MP4File& file, const string& code, uint64_t cpp, const uint64_t* c )
755 {
756     if( c ) {
757         uint8_t buf[8];
758 
759         buf[0] = uint8_t((cpp & 0xff00000000000000LL) >> 56 );
760         buf[1] = uint8_t((cpp & 0x00ff000000000000LL) >> 48 );
761         buf[2] = uint8_t((cpp & 0x0000ff0000000000LL) >> 40 );
762         buf[3] = uint8_t((cpp & 0x000000ff00000000LL) >> 32 );
763         buf[4] = uint8_t((cpp & 0x00000000ff000000LL) >> 24 );
764         buf[5] = uint8_t((cpp & 0x0000000000ff0000LL) >> 16 );
765         buf[6] = uint8_t((cpp & 0x000000000000ff00LL) >>  8 );
766         buf[7] = uint8_t((cpp & 0x00000000000000ffLL)       );
767 
768         store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
769     }
770     else {
771         remove( file, code );
772     }
773 }
774 
775 ///////////////////////////////////////////////////////////////////////////////
776 
777 void
storeString(MP4File & file,const string & code,const string & cpp,const char * c)778 Tags::storeString( MP4File& file, const string& code, const string& cpp, const char* c )
779 {
780     if( c )
781         store( file, code, MP4_ITMF_BT_UTF8, cpp.c_str(), (uint32_t)cpp.size() );
782     else
783         remove( file, code );
784 }
785 
786 ///////////////////////////////////////////////////////////////////////////////
787 
788 void
updateArtworkShadow(MP4Tags * & tags)789 Tags::updateArtworkShadow( MP4Tags*& tags )
790 {
791     if( tags->artwork ) {
792         delete[] tags->artwork;
793         tags->artwork = NULL;
794         tags->artworkCount = 0;
795     }
796 
797     if( artwork.empty() )
798         return;
799 
800     MP4TagArtwork* const cartwork = new MP4TagArtwork[ artwork.size() ];
801     uint32_t max = (uint32_t)artwork.size();
802 
803     for( uint32_t i = 0; i < max; i++ ) {
804         MP4TagArtwork& a = cartwork[i];
805         CoverArtBox::Item& item = artwork[i];
806 
807         a.data = item.buffer;
808         a.size = item.size;
809 
810         switch( item.type ) {
811             case BT_BMP:
812                 a.type = MP4_ART_BMP;
813                 break;
814 
815             case BT_GIF:
816                 a.type = MP4_ART_GIF;
817                 break;
818 
819             case BT_JPEG:
820                 a.type = MP4_ART_JPEG;
821                 break;
822 
823             case BT_PNG:
824                 a.type = MP4_ART_PNG;
825                 break;
826 
827             default:
828                 a.type = MP4_ART_UNDEFINED;
829                 break;
830         }
831     }
832 
833     tags->artwork      = cartwork;
834     tags->artworkCount = max;
835 }
836 
837 ///////////////////////////////////////////////////////////////////////////////
838 
839 const string Tags::CODE_NAME              = "\xa9" "nam";
840 const string Tags::CODE_ARTIST            = "\xa9" "ART";
841 const string Tags::CODE_ALBUMARTIST       = "aART";
842 const string Tags::CODE_ALBUM             = "\xa9" "alb";
843 const string Tags::CODE_GROUPING          = "\xa9" "grp";
844 const string Tags::CODE_COMPOSER          = "\xa9" "wrt";
845 const string Tags::CODE_COMMENTS          = "\xa9" "cmt";
846 const string Tags::CODE_GENRE             = "\xa9" "gen";
847 const string Tags::CODE_GENRETYPE         = "gnre";
848 const string Tags::CODE_RELEASEDATE       = "\xa9" "day";
849 const string Tags::CODE_TRACK             = "trkn";
850 const string Tags::CODE_DISK              = "disk";
851 const string Tags::CODE_TEMPO             = "tmpo";
852 const string Tags::CODE_COMPILATION       = "cpil";
853 
854 const string Tags::CODE_TVSHOW            = "tvsh";
855 const string Tags::CODE_TVNETWORK         = "tvnn";
856 const string Tags::CODE_TVEPISODEID       = "tven";
857 const string Tags::CODE_TVSEASON          = "tvsn";
858 const string Tags::CODE_TVEPISODE         = "tves";
859 
860 const string Tags::CODE_DESCRIPTION       = "desc";
861 const string Tags::CODE_LONGDESCRIPTION   = "ldes";
862 const string Tags::CODE_LYRICS            = "\xa9" "lyr";
863 
864 const string Tags::CODE_SORTNAME          = "sonm";
865 const string Tags::CODE_SORTARTIST        = "soar";
866 const string Tags::CODE_SORTALBUMARTIST   = "soaa";
867 const string Tags::CODE_SORTALBUM         = "soal";
868 const string Tags::CODE_SORTCOMPOSER      = "soco";
869 const string Tags::CODE_SORTTVSHOW        = "sosn";
870 
871 const string Tags::CODE_COPYRIGHT         = "cprt";
872 const string Tags::CODE_ENCODINGTOOL      = "\xa9" "too";
873 const string Tags::CODE_ENCODEDBY         = "\xa9" "enc";
874 const string Tags::CODE_PURCHASEDATE      = "purd";
875 
876 const string Tags::CODE_PODCAST           = "pcst";
877 const string Tags::CODE_KEYWORDS          = "keyw";
878 const string Tags::CODE_CATEGORY          = "catg";
879 
880 const string Tags::CODE_HDVIDEO           = "hdvd";
881 const string Tags::CODE_MEDIATYPE         = "stik";
882 const string Tags::CODE_CONTENTRATING     = "rtng";
883 const string Tags::CODE_GAPLESS           = "pgap";
884 
885 const string Tags::CODE_ITUNESACCOUNT     = "apID";
886 const string Tags::CODE_ITUNESACCOUNTTYPE = "akID";
887 const string Tags::CODE_ITUNESCOUNTRY     = "sfID";
888 const string Tags::CODE_CONTENTID         = "cnID";
889 const string Tags::CODE_ARTISTID          = "atID";
890 const string Tags::CODE_PLAYLISTID        = "plID";
891 const string Tags::CODE_GENREID           = "geID";
892 const string Tags::CODE_COMPOSERID        = "cmID";
893 const string Tags::CODE_XID               = "xid ";
894 
895 ///////////////////////////////////////////////////////////////////////////////
896 
897 }}} // namespace mp4v2::impl::itmf
898