text: /*
   * ID3v1 header parser
   * Copyright (c) 2003 Fabrice Bellard
   *
   * This file is part of FFmpeg.
   *
   * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
  #include "id3v1.h"
  #include "libavcodec/avcodec.h"
  #include "libavutil/dict.h"
  
  /* See Genre List at http://id3.org/id3v2.3.0 */
  const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = {
        [0] = "Blues",
        [1] = "Classic Rock",
        [2] = "Country",
        [3] = "Dance",
        [4] = "Disco",
        [5] = "Funk",
        [6] = "Grunge",
        [7] = "Hip-Hop",
        [8] = "Jazz",
        [9] = "Metal",
       [10] = "New Age",
       [11] = "Oldies",
       [12] = "Other",
       [13] = "Pop",
       [14] = "R&B",
       [15] = "Rap",
       [16] = "Reggae",
       [17] = "Rock",
       [18] = "Techno",
       [19] = "Industrial",
       [20] = "Alternative",
       [21] = "Ska",
       [22] = "Death Metal",
       [23] = "Pranks",
       [24] = "Soundtrack",
       [25] = "Euro-Techno",
       [26] = "Ambient",
       [27] = "Trip-Hop",
       [28] = "Vocal",
       [29] = "Jazz+Funk",
       [30] = "Fusion",
       [31] = "Trance",
       [32] = "Classical",
       [33] = "Instrumental",
       [34] = "Acid",
       [35] = "House",
       [36] = "Game",
       [37] = "Sound Clip",
       [38] = "Gospel",
       [39] = "Noise",
       [40] = "AlternRock",
       [41] = "Bass",
       [42] = "Soul",
       [43] = "Punk",
       [44] = "Space",
       [45] = "Meditative",
       [46] = "Instrumental Pop",
       [47] = "Instrumental Rock",
       [48] = "Ethnic",
       [49] = "Gothic",
       [50] = "Darkwave",
       [51] = "Techno-Industrial",
       [52] = "Electronic",
       [53] = "Pop-Folk",
       [54] = "Eurodance",
       [55] = "Dream",
       [56] = "Southern Rock",
       [57] = "Comedy",
       [58] = "Cult",
       [59] = "Gangsta",
       [60] = "Top 40",
       [61] = "Christian Rap",
       [62] = "Pop/Funk",
       [63] = "Jungle",
       [64] = "Native American",
       [65] = "Cabaret",
       [66] = "New Wave",
       [67] = "Psychadelic", /* sic, the misspelling is used in the specification */
       [68] = "Rave",
       [69] = "Showtunes",
       [70] = "Trailer",
       [71] = "Lo-Fi",
       [72] = "Tribal",
       [73] = "Acid Punk",
       [74] = "Acid Jazz",
       [75] = "Polka",
       [76] = "Retro",
       [77] = "Musical",
       [78] = "Rock & Roll",
       [79] = "Hard Rock",
       [80] = "Folk",
       [81] = "Folk-Rock",
       [82] = "National Folk",
       [83] = "Swing",
       [84] = "Fast Fusion",
       [85] = "Bebob",
       [86] = "Latin",
       [87] = "Revival",
       [88] = "Celtic",
       [89] = "Bluegrass",
       [90] = "Avantgarde",
       [91] = "Gothic Rock",
       [92] = "Progressive Rock",
       [93] = "Psychedelic Rock",
       [94] = "Symphonic Rock",
       [95] = "Slow Rock",
       [96] = "Big Band",
       [97] = "Chorus",
       [98] = "Easy Listening",
       [99] = "Acoustic",
      [100] = "Humour",
      [101] = "Speech",
      [102] = "Chanson",
      [103] = "Opera",
      [104] = "Chamber Music",
      [105] = "Sonata",
      [106] = "Symphony",
      [107] = "Booty Bass",
      [108] = "Primus",
      [109] = "Porn Groove",
      [110] = "Satire",
      [111] = "Slow Jam",
      [112] = "Club",
      [113] = "Tango",
      [114] = "Samba",
      [115] = "Folklore",
      [116] = "Ballad",
      [117] = "Power Ballad",
      [118] = "Rhythmic Soul",
      [119] = "Freestyle",
      [120] = "Duet",
      [121] = "Punk Rock",
      [122] = "Drum Solo",
      [123] = "A capella",
      [124] = "Euro-House",
      [125] = "Dance Hall",
      [126] = "Goa",
      [127] = "Drum & Bass",
      [128] = "Club-House",
      [129] = "Hardcore",
      [130] = "Terror",
      [131] = "Indie",
      [132] = "BritPop",
      [133] = "Negerpunk",
      [134] = "Polsk Punk",
      [135] = "Beat",
      [136] = "Christian Gangsta",
      [137] = "Heavy Metal",
      [138] = "Black Metal",
      [139] = "Crossover",
      [140] = "Contemporary Christian",
      [141] = "Christian Rock",
      [142] = "Merengue",
      [143] = "Salsa",
      [144] = "Thrash Metal",
      [145] = "Anime",
      [146] = "JPop",
      [147] = "SynthPop",
  };
  
  static void get_string(AVFormatContext *s, const char *key,
                         const uint8_t *buf, int buf_size)
  {
      int i, c;
      char *q, str[512];
  
      q = str;
      for(i = 0; i < buf_size; i++) {
          c = buf[i];
          if (c == '\0')
              break;
          if ((q - str) >= sizeof(str) - 1)
              break;
          *q++ = c;
      }
      *q = '\0';
  
      if (*str)
          av_dict_set(&s->metadata, key, str, 0);
  }
  
  /**
   * Parse an ID3v1 tag
   *
   * @param buf ID3v1_TAG_SIZE long buffer containing the tag
   */
  static int parse_tag(AVFormatContext *s, const uint8_t *buf)
  {
      int genre;
  
      if (!(buf[0] == 'T' &&
            buf[1] == 'A' &&
            buf[2] == 'G'))
          return -1;
      get_string(s, "title",   buf +  3, 30);
      get_string(s, "artist",  buf + 33, 30);
      get_string(s, "album",   buf + 63, 30);
      get_string(s, "date",    buf + 93,  4);
      get_string(s, "comment", buf + 97, 30);
      if (buf[125] == 0 && buf[126] != 0) {
          av_dict_set_int(&s->metadata, "track", buf[126], 0);
      }
      genre = buf[127];
      if (genre <= ID3v1_GENRE_MAX)
          av_dict_set(&s->metadata, "genre", ff_id3v1_genre_str[genre], 0);
      return 0;
  }
  
  void ff_id3v1_read(AVFormatContext *s)
  {
      int ret;
      uint8_t buf[ID3v1_TAG_SIZE];
      int64_t filesize, position = avio_tell(s->pb);
  
      if (s->pb->seekable) {
          /* XXX: change that */
          filesize = avio_size(s->pb);
          if (filesize > 128) {
              avio_seek(s->pb, filesize - 128, SEEK_SET);
              ret = avio_read(s->pb, buf, ID3v1_TAG_SIZE);
              if (ret == ID3v1_TAG_SIZE) {
                  parse_tag(s, buf);
              }
              avio_seek(s->pb, position, SEEK_SET);
          }
      }
  }
  