3
#include "QCDTagsDLL.h"
6
//..............................................................................
15
typedef struct medialib_tags
21
int tag_add_field(medialib_tags *tags, const char *item, const char *value)
23
void *backup = (void *)tags->tags;
25
if (!item || (item && !*item) || !value) return 0;
27
tags->tags = (struct tag *)realloc(tags->tags, (tags->count+1) * sizeof(tag));
29
if (backup) free(backup);
34
int i_len = strlen(item);
35
int v_len = strlen(value);
37
tags->tags[tags->count].item = (char *)malloc(i_len+1);
38
tags->tags[tags->count].value = (char *)malloc(v_len+1);
40
if (!tags->tags[tags->count].item || !tags->tags[tags->count].value)
42
if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item);
43
if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value);
44
tags->tags[tags->count].item = NULL;
45
tags->tags[tags->count].value = NULL;
49
memcpy(tags->tags[tags->count].item, item, i_len);
50
memcpy(tags->tags[tags->count].value, value, v_len);
51
tags->tags[tags->count].item[i_len] = '\0';
52
tags->tags[tags->count].value[v_len] = '\0';
59
int tag_set_field(medialib_tags *tags, const char *item, const char *value)
63
if (!item || (item && !*item) || !value) return 0;
65
for (i = 0; i < tags->count; i++)
67
if (!stricmp(tags->tags[i].item, item))
69
void *backup = (void *)tags->tags[i].value;
70
int v_len = strlen(value);
72
tags->tags[i].value = (char *)realloc(tags->tags[i].value, v_len+1);
73
if (!tags->tags[i].value)
75
if (backup) free(backup);
79
memcpy(tags->tags[i].value, value, v_len);
80
tags->tags[i].value[v_len] = '\0';
86
return tag_add_field(tags, item, value);
89
void tag_delete(medialib_tags *tags)
93
for (i = 0; i < tags->count; i++)
95
if (tags->tags[i].item) free(tags->tags[i].item);
96
if (tags->tags[i].value) free(tags->tags[i].value);
99
if (tags->tags) free(tags->tags);
105
int ReadMP4Tag(MP4FileHandle file, medialib_tags *tags)
107
unsigned __int32 valueSize;
108
unsigned __int8 *pValue;
117
MP4GetMetadataByIndex(file, i, (const char **)&pName, &pValue, &valueSize);
121
char *val = (char *)malloc(valueSize+1);
123
memcpy(val, pValue, valueSize);
124
val[valueSize] = '\0';
126
if (pName[0] == '\xa9')
128
if (memcmp(pName, "ļæ½nam", 4) == 0)
130
tag_add_field(tags, "title", val);
131
} else if (memcmp(pName, "ļæ½ART", 4) == 0) {
132
tag_add_field(tags, "artist", val);
133
} else if (memcmp(pName, "ļæ½wrt", 4) == 0) {
134
tag_add_field(tags, "writer", val);
135
} else if (memcmp(pName, "ļæ½alb", 4) == 0) {
136
tag_add_field(tags, "album", val);
137
} else if (memcmp(pName, "ļæ½day", 4) == 0) {
138
tag_add_field(tags, "date", val);
139
} else if (memcmp(pName, "ļæ½too", 4) == 0) {
140
tag_add_field(tags, "tool", val);
141
} else if (memcmp(pName, "ļæ½cmt", 4) == 0) {
142
tag_add_field(tags, "comment", val);
143
} else if (memcmp(pName, "ļæ½gen", 4) == 0) {
144
tag_add_field(tags, "genre", val);
146
tag_add_field(tags, pName, val);
148
} else if (memcmp(pName, "gnre", 4) == 0) {
150
if (MP4GetMetadataGenre(file, &t))
152
tag_add_field(tags, "genre", t);
154
} else if (memcmp(pName, "trkn", 4) == 0) {
155
unsigned __int16 trkn = 0, tot = 0;
157
if (MP4GetMetadataTrack(file, &trkn, &tot))
160
wsprintf(t, "%d/%d", trkn, tot);
162
wsprintf(t, "%d", trkn);
163
tag_add_field(tags, "tracknumber", t);
165
} else if (memcmp(pName, "disk", 4) == 0) {
166
unsigned __int16 disk = 0, tot = 0;
168
if (MP4GetMetadataDisk(file, &disk, &tot))
171
wsprintf(t, "%d/%d", disk, tot);
173
wsprintf(t, "%d", disk);
174
tag_add_field(tags, "disc", t);
176
} else if (memcmp(pName, "cpil", 4) == 0) {
177
unsigned __int8 cpil = 0;
179
if (MP4GetMetadataCompilation(file, &cpil))
181
wsprintf(t, "%d", cpil);
182
tag_add_field(tags, "compilation", t);
184
} else if (memcmp(pName, "tmpo", 4) == 0) {
185
unsigned __int16 tempo = 0;
187
if (MP4GetMetadataTempo(file, &tempo))
189
wsprintf(t, "%d BPM", tempo);
190
tag_add_field(tags, "tempo", t);
192
} else if (memcmp(pName, "NDFL", 4) == 0) {
195
tag_add_field(tags, pName, val);
202
} while (valueSize > 0);
207
int mp4_set_metadata(MP4FileHandle file, const char *item, const char *val)
209
if (!item || (item && !*item) || !val || (val && !*val)) return 0;
211
if (!stricmp(item, "track") || !stricmp(item, "tracknumber"))
213
unsigned __int16 trkn, tot;
215
sscanf(val, "%d/%d", &t1, &t2);
218
if (MP4SetMetadataTrack(file, trkn, tot)) return 1;
220
else if (!stricmp(item, "disc") || !stricmp(item, "disknumber"))
222
unsigned __int16 disk, tot;
224
sscanf(val, "%d/%d", &t1, &t2);
227
if (MP4SetMetadataDisk(file, disk, tot)) return 1;
229
else if (!stricmp(item, "compilation"))
231
unsigned __int8 cpil = atoi(val);
233
if (MP4SetMetadataCompilation(file, cpil)) return 1;
235
else if (!stricmp(item, "tempo"))
237
unsigned __int16 tempo = atoi(val);
238
if (!tempo) return 1;
239
if (MP4SetMetadataTempo(file, tempo)) return 1;
241
else if (!stricmp(item, "artist"))
243
if (MP4SetMetadataArtist(file, val)) return 1;
245
else if (!stricmp(item, "writer"))
247
if (MP4SetMetadataWriter(file, val)) return 1;
249
else if (!stricmp(item, "title"))
251
if (MP4SetMetadataName(file, val)) return 1;
253
else if (!stricmp(item, "album"))
255
if (MP4SetMetadataAlbum(file, val)) return 1;
257
else if (!stricmp(item, "date") || !stricmp(item, "year"))
259
if (MP4SetMetadataYear(file, val)) return 1;
261
else if (!stricmp(item, "comment"))
263
if (MP4SetMetadataComment(file, val)) return 1;
265
else if (!stricmp(item, "genre"))
267
if (MP4SetMetadataGenre(file, val)) return 1;
269
else if (!stricmp(item, "tool"))
271
if (MP4SetMetadataTool(file, val)) return 1;
275
if (MP4SetMetadataFreeForm(file, (char *)item, (u_int8_t *)val, (u_int32_t)strlen(val) + 1)) return 1;
281
void WriteMP4Tag(MP4FileHandle file, const medialib_tags *tags)
285
for (i = 0; i < tags->count; i++)
287
const char *item = tags->tags[i].item;
288
const char *value = tags->tags[i].value;
292
mp4_set_metadata(file, item, value);
297
QCDModInitTag ModInitTag;
301
BOOL uSetDlgItemText(void *tagHandle, int fieldId, const char *str);
302
UINT uGetDlgItemText(void *tagHandle, int fieldId, char *str, int max);
304
//------------------------------------------------------------------------------
306
PLUGIN_API QCDModInitTag* TAGEDITORDLL_ENTRY_POINT()
308
ModInitTag.size = sizeof(QCDModInitTag);
309
ModInitTag.version = PLUGIN_API_VERSION;
310
ModInitTag.ShutDown = ShutDown_Tag;
312
ModInitTag.Read = Read_Tag;
313
ModInitTag.Write = Write_Tag; // Leave null for operations that plugin does not support
314
ModInitTag.Strip = Strip_Tag; // ie: if plugin only reads tags, leave Write and Strip null
316
ModInitTag.description = "MP4 Tags";
317
ModInitTag.defaultexts = "MP4:M4A";
322
//-----------------------------------------------------------------------------
324
void ShutDown_Tag(int flags)
327
// prepare plugin to be unloaded. All allocations should be freed.
328
// flags param is unused
332
//-----------------------------------------------------------------------------
334
bool Read_Tag(LPCSTR filename, void* tagHandle)
337
// read metadata from tag and set each field to tagHandle
338
// only TAGFIELD_* are supported (see QCDModTagEditor.h)
340
// example of how to set value to tagHandle
341
// use SetFieldA for ASCII or MultiBytes strings.
342
// use SetFieldW for UNICODE strings
344
// ModInitTag.SetFieldW(tagHandle, TAGFIELD_COMPOSER, szwValue);
346
// return true for successfull read, false for failure
348
MP4FileHandle file = MP4_INVALID_FILE_HANDLE;
349
char *pVal, dummy1[1024];
351
u_int32_t valueSize = 0;
354
in_mp4_DebugOutput("mp4_tag_read");
357
file = MP4Read(filename, 0);
359
if (file == MP4_INVALID_FILE_HANDLE)
365
MP4GetMetadataName(file, &pVal);
366
uSetDlgItemText(tagHandle, TAGFIELD_TITLE, pVal);
369
MP4GetMetadataArtist(file, &pVal);
370
uSetDlgItemText(tagHandle, TAGFIELD_ARTIST, pVal);
373
MP4GetMetadataWriter(file, &pVal);
374
uSetDlgItemText(tagHandle, TAGFIELD_COMPOSER, pVal);
377
MP4GetMetadataComment(file, &pVal);
378
uSetDlgItemText(tagHandle, TAGFIELD_COMMENT, pVal);
381
MP4GetMetadataAlbum(file, &pVal);
382
uSetDlgItemText(tagHandle, TAGFIELD_ALBUM, pVal);
385
MP4GetMetadataGenre(file, &pVal);
386
uSetDlgItemText(tagHandle, TAGFIELD_GENRE, pVal);
389
//MP4GetMetadataTempo(file, &dummy);
392
// wsprintf(dummy1, "%d", dummy);
393
// SetDlgItemText(hwndDlg,IDC_METATEMPO, dummy1);
396
dummy = 0; dummy2 = 0;
397
MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2);
400
wsprintf(dummy1, "%d", dummy);
401
ModInitTag.SetFieldA(tagHandle, TAGFIELD_TRACK, dummy1);
405
// wsprintf(dummy1, "%d", dummy2);
406
// SetDlgItemText(hwndDlg,IDC_METATRACK2, dummy1);
409
//dummy = 0; dummy2 = 0;
410
//MP4GetMetadataDisk(file, &dummy, &dummy2);
413
// wsprintf(dummy1, "%d", dummy);
414
// SetDlgItemText(hwndDlg,IDC_METADISK1, dummy1);
418
// wsprintf(dummy1, "%d", dummy2);
419
// SetDlgItemText(hwndDlg,IDC_METADISK2, dummy1);
423
if (MP4GetMetadataYear(file, &pVal))
424
uSetDlgItemText(tagHandle, TAGFIELD_YEAR, pVal);
427
//MP4GetMetadataCompilation(file, &dummy3);
429
// SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_SETCHECK, BST_CHECKED, 0);
432
MP4GetMetadataTool(file, &pVal);
433
uSetDlgItemText(tagHandle, TAGFIELD_ENCODER, pVal);
436
MP4GetMetadataFreeForm(file, "CONDUCTOR", (unsigned __int8**)&pVal, &valueSize);
437
uSetDlgItemText(tagHandle, TAGFIELD_CONDUCTOR, pVal);
440
MP4GetMetadataFreeForm(file, "ORCHESTRA", (unsigned __int8**)&pVal, &valueSize);
441
uSetDlgItemText(tagHandle, TAGFIELD_ORCHESTRA, pVal);
444
MP4GetMetadataFreeForm(file, "YEARCOMPOSED", (unsigned __int8**)&pVal, &valueSize);
445
uSetDlgItemText(tagHandle, TAGFIELD_YEARCOMPOSED, pVal);
448
MP4GetMetadataFreeForm(file, "ORIGARTIST", (unsigned __int8**)&pVal, &valueSize);
449
uSetDlgItemText(tagHandle, TAGFIELD_ORIGARTIST, pVal);
452
MP4GetMetadataFreeForm(file, "LABEL", (unsigned __int8**)&pVal, &valueSize);
453
uSetDlgItemText(tagHandle, TAGFIELD_LABEL, pVal);
456
MP4GetMetadataFreeForm(file, "COPYRIGHT", (unsigned __int8**)&pVal, &valueSize);
457
uSetDlgItemText(tagHandle, TAGFIELD_COPYRIGHT, pVal);
460
MP4GetMetadataFreeForm(file, "CDDBTAGID", (unsigned __int8**)&pVal, &valueSize);
461
uSetDlgItemText(tagHandle, TAGFIELD_CDDBTAGID, pVal);
470
//-----------------------------------------------------------------------------
472
bool Write_Tag(LPCSTR filename, void* tagHandle)
475
// read metadata from tagHandle and set each field to supported tag
476
// only TAGFIELD_* are supported (see QCDModTagEditor.h)
478
// example of how to get value from tagHandle
479
// use SetFieldA for ASCII or MultiBytes strings.
480
// use SetFieldW for UNICODE strings
482
// szwValue = ModInitTag.GetFieldW(tagHandle, TAGFIELD_ORCHESTRA);
486
MP4FileHandle file = MP4_INVALID_FILE_HANDLE;
492
in_mp4_DebugOutput("mp4_tag_write");
495
/* save Metadata changes */
498
file = MP4Read(filename, 0);
499
if (file != MP4_INVALID_FILE_HANDLE)
501
ReadMP4Tag(file, &tags);
504
file = MP4Modify(filename, 0, 0);
505
if (file != MP4_INVALID_FILE_HANDLE)
507
MP4MetadataDelete(file);
512
file = MP4Modify(filename, 0, 0);
513
if (file == MP4_INVALID_FILE_HANDLE)
516
//EndDialog(hwndDlg, wParam);
520
uGetDlgItemText(tagHandle, TAGFIELD_TITLE, dummy1, 1024);
521
tag_set_field(&tags, "title", dummy1);
523
uGetDlgItemText(tagHandle, TAGFIELD_COMPOSER, dummy1, 1024);
524
tag_set_field(&tags, "writer", dummy1);
526
uGetDlgItemText(tagHandle, TAGFIELD_ARTIST, dummy1, 1024);
527
tag_set_field(&tags, "artist", dummy1);
529
uGetDlgItemText(tagHandle, TAGFIELD_ALBUM, dummy1, 1024);
530
tag_set_field(&tags, "album", dummy1);
532
uGetDlgItemText(tagHandle, TAGFIELD_COMMENT, dummy1, 1024);
533
tag_set_field(&tags, "comment", dummy1);
535
uGetDlgItemText(tagHandle, TAGFIELD_GENRE, dummy1, 1024);
536
tag_set_field(&tags, "genre", dummy1);
538
uGetDlgItemText(tagHandle, TAGFIELD_YEAR, dummy1, 1024);
539
tag_set_field(&tags, "year", dummy1);
542
MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2);
543
memcpy(dummy1, ModInitTag.GetFieldA(tagHandle, TAGFIELD_TRACK), sizeof(dummy1));
544
dummy = atoi(dummy1);
545
wsprintf(temp, "%d/%d", dummy, dummy2);
546
tag_set_field(&tags, "track", temp);
548
//GetDlgItemText(hwndDlg, IDC_METADISK1, dummy1, 1024);
549
//dummy = atoi(dummy1);
550
//GetDlgItemText(hwndDlg, IDC_METADISK2, dummy1, 1024);
551
//dummy2 = atoi(dummy1);
552
//wsprintf(temp, "%d/%d", dummy, dummy2);
553
//tag_set_field(&tags, "disc", temp);
555
//GetDlgItemText(hwndDlg, IDC_METATEMPO, dummy1, 1024);
556
//tag_set_field(&tags, "tempo", dummy1);
558
//dummy3 = SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_GETCHECK, 0, 0);
559
//tag_set_field(&tags, "compilation", (dummy3 ? "1" : "0"));
561
uGetDlgItemText(tagHandle, TAGFIELD_ENCODER, dummy1, 1024);
562
tag_set_field(&tags, "tool", dummy1);
564
uGetDlgItemText(tagHandle, TAGFIELD_CONDUCTOR, dummy1, 1024);
565
tag_set_field(&tags, "CONDUCTOR", dummy1);
567
uGetDlgItemText(tagHandle, TAGFIELD_ORCHESTRA, dummy1, 1024);
568
tag_set_field(&tags, "ORCHESTRA", dummy1);
570
uGetDlgItemText(tagHandle, TAGFIELD_YEARCOMPOSED, dummy1, 1024);
571
tag_set_field(&tags, "YEARCOMPOSED", dummy1);
573
uGetDlgItemText(tagHandle, TAGFIELD_ORIGARTIST, dummy1, 1024);
574
tag_set_field(&tags, "ORIGARTIST", dummy1);
576
uGetDlgItemText(tagHandle, TAGFIELD_LABEL, dummy1, 1024);
577
tag_set_field(&tags, "LABEL", dummy1);
579
uGetDlgItemText(tagHandle, TAGFIELD_COPYRIGHT, dummy1, 1024);
580
tag_set_field(&tags, "COPYRIGHT", dummy1);
582
uGetDlgItemText(tagHandle, TAGFIELD_CDDBTAGID, dummy1, 1024);
583
tag_set_field(&tags, "CDDBTAGID", dummy1);
585
WriteMP4Tag(file, &tags);
589
MP4Optimize(filename, NULL, 0);
595
//-----------------------------------------------------------------------------
597
bool Strip_Tag(LPCSTR filename)
600
// remove tag from file.
601
// do whatever is need to remove the supported tag from filename
603
// return true for successfull strip, false for failure
607
file = MP4Modify(filename, 0, 0);
608
if (file == MP4_INVALID_FILE_HANDLE)
611
MP4MetadataDelete(file);
618
//-----------------------------------------------------------------------------
620
/* Convert UNICODE to UTF-8
621
Return number of bytes written */
622
int unicodeToUtf8 ( const WCHAR* lpWideCharStr, char* lpMultiByteStr, int cwcChars )
624
const unsigned short* pwc = (unsigned short *)lpWideCharStr;
625
unsigned char* pmb = (unsigned char *)lpMultiByteStr;
626
const unsigned short* pwce;
629
if ( cwcChars >= 0 ) {
630
pwce = pwc + cwcChars;
632
pwce = (unsigned short *)((size_t)-1);
635
while ( pwc < pwce ) {
636
unsigned short wc = *pwc++;
638
if ( wc < 0x00000080 ) {
642
if ( wc < 0x00000800 ) {
643
*pmb++ = (char)(0xC0 | ((wc >> 6) & 0x1F));
645
*pmb++ = (char)(0x80 | (wc & 0x3F));
648
if ( wc < 0x00010000 ) {
649
*pmb++ = (char)(0xE0 | ((wc >> 12) & 0x0F));
651
*pmb++ = (char)(0x80 | ((wc >> 6) & 0x3F));
653
*pmb++ = (char)(0x80 | (wc & 0x3F));
663
/* Convert UTF-8 coded string to UNICODE
664
Return number of characters converted */
665
int utf8ToUnicode ( const char* lpMultiByteStr, WCHAR* lpWideCharStr, int cmbChars )
667
const unsigned char* pmb = (unsigned char *)lpMultiByteStr;
668
unsigned short* pwc = (unsigned short *)lpWideCharStr;
669
const unsigned char* pmbe;
672
if ( cmbChars >= 0 ) {
673
pmbe = pmb + cmbChars;
675
pmbe = (unsigned char *)((size_t)-1);
678
while ( pmb < pmbe ) {
683
while ( (cc < 7) && (mb & (1 << (7 - cc)))) {
687
if ( cc == 1 || cc > 6 ) // illegal character combination for UTF-8
693
wc = (mb & ((1 << (7 - cc)) - 1)) << ((cc - 1) * 6);
695
if ( pmb == pmbe ) // reached end of the buffer
698
if ( ((mb >> 6) & 0x03) != 2 ) // not part of multibyte character
700
wc |= (mb & 0x3F) << ((cc - 1) * 6);
704
if ( wc & 0xFFFF0000 )
715
/* convert Windows ANSI to UTF-8 */
716
int ConvertANSIToUTF8 ( const char* ansi, char* utf8 )
718
WCHAR* wszValue; // Unicode value
726
ansi_len = strlen ( ansi );
728
if ( (wszValue = (WCHAR *)malloc ( (ansi_len + 1) * 2 )) == NULL )
731
/* Convert ANSI value to Unicode */
732
if ( (len = MultiByteToWideChar ( CP_ACP, 0, ansi, ansi_len + 1, wszValue, (ansi_len + 1) * 2 )) == 0 ) {
737
/* Convert Unicode value to UTF-8 */
738
if ( (len = unicodeToUtf8 ( wszValue, utf8, -1 )) == 0 ) {
748
/* convert UTF-8 to Windows ANSI */
749
int ConvertUTF8ToANSI ( const char* utf8, char* ansi )
751
WCHAR* wszValue; // Unicode value
759
utf8_len = strlen ( utf8 );
761
if ( (wszValue = (WCHAR *)malloc ( (utf8_len + 1) * 2 )) == NULL )
764
/* Convert UTF-8 value to Unicode */
765
if ( (len = utf8ToUnicode ( utf8, wszValue, utf8_len + 1 )) == 0 ) {
770
/* Convert Unicode value to ANSI */
771
if ( (len = WideCharToMultiByte ( CP_ACP, 0, wszValue, -1, ansi, (utf8_len + 1) * 2, NULL, NULL )) == 0 ) {
781
BOOL uSetDlgItemText(void *tagHandle, int fieldId, const char *str)
787
if (!str) return FALSE;
788
if (!*str) return FALSE;
790
temp = (char *)malloc(len+1);
791
if (!temp) return FALSE;
792
memset(temp, '\0', len+1);
793
r = ConvertUTF8ToANSI(str, temp);
795
ModInitTag.SetFieldA(tagHandle, fieldId, temp);
798
return r>0 ? TRUE : FALSE;
801
UINT uGetDlgItemText(void *tagHandle, int fieldId, char *str, int max)
808
if (!str || !max) return 0;
809
len = strlen( ModInitTag.GetFieldA(tagHandle, fieldId) );
810
temp = (char *)malloc(len+1);
812
utf8 = (char *)malloc((len+1)*4);
819
memset(temp, '\0', len+1);
820
memset(utf8, '\0', (len+1)*4);
821
memset(str, '\0', max);
822
p = ModInitTag.GetFieldA(tagHandle, fieldId);
823
memcpy(temp, p, len+1);
826
len = ConvertANSIToUTF8(temp, utf8);
832
memcpy(str, utf8, len+1);
3
#include "QCDTagsDLL.h"
6
//..............................................................................
15
typedef struct medialib_tags
21
int tag_add_field(medialib_tags *tags, const char *item, const char *value)
23
void *backup = (void *)tags->tags;
25
if (!item || (item && !*item) || !value) return 0;
27
tags->tags = (struct tag *)realloc(tags->tags, (tags->count+1) * sizeof(tag));
29
if (backup) free(backup);
34
int i_len = strlen(item);
35
int v_len = strlen(value);
37
tags->tags[tags->count].item = (char *)malloc(i_len+1);
38
tags->tags[tags->count].value = (char *)malloc(v_len+1);
40
if (!tags->tags[tags->count].item || !tags->tags[tags->count].value)
42
if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item);
43
if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value);
44
tags->tags[tags->count].item = NULL;
45
tags->tags[tags->count].value = NULL;
49
memcpy(tags->tags[tags->count].item, item, i_len);
50
memcpy(tags->tags[tags->count].value, value, v_len);
51
tags->tags[tags->count].item[i_len] = '\0';
52
tags->tags[tags->count].value[v_len] = '\0';
59
int tag_set_field(medialib_tags *tags, const char *item, const char *value)
63
if (!item || (item && !*item) || !value) return 0;
65
for (i = 0; i < tags->count; i++)
67
if (!stricmp(tags->tags[i].item, item))
69
void *backup = (void *)tags->tags[i].value;
70
int v_len = strlen(value);
72
tags->tags[i].value = (char *)realloc(tags->tags[i].value, v_len+1);
73
if (!tags->tags[i].value)
75
if (backup) free(backup);
79
memcpy(tags->tags[i].value, value, v_len);
80
tags->tags[i].value[v_len] = '\0';
86
return tag_add_field(tags, item, value);
89
void tag_delete(medialib_tags *tags)
93
for (i = 0; i < tags->count; i++)
95
if (tags->tags[i].item) free(tags->tags[i].item);
96
if (tags->tags[i].value) free(tags->tags[i].value);
99
if (tags->tags) free(tags->tags);
105
int ReadMP4Tag(MP4FileHandle file, medialib_tags *tags)
107
unsigned __int32 valueSize;
108
unsigned __int8 *pValue;
117
MP4GetMetadataByIndex(file, i, (const char **)&pName, &pValue, &valueSize);
121
char *val = (char *)malloc(valueSize+1);
123
memcpy(val, pValue, valueSize);
124
val[valueSize] = '\0';
126
if (pName[0] == '\xa9')
128
if (memcmp(pName, "ļæ½nam", 4) == 0)
130
tag_add_field(tags, "title", val);
131
} else if (memcmp(pName, "ļæ½ART", 4) == 0) {
132
tag_add_field(tags, "artist", val);
133
} else if (memcmp(pName, "ļæ½wrt", 4) == 0) {
134
tag_add_field(tags, "writer", val);
135
} else if (memcmp(pName, "ļæ½alb", 4) == 0) {
136
tag_add_field(tags, "album", val);
137
} else if (memcmp(pName, "ļæ½day", 4) == 0) {
138
tag_add_field(tags, "date", val);
139
} else if (memcmp(pName, "ļæ½too", 4) == 0) {
140
tag_add_field(tags, "tool", val);
141
} else if (memcmp(pName, "ļæ½cmt", 4) == 0) {
142
tag_add_field(tags, "comment", val);
143
} else if (memcmp(pName, "ļæ½gen", 4) == 0) {
144
tag_add_field(tags, "genre", val);
146
tag_add_field(tags, pName, val);
148
} else if (memcmp(pName, "gnre", 4) == 0) {
150
if (MP4GetMetadataGenre(file, &t))
152
tag_add_field(tags, "genre", t);
154
} else if (memcmp(pName, "trkn", 4) == 0) {
155
unsigned __int16 trkn = 0, tot = 0;
157
if (MP4GetMetadataTrack(file, &trkn, &tot))
160
wsprintf(t, "%d/%d", trkn, tot);
162
wsprintf(t, "%d", trkn);
163
tag_add_field(tags, "tracknumber", t);
165
} else if (memcmp(pName, "disk", 4) == 0) {
166
unsigned __int16 disk = 0, tot = 0;
168
if (MP4GetMetadataDisk(file, &disk, &tot))
171
wsprintf(t, "%d/%d", disk, tot);
173
wsprintf(t, "%d", disk);
174
tag_add_field(tags, "disc", t);
176
} else if (memcmp(pName, "cpil", 4) == 0) {
177
unsigned __int8 cpil = 0;
179
if (MP4GetMetadataCompilation(file, &cpil))
181
wsprintf(t, "%d", cpil);
182
tag_add_field(tags, "compilation", t);
184
} else if (memcmp(pName, "tmpo", 4) == 0) {
185
unsigned __int16 tempo = 0;
187
if (MP4GetMetadataTempo(file, &tempo))
189
wsprintf(t, "%d BPM", tempo);
190
tag_add_field(tags, "tempo", t);
192
} else if (memcmp(pName, "NDFL", 4) == 0) {
195
tag_add_field(tags, pName, val);
202
} while (valueSize > 0);
207
int mp4_set_metadata(MP4FileHandle file, const char *item, const char *val)
209
if (!item || (item && !*item) || !val || (val && !*val)) return 0;
211
if (!stricmp(item, "track") || !stricmp(item, "tracknumber"))
213
unsigned __int16 trkn, tot;
215
sscanf(val, "%d/%d", &t1, &t2);
218
if (MP4SetMetadataTrack(file, trkn, tot)) return 1;
220
else if (!stricmp(item, "disc") || !stricmp(item, "disknumber"))
222
unsigned __int16 disk, tot;
224
sscanf(val, "%d/%d", &t1, &t2);
227
if (MP4SetMetadataDisk(file, disk, tot)) return 1;
229
else if (!stricmp(item, "compilation"))
231
unsigned __int8 cpil = atoi(val);
233
if (MP4SetMetadataCompilation(file, cpil)) return 1;
235
else if (!stricmp(item, "tempo"))
237
unsigned __int16 tempo = atoi(val);
238
if (!tempo) return 1;
239
if (MP4SetMetadataTempo(file, tempo)) return 1;
241
else if (!stricmp(item, "artist"))
243
if (MP4SetMetadataArtist(file, val)) return 1;
245
else if (!stricmp(item, "writer"))
247
if (MP4SetMetadataWriter(file, val)) return 1;
249
else if (!stricmp(item, "title"))
251
if (MP4SetMetadataName(file, val)) return 1;
253
else if (!stricmp(item, "album"))
255
if (MP4SetMetadataAlbum(file, val)) return 1;
257
else if (!stricmp(item, "date") || !stricmp(item, "year"))
259
if (MP4SetMetadataYear(file, val)) return 1;
261
else if (!stricmp(item, "comment"))
263
if (MP4SetMetadataComment(file, val)) return 1;
265
else if (!stricmp(item, "genre"))
267
if (MP4SetMetadataGenre(file, val)) return 1;
269
else if (!stricmp(item, "tool"))
271
if (MP4SetMetadataTool(file, val)) return 1;
275
if (MP4SetMetadataFreeForm(file, (char *)item, (u_int8_t *)val, (u_int32_t)strlen(val) + 1)) return 1;
281
void WriteMP4Tag(MP4FileHandle file, const medialib_tags *tags)
285
for (i = 0; i < tags->count; i++)
287
const char *item = tags->tags[i].item;
288
const char *value = tags->tags[i].value;
292
mp4_set_metadata(file, item, value);
297
QCDModInitTag ModInitTag;
301
BOOL uSetDlgItemText(void *tagHandle, int fieldId, const char *str);
302
UINT uGetDlgItemText(void *tagHandle, int fieldId, char *str, int max);
304
//------------------------------------------------------------------------------
306
PLUGIN_API QCDModInitTag* TAGEDITORDLL_ENTRY_POINT()
308
ModInitTag.size = sizeof(QCDModInitTag);
309
ModInitTag.version = PLUGIN_API_VERSION;
310
ModInitTag.ShutDown = ShutDown_Tag;
312
ModInitTag.Read = Read_Tag;
313
ModInitTag.Write = Write_Tag; // Leave null for operations that plugin does not support
314
ModInitTag.Strip = Strip_Tag; // ie: if plugin only reads tags, leave Write and Strip null
316
ModInitTag.description = "MP4 Tags";
317
ModInitTag.defaultexts = "MP4:M4A";
322
//-----------------------------------------------------------------------------
324
void ShutDown_Tag(int flags)
327
// prepare plugin to be unloaded. All allocations should be freed.
328
// flags param is unused
332
//-----------------------------------------------------------------------------
334
bool Read_Tag(LPCSTR filename, void* tagHandle)
337
// read metadata from tag and set each field to tagHandle
338
// only TAGFIELD_* are supported (see QCDModTagEditor.h)
340
// example of how to set value to tagHandle
341
// use SetFieldA for ASCII or MultiBytes strings.
342
// use SetFieldW for UNICODE strings
344
// ModInitTag.SetFieldW(tagHandle, TAGFIELD_COMPOSER, szwValue);
346
// return true for successfull read, false for failure
348
MP4FileHandle file = MP4_INVALID_FILE_HANDLE;
349
char *pVal, dummy1[1024];
351
u_int32_t valueSize = 0;
354
in_mp4_DebugOutput("mp4_tag_read");
357
file = MP4Read(filename, 0);
359
if (file == MP4_INVALID_FILE_HANDLE)
365
MP4GetMetadataName(file, &pVal);
366
uSetDlgItemText(tagHandle, TAGFIELD_TITLE, pVal);
369
MP4GetMetadataArtist(file, &pVal);
370
uSetDlgItemText(tagHandle, TAGFIELD_ARTIST, pVal);
373
MP4GetMetadataWriter(file, &pVal);
374
uSetDlgItemText(tagHandle, TAGFIELD_COMPOSER, pVal);
377
MP4GetMetadataComment(file, &pVal);
378
uSetDlgItemText(tagHandle, TAGFIELD_COMMENT, pVal);
381
MP4GetMetadataAlbum(file, &pVal);
382
uSetDlgItemText(tagHandle, TAGFIELD_ALBUM, pVal);
385
MP4GetMetadataGenre(file, &pVal);
386
uSetDlgItemText(tagHandle, TAGFIELD_GENRE, pVal);
389
//MP4GetMetadataTempo(file, &dummy);
392
// wsprintf(dummy1, "%d", dummy);
393
// SetDlgItemText(hwndDlg,IDC_METATEMPO, dummy1);
396
dummy = 0; dummy2 = 0;
397
MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2);
400
wsprintf(dummy1, "%d", dummy);
401
ModInitTag.SetFieldA(tagHandle, TAGFIELD_TRACK, dummy1);
405
// wsprintf(dummy1, "%d", dummy2);
406
// SetDlgItemText(hwndDlg,IDC_METATRACK2, dummy1);
409
//dummy = 0; dummy2 = 0;
410
//MP4GetMetadataDisk(file, &dummy, &dummy2);
413
// wsprintf(dummy1, "%d", dummy);
414
// SetDlgItemText(hwndDlg,IDC_METADISK1, dummy1);
418
// wsprintf(dummy1, "%d", dummy2);
419
// SetDlgItemText(hwndDlg,IDC_METADISK2, dummy1);
423
if (MP4GetMetadataYear(file, &pVal))
424
uSetDlgItemText(tagHandle, TAGFIELD_YEAR, pVal);
427
//MP4GetMetadataCompilation(file, &dummy3);
429
// SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_SETCHECK, BST_CHECKED, 0);
432
MP4GetMetadataTool(file, &pVal);
433
uSetDlgItemText(tagHandle, TAGFIELD_ENCODER, pVal);
436
MP4GetMetadataFreeForm(file, "CONDUCTOR", (unsigned __int8**)&pVal, &valueSize);
437
uSetDlgItemText(tagHandle, TAGFIELD_CONDUCTOR, pVal);
440
MP4GetMetadataFreeForm(file, "ORCHESTRA", (unsigned __int8**)&pVal, &valueSize);
441
uSetDlgItemText(tagHandle, TAGFIELD_ORCHESTRA, pVal);
444
MP4GetMetadataFreeForm(file, "YEARCOMPOSED", (unsigned __int8**)&pVal, &valueSize);
445
uSetDlgItemText(tagHandle, TAGFIELD_YEARCOMPOSED, pVal);
448
MP4GetMetadataFreeForm(file, "ORIGARTIST", (unsigned __int8**)&pVal, &valueSize);
449
uSetDlgItemText(tagHandle, TAGFIELD_ORIGARTIST, pVal);
452
MP4GetMetadataFreeForm(file, "LABEL", (unsigned __int8**)&pVal, &valueSize);
453
uSetDlgItemText(tagHandle, TAGFIELD_LABEL, pVal);
456
MP4GetMetadataFreeForm(file, "COPYRIGHT", (unsigned __int8**)&pVal, &valueSize);
457
uSetDlgItemText(tagHandle, TAGFIELD_COPYRIGHT, pVal);
460
MP4GetMetadataFreeForm(file, "CDDBTAGID", (unsigned __int8**)&pVal, &valueSize);
461
uSetDlgItemText(tagHandle, TAGFIELD_CDDBTAGID, pVal);
470
//-----------------------------------------------------------------------------
472
bool Write_Tag(LPCSTR filename, void* tagHandle)
475
// read metadata from tagHandle and set each field to supported tag
476
// only TAGFIELD_* are supported (see QCDModTagEditor.h)
478
// example of how to get value from tagHandle
479
// use SetFieldA for ASCII or MultiBytes strings.
480
// use SetFieldW for UNICODE strings
482
// szwValue = ModInitTag.GetFieldW(tagHandle, TAGFIELD_ORCHESTRA);
486
MP4FileHandle file = MP4_INVALID_FILE_HANDLE;
492
in_mp4_DebugOutput("mp4_tag_write");
495
/* save Metadata changes */
498
file = MP4Read(filename, 0);
499
if (file != MP4_INVALID_FILE_HANDLE)
501
ReadMP4Tag(file, &tags);
504
file = MP4Modify(filename, 0, 0);
505
if (file != MP4_INVALID_FILE_HANDLE)
507
MP4MetadataDelete(file);
512
file = MP4Modify(filename, 0, 0);
513
if (file == MP4_INVALID_FILE_HANDLE)
516
//EndDialog(hwndDlg, wParam);
520
uGetDlgItemText(tagHandle, TAGFIELD_TITLE, dummy1, 1024);
521
tag_set_field(&tags, "title", dummy1);
523
uGetDlgItemText(tagHandle, TAGFIELD_COMPOSER, dummy1, 1024);
524
tag_set_field(&tags, "writer", dummy1);
526
uGetDlgItemText(tagHandle, TAGFIELD_ARTIST, dummy1, 1024);
527
tag_set_field(&tags, "artist", dummy1);
529
uGetDlgItemText(tagHandle, TAGFIELD_ALBUM, dummy1, 1024);
530
tag_set_field(&tags, "album", dummy1);
532
uGetDlgItemText(tagHandle, TAGFIELD_COMMENT, dummy1, 1024);
533
tag_set_field(&tags, "comment", dummy1);
535
uGetDlgItemText(tagHandle, TAGFIELD_GENRE, dummy1, 1024);
536
tag_set_field(&tags, "genre", dummy1);
538
uGetDlgItemText(tagHandle, TAGFIELD_YEAR, dummy1, 1024);
539
tag_set_field(&tags, "year", dummy1);
542
MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2);
543
memcpy(dummy1, ModInitTag.GetFieldA(tagHandle, TAGFIELD_TRACK), sizeof(dummy1));
544
dummy = atoi(dummy1);
545
wsprintf(temp, "%d/%d", dummy, dummy2);
546
tag_set_field(&tags, "track", temp);
548
//GetDlgItemText(hwndDlg, IDC_METADISK1, dummy1, 1024);
549
//dummy = atoi(dummy1);
550
//GetDlgItemText(hwndDlg, IDC_METADISK2, dummy1, 1024);
551
//dummy2 = atoi(dummy1);
552
//wsprintf(temp, "%d/%d", dummy, dummy2);
553
//tag_set_field(&tags, "disc", temp);
555
//GetDlgItemText(hwndDlg, IDC_METATEMPO, dummy1, 1024);
556
//tag_set_field(&tags, "tempo", dummy1);
558
//dummy3 = SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_GETCHECK, 0, 0);
559
//tag_set_field(&tags, "compilation", (dummy3 ? "1" : "0"));
561
uGetDlgItemText(tagHandle, TAGFIELD_ENCODER, dummy1, 1024);
562
tag_set_field(&tags, "tool", dummy1);
564
uGetDlgItemText(tagHandle, TAGFIELD_CONDUCTOR, dummy1, 1024);
565
tag_set_field(&tags, "CONDUCTOR", dummy1);
567
uGetDlgItemText(tagHandle, TAGFIELD_ORCHESTRA, dummy1, 1024);
568
tag_set_field(&tags, "ORCHESTRA", dummy1);
570
uGetDlgItemText(tagHandle, TAGFIELD_YEARCOMPOSED, dummy1, 1024);
571
tag_set_field(&tags, "YEARCOMPOSED", dummy1);
573
uGetDlgItemText(tagHandle, TAGFIELD_ORIGARTIST, dummy1, 1024);
574
tag_set_field(&tags, "ORIGARTIST", dummy1);
576
uGetDlgItemText(tagHandle, TAGFIELD_LABEL, dummy1, 1024);
577
tag_set_field(&tags, "LABEL", dummy1);
579
uGetDlgItemText(tagHandle, TAGFIELD_COPYRIGHT, dummy1, 1024);
580
tag_set_field(&tags, "COPYRIGHT", dummy1);
582
uGetDlgItemText(tagHandle, TAGFIELD_CDDBTAGID, dummy1, 1024);
583
tag_set_field(&tags, "CDDBTAGID", dummy1);
585
WriteMP4Tag(file, &tags);
589
MP4Optimize(filename, NULL, 0);
595
//-----------------------------------------------------------------------------
597
bool Strip_Tag(LPCSTR filename)
600
// remove tag from file.
601
// do whatever is need to remove the supported tag from filename
603
// return true for successfull strip, false for failure
607
file = MP4Modify(filename, 0, 0);
608
if (file == MP4_INVALID_FILE_HANDLE)
611
MP4MetadataDelete(file);
618
//-----------------------------------------------------------------------------
620
/* Convert UNICODE to UTF-8
621
Return number of bytes written */
622
int unicodeToUtf8 ( const WCHAR* lpWideCharStr, char* lpMultiByteStr, int cwcChars )
624
const unsigned short* pwc = (unsigned short *)lpWideCharStr;
625
unsigned char* pmb = (unsigned char *)lpMultiByteStr;
626
const unsigned short* pwce;
629
if ( cwcChars >= 0 ) {
630
pwce = pwc + cwcChars;
632
pwce = (unsigned short *)((size_t)-1);
635
while ( pwc < pwce ) {
636
unsigned short wc = *pwc++;
638
if ( wc < 0x00000080 ) {
642
if ( wc < 0x00000800 ) {
643
*pmb++ = (char)(0xC0 | ((wc >> 6) & 0x1F));
645
*pmb++ = (char)(0x80 | (wc & 0x3F));
648
if ( wc < 0x00010000 ) {
649
*pmb++ = (char)(0xE0 | ((wc >> 12) & 0x0F));
651
*pmb++ = (char)(0x80 | ((wc >> 6) & 0x3F));
653
*pmb++ = (char)(0x80 | (wc & 0x3F));
663
/* Convert UTF-8 coded string to UNICODE
664
Return number of characters converted */
665
int utf8ToUnicode ( const char* lpMultiByteStr, WCHAR* lpWideCharStr, int cmbChars )
667
const unsigned char* pmb = (unsigned char *)lpMultiByteStr;
668
unsigned short* pwc = (unsigned short *)lpWideCharStr;
669
const unsigned char* pmbe;
672
if ( cmbChars >= 0 ) {
673
pmbe = pmb + cmbChars;
675
pmbe = (unsigned char *)((size_t)-1);
678
while ( pmb < pmbe ) {
683
while ( (cc < 7) && (mb & (1 << (7 - cc)))) {
687
if ( cc == 1 || cc > 6 ) // illegal character combination for UTF-8
693
wc = (mb & ((1 << (7 - cc)) - 1)) << ((cc - 1) * 6);
695
if ( pmb == pmbe ) // reached end of the buffer
698
if ( ((mb >> 6) & 0x03) != 2 ) // not part of multibyte character
700
wc |= (mb & 0x3F) << ((cc - 1) * 6);
704
if ( wc & 0xFFFF0000 )
715
/* convert Windows ANSI to UTF-8 */
716
int ConvertANSIToUTF8 ( const char* ansi, char* utf8 )
718
WCHAR* wszValue; // Unicode value
726
ansi_len = strlen ( ansi );
728
if ( (wszValue = (WCHAR *)malloc ( (ansi_len + 1) * 2 )) == NULL )
731
/* Convert ANSI value to Unicode */
732
if ( (len = MultiByteToWideChar ( CP_ACP, 0, ansi, ansi_len + 1, wszValue, (ansi_len + 1) * 2 )) == 0 ) {
737
/* Convert Unicode value to UTF-8 */
738
if ( (len = unicodeToUtf8 ( wszValue, utf8, -1 )) == 0 ) {
748
/* convert UTF-8 to Windows ANSI */
749
int ConvertUTF8ToANSI ( const char* utf8, char* ansi )
751
WCHAR* wszValue; // Unicode value
759
utf8_len = strlen ( utf8 );
761
if ( (wszValue = (WCHAR *)malloc ( (utf8_len + 1) * 2 )) == NULL )
764
/* Convert UTF-8 value to Unicode */
765
if ( (len = utf8ToUnicode ( utf8, wszValue, utf8_len + 1 )) == 0 ) {
770
/* Convert Unicode value to ANSI */
771
if ( (len = WideCharToMultiByte ( CP_ACP, 0, wszValue, -1, ansi, (utf8_len + 1) * 2, NULL, NULL )) == 0 ) {
781
BOOL uSetDlgItemText(void *tagHandle, int fieldId, const char *str)
787
if (!str) return FALSE;
788
if (!*str) return FALSE;
790
temp = (char *)malloc(len+1);
791
if (!temp) return FALSE;
792
memset(temp, '\0', len+1);
793
r = ConvertUTF8ToANSI(str, temp);
795
ModInitTag.SetFieldA(tagHandle, fieldId, temp);
798
return r>0 ? TRUE : FALSE;
801
UINT uGetDlgItemText(void *tagHandle, int fieldId, char *str, int max)
808
if (!str || !max) return 0;
809
len = strlen( ModInitTag.GetFieldA(tagHandle, fieldId) );
810
temp = (char *)malloc(len+1);
812
utf8 = (char *)malloc((len+1)*4);
819
memset(temp, '\0', len+1);
820
memset(utf8, '\0', (len+1)*4);
821
memset(str, '\0', max);
822
p = ModInitTag.GetFieldA(tagHandle, fieldId);
823
memcpy(temp, p, len+1);
826
len = ConvertANSIToUTF8(temp, utf8);
832
memcpy(str, utf8, len+1);