ID3v2.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. #include "ID3v2.h"
  2. #include "id3.h"
  3. #include "config.h"
  4. #include "api__in_mp3.h"
  5. #include "../Agave/AlbumArt/svc_albumArtProvider.h"
  6. #include "../nu/AutoChar.h"
  7. #include "../nu/AutoWide.h"
  8. #include <strsafe.h>
  9. static inline const wchar_t *IncSafe(const wchar_t *val, int x)
  10. {
  11. while (x--)
  12. {
  13. if (val && *val)
  14. val++;
  15. }
  16. return val;
  17. }
  18. extern const wchar_t *id3v1_genres[];
  19. extern size_t numGenres;
  20. ID3v2::ID3v2()
  21. {
  22. hasData=false;
  23. dirty=false;
  24. }
  25. int ID3v2::Decode(const void *data, size_t len)
  26. {
  27. if (!config_parse_id3v2 || !data)
  28. {
  29. hasData=false;
  30. return 0;
  31. }
  32. id3v2.Parse((uchar *)data, (uchar *)data+ID3_TAGHEADERSIZE);
  33. if (id3v2.NumFrames() > 0)
  34. {
  35. hasData=true;
  36. return 0;
  37. }
  38. else
  39. return 1;
  40. }
  41. // return -1 for empty, 1 for OK, 0 for "don't understand tag name"
  42. int ID3v2::GetString(const char *tag, wchar_t *data, int dataLen)
  43. {
  44. if (!_stricmp(tag, "title"))
  45. return ID3_GetTagText(&id3v2, ID3FID_TITLE, data, dataLen)?1:-1;
  46. else if (!_stricmp(tag, "album"))
  47. return ID3_GetTagText(&id3v2, ID3FID_ALBUM, data, dataLen)?1:-1;
  48. else if (!_stricmp(tag, "artist"))
  49. return ID3_GetTagText(&id3v2, ID3FID_LEADARTIST, data, dataLen)?1:-1;
  50. else if (!_stricmp(tag, "albumartist"))
  51. {
  52. if (!ID3_GetTagText(&id3v2, ID3FID_BAND, data, dataLen) && !ID3_GetUserText(&id3v2, L"ALBUM ARTIST", data, dataLen) && !ID3_GetUserText(&id3v2, L"ALBUMARTIST", data, dataLen))
  53. return ID3_GetUserText(&id3v2, L"Band", data, dataLen)?1:-1;
  54. else
  55. return 1;
  56. }
  57. else if (!_stricmp(tag, "comment"))
  58. return ID3_GetComment(&id3v2, data, dataLen)?1:-1;
  59. else if (!_stricmp(tag, "year"))
  60. {
  61. if (!ID3_GetTagText(&id3v2, ID3FID_RECORDINGTIME, data, dataLen))
  62. return ID3_GetTagText(&id3v2, ID3FID_YEAR, data, dataLen)?1:-1;
  63. else
  64. return 1;
  65. }
  66. else if (!_stricmp(tag, "composer"))
  67. return ID3_GetTagText(&id3v2, ID3FID_COMPOSER, data, dataLen)?1:-1;
  68. else if (!_stricmp(tag, "replaygain_track_gain"))
  69. return ID3_GetUserText(&id3v2, L"replaygain_track_gain", data, dataLen)?1:-1;
  70. else if (!_stricmp(tag, "replaygain_album_gain"))
  71. return ID3_GetUserText(&id3v2, L"replaygain_album_gain", data, dataLen)?1:-1;
  72. else if (!_stricmp(tag, "replaygain_track_peak"))
  73. return ID3_GetUserText(&id3v2, L"replaygain_track_peak", data, dataLen)?1:-1;
  74. else if (!_stricmp(tag, "replaygain_album_peak"))
  75. return ID3_GetUserText(&id3v2, L"replaygain_album_peak", data, dataLen)?1:-1;
  76. else if (!_stricmp(tag, "genre"))
  77. {
  78. data[0] = 0;
  79. if (ID3_GetTagText(&id3v2, ID3FID_CONTENTTYPE, data, dataLen))
  80. {
  81. wchar_t *tmp = data;
  82. while (tmp && *tmp == ' ') tmp++;
  83. if (tmp && (*tmp == '(' || (*tmp >= '0' && *tmp <= '9'))) // both (%d) and %d forms
  84. {
  85. int noparam = 0;
  86. if (*tmp == '(') tmp++;
  87. else noparam = 1;
  88. size_t genre_index = _wtoi(tmp);
  89. int cnt = 0;
  90. while (tmp && *tmp >= '0' && *tmp <= '9') cnt++, tmp++;
  91. while (tmp && *tmp == ' ') tmp++;
  92. if (tmp && (((!*tmp && noparam) || (!noparam && *tmp == ')')) && cnt > 0))
  93. {
  94. if (genre_index < numGenres)
  95. StringCchCopyW(data, dataLen, id3v1_genres[genre_index]);
  96. }
  97. }
  98. return 1;
  99. }
  100. return -1;
  101. }
  102. else if (!_stricmp(tag, "track"))
  103. return ID3_GetTagText(&id3v2, ID3FID_TRACKNUM, data, dataLen)?1:-1;
  104. else if (!_stricmp(tag, "disc"))
  105. return ID3_GetTagText(&id3v2, ID3FID_PARTINSET, data, dataLen)?1:-1;
  106. else if (!_stricmp(tag, "bpm"))
  107. return ID3_GetTagText(&id3v2, ID3FID_BPM, data, dataLen)?1:-1;
  108. else if (!_stricmp(tag, "rating"))
  109. return ID3_GetRating(&id3v2, data, dataLen)?1:-1;
  110. else if (!_stricmp(tag, "conductor"))
  111. return ID3_GetTagText(&id3v2, ID3FID_CONDUCTOR, data, dataLen)?1:-1;
  112. else if (!_stricmp(tag, "key"))
  113. return ID3_GetTagText(&id3v2, ID3FID_KEY, data, dataLen)?1:-1;
  114. else if (!_stricmp(tag, "mood"))
  115. return ID3_GetTagText(&id3v2, ID3FID_MOOD, data, dataLen)?1:-1;
  116. else if (!_stricmp(tag, "subtitle"))
  117. return ID3_GetTagText(&id3v2, ID3FID_SUBTITLE, data, dataLen)?1:-1;
  118. else if (!_stricmp(tag, "lyricist"))
  119. return ID3_GetTagText(&id3v2, ID3FID_LYRICIST, data, dataLen)?1:-1;
  120. else if (!_stricmp(tag, "ISRC"))
  121. return ID3_GetTagText(&id3v2, ID3FID_ISRC, data, dataLen)?1:-1;
  122. else if (!_stricmp(tag, "media"))
  123. return ID3_GetTagText(&id3v2, ID3FID_MEDIATYPE, data, dataLen)?1:-1;
  124. else if (!_stricmp(tag, "remixing"))
  125. return ID3_GetTagText(&id3v2, ID3FID_MIXARTIST, data, dataLen)?1:-1;
  126. else if (!_stricmp(tag, "originalartist"))
  127. return ID3_GetTagText(&id3v2, ID3FID_ORIGARTIST, data, dataLen)?1:-1;
  128. else if (!_stricmp(tag, "encoder"))
  129. return ID3_GetTagText(&id3v2, ID3FID_ENCODERSETTINGS, data, dataLen)?1:-1;
  130. else if (!_stricmp(tag, "publisher"))
  131. return ID3_GetTagText(&id3v2, ID3FID_PUBLISHER, data, dataLen)?1:-1;
  132. else if (!_stricmp(tag, "copyright"))
  133. return ID3_GetTagText(&id3v2, ID3FID_COPYRIGHT, data, dataLen)?1:-1;
  134. else if (!_stricmp(tag, "compilation"))
  135. return ID3_GetTagText(&id3v2, ID3FID_COMPILATION, data, dataLen)?1:-1;
  136. else if (!_stricmp(tag, "url"))
  137. return ID3_GetTagUrl(&id3v2, ID3FID_WWWUSER, data, dataLen)?1:-1;
  138. else if (!_stricmp(tag, "GracenoteFileID"))
  139. return ID3_GetGracenoteTagID(&id3v2, data, dataLen)?1:-1;
  140. else if (!_stricmp(tag, "GracenoteExtData"))
  141. {
  142. if (!ID3_GetUserText(&id3v2, L"GN_ExtData", data, dataLen))
  143. return ID3_GetUserText(&id3v2, L"GN/ExtData", data, dataLen)?1:-1;
  144. else
  145. return 1;
  146. }
  147. else if (!_stricmp(tag, "tool"))
  148. return ID3_GetTagText(&id3v2, ID3FID_ENCODEDBY, data, dataLen)?1:-1;
  149. else if (!_stricmp(tag, "pregap"))
  150. {
  151. data[0] = 0;
  152. // first, check for stupid iTunSMPB TXXX frame
  153. wchar_t gaps[128] = L"";
  154. const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
  155. if (itr && *itr)
  156. {
  157. itr = IncSafe(itr, 9);
  158. unsigned int prepad = wcstoul(itr, 0, 16);
  159. StringCchPrintfW(data, dataLen, L"%u", prepad);
  160. return 1;
  161. }
  162. return -1;
  163. }
  164. else if (!_stricmp(tag, "postgap"))
  165. {
  166. data[0] = 0;
  167. // first, check for stupid iTunSMPB TXXX frame
  168. wchar_t gaps[128] = L"";
  169. const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
  170. if (itr && *itr)
  171. {
  172. itr = IncSafe(itr, 18);
  173. unsigned int postpad = wcstoul(itr, 0, 16);
  174. StringCchPrintfW(data, dataLen, L"%u", postpad);
  175. return 1;
  176. }
  177. return -1;
  178. }
  179. else if (!_stricmp(tag, "numsamples"))
  180. {
  181. data[0] = 0;
  182. // first, check for stupid iTunSMPB TXXX frame
  183. wchar_t gaps[128] = L"";
  184. const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
  185. if (itr && *itr)
  186. {
  187. itr = IncSafe(itr, 27);
  188. unsigned __int64 samples = wcstoul(itr, 0, 16);
  189. StringCchPrintfW(data, dataLen, L"%I64u", samples);
  190. return 1;
  191. }
  192. return -1;
  193. }
  194. else if (!_stricmp(tag, "endoffset"))
  195. {
  196. data[0] = 0;
  197. // first, check for stupid iTunSMPB TXXX frame
  198. wchar_t gaps[128] = L"";
  199. const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
  200. if (itr && *itr)
  201. {
  202. itr = IncSafe(itr, 53);
  203. unsigned __int32 endoffset = wcstoul(itr, 0, 16);
  204. StringCchPrintfW(data, dataLen, L"%I32u", endoffset);
  205. return 1;
  206. }
  207. return -1;
  208. }
  209. else if (!_stricmp(tag, "category"))
  210. {
  211. return ID3_GetTagText(&id3v2, ID3FID_CONTENTGROUP, data, dataLen)?1:-1;
  212. }
  213. // things generally added by Musicbrainz tagging (either specific or additional)
  214. else if (!_stricmp(tag, "acoustid") || !_stricmp(tag, "acoustid_id"))
  215. {
  216. return ID3_GetUserText(&id3v2, L"Acoustid Id", data, dataLen)?1:-1;
  217. }
  218. else if (!_stricmp(tag, "acoustid_fingerprint"))
  219. {
  220. return ID3_GetUserText(&id3v2, L"Acoustid Fingerprint", data, dataLen)?1:-1;
  221. }
  222. else if (!_stricmp(tag, "asin"))
  223. {
  224. return ID3_GetUserText(&id3v2, L"ASIN", data, dataLen)?1:-1;
  225. }
  226. else if (!_stricmp(tag, "barcode"))
  227. {
  228. return ID3_GetUserText(&id3v2, L"BARCODE", data, dataLen)?1:-1;
  229. }
  230. else if (!_stricmp(tag, "catalognumber"))
  231. {
  232. return ID3_GetUserText(&id3v2, L"CATALOGNUMBER", data, dataLen)?1:-1;
  233. }
  234. else if (!_stricmp(tag, "script"))
  235. {
  236. return ID3_GetUserText(&id3v2, L"SCRIPT", data, dataLen)?1:-1;
  237. }
  238. else if (!_stricmp(tag, "musicbrainz_recordingid")) // (track id)
  239. {
  240. return ID3_GetMusicbrainzRecordingID(&id3v2, data, dataLen)?1:-1;
  241. }
  242. else if (!_stricmp(tag, "musicbrainz_trackid")) // TODO not working (album track id)
  243. {
  244. return ID3_GetUserText(&id3v2, L"MusicBrainz Release Track Id", data, dataLen)?1:-1;
  245. }
  246. else if (!_stricmp(tag, "musicbrainz_albumid"))
  247. {
  248. return ID3_GetUserText(&id3v2, L"MusicBrainz Album Id", data, dataLen)?1:-1;
  249. }
  250. else if (!_stricmp(tag, "musicbrainz_artistid"))
  251. {
  252. return ID3_GetUserText(&id3v2, L"MusicBrainz Artist Id", data, dataLen)?1:-1;
  253. }
  254. else if (!_stricmp(tag, "musicbrainz_albumartistid"))
  255. {
  256. return ID3_GetUserText(&id3v2, L"MusicBrainz Album Artist Id", data, dataLen)?1:-1;
  257. }
  258. else if (!_stricmp(tag, "musicbrainz_releasestatus") || !_stricmp(tag, "musicbrainz_albumstatus"))
  259. {
  260. return ID3_GetUserText(&id3v2, L"MusicBrainz Album Status", data, dataLen)?1:-1;
  261. }
  262. else if (!_stricmp(tag, "musicbrainz_releasetype") || !_stricmp(tag, "musicbrainz_albumtype"))
  263. {
  264. return ID3_GetUserText(&id3v2, L"MusicBrainz Album Type", data, dataLen)?1:-1;
  265. }
  266. else if (!_stricmp(tag, "musicbrainz_releasecountry") || !_stricmp(tag, "musicbrainz_albumcountry"))
  267. {
  268. return ID3_GetUserText(&id3v2, L"MusicBrainz Album Release Country", data, dataLen)?1:-1;
  269. }
  270. else if (!_stricmp(tag, "musicbrainz_releasegroupid") || !_stricmp(tag, "musicbrainz_albumgroupid"))
  271. {
  272. return ID3_GetUserText(&id3v2, L"MusicBrainz Release Group Id", data, dataLen)?1:-1;
  273. }
  274. else
  275. {
  276. return 0;
  277. }
  278. }
  279. void ID3v2::add_set_latin_id3v2_frame(ID3_FrameID id, const wchar_t *c)
  280. {
  281. ID3_Frame *f = id3v2.Find(id);
  282. if (!c)
  283. {
  284. if (f)
  285. id3v2.RemoveFrame(f);
  286. }
  287. else
  288. {
  289. if (f)
  290. {
  291. SetFrameEncoding(f, ENCODING_FORCE_ASCII);
  292. AutoChar temp(c); //AutoChar temp(c, 28591); // todo: benski> changed back to local to keep old winamp tagged files working
  293. 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
  294. }
  295. else
  296. {
  297. f = new ID3_Frame(id);
  298. SetFrameEncoding(f, ENCODING_FORCE_ASCII);
  299. AutoChar temp(c); //AutoChar temp(c, 28591); // todo: benski> changed back to local to keep old winamp tagged files working
  300. 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
  301. id3v2.AddFrame(f, TRUE);
  302. }
  303. }
  304. }
  305. int ID3v2::SetString(const char *tag, const wchar_t *data)
  306. {
  307. if (!_stricmp(tag, "artist"))
  308. add_set_id3v2_frame(ID3FID_LEADARTIST, data);
  309. else if (!_stricmp(tag, "album"))
  310. add_set_id3v2_frame(ID3FID_ALBUM, data);
  311. else if (!_stricmp(tag, "albumartist"))
  312. {
  313. add_set_id3v2_frame(ID3FID_BAND, data);
  314. if (!data || !*data) // if we're deleting the field
  315. {
  316. 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
  317. 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
  318. 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
  319. }
  320. }
  321. else if (!_stricmp(tag, "comment"))
  322. ID3_AddSetComment(&id3v2, data);
  323. else if (!_stricmp(tag, "title"))
  324. add_set_id3v2_frame(ID3FID_TITLE, data);
  325. else if (!_stricmp(tag, "year"))
  326. {
  327. add_set_id3v2_frame(ID3FID_YEAR, data);
  328. if (id3v2.version >= 4) // work around the fact that our id3 code doesn't handle versioning like this too well
  329. add_set_id3v2_frame(ID3FID_RECORDINGTIME, data);
  330. else
  331. add_set_id3v2_frame(ID3FID_RECORDINGTIME, (wchar_t *)0);
  332. }
  333. else if (!_stricmp(tag, "genre"))
  334. add_set_id3v2_frame(ID3FID_CONTENTTYPE, data);
  335. else if (!_stricmp(tag, "track"))
  336. add_set_id3v2_frame(ID3FID_TRACKNUM, data);
  337. else if (!_stricmp(tag, "disc"))
  338. add_set_id3v2_frame(ID3FID_PARTINSET, data);
  339. else if (!_stricmp(tag, "bpm"))
  340. add_set_id3v2_frame(ID3FID_BPM, data);
  341. else if (!_stricmp(tag, "rating"))
  342. ID3_AddSetRating(&id3v2, data);
  343. else if (!_stricmp(tag, "tool"))
  344. add_set_id3v2_frame(ID3FID_ENCODEDBY, data);
  345. else if (!_stricmp(tag, "composer"))
  346. add_set_id3v2_frame(ID3FID_COMPOSER, data);
  347. else if (!_stricmp(tag, "replaygain_track_gain"))
  348. ID3_AddUserText(&id3v2, L"replaygain_track_gain", data, ENCODING_FORCE_ASCII);
  349. else if (!_stricmp(tag, "replaygain_track_peak"))
  350. ID3_AddUserText(&id3v2, L"replaygain_track_peak", data, ENCODING_FORCE_ASCII);
  351. else if (!_stricmp(tag, "replaygain_album_gain"))
  352. ID3_AddUserText(&id3v2, L"replaygain_album_gain", data, ENCODING_FORCE_ASCII);
  353. else if (!_stricmp(tag, "replaygain_album_peak"))
  354. ID3_AddUserText(&id3v2, L"replaygain_album_peak", data, ENCODING_FORCE_ASCII);
  355. else if (!_stricmp(tag, "originalartist"))
  356. add_set_id3v2_frame(ID3FID_ORIGARTIST, data);
  357. else if (!_stricmp(tag, "encoder"))
  358. add_set_id3v2_frame(ID3FID_ENCODERSETTINGS, data);
  359. else if (!_stricmp(tag, "publisher"))
  360. add_set_id3v2_frame(ID3FID_PUBLISHER, data);
  361. else if (!_stricmp(tag, "copyright"))
  362. add_set_id3v2_frame(ID3FID_COPYRIGHT, data);
  363. else if (!_stricmp(tag, "compilation"))
  364. add_set_id3v2_frame(ID3FID_COMPILATION, data);
  365. else if (!_stricmp(tag, "remixing"))
  366. add_set_id3v2_frame(ID3FID_MIXARTIST, data);
  367. else if (!_stricmp(tag, "ISRC"))
  368. add_set_id3v2_frame(ID3FID_ISRC, data);
  369. else if (!_stricmp(tag, "url"))
  370. add_set_latin_id3v2_frame(ID3FID_WWWUSER, data); // TODO: we should %## escape invalid characters
  371. //add_set_id3v2_frame(ID3FID_WWWUSER, data);
  372. else if (!_stricmp(tag, "GracenoteFileID"))
  373. ID3_AddSetGracenoteTagID(&id3v2, data);
  374. else if (!_stricmp(tag, "GracenoteExtData"))
  375. {
  376. ID3_AddUserText(&id3v2, L"GN_ExtData", data, ENCODING_FORCE_ASCII);
  377. ID3_AddUserText(&id3v2, L"GN_ExtData",0); // delete this alternate field also
  378. }
  379. else if (!_stricmp(tag, "category"))
  380. add_set_id3v2_frame(ID3FID_CONTENTGROUP, data);
  381. else
  382. return 0;
  383. hasData=true;
  384. dirty=true;
  385. return 1;
  386. }
  387. void ID3v2::add_set_id3v2_frame(ID3_FrameID id, const wchar_t *c)
  388. {
  389. ID3_Frame *f = id3v2.Find(id);
  390. if (!c || !*c)
  391. {
  392. if (f)
  393. id3v2.RemoveFrame(f);
  394. }
  395. else
  396. {
  397. if (f)
  398. {
  399. SetFrameEncoding(f);
  400. f->Field(ID3FN_TEXT).SetUnicode(c);
  401. }
  402. else
  403. {
  404. f = new ID3_Frame(id);
  405. SetFrameEncoding(f);
  406. f->Field(ID3FN_TEXT).SetUnicode(c);
  407. id3v2.AddFrame(f, TRUE);
  408. }
  409. }
  410. }
  411. uint32_t ID3v2::EncodeSize()
  412. {
  413. if (!hasData)
  414. return 0; // simple :)
  415. return (uint32_t)id3v2.Size();
  416. }
  417. int ID3v2::Encode(const void *data, size_t len)
  418. {
  419. id3v2.Render((uchar *)data);
  420. return 0;
  421. }
  422. static bool NameToAPICType(const wchar_t *name, int &num)
  423. {
  424. if (!name || !*name) // default to cover
  425. num=0x3;
  426. else if (!_wcsicmp(name, L"fileicon")) // 32x32 pixels 'file icon' (PNG only)
  427. num=0x1;
  428. else if (!_wcsicmp(name, L"icon")) // Other file icon
  429. num=0x2;
  430. else if (!_wcsicmp(name, L"cover")) // Cover (front)
  431. num=0x3;
  432. else if (!_wcsicmp(name, L"back")) // Cover (back)
  433. num=0x4;
  434. else if (!_wcsicmp(name, L"leaflet")) // Leaflet page
  435. num=0x5;
  436. else if (!_wcsicmp(name, L"media")) // Media (e.g. lable side of CD)
  437. num=0x6;
  438. else if (!_wcsicmp(name, L"leadartist")) //Lead artist/lead performer/soloist
  439. num=0x7;
  440. else if (!_wcsicmp(name, L"artist")) // Artist/performer
  441. num=0x8;
  442. else if (!_wcsicmp(name, L"conductor")) // Conductor
  443. num=0x9;
  444. else if (!_wcsicmp(name, L"band")) // Band/Orchestra
  445. num=0xA;
  446. else if (!_wcsicmp(name, L"composer")) // Composer
  447. num=0xB;
  448. else if (!_wcsicmp(name, L"lyricist")) // Lyricist/text writer
  449. num=0xC;
  450. else if (!_wcsicmp(name, L"location")) // Recording Location
  451. num=0xD;
  452. else if (!_wcsicmp(name, L"recording")) // During recording
  453. num=0xE;
  454. else if (!_wcsicmp(name, L"performance")) // During performance
  455. num=0xF;
  456. else if (!_wcsicmp(name, L"preview")) // Movie/video screen capture
  457. num=0x10;
  458. else if (!_wcsicmp(name, L"fish")) // A bright coloured fish
  459. num=0x11;
  460. else if (!_wcsicmp(name, L"illustration")) // Illustration
  461. num=0x12;
  462. else if (!_wcsicmp(name, L"artistlogo")) // Band/artist logotype
  463. num=0x13;
  464. else if (!_wcsicmp(name, L"publisherlogo")) // Publisher/Studio logotype
  465. num=0x14;
  466. else
  467. return false;
  468. return true;
  469. }
  470. int ID3v2::GetAlbumArt(const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
  471. {
  472. int pictype = 0;
  473. if (NameToAPICType(type, pictype))
  474. {
  475. // try to get our specific picture type
  476. ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
  477. if (!frame && pictype == 3) // if not, just try a generic one
  478. {
  479. frame = id3v2.Find(ID3FID_PICTURE);
  480. /*benski> CUT!
  481. if (frame)
  482. {
  483. ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
  484. if (field.Get())
  485. frame=0;
  486. }*/
  487. }
  488. if (frame)
  489. {
  490. char *fulltype = ID3_GetString(frame, ID3FN_MIMETYPE);
  491. char *type = 0;
  492. if (fulltype && *fulltype)
  493. {
  494. type = strchr(fulltype, '/');
  495. }
  496. if (type && *type)
  497. {
  498. type++;
  499. char *type2 = strchr(type, '/');
  500. if (type2 && *type2) type2++;
  501. else type2 = type;
  502. int typelen = MultiByteToWideChar(CP_ACP, 0, type2, -1, 0, 0);
  503. *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
  504. MultiByteToWideChar(CP_ACP, 0, type2, -1, *mimeType, typelen);
  505. free(fulltype);
  506. }
  507. else
  508. {
  509. // attempt to work out a mime type from known 'invalid' values
  510. if (fulltype && *fulltype)
  511. {
  512. if (!strcmpi(fulltype, "png") || !strcmpi(fulltype, "bmp") ||
  513. !strcmpi(fulltype, "jpg") || !strcmpi(fulltype, "jpeg") ||
  514. !strcmpi(fulltype, "gif"))
  515. {
  516. int typelen = MultiByteToWideChar(CP_ACP, 0, fulltype, -1, 0, 0);// + 6;
  517. *mimeType = (wchar_t*)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
  518. MultiByteToWideChar(CP_ACP, 0, fulltype, -1, *mimeType, typelen);
  519. CharLowerBuff(*mimeType, typelen);
  520. free(fulltype);
  521. fulltype = 0;
  522. }
  523. if (0 != fulltype)
  524. {
  525. free(fulltype);
  526. fulltype = 0;
  527. }
  528. }
  529. else
  530. {
  531. *mimeType = 0; // unknown!
  532. }
  533. }
  534. ID3_Field &field = frame->Field(ID3FN_DATA);
  535. *len = field.Size();
  536. *bits = WASABI_API_MEMMGR->sysMalloc(*len);
  537. field.Get((uchar *)*bits, *len);
  538. return ALBUMARTPROVIDER_SUCCESS;
  539. }
  540. }
  541. return ALBUMARTPROVIDER_FAILURE;
  542. }
  543. int ID3v2::SetAlbumArt(const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
  544. {
  545. int pictype;
  546. if (NameToAPICType(type, pictype))
  547. {
  548. // try to get our specific picture type
  549. ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
  550. if (!frame && pictype == 3) // if not, just try a generic one
  551. {
  552. frame = id3v2.Find(ID3FID_PICTURE);
  553. /* benski> cut
  554. if (frame)
  555. {
  556. ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
  557. if (field.Get())
  558. frame=0;
  559. }*/
  560. }
  561. bool newFrame=false;
  562. if (!frame)
  563. {
  564. frame = new ID3_Frame(ID3FID_PICTURE);
  565. newFrame = true;
  566. }
  567. if (frame)
  568. {
  569. wchar_t mt[32] = {L"image/jpeg"};
  570. if (mimeType)
  571. {
  572. if (wcsstr(mimeType, L"/") != 0)
  573. {
  574. StringCchCopyW(mt, 32, mimeType);
  575. }
  576. else
  577. {
  578. StringCchPrintfW(mt, 32, L"image/%s", mimeType);
  579. }
  580. }
  581. frame->Field(ID3FN_MIMETYPE).SetLatin(AutoChar(mt, 28591));
  582. frame->Field(ID3FN_PICTURETYPE).Set(pictype);
  583. frame->Field(ID3FN_DESCRIPTION).Clear();
  584. frame->Field(ID3FN_DATA).Set((uchar *)bits, len);
  585. if (newFrame)
  586. id3v2.AddFrame(frame, TRUE);
  587. dirty=1;
  588. return ALBUMARTPROVIDER_SUCCESS;
  589. }
  590. }
  591. return ALBUMARTPROVIDER_FAILURE;
  592. }
  593. int ID3v2::DeleteAlbumArt(const wchar_t *type)
  594. {
  595. int pictype;
  596. if (NameToAPICType(type, pictype))
  597. {
  598. // try to get our specific picture type
  599. ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
  600. if (!frame && pictype == 3) // if not, just try a generic one
  601. {
  602. frame = id3v2.Find(ID3FID_PICTURE);
  603. /* benski> cut
  604. if (frame)
  605. {
  606. ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
  607. if (field.Get())
  608. frame=0;
  609. }
  610. */
  611. }
  612. if (frame)
  613. {
  614. id3v2.RemoveFrame(frame);
  615. dirty=1;
  616. return ALBUMARTPROVIDER_SUCCESS;
  617. }
  618. }
  619. return ALBUMARTPROVIDER_FAILURE;
  620. }
  621. void ID3v2::Clear()
  622. {
  623. dirty=1;
  624. hasData=false;
  625. id3v2.Clear();
  626. }