PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/libtunepimp-0.5.3/plugins/mp3/id3tag-2.3/tag.c

#
C | 973 lines | 646 code | 221 blank | 106 comment | 208 complexity | 4646c351d24b1ed090a96eb4210fb846 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, GPL-2.0, LGPL-2.0
  1. /*
  2. * libid3tag - ID3 tag manipulation library
  3. * Copyright (C) 2000-2003 Underbit Technologies, Inc.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *
  19. * $Id: tag.c 8460 2006-09-03 18:37:43Z luks $
  20. */
  21. # ifdef HAVE_CONFIG_H
  22. # include "config.h"
  23. # endif
  24. #ifdef WIN32
  25. # include "../../../config_win32.h"
  26. #endif
  27. # include "global.h"
  28. # include <string.h>
  29. # include <stdlib.h>
  30. # include <stdio.h>
  31. # ifdef HAVE_ASSERT_H
  32. # include <assert.h>
  33. # endif
  34. # include "id3tag.h"
  35. # include "tag.h"
  36. # include "frame.h"
  37. # include "compat.h"
  38. # include "parse.h"
  39. # include "render.h"
  40. # include "latin1.h"
  41. # include "ucs4.h"
  42. # include "genre.h"
  43. # include "crc.h"
  44. # include "field.h"
  45. # include "util.h"
  46. /* If a v2 tag is too small to fit and the file needs to be rewritten, add at least
  47. * TAG_PADDING number of bytes to hopefully prevent having to rewrite the file
  48. * in the future.
  49. */
  50. #define TAG_PADDING 256
  51. /*
  52. * NAME: tag->new()
  53. * DESCRIPTION: allocate and return a new, empty tag
  54. */
  55. struct id3_2_3_tag *id3_2_3_tag_new(void)
  56. {
  57. struct id3_2_3_tag *tag;
  58. tag = malloc(sizeof(*tag));
  59. if (tag) {
  60. tag->refcount = 0;
  61. tag->version = ID3_2_3_TAG_VERSION;
  62. tag->flags = 0;
  63. tag->extendedflags = 0;
  64. tag->restrictions = 0;
  65. tag->options = /* ID3_2_3_TAG_OPTION_UNSYNCHRONISATION | */
  66. ID3_2_3_TAG_OPTION_COMPRESSION | ID3_2_3_TAG_OPTION_CRC;
  67. tag->nframes = 0;
  68. tag->frames = 0;
  69. tag->paddedsize = 0;
  70. }
  71. return tag;
  72. }
  73. /*
  74. * NAME: tag->delete()
  75. * DESCRIPTION: destroy a tag and deallocate all associated memory
  76. */
  77. void id3_2_3_tag_delete(struct id3_2_3_tag *tag)
  78. {
  79. assert(tag);
  80. if (tag->refcount == 0) {
  81. id3_2_3_tag_clearframes(tag);
  82. if (tag->frames)
  83. free(tag->frames);
  84. free(tag);
  85. }
  86. }
  87. /*
  88. * NAME: tag->addref()
  89. * DESCRIPTION: add an external reference to a tag
  90. */
  91. void id3_2_3_tag_addref(struct id3_2_3_tag *tag)
  92. {
  93. assert(tag);
  94. ++tag->refcount;
  95. }
  96. /*
  97. * NAME: tag->delref()
  98. * DESCRIPTION: remove an external reference to a tag
  99. */
  100. void id3_2_3_tag_delref(struct id3_2_3_tag *tag)
  101. {
  102. assert(tag && tag->refcount > 0);
  103. --tag->refcount;
  104. }
  105. /*
  106. * NAME: tag->version()
  107. * DESCRIPTION: return the tag's original ID3 version number
  108. */
  109. unsigned int id3_2_3_tag_version(struct id3_2_3_tag const *tag)
  110. {
  111. assert(tag);
  112. return tag->version;
  113. }
  114. /*
  115. * NAME: tag->options()
  116. * DESCRIPTION: get or set tag options
  117. */
  118. int id3_2_3_tag_options(struct id3_2_3_tag *tag, int mask, int values)
  119. {
  120. assert(tag);
  121. if (mask)
  122. tag->options = (tag->options & ~mask) | (values & mask);
  123. return tag->options;
  124. }
  125. /*
  126. * NAME: tag->setlength()
  127. * DESCRIPTION: set the minimum rendered tag size
  128. */
  129. void id3_2_3_tag_setlength(struct id3_2_3_tag *tag, id3_2_3_length_t length)
  130. {
  131. assert(tag);
  132. tag->paddedsize = length;
  133. }
  134. /*
  135. * NAME: tag->clearframes()
  136. * DESCRIPTION: detach and delete all frames associated with a tag
  137. */
  138. void id3_2_3_tag_clearframes(struct id3_2_3_tag *tag)
  139. {
  140. unsigned int i;
  141. assert(tag);
  142. for (i = 0; i < tag->nframes; ++i) {
  143. id3_2_3_frame_delref(tag->frames[i]);
  144. id3_2_3_frame_delete(tag->frames[i]);
  145. }
  146. tag->nframes = 0;
  147. }
  148. /*
  149. * NAME: tag->sanitycheckframe()
  150. * DESCRIPTION: Check to see if the frames are valid. If not return 0.
  151. */
  152. int id3_2_3_tag_sanitycheckframe(struct id3_2_3_frame *frame)
  153. {
  154. union id3_2_3_field *field;
  155. int i;
  156. id3_2_3_latin1_t *str;
  157. const id3_2_3_ucs4_t *unicode;
  158. if (strcmp(frame->id, "TCON"))
  159. return 1;
  160. for(i = 0;; i++)
  161. {
  162. field = id3_2_3_frame_field(frame, i);
  163. if (!field)
  164. break;
  165. if (id3_2_3_field_type(field) == ID3_2_3_FIELD_TYPE_STRINGLIST)
  166. {
  167. unicode = id3_2_3_field_getstrings(field, 0);
  168. if (unicode)
  169. {
  170. str = id3_2_3_ucs4_latin1duplicate(unicode);
  171. if (strcasecmp(str, "other") == 0)
  172. {
  173. free(str);
  174. return 0;
  175. }
  176. free(str);
  177. }
  178. }
  179. }
  180. return 1;
  181. }
  182. /*
  183. * NAME: tag->attachframe()
  184. * DESCRIPTION: attach a frame to a tag
  185. */
  186. int id3_2_3_tag_attachframe(struct id3_2_3_tag *tag, struct id3_2_3_frame *frame)
  187. {
  188. struct id3_2_3_frame **frames;
  189. assert(tag && frame);
  190. if (!id3_2_3_tag_sanitycheckframe(frame))
  191. return 0;
  192. frames = realloc(tag->frames, (tag->nframes + 1) * sizeof(*frames));
  193. if (frames == 0)
  194. return -1;
  195. tag->frames = frames;
  196. tag->frames[tag->nframes++] = frame;
  197. id3_2_3_frame_addref(frame);
  198. return 0;
  199. }
  200. /*
  201. * NAME: tag->detachframe()
  202. * DESCRIPTION: detach (but don't delete) a frame from a tag
  203. */
  204. int id3_2_3_tag_detachframe(struct id3_2_3_tag *tag, struct id3_2_3_frame *frame)
  205. {
  206. unsigned int i;
  207. assert(tag && frame);
  208. for (i = 0; i < tag->nframes; ++i) {
  209. if (tag->frames[i] == frame)
  210. break;
  211. }
  212. if (i == tag->nframes)
  213. return -1;
  214. --tag->nframes;
  215. while (i++ < tag->nframes)
  216. tag->frames[i - 1] = tag->frames[i];
  217. id3_2_3_frame_delref(frame);
  218. return 0;
  219. }
  220. /*
  221. * NAME: tag->findframe()
  222. * DESCRIPTION: find in a tag the nth (0-based) frame with the given frame ID
  223. */
  224. struct id3_2_3_frame *id3_2_3_tag_findframe(struct id3_2_3_tag const *tag,
  225. char const *id, unsigned int index)
  226. {
  227. unsigned int len, i;
  228. assert(tag);
  229. if (id == 0 || *id == 0)
  230. return (index < tag->nframes) ? tag->frames[index] : 0;
  231. len = strlen(id);
  232. if (len == 4) {
  233. struct id3_2_3_compat const *compat;
  234. compat = id3_2_3_compat_lookup(id, len);
  235. if (compat && compat->equiv && !compat->translate) {
  236. id = compat->equiv;
  237. len = strlen(id);
  238. }
  239. }
  240. for (i = 0; i < tag->nframes; ++i) {
  241. if (strncmp(tag->frames[i]->id, id, len) == 0 && index-- == 0)
  242. return tag->frames[i];
  243. }
  244. return 0;
  245. }
  246. enum tagtype {
  247. TAGTYPE_NONE = 0,
  248. TAGTYPE_ID3V1,
  249. TAGTYPE_ID3V2,
  250. TAGTYPE_ID3V2_FOOTER
  251. };
  252. static
  253. enum tagtype tagtype(id3_2_3_byte_t const *data, id3_2_3_length_t length)
  254. {
  255. if (length >= 3 &&
  256. data[0] == 'T' && data[1] == 'A' && data[2] == 'G')
  257. return TAGTYPE_ID3V1;
  258. if (length >= 10 &&
  259. ((data[0] == 'I' && data[1] == 'D' && data[2] == '3') ||
  260. (data[0] == '3' && data[1] == 'D' && data[2] == 'I')) &&
  261. data[3] < 0xff && data[4] < 0xff &&
  262. data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80)
  263. return data[0] == 'I' ? TAGTYPE_ID3V2 : TAGTYPE_ID3V2_FOOTER;
  264. return TAGTYPE_NONE;
  265. }
  266. static
  267. void parse_header(id3_2_3_byte_t const **ptr,
  268. unsigned int *version, int *flags, id3_2_3_length_t *size)
  269. {
  270. *ptr += 3;
  271. *version = id3_2_3_parse_uint(ptr, 2);
  272. *flags = id3_2_3_parse_uint(ptr, 1);
  273. *size = id3_2_3_parse_syncsafe(ptr, 4);
  274. }
  275. /*
  276. * NAME: tag->query()
  277. * DESCRIPTION: if a tag begins at the given location, return its size
  278. */
  279. signed long id3_2_3_tag_query(id3_2_3_byte_t const *data, id3_2_3_length_t length)
  280. {
  281. unsigned int version;
  282. int flags;
  283. id3_2_3_length_t size;
  284. assert(data);
  285. switch (tagtype(data, length)) {
  286. case TAGTYPE_ID3V1:
  287. return 128;
  288. case TAGTYPE_ID3V2:
  289. parse_header(&data, &version, &flags, &size);
  290. if (flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT)
  291. size += 10;
  292. return 10 + size;
  293. case TAGTYPE_ID3V2_FOOTER:
  294. parse_header(&data, &version, &flags, &size);
  295. return -size - 10;
  296. case TAGTYPE_NONE:
  297. break;
  298. }
  299. return 0;
  300. }
  301. static
  302. void trim(char *str)
  303. {
  304. char *ptr;
  305. ptr = str + strlen(str);
  306. while (ptr > str && ptr[-1] == ' ')
  307. --ptr;
  308. *ptr = 0;
  309. }
  310. static
  311. int v1_attachstr(struct id3_2_3_tag *tag, char const *id,
  312. char *text, unsigned long number)
  313. {
  314. struct id3_2_3_frame *frame;
  315. id3_2_3_ucs4_t ucs4[31];
  316. if (text) {
  317. trim(text);
  318. if (*text == 0)
  319. return 0;
  320. }
  321. frame = id3_2_3_frame_new(id);
  322. if (frame == 0)
  323. return -1;
  324. if (id3_2_3_field_settextencoding(&frame->fields[0],
  325. ID3_2_3_FIELD_TEXTENCODING_ISO_8859_1) == -1)
  326. goto fail;
  327. if (text)
  328. id3_2_3_latin1_decode(text, ucs4);
  329. else
  330. id3_2_3_ucs4_putnumber(ucs4, number);
  331. if (strcmp(id, ID3_2_3_FRAME_COMMENT) == 0) {
  332. if (id3_2_3_field_setlanguage(&frame->fields[1], "XXX") == -1 ||
  333. id3_2_3_field_setstring(&frame->fields[2], id3_2_3_ucs4_empty) == -1 ||
  334. id3_2_3_field_setfullstring(&frame->fields[3], ucs4) == -1)
  335. goto fail;
  336. }
  337. else {
  338. id3_2_3_ucs4_t *ptr = ucs4;
  339. if (id3_2_3_field_setstrings(&frame->fields[1], 1, &ptr) == -1)
  340. goto fail;
  341. }
  342. if (id3_2_3_tag_attachframe(tag, frame) == -1)
  343. goto fail;
  344. return 0;
  345. fail:
  346. id3_2_3_frame_delete(frame);
  347. return -1;
  348. }
  349. static
  350. struct id3_2_3_tag *v1_parse(id3_2_3_byte_t const *data)
  351. {
  352. struct id3_2_3_tag *tag;
  353. tag = id3_2_3_tag_new();
  354. if (tag) {
  355. char title[31], artist[31], album[31], year[5], comment[31], genreStr[255];
  356. unsigned int genre, track;
  357. tag->version = 0x0100;
  358. tag->options |= ID3_2_3_TAG_OPTION_ID3V1;
  359. tag->options &= ~ID3_2_3_TAG_OPTION_COMPRESSION;
  360. tag->restrictions =
  361. ID3_2_3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 |
  362. ID3_2_3_TAG_RESTRICTION_TEXTSIZE_30_CHARS;
  363. title[30] = artist[30] = album[30] = year[4] = comment[30] = 0;
  364. memcpy(title, &data[3], 30);
  365. memcpy(artist, &data[33], 30);
  366. memcpy(album, &data[63], 30);
  367. memcpy(year, &data[93], 4);
  368. memcpy(comment, &data[97], 30);
  369. genre = data[127];
  370. track = 0;
  371. if (comment[28] == 0 && comment[29] != 0) {
  372. track = comment[29];
  373. tag->version = 0x0101;
  374. }
  375. /* populate tag frames */
  376. sprintf(genreStr, "(%d)", genre);
  377. if (v1_attachstr(tag, ID3_2_3_FRAME_TITLE, title, 0) == -1 ||
  378. v1_attachstr(tag, ID3_2_3_FRAME_ARTIST, artist, 0) == -1 ||
  379. v1_attachstr(tag, ID3_2_3_FRAME_ALBUM, album, 0) == -1 ||
  380. v1_attachstr(tag, ID3_2_3_FRAME_YEAR, year, 0) == -1 ||
  381. (track && v1_attachstr(tag, ID3_2_3_FRAME_TRACK, 0, track) == -1) ||
  382. (genre < 0xff && v1_attachstr(tag, ID3_2_3_FRAME_GENRE, genreStr, 0) == -1) ||
  383. v1_attachstr(tag, ID3_2_3_FRAME_COMMENT, comment, 0) == -1) {
  384. id3_2_3_tag_delete(tag);
  385. tag = 0;
  386. }
  387. }
  388. return tag;
  389. }
  390. static
  391. struct id3_2_3_tag *v2_parse(id3_2_3_byte_t const *ptr)
  392. {
  393. struct id3_2_3_tag *tag;
  394. id3_2_3_byte_t *mem = 0;
  395. tag = id3_2_3_tag_new();
  396. if (tag) {
  397. id3_2_3_byte_t const *end;
  398. id3_2_3_length_t size;
  399. parse_header(&ptr, &tag->version, &tag->flags, &size);
  400. tag->paddedsize = 10 + size;
  401. if ((tag->flags & ID3_2_3_TAG_FLAG_UNSYNCHRONISATION) &&
  402. ID3_2_3_TAG_VERSION_MAJOR(tag->version) < 4) {
  403. mem = malloc(size);
  404. if (mem == 0)
  405. goto fail;
  406. memcpy(mem, ptr, size);
  407. size = id3_2_3_util_deunsynchronise(mem, size);
  408. ptr = mem;
  409. }
  410. end = ptr + size;
  411. if (tag->flags & ID3_2_3_TAG_FLAG_EXTENDEDHEADER) {
  412. switch (ID3_2_3_TAG_VERSION_MAJOR(tag->version)) {
  413. case 2:
  414. goto fail;
  415. case 3:
  416. {
  417. id3_2_3_byte_t const *ehptr, *ehend;
  418. id3_2_3_length_t ehsize;
  419. enum {
  420. EH_FLAG_CRC = 0x8000 /* CRC data present */
  421. };
  422. if (end - ptr < 4)
  423. goto fail;
  424. ehsize = id3_2_3_parse_uint(&ptr, 4);
  425. if (ehsize > end - ptr)
  426. goto fail;
  427. ehptr = ptr;
  428. ehend = ptr + ehsize;
  429. ptr = ehend;
  430. if (ehend - ehptr >= 6) {
  431. int ehflags;
  432. id3_2_3_length_t padsize;
  433. ehflags = id3_2_3_parse_uint(&ehptr, 2);
  434. padsize = id3_2_3_parse_uint(&ehptr, 4);
  435. if (padsize > end - ptr)
  436. goto fail;
  437. end -= padsize;
  438. if (ehflags & EH_FLAG_CRC) {
  439. unsigned long crc;
  440. if (ehend - ehptr < 4)
  441. goto fail;
  442. crc = id3_2_3_parse_uint(&ehptr, 4);
  443. if (crc != id3_2_3_crc_calculate(ptr, end - ptr))
  444. goto fail;
  445. tag->extendedflags |= ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
  446. }
  447. }
  448. }
  449. break;
  450. case 4:
  451. {
  452. id3_2_3_byte_t const *ehptr, *ehend;
  453. id3_2_3_length_t ehsize;
  454. unsigned int bytes;
  455. if (end - ptr < 4)
  456. goto fail;
  457. ehptr = ptr;
  458. ehsize = id3_2_3_parse_syncsafe(&ptr, 4);
  459. if (ehsize < 6 || ehsize > end - ehptr)
  460. goto fail;
  461. ehend = ehptr + ehsize;
  462. bytes = id3_2_3_parse_uint(&ptr, 1);
  463. if (bytes < 1 || bytes > ehend - ptr)
  464. goto fail;
  465. ehptr = ptr + bytes;
  466. /* verify extended header size */
  467. {
  468. id3_2_3_byte_t const *flagsptr = ptr, *dataptr = ehptr;
  469. unsigned int datalen;
  470. int ehflags;
  471. while (bytes--) {
  472. for (ehflags = id3_2_3_parse_uint(&flagsptr, 1); ehflags;
  473. ehflags = (ehflags << 1) & 0xff) {
  474. if (ehflags & 0x80) {
  475. if (dataptr == ehend)
  476. goto fail;
  477. datalen = id3_2_3_parse_uint(&dataptr, 1);
  478. if (datalen > 0x7f || datalen > ehend - dataptr)
  479. goto fail;
  480. dataptr += datalen;
  481. }
  482. }
  483. }
  484. }
  485. tag->extendedflags = id3_2_3_parse_uint(&ptr, 1);
  486. ptr = ehend;
  487. if (tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGISANUPDATE) {
  488. bytes = id3_2_3_parse_uint(&ehptr, 1);
  489. ehptr += bytes;
  490. }
  491. if (tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
  492. unsigned long crc;
  493. bytes = id3_2_3_parse_uint(&ehptr, 1);
  494. if (bytes < 5)
  495. goto fail;
  496. crc = id3_2_3_parse_syncsafe(&ehptr, 5);
  497. ehptr += bytes - 5;
  498. if (crc != id3_2_3_crc_calculate(ptr, end - ptr))
  499. goto fail;
  500. }
  501. if (tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
  502. bytes = id3_2_3_parse_uint(&ehptr, 1);
  503. if (bytes < 1)
  504. goto fail;
  505. tag->restrictions = id3_2_3_parse_uint(&ehptr, 1);
  506. ehptr += bytes - 1;
  507. }
  508. }
  509. break;
  510. }
  511. }
  512. /* frames */
  513. while (ptr < end) {
  514. struct id3_2_3_frame *frame;
  515. if (*ptr == 0)
  516. break; /* padding */
  517. if (memcmp(ptr, "MP3ext", end - ptr < 6 ? end - ptr : 6) == 0)
  518. break; /* non-standard 'MP3ext' padding */
  519. frame = id3_2_3_frame_parse(&ptr, end - ptr, tag->version);
  520. if (frame == 0 || id3_2_3_tag_attachframe(tag, frame) == -1)
  521. goto fail;
  522. }
  523. if (ID3_2_3_TAG_VERSION_MAJOR(tag->version) < 4 &&
  524. ID3_2_3_TAG_VERSION == 4 &&
  525. id3_2_3_compat_fixup(tag) == -1)
  526. goto fail;
  527. }
  528. if (0) {
  529. fail:
  530. id3_2_3_tag_delete(tag);
  531. tag = 0;
  532. }
  533. if (mem)
  534. free(mem);
  535. return tag;
  536. }
  537. /*
  538. * NAME: tag->parse()
  539. * DESCRIPTION: parse a complete ID3 tag
  540. */
  541. struct id3_2_3_tag *id3_2_3_tag_parse(id3_2_3_byte_t const *data, id3_2_3_length_t length)
  542. {
  543. id3_2_3_byte_t const *ptr;
  544. unsigned int version;
  545. int flags;
  546. id3_2_3_length_t size;
  547. assert(data);
  548. switch (tagtype(data, length)) {
  549. case TAGTYPE_ID3V1:
  550. return (length < 128) ? 0 : v1_parse(data);
  551. case TAGTYPE_ID3V2:
  552. break;
  553. case TAGTYPE_ID3V2_FOOTER:
  554. case TAGTYPE_NONE:
  555. return 0;
  556. }
  557. /* ID3v2.x */
  558. ptr = data;
  559. parse_header(&ptr, &version, &flags, &size);
  560. switch (ID3_2_3_TAG_VERSION_MAJOR(version)) {
  561. case 4:
  562. if (flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT)
  563. size += 10;
  564. case 2:
  565. case 3:
  566. return (length < 10 + size) ? 0 : v2_parse(data);
  567. }
  568. return 0;
  569. }
  570. static
  571. void v1_renderstr(struct id3_2_3_tag const *tag, char const *frameid,
  572. id3_2_3_byte_t **buffer, id3_2_3_length_t length)
  573. {
  574. struct id3_2_3_frame *frame;
  575. id3_2_3_ucs4_t const *string;
  576. frame = id3_2_3_tag_findframe(tag, frameid, 0);
  577. if (frame == 0)
  578. string = id3_2_3_ucs4_empty;
  579. else {
  580. if (strcmp(frameid, ID3_2_3_FRAME_COMMENT) == 0)
  581. string = id3_2_3_field_getfullstring(&frame->fields[3]);
  582. else
  583. string = id3_2_3_field_getstrings(&frame->fields[1], 0);
  584. }
  585. id3_2_3_render_paddedstring(buffer, string, length);
  586. }
  587. /*
  588. * NAME: v1->render()
  589. * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag
  590. */
  591. static
  592. id3_2_3_length_t v1_render(struct id3_2_3_tag const *tag, id3_2_3_byte_t *buffer)
  593. {
  594. id3_2_3_byte_t data[128], *ptr;
  595. struct id3_2_3_frame *frame;
  596. unsigned int i;
  597. int genre = -1;
  598. ptr = data;
  599. id3_2_3_render_immediate(&ptr, "TAG", 3);
  600. v1_renderstr(tag, ID3_2_3_FRAME_TITLE, &ptr, 30);
  601. v1_renderstr(tag, ID3_2_3_FRAME_ARTIST, &ptr, 30);
  602. v1_renderstr(tag, ID3_2_3_FRAME_ALBUM, &ptr, 30);
  603. v1_renderstr(tag, ID3_2_3_FRAME_YEAR, &ptr, 4);
  604. v1_renderstr(tag, ID3_2_3_FRAME_COMMENT, &ptr, 30);
  605. /* ID3v1.1 track number */
  606. frame = id3_2_3_tag_findframe(tag, ID3_2_3_FRAME_TRACK, 0);
  607. if (frame) {
  608. unsigned int track;
  609. track = id3_2_3_ucs4_getnumber(id3_2_3_field_getstrings(&frame->fields[1], 0));
  610. if (track > 0 && track <= 0xff) {
  611. ptr[-2] = 0;
  612. ptr[-1] = track;
  613. }
  614. }
  615. /* ID3v1 genre number */
  616. frame = id3_2_3_tag_findframe(tag, ID3_2_3_FRAME_GENRE, 0);
  617. if (frame) {
  618. unsigned int nstrings;
  619. nstrings = id3_2_3_field_getnstrings(&frame->fields[1]);
  620. for (i = 0; i < nstrings; ++i) {
  621. genre = id3_2_3_genre_number(id3_2_3_field_getstrings(&frame->fields[1], i));
  622. if (genre != -1)
  623. break;
  624. }
  625. if (i == nstrings && nstrings > 0)
  626. genre = ID3_2_3_GENRE_OTHER;
  627. }
  628. id3_2_3_render_int(&ptr, genre, 1);
  629. /* make sure the tag is not empty */
  630. if (genre == -1) {
  631. for (i = 3; i < 127; ++i) {
  632. if (data[i] != ' ')
  633. break;
  634. }
  635. if (i == 127)
  636. return 0;
  637. }
  638. if (buffer)
  639. memcpy(buffer, data, 128);
  640. return 128;
  641. }
  642. /*
  643. * NAME: tag->render()
  644. * DESCRIPTION: render a complete ID3 tag
  645. */
  646. /* RAK: Note: This file used to be const, but since the padding might be
  647. * adjusted by this function, we can't do that anymore.
  648. */
  649. id3_2_3_length_t id3_2_3_tag_render(struct id3_2_3_tag *tag, id3_2_3_byte_t *buffer)
  650. {
  651. id3_2_3_length_t size = 0;
  652. id3_2_3_byte_t **ptr,
  653. *header_ptr = 0, *tagsize_ptr = 0, *crc_ptr = 0, *frames_ptr = 0;
  654. int flags, extendedflags;
  655. unsigned int i;
  656. assert(tag);
  657. if (tag->options & ID3_2_3_TAG_OPTION_ID3V1)
  658. return v1_render(tag, buffer);
  659. /* a tag must contain at least one (renderable) frame */
  660. for (i = 0; i < tag->nframes; ++i) {
  661. if (id3_2_3_frame_render(tag->frames[i], 0, 0) > 0)
  662. break;
  663. }
  664. if (i == tag->nframes)
  665. return 0;
  666. ptr = buffer ? &buffer : 0;
  667. /* get flags */
  668. flags = tag->flags & ID3_2_3_TAG_FLAG_KNOWNFLAGS;
  669. extendedflags = tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_KNOWNFLAGS;
  670. extendedflags &= ~ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
  671. if (tag->options & ID3_2_3_TAG_OPTION_CRC)
  672. extendedflags |= ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
  673. extendedflags &= ~ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
  674. if (tag->restrictions)
  675. extendedflags |= ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
  676. flags &= ~ID3_2_3_TAG_FLAG_UNSYNCHRONISATION;
  677. if (tag->options & ID3_2_3_TAG_OPTION_UNSYNCHRONISATION)
  678. flags |= ID3_2_3_TAG_FLAG_UNSYNCHRONISATION;
  679. flags &= ~ID3_2_3_TAG_FLAG_EXTENDEDHEADER;
  680. if (extendedflags)
  681. flags |= ID3_2_3_TAG_FLAG_EXTENDEDHEADER;
  682. flags &= ~ID3_2_3_TAG_FLAG_FOOTERPRESENT;
  683. if (tag->options & ID3_2_3_TAG_OPTION_APPENDEDTAG)
  684. flags |= ID3_2_3_TAG_FLAG_FOOTERPRESENT;
  685. /* header */
  686. if (ptr)
  687. header_ptr = *ptr;
  688. size += id3_2_3_render_immediate(ptr, "ID3", 3);
  689. size += id3_2_3_render_int(ptr, ID3_2_3_TAG_VERSION, 2);
  690. size += id3_2_3_render_int(ptr, flags, 1);
  691. if (ptr)
  692. tagsize_ptr = *ptr;
  693. size += id3_2_3_render_syncsafe(ptr, 0, 4);
  694. /* extended header */
  695. if (flags & ID3_2_3_TAG_FLAG_EXTENDEDHEADER) {
  696. id3_2_3_length_t ehsize = 0;
  697. id3_2_3_byte_t *ehsize_ptr = 0;
  698. if (ptr)
  699. ehsize_ptr = *ptr;
  700. ehsize += id3_2_3_render_syncsafe(ptr, 0, 4);
  701. ehsize += id3_2_3_render_int(ptr, 1, 1);
  702. ehsize += id3_2_3_render_int(ptr, extendedflags, 1);
  703. if (extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGISANUPDATE)
  704. ehsize += id3_2_3_render_int(ptr, 0, 1);
  705. if (extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
  706. ehsize += id3_2_3_render_int(ptr, 5, 1);
  707. if (ptr)
  708. crc_ptr = *ptr;
  709. ehsize += id3_2_3_render_syncsafe(ptr, 0, 5);
  710. }
  711. if (extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
  712. ehsize += id3_2_3_render_int(ptr, 1, 1);
  713. ehsize += id3_2_3_render_int(ptr, tag->restrictions, 1);
  714. }
  715. if (ehsize_ptr)
  716. id3_2_3_render_syncsafe(&ehsize_ptr, ehsize, 4);
  717. size += ehsize;
  718. }
  719. /* frames */
  720. if (ptr)
  721. frames_ptr = *ptr;
  722. for (i = 0; i < tag->nframes; ++i)
  723. size += id3_2_3_frame_render(tag->frames[i], ptr, tag->options);
  724. /* padding */
  725. if (!(flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT)) {
  726. if (size > tag->paddedsize)
  727. tag->paddedsize += TAG_PADDING + size;
  728. if (size < tag->paddedsize)
  729. size += id3_2_3_render_padding(ptr, 0, tag->paddedsize - size);
  730. else if (tag->options & ID3_2_3_TAG_OPTION_UNSYNCHRONISATION) {
  731. if (ptr == 0)
  732. size += 1;
  733. else {
  734. if ((*ptr)[-1] == 0xff)
  735. size += id3_2_3_render_padding(ptr, 0, 1);
  736. }
  737. }
  738. }
  739. /* patch tag size and CRC */
  740. if (tagsize_ptr)
  741. id3_2_3_render_syncsafe(&tagsize_ptr, size - 10, 4);
  742. if (crc_ptr) {
  743. id3_2_3_render_syncsafe(&crc_ptr,
  744. id3_2_3_crc_calculate(frames_ptr, *ptr - frames_ptr), 5);
  745. }
  746. /* footer */
  747. if (flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT) {
  748. size += id3_2_3_render_immediate(ptr, "3DI", 3);
  749. size += id3_2_3_render_binary(ptr, header_ptr + 3, 7);
  750. }
  751. return size;
  752. }