Submit initial patch from FS#12176. Adds support for several new game music formats...
[kugel-rb.git] / apps / codecs / libgme / nsfe_info.c
blobd22b76317372bef5c3c789feff5335a976ae05fd
1 // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
3 #include "nsf_emu.h"
5 #include "blargg_endian.h"
6 #include <string.h>
8 /* Copyright (C) 2005-2006 Shay Green. This module is free software; you
9 can redistribute it and/or modify it under the terms of the GNU Lesser
10 General Public License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version. This
12 module is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 details. You should have received a copy of the GNU Lesser General Public
16 License along with this module; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 #include "blargg_source.h"
21 void Info_init( struct Nsfe_Info* this )
23 this->playlist_disabled = false;
26 void Info_unload( struct Nsfe_Info* this )
28 memset(this->playlist, 0, 256);
29 memset(this->track_times, 0, 256 * sizeof(int32_t));
31 this->playlist_size = 0;
32 this->track_times_size = 0;
35 // TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ?
36 void Info_disable_playlist( struct Nsfe_Info* this, bool b )
38 this->playlist_disabled = b;
39 this->track_count = this->playlist_size;
40 if ( !this->track_count || this->playlist_disabled )
41 this->track_count = this->actual_track_count_;
44 int Info_remap_track( struct Nsfe_Info* this, int track )
46 if ( !this->playlist_disabled && (unsigned) track < (unsigned) this->playlist_size )
47 track = this->playlist [track];
48 return track;
51 const char eof_error [] = "Unexpected end of file";
53 // Read n bytes from memory buffer
54 static blargg_err_t in_read( void* dst, long bytes, void* data, long* offset, long size )
56 if ((*offset + bytes) > size) return eof_error;
58 memcpy(dst, (char*) data + *offset, bytes);
59 *offset += bytes;
60 return 0;
63 static blargg_err_t in_skip( long bytes, long *offset, long size )
65 if ((*offset + bytes) > size) return eof_error;
67 *offset += bytes;
68 return 0;
71 // Skip n bytes from memory buffer
73 // Read multiple strings and separate into individual strings
74 static int read_strs( void* data, long bytes, long* offset, long size,
75 const char* strs [4] )
77 char* chars = (char*) data + *offset;
78 chars [bytes - 1] = 0; // in case last string doesn't have terminator
80 if ( in_skip( bytes, offset, size) )
81 return -1;
83 int count = 0, i;
84 for ( i = 0; i < bytes; i++ )
86 strs [count] = &chars [i];
87 while ( i < bytes && chars [i] )
88 i++;
90 count++;
91 if (count >= 4)
92 break;
95 return count;
98 struct nsfe_info_t
100 byte load_addr [2];
101 byte init_addr [2];
102 byte play_addr [2];
103 byte speed_flags;
104 byte chip_flags;
105 byte track_count;
106 byte first_track;
107 byte unused [6];
110 blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Nsf_Emu* nsf_emu )
112 long offset = 0;
113 int const nsfe_info_size = 16;
114 assert( offsetof (struct nsfe_info_t,unused [6]) == nsfe_info_size );
116 // check header
117 byte signature [4];
118 blargg_err_t err = in_read( signature, sizeof signature, data, &offset, size );
119 if ( err )
120 return (err == eof_error ? gme_wrong_file_type : err);
121 if ( memcmp( signature, "NSFE", 4 ) ) {
124 // free previous info
125 /* TODO: clear track_names */
126 memset(this->playlist, 0, 256);
127 memset(this->track_times, 0, 256 * sizeof(int32_t));
129 this->playlist_size = 0;
130 this->track_times_size = 0;
132 // default nsf header
133 static const struct header_t base_header =
135 {'N','E','S','M','\x1A'},// tag
136 1, // version
137 1, 1, // track count, first track
138 {0,0},{0,0},{0,0}, // addresses
139 "","","", // strings
140 {0x1A, 0x41}, // NTSC rate
141 {0,0,0,0,0,0,0,0}, // banks
142 {0x20, 0x4E}, // PAL rate
143 0, 0, // flags
144 {0,0,0,0} // unused
147 memcpy( &nsf_emu->header, &base_header, sizeof base_header );
149 // parse tags
150 int phase = 0;
151 while ( phase != 3 )
153 // read size and tag
154 byte block_header [2] [4];
155 RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) );
157 blargg_long chunk_size = get_le32( block_header [0] );
158 blargg_long tag = get_le32( block_header [1] );
160 switch ( tag )
162 case BLARGG_4CHAR('O','F','N','I'): {
163 check( phase == 0 );
164 if ( chunk_size < 8 )
165 return "Corrupt file";
167 struct nsfe_info_t finfo;
168 finfo.track_count = 1;
169 finfo.first_track = 0;
171 RETURN_ERR( in_read( &finfo, min( chunk_size, (blargg_long) nsfe_info_size ),
172 (char*) data, &offset, size ) );
174 if ( chunk_size > nsfe_info_size )
175 RETURN_ERR( in_skip( chunk_size - nsfe_info_size, &offset, size ) );
177 phase = 1;
178 nsf_emu->header.speed_flags = finfo.speed_flags;
179 nsf_emu->header.chip_flags = finfo.chip_flags;
180 nsf_emu->header.track_count = finfo.track_count;
181 this->actual_track_count_ = finfo.track_count;
182 nsf_emu->header.first_track = finfo.first_track;
183 memcpy( nsf_emu->header.load_addr, finfo.load_addr, 2 * 3 );
184 break;
187 case BLARGG_4CHAR('K','N','A','B'):
188 if ( chunk_size > (int) sizeof nsf_emu->header.banks )
189 return "Corrupt file";
190 RETURN_ERR( in_read( nsf_emu->header.banks, chunk_size, data, &offset, size ) );
191 break;
193 case BLARGG_4CHAR('h','t','u','a'): {
194 const char* strs [4];
195 int n = read_strs( data, chunk_size, &offset, size, strs );
196 if ( n < 0 )
197 return eof_error;
198 break;
201 case BLARGG_4CHAR('e','m','i','t'):
202 this->track_times_size = chunk_size / 4;
203 RETURN_ERR( in_read( this->track_times, this->track_times_size * 4, data, &offset, size ) );
204 break;
206 case BLARGG_4CHAR('l','b','l','t'):
207 RETURN_ERR( in_skip( chunk_size, &offset, size ) );
208 break;
210 case BLARGG_4CHAR('t','s','l','p'):
211 this->playlist_size = chunk_size;
212 RETURN_ERR( in_read( &this->playlist [0], chunk_size, data, &offset, size ) );
213 break;
215 case BLARGG_4CHAR('A','T','A','D'): {
216 check( phase == 1 );
217 phase = 2;
218 if ( !nsf_emu )
220 RETURN_ERR( in_skip( chunk_size, &offset, size ) );
222 else
224 // Avoid unexpected end of file
225 if ( (offset + chunk_size) > size )
226 return eof_error;
228 RETURN_ERR( Rom_load( &nsf_emu->rom, (char*) data + offset, chunk_size, 0, 0, 0 ) );
229 RETURN_ERR( Nsf_post_load( nsf_emu ) );
230 offset += chunk_size;
232 break;
235 case BLARGG_4CHAR('D','N','E','N'):
236 check( phase == 2 );
237 phase = 3;
238 break;
240 default:
241 // tags that can be skipped start with a lowercase character
242 check( islower( (tag >> 24) & 0xFF ) );
243 RETURN_ERR( in_skip( chunk_size, &offset, size ) );
244 break;
248 return 0;
251 long Track_length( struct Nsf_Emu* this, int n )
253 long length = 0;
254 if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
255 struct entry_t* entry = &this->m3u.entries [n];
256 length = entry->length;
258 else if ( (this->info.playlist_size > 0) && (n < this->info.playlist_size) ) {
259 int remapped = Info_remap_track( &this->info, n );
260 if ( (unsigned) remapped < (unsigned) this->info.track_times_size )
261 length = (int32_t) get_le32( &this->info.track_times [remapped] );
263 else if( (unsigned) n < (unsigned) this->info.track_times_size )
264 length = (int32_t) get_le32( &this->info.track_times [n] );
266 /* Length will be 2,30 minutes for one track songs,
267 and 1,45 minutes for multitrack songs */
268 if ( length <= 0 )
269 length = (this->track_count > 1 ? 105 : 150) * 1000;
271 return length;