1 // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
5 #include "blargg_endian.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
];
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
);
63 static blargg_err_t
in_skip( long bytes
, long *offset
, long size
)
65 if ((*offset
+ bytes
) > size
) return eof_error
;
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
) )
84 for ( i
= 0; i
< bytes
; i
++ )
86 strs
[count
] = &chars
[i
];
87 while ( i
< bytes
&& chars
[i
] )
110 blargg_err_t
Info_load( struct Nsfe_Info
* this, void* data
, long size
, struct Nsf_Emu
* nsf_emu
)
113 int const nsfe_info_size
= 16;
114 assert( offsetof (struct nsfe_info_t
,unused
[6]) == nsfe_info_size
);
118 blargg_err_t err
= in_read( signature
, sizeof signature
, data
, &offset
, size
);
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
137 1, 1, // track count, first track
138 {0,0},{0,0},{0,0}, // addresses
140 {0x1A, 0x41}, // NTSC rate
141 {0,0,0,0,0,0,0,0}, // banks
142 {0x20, 0x4E}, // PAL rate
147 memcpy( &nsf_emu
->header
, &base_header
, sizeof base_header
);
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] );
162 case BLARGG_4CHAR('O','F','N','I'): {
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
) );
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 );
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
) );
193 case BLARGG_4CHAR('h','t','u','a'): {
194 const char* strs
[4];
195 int n
= read_strs( data
, chunk_size
, &offset
, size
, strs
);
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
) );
206 case BLARGG_4CHAR('l','b','l','t'):
207 RETURN_ERR( in_skip( chunk_size
, &offset
, size
) );
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
) );
215 case BLARGG_4CHAR('A','T','A','D'): {
220 RETURN_ERR( in_skip( chunk_size
, &offset
, size
) );
224 // Avoid unexpected end of file
225 if ( (offset
+ chunk_size
) > size
)
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
;
235 case BLARGG_4CHAR('D','N','E','N'):
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
) );
251 long Track_length( struct Nsf_Emu
* this, int n
)
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 */
269 length
= (this->track_count
> 1 ? 105 : 150) * 1000;