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 */