comparison src/console/M3u_Playlist.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
children 986f098da058
comparison
equal deleted inserted replaced
315:2294f3a6f136 316:fb513e10174e
1 // Game_Music_Emu 0.5.1. http://www.slack.net/~ant/
2
3 #include "M3u_Playlist.h"
4 #include "Music_Emu.h"
5
6 #include <string.h>
7
8 /* Copyright (C) 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 */
18
19 #include "blargg_source.h"
20
21 // gme functions defined here to avoid linking in m3u code unless it's used
22
23 blargg_err_t Gme_File::load_m3u_( blargg_err_t err )
24 {
25 if ( !err && playlist.size() )
26 track_count_ = playlist.size();
27 require( raw_track_count_ ); // file must be loaded first
28 return err;
29 }
30
31 blargg_err_t Gme_File::load_m3u( const char* path ) { return load_m3u_( playlist.load( path ) ); }
32
33 blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); }
34
35 const char* gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); }
36
37 const char* gme_load_m3u_data( Music_Emu* me, const void* data, long size )
38 {
39 Mem_File_Reader in( data, size );
40 return me->load_m3u( in );
41 }
42
43
44
45 static char* skip_white( char* in )
46 {
47 while ( *in == ' ' )
48 in++;
49 return in;
50 }
51
52 inline unsigned from_dec( unsigned n ) { return n - '0'; }
53
54 static char* parse_filename( char* in, M3u_Playlist::entry_t& entry )
55 {
56 entry.file = in;
57 entry.type = "";
58 char* out = in;
59 while ( 1 )
60 {
61 int c = *in;
62 if ( !c ) break;
63 in++;
64
65 if ( c == ',' ) // commas in filename
66 {
67 char* p = skip_white( in );
68 if ( *p == '$' || from_dec( *p ) <= 9 )
69 {
70 in = p;
71 break;
72 }
73 }
74
75 if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix
76 {
77 entry.type = ++in;
78 while ( (c = *in) != 0 && c != ',' )
79 in++;
80 if ( c == ',' )
81 {
82 *in++ = 0; // terminate type
83 in = skip_white( in );
84 }
85 break;
86 }
87
88 if ( c == '\\' ) // \ prefix for special characters
89 {
90 c = *in;
91 if ( !c ) break;
92 in++;
93 }
94 *out++ = (char) c;
95 }
96 *out = 0; // terminate string
97 return in;
98 }
99
100 static char* next_field( char* in, int* result )
101 {
102 while ( 1 )
103 {
104 in = skip_white( in );
105
106 if ( !*in )
107 break;
108
109 if ( *in == ',' )
110 {
111 in++;
112 break;
113 }
114
115 *result = 1;
116 in++;
117 }
118 return skip_white( in );
119 }
120
121 static char* parse_int_( char* in, int* out )
122 {
123 int n = 0;
124 while ( 1 )
125 {
126 unsigned d = from_dec( *in );
127 if ( d > 9 )
128 break;
129 in++;
130 n = n * 10 + d;
131 *out = n;
132 }
133 return in;
134 }
135
136 static char* parse_int( char* in, int* out, int* result )
137 {
138 return next_field( parse_int_( in, out ), result );
139 }
140
141 // Returns 16 or greater if not hex
142 inline int from_hex_char( int h )
143 {
144 h -= 0x30;
145 if ( (unsigned) h > 9 )
146 h = ((h - 0x11) & 0xDF) + 10;
147 return h;
148 }
149
150 static char* parse_track( char* in, M3u_Playlist::entry_t& entry, int* result )
151 {
152 if ( *in == '$' )
153 {
154 in++;
155 int n = 0;
156 while ( 1 )
157 {
158 int h = from_hex_char( *in );
159 if ( h > 15 )
160 break;
161 in++;
162 n = n * 16 + h;
163 entry.track = n;
164 }
165 }
166 else
167 {
168 in = parse_int_( in, &entry.track );
169 if ( entry.track >= 0 )
170 entry.decimal_track = 1;
171 }
172 return next_field( in, result );
173 }
174
175 static char* parse_time_( char* in, int* out )
176 {
177 *out = -1;
178 int n = -1;
179 in = parse_int_( in, &n );
180 if ( n >= 0 )
181 {
182 *out = n;
183 if ( *in == ':' )
184 {
185 n = -1;
186 in = parse_int_( in + 1, &n );
187 if ( n >= 0 )
188 *out = *out * 60 + n;
189 }
190 }
191 return in;
192 }
193
194 static char* parse_time( char* in, int* out, int* result )
195 {
196 return next_field( parse_time_( in, out ), result );
197 }
198
199 static char* parse_name( char* in )
200 {
201 char* out = in;
202 while ( 1 )
203 {
204 int c = *in;
205 if ( !c ) break;
206 in++;
207
208 if ( c == ',' ) // commas in string
209 {
210 char* p = skip_white( in );
211 if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 )
212 {
213 in = p;
214 break;
215 }
216 }
217
218 if ( c == '\\' ) // \ prefix for special characters
219 {
220 c = *in;
221 if ( !c ) break;
222 in++;
223 }
224 *out++ = (char) c;
225 }
226 *out = 0; // terminate string
227 return in;
228 }
229
230 static int parse_line( char* in, M3u_Playlist::entry_t& entry )
231 {
232 int result = 0;
233
234 // file
235 entry.file = in;
236 entry.type = "";
237 in = parse_filename( in, entry );
238
239 // track
240 entry.track = -1;
241 entry.decimal_track = 0;
242 in = parse_track( in, entry, &result );
243
244 // name
245 entry.name = in;
246 in = parse_name( in );
247
248 // time
249 entry.length = -1;
250 in = parse_time( in, &entry.length, &result );
251
252 // loop
253 entry.intro = -1;
254 entry.loop = -1;
255 if ( *in == '-' )
256 {
257 entry.loop = entry.length;
258 in++;
259 }
260 else
261 {
262 in = parse_time_( in, &entry.loop );
263 if ( entry.loop >= 0 )
264 {
265 entry.intro = 0;
266 if ( *in == '-' ) // trailing '-' means that intro length was specified
267 {
268 in++;
269 entry.intro = entry.loop;
270 entry.loop = entry.length - entry.intro;
271 }
272 }
273 }
274 in = next_field( in, &result );
275
276 // fade
277 entry.fade = -1;
278 in = parse_time( in, &entry.fade, &result );
279
280 // repeat
281 entry.repeat = -1;
282 in = parse_int( in, &entry.repeat, &result );
283
284 return result;
285 }
286
287 static void parse_comment( char* in, M3u_Playlist::info_t& info, bool first )
288 {
289 in = skip_white( in + 1 );
290 const char* field = in;
291 while ( *in && *in != ':' )
292 in++;
293
294 if ( *in == ':' )
295 {
296 const char* text = skip_white( in + 1 );
297 if ( *text )
298 {
299 *in = 0;
300 if ( !strcmp( "Composer", field ) ) info.composer = text;
301 else if ( !strcmp( "Engineer", field ) ) info.engineer = text;
302 else if ( !strcmp( "Ripping" , field ) ) info.ripping = text;
303 else if ( !strcmp( "Tagging" , field ) ) info.tagging = text;
304 else
305 text = 0;
306 if ( text )
307 return;
308 *in = ':';
309 }
310 }
311
312 if ( first )
313 info.title = field;
314 }
315
316 blargg_err_t M3u_Playlist::parse_()
317 {
318 info_.title = "";
319 info_.composer = "";
320 info_.engineer = "";
321 info_.ripping = "";
322 info_.tagging = "";
323
324 int const CR = 13;
325 int const LF = 10;
326
327 data.end() [-1] = LF; // terminate input
328
329 first_error_ = 0;
330 bool first_comment = true;
331 int line = 0;
332 int count = 0;
333 char* in = data.begin();
334 while ( in < data.end() )
335 {
336 // find end of line and terminate it
337 line++;
338 char* begin = in;
339 while ( *in != CR && *in != LF )
340 {
341 if ( !*in )
342 return "Not an m3u playlist";
343 in++;
344 }
345 if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line
346 *in++ = 0;
347 *in++ = 0;
348
349 // parse line
350 if ( *begin == '#' )
351 {
352 parse_comment( begin, info_, first_comment );
353 first_comment = false;
354 }
355 else if ( *begin )
356 {
357 if ( (int) entries.size() <= count )
358 RETURN_ERR( entries.resize( count * 2 + 64 ) );
359
360 if ( !parse_line( begin, entries [count] ) )
361 count++;
362 else if ( !first_error_ )
363 first_error_ = line;
364 first_comment = false;
365 }
366 }
367 if ( count <= 0 )
368 return "Not an m3u playlist";
369
370 if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) )
371 info_.title = "";
372
373 return entries.resize( count );
374 }
375
376 blargg_err_t M3u_Playlist::parse()
377 {
378 blargg_err_t err = parse_();
379 if ( err )
380 {
381 entries.clear();
382 data.clear();
383 }
384 return err;
385 }
386
387 blargg_err_t M3u_Playlist::load( Data_Reader& in )
388 {
389 RETURN_ERR( data.resize( in.remain() + 1 ) );
390 RETURN_ERR( in.read( data.begin(), data.size() - 1 ) );
391 return parse();
392 }
393
394 blargg_err_t M3u_Playlist::load( const char* path )
395 {
396 GME_FILE_READER in;
397 RETURN_ERR( in.open( path ) );
398 return load( in );
399 }
400
401 blargg_err_t M3u_Playlist::load( void const* in, long size )
402 {
403 RETURN_ERR( data.resize( size + 1 ) );
404 memcpy( data.begin(), in, size );
405 return parse();
406 }