7 static bool is_numeric(
char c) {
return c>=
'0' && c<=
'9';}
12 return c ==
' ' || c ==
'\t';
17 return c ==
'\n' || c ==
'\r';
32 pfc::throw_exception_with_message< exception_cue >(PFC_string_formatter() <<
"expected WAVE, MP3 or AIFF, got : \"" <<
pfc::string_part(p_type,p_type_length) <<
"\"");
37 class NOVTABLE cue_parser_callback
40 virtual void on_file(
const char * p_file,
t_size p_file_length,
const char * p_type,
t_size p_type_length) = 0;
41 virtual void on_track(
unsigned p_index,
const char * p_type,
t_size p_type_length) = 0;
42 virtual void on_pregap(
unsigned p_value) = 0;
43 virtual void on_index(
unsigned p_index,
unsigned p_value) = 0;
44 virtual void on_title(
const char * p_title,
t_size p_title_length) = 0;
45 virtual void on_performer(
const char * p_performer,
t_size p_performer_length) = 0;
46 virtual void on_songwriter(
const char * p_songwriter,
t_size p_songwriter_length) = 0;
47 virtual void on_isrc(
const char * p_isrc,
t_size p_isrc_length) = 0;
48 virtual void on_catalog(
const char * p_catalog,
t_size p_catalog_length) = 0;
49 virtual void on_comment(
const char * p_comment,
t_size p_comment_length) = 0;
50 virtual void on_flags(
const char * p_flags,
t_size p_flags_length) = 0;
53 class NOVTABLE cue_parser_callback_meta :
public cue_parser_callback
56 virtual void on_file(
const char * p_file,
t_size p_file_length,
const char * p_type,
t_size p_type_length) = 0;
57 virtual void on_track(
unsigned p_index,
const char * p_type,
t_size p_type_length) = 0;
58 virtual void on_pregap(
unsigned p_value) = 0;
59 virtual void on_index(
unsigned p_index,
unsigned p_value) = 0;
60 virtual void on_meta(
const char * p_name,
t_size p_name_length,
const char * p_value,
t_size p_value_length) = 0;
62 static bool is_known_meta(
const char * p_name,
t_size p_length)
64 static const char * metas[] = {
"genre",
"date",
"discid",
"comment",
"replaygain_track_gain",
"replaygain_track_peak",
"replaygain_album_gain",
"replaygain_album_peak"};
65 for(
t_size n=0;n<PFC_TABSIZE(metas);n++) {
66 if (!
stricmp_utf8_ex(p_name,p_length,metas[n],pfc_infinite))
return true;
71 void on_comment(
const char * p_comment,
t_size p_comment_length)
74 while(ptr < p_comment_length && !
is_spacing(p_comment[ptr])) ptr++;
75 if (is_known_meta(p_comment, ptr))
77 unsigned name_length = ptr;
78 while(ptr < p_comment_length &&
is_spacing(p_comment[ptr])) ptr++;
79 if (ptr < p_comment_length)
81 if (p_comment[ptr] ==
'\"')
84 unsigned value_base = ptr;
85 while(ptr < p_comment_length && p_comment[ptr] !=
'\"') ptr++;
86 if (ptr == p_comment_length) pfc::throw_exception_with_message<exception_cue>(
"invalid REM syntax");
87 if (ptr > value_base) on_meta(p_comment,name_length,p_comment + value_base,ptr - value_base);
91 unsigned value_base = ptr;
92 while(ptr < p_comment_length ) ptr++;
93 if (ptr > value_base) on_meta(p_comment,name_length,p_comment + value_base,ptr - value_base);
98 void on_title(
const char * p_title,
t_size p_title_length)
100 on_meta(
"title",pfc_infinite,p_title,p_title_length);
102 void on_songwriter(
const char * p_songwriter,
t_size p_songwriter_length) {
103 on_meta(
"songwriter",pfc_infinite,p_songwriter,p_songwriter_length);
105 void on_performer(
const char * p_performer,
t_size p_performer_length)
107 on_meta(
"artist",pfc_infinite,p_performer,p_performer_length);
110 void on_isrc(
const char * p_isrc,
t_size p_isrc_length)
112 on_meta(
"isrc",pfc_infinite,p_isrc,p_isrc_length);
114 void on_catalog(
const char * p_catalog,
t_size p_catalog_length)
116 on_meta(
"catalog",pfc_infinite,p_catalog,p_catalog_length);
118 void on_flags(
const char * p_flags,
t_size p_flags_length) {}
122 class cue_parser_callback_retrievelist :
public cue_parser_callback
125 cue_parser_callback_retrievelist(
cue_parser::t_cue_entry_list & p_out) : m_out(p_out), m_track(0), m_pregap(0), m_index0_set(
false), m_index1_set(
false)
129 void on_file(
const char * p_file,
t_size p_file_length,
const char * p_type,
t_size p_type_length)
132 m_file.set_string(p_file,p_file_length);
135 void on_track(
unsigned p_index,
const char * p_type,
t_size p_type_length)
137 if (
stricmp_utf8_ex(p_type,p_type_length,
"audio",pfc_infinite)) pfc::throw_exception_with_message<exception_cue>(
"only tracks of type AUDIO supported");
139 if (m_track != 0) finalize_track();
140 if (m_file.is_empty()) pfc::throw_exception_with_message<exception_cue>(
"declaring a track with no file set");
141 m_trackfile = m_file;
145 void on_pregap(
unsigned p_value) {m_pregap = (double) p_value / 75.0;}
147 void on_index(
unsigned p_index,
unsigned p_value)
153 case 0: m_index0_set =
true;
break;
154 case 1: m_index1_set =
true;
break;
156 m_index_list.m_positions[p_index] = (double) p_value / 75.0;
160 void on_title(
const char * p_title,
t_size p_title_length) {}
161 void on_performer(
const char * p_performer,
t_size p_performer_length) {}
162 void on_songwriter(
const char * p_songwriter,
t_size p_songwriter_length) {}
163 void on_isrc(
const char * p_isrc,
t_size p_isrc_length) {}
164 void on_catalog(
const char * p_catalog,
t_size p_catalog_length) {}
165 void on_comment(
const char * p_comment,
t_size p_comment_length) {}
166 void on_flags(
const char * p_flags,
t_size p_flags_length) {}
178 void finalize_track()
180 if (!m_index1_set) pfc::throw_exception_with_message< exception_cue > (
"INDEX 01 not set");
181 if (!m_index0_set) m_index_list.m_positions[0] = m_index_list.m_positions[1] - m_pregap;
182 if (!m_index_list.is_valid()) pfc::throw_exception_with_message< exception_cue > (
"invalid index list");
185 iter = m_out.insert_last();
186 if (m_trackfile.is_empty()) pfc::throw_exception_with_message< exception_cue > (
"track has no file assigned");
187 iter->m_file = m_trackfile;
188 iter->m_track_number = m_track;
189 iter->m_indexes = m_index_list;
191 m_index_list.reset();
192 m_index0_set =
false;
193 m_index1_set =
false;
197 bool m_index0_set,m_index1_set;
205 class cue_parser_callback_retrieveinfo :
public cue_parser_callback_meta
208 cue_parser_callback_retrieveinfo(
file_info & p_out,
unsigned p_wanted_track) : m_out(p_out), m_wanted_track(p_wanted_track), m_track(0), m_is_va(
false), m_index0_set(
false), m_index1_set(
false), m_pregap(0), m_totaltracks(0) {}
210 void on_file(
const char * p_file,
t_size p_file_length,
const char * p_type,
t_size p_type_length) {}
212 void on_track(
unsigned p_index,
const char * p_type,
t_size p_type_length)
214 if (p_index == 0) pfc::throw_exception_with_message< exception_cue > (
"invalid TRACK index");
215 if (p_index == m_wanted_track)
217 if (
stricmp_utf8_ex(p_type,p_type_length,
"audio",pfc_infinite)) pfc::throw_exception_with_message< exception_cue > (
"only tracks of type AUDIO supported");
223 void on_pregap(
unsigned p_value) {
if (m_track == m_wanted_track) m_pregap = (double) p_value / 75.0;}
225 void on_index(
unsigned p_index,
unsigned p_value)
231 case 0: m_index0_set =
true;
break;
232 case 1: m_index1_set =
true;
break;
234 m_indexes.m_positions[p_index] = (double) p_value / 75.0;
239 void on_meta(
const char * p_name,
t_size p_name_length,
const char * p_value,
t_size p_value_length)
241 t_meta_list::iterator iter;
252 m_album_artist.set_string(p_value,p_value_length);
255 iter = m_globals.insert_last();
263 if (!m_album_artist.is_empty())
265 if (
stricmp_utf8_ex(p_value,p_value_length,m_album_artist,m_album_artist.length())) m_is_va =
true;
270 if (m_track == m_wanted_track)
272 iter = m_locals.insert_last();
277 iter->m_name.set_string(p_name,p_name_length);
278 iter->m_value.set_string(p_value,p_value_length);
284 if (!m_index1_set) pfc::throw_exception_with_message< exception_cue > (
"INDEX 01 not set");
285 if (!m_index0_set) m_indexes.m_positions[0] = m_indexes.m_positions[1] - m_pregap;
286 m_indexes.to_infos(m_out);
290 t_meta_list::const_iterator iter;
296 t_meta_list::const_iterator iter_global,iter_local;
298 iter_global = find_first_field(m_globals,
"artist");
299 iter_local = find_first_field(m_locals,
"artist");
300 if (iter_global.is_valid())
302 m_out.meta_set(
"album artist",iter_global->m_value);
303 if (iter_local.is_valid()) m_out.meta_set(
"artist",iter_local->m_value);
304 else m_out.meta_set(
"artist",iter_global->m_value);
308 if (iter_local.is_valid()) m_out.meta_set(
"artist",iter_local->m_value);
312 wipe_field(m_globals,
"artist");
313 wipe_field(m_locals,
"artist");
317 for(iter=m_globals.first();iter.is_valid();iter++)
320 m_out.meta_set(iter->m_name,iter->m_value);
322 for(iter=m_locals.first();iter.is_valid();iter++)
325 m_out.meta_set(iter->m_name,iter->m_value);
327 m_out.meta_set(
"tracknumber",PFC_string_formatter() << m_wanted_track);
328 m_out.meta_set(
"totaltracks", PFC_string_formatter() << m_totaltracks);
329 m_out.set_replaygain(rg);
333 struct t_meta_entry {
338 static t_meta_list::const_iterator find_first_field(t_meta_list
const & p_list,
const char * p_field)
340 t_meta_list::const_iterator iter;
341 for(iter=p_list.first();iter.is_valid();++iter)
345 return t_meta_list::const_iterator();
348 static void wipe_field(t_meta_list & p_list,
const char * p_field)
350 t_meta_list::iterator iter;
351 for(iter=p_list.first();iter.is_valid();)
355 t_meta_list::iterator temp = iter;
357 p_list.remove_single(iter);
367 t_meta_list m_globals,m_locals;
369 unsigned m_wanted_track, m_track,m_totaltracks;
373 bool m_index0_set,m_index1_set;
383 while(ptr < p_line_length && !
is_spacing(p_line[ptr])) ptr++;
386 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
387 t_size file_base,file_length, type_base,type_length;
389 if (p_line[ptr] ==
'\"')
393 while(ptr < p_line_length && p_line[ptr] !=
'\"') ptr++;
394 if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid FILE syntax");
395 file_length = ptr - file_base;
397 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
402 while(ptr < p_line_length && !
is_spacing(p_line[ptr])) ptr++;
403 file_length = ptr - file_base;
404 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
408 while(ptr < p_line_length && !
is_spacing(p_line[ptr])) ptr++;
409 type_length = ptr - type_base;
410 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
412 if (ptr != p_line_length || file_length == 0 || type_length == 0) pfc::throw_exception_with_message< exception_cue > (
"invalid FILE syntax");
414 p_callback.on_file(p_line + file_base, file_length, p_line + type_base, type_length);
418 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
420 t_size track_base = ptr, track_length;
421 while(ptr < p_line_length && !
is_spacing(p_line[ptr]))
423 if (!
is_numeric(p_line[ptr])) pfc::throw_exception_with_message< exception_cue > (
"invalid TRACK syntax");
426 track_length = ptr - track_base;
427 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
429 t_size type_base = ptr, type_length;
430 while(ptr < p_line_length && !
is_spacing(p_line[ptr])) ptr++;
431 type_length = ptr - type_base;
433 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
434 if (ptr != p_line_length || type_length == 0) pfc::throw_exception_with_message< exception_cue > (
"invalid TRACK syntax");
435 unsigned track =
pfc::atoui_ex(p_line+track_base,track_length);
436 if (track < 1 || track > 99) pfc::throw_exception_with_message< exception_cue > (
"invalid track number");
438 p_callback.on_track(track,p_line + type_base, type_length);
442 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
444 t_size index_base,index_length, time_base,time_length;
446 while(ptr < p_line_length && !
is_spacing(p_line[ptr]))
448 if (!
is_numeric(p_line[ptr])) pfc::throw_exception_with_message< exception_cue > (
"invalid INDEX syntax" );
451 index_length = ptr - index_base;
453 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
455 while(ptr < p_line_length && !
is_spacing(p_line[ptr]))
457 if (!
is_numeric(p_line[ptr]) && p_line[ptr] !=
':')
458 pfc::throw_exception_with_message< exception_cue > (
"invalid INDEX syntax");
461 time_length = ptr - time_base;
463 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
465 if (ptr != p_line_length || index_length == 0 || time_length == 0)
466 pfc::throw_exception_with_message< exception_cue > (
"invalid INDEX syntax");
468 unsigned index =
pfc::atoui_ex(p_line+index_base,index_length);
469 if (index > 99) pfc::throw_exception_with_message< exception_cue > (
"invalid INDEX syntax");
472 p_callback.on_index(index,time);
476 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
478 t_size time_base, time_length;
480 while(ptr < p_line_length && !
is_spacing(p_line[ptr]))
482 if (!
is_numeric(p_line[ptr]) && p_line[ptr] !=
':')
483 pfc::throw_exception_with_message< exception_cue > (
"invalid PREGAP syntax");
486 time_length = ptr - time_base;
488 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
490 if (ptr != p_line_length || time_length == 0)
491 pfc::throw_exception_with_message< exception_cue > (
"invalid PREGAP syntax");
495 p_callback.on_pregap(time);
499 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
500 if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid TITLE syntax");
501 if (p_line[ptr] ==
'\"')
505 while(ptr < p_line_length && p_line[ptr] !=
'\"') ptr++;
506 if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid TITLE syntax");
509 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
510 if (ptr != p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid TITLE syntax");
511 p_callback.on_title(p_line+base,length);
515 p_callback.on_title(p_line+ptr,p_line_length-ptr);
520 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
521 if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid PERFORMER syntax");
522 if (p_line[ptr] ==
'\"')
526 while(ptr < p_line_length && p_line[ptr] !=
'\"') ptr++;
527 if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid PERFORMER syntax");
530 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
531 if (ptr != p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid PERFORMER syntax");
532 p_callback.on_performer(p_line+base,length);
536 p_callback.on_performer(p_line+ptr,p_line_length-ptr);
541 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
542 if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid SONGWRITER syntax");
543 if (p_line[ptr] ==
'\"')
547 while(ptr < p_line_length && p_line[ptr] !=
'\"') ptr++;
548 if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid SONGWRITER syntax");
551 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
552 if (ptr != p_line_length) pfc::throw_exception_with_message< exception_cue > (
"invalid SONGWRITER syntax");
553 p_callback.on_songwriter(p_line+base,length);
557 p_callback.on_songwriter(p_line+ptr,p_line_length-ptr);
562 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
563 t_size length = p_line_length - ptr;
564 if (length == 0) pfc::throw_exception_with_message< exception_cue > (
"invalid ISRC syntax");
565 p_callback.on_isrc(p_line+ptr,length);
569 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
570 t_size length = p_line_length - ptr;
571 if (length == 0) pfc::throw_exception_with_message< exception_cue > (
"invalid CATALOG syntax");
572 p_callback.on_catalog(p_line+ptr,length);
576 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
577 if (ptr < p_line_length)
578 p_callback.on_flags(p_line + ptr, p_line_length - ptr);
582 while(ptr < p_line_length &&
is_spacing(p_line[ptr])) ptr++;
583 if (ptr < p_line_length)
584 p_callback.on_comment(p_line + ptr, p_line_length - ptr);
587 pfc::throw_exception_with_message< exception_cue > (
"POSTGAP is not supported");
591 else pfc::throw_exception_with_message< exception_cue > (
"unknown cuesheet item");
594 static void g_parse_cue(
const char * p_cuesheet,cue_parser_callback & p_callback)
596 const char * parseptr = p_cuesheet;
604 while(parseptr[length] && !
is_linebreak(parseptr[length])) length++;
608 }
catch(exception_cue
const & e) {
609 pfc::throw_exception_with_message< exception_cue > (PFC_string_formatter() << e.what() <<
" (line " << (unsigned)lineidx <<
")");
614 if (*parseptr ==
'\n') lineidx++;
623 cue_parser_callback_retrievelist callback(p_out);
626 }
catch(exception_cue
const & e) {
627 pfc::throw_exception_with_message<exception_bad_cuesheet>(PFC_string_formatter() <<
"Error parsing cuesheet: " << e.what());
632 cue_parser_callback_retrieveinfo callback(p_info,p_index);
635 }
catch(exception_cue
const & e) {
636 pfc::throw_exception_with_message< exception_bad_cuesheet > (PFC_string_formatter() <<
"Error parsing cuesheet: " << e.what());
642 class cue_parser_callback_retrievecount :
public cue_parser_callback
645 cue_parser_callback_retrievecount() : m_count(0) {}
646 unsigned get_count()
const {
return m_count;}
647 void on_file(
const char * p_file,
t_size p_file_length,
const char * p_type,
t_size p_type_length) {}
648 void on_track(
unsigned p_index,
const char * p_type,
t_size p_type_length) {m_count++;}
649 void on_pregap(
unsigned p_value) {}
650 void on_index(
unsigned p_index,
unsigned p_value) {}
651 void on_title(
const char * p_title,
t_size p_title_length) {}
652 void on_performer(
const char * p_performer,
t_size p_performer_length) {}
653 void on_isrc(
const char * p_isrc,
t_size p_isrc_length) {}
654 void on_catalog(
const char * p_catalog,
t_size p_catalog_length) {}
655 void on_comment(
const char * p_comment,
t_size p_comment_length) {}
656 void on_flags(
const char * p_flags,
t_size p_flags_length) {}
661 class cue_parser_callback_retrievecreatorentries :
public cue_parser_callback
664 cue_parser_callback_retrievecreatorentries(
cue_creator::t_entry_list & p_out) : m_out(p_out), m_track(0), m_pregap(0), m_index0_set(
false), m_index1_set(
false) {}
666 void on_file(
const char * p_file,
t_size p_file_length,
const char * p_type,
t_size p_type_length) {
668 m_file.set_string(p_file,p_file_length);
671 void on_track(
unsigned p_index,
const char * p_type,
t_size p_type_length)
673 if (
stricmp_utf8_ex(p_type,p_type_length,
"audio",pfc_infinite)) pfc::throw_exception_with_message< exception_cue > (
"only tracks of type AUDIO supported");
675 if (m_track != 0) finalize_track();
676 if (m_file.is_empty()) pfc::throw_exception_with_message< exception_cue > (
"declaring a track with no file set");
677 m_trackfile = m_file;
681 void on_pregap(
unsigned p_value)
683 m_pregap = (double) p_value / 75.0;
686 void on_index(
unsigned p_index,
unsigned p_value)
692 case 0: m_index0_set =
true;
break;
693 case 1: m_index1_set =
true;
break;
695 m_indexes.m_positions[p_index] = (double) p_value / 75.0;
698 void on_title(
const char * p_title,
t_size p_title_length) {}
699 void on_performer(
const char * p_performer,
t_size p_performer_length) {}
700 void on_songwriter(
const char * p_performer,
t_size p_performer_length) {}
701 void on_isrc(
const char * p_isrc,
t_size p_isrc_length) {}
702 void on_catalog(
const char * p_catalog,
t_size p_catalog_length) {}
703 void on_comment(
const char * p_comment,
t_size p_comment_length) {}
712 void on_flags(
const char * p_flags,
t_size p_flags_length) {
713 m_flags.set_string(p_flags,p_flags_length);
716 void finalize_track()
718 if (m_track < 1 || m_track > 99) pfc::throw_exception_with_message< exception_cue > (
"track number out of range");
719 if (!m_index1_set) pfc::throw_exception_with_message< exception_cue > (
"INDEX 01 not set");
720 if (!m_index0_set) m_indexes.m_positions[0] = m_indexes.m_positions[1] - m_pregap;
721 if (!m_indexes.is_valid()) pfc::throw_exception_with_message< exception_cue > (
"invalid index list");
724 iter = m_out.insert_last();
725 iter->m_track_number = m_track;
726 iter->m_file = m_trackfile;
727 iter->m_index_list = m_indexes;
728 iter->m_flags = m_flags;
731 m_index0_set = m_index1_set =
false;
735 bool m_index0_set,m_index1_set;
747 cue_parser_callback_retrievecreatorentries callback(p_out);
756 cue_parser_callback_retrieveinfo callback(iter->m_infos,iter->m_track_number);
761 }
catch(exception_cue
const & e) {
762 pfc::throw_exception_with_message< exception_bad_cuesheet > (PFC_string_formatter() <<
"Error parsing cuesheet: " << e.what());
static void g_parse_cue_line(const char *p_line, t_size p_line_length, cue_parser_callback &p_callback)
static void validate_file_type(const char *p_type, t_size p_type_length)
int SHARED_EXPORT stricmp_utf8_ex(const char *p1, t_size len1, const char *p2, t_size len2)
bool set_from_meta(const char *p_name, const char *p_value)
static bool is_linebreak(char c)
Differences between chain_list_v2_t<> and old chain_list_t<>: Iterators pointing to removed items as...
int SHARED_EXPORT stricmp_utf8(const char *p1, const char *p2)
Structure containing ReplayGain scan results from some playable object, also providing various helper...
Main interface class for information about some playable object.
void parse(const char *p_cuesheet, t_cue_entry_list &p_out)
Throws exception_bad_cuesheet on failure.
PFC_DECLARE_EXCEPTION(exception_bad_cuesheet, exception_io_data,"Invalid cuesheet")
unsigned atoui_ex(const char *p_string, t_size p_string_len)
unsigned cuesheet_parse_index_time_ticks_e(const char *p_string, t_size p_length)
static bool is_numeric(char c)
static bool is_spacing(char c)
static void g_parse_cue(const char *p_cuesheet, cue_parser_callback &p_callback)
string_part_ref string_part(const char *ptr, t_size len)
void parse_full(const char *p_cuesheet, cue_creator::t_entry_list &p_out)
Throws exception_bad_cuesheet on failure.
void parse_info(const char *p_cuesheet, file_info &p_info, unsigned p_index)
Throws exception_bad_cuesheet on failure.