Example #1
0
            private void Parse(LogBuffer parser)
            {
                List <int> arItems = null;
                int        arV2    = 0;
                string     lx      = parser.ReadLineLTrim();

                if (lx.StartsWith("X Lossless Decoder"))
                {
                    Data.Application = lx;
                }

                lx = parser.ReadLineLTrim();
                if (lx.StartsWith("XLD extraction logfile from "))
                {
                    Data.RipDate = lx.Substring(28);
                }

                lx = parser.ReadLineNonempty();
                int slashPos = lx.IndexOf(" / ");

                if (slashPos < 0)
                {
                    IssueModel.Add("Missing '<artist> / <album>', " + parser.GetPlace() + ".");
                    return;
                }
                Data.RipArtist = lx.Substring(0, slashPos);
                Data.RipAlbum  = lx.Substring(slashPos + 3);

                for (;;)
                {
                    lx = parser.ReadLineNonempty();
                    if (parser.EOF)
                    {
                        return;
                    }

                    string px = ParseArg(lx, out string arg);
                    if (arg == null)
                    {
                        break;
                    }
                    if (px == "Used drive")
                    {
                        Data.Drive = arg;
                    }
                    else if (px == "Media type")
                    {
                        Data.Media = arg;
                    }
                    else if (px == "Ripper mode")
                    {
                        Data.ReadMode = arg;
                    }
                    else if (px == "Disable audio cache")
                    {
                        Data.DefeatCache = arg;
                    }
                    else if (px == "Make use of C2 pointers")
                    {
                        Data.UseC2 = arg;
                    }
                    else if (px == "Read offset correction")
                    {
                        Data.ReadOffset = arg;
                    }
                    else if (px == "Gap status")
                    {
                        Data.GapHandling = arg;
                    }
                }

                for (;;)
                {
                    if (parser.EOF || lx == null)
                    {
                        return;
                    }
                    if (lx.StartsWith("TOC"))
                    {
                        ParseToC();
                    }
                    if (lx.StartsWith("AccurateRip Summary"))
                    {
                        ParseAccurateRip();
                    }
                    else if (lx.StartsWith("Track "))
                    {
                        ParseTracks();
                    }
                    else if (lx == "No errors occurred")
                    {
                        Data.HasNoErrors = true;
                        lx = parser.ReadLine();
                        break;
                    }
                    else
                    {
                        lx = parser.ReadLine();
                    }
                }

                for (;;)
                {
                    if (parser.EOF)
                    {
                        break;
                    }
                    if (lx == "-----BEGIN XLD SIGNATURE-----")
                    {
                        lx = parser.ReadLineLTrim();
                        if (!parser.EOF)
                        {
                            Data.storedHash = lx;
                            lx = parser.ReadLineLTrim();
                        }
                    }
                    lx = parser.ReadLineLTrim();
                }

                if (arItems != null)
                {
                    if (arItems.Count != TracksModel.Data.Items.Count || arItems.Count == 0)
                    {
                        Data.ArIssue = IssueModel.Add("AccurateRip table mismatch.", Severity.Error, IssueTags.Failure);
                    }
                    else
                    {
                        Data.AccurateRipConfidence = arItems.Min();
                        Data.AccurateRip           = arV2 == arItems.Count ? 2 : 1;
                    }
                }
                return;

                void ParseToC()
                {
                    // Q: does the ToC include data tracks?
                    lx = parser.ReadLine();
                }

                void ParseAccurateRip()
                {
                    lx = parser.ReadLine();
                    if (lx != null && lx.Contains("Disc not found"))
                    {
                        Data.AccurateRipConfidence = 0; return;
                    }

                    arItems = new List <int>();
                    for (;;)
                    {
                        if (String.IsNullOrWhiteSpace(lx) || lx.StartsWith("->"))
                        {
                            return;
                        }
                        string px = ParseArg(lx, out string arg);
                        if (px.Length != 8 || !Char.IsDigit(px[6]) || !Char.IsDigit(px[7]))
                        {
                            return;
                        }
                        int.TryParse(px.Substring(6), out int tn);
                        if (tn != arItems.Count + 1)
                        {
                            return;
                        }
                        if (arg == "Not Found")
                        {
                            arItems.Add(0);
                        }
                        else if (arg.Contains("OK "))
                        {
                            int p1 = arg.IndexOf("confidence ");
                            if (p1 < 0)
                            {
                                return;         // malformed AR
                            }
                            int p2 = p1 = p1 + 11;
                            for (; p2 < arg.Length; ++p2)
                            {
                                if (!Char.IsDigit(arg[p2]))
                                {
                                    break;
                                }
                            }
                            if (p2 == p1)
                            {
                                return; // malformed AR
                            }
                            int.TryParse(arg.Substring(p1, p2 - p1), out int val);
                            arItems.Add(val);
                            if (arg.Contains("v2"))
                            {
                                ++arV2;
                            }
                        }
                        else
                        {
                            arItems.Add(-1);
                        }
                        lx = parser.ReadLine();
                    }
                }

                void ParseTracks()
                {
                    for (;;)
                    {
                        int    number = 0;
                        string fileName = null;
                        uint?  testCRC = null, copyCRC = null;
                        string arg  = lx.Substring(6);
                        bool   isOk = arg.Length >= 2 && Char.IsDigit(arg[0]);
                        if (isOk)
                        {
                            isOk = int.TryParse(arg, out number);
                        }
                        if (!isOk)
                        {
                            IssueModel.Add($"Malformed track number '{arg}'."); return;
                        }

                        for (;;)
                        {
                            lx = parser.ReadLine();
                            if (parser.EOF || (lx.Length > 0 && lx[0] != ' '))
                            {
                                TracksModel.Add(number, fileName, testCRC, copyCRC);
                                if (lx.StartsWith("Track "))
                                {
                                    break;
                                }
                                else
                                {
                                    return;
                                }
                            }
                            string px = ParseArg(lx, out string ax);
                            if (px == "Filename")
                            {
                                fileName = px;
                            }
                            else if (px == "CRC32 hash (test run)")
                            {
                                if (uint.TryParse(ax, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint word))
                                {
                                    testCRC = word;
                                }
                                else
                                {
                                    IssueModel.Add("Malformed test CRC"); return;
                                }
                            }
                            else if (px == "CRC32 hash")
                            {
                                if (uint.TryParse(ax, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint word))
                                {
                                    copyCRC = word;
                                }
                                else
                                {
                                    IssueModel.Add("Malformed copy CRC"); return;
                                }
                            }
                        }
                    }
                }
            }
Example #2
0
            string Parse(LogBuffer parser)
            {
                string lx = null;

                ParseHeader();

                if (!Data.IsRangeRip)
                {
                    lx = ParseTracks();
                    if (Data.Issues.HasFatal)
                    {
                        return(null);
                    }
                }

                while (!parser.EOF && !lx.Contains("errors") && !lx.StartsWith("==== "))
                {
                    lx = parser.ReadLineLTrim();
                }

                if (lx == "No errors occured" || lx == "No errors occurred")
                {
                    lx = parser.ReadLineLTrim();
                }
                else if (lx == "There were errors")
                {
                    if (Data.Issues.MaxSeverity < Severity.Error)
                    {
                        IssueModel.Add("There were errors.");
                    }
                    lx = parser.ReadLineLTrim();
                }
                else
                {
                    IssueModel.Add("Missing 'errors' line.");
                }

                if (Data.IsRangeRip)
                {
                    if (lx == "AccurateRip summary")
                    {
                        for (;;)
                        {
                            lx = parser.ReadLineLTrim();
                            if (parser.EOF || !lx.StartsWith("Track "))
                            {
                                break;
                            }

                            if (lx.Contains("ccurately ripped (confidence "))
                            {
                                int arVersion    = lx.Contains("AR v2") ? 2 : 1;
                                int advanced     = ConvertTo.FromStringToInt32(lx, 40, out int val);
                                int arConfidence = advanced > 0 && val > 0 ? val : -1;
                                lx = parser.ReadLineLTrim();

                                if (Data.AccurateRipConfidence == null || Data.AccurateRipConfidence.Value > arConfidence)
                                {
                                    Data.AccurateRipConfidence = arConfidence;
                                }
                                if (Data.AccurateRip == null || Data.AccurateRip.Value > arVersion)
                                {
                                    Data.AccurateRip = arVersion;
                                }
                            }
                        }
                    }
                }

                if (lx == "All tracks accurately ripped")
                {
                    lx = parser.ReadLineLTrim();
                }

                if (lx.StartsWith("End of status report"))
                {
                    lx = parser.ReadLineLTrim();
                }

                if (lx.StartsWith("---- CUETools"))
                {
                    lx = ParseCueTools();
                }

                while (!parser.EOF && !lx.StartsWith("==== "))
                {
                    lx = parser.ReadLine();
                }

                if (lx.StartsWith("==== Log checksum ") && lx.Length >= 82)
                {
                    Data.storedHash = ConvertTo.FromHexStringToBytes(lx, 18, 32);

                    lx = parser.ReadLine();
                    if (!parser.EOF || !String.IsNullOrEmpty(lx))
                    {
                        IssueModel.Add("Unexpected content at end of file.", Severity.Warning, IssueTags.StrictErr);
                    }
                }

                return(lx);

                string ParseHeader()
                {
                    lx = parser.ReadLine();
                    if (lx.StartsWith("Exact Audio Copy V"))
                    {
                        var spacePos = lx.IndexOf(' ', 18);
                        if (spacePos > 0)
                        {
                            Data.EacVersionString = lx.Substring(18, spacePos - 18);
                        }

                        lx = parser.ReadLine();
                        lx = parser.ReadLine();
                    }

                    if (!lx.StartsWith("EAC extraction logfile from "))
                    {
                        IssueModel.Add("Unexpected contents, " + parser.GetPlace() + ": Expecting 'EAC extraction logfile'.");
                        return(lx);
                    }

                    lx = lx.Substring(28);
                    if (lx.Length < 12)
                    {
                        IssueModel.Add("Missing rip date, " + parser.GetPlace() + ".");
                        return(lx);
                    }
                    Data.RipDate = lx.EndsWith(" for CD") ? lx.Substring(0, lx.Length - 7) : lx;

                    lx = parser.ReadLine();
                    if (String.IsNullOrWhiteSpace(lx))
                    {
                        lx = parser.ReadLine();
                    }

                    int slashPos = lx.IndexOf('/');

                    if (slashPos < 0)
                    {
                        IssueModel.Add("Missing '<artist> / <album>', " + parser.GetPlace() + ".");
                        return(lx);
                    }
                    Data.RipArtist = lx.Substring(0, slashPos).Trim();
                    Data.RipAlbum  = lx.Substring(slashPos + 1).Trim();

                    for (;;)
                    {
TOP:
                        if (parser.EOF)
                        {
                            lx = String.Empty;
                            return(lx);
                        }

                        lx = parser.ReadLine();
                        if (lx.StartsWith("Track ") || lx.StartsWith("===="))
                        {
                            return(lx);
                        }

                        if (lx == "Range status and errors")
                        {
                            Data.IsRangeRip = true;
                            lx = parser.ReadLine();
                            return(lx);
                        }

                        if (lx == "TOC of the extracted CD")
                        {
                            for (Data.TocTrackCount = 0;;)
                            {
                                lx = parser.ReadLine();
                                if (parser.EOF || lx.StartsWith("==== ") || (lx.StartsWith("Track") && !lx.Contains("|")))
                                {
                                    return(lx);
                                }

                                if (lx == "Range status and errors")
                                {
                                    Data.IsRangeRip = true;
                                    lx = parser.ReadLine();
                                    return(lx);
                                }

                                if (lx.Length >= 60)
                                {
                                    if (int.TryParse(lx.Substring(0, 9), out int tn))
                                    {
                                        ++Data.TocTrackCount;
                                    }
                                }
                            }
                        }

                        int ik0 = 0, ik1 = 0, ii = 0, iv0 = 0, iv1;
                        for (;; ++ik0)
                        {
                            if (ik0 >= lx.Length)
                            {
                                goto TOP;
                            }
                            else if (lx[ik0] != ' ')
                            {
                                for (ii = ik0, ik1 = ii;;)
                                {
                                    ++ii;
                                    if (ii >= lx.Length)
                                    {
                                        string kk = lx.Substring(ik0, ik1 - ik0 + 1);
                                        if (kk == "Installed external ASPI interface" || kk == "Native Win32 interface for Win NT & 2000")
                                        {
                                            Data.Interface = kk;
                                        }
                                        goto TOP;
                                    }
                                    if (lx[ii] == ':')
                                    {
                                        break;
                                    }
                                    if (lx[ii] != ' ')
                                    {
                                        ik1 = ii;
                                    }
                                }

                                for (iv0 = ii + 1;; ++iv0)
                                {
                                    if (iv0 >= lx.Length)
                                    {
                                        goto TOP;
                                    }
                                    else if (lx[iv0] != ' ')
                                    {
                                        for (ii = iv0 + 1, iv1 = iv0;; ++ii)
                                        {
                                            if (ii == lx.Length)
                                            {
                                                break;
                                            }
                                            else if (lx[ii] != ' ')
                                            {
                                                iv1 = ii;
                                            }
                                        }

                                        string optKey = lx.Substring(ik0, ik1 - ik0 + 1),
                                               optVal = lx.Substring(iv0, iv1 - iv0 + 1);
                                        if (optKey == "Used drive")
                                        {
                                            Data.Drive = optVal;
                                        }
                                        else if (optKey == "Utilize accurate stream")
                                        {
                                            Data.AccurateStream = optVal;
                                        }
                                        else if (optKey == "Defeat audio cache")
                                        {
                                            Data.DefeatCache = optVal;
                                        }
                                        else if (optKey == "Make use of C2 pointers")
                                        {
                                            Data.UseC2 = optVal;
                                        }
                                        else if (optKey == "Read mode")
                                        {
                                            Data.ReadMode = optVal;
                                        }
                                        else if (optKey == "Read offset correction" || optKey == "Combined read/write offset correction")
                                        {
                                            Data.ReadOffset = optVal;
                                        }
                                        else if (optKey == "Overread into Lead-In and Lead-Out")
                                        {
                                            Data.Overread = optVal;
                                        }
                                        else if (optKey == "Fill up missing offset samples with silence")
                                        {
                                            Data.FillWithSilence = optVal;
                                        }
                                        else if (optKey == "Delete leading and trailing silent blocks")
                                        {
                                            Data.TrimSilence = optVal;
                                        }
                                        else if (optKey == "Null samples used in CRC calculations")
                                        {
                                            Data.CalcWithNulls = optVal;
                                        }
                                        else if (optKey == "Normalize to")
                                        {
                                            Data.NormalizeTo = optVal;
                                        }
                                        else if (optKey == "Used interface")
                                        {
                                            Data.Interface = optVal;
                                        }
                                        else if (optKey == "Gap handling")
                                        {
                                            Data.GapHandling = optVal;
                                        }
                                        else if (optKey == "Sample format")
                                        {
                                            Data.SampleFormat = optVal;
                                        }
                                        else if (optKey == "Quality")
                                        {
                                            Data.Quality = optVal;
                                        }
                                        else if (optKey == "Add ID3 tag")
                                        {
                                            Data.Id3Tag = optVal;
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                string ParseTracks()
                {
                    for (;;)
                    {
                        if (parser.EOF ||
                            lx == "No errors occured" ||
                            lx == "No errors occurred" ||
                            lx == "There were errors" ||
                            lx.Contains("accurate"))
                        {
                            break;
                        }

                        if (!lx.StartsWith("Track") || lx.StartsWith("Track quality"))
                        {
                            lx = parser.ReadLineLTrim();
                            continue;
                        }

                        bool success = Int32.TryParse(lx.Substring(6), out int num);
                        if (!success)
                        {
                            Data.TkIssue = IssueModel.Add("Invalid track " + parser.GetPlace(), Severity.Fatal, IssueTags.Failure);
                            break;
                        }

                        lx = parser.ReadLineLTrim();
                        if (!lx.StartsWith("Filename "))
                        {
                            Data.TkIssue = IssueModel.Add($"Track {num}: Missing 'Filename'.", Severity.Error, IssueTags.Failure);
                        }
                        else
                        {
                            lx = ParseTrack(num);
                        }
                    }
                    return(lx);
                }

                string ParseTrack(int num)
                {
                    string name = "", peak = "", speed = "", pregap = "", qual = "";
                    int?   arVersion = null;
                    int?   arConfidence = null;
                    uint?  testCrc = null, copyCrc = null;
                    bool   trackErr = false;
                    uint   word;

                    name = lx.Substring(9);
                    if (name.Length < 10 || (lx.Length < 252 && !lx.EndsWith(".wav")))
                    {
                        IssueModel.Add("Unexpected extension " + parser.GetPlace());
                    }

                    lx = parser.ReadLineLTrim();
                    if (lx.StartsWith("Pre-gap length  "))
                    {
                        pregap = lx.Substring(16); lx = parser.ReadLineLTrim();
                    }

                    for (;;)
                    {
                        if (parser.EOF)
                        {
                            return(lx);
                        }

                        if (lx.StartsWith("Track quality") || lx.StartsWith("Peak level "))
                        {
                            break;
                        }

                        if (lx.StartsWith("Track "))
                        {
                            return(lx);  // Unexpected start of next track.
                        }
                        if (!trackErr)
                        {
                            trackErr = true;
                            IssueModel.Add($"Track {num}: '{lx}'.");
                        }
                        lx = parser.ReadLineLTrim();
                    }

                    if (lx.StartsWith("Peak level "))
                    {
                        peak = lx.Substring(11); lx = parser.ReadLineLTrim();
                    }

                    if (lx.StartsWith("Extraction speed "))
                    {
                        speed = lx.Substring(17); lx = parser.ReadLineLTrim();
                    }

                    if (lx.StartsWith("Track quality "))
                    {
                        qual = lx.Substring(14); lx = parser.ReadLineLTrim();
                    }

                    if (lx.StartsWith("Test CRC "))
                    {
                        if (uint.TryParse(lx.Substring(9), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out word))
                        {
                            testCrc = word;
                        }
                        else
                        {
                            Data.TpIssue = IssueModel.Add($"Track {num}: Invalid test CRC-32.", Severity.Error, IssueTags.Failure);
                        }
                        lx = parser.ReadLineLTrim();
                    }

                    if (!lx.StartsWith("Copy CRC "))
                    {
                        IssueModel.Add($"Track {num}: Missing copy CRC-32.", Severity.Warning);
                    }
                    else
                    {
                        if (uint.TryParse(lx.Substring(9), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out word))
                        {
                            copyCrc = word;
                        }
                        else
                        {
                            Data.TkIssue = IssueModel.Add($"Track {num}: Invalid copy CRC-32.", Severity.Error, IssueTags.Failure);
                        }
                        lx = parser.ReadLineLTrim();
                    }

                    if (lx.StartsWith("Accurately ripped (confidence "))
                    {
                        arVersion = lx.Contains("AR v2") ? 2 : 1;
                        int advanced = ConvertTo.FromStringToInt32(lx, 30, out int val);
                        arConfidence = advanced > 0 && val > 0 ? val : -1;
                        lx           = parser.ReadLineLTrim();
                    }
                    else if (lx.StartsWith("Cannot be verified"))
                    {
                        arVersion    = lx.Contains("AR v2") ? 2 : 1;
                        arConfidence = -1;
                        lx           = parser.ReadLineLTrim();
                    }
                    else if (lx.StartsWith("Track not present"))
                    {
                        if (peak != "0.0 %")
                        {
                            arConfidence = 0;
                        }
                        lx = parser.ReadLineLTrim();
                    }

                    if (arConfidence != null)
                    {
                        if (Data.AccurateRipConfidence == null || Data.AccurateRipConfidence > arConfidence)
                        {
                            Data.AccurateRipConfidence = arConfidence;
                        }
                        if (arVersion != null)
                        {
                            if (Data.AccurateRip == null || Data.AccurateRip.Value > arVersion.Value)
                            {
                                Data.AccurateRip = arVersion;
                            }
                        }
                    }

                    bool hasOK = false;

                    if (lx == "Copy OK")
                    {
                        hasOK = true;
                        lx    = parser.ReadLineLTrim();
                    }

                    TracksModel.Add(num, name, pregap, peak, speed, qual, testCrc, copyCrc, hasOK, arVersion, arConfidence);
                    return(lx);
                }

                string ParseCueTools()
                {
                    do
                    {
                        lx = parser.ReadLineLTrim();
                        if (parser.EOF || lx.StartsWith("==== "))
                        {
                            return(lx);
                        }
                    } while (!lx.StartsWith("[CTDB"));

                    if (lx.Contains("not present"))
                    {
                        Data.CueToolsConfidence = 0;
                        lx = parser.ReadLineLTrim();
                        if (!parser.EOF && lx.StartsWith("Submit"))
                        {
                            lx = parser.ReadLineLTrim();
                        }
                        return(lx);
                    }

                    if (!lx.Contains(" found"))
                    {
                        return(lx);
                    }

                    do
                    {
                        lx = parser.ReadLineLTrim();
                        if (parser.EOF || lx.StartsWith("==== "))
                        {
                            return(lx);
                        }

                        if (lx.StartsWith("["))
                        {
                            int ctConfidence = -1;
                            if (lx.Contains("Accurately ripped"))
                            {
                                int advanced = ConvertTo.FromStringToInt32(lx, 12, out ctConfidence);
                            }
                            Data.CueToolsConfidence = ctConfidence;
                            lx = parser.ReadLineLTrim();
                            return(lx);
                        }
                    } while (!lx.StartsWith("Track"));

                    for (int tx = 0; ; ++tx)
                    {
                        lx = parser.ReadLine();
                        if (parser.EOF || lx.StartsWith("==== ") || lx.StartsWith("All tracks"))
                        {
                            return(lx);
                        }

                        int ctConfidence;
                        if (lx.Contains("Accurately ripped"))
                        {
                            int advanced = ConvertTo.FromStringToInt32(lx, 9, out ctConfidence);
                            if (advanced == 0)
                            {
                                Data.CueToolsConfidence = -1; return(lx);
                            }
                        }
                        else if (lx.Contains("Differs"))
                        {
                            ctConfidence = -1;
                        }
                        else
                        {
                            break;
                        }

                        if (tx < TracksModel.Data.Items.Count)
                        {
                            TracksModel.SetCtConfidence(tx, ctConfidence);
                        }
                        if (Data.CueToolsConfidence == null || Data.CueToolsConfidence > ctConfidence)
                        {
                            Data.CueToolsConfidence = ctConfidence;
                        }
                    }
                    return(parser.ReadLineLTrim());
                }
            }