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