654 lines
20 KiB
C++
654 lines
20 KiB
C++
|
#include "ID3v2.h"
|
||
|
#include "id3.h"
|
||
|
#include "config.h"
|
||
|
#include "api__in_mp3.h"
|
||
|
#include "../Agave/AlbumArt/svc_albumArtProvider.h"
|
||
|
#include "../nu/AutoChar.h"
|
||
|
#include "../nu/AutoWide.h"
|
||
|
#include <strsafe.h>
|
||
|
|
||
|
static inline const wchar_t *IncSafe(const wchar_t *val, int x)
|
||
|
{
|
||
|
while (x--)
|
||
|
{
|
||
|
if (val && *val)
|
||
|
val++;
|
||
|
}
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
extern const wchar_t *id3v1_genres[];
|
||
|
extern size_t numGenres;
|
||
|
|
||
|
ID3v2::ID3v2()
|
||
|
{
|
||
|
hasData=false;
|
||
|
dirty=false;
|
||
|
}
|
||
|
|
||
|
int ID3v2::Decode(const void *data, size_t len)
|
||
|
{
|
||
|
if (!config_parse_id3v2 || !data)
|
||
|
{
|
||
|
hasData=false;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
id3v2.Parse((uchar *)data, (uchar *)data+ID3_TAGHEADERSIZE);
|
||
|
if (id3v2.NumFrames() > 0)
|
||
|
{
|
||
|
hasData=true;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// return -1 for empty, 1 for OK, 0 for "don't understand tag name"
|
||
|
int ID3v2::GetString(const char *tag, wchar_t *data, int dataLen)
|
||
|
{
|
||
|
if (!_stricmp(tag, "title"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_TITLE, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "album"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_ALBUM, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "artist"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_LEADARTIST, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "albumartist"))
|
||
|
{
|
||
|
if (!ID3_GetTagText(&id3v2, ID3FID_BAND, data, dataLen) && !ID3_GetUserText(&id3v2, L"ALBUM ARTIST", data, dataLen) && !ID3_GetUserText(&id3v2, L"ALBUMARTIST", data, dataLen))
|
||
|
return ID3_GetUserText(&id3v2, L"Band", data, dataLen)?1:-1;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "comment"))
|
||
|
return ID3_GetComment(&id3v2, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "year"))
|
||
|
{
|
||
|
if (!ID3_GetTagText(&id3v2, ID3FID_RECORDINGTIME, data, dataLen))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_YEAR, data, dataLen)?1:-1;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "composer"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_COMPOSER, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "replaygain_track_gain"))
|
||
|
return ID3_GetUserText(&id3v2, L"replaygain_track_gain", data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "replaygain_album_gain"))
|
||
|
return ID3_GetUserText(&id3v2, L"replaygain_album_gain", data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "replaygain_track_peak"))
|
||
|
return ID3_GetUserText(&id3v2, L"replaygain_track_peak", data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "replaygain_album_peak"))
|
||
|
return ID3_GetUserText(&id3v2, L"replaygain_album_peak", data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "genre"))
|
||
|
{
|
||
|
data[0] = 0;
|
||
|
if (ID3_GetTagText(&id3v2, ID3FID_CONTENTTYPE, data, dataLen))
|
||
|
{
|
||
|
wchar_t *tmp = data;
|
||
|
while (tmp && *tmp == ' ') tmp++;
|
||
|
if (tmp && (*tmp == '(' || (*tmp >= '0' && *tmp <= '9'))) // both (%d) and %d forms
|
||
|
{
|
||
|
int noparam = 0;
|
||
|
if (*tmp == '(') tmp++;
|
||
|
else noparam = 1;
|
||
|
size_t genre_index = _wtoi(tmp);
|
||
|
int cnt = 0;
|
||
|
while (tmp && *tmp >= '0' && *tmp <= '9') cnt++, tmp++;
|
||
|
while (tmp && *tmp == ' ') tmp++;
|
||
|
|
||
|
if (tmp && (((!*tmp && noparam) || (!noparam && *tmp == ')')) && cnt > 0))
|
||
|
{
|
||
|
if (genre_index < numGenres)
|
||
|
StringCchCopyW(data, dataLen, id3v1_genres[genre_index]);
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "track"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_TRACKNUM, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "disc"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_PARTINSET, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "bpm"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_BPM, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "rating"))
|
||
|
return ID3_GetRating(&id3v2, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "conductor"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_CONDUCTOR, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "key"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_KEY, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "mood"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_MOOD, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "subtitle"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_SUBTITLE, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "lyricist"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_LYRICIST, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "ISRC"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_ISRC, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "media"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_MEDIATYPE, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "remixing"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_MIXARTIST, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "originalartist"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_ORIGARTIST, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "encoder"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_ENCODERSETTINGS, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "publisher"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_PUBLISHER, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "copyright"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_COPYRIGHT, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "compilation"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_COMPILATION, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "url"))
|
||
|
return ID3_GetTagUrl(&id3v2, ID3FID_WWWUSER, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "GracenoteFileID"))
|
||
|
return ID3_GetGracenoteTagID(&id3v2, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "GracenoteExtData"))
|
||
|
{
|
||
|
if (!ID3_GetUserText(&id3v2, L"GN_ExtData", data, dataLen))
|
||
|
return ID3_GetUserText(&id3v2, L"GN/ExtData", data, dataLen)?1:-1;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "tool"))
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_ENCODEDBY, data, dataLen)?1:-1;
|
||
|
else if (!_stricmp(tag, "pregap"))
|
||
|
{
|
||
|
data[0] = 0;
|
||
|
// first, check for stupid iTunSMPB TXXX frame
|
||
|
wchar_t gaps[128] = L"";
|
||
|
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||
|
if (itr && *itr)
|
||
|
{
|
||
|
itr = IncSafe(itr, 9);
|
||
|
unsigned int prepad = wcstoul(itr, 0, 16);
|
||
|
StringCchPrintfW(data, dataLen, L"%u", prepad);
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "postgap"))
|
||
|
{
|
||
|
data[0] = 0;
|
||
|
// first, check for stupid iTunSMPB TXXX frame
|
||
|
wchar_t gaps[128] = L"";
|
||
|
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||
|
if (itr && *itr)
|
||
|
{
|
||
|
itr = IncSafe(itr, 18);
|
||
|
unsigned int postpad = wcstoul(itr, 0, 16);
|
||
|
StringCchPrintfW(data, dataLen, L"%u", postpad);
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "numsamples"))
|
||
|
{
|
||
|
data[0] = 0;
|
||
|
// first, check for stupid iTunSMPB TXXX frame
|
||
|
wchar_t gaps[128] = L"";
|
||
|
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||
|
if (itr && *itr)
|
||
|
{
|
||
|
itr = IncSafe(itr, 27);
|
||
|
unsigned __int64 samples = wcstoul(itr, 0, 16);
|
||
|
StringCchPrintfW(data, dataLen, L"%I64u", samples);
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "endoffset"))
|
||
|
{
|
||
|
data[0] = 0;
|
||
|
// first, check for stupid iTunSMPB TXXX frame
|
||
|
wchar_t gaps[128] = L"";
|
||
|
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||
|
if (itr && *itr)
|
||
|
{
|
||
|
itr = IncSafe(itr, 53);
|
||
|
unsigned __int32 endoffset = wcstoul(itr, 0, 16);
|
||
|
StringCchPrintfW(data, dataLen, L"%I32u", endoffset);
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "category"))
|
||
|
{
|
||
|
return ID3_GetTagText(&id3v2, ID3FID_CONTENTGROUP, data, dataLen)?1:-1;
|
||
|
}
|
||
|
// things generally added by Musicbrainz tagging (either specific or additional)
|
||
|
else if (!_stricmp(tag, "acoustid") || !_stricmp(tag, "acoustid_id"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"Acoustid Id", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "acoustid_fingerprint"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"Acoustid Fingerprint", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "asin"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"ASIN", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "barcode"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"BARCODE", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "catalognumber"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"CATALOGNUMBER", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "script"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"SCRIPT", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_recordingid")) // (track id)
|
||
|
{
|
||
|
return ID3_GetMusicbrainzRecordingID(&id3v2, data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_trackid")) // TODO not working (album track id)
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Release Track Id", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_albumid"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Id", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_artistid"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Artist Id", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_albumartistid"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Artist Id", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_releasestatus") || !_stricmp(tag, "musicbrainz_albumstatus"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Status", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_releasetype") || !_stricmp(tag, "musicbrainz_albumtype"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Type", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_releasecountry") || !_stricmp(tag, "musicbrainz_albumcountry"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Release Country", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else if (!_stricmp(tag, "musicbrainz_releasegroupid") || !_stricmp(tag, "musicbrainz_albumgroupid"))
|
||
|
{
|
||
|
return ID3_GetUserText(&id3v2, L"MusicBrainz Release Group Id", data, dataLen)?1:-1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ID3v2::add_set_latin_id3v2_frame(ID3_FrameID id, const wchar_t *c)
|
||
|
{
|
||
|
ID3_Frame *f = id3v2.Find(id);
|
||
|
if (!c)
|
||
|
{
|
||
|
if (f)
|
||
|
id3v2.RemoveFrame(f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (f)
|
||
|
{
|
||
|
SetFrameEncoding(f, ENCODING_FORCE_ASCII);
|
||
|
AutoChar temp(c); //AutoChar temp(c, 28591); // todo: benski> changed back to local to keep old winamp tagged files working
|
||
|
f->Field(ID3FN_URL).SetLocal(temp); //f->Field(ID3FN_TEXT).SetLatin(temp);// todo: benski> changed back to local to keep old winamp tagged files working
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
f = new ID3_Frame(id);
|
||
|
SetFrameEncoding(f, ENCODING_FORCE_ASCII);
|
||
|
AutoChar temp(c); //AutoChar temp(c, 28591); // todo: benski> changed back to local to keep old winamp tagged files working
|
||
|
f->Field(ID3FN_URL).SetLocal(temp); //f->Field(ID3FN_TEXT).SetLatin(temp);// todo: benski> changed back to local to keep old winamp tagged files working
|
||
|
id3v2.AddFrame(f, TRUE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int ID3v2::SetString(const char *tag, const wchar_t *data)
|
||
|
{
|
||
|
if (!_stricmp(tag, "artist"))
|
||
|
add_set_id3v2_frame(ID3FID_LEADARTIST, data);
|
||
|
else if (!_stricmp(tag, "album"))
|
||
|
add_set_id3v2_frame(ID3FID_ALBUM, data);
|
||
|
else if (!_stricmp(tag, "albumartist"))
|
||
|
{
|
||
|
add_set_id3v2_frame(ID3FID_BAND, data);
|
||
|
if (!data || !*data) // if we're deleting the field
|
||
|
{
|
||
|
ID3_AddUserText(&id3v2, L"ALBUM ARTIST", data); // delete this alternate field also, or it's gonna look like it didn't "take" with a fb2k file
|
||
|
ID3_AddUserText(&id3v2, L"ALBUMARTIST", data); // delete this alternate field also, or it's gonna look like it didn't "take" with an mp3tag file
|
||
|
ID3_AddUserText(&id3v2, L"Band", data); // delete this alternate field also, or it's gonna look like it didn't "take" with an audacity file
|
||
|
}
|
||
|
}
|
||
|
else if (!_stricmp(tag, "comment"))
|
||
|
ID3_AddSetComment(&id3v2, data);
|
||
|
else if (!_stricmp(tag, "title"))
|
||
|
add_set_id3v2_frame(ID3FID_TITLE, data);
|
||
|
else if (!_stricmp(tag, "year"))
|
||
|
{
|
||
|
add_set_id3v2_frame(ID3FID_YEAR, data);
|
||
|
if (id3v2.version >= 4) // work around the fact that our id3 code doesn't handle versioning like this too well
|
||
|
add_set_id3v2_frame(ID3FID_RECORDINGTIME, data);
|
||
|
else
|
||
|
add_set_id3v2_frame(ID3FID_RECORDINGTIME, (wchar_t *)0);
|
||
|
}
|
||
|
else if (!_stricmp(tag, "genre"))
|
||
|
add_set_id3v2_frame(ID3FID_CONTENTTYPE, data);
|
||
|
else if (!_stricmp(tag, "track"))
|
||
|
add_set_id3v2_frame(ID3FID_TRACKNUM, data);
|
||
|
else if (!_stricmp(tag, "disc"))
|
||
|
add_set_id3v2_frame(ID3FID_PARTINSET, data);
|
||
|
else if (!_stricmp(tag, "bpm"))
|
||
|
add_set_id3v2_frame(ID3FID_BPM, data);
|
||
|
else if (!_stricmp(tag, "rating"))
|
||
|
ID3_AddSetRating(&id3v2, data);
|
||
|
else if (!_stricmp(tag, "tool"))
|
||
|
add_set_id3v2_frame(ID3FID_ENCODEDBY, data);
|
||
|
else if (!_stricmp(tag, "composer"))
|
||
|
add_set_id3v2_frame(ID3FID_COMPOSER, data);
|
||
|
else if (!_stricmp(tag, "replaygain_track_gain"))
|
||
|
ID3_AddUserText(&id3v2, L"replaygain_track_gain", data, ENCODING_FORCE_ASCII);
|
||
|
else if (!_stricmp(tag, "replaygain_track_peak"))
|
||
|
ID3_AddUserText(&id3v2, L"replaygain_track_peak", data, ENCODING_FORCE_ASCII);
|
||
|
else if (!_stricmp(tag, "replaygain_album_gain"))
|
||
|
ID3_AddUserText(&id3v2, L"replaygain_album_gain", data, ENCODING_FORCE_ASCII);
|
||
|
else if (!_stricmp(tag, "replaygain_album_peak"))
|
||
|
ID3_AddUserText(&id3v2, L"replaygain_album_peak", data, ENCODING_FORCE_ASCII);
|
||
|
else if (!_stricmp(tag, "originalartist"))
|
||
|
add_set_id3v2_frame(ID3FID_ORIGARTIST, data);
|
||
|
else if (!_stricmp(tag, "encoder"))
|
||
|
add_set_id3v2_frame(ID3FID_ENCODERSETTINGS, data);
|
||
|
else if (!_stricmp(tag, "publisher"))
|
||
|
add_set_id3v2_frame(ID3FID_PUBLISHER, data);
|
||
|
else if (!_stricmp(tag, "copyright"))
|
||
|
add_set_id3v2_frame(ID3FID_COPYRIGHT, data);
|
||
|
else if (!_stricmp(tag, "compilation"))
|
||
|
add_set_id3v2_frame(ID3FID_COMPILATION, data);
|
||
|
else if (!_stricmp(tag, "remixing"))
|
||
|
add_set_id3v2_frame(ID3FID_MIXARTIST, data);
|
||
|
else if (!_stricmp(tag, "ISRC"))
|
||
|
add_set_id3v2_frame(ID3FID_ISRC, data);
|
||
|
else if (!_stricmp(tag, "url"))
|
||
|
add_set_latin_id3v2_frame(ID3FID_WWWUSER, data); // TODO: we should %## escape invalid characters
|
||
|
//add_set_id3v2_frame(ID3FID_WWWUSER, data);
|
||
|
else if (!_stricmp(tag, "GracenoteFileID"))
|
||
|
ID3_AddSetGracenoteTagID(&id3v2, data);
|
||
|
else if (!_stricmp(tag, "GracenoteExtData"))
|
||
|
{
|
||
|
ID3_AddUserText(&id3v2, L"GN_ExtData", data, ENCODING_FORCE_ASCII);
|
||
|
ID3_AddUserText(&id3v2, L"GN_ExtData",0); // delete this alternate field also
|
||
|
}
|
||
|
else if (!_stricmp(tag, "category"))
|
||
|
add_set_id3v2_frame(ID3FID_CONTENTGROUP, data);
|
||
|
else
|
||
|
return 0;
|
||
|
hasData=true;
|
||
|
dirty=true;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
void ID3v2::add_set_id3v2_frame(ID3_FrameID id, const wchar_t *c)
|
||
|
{
|
||
|
ID3_Frame *f = id3v2.Find(id);
|
||
|
if (!c || !*c)
|
||
|
{
|
||
|
if (f)
|
||
|
id3v2.RemoveFrame(f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (f)
|
||
|
{
|
||
|
SetFrameEncoding(f);
|
||
|
f->Field(ID3FN_TEXT).SetUnicode(c);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
f = new ID3_Frame(id);
|
||
|
SetFrameEncoding(f);
|
||
|
f->Field(ID3FN_TEXT).SetUnicode(c);
|
||
|
id3v2.AddFrame(f, TRUE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint32_t ID3v2::EncodeSize()
|
||
|
{
|
||
|
if (!hasData)
|
||
|
return 0; // simple :)
|
||
|
|
||
|
return (uint32_t)id3v2.Size();
|
||
|
}
|
||
|
|
||
|
int ID3v2::Encode(const void *data, size_t len)
|
||
|
{
|
||
|
id3v2.Render((uchar *)data);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static bool NameToAPICType(const wchar_t *name, int &num)
|
||
|
{
|
||
|
if (!name || !*name) // default to cover
|
||
|
num=0x3;
|
||
|
else if (!_wcsicmp(name, L"fileicon")) // 32x32 pixels 'file icon' (PNG only)
|
||
|
num=0x1;
|
||
|
else if (!_wcsicmp(name, L"icon")) // Other file icon
|
||
|
num=0x2;
|
||
|
else if (!_wcsicmp(name, L"cover")) // Cover (front)
|
||
|
num=0x3;
|
||
|
else if (!_wcsicmp(name, L"back")) // Cover (back)
|
||
|
num=0x4;
|
||
|
else if (!_wcsicmp(name, L"leaflet")) // Leaflet page
|
||
|
num=0x5;
|
||
|
else if (!_wcsicmp(name, L"media")) // Media (e.g. lable side of CD)
|
||
|
num=0x6;
|
||
|
else if (!_wcsicmp(name, L"leadartist")) //Lead artist/lead performer/soloist
|
||
|
num=0x7;
|
||
|
else if (!_wcsicmp(name, L"artist")) // Artist/performer
|
||
|
num=0x8;
|
||
|
else if (!_wcsicmp(name, L"conductor")) // Conductor
|
||
|
num=0x9;
|
||
|
else if (!_wcsicmp(name, L"band")) // Band/Orchestra
|
||
|
num=0xA;
|
||
|
else if (!_wcsicmp(name, L"composer")) // Composer
|
||
|
num=0xB;
|
||
|
else if (!_wcsicmp(name, L"lyricist")) // Lyricist/text writer
|
||
|
num=0xC;
|
||
|
else if (!_wcsicmp(name, L"location")) // Recording Location
|
||
|
num=0xD;
|
||
|
else if (!_wcsicmp(name, L"recording")) // During recording
|
||
|
num=0xE;
|
||
|
else if (!_wcsicmp(name, L"performance")) // During performance
|
||
|
num=0xF;
|
||
|
else if (!_wcsicmp(name, L"preview")) // Movie/video screen capture
|
||
|
num=0x10;
|
||
|
else if (!_wcsicmp(name, L"fish")) // A bright coloured fish
|
||
|
num=0x11;
|
||
|
else if (!_wcsicmp(name, L"illustration")) // Illustration
|
||
|
num=0x12;
|
||
|
else if (!_wcsicmp(name, L"artistlogo")) // Band/artist logotype
|
||
|
num=0x13;
|
||
|
else if (!_wcsicmp(name, L"publisherlogo")) // Publisher/Studio logotype
|
||
|
num=0x14;
|
||
|
else
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int ID3v2::GetAlbumArt(const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
|
||
|
{
|
||
|
int pictype = 0;
|
||
|
if (NameToAPICType(type, pictype))
|
||
|
{
|
||
|
// try to get our specific picture type
|
||
|
ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
|
||
|
|
||
|
if (!frame && pictype == 3) // if not, just try a generic one
|
||
|
{
|
||
|
frame = id3v2.Find(ID3FID_PICTURE);
|
||
|
/*benski> CUT!
|
||
|
if (frame)
|
||
|
{
|
||
|
ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
|
||
|
if (field.Get())
|
||
|
frame=0;
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
if (frame)
|
||
|
{
|
||
|
char *fulltype = ID3_GetString(frame, ID3FN_MIMETYPE);
|
||
|
char *type = 0;
|
||
|
if (fulltype && *fulltype)
|
||
|
{
|
||
|
type = strchr(fulltype, '/');
|
||
|
}
|
||
|
|
||
|
if (type && *type)
|
||
|
{
|
||
|
type++;
|
||
|
|
||
|
char *type2 = strchr(type, '/');
|
||
|
if (type2 && *type2) type2++;
|
||
|
else type2 = type;
|
||
|
|
||
|
int typelen = MultiByteToWideChar(CP_ACP, 0, type2, -1, 0, 0);
|
||
|
*mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
|
||
|
MultiByteToWideChar(CP_ACP, 0, type2, -1, *mimeType, typelen);
|
||
|
free(fulltype);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// attempt to work out a mime type from known 'invalid' values
|
||
|
if (fulltype && *fulltype)
|
||
|
{
|
||
|
if (!strcmpi(fulltype, "png") || !strcmpi(fulltype, "bmp") ||
|
||
|
!strcmpi(fulltype, "jpg") || !strcmpi(fulltype, "jpeg") ||
|
||
|
!strcmpi(fulltype, "gif"))
|
||
|
{
|
||
|
int typelen = MultiByteToWideChar(CP_ACP, 0, fulltype, -1, 0, 0);// + 6;
|
||
|
*mimeType = (wchar_t*)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
|
||
|
MultiByteToWideChar(CP_ACP, 0, fulltype, -1, *mimeType, typelen);
|
||
|
CharLowerBuff(*mimeType, typelen);
|
||
|
free(fulltype);
|
||
|
fulltype = 0;
|
||
|
}
|
||
|
if (0 != fulltype)
|
||
|
{
|
||
|
free(fulltype);
|
||
|
fulltype = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*mimeType = 0; // unknown!
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ID3_Field &field = frame->Field(ID3FN_DATA);
|
||
|
*len = field.Size();
|
||
|
*bits = WASABI_API_MEMMGR->sysMalloc(*len);
|
||
|
field.Get((uchar *)*bits, *len);
|
||
|
return ALBUMARTPROVIDER_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
return ALBUMARTPROVIDER_FAILURE;
|
||
|
}
|
||
|
|
||
|
int ID3v2::SetAlbumArt(const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
|
||
|
{
|
||
|
int pictype;
|
||
|
if (NameToAPICType(type, pictype))
|
||
|
{
|
||
|
// try to get our specific picture type
|
||
|
ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
|
||
|
|
||
|
if (!frame && pictype == 3) // if not, just try a generic one
|
||
|
{
|
||
|
frame = id3v2.Find(ID3FID_PICTURE);
|
||
|
/* benski> cut
|
||
|
if (frame)
|
||
|
{
|
||
|
ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
|
||
|
if (field.Get())
|
||
|
frame=0;
|
||
|
}*/
|
||
|
}
|
||
|
bool newFrame=false;
|
||
|
if (!frame)
|
||
|
{
|
||
|
frame = new ID3_Frame(ID3FID_PICTURE);
|
||
|
newFrame = true;
|
||
|
}
|
||
|
|
||
|
if (frame)
|
||
|
{
|
||
|
wchar_t mt[32] = {L"image/jpeg"};
|
||
|
if (mimeType)
|
||
|
{
|
||
|
if (wcsstr(mimeType, L"/") != 0)
|
||
|
{
|
||
|
StringCchCopyW(mt, 32, mimeType);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
StringCchPrintfW(mt, 32, L"image/%s", mimeType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
frame->Field(ID3FN_MIMETYPE).SetLatin(AutoChar(mt, 28591));
|
||
|
frame->Field(ID3FN_PICTURETYPE).Set(pictype);
|
||
|
frame->Field(ID3FN_DESCRIPTION).Clear();
|
||
|
frame->Field(ID3FN_DATA).Set((uchar *)bits, len);
|
||
|
if (newFrame)
|
||
|
id3v2.AddFrame(frame, TRUE);
|
||
|
dirty=1;
|
||
|
return ALBUMARTPROVIDER_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
return ALBUMARTPROVIDER_FAILURE;
|
||
|
}
|
||
|
|
||
|
int ID3v2::DeleteAlbumArt(const wchar_t *type)
|
||
|
{
|
||
|
int pictype;
|
||
|
if (NameToAPICType(type, pictype))
|
||
|
{
|
||
|
// try to get our specific picture type
|
||
|
ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
|
||
|
|
||
|
if (!frame && pictype == 3) // if not, just try a generic one
|
||
|
{
|
||
|
frame = id3v2.Find(ID3FID_PICTURE);
|
||
|
/* benski> cut
|
||
|
if (frame)
|
||
|
{
|
||
|
ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
|
||
|
if (field.Get())
|
||
|
frame=0;
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
if (frame)
|
||
|
{
|
||
|
id3v2.RemoveFrame(frame);
|
||
|
dirty=1;
|
||
|
return ALBUMARTPROVIDER_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
return ALBUMARTPROVIDER_FAILURE;
|
||
|
}
|
||
|
|
||
|
void ID3v2::Clear()
|
||
|
{
|
||
|
dirty=1;
|
||
|
hasData=false;
|
||
|
id3v2.Clear();
|
||
|
}
|