From acb0917556fc33681c1df5a530cf754193e67705 Mon Sep 17 00:00:00 2001 From: Andree Buschmann Date: Sun, 7 Aug 2011 20:01:04 +0000 Subject: Submit initial patch from FS#12176. Adds support for several new game music formats (AY, GBS, HES, KSS, SGC, VGM and VGZ) and replaces the current NSF and NSFE with a new implementation based on a port of the Game Music Emu library 'GME'. This first submit does not cover the full functionality provided by the author's original patch: Coleco-SGV is not supported, some GME-specific m3u-support has been removed and IRAM is not used yet. Further changes are very likely to follow this submit. Thanks to Mauricio Garrido. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30264 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/libgme/nsfe_info.c | 272 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 apps/codecs/libgme/nsfe_info.c (limited to 'apps/codecs/libgme/nsfe_info.c') diff --git a/apps/codecs/libgme/nsfe_info.c b/apps/codecs/libgme/nsfe_info.c new file mode 100644 index 0000000000..d22b763173 --- /dev/null +++ b/apps/codecs/libgme/nsfe_info.c @@ -0,0 +1,272 @@ +// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/ + +#include "nsf_emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2005-2006 Shay Green. This module 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. This +module 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 this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Info_init( struct Nsfe_Info* this ) +{ + this->playlist_disabled = false; +} + +void Info_unload( struct Nsfe_Info* this ) +{ + memset(this->playlist, 0, 256); + memset(this->track_times, 0, 256 * sizeof(int32_t)); + + this->playlist_size = 0; + this->track_times_size = 0; +} + +// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ? +void Info_disable_playlist( struct Nsfe_Info* this, bool b ) +{ + this->playlist_disabled = b; + this->track_count = this->playlist_size; + if ( !this->track_count || this->playlist_disabled ) + this->track_count = this->actual_track_count_; +} + +int Info_remap_track( struct Nsfe_Info* this, int track ) +{ + if ( !this->playlist_disabled && (unsigned) track < (unsigned) this->playlist_size ) + track = this->playlist [track]; + return track; +} + +const char eof_error [] = "Unexpected end of file"; + +// Read n bytes from memory buffer +static blargg_err_t in_read( void* dst, long bytes, void* data, long* offset, long size ) +{ + if ((*offset + bytes) > size) return eof_error; + + memcpy(dst, (char*) data + *offset, bytes); + *offset += bytes; + return 0; +} + +static blargg_err_t in_skip( long bytes, long *offset, long size ) +{ + if ((*offset + bytes) > size) return eof_error; + + *offset += bytes; + return 0; +} + +// Skip n bytes from memory buffer + +// Read multiple strings and separate into individual strings +static int read_strs( void* data, long bytes, long* offset, long size, + const char* strs [4] ) +{ + char* chars = (char*) data + *offset; + chars [bytes - 1] = 0; // in case last string doesn't have terminator + + if ( in_skip( bytes, offset, size) ) + return -1; + + int count = 0, i; + for ( i = 0; i < bytes; i++ ) + { + strs [count] = &chars [i]; + while ( i < bytes && chars [i] ) + i++; + + count++; + if (count >= 4) + break; + } + + return count; +} + +struct nsfe_info_t +{ + byte load_addr [2]; + byte init_addr [2]; + byte play_addr [2]; + byte speed_flags; + byte chip_flags; + byte track_count; + byte first_track; + byte unused [6]; +}; + +blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Nsf_Emu* nsf_emu ) +{ + long offset = 0; + int const nsfe_info_size = 16; + assert( offsetof (struct nsfe_info_t,unused [6]) == nsfe_info_size ); + + // check header + byte signature [4]; + blargg_err_t err = in_read( signature, sizeof signature, data, &offset, size ); + if ( err ) + return (err == eof_error ? gme_wrong_file_type : err); + if ( memcmp( signature, "NSFE", 4 ) ) { + } + + // free previous info + /* TODO: clear track_names */ + memset(this->playlist, 0, 256); + memset(this->track_times, 0, 256 * sizeof(int32_t)); + + this->playlist_size = 0; + this->track_times_size = 0; + + // default nsf header + static const struct header_t base_header = + { + {'N','E','S','M','\x1A'},// tag + 1, // version + 1, 1, // track count, first track + {0,0},{0,0},{0,0}, // addresses + "","","", // strings + {0x1A, 0x41}, // NTSC rate + {0,0,0,0,0,0,0,0}, // banks + {0x20, 0x4E}, // PAL rate + 0, 0, // flags + {0,0,0,0} // unused + }; + + memcpy( &nsf_emu->header, &base_header, sizeof base_header ); + + // parse tags + int phase = 0; + while ( phase != 3 ) + { + // read size and tag + byte block_header [2] [4]; + RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) ); + + blargg_long chunk_size = get_le32( block_header [0] ); + blargg_long tag = get_le32( block_header [1] ); + + switch ( tag ) + { + case BLARGG_4CHAR('O','F','N','I'): { + check( phase == 0 ); + if ( chunk_size < 8 ) + return "Corrupt file"; + + struct nsfe_info_t finfo; + finfo.track_count = 1; + finfo.first_track = 0; + + RETURN_ERR( in_read( &finfo, min( chunk_size, (blargg_long) nsfe_info_size ), + (char*) data, &offset, size ) ); + + if ( chunk_size > nsfe_info_size ) + RETURN_ERR( in_skip( chunk_size - nsfe_info_size, &offset, size ) ); + + phase = 1; + nsf_emu->header.speed_flags = finfo.speed_flags; + nsf_emu->header.chip_flags = finfo.chip_flags; + nsf_emu->header.track_count = finfo.track_count; + this->actual_track_count_ = finfo.track_count; + nsf_emu->header.first_track = finfo.first_track; + memcpy( nsf_emu->header.load_addr, finfo.load_addr, 2 * 3 ); + break; + } + + case BLARGG_4CHAR('K','N','A','B'): + if ( chunk_size > (int) sizeof nsf_emu->header.banks ) + return "Corrupt file"; + RETURN_ERR( in_read( nsf_emu->header.banks, chunk_size, data, &offset, size ) ); + break; + + case BLARGG_4CHAR('h','t','u','a'): { + const char* strs [4]; + int n = read_strs( data, chunk_size, &offset, size, strs ); + if ( n < 0 ) + return eof_error; + break; + } + + case BLARGG_4CHAR('e','m','i','t'): + this->track_times_size = chunk_size / 4; + RETURN_ERR( in_read( this->track_times, this->track_times_size * 4, data, &offset, size ) ); + break; + + case BLARGG_4CHAR('l','b','l','t'): + RETURN_ERR( in_skip( chunk_size, &offset, size ) ); + break; + + case BLARGG_4CHAR('t','s','l','p'): + this->playlist_size = chunk_size; + RETURN_ERR( in_read( &this->playlist [0], chunk_size, data, &offset, size ) ); + break; + + case BLARGG_4CHAR('A','T','A','D'): { + check( phase == 1 ); + phase = 2; + if ( !nsf_emu ) + { + RETURN_ERR( in_skip( chunk_size, &offset, size ) ); + } + else + { + // Avoid unexpected end of file + if ( (offset + chunk_size) > size ) + return eof_error; + + RETURN_ERR( Rom_load( &nsf_emu->rom, (char*) data + offset, chunk_size, 0, 0, 0 ) ); + RETURN_ERR( Nsf_post_load( nsf_emu ) ); + offset += chunk_size; + } + break; + } + + case BLARGG_4CHAR('D','N','E','N'): + check( phase == 2 ); + phase = 3; + break; + + default: + // tags that can be skipped start with a lowercase character + check( islower( (tag >> 24) & 0xFF ) ); + RETURN_ERR( in_skip( chunk_size, &offset, size ) ); + break; + } + } + + return 0; +} + +long Track_length( struct Nsf_Emu* this, int n ) +{ + long length = 0; + if ( (this->m3u.size > 0) && (n < this->m3u.size) ) { + struct entry_t* entry = &this->m3u.entries [n]; + length = entry->length; + } + else if ( (this->info.playlist_size > 0) && (n < this->info.playlist_size) ) { + int remapped = Info_remap_track( &this->info, n ); + if ( (unsigned) remapped < (unsigned) this->info.track_times_size ) + length = (int32_t) get_le32( &this->info.track_times [remapped] ); + } + else if( (unsigned) n < (unsigned) this->info.track_times_size ) + length = (int32_t) get_le32( &this->info.track_times [n] ); + + /* Length will be 2,30 minutes for one track songs, + and 1,45 minutes for multitrack songs */ + if ( length <= 0 ) + length = (this->track_count > 1 ? 105 : 150) * 1000; + + return length; +} -- cgit v1.2.3