libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

nsfe_extractor.c (9205B)


      1 /*
      2  * This file is part of libextractor.
      3  * Copyright (C) 2007, 2009, 2012 Toni Ruottu and Christian Grothoff
      4  *
      5  * libextractor is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published
      7  * by the Free Software Foundation; either version 3, or (at your
      8  * option) any later version.
      9  *
     10  * libextractor is distributed in the hope that it will be useful, but
     11  * WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with libextractor; see the file COPYING.  If not, write to the
     17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  *
     20  */
     21 /**
     22  * @file plugins/nsfe_extractor.c
     23  * @brief plugin to support Nes Sound Format files
     24  * @author Toni Ruottu
     25  * @author Christian Grothoff
     26  */
     27 #include "platform.h"
     28 #include "extractor.h"
     29 #include "convert.h"
     30 
     31 
     32 /* television system flags */
     33 #define PAL_FLAG     0x01
     34 #define DUAL_FLAG    0x02
     35 
     36 /* sound chip flags */
     37 #define VRCVI_FLAG   0x01
     38 #define VRCVII_FLAG  0x02
     39 #define FDS_FLAG     0x04
     40 #define MMC5_FLAG    0x08
     41 #define NAMCO_FLAG   0x10
     42 #define SUNSOFT_FLAG 0x20
     43 
     44 /**
     45  * "Header" of an NSFE file.
     46  */
     47 struct header
     48 {
     49   char magicid[4];
     50 };
     51 
     52 
     53 /**
     54  * Read an unsigned integer at the current offset.
     55  *
     56  * @param data input data to parse
     57  * @return parsed integer
     58  */
     59 static uint32_t
     60 nsfeuint (const char *data)
     61 {
     62   uint32_t value = 0;
     63   int i;
     64 
     65   for (i = 3; i > 0; i--)
     66   {
     67     value += (unsigned char) data[i];
     68     value *= 0x100;
     69   }
     70   value += (unsigned char) data[0];
     71   return value;
     72 }
     73 
     74 
     75 /**
     76  * Copy string starting at 'data' with at most
     77  * 'size' bytes. (strndup).
     78  *
     79  * @param data input data to copy
     80  * @param size number of bytes in 'data'
     81  * @return copy of the string at data
     82  */
     83 static char *
     84 nsfestring (const char *data,
     85             size_t size)
     86 {
     87   char *s;
     88   size_t length;
     89 
     90   length = 0;
     91   while ( (length < size) &&
     92           (data[length] != '\0') )
     93     length++;
     94   if (NULL == (s = malloc (length + 1)))
     95     return NULL;
     96   memcpy (s, data, length);
     97   s[length] = '\0';
     98   return s;
     99 }
    100 
    101 
    102 /**
    103  * Give metadata to LE; return if 'proc' returns non-zero.
    104  *
    105  * @param s metadata value as UTF8
    106  * @param t metadata type to use
    107  */
    108 #define ADD(s,t) do { if (0 != ec->proc (ec->cls, "nsfe", t, \
    109                                          EXTRACTOR_METAFORMAT_UTF8, \
    110                                          "text/plain", s, strlen (s) \
    111                                          + 1)) return 1; \
    112 } while (0)
    113 
    114 
    115 /**
    116  * Give metadata to LE; return if 'proc' returns non-zero.
    117  *
    118  * @param s metadata value as UTF8, free at the end
    119  * @param t metadata type to use
    120  */
    121 #define ADDF(s,t) do { if (0 != ec->proc (ec->cls, "nsfe", t, \
    122                                           EXTRACTOR_METAFORMAT_UTF8, \
    123                                           "text/plain", s, strlen (s) \
    124                                           + 1)) { free (s); \
    125                                                   return 1; \
    126                        } free (s); } while (0)
    127 
    128 
    129 /**
    130  * Format of an 'INFO' chunk.  Last two bytes are optional.
    131  */
    132 struct infochunk
    133 {
    134   /**
    135    * Unknown.
    136    */
    137   uint16_t loadaddr;
    138 
    139   /**
    140    * Unknown.
    141    */
    142   uint16_t initaddr;
    143 
    144   /**
    145    * Unknown.
    146    */
    147   uint16_t playaddr;
    148 
    149   /**
    150    * TV encoding flags.
    151    */
    152   char tvflags;
    153 
    154   /**
    155    * Chipset encoding flags.
    156    */
    157   char chipflags;
    158 
    159   /**
    160    * Number of songs.
    161    */
    162   unsigned char songs;
    163 
    164   /**
    165    * Starting song.
    166    */
    167   unsigned char firstsong;
    168 };
    169 
    170 
    171 /**
    172  * Extract data from the INFO chunk.
    173  *
    174  * @param ec extraction context
    175  * @param size number of bytes in INFO chunk
    176  * @return 0 to continue extrating
    177  */
    178 static int
    179 info_extract (struct EXTRACTOR_ExtractContext *ec,
    180               uint32_t size)
    181 {
    182   void *data;
    183   const struct infochunk *ichunk;
    184   char songs[32];
    185 
    186   if (size < 8)
    187     return 0;
    188   if ((ssize_t) size >
    189       ec->read (ec->cls,
    190                 &data,
    191                 size))
    192     return 1;
    193   ichunk = data;
    194 
    195   if (0 != (ichunk->tvflags & DUAL_FLAG))
    196   {
    197     ADD ("PAL/NTSC", EXTRACTOR_METATYPE_BROADCAST_TELEVISION_SYSTEM);
    198   }
    199   else
    200   {
    201     if (0 != (ichunk->tvflags & PAL_FLAG))
    202       ADD ("PAL", EXTRACTOR_METATYPE_BROADCAST_TELEVISION_SYSTEM);
    203     else
    204       ADD ("NTSC", EXTRACTOR_METATYPE_BROADCAST_TELEVISION_SYSTEM);
    205   }
    206 
    207   if (0 != (ichunk->chipflags & VRCVI_FLAG))
    208     ADD ("VRCVI", EXTRACTOR_METATYPE_TARGET_ARCHITECTURE);
    209   if (0 != (ichunk->chipflags & VRCVII_FLAG))
    210     ADD ("VRCVII", EXTRACTOR_METATYPE_TARGET_ARCHITECTURE);
    211   if (0 != (ichunk->chipflags & FDS_FLAG))
    212     ADD ("FDS Sound", EXTRACTOR_METATYPE_TARGET_ARCHITECTURE);
    213   if (0 != (ichunk->chipflags & MMC5_FLAG))
    214     ADD ("MMC5 audio", EXTRACTOR_METATYPE_TARGET_ARCHITECTURE);
    215   if (0 != (ichunk->chipflags & NAMCO_FLAG))
    216     ADD ("Namco 106", EXTRACTOR_METATYPE_TARGET_ARCHITECTURE);
    217   if (0 != (ichunk->chipflags & SUNSOFT_FLAG))
    218     ADD ("Sunsoft FME-07", EXTRACTOR_METATYPE_TARGET_ARCHITECTURE);
    219 
    220   if (size < sizeof (struct infochunk))
    221   {
    222     ADD ("1", EXTRACTOR_METATYPE_SONG_COUNT);
    223     return 0;
    224   }
    225   snprintf (songs,
    226             sizeof (songs),
    227             "%d",
    228             ichunk->songs);
    229   ADD (songs, EXTRACTOR_METATYPE_SONG_COUNT);
    230   snprintf (songs,
    231             sizeof (songs),
    232             "%d",
    233             ichunk->firstsong);
    234   ADD (songs, EXTRACTOR_METATYPE_STARTING_SONG);
    235   return 0;
    236 }
    237 
    238 
    239 /**
    240  * Extract data from the TLBL chunk.
    241  *
    242  * @param ec extraction context
    243  * @param size number of bytes in TLBL chunk
    244  * @return 0 to continue extrating
    245  */
    246 static int
    247 tlbl_extract (struct EXTRACTOR_ExtractContext *ec,
    248               uint32_t size)
    249 {
    250   char *title;
    251   ssize_t left;
    252   size_t length;
    253   void *data;
    254   const char *cdata;
    255 
    256   if ((ssize_t) size >
    257       ec->read (ec->cls,
    258                 &data,
    259                 size))
    260     return 1;
    261   cdata = data;
    262 
    263   left = size;
    264   while (left > 0)
    265   {
    266     title = nsfestring (&cdata[size - left], left);
    267     if (NULL == title)
    268       return 0;
    269     length = strlen (title) + 1;
    270     ADDF (title, EXTRACTOR_METATYPE_TITLE);
    271     left -= length;
    272   }
    273   return 0;
    274 }
    275 
    276 
    277 /**
    278  * Extract data from the AUTH chunk.
    279  *
    280  * @param ec extraction context
    281  * @param size number of bytes in AUTH chunk
    282  * @return 0 to continue extrating
    283  */
    284 static int
    285 auth_extract (struct EXTRACTOR_ExtractContext *ec,
    286               uint32_t size)
    287 {
    288   char *album;
    289   char *artist;
    290   char *copyright;
    291   char *ripper;
    292   uint32_t left = size;
    293   void *data;
    294   const char *cdata;
    295 
    296   if (left < 1)
    297     return 0;
    298   if ((ssize_t) size >
    299       ec->read (ec->cls,
    300                 &data,
    301                 size))
    302     return 1;
    303   cdata = data;
    304 
    305   album = nsfestring (&cdata[size - left], left);
    306   if (NULL != album)
    307   {
    308     left -= (strlen (album) + 1);
    309     ADDF (album, EXTRACTOR_METATYPE_ALBUM);
    310     if (left < 1)
    311       return 0;
    312   }
    313 
    314   artist = nsfestring (&cdata[size - left], left);
    315   if (NULL != artist)
    316   {
    317     left -= (strlen (artist) + 1);
    318     ADDF (artist, EXTRACTOR_METATYPE_ARTIST);
    319     if (left < 1)
    320       return 0;
    321   }
    322 
    323   copyright = nsfestring (&cdata[size - left], left);
    324   if (NULL != copyright)
    325   {
    326     left -= (strlen (copyright) + 1);
    327     ADDF (copyright, EXTRACTOR_METATYPE_COPYRIGHT);
    328     if (left < 1)
    329       return 0;
    330   }
    331   ripper = nsfestring (&cdata[size - left], left);
    332   if (NULL != ripper)
    333     ADDF (ripper, EXTRACTOR_METATYPE_RIPPER);
    334   return 0;
    335 }
    336 
    337 
    338 /**
    339  * "extract" meta data from an Extended Nintendo Sound Format file
    340  *
    341  * NSFE specification revision 2 (Sep. 3, 2003) was used, while this
    342  * piece of software was originally written.
    343  *
    344  * @param ec extraction context
    345  */
    346 void
    347 EXTRACTOR_nsfe_extract_method (struct EXTRACTOR_ExtractContext *ec)
    348 {
    349   const struct header *head;
    350   void *data;
    351   uint64_t off;
    352   uint32_t chunksize;
    353   int ret;
    354 
    355   if ((ssize_t) sizeof (struct header) >
    356       ec->read (ec->cls,
    357                 &data,
    358                 sizeof (struct header)))
    359     return;
    360   head = data;
    361   if (0 != memcmp (head->magicid, "NSFE", 4))
    362     return;
    363 
    364   if (0 != ec->proc (ec->cls,
    365                      "nsfe",
    366                      EXTRACTOR_METATYPE_MIMETYPE,
    367                      EXTRACTOR_METAFORMAT_UTF8,
    368                      "text/plain",
    369                      "audio/x-nsfe",
    370                      strlen ("audio/x-nsfe") + 1))
    371     return;
    372   off = sizeof (struct header);
    373   ret = 0;
    374   while (0 == ret)
    375   {
    376     if (off != ec->seek (ec->cls,
    377                          off,
    378                          SEEK_SET))
    379       break;
    380     if (8 >
    381         ec->read (ec->cls,
    382                   &data,
    383                   8))
    384       break;
    385     chunksize = nsfeuint (data);
    386     if (off + chunksize + 8LLU <= off)
    387       break;   /* protect against looping */
    388     off += 8LLU + chunksize;
    389     if (0 == memcmp (data + 4, "INFO", 4))
    390       ret = info_extract (ec, chunksize);
    391     else if (0 == memcmp (data + 4, "auth", 4))
    392       ret = auth_extract (ec, chunksize);
    393     else if (0 == memcmp (data + 4, "tlbl", 4))
    394       ret = tlbl_extract (ec, chunksize);
    395     /* Ignored chunks: DATA, NEND, plst, time, fade, BANK */
    396   }
    397 }
    398 
    399 
    400 /* end of nsfe_extractor.c */