APEv2Metadata.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include "APEv2Metadata.h"
  2. #include "metadata/MetadataKeys.h"
  3. #include "nu/ByteReader.h"
  4. #include "nswasabi/ReferenceCounted.h"
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. static inline bool TestFlag(int flags, int flag_to_check)
  8. {
  9. if (flags & flag_to_check)
  10. return true;
  11. return false;
  12. }
  13. api_metadata *APEv2Metadata::metadata_api=0;
  14. APEv2Metadata::APEv2Metadata()
  15. {
  16. apev2_tag=0;
  17. }
  18. APEv2Metadata::~APEv2Metadata()
  19. {
  20. }
  21. int APEv2Metadata::Initialize(api_metadata *metadata_api)
  22. {
  23. APEv2Metadata::metadata_api = metadata_api;
  24. return NErr_Success;
  25. }
  26. int APEv2Metadata::Initialize(nsapev2_tag_t tag)
  27. {
  28. apev2_tag = tag;
  29. return NErr_Success;
  30. }
  31. /* ifc_metadata implementation */
  32. int APEv2Metadata::Metadata_GetField(int field, unsigned int index, nx_string_t *value)
  33. {
  34. if (!apev2_tag)
  35. return NErr_Unknown;
  36. switch (field)
  37. {
  38. case MetadataKeys::TRACK_GAIN:
  39. return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_GAIN", index, value);
  40. case MetadataKeys::TRACK_PEAK:
  41. return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_PEAK", index, value);
  42. case MetadataKeys::ALBUM_GAIN:
  43. return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_GAIN", index, value);
  44. case MetadataKeys::ALBUM_PEAK:
  45. return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_PEAK", index, value);
  46. }
  47. return NErr_Unknown;
  48. }
  49. int APEv2Metadata::Metadata_GetInteger(int field, unsigned int index, int64_t *value)
  50. {
  51. if (!apev2_tag)
  52. return NErr_Unknown;
  53. return NErr_Unknown;
  54. }
  55. int APEv2Metadata::Metadata_GetReal(int field, unsigned int index, double *value)
  56. {
  57. if (!apev2_tag)
  58. return NErr_Unknown;
  59. int ret;
  60. nx_string_t str;
  61. switch (field)
  62. {
  63. case MetadataKeys::TRACK_GAIN:
  64. ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_GAIN", index, &str);
  65. if (ret == NErr_Success)
  66. {
  67. ret = NXStringGetDoubleValue(str, value);
  68. NXStringRelease(str);
  69. }
  70. return ret;
  71. case MetadataKeys::TRACK_PEAK:
  72. ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_PEAK", index, &str);
  73. if (ret == NErr_Success)
  74. {
  75. ret = NXStringGetDoubleValue(str, value);
  76. NXStringRelease(str);
  77. }
  78. return ret;
  79. case MetadataKeys::ALBUM_GAIN:
  80. ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_GAIN", index, &str);
  81. if (ret == NErr_Success)
  82. {
  83. ret = NXStringGetDoubleValue(str, value);
  84. NXStringRelease(str);
  85. }
  86. return ret;
  87. case MetadataKeys::ALBUM_PEAK:
  88. ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_PEAK", index, &str);
  89. if (ret == NErr_Success)
  90. {
  91. ret = NXStringGetDoubleValue(str, value);
  92. NXStringRelease(str);
  93. }
  94. return ret;
  95. }
  96. return NErr_Unknown;
  97. }
  98. #ifdef _WIN32
  99. #define strcasecmp _stricmp
  100. #endif
  101. static const char *APEv2_GetMIME(const char *extension)
  102. {
  103. if (!extension)
  104. return 0;
  105. if (strcasecmp(extension, "JPG") == 0 || strcasecmp(extension, "JPEG") == 0)
  106. return "image/jpeg";
  107. if (strcasecmp(extension, "PNG") == 0)
  108. return "image/png";
  109. if (strcasecmp(extension, "GIF") == 0)
  110. return "image/gif";
  111. if (strcasecmp(extension, "BMP") == 0)
  112. return "image/bmp";
  113. return 0;
  114. }
  115. static int APEv2_ParseArt(const void *bytes, size_t length, artwork_t *out_data, data_flags_t flags)
  116. {
  117. if (out_data)
  118. {
  119. nx_data_t data=0;
  120. if (flags != DATA_FLAG_NONE)
  121. {
  122. bytereader_s byte_reader;
  123. bytereader_init(&byte_reader, bytes, length);
  124. if (bytereader_size(&byte_reader) == 0)
  125. return NErr_Insufficient;
  126. const char *description_start = (const char *)bytereader_pointer(&byte_reader);
  127. const char *extension_start=0;
  128. uint8_t byte;
  129. do
  130. {
  131. if (bytereader_size(&byte_reader) == 0)
  132. return NErr_Insufficient;
  133. byte = bytereader_read_u8(&byte_reader);
  134. if (byte == '.') // found extension
  135. {
  136. extension_start = (const char *)bytereader_pointer(&byte_reader);
  137. }
  138. } while (byte && bytereader_size(&byte_reader));
  139. size_t length = bytereader_size(&byte_reader);
  140. if (length == 0)
  141. return NErr_Empty;
  142. if (TestFlag(flags, DATA_FLAG_DATA))
  143. {
  144. int ret = NXDataCreate(&data, bytereader_pointer(&byte_reader), length);
  145. if (ret != NErr_Success)
  146. return ret;
  147. }
  148. else
  149. {
  150. int ret = NXDataCreateEmpty(&data);
  151. if (ret != NErr_Success)
  152. return ret;
  153. }
  154. if (TestFlag(flags, DATA_FLAG_DESCRIPTION))
  155. {
  156. ReferenceCountedNXString description;
  157. size_t length;
  158. if (extension_start)
  159. length = (size_t)extension_start - (size_t)description_start - 1;
  160. else
  161. length = (size_t)bytereader_pointer(&byte_reader) - (size_t)description_start - 1;
  162. if (length)
  163. {
  164. int ret = NXStringCreateWithBytes(&description, description_start, length, nx_charset_utf8);
  165. if (ret != NErr_Success)
  166. {
  167. NXDataRelease(data);
  168. return ret;
  169. }
  170. NXDataSetDescription(data, description);
  171. }
  172. }
  173. if (TestFlag(flags, DATA_FLAG_MIME))
  174. {
  175. ReferenceCountedNXString mime_type;
  176. const char *mime_string = APEv2_GetMIME(extension_start);
  177. if (mime_string)
  178. {
  179. int ret = NXStringCreateWithUTF8(&mime_type, mime_string);
  180. if (ret != NErr_Success)
  181. {
  182. NXDataRelease(data);
  183. return ret;
  184. }
  185. }
  186. }
  187. }
  188. out_data->data = data;
  189. /* we don't know these */
  190. out_data->height=0;
  191. out_data->width=0;
  192. }
  193. return NErr_Success;
  194. }
  195. int APEv2Metadata::Metadata_GetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags)
  196. {
  197. if (!apev2_tag)
  198. return NErr_Unknown;
  199. int ret;
  200. const void *bytes;
  201. size_t length;
  202. switch(field)
  203. {
  204. case MetadataKeys::ALBUM:
  205. ret = NSAPEv2_Tag_GetBinary(apev2_tag, "Cover Art (front)", index, &bytes, &length);
  206. if (ret == NErr_Success)
  207. return APEv2_ParseArt(bytes, length, data, flags);
  208. return ret;
  209. }
  210. return NErr_Unknown;
  211. }
  212. int APEv2Metadata::MetadataEditor_SetField(int field, unsigned int index, nx_string_t value)
  213. {
  214. switch (field)
  215. {
  216. case MetadataKeys::TRACK_GAIN:
  217. return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_TRACK_GAIN", index, value);
  218. case MetadataKeys::TRACK_PEAK:
  219. return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_TRACK_PEAK", index, value);
  220. case MetadataKeys::ALBUM_GAIN:
  221. return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_ALBUM_GAIN", index, value);
  222. case MetadataKeys::ALBUM_PEAK:
  223. return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_ALBUM_PEAK", index, value);
  224. }
  225. return NErr_Unknown;
  226. }
  227. int APEv2Metadata::MetadataEditor_SetInteger(int field, unsigned int index, int64_t value)
  228. {
  229. return NErr_Unknown;
  230. }
  231. int APEv2Metadata::MetadataEditor_SetReal(int field, unsigned int index, double value)
  232. {
  233. // TODO: but we need NXStringCreateFromDouble which I don't feel like writing right now
  234. return NErr_Unknown;
  235. }
  236. static void APEv2_GetFilenameForMIME(char *filename, const char *type, nx_string_t mime_type)
  237. {
  238. if (mime_type)
  239. {
  240. if (NXStringKeywordCompareWithCString(mime_type, "image/jpeg") == NErr_True || NXStringKeywordCompareWithCString(mime_type, "image/jpg") == NErr_True)
  241. sprintf(filename, "%s.jpeg", type);
  242. else if (NXStringKeywordCompareWithCString(mime_type, "image/png") == NErr_True)
  243. sprintf(filename, "%s.png", type);
  244. if (NXStringKeywordCompareWithCString(mime_type, "image/gif") == NErr_True)
  245. sprintf(filename, "%s.gif", type);
  246. if (NXStringKeywordCompareWithCString(mime_type, "image/bmp") == NErr_True)
  247. sprintf(filename, "%s.bmp", type);
  248. else
  249. sprintf(filename, "%s.jpg", type); // TODO: perhaps we could use whatever is after image/
  250. }
  251. else
  252. sprintf(filename, "%s.jpg", type); // ehh, just guess
  253. }
  254. int APEv2Metadata::MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags)
  255. {
  256. ReferenceCountedNXString mime_type;
  257. const void *bytes = 0;
  258. size_t length = 0;
  259. switch(field)
  260. {
  261. case MetadataKeys::ALBUM:
  262. if (data && NXDataGet(data->data, &bytes, &length) == NErr_Success)
  263. {
  264. char filename[256] = {0};
  265. NXDataGetMIME(data->data, &mime_type);
  266. APEv2_GetFilenameForMIME(filename, "cover", mime_type); /* TODO: perhaps use description, instead? */
  267. return NSAPEv2_Tag_SetArtwork(apev2_tag, "Cover Art (front)", index, filename, bytes, length);
  268. }
  269. else
  270. return NSAPEv2_Tag_SetArtwork(apev2_tag, "Cover Art (front)", index, 0, 0, 0);
  271. }
  272. return NErr_Unknown;
  273. }