1 // Game_Music_Emu $vers. http://www.slack.net/~ant/
2 
3 #include "Nsfe_Emu.h"
4 
5 #include "blargg_endian.h"
6 #include <ctype.h>
7 
8 /* Copyright (C) 2005-2009 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 */
18 
19 #include "blargg_source.h"
20 
Nsfe_Info()21 Nsfe_Info::Nsfe_Info() { playlist_disabled = false; }
22 
~Nsfe_Info()23 Nsfe_Info::~Nsfe_Info() { }
24 
unload()25 inline void Nsfe_Info::unload()
26 {
27 	data.clear();
28 	track_name_data.clear();
29 	track_names.clear();
30 	playlist.clear();
31 	track_times.clear();
32 }
33 
34 // TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ?
disable_playlist(bool b)35 void Nsfe_Info::disable_playlist( bool b )
36 {
37 	playlist_disabled = b;
38 	info.track_count = playlist.size();
39 	if ( !info.track_count || playlist_disabled )
40 		info.track_count = actual_track_count_;
41 }
42 
remap_track(int track) const43 int Nsfe_Info::remap_track( int track ) const
44 {
45 	if ( !playlist_disabled && (unsigned) track < playlist.size() )
46 		track = playlist [track];
47 	return track;
48 }
49 
50 // Read multiple strings and separate into individual strings
read_strs(Data_Reader & in,int size,blargg_vector<char> & chars,blargg_vector<const char * > & strs)51 static blargg_err_t read_strs( Data_Reader& in, int size, blargg_vector<char>& chars,
52 		blargg_vector<const char*>& strs )
53 {
54 	RETURN_ERR( chars.resize( size + 1 ) );
55 	chars [size] = 0; // in case last string doesn't have terminator
56 	RETURN_ERR( in.read( &chars [0], size ) );
57 
58 	RETURN_ERR( strs.resize( 128 ) );
59 	int count = 0;
60 	for ( int i = 0; i < size; i++ )
61 	{
62 		if ( (int) strs.size() <= count )
63 			RETURN_ERR( strs.resize( count * 2 ) );
64 		strs [count++] = &chars [i];
65 		while ( i < size && chars [i] )
66 			i++;
67 	}
68 
69 	return strs.resize( count );
70 }
71 
72 // Copy in to out, where out has out_max characters allocated. Truncate to
73 // out_max - 1 characters.
copy_str(const char in[],char out[],int out_max)74 static void copy_str( const char in [], char out [], int out_max )
75 {
76 	out [out_max - 1] = 0;
77 	strncpy( out, in, out_max - 1 );
78 }
79 
80 struct nsfe_info_t
81 {
82 	byte load_addr [2];
83 	byte init_addr [2];
84 	byte play_addr [2];
85 	byte speed_flags;
86 	byte chip_flags;
87 	byte track_count;
88 	byte first_track;
89 	byte unused [6];
90 };
91 
load(Data_Reader & in,Nsfe_Emu * nsf_emu)92 blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsfe_Emu* nsf_emu )
93 {
94 	int const nsfe_info_size = 16;
95 	assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size );
96 
97 	// check header
98 	byte signature [4];
99 	blargg_err_t err = in.read( signature, sizeof signature );
100 	if ( err )
101 		return (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err);
102 	if ( memcmp( signature, "NSFE", 4 ) )
103 		return blargg_err_file_type;
104 
105 	// free previous info
106 	track_name_data.clear();
107 	track_names.clear();
108 	playlist.clear();
109 	track_times.clear();
110 
111 	// default nsf header
112 	static const Nsf_Emu::header_t base_header =
113 	{
114 		{'N','E','S','M','\x1A'},// tag
115 		1,                  // version
116 		1, 1,               // track count, first track
117 		{0,0},{0,0},{0,0},  // addresses
118 		"","","",           // strings
119 		{0x1A, 0x41},       // NTSC rate
120 		{0,0,0,0,0,0,0,0},  // banks
121 		{0x20, 0x4E},       // PAL rate
122 		0, 0,               // flags
123 		{0,0,0,0}           // unused
124 	};
125 	Nsf_Emu::header_t& header = info;
126 	header = base_header;
127 
128 	// parse tags
129 	int phase = 0;
130 	while ( phase != 3 )
131 	{
132 		// read size and tag
133 		byte block_header [2] [4];
134 		RETURN_ERR( in.read( block_header, sizeof block_header ) );
135 		int size = get_le32( block_header [0] );
136 		int tag  = get_le32( block_header [1] );
137 
138 		//dprintf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) );
139 
140 		switch ( tag )
141 		{
142 			case BLARGG_4CHAR('O','F','N','I'): {
143 				check( phase == 0 );
144 				if ( size < 8 )
145 					return blargg_err_file_corrupt;
146 
147 				nsfe_info_t finfo;
148 				finfo.track_count = 1;
149 				finfo.first_track = 0;
150 
151 				RETURN_ERR( in.read( &finfo, min( size, (int) nsfe_info_size ) ) );
152 				if ( size > nsfe_info_size )
153 					RETURN_ERR( in.skip( size - nsfe_info_size ) );
154 				phase = 1;
155 				info.speed_flags = finfo.speed_flags;
156 				info.chip_flags  = finfo.chip_flags;
157 				info.track_count = finfo.track_count;
158 				this->actual_track_count_ = finfo.track_count;
159 				info.first_track = finfo.first_track;
160 				memcpy( info.load_addr, finfo.load_addr, 2 * 3 );
161 				break;
162 			}
163 
164 			case BLARGG_4CHAR('K','N','A','B'):
165 				if ( size > (int) sizeof info.banks )
166 					return blargg_err_file_corrupt;
167 				RETURN_ERR( in.read( info.banks, size ) );
168 				break;
169 
170 			case BLARGG_4CHAR('h','t','u','a'): {
171 				blargg_vector<char> chars;
172 				blargg_vector<const char*> strs;
173 				RETURN_ERR( read_strs( in, size, chars, strs ) );
174 				int n = strs.size();
175 
176 				if ( n > 3 )
177 					copy_str( strs [3], info.dumper, sizeof info.dumper );
178 
179 				if ( n > 2 )
180 					copy_str( strs [2], info.copyright, sizeof info.copyright );
181 
182 				if ( n > 1 )
183 					copy_str( strs [1], info.author, sizeof info.author );
184 
185 				if ( n > 0 )
186 					copy_str( strs [0], info.game, sizeof info.game );
187 
188 				break;
189 			}
190 
191 			case BLARGG_4CHAR('e','m','i','t'):
192 				RETURN_ERR( track_times.resize( size / 4 ) );
193 				RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) );
194 				break;
195 
196 			case BLARGG_4CHAR('l','b','l','t'):
197 				RETURN_ERR( read_strs( in, size, track_name_data, track_names ) );
198 				break;
199 
200 			case BLARGG_4CHAR('t','s','l','p'):
201 				RETURN_ERR( playlist.resize( size ) );
202 				RETURN_ERR( in.read( &playlist [0], size ) );
203 				break;
204 
205 			case BLARGG_4CHAR('A','T','A','D'): {
206 				check( phase == 1 );
207 				phase = 2;
208 				if ( !nsf_emu )
209 				{
210 					RETURN_ERR( data.resize( size ) );
211 					RETURN_ERR( in.read( data.begin(), size ) );
212 				}
213 				else
214 				{
215 					Subset_Reader sub( &in, size ); // limit emu to nsf data
216 					Remaining_Reader rem( &header, header.size, &sub );
217 					RETURN_ERR( nsf_emu->Nsf_Emu::load_( rem ) );
218 					check( rem.remain() == 0 );
219 				}
220 				break;
221 			}
222 
223 			case BLARGG_4CHAR('D','N','E','N'):
224 				check( phase == 2 );
225 				phase = 3;
226 				break;
227 
228 			default:
229 				// tags that can be skipped start with a lowercase character
230 				check( islower( (tag >> 24) & 0xFF ) );
231 				RETURN_ERR( in.skip( size ) );
232 				break;
233 		}
234 	}
235 
236 	return blargg_ok;
237 }
238 
track_info_(track_info_t * out,int track) const239 blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const
240 {
241 	int remapped = remap_track( track );
242 	if ( (unsigned) remapped < track_times.size() )
243 	{
244 		int length = (BOOST::int32_t) get_le32( track_times [remapped] );
245 		if ( length > 0 )
246 			out->length = length;
247 	}
248 	if ( (unsigned) remapped < track_names.size() )
249 		Gme_File::copy_field_( out->song, track_names [remapped] );
250 
251 	GME_COPY_FIELD( info, out, game );
252 	GME_COPY_FIELD( info, out, author );
253 	GME_COPY_FIELD( info, out, copyright );
254 	GME_COPY_FIELD( info, out, dumper );
255 	return blargg_ok;
256 }
257 
Nsfe_Emu()258 Nsfe_Emu::Nsfe_Emu()
259 {
260 	set_type( gme_nsfe_type );
261 }
262 
~Nsfe_Emu()263 Nsfe_Emu::~Nsfe_Emu() { }
264 
unload()265 void Nsfe_Emu::unload()
266 {
267 	info.unload();
268 	Nsf_Emu::unload();
269 }
270 
track_info_(track_info_t * out,int track) const271 blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const
272 {
273 	return info.track_info_( out, track );
274 }
275 
276 struct Nsfe_File : Gme_Info_
277 {
278 	Nsfe_Info info;
279 
Nsfe_FileNsfe_File280 	Nsfe_File() { set_type( gme_nsfe_type ); }
281 
load_Nsfe_File282 	blargg_err_t load_( Data_Reader& in )
283 	{
284 		RETURN_ERR( info.load( in, 0 ) );
285 		info.disable_playlist( false );
286 		set_track_count( info.info.track_count );
287 		return blargg_ok;
288 	}
289 
track_info_Nsfe_File290 	blargg_err_t track_info_( track_info_t* out, int track ) const
291 	{
292 		return info.track_info_( out, track );
293 	}
294 
hash_Nsfe_File295 	blargg_err_t hash_( Hash_Function& out ) const
296 	{
297 		hash_nsf_file( info.info, info.data.begin(), info.data.end() - info.data.begin(), out );
298 		return blargg_ok;
299 	}
300 };
301 
new_nsfe_emu()302 static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; }
new_nsfe_file()303 static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; }
304 
305 gme_type_t_ const gme_nsfe_type [1] = {{ "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }};
306 
load_(Data_Reader & in)307 blargg_err_t Nsfe_Emu::load_( Data_Reader& in )
308 {
309 	RETURN_ERR( info.load( in, this ) );
310 	disable_playlist_( false );
311 	return blargg_ok;
312 }
313 
disable_playlist_(bool b)314 void Nsfe_Emu::disable_playlist_( bool b )
315 {
316 	info.disable_playlist( b );
317 	set_track_count( info.info.track_count );
318 }
319 
clear_playlist_()320 void Nsfe_Emu::clear_playlist_()
321 {
322 	disable_playlist_( true );
323 	Nsf_Emu::clear_playlist_();
324 }
325 
start_track_(int track)326 blargg_err_t Nsfe_Emu::start_track_( int track )
327 {
328 	return Nsf_Emu::start_track_( info.remap_track( track ) );
329 }
330