Mercurial > audlegacy-plugins
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 } |