From 3d9c0628220175603757ea1fabce7a9d34a75739 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Wed, 27 Apr 2011 16:46:27 +0000 Subject: Get NSF fixed up a bit and parse metadata in the core. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29790 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/nsf.c | 25 +++--- apps/metadata/nsf.c | 228 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 209 insertions(+), 44 deletions(-) diff --git a/apps/codecs/nsf.c b/apps/codecs/nsf.c index 72f6974214..b1bfb82dcd 100644 --- a/apps/codecs/nsf.c +++ b/apps/codecs/nsf.c @@ -4319,7 +4319,7 @@ static int dontresettrack = 0; enum codec_status codec_main(enum codec_entry_call_reason reason) { if (reason == CODEC_LOAD) { - /* we only render 16 bits, 44.1KHz, Stereo */ + /* we only render 16 bits, 44.1KHz, Mono */ ci->configure(DSP_SET_SAMPLE_DEPTH, 16); ci->configure(DSP_SET_FREQUENCY, 44100); ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); @@ -4338,6 +4338,7 @@ enum codec_status codec_run(void) size_t n; int endofstream; /* end of stream flag */ int usingplaylist = 0; + intptr_t param; DEBUGF("NSF: next_track\n"); if (codec_init()) { @@ -4406,27 +4407,21 @@ init_nsf: reset_profile_timers(); while (!endofstream) { - intptr_t param; enum codec_command_action action = ci->get_command(¶m); if (action == CODEC_ACTION_HALT) break; if (action == CODEC_ACTION_SEEK_TIME) { - if (param > 0) { - track=param/1000; - if (usingplaylist) { - if (track>=nPlaylistSize) break; - } else { - if (track>=nTrackCount) break; - } - dontresettrack=1; - ci->seek_complete(); - goto init_nsf; - } - else { - ci->seek_complete(); + track=param/1000; + if (usingplaylist) { + if (track>=nPlaylistSize) break; + } else { + if (track>=nTrackCount) break; } + dontresettrack=1; + ci->seek_complete(); + goto init_nsf; } ENTER_TIMER(total); diff --git a/apps/metadata/nsf.c b/apps/metadata/nsf.c index 9207a14048..58fc038b42 100644 --- a/apps/metadata/nsf.c +++ b/apps/metadata/nsf.c @@ -10,53 +10,223 @@ #include "metadata_parsers.h" #include "rbunicode.h" -bool get_nsf_metadata(int fd, struct mp3entry* id3) +struct NESM_HEADER { - /* Use the trackname part of the id3 structure as a temporary buffer */ - unsigned char* buf = (unsigned char *)id3->path; - char *p; - - if ((lseek(fd, 0, SEEK_SET) < 0) - || (read(fd, buf, 110) < 110)) - { - return false; - } + uint32_t nHeader; + uint8_t nHeaderExtra; + uint8_t nVersion; + uint8_t nTrackCount; + uint8_t nInitialTrack; + uint16_t nLoadAddress; + uint16_t nInitAddress; + uint16_t nPlayAddress; + uint8_t szGameTitle[32]; + uint8_t szArtist[32]; + uint8_t szCopyright[32]; + uint16_t nSpeedNTSC; + uint8_t nBankSwitch[8]; + uint16_t nSpeedPAL; + uint8_t nNTSC_PAL; + uint8_t nExtraChip; + uint8_t nExpansion[4]; +} __attribute__((packed)); + +struct NSFE_INFOCHUNK +{ + uint16_t nLoadAddress; + uint16_t nInitAddress; + uint16_t nPlayAddress; + uint8_t nIsPal; + uint8_t nExt; + uint8_t nTrackCount; + uint8_t nStartingTrack; +} __attribute__((packed)); - id3->length = 120*1000; - id3->vbr = false; - id3->filesize = filesize(fd); - if (memcmp(buf,"NSFE",4) == 0) /* only NESM contain metadata */ +#define CHAR4_CONST(a, b, c, d) FOURCC(a, b, c, d) + +static bool parse_nsfe(int fd, struct mp3entry *id3) +{ + bool info_found = false; + bool end_found = false; + bool data_found = false; + + struct NSFE_INFOCHUNK info; + memset(&info, 0, sizeof(struct NSFE_INFOCHUNK)); + + /* default values */ + info.nTrackCount = 1; + id3->length = 2*1000*60; + + /* begin reading chunks */ + while (!end_found) { - return true; - } - else - { - if (memcmp(buf, "NESM",4) != 0) /* not a valid format*/ - { + int32_t chunk_size, chunk_type; + + if (read_uint32le(fd, &chunk_size) != (int)sizeof(uint32_t)) + return false; + + if (read_uint32be(fd, &chunk_type) != (int)sizeof(uint32_t)) return false; - } - } - p = id3->id3v2buf; + switch (chunk_type) + { + case CHAR4_CONST('I', 'N', 'F', 'O'): + { + /* only one info chunk permitted */ + if (info_found) + return false; + + info_found = true; + + /* minimum size */ + if (chunk_size < 8) + return false; + + ssize_t size = MIN((ssize_t)sizeof(struct NSFE_INFOCHUNK), + chunk_size); + + if (read(fd, &info, size) != size) + return false; + + if (size >= 9) + id3->length = info.nTrackCount*1000; + + lseek(fd, chunk_size - size, SEEK_CUR); + break; + } + + case CHAR4_CONST('a', 'u', 't', 'h'): + { + if (!info_found) + return false; + + /* szGameTitle, szArtist, szCopyright */ + char ** const ar[] = { &id3->title, &id3->artist, &id3->album }; + + char *p = id3->id3v2buf; + long buf_rem = sizeof (id3->id3v2buf); + unsigned int i; + + for (i = 0; i < ARRAYLEN(ar) && chunk_size && buf_rem; i++) + { + long len = read_string(fd, p, buf_rem, '\0', chunk_size); + + if (len < 0) + return false; + + *ar[i] = p; + p += len; + buf_rem -= len; + + if (chunk_size >= len) + chunk_size -= len; + else + chunk_size = 0; + } + + lseek(fd, chunk_size, SEEK_CUR); + break; + } + + + case CHAR4_CONST('D', 'A', 'T', 'A'): + if (chunk_size < 1) + return false; + + data_found = true; + /* fall through */ + case CHAR4_CONST('f', 'a', 'd', 'e'): + case CHAR4_CONST('t', 'i', 'm', 'e'): + case CHAR4_CONST('B', 'A', 'N', 'K'): + case CHAR4_CONST('p', 'l', 's', 't'): + case CHAR4_CONST('t', 'l', 'b', 'l'): /* we unfortunately can't use these anyway */ + { + if (!info_found) + return false; + + lseek(fd, chunk_size, SEEK_CUR); + break; + } + + case CHAR4_CONST('N', 'E', 'N', 'D'): + { + end_found = true; + break; + } + + default: /* unknown chunk */ + { + /* check the first byte */ + chunk_type = (uint8_t)chunk_type; + + /* chunk is vital... don't continue */ + if(chunk_type >= 'A' && chunk_type <= 'Z') + return false; + + /* otherwise, just skip it */ + lseek(fd, chunk_size, SEEK_CUR); + break; + } + } /* end switch */ + } /* end while */ + + /* + * if we exited the while loop without a 'return', we must have hit an NEND + * chunk if this is the case, the file was layed out as it was expected. + * now.. make sure we found both an info chunk, AND a data chunk... since + * these are minimum requirements for a valid NSFE file + */ + return info_found && data_found; +} + +static bool parse_nesm(int fd, struct mp3entry *id3) +{ + struct NESM_HEADER hdr; + char *p = id3->id3v2buf; + + lseek(fd, 0, SEEK_SET); + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) + return false; /* Length */ - id3->length = buf[6]*1000; + id3->length = hdr.nTrackCount*1000; /* Title */ - memcpy(p, &buf[14], 32); id3->title = p; - p += strlen(p)+1; + p += strlcpy(p, hdr.szGameTitle, 32) + 1; /* Artist */ - memcpy(p, &buf[46], 32); id3->artist = p; - p += strlen(p)+1; + p += strlcpy(p, hdr.szArtist, 32) + 1; /* Copyright (per codec) */ - memcpy(p, &buf[78], 32); id3->album = p; + strlcpy(p, hdr.szCopyright, 32); return true; } +bool get_nsf_metadata(int fd, struct mp3entry* id3) +{ + uint32_t nsf_type; + + if (lseek(fd, 0, SEEK_SET) < 0 || + read_uint32be(fd, &nsf_type) != (int)sizeof(nsf_type)) + return false; + + id3->vbr = false; + id3->filesize = filesize(fd); + /* we only render 16 bits, 44.1KHz, Mono */ + id3->bitrate = 706; + id3->frequency = 44100; + + if (nsf_type == CHAR4_CONST('N', 'S', 'F', 'E')) + return parse_nsfe(fd, id3); + else if (nsf_type == CHAR4_CONST('N', 'E', 'S', 'M')) + return parse_nesm(fd, id3); + + /* not a valid format*/ + return false; +} + -- cgit v1.2.3