void LoadFromString(ParseCueJob job) { string cueString = job.IN_CueString; TextReader tr = new StringReader(cueString); for (; ; ) { job.CurrentLine++; string line = tr.ReadLine(); if (line == null) break; line = line.Trim(); if (line == "") continue; var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpperInvariant(); if (key.StartsWith(";")) { clp.EOF = true; OUT_CueFile.Commands.Add(new CUE_File.Command.COMMENT() { Value = line }); } else switch (key) { default: job.Warn("Unknown command: " + key); break; case "CATALOG": if (OUT_CueFile.GlobalDiscInfo.Catalog != null) job.Warn("Multiple CATALOG commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty CATALOG command"); else OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.Catalog = new CUE_File.Command.CATALOG() { Value = clp.ReadToken() }); break; case "CDTEXTFILE": if (OUT_CueFile.GlobalDiscInfo.CDTextFile != null) job.Warn("Multiple CDTEXTFILE commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty CDTEXTFILE command"); else OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.CDTextFile = new CUE_File.Command.CDTEXTFILE() { Path = clp.ReadPath() }); break; case "FILE": { var path = clp.ReadPath(); CueFileType ft; if (clp.EOF) { job.Error("FILE command is missing file type."); ft = CueFileType.Unspecified; } else { var strType = clp.ReadToken().ToUpperInvariant(); switch (strType) { default: job.Error("Unknown FILE type: " + strType); ft = CueFileType.Unspecified; break; case "BINARY": ft = CueFileType.BINARY; break; case "MOTOROLA": ft = CueFileType.MOTOROLA; break; case "BINARAIFF": ft = CueFileType.AIFF; break; case "WAVE": ft = CueFileType.WAVE; break; case "MP3": ft = CueFileType.MP3; break; } } OUT_CueFile.Commands.Add(new CUE_File.Command.FILE() { Path = path, Type = ft }); } break; case "FLAGS": { var cmd = new CUE_File.Command.FLAGS(); OUT_CueFile.Commands.Add(cmd); while (!clp.EOF) { var flag = clp.ReadToken().ToUpperInvariant(); switch (flag) { case "DATA": default: job.Warn("Unknown FLAG: " + flag); break; case "DCP": cmd.Flags |= CueTrackFlags.DCP; break; case "4CH": cmd.Flags |= CueTrackFlags._4CH; break; case "PRE": cmd.Flags |= CueTrackFlags.PRE; break; case "SCMS": cmd.Flags |= CueTrackFlags.SCMS; break; } } if (cmd.Flags == CueTrackFlags.None) job.Warn("Empty FLAG command"); } break; case "INDEX": { if (clp.EOF) { job.Error("Incomplete INDEX command"); break; } string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum) || indexnum < 0 || indexnum > 99) { job.Error("Invalid INDEX number: " + strindexnum); break; } string str_timestamp = clp.ReadToken(); var ts = new Timestamp(str_timestamp); if (!ts.Valid) { job.Error("Invalid INDEX timestamp: " + str_timestamp); break; } OUT_CueFile.Commands.Add(new CUE_File.Command.INDEX() { Number = indexnum, Timestamp = ts }); } break; case "ISRC": if (OUT_CueFile.GlobalDiscInfo.ISRC != null) job.Warn("Multiple ISRC commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty ISRC command"); else { var isrc = clp.ReadToken(); if (isrc.Length != 12) job.Warn("Invalid ISRC code ignored: " + isrc); else { OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.ISRC = new CUE_File.Command.ISRC() { Value = isrc }); } } break; case "PERFORMER": OUT_CueFile.Commands.Add(new CUE_File.Command.PERFORMER() { Value = clp.ReadPath() ?? "" }); break; case "POSTGAP": case "PREGAP": { var str_msf = clp.ReadToken(); var msf = new Timestamp(str_msf); if (!msf.Valid) job.Error("Ignoring {0} with invalid length MSF: " + str_msf, key); else { if (key == "POSTGAP") OUT_CueFile.Commands.Add(new CUE_File.Command.POSTGAP() { Length = msf }); else OUT_CueFile.Commands.Add(new CUE_File.Command.PREGAP() { Length = msf }); } } break; case "REM": OUT_CueFile.Commands.Add(new CUE_File.Command.REM() { Value = clp.ReadLine() }); break; case "SONGWRITER": OUT_CueFile.Commands.Add(new CUE_File.Command.SONGWRITER() { Value = clp.ReadPath() ?? "" }); break; case "TITLE": OUT_CueFile.Commands.Add(new CUE_File.Command.TITLE() { Value = clp.ReadPath() ?? "" }); break; case "TRACK": { if (clp.EOF) { job.Error("Incomplete TRACK command"); break; } string str_tracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(str_tracknum, out tracknum) || tracknum < 1 || tracknum > 99) { job.Error("Invalid TRACK number: " + str_tracknum); break; } //TODO - check sequentiality? maybe as a warning CueTrackType tt; var str_trackType = clp.ReadToken(); switch (str_trackType.ToUpperInvariant()) { default: job.Error("Unknown TRACK type: " + str_trackType); tt = CueTrackType.Unknown; break; case "AUDIO": tt = CueTrackType.Audio; break; case "CDG": tt = CueTrackType.CDG; break; case "MODE1/2048": tt = CueTrackType.Mode1_2048; break; case "MODE1/2352": tt = CueTrackType.Mode1_2352; break; case "MODE2/2336": tt = CueTrackType.Mode2_2336; break; case "MODE2/2352": tt = CueTrackType.Mode2_2352; break; case "CDI/2336": tt = CueTrackType.CDI_2336; break; case "CDI/2352": tt = CueTrackType.CDI_2352; break; } OUT_CueFile.Commands.Add(new CUE_File.Command.TRACK() { Number = tracknum, Type = tt }); } break; } if (!clp.EOF) { var remainder = clp.ReadLine(); if (remainder.TrimStart().StartsWith(";")) { //add a comment OUT_CueFile.Commands.Add(new CUE_File.Command.COMMENT() { Value = remainder }); } else job.Warn("Unknown text at end of line after processing command: " + key); } } //end cue parsing loop job.FinishLog(); } //LoadFromString
public void LoadFromString(string cueString) { TextReader tr = new StringReader(cueString); bool track_has_pregap = false; bool track_has_postgap = false; int last_index_num = -1; CueFile currFile = null; CueTrack currTrack = null; for (; ; ) { string line = tr.ReadLine(); if (line == null) break; line = line.Trim(); if (line == "") continue; var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpper(); switch (key) { case "REM": break; case "FILE": { currTrack = null; currFile = new CueFile(); Files.Add(currFile); currFile.Path = clp.ReadPath().Trim('"'); if (!clp.EOF) { string temp = clp.ReadToken().ToUpper(); switch (temp) { case "BINARY": currFile.FileType = CueFileType.Binary; break; case "WAVE": case "MP3": currFile.FileType = CueFileType.Wave; break; } currFile.StrFileType = temp; } break; } case "TRACK": { if (currFile == null) throw new CueBrokenException("invalid cue structure"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); string strtracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(strtracknum, out tracknum)) throw new CueBrokenException("malformed track number"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); if (tracknum < 0 || tracknum > 99) throw new CueBrokenException("`All track numbers must be between 1 and 99 inclusive.`"); string strtracktype = clp.ReadToken().ToUpper(); currTrack = new CueTrack(); switch (strtracktype) { case "MODE1/2352": currTrack.TrackType = ETrackType.Mode1_2352; break; case "MODE1/2048": currTrack.TrackType = ETrackType.Mode1_2048; break; case "MODE2/2352": currTrack.TrackType = ETrackType.Mode2_2352; break; case "AUDIO": currTrack.TrackType = ETrackType.Audio; break; default: throw new CueBrokenException("unhandled track type"); } currTrack.TrackNum = tracknum; currFile.Tracks.Add(currTrack); track_has_pregap = false; track_has_postgap = false; last_index_num = -1; break; } case "INDEX": { if (currTrack == null) throw new CueBrokenException("invalid cue structure"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); if (track_has_postgap) throw new CueBrokenException("`The POSTGAP command must appear after all INDEX commands for the current track.`"); string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum)) throw new CueBrokenException("malformed index number"); if (clp.EOF) throw new CueBrokenException("invalid cue structure (missing index timestamp)"); string str_timestamp = clp.ReadToken(); if (indexnum < 0 || indexnum > 99) throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`"); if (indexnum != 1 && indexnum != last_index_num + 1) throw new CueBrokenException("`The first index must be 0 or 1 with all other indexes being sequential to the first one.`"); last_index_num = indexnum; CueTrackIndex cti = new CueTrackIndex(indexnum) { Timestamp = new Timestamp(str_timestamp), IndexNum = indexnum }; currTrack.Indexes[indexnum] = cti; break; } case "PREGAP": if (track_has_pregap) throw new CueBrokenException("`Only one PREGAP command is allowed per track.`"); if (currTrack.Indexes.Count > 0) throw new CueBrokenException("`The PREGAP command must appear after a TRACK command, but before any INDEX commands.`"); currTrack.PreGap = new Timestamp(clp.ReadToken()); track_has_pregap = true; break; case "POSTGAP": if (track_has_postgap) throw new CueBrokenException("`Only one POSTGAP command is allowed per track.`"); track_has_postgap = true; currTrack.PostGap = new Timestamp(clp.ReadToken()); break; case "CATALOG": case "PERFORMER": case "SONGWRITER": case "TITLE": case "ISRC": //TODO - keep these for later? break; default: throw new CueBrokenException("unsupported cue command: " + key); } } //end cue parsing loop }
public void LoadFromString(string cueString) { TextReader tr = new StringReader(cueString); bool track_has_pregap = false; bool track_has_postgap = false; int last_index_num = -1; CueFile currFile = null; CueTrack currTrack = null; for (; ;) { string line = tr.ReadLine(); if (line == null) { break; } line = line.Trim(); if (line == "") { continue; } var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpper(); switch (key) { case "REM": break; case "FILE": { currTrack = null; currFile = new CueFile(); Files.Add(currFile); currFile.Path = clp.ReadPath().Trim('"'); if (!clp.EOF) { string temp = clp.ReadToken().ToUpper(); switch (temp) { case "BINARY": currFile.FileType = CueFileType.Binary; break; case "WAVE": case "MP3": currFile.FileType = CueFileType.Wave; break; } currFile.StrFileType = temp; } break; } case "TRACK": { if (currFile == null) { throw new CueBrokenException("invalid cue structure"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } string strtracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(strtracknum, out tracknum)) { throw new CueBrokenException("malformed track number"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } if (tracknum < 0 || tracknum > 99) { throw new CueBrokenException("`All track numbers must be between 1 and 99 inclusive.`"); } string strtracktype = clp.ReadToken().ToUpper(); currTrack = new CueTrack(); switch (strtracktype) { case "MODE1/2352": currTrack.TrackType = ETrackType.Mode1_2352; break; case "MODE1/2048": currTrack.TrackType = ETrackType.Mode1_2048; break; case "MODE2/2352": currTrack.TrackType = ETrackType.Mode2_2352; break; case "AUDIO": currTrack.TrackType = ETrackType.Audio; break; default: throw new CueBrokenException("unhandled track type"); } currTrack.TrackNum = tracknum; currFile.Tracks.Add(currTrack); track_has_pregap = false; track_has_postgap = false; last_index_num = -1; break; } case "INDEX": { if (currTrack == null) { throw new CueBrokenException("invalid cue structure"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } if (track_has_postgap) { throw new CueBrokenException("`The POSTGAP command must appear after all INDEX commands for the current track.`"); } string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum)) { throw new CueBrokenException("malformed index number"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure (missing index timestamp)"); } string str_timestamp = clp.ReadToken(); if (indexnum < 0 || indexnum > 99) { throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`"); } if (indexnum != 1 && indexnum != last_index_num + 1) { throw new CueBrokenException("`The first index must be 0 or 1 with all other indexes being sequential to the first one.`"); } last_index_num = indexnum; CueTrackIndex cti = new CueTrackIndex(indexnum) { Timestamp = new Timestamp(str_timestamp), IndexNum = indexnum }; currTrack.Indexes[indexnum] = cti; break; } case "PREGAP": if (track_has_pregap) { throw new CueBrokenException("`Only one PREGAP command is allowed per track.`"); } if (currTrack.Indexes.Count > 0) { throw new CueBrokenException("`The PREGAP command must appear after a TRACK command, but before any INDEX commands.`"); } currTrack.PreGap = new Timestamp(clp.ReadToken()); track_has_pregap = true; break; case "POSTGAP": if (track_has_postgap) { throw new CueBrokenException("`Only one POSTGAP command is allowed per track.`"); } track_has_postgap = true; currTrack.PostGap = new Timestamp(clp.ReadToken()); break; case "CATALOG": case "PERFORMER": case "SONGWRITER": case "TITLE": case "ISRC": //TODO - keep these for later? break; default: throw new CueBrokenException("unsupported cue command: " + key); } } //end cue parsing loop }
void LoadFromString(ParseCueJob job) { string cueString = job.IN_CueString; TextReader tr = new StringReader(cueString); for (; ; ) { job.CurrentLine++; string line = tr.ReadLine(); if (line == null) break; line = line.Trim(); if (line == "") continue; var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpperInvariant(); //remove nonsense at beginning if (!IN_Strict) { while (key.Length > 0) { char c = key[0]; if(c == ';') break; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) break; key = key.Substring(1); } } bool startsWithSemicolon = key.StartsWith(";"); if (startsWithSemicolon) { clp.EOF = true; OUT_CueFile.Commands.Add(new CUE_File.Command.COMMENT() { Value = line }); } else switch (key) { default: job.Warn("Unknown command: " + key); break; case "CATALOG": if (OUT_CueFile.GlobalDiscInfo.Catalog != null) job.Warn("Multiple CATALOG commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty CATALOG command"); else OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.Catalog = new CUE_File.Command.CATALOG() { Value = clp.ReadToken() }); break; case "CDTEXTFILE": if (OUT_CueFile.GlobalDiscInfo.CDTextFile != null) job.Warn("Multiple CDTEXTFILE commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty CDTEXTFILE command"); else OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.CDTextFile = new CUE_File.Command.CDTEXTFILE() { Path = clp.ReadPath() }); break; case "FILE": { var path = clp.ReadPath(); CueFileType ft; if (clp.EOF) { job.Error("FILE command is missing file type."); ft = CueFileType.Unspecified; } else { var strType = clp.ReadToken().ToUpperInvariant(); switch (strType) { default: job.Error("Unknown FILE type: " + strType); ft = CueFileType.Unspecified; break; case "BINARY": ft = CueFileType.BINARY; break; case "MOTOROLA": ft = CueFileType.MOTOROLA; break; case "BINARAIFF": ft = CueFileType.AIFF; break; case "WAVE": ft = CueFileType.WAVE; break; case "MP3": ft = CueFileType.MP3; break; } } OUT_CueFile.Commands.Add(new CUE_File.Command.FILE() { Path = path, Type = ft }); } break; case "FLAGS": { var cmd = new CUE_File.Command.FLAGS(); OUT_CueFile.Commands.Add(cmd); while (!clp.EOF) { var flag = clp.ReadToken().ToUpperInvariant(); switch (flag) { case "DATA": default: job.Warn("Unknown FLAG: " + flag); break; case "DCP": cmd.Flags |= CueTrackFlags.DCP; break; case "4CH": cmd.Flags |= CueTrackFlags._4CH; break; case "PRE": cmd.Flags |= CueTrackFlags.PRE; break; case "SCMS": cmd.Flags |= CueTrackFlags.SCMS; break; } } if (cmd.Flags == CueTrackFlags.None) job.Warn("Empty FLAG command"); } break; case "INDEX": { if (clp.EOF) { job.Error("Incomplete INDEX command"); break; } string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum) || indexnum < 0 || indexnum > 99) { job.Error("Invalid INDEX number: " + strindexnum); break; } string str_timestamp = clp.ReadToken(); var ts = new Timestamp(str_timestamp); if (!ts.Valid && !IN_Strict) { //try cleaning it up str_timestamp = Regex.Replace(str_timestamp, "[^0-9:]", ""); ts = new Timestamp(str_timestamp); } if (!ts.Valid) { if (IN_Strict) job.Error("Invalid INDEX timestamp: " + str_timestamp); break; } OUT_CueFile.Commands.Add(new CUE_File.Command.INDEX() { Number = indexnum, Timestamp = ts }); } break; case "ISRC": if (OUT_CueFile.GlobalDiscInfo.ISRC != null) job.Warn("Multiple ISRC commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty ISRC command"); else { var isrc = clp.ReadToken(); if (isrc.Length != 12) job.Warn("Invalid ISRC code ignored: " + isrc); else { OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.ISRC = new CUE_File.Command.ISRC() { Value = isrc }); } } break; case "PERFORMER": OUT_CueFile.Commands.Add(new CUE_File.Command.PERFORMER() { Value = clp.ReadPath() ?? "" }); break; case "POSTGAP": case "PREGAP": { var str_msf = clp.ReadToken(); var msf = new Timestamp(str_msf); if (!msf.Valid) job.Error("Ignoring {0} with invalid length MSF: " + str_msf, key); else { if (key == "POSTGAP") OUT_CueFile.Commands.Add(new CUE_File.Command.POSTGAP() { Length = msf }); else OUT_CueFile.Commands.Add(new CUE_File.Command.PREGAP() { Length = msf }); } } break; case "REM": OUT_CueFile.Commands.Add(new CUE_File.Command.REM() { Value = clp.ReadLine() }); break; case "SONGWRITER": OUT_CueFile.Commands.Add(new CUE_File.Command.SONGWRITER() { Value = clp.ReadPath() ?? "" }); break; case "TITLE": OUT_CueFile.Commands.Add(new CUE_File.Command.TITLE() { Value = clp.ReadPath() ?? "" }); break; case "TRACK": { if (clp.EOF) { job.Error("Incomplete TRACK command"); break; } string str_tracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(str_tracknum, out tracknum) || tracknum < 1 || tracknum > 99) { job.Error("Invalid TRACK number: " + str_tracknum); break; } //TODO - check sequentiality? maybe as a warning CueTrackType tt; var str_trackType = clp.ReadToken(); switch (str_trackType.ToUpperInvariant()) { default: job.Error("Unknown TRACK type: " + str_trackType); tt = CueTrackType.Unknown; break; case "AUDIO": tt = CueTrackType.Audio; break; case "CDG": tt = CueTrackType.CDG; break; case "MODE1/2048": tt = CueTrackType.Mode1_2048; break; case "MODE1/2352": tt = CueTrackType.Mode1_2352; break; case "MODE2/2336": tt = CueTrackType.Mode2_2336; break; case "MODE2/2352": tt = CueTrackType.Mode2_2352; break; case "CDI/2336": tt = CueTrackType.CDI_2336; break; case "CDI/2352": tt = CueTrackType.CDI_2352; break; } OUT_CueFile.Commands.Add(new CUE_File.Command.TRACK() { Number = tracknum, Type = tt }); } break; } if (!clp.EOF) { var remainder = clp.ReadLine(); if (remainder.TrimStart().StartsWith(";")) { //add a comment OUT_CueFile.Commands.Add(new CUE_File.Command.COMMENT() { Value = remainder }); } else job.Warn("Unknown text at end of line after processing command: " + key); } } //end cue parsing loop job.FinishLog(); } //LoadFromString
private void LoadFromString(ParseCueJob job) { string cueString = job.IN_CueString; TextReader tr = new StringReader(cueString); for (; ; ) { job.CurrentLine++; string line = tr.ReadLine(); if (line == null) break; line = line.Trim(); if (line == "") continue; var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpperInvariant(); //remove nonsense at beginning if (!IN_Strict) { while (key.Length > 0) { char c = key[0]; if(c == ';') break; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) break; key = key.Substring(1); } } bool startsWithSemicolon = key.StartsWith(";"); if (startsWithSemicolon) { clp.EOF = true; OUT_CueFile.Commands.Add(new CUE_File.Command.COMMENT(line)); } else switch (key) { default: job.Warn($"Unknown command: {key}"); break; case "CATALOG": if (OUT_CueFile.GlobalDiscInfo.Catalog != null) job.Warn("Multiple CATALOG commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty CATALOG command"); else OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.Catalog = new CUE_File.Command.CATALOG(clp.ReadToken())); break; case "CDTEXTFILE": if (OUT_CueFile.GlobalDiscInfo.CDTextFile != null) job.Warn("Multiple CDTEXTFILE commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty CDTEXTFILE command"); else OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.CDTextFile = new CUE_File.Command.CDTEXTFILE(clp.ReadPath())); break; case "FILE": { var path = clp.ReadPath(); CueFileType ft; if (clp.EOF) { job.Error("FILE command is missing file type."); ft = CueFileType.Unspecified; } else { var strType = clp.ReadToken().ToUpperInvariant(); switch (strType) { default: job.Error($"Unknown FILE type: {strType}"); ft = CueFileType.Unspecified; break; case "BINARY": ft = CueFileType.BINARY; break; case "MOTOROLA": ft = CueFileType.MOTOROLA; break; case "BINARAIFF": ft = CueFileType.AIFF; break; case "WAVE": ft = CueFileType.WAVE; break; case "MP3": ft = CueFileType.MP3; break; } } OUT_CueFile.Commands.Add(new CUE_File.Command.FILE(path, ft)); } break; case "FLAGS": { CueTrackFlags flags = default; while (!clp.EOF) { var flag = clp.ReadToken().ToUpperInvariant(); switch (flag) { case "DATA": default: job.Warn($"Unknown FLAG: {flag}"); break; case "DCP": flags |= CueTrackFlags.DCP; break; case "4CH": flags |= CueTrackFlags._4CH; break; case "PRE": flags |= CueTrackFlags.PRE; break; case "SCMS": flags |= CueTrackFlags.SCMS; break; } } if (flags == CueTrackFlags.None) job.Warn("Empty FLAG command"); OUT_CueFile.Commands.Add(new CUE_File.Command.FLAGS(flags)); } break; case "INDEX": { if (clp.EOF) { job.Error("Incomplete INDEX command"); break; } string strindexnum = clp.ReadToken(); if (!int.TryParse(strindexnum, out var indexnum) || indexnum < 0 || indexnum > 99) { job.Error($"Invalid INDEX number: {strindexnum}"); break; } string str_timestamp = clp.ReadToken(); var ts = new Timestamp(str_timestamp); if (!ts.Valid && !IN_Strict) { //try cleaning it up str_timestamp = Regex.Replace(str_timestamp, "[^0-9:]", ""); ts = new Timestamp(str_timestamp); } if (!ts.Valid) { if (IN_Strict) job.Error($"Invalid INDEX timestamp: {str_timestamp}"); break; } OUT_CueFile.Commands.Add(new CUE_File.Command.INDEX(indexnum, ts)); } break; case "ISRC": if (OUT_CueFile.GlobalDiscInfo.ISRC != null) job.Warn("Multiple ISRC commands detected. Subsequent ones are ignored."); else if (clp.EOF) job.Warn("Ignoring empty ISRC command"); else { var isrc = clp.ReadToken(); if (isrc.Length != 12) job.Warn($"Invalid ISRC code ignored: {isrc}"); else { OUT_CueFile.Commands.Add(OUT_CueFile.GlobalDiscInfo.ISRC = new CUE_File.Command.ISRC(isrc)); } } break; case "PERFORMER": OUT_CueFile.Commands.Add(new CUE_File.Command.PERFORMER(clp.ReadPath() ?? "")); break; case "POSTGAP": case "PREGAP": { var str_msf = clp.ReadToken(); var msf = new Timestamp(str_msf); if (!msf.Valid) job.Error($"Ignoring {{0}} with invalid length MSF: {str_msf}", key); else { if (key == "POSTGAP") OUT_CueFile.Commands.Add(new CUE_File.Command.POSTGAP(msf)); else OUT_CueFile.Commands.Add(new CUE_File.Command.PREGAP(msf)); } } break; case "REM": OUT_CueFile.Commands.Add(new CUE_File.Command.REM(clp.ReadLine())); break; case "SONGWRITER": OUT_CueFile.Commands.Add(new CUE_File.Command.SONGWRITER(clp.ReadPath() ?? "")); break; case "TITLE": OUT_CueFile.Commands.Add(new CUE_File.Command.TITLE(clp.ReadPath() ?? "")); break; case "TRACK": { if (clp.EOF) { job.Error("Incomplete TRACK command"); break; } string str_tracknum = clp.ReadToken(); if (!int.TryParse(str_tracknum, out int tracknum) || tracknum < 1 || tracknum > 99) { job.Error($"Invalid TRACK number: {str_tracknum}"); break; } // TODO - check sequentiality? maybe as a warning CueTrackType tt; var str_trackType = clp.ReadToken(); switch (str_trackType.ToUpperInvariant()) { default: job.Error($"Unknown TRACK type: {str_trackType}"); tt = CueTrackType.Unknown; break; case "AUDIO": tt = CueTrackType.Audio; break; case "CDG": tt = CueTrackType.CDG; break; case "MODE1/2048": tt = CueTrackType.Mode1_2048; break; case "MODE1/2352": tt = CueTrackType.Mode1_2352; break; case "MODE2/2336": tt = CueTrackType.Mode2_2336; break; case "MODE2/2352": tt = CueTrackType.Mode2_2352; break; case "CDI/2336": tt = CueTrackType.CDI_2336; break; case "CDI/2352": tt = CueTrackType.CDI_2352; break; } OUT_CueFile.Commands.Add(new CUE_File.Command.TRACK(tracknum, tt)); } break; } if (!clp.EOF) { var remainder = clp.ReadLine(); if (remainder.TrimStart().StartsWith(";")) { //add a comment OUT_CueFile.Commands.Add(new CUE_File.Command.COMMENT(remainder)); } else job.Warn($"Unknown text at end of line after processing command: {key}"); } } //end cue parsing loop job.FinishLog(); } //LoadFromString