comparison src/console/Nsfe_Emu.cxx @ 316:fb513e10174e trunk

[svn] - merge libconsole-blargg into mainline libconsole: + obsoletes plugins-ugly:sapplug
author nenolod
date Thu, 30 Nov 2006 19:54:33 -0800
parents 4ddab3548cd0
children 31c9d8f37474
comparison
equal deleted inserted replaced
315:2294f3a6f136 316:fb513e10174e
1 1 // Game_Music_Emu 0.5.1. http://www.slack.net/~ant/
2 // Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
3 2
4 #include "Nsfe_Emu.h" 3 #include "Nsfe_Emu.h"
5 4
6 #include "blargg_endian.h" 5 #include "blargg_endian.h"
7 #include <string.h> 6 #include <string.h>
7 #include <ctype.h>
8 8
9 /* Copyright (C) 2005-2006 Shay Green. This module is free software; you 9 /* Copyright (C) 2005-2006 Shay Green. This module is free software; you
10 can redistribute it and/or modify it under the terms of the GNU Lesser 10 can redistribute it and/or modify it under the terms of the GNU Lesser
11 General Public License as published by the Free Software Foundation; either 11 General Public License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version. This 12 version 2.1 of the License, or (at your option) any later version. This
13 module is distributed in the hope that it will be useful, but WITHOUT ANY 13 module is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16 more details. You should have received a copy of the GNU Lesser General 16 details. You should have received a copy of the GNU Lesser General Public
17 Public License along with this module; if not, write to the Free Software 17 License along with this module; if not, write to the Free Software Foundation,
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 19
20 #include BLARGG_SOURCE_BEGIN 20 #include "blargg_source.h"
21 21
22 #define NSFE_TAG( a, b, c, d ) (d*0x1000000L + c*0x10000L + b*0x100L + a) 22 Nsfe_Info::Nsfe_Info() { playlist_disabled = false; }
23
24 Nsfe_Info::Nsfe_Info()
25 {
26 playlist_enabled = false;
27 }
28 23
29 Nsfe_Info::~Nsfe_Info() { } 24 Nsfe_Info::~Nsfe_Info() { }
30 25
31 void Nsfe_Info::enable_playlist( bool b ) 26 inline void Nsfe_Info::unload()
32 { 27 {
33 playlist_enabled = b; 28 track_name_data.clear();
34 info_.track_count = (b && playlist_size()) ? playlist_size() : track_count_; 29 track_names.clear();
35 } 30 playlist.clear();
36 31 track_times.clear();
37 int Nsfe_Info::remap_track( int i ) const 32 }
38 { 33
39 if ( !playlist_enabled || !playlist_size() ) 34 // TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ?
40 return i; 35 void Nsfe_Info::disable_playlist( bool b )
41 36 {
42 return playlist_entry( i ); 37 playlist_disabled = b;
43 } 38 info.track_count = playlist.size();
44 39 if ( !info.track_count || playlist_disabled )
45 void Nsfe_Emu::start_track( int i ) 40 info.track_count = actual_track_count_;
46 { 41 }
47 Nsf_Emu::start_track( remap_track( i ) ); 42
48 } 43 int Nsfe_Info::remap_track( int track ) const
49 44 {
50 const char* Nsfe_Info::track_name( unsigned i ) const 45 if ( !playlist_disabled && (unsigned) track < playlist.size() )
51 { 46 track = playlist [track];
52 i = remap_track( i ); 47 return track;
53 if ( i < track_names.size() )
54 return track_names [i];
55
56 return "";
57 }
58
59 long Nsfe_Info::track_time( unsigned i ) const
60 {
61 i = remap_track( i );
62 if ( i < track_times.size() )
63 return track_times [i];
64
65 return 0;
66 }
67
68 // Read little-endian 32-bit int
69 static blargg_err_t read_le32( Emu_Reader& in, long* out )
70 {
71 unsigned char buf [4];
72 BLARGG_RETURN_ERR( in.read( buf, sizeof buf ) );
73 *out = get_le32( buf );
74 return blargg_success;
75 } 48 }
76 49
77 // Read multiple strings and separate into individual strings 50 // Read multiple strings and separate into individual strings
78 static blargg_err_t read_strs( Emu_Reader& in, long size, std::vector<char>& chars, 51 static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector<char>& chars,
79 std::vector<const char*>& strs ) 52 blargg_vector<const char*>& strs )
80 { 53 {
81 chars.resize( size + 1 ); 54 RETURN_ERR( chars.resize( size + 1 ) );
82 chars [size] = 0; // in case last string doesn't have terminator 55 chars [size] = 0; // in case last string doesn't have terminator
83 BLARGG_RETURN_ERR( in.read( &chars [0], size ) ); 56 RETURN_ERR( in.read( &chars [0], size ) );
84 57
58 RETURN_ERR( strs.resize( 128 ) );
59 int count = 0;
85 for ( int i = 0; i < size; i++ ) 60 for ( int i = 0; i < size; i++ )
86 { 61 {
87 strs.push_back( &chars [i] ); 62 if ( (int) strs.size() <= count )
63 RETURN_ERR( strs.resize( count * 2 ) );
64 strs [count++] = &chars [i];
88 while ( i < size && chars [i] ) 65 while ( i < size && chars [i] )
89 i++; 66 i++;
90 } 67 }
91 68
92 return blargg_success; 69 return strs.resize( count );
93 } 70 }
94 71
95 // Copy in to out, where out has out_max characters allocated. Truncate to 72 // Copy in to out, where out has out_max characters allocated. Truncate to
96 // out_max - 1 characters. 73 // out_max - 1 characters.
97 static void copy_str( const char* in, char* out, int out_max ) 74 static void copy_str( const char* in, char* out, int out_max )
98 { 75 {
99 out [out_max - 1] = 0; 76 out [out_max - 1] = 0;
100 strncpy( out, in, out_max - 1 ); 77 strncpy( out, in, out_max - 1 );
101 } 78 }
102 79
103 struct nsfe_info_t { 80 struct nsfe_info_t
104 unsigned char load_addr [2]; 81 {
105 unsigned char init_addr [2]; 82 byte load_addr [2];
106 unsigned char play_addr [2]; 83 byte init_addr [2];
107 unsigned char speed_flags; 84 byte play_addr [2];
108 unsigned char chip_flags; 85 byte speed_flags;
109 unsigned char track_count; 86 byte chip_flags;
110 unsigned char first_track; 87 byte track_count;
88 byte first_track;
89 byte unused [6];
111 }; 90 };
112 91 BOOST_STATIC_ASSERT( sizeof (nsfe_info_t) == 16 );
113 blargg_err_t Nsfe_Info::load( const header_t& nsfe_tag, Emu_Reader& in, Nsf_Emu* nsf_emu ) 92
93 blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
114 { 94 {
115 // check header 95 // check header
116 if ( memcmp( nsfe_tag.tag, "NSFE", 4 ) ) 96 byte signature [4];
117 return "Not an NSFE file"; 97 blargg_err_t err = in.read( signature, sizeof signature );
98 if ( err )
99 return (err == in.eof_error ? gme_wrong_file_type : err);
100 if ( memcmp( signature, "NSFE", 4 ) )
101 return gme_wrong_file_type;
118 102
119 // free previous info 103 // free previous info
120 track_name_data.clear(); 104 track_name_data.clear();
121 track_names.clear(); 105 track_names.clear();
122 playlist.clear(); 106 playlist.clear();
134 {0,0,0,0,0,0,0,0}, // banks 118 {0,0,0,0,0,0,0,0}, // banks
135 {0x20, 0x4E}, // PAL rate 119 {0x20, 0x4E}, // PAL rate
136 0, 0, // flags 120 0, 0, // flags
137 {0,0,0,0} // unused 121 {0,0,0,0} // unused
138 }; 122 };
139 Nsf_Emu::header_t& header = info_; 123 Nsf_Emu::header_t& header = info;
140 header = base_header; 124 header = base_header;
141 125
142 // parse tags 126 // parse tags
143 int phase = 0; 127 int phase = 0;
144 while ( phase != 3 ) 128 while ( phase != 3 )
145 { 129 {
146 // read size and tag 130 // read size and tag
147 long size = 0; 131 byte block_header [2] [4];
148 long tag = 0; 132 RETURN_ERR( in.read( block_header, sizeof block_header ) );
149 BLARGG_RETURN_ERR( read_le32( in, &size ) ); 133 blargg_long size = get_le32( block_header [0] );
150 BLARGG_RETURN_ERR( read_le32( in, &tag ) ); 134 blargg_long tag = get_le32( block_header [1] );
135
136 //dprintf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) );
151 137
152 switch ( tag ) 138 switch ( tag )
153 { 139 {
154 case NSFE_TAG('I','N','F','O'): { 140 case BLARGG_4CHAR('O','F','N','I'): {
155 check( phase == 0 ); 141 check( phase == 0 );
156 if ( size < 8 ) 142 if ( size < 8 )
157 return "Bad NSFE file"; 143 return "Corrupt file";
158 144
159 nsfe_info_t info; 145 nsfe_info_t finfo;
160 info.track_count = 1; 146 finfo.track_count = 1;
161 info.first_track = 0; 147 finfo.first_track = 0;
162 148
163 int s = size; 149 RETURN_ERR( in.read( &finfo, min( size, (blargg_long) sizeof finfo ) ) );
164 if ( s > (int) sizeof info ) 150 if ( size > (int) sizeof finfo )
165 s = sizeof info; 151 RETURN_ERR( in.skip( size - sizeof finfo ) );
166 BLARGG_RETURN_ERR( in.read( &info, s ) );
167 BLARGG_RETURN_ERR( in.skip( size - s ) );
168 phase = 1; 152 phase = 1;
169 info_.speed_flags = info.speed_flags; 153 info.speed_flags = finfo.speed_flags;
170 info_.chip_flags = info.chip_flags; 154 info.chip_flags = finfo.chip_flags;
171 info_.track_count = info.track_count; 155 info.track_count = finfo.track_count;
172 this->track_count_ = info.track_count; 156 this->actual_track_count_ = finfo.track_count;
173 info_.first_track = info.first_track; 157 info.first_track = finfo.first_track;
174 std::memcpy( info_.load_addr, info.load_addr, 2 * 3 ); 158 memcpy( info.load_addr, finfo.load_addr, 2 * 3 );
175 break; 159 break;
176 } 160 }
177 161
178 case NSFE_TAG('B','A','N','K'): 162 case BLARGG_4CHAR('K','N','A','B'):
179 if ( size > (int) sizeof info_.banks ) 163 if ( size > (int) sizeof info.banks )
180 return "Bad NSFE file"; 164 return "Corrupt file";
181 BLARGG_RETURN_ERR( in.read( info_.banks, size ) ); 165 RETURN_ERR( in.read( info.banks, size ) );
182 break; 166 break;
183 167
184 case NSFE_TAG('a','u','t','h'): { 168 case BLARGG_4CHAR('h','t','u','a'): {
185 std::vector<char> chars; 169 blargg_vector<char> chars;
186 std::vector<const char*> strs; 170 blargg_vector<const char*> strs;
187 BLARGG_RETURN_ERR( read_strs( in, size, chars, strs ) ); 171 RETURN_ERR( read_strs( in, size, chars, strs ) );
188 int n = strs.size(); 172 int n = strs.size();
189 173
190 if ( n > 3 ) 174 if ( n > 3 )
191 copy_str( strs [3], info_.ripper, sizeof info_.ripper ); 175 copy_str( strs [3], info.dumper, sizeof info.dumper );
192 176
193 if ( n > 2 ) 177 if ( n > 2 )
194 copy_str( strs [2], info_.copyright, sizeof info_.copyright ); 178 copy_str( strs [2], info.copyright, sizeof info.copyright );
195 179
196 if ( n > 1 ) 180 if ( n > 1 )
197 copy_str( strs [1], info_.author, sizeof info_.author ); 181 copy_str( strs [1], info.author, sizeof info.author );
198 182
199 if ( n > 0 ) 183 if ( n > 0 )
200 copy_str( strs [0], info_.game, sizeof info_.game ); 184 copy_str( strs [0], info.game, sizeof info.game );
201 185
202 break; 186 break;
203 } 187 }
204 188
205 case NSFE_TAG('t','i','m','e'): { 189 case BLARGG_4CHAR('e','m','i','t'):
206 track_times.resize( size / 4 ); 190 RETURN_ERR( track_times.resize( size / 4 ) );
207 for ( unsigned i = 0; i < track_times.size(); i++ ) 191 RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) );
208 BLARGG_RETURN_ERR( read_le32( in, &track_times [i] ) ); 192 break;
209 break; 193
210 } 194 case BLARGG_4CHAR('l','b','l','t'):
211 195 RETURN_ERR( read_strs( in, size, track_name_data, track_names ) );
212 case NSFE_TAG('t','l','b','l'): 196 break;
213 BLARGG_RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); 197
214 break; 198 case BLARGG_4CHAR('t','s','l','p'):
215 199 RETURN_ERR( playlist.resize( size ) );
216 case NSFE_TAG('p','l','s','t'): 200 RETURN_ERR( in.read( &playlist [0], size ) );
217 playlist.resize( size ); 201 break;
218 BLARGG_RETURN_ERR( in.read( &playlist [0], size ) ); 202
219 break; 203 case BLARGG_4CHAR('A','T','A','D'): {
220
221 case NSFE_TAG('D','A','T','A'): {
222 check( phase == 1 ); 204 check( phase == 1 );
223 phase = 2; 205 phase = 2;
206 disable_playlist( false );
224 if ( !nsf_emu ) 207 if ( !nsf_emu )
225 { 208 {
226 in.skip( size ); 209 in.skip( size );
227 } 210 }
228 else 211 else
229 { 212 {
230 Subset_Reader sub( &in, size ); // limit emu to nsf data 213 Subset_Reader sub( &in, size ); // limit emu to nsf data
231 BLARGG_RETURN_ERR( nsf_emu->load( info_, sub ) ); 214 Remaining_Reader rem( &header, sizeof header, &sub );
232 check( sub.remain() == 0 ); 215 RETURN_ERR( nsf_emu->load( rem ) );
216 check( rem.remain() == 0 );
233 } 217 }
218 disable_playlist( false ); // TODO: fix this crappy hack (unload() disables playlist)
234 break; 219 break;
235 } 220 }
236 221
237 case NSFE_TAG('N','E','N','D'): 222 case BLARGG_4CHAR('D','N','E','N'):
238 check( phase == 2 ); 223 check( phase == 2 );
239 phase = 3; 224 phase = 3;
240 break; 225 break;
241 226
242 default: 227 default:
243 // tags that can be skipped start with a lowercase character 228 // tags that can be skipped start with a lowercase character
244 check( std::islower( (tag >> 24) & 0xff ) ); 229 check( islower( (tag >> 24) & 0xFF ) );
245 BLARGG_RETURN_ERR( in.skip( size ) ); 230 RETURN_ERR( in.skip( size ) );
246 break; 231 break;
247 } 232 }
248 } 233 }
249 234
250 enable_playlist( playlist_enabled ); 235 return 0;
251 236 }
252 return blargg_success; 237
253 } 238 blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const
254 239 {
255 blargg_err_t Nsfe_Info::load( Emu_Reader& in, Nsf_Emu* nsf_emu ) 240 int remapped = remap_track( track );
256 { 241 if ( (unsigned) remapped < track_times.size() )
257 header_t h; 242 {
258 BLARGG_RETURN_ERR( in.read( &h, sizeof h ) ); 243 long length = (BOOST::int32_t) get_le32( track_times [remapped] );
259 return load( h, in, nsf_emu ); 244 if ( length > 0 )
260 } 245 out->length = length;
261 246 }
262 blargg_err_t Nsfe_Info::load_file( const char* path, Nsf_Emu* emu ) 247 if ( (unsigned) remapped < track_names.size() )
263 { 248 Gme_File::copy_field_( out->song, track_names [remapped] );
264 Std_File_Reader in; 249
265 BLARGG_RETURN_ERR( in.open( path ) ); 250 GME_COPY_FIELD( info, out, game );
266 return load( in, emu ); 251 GME_COPY_FIELD( info, out, author );
267 } 252 GME_COPY_FIELD( info, out, copyright );
268 253 GME_COPY_FIELD( info, out, dumper );
254 return 0;
255 }
256
257 Nsfe_Emu::Nsfe_Emu()
258 {
259 loading = false;
260 set_type( gme_nsfe_type );
261 }
262
263 Nsfe_Emu::~Nsfe_Emu() { }
264
265 void Nsfe_Emu::unload()
266 {
267 if ( !loading )
268 info.unload(); // TODO: extremely hacky!
269 Nsf_Emu::unload();
270 }
271
272 blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const
273 {
274 return info.track_info_( out, track );
275 }
276
277 struct Nsfe_File : Gme_Info_
278 {
279 Nsfe_Info info;
280
281 Nsfe_File() { set_type( gme_nsfe_type ); }
282
283 blargg_err_t load_( Data_Reader& in )
284 {
285 RETURN_ERR( info.load( in, 0 ) );
286 set_track_count( info.info.track_count );
287 return 0;
288 }
289
290 blargg_err_t track_info_( track_info_t* out, int track ) const
291 {
292 return info.track_info_( out, track );
293 }
294 };
295
296 static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; }
297 static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; }
298
299 gme_type_t_ const gme_nsfe_type [1] = { "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 };
300
301 blargg_err_t Nsfe_Emu::load_( Data_Reader& in )
302 {
303 if ( loading )
304 return Nsf_Emu::load_( in );
305
306 // TODO: this hacky recursion-avoidance could have subtle problems
307 loading = true;
308 blargg_err_t err = info.load( in, this );
309 loading = false;
310 return err;
311 }
312
313 void Nsfe_Emu::disable_playlist( bool b )
314 {
315 info.disable_playlist( b );
316 set_track_count( info.info.track_count );
317 }
318
319 void Nsfe_Emu::clear_playlist_()
320 {
321 disable_playlist();
322 Nsf_Emu::clear_playlist_();
323 }
324
325 blargg_err_t Nsfe_Emu::start_track_( int track )
326 {
327 return Nsf_Emu::start_track_( info.remap_track( track ) );
328 }