public static Mp3XingBlock.Model Create(byte[] buf, Mp3Header header) { System.Diagnostics.Debug.Assert(header.IsMpegLayer3); int xix = header.XingOffset; string xingString = ConvertTo.FromAsciiToString(buf, xix, 4); if (xingString == "Info" || xingString == "Xing") { string lameString = ConvertTo.FromAsciiToString(buf, xix + 0x78, 9); if (lameString.StartsWith("LAME")) { return(new Mp3LameBlock.Model(buf, xix, header, xingString, lameString)); } else { return(new Mp3XingBlock.Model(buf, xix, header, xingString)); } } return(null); }
public override void GetReportDetail(IList <string> report) { if (report.Count > 0) { report.Add(String.Empty); } report.Add("Meta header:"); report.Add($" Minimum block size = {MinBlockSize}"); report.Add($" Maximum block size = {MaxBlockSize}"); report.Add($" Minimum frame size = {MinFrameSize}"); report.Add($" Maximum frame size = {MaxFrameSize}"); report.Add($" Sample rate = {MetaSampleRate} Hz"); report.Add($" Number of channels = {ChannelCount}"); report.Add($" Bits per sample = {BitsPerSample}"); report.Add(" Total samples = " + (TotalSamples != 0? TotalSamples.ToString() : " (unknown)")); report.Add(String.Empty); report.Add("Raw audio header: " + ConvertTo.ToBitString(aHdr, 1)); report.Add(String.Empty); report.Add("Cooked audio header:"); report.Add($" Blocking strategy = {BlockingStrategyText}"); report.Add($" Block size = {BlockSize} samples"); report.Add($" Sample rate = {SampleRateText}"); report.Add($" Channel assignment = {ChannelAssignmentText}"); report.Add($" Sample size = {SampleSizeText}"); report.Add($" Sample/frame number = {SampleOrFrameNumber}"); report.Add(String.Empty); report.Add("Checks:"); report.Add($" Stored audio header CRC-8 = {StoredAudioHeaderCRC8ToHex}"); if (ActualAudioHeaderCRC8 != null) { report.Add($" Actual audio header CRC-8 = {ActualAudioHeaderCRC8ToHex}"); } report.Add($" Stored audio block CRC-16 = {StoredAudioBlockCRC16ToHex}"); if (ActualAudioBlockCRC16 != null) { report.Add($" Actual audio block CRC-16 = {ActualAudioBlockCRC16ToHex}"); } report.Add($" Stored PCM MD5 = {StoredAudioDataMD5ToHex}"); if (actualAudioDataMD5 != null) { report.Add($" Actual PCM MD5 = {ActualAudioDataMD5ToHex}"); } if (ActualPcmCRC32 != null) { report.Add($" Actual PCM CRC-32 = {ActualPcmCRC32ToHex}"); } report.Add(String.Empty); report.Add($"Layout = {Layout}"); if (Blocks.Tags != null) { report.Add(String.Empty); report.Add("Tags:"); report.Add($" Vendor: {Blocks.Tags.Vendor}"); foreach (var item in Blocks.Tags.Lines) { report.Add($" {item}"); } } }
public Model(Stream stream, byte[] hdr, string path) { base._data = Data = new FlacFormat(this, stream, path); Data.MetadataBlockStreamInfoSize = ConvertTo.FromBig24ToInt32(hdr, 5); if (Data.MetadataBlockStreamInfoSize < 34) { IssueModel.Add($"Bad metablock size of {Data.MetadataBlockStreamInfoSize}", Severity.Fatal); return; } var bb = new byte[Data.MetadataBlockStreamInfoSize]; Data.ValidSize = 8; Data.fbs.Position = Data.ValidSize; var got = Data.fbs.Read(bb, 0, Data.MetadataBlockStreamInfoSize); if (got != Data.MetadataBlockStreamInfoSize) { IssueModel.Add("File truncated", Severity.Fatal); return; } Data.MinBlockSize = ConvertTo.FromBig16ToInt32(bb, 0); Data.MinBlockSize = ConvertTo.FromBig16ToInt32(bb, 2); Data.MinFrameSize = ConvertTo.FromBig24ToInt32(bb, 4); Data.MaxFrameSize = ConvertTo.FromBig24ToInt32(bb, 7); Data.MetaSampleRate = bb[10] << 12 | bb[11] << 4 | bb[12] >> 4; Data.ChannelCount = ((bb[12] & 0x0E) >> 1) + 1; Data.BitsPerSample = (((bb[12] & 1) << 4) | (bb[13] >> 4)) + 1; Data.TotalSamples = ((bb[13] & 0x0F) << 32) | bb[14] << 24 | bb[15] << 16 | bb[16] << 8 | bb[17]; Data.storedAudioDataMD5 = new byte[16]; Array.Copy(bb, 18, Data.storedAudioDataMD5, 0, 16); Data.ValidSize += Data.MetadataBlockStreamInfoSize; for (;;) { bb = new byte[12]; try { Data.fbs.Position = Data.ValidSize; } catch (EndOfStreamException) { IssueModel.Add("File truncated near meta data", Severity.Fatal); return; } Data.fbs.Position = Data.ValidSize; got = Data.fbs.Read(bb, 0, 4); if (got != 4) { IssueModel.Add("File truncated near meta data", Severity.Fatal); return; } if (bb[0] == 0xFF) { break; } int blockSize = ConvertTo.FromBig24ToInt32(bb, 1); Data.ValidSize += 4; switch ((FlacBlockType)(bb[0] & 0x7F)) { case FlacBlockType.Padding: Data.Blocks.AddPad((int)Data.ValidSize, blockSize); break; case FlacBlockType.Application: got = Data.fbs.Read(bb, 0, 4); if (got != 4) { IssueModel.Add("File truncated near tags", Severity.Fatal); return; } int appId = ConvertTo.FromBig32ToInt32(bb, 0); Data.Blocks.AddApp((int)Data.ValidSize, blockSize, appId); break; case FlacBlockType.SeekTable: var st = new byte[blockSize]; got = Data.fbs.Read(st, 0, blockSize); if (got != blockSize) { IssueModel.Add("File truncated near seek table", Severity.Fatal); return; } Data.Blocks.AddSeekTable((int)Data.ValidSize, blockSize, st); break; case FlacBlockType.Tags: bb = new byte[blockSize]; Data.fbs.Position = Data.ValidSize; got = Data.fbs.Read(bb, 0, blockSize); if (got != blockSize) { IssueModel.Add("File truncated near tags", Severity.Fatal); return; } if (Data.Blocks.Tags != null) { IssueModel.Add("Contains multiple tag blocks", Severity.Error); } else { Data.Blocks.AddTags((int)Data.ValidSize, blockSize, bb); } break; case FlacBlockType.CueSheet: var sb = new byte[284]; got = Data.fbs.Read(sb, 0, 284); if (got != 284) { IssueModel.Add("File truncated near cuesheet", Severity.Fatal); return; } var isCD = (sb[24] & 0x80) != 0; int trackCount = sb[283]; Data.Blocks.AddCuesheet((int)Data.ValidSize, blockSize, isCD, trackCount); break; case FlacBlockType.Picture: var pb = new byte[blockSize]; got = Data.fbs.Read(pb, 0, blockSize); if (got != blockSize) { IssueModel.Add("File truncated near picture", Severity.Fatal); return; } var picType = (PicType)ConvertTo.FromBig32ToInt32(pb, 0); var mimeLen = ConvertTo.FromBig32ToInt32(pb, 4); var mime = Encoding.UTF8.GetString(pb, 8, mimeLen); var descLen = ConvertTo.FromBig32ToInt32(pb, mimeLen + 8); var desc = Encoding.UTF8.GetString(pb, mimeLen + 12, descLen); var width = ConvertTo.FromBig32ToInt32(pb, mimeLen + descLen + 12); var height = ConvertTo.FromBig32ToInt32(pb, mimeLen + descLen + 16); Data.Blocks.AddPic((int)Data.ValidSize, blockSize, picType, width, height); break; case FlacBlockType.Invalid: IssueModel.Add("Encountered invalid block type", Severity.Fatal); return; default: IssueModel.Add($"Encountered reserved block type '{bb[0]}'", Severity.Warning); break; } Data.ValidSize += blockSize; } try { Data.fbs.Position = Data.ValidSize; } catch (EndOfStreamException) { IssueModel.Add("File truncated near frame header", Severity.Fatal); return; } got = Data.fbs.Read(bb, 0, 4); if (got != 4) { IssueModel.Add("File truncated", Severity.Fatal); return; } // Detect frame header sync code if (bb[0] != 0xFF || (bb[1] & 0xFC) != 0xF8) { IssueModel.Add("Audio data not found", Severity.Fatal); return; } Data.mediaPosition = Data.ValidSize; Data.SampleOrFrameNumber = Data.fbs.ReadWobbly(out byte[] wtfBuf); if (Data.SampleOrFrameNumber < 0) { IssueModel.Add("File truncated or badly formed sample/frame number.", Severity.Fatal); return; } Array.Copy(wtfBuf, 0, bb, 4, wtfBuf.Length); int bPos = 4 + wtfBuf.Length; Data.RawBlockingStrategy = bb[1] & 1; Data.RawBlockSize = bb[2] >> 4; if (Data.RawBlockSize == 0) { Data.BlockSize = 0; } else if (Data.RawBlockSize == 1) { Data.BlockSize = 192; } else if (Data.RawBlockSize >= 2 && Data.RawBlockSize <= 5) { Data.BlockSize = 576 * (1 << (Data.RawBlockSize - 2)); } else if (Data.RawBlockSize == 6) { got = Data.fbs.Read(bb, bPos, 1); Data.BlockSize = bb[bPos] + 1; bPos += 1; } else if (Data.RawBlockSize == 7) { got = Data.fbs.Read(bb, bPos, 2); Data.BlockSize = (bb[bPos] << 8) + bb[bPos + 1] + 1; bPos += 2; } else { Data.BlockSize = 256 * (1 << (Data.RawBlockSize - 8)); } Data.RawSampleRate = bb[2] & 0xF; if (Data.RawSampleRate == 0xC) { got = Data.fbs.Read(bb, bPos, 1); Data.SampleRateText = bb[bPos] + "kHz"; bPos += 1; } else if (Data.RawSampleRate == 0xD || Data.RawSampleRate == 0xE) { got = Data.fbs.Read(bb, bPos, 2); Data.SampleRateText = (bb[bPos] << 8).ToString() + bb[bPos + 1] + (Data.RawSampleRate == 0xD? " Hz" : " kHz"); bPos += 2; } else if (Data.RawSampleRate == 0) { Data.SampleRateText = Data.MetaSampleRate.ToString() + " Hz"; } else { Data.SampleRateText = SampleRateMap[Data.RawSampleRate]; } Data.RawChannelAssignment = bb[3] >> 4; Data.RawSampleSize = (bb[3] & 0xE) >> 1; if (Data.RawSampleSize == 0) { Data.SampleSizeText = Data.BitsPerSample.ToString() + " bits"; } else { Data.SampleSizeText = SampleSizeMap[Data.RawSampleSize]; } Data.aHdr = new byte[bPos]; Array.Copy(bb, Data.aHdr, bPos); Data.ValidSize += bPos; Data.fbs.Position = Data.ValidSize; int octet = Data.fbs.ReadByte(); if (octet < 0) { IssueModel.Add("File truncated near CRC-8", Severity.Fatal); return; } Data.StoredAudioHeaderCRC8 = (Byte)octet; try { Data.fbs.Position = Data.mediaPosition; } catch (EndOfStreamException) { IssueModel.Add("File truncated near audio data", Severity.Fatal); return; } try { Data.fbs.Position = Data.FileSize - 2; } catch (EndOfStreamException) { IssueModel.Add("File truncated looking for end", Severity.Fatal); return; } bb = new byte[2]; if (Data.fbs.Read(bb, 0, 2) != 2) { IssueModel.Add("Read failed on audio block CRC-16", Severity.Fatal); return; } Data.StoredAudioBlockCRC16 = (UInt16)(bb[0] << 8 | bb[1]); Data.MediaCount = Data.FileSize - Data.mediaPosition; GetDiagnostics(); }
public EbmlNodeCRC(EbmlSig element, byte[] payload, long start, long count) : base(element) { this.Start = start; this.Count = count; this.StoredCRC32 = ConvertTo.FromLit32ToUInt32(payload, 0); }
public override void GetReportDetail(IList <string> report) { if (Issues.HasFatal) { return; } report.Add($"MPEG size = {MediaCount}"); if (Xing != null) { report.Add($"XING size = {Xing.XingSize}"); } if (Lame != null) { report.Add($"LAME size = {Lame.LameSize}"); } report.Add(String.Empty); report.Add($"Raw audio header: {ConvertTo.ToBitString (Header.Bits, 32)}"); report.Add(String.Empty); report.Add("Cooked audio header:"); report.Add($" Codec = {Header.Codec}"); report.Add($" Bit rate = {Header.BitRateText}"); report.Add($" Frequency = {Header.SampleRateText}"); report.Add($" Mode = {Header.ModeText}"); report.Add($" CRC protection bit = {Header.CrcProtectedBit}"); report.Add($" Padding bit = {Header.PaddingBit}"); report.Add($" Private bit = {Header.PrivateBit}"); report.Add($" Copyright bit = {Header.CopyrightBit}"); report.Add($" Original bit = {Header.OriginalBit}"); report.Add($" Emphasis = {Header.EmphasisText}"); if (Xing != null) { report.Add(String.Empty); report.Add("XING:"); report.Add($" String = {Xing.XingString}"); report.Add($" Layout = {Xing.Layout}"); } if (Lame != null) { report.Add(String.Empty); report.Add("LAME:"); report.Add($" Version string = {Lame.LameVersion}"); report.Add($" Profile string = {Lame.Profile}"); report.Add($" Profile detail = {Lame.Method}"); report.Add($" Tag revision = {Lame.TagRevision}"); report.Add($" Lowpass filter = {Lame.LowpassFilter}"); report.Add($" Replay Gain: Peak = {Lame.ReplayGainPeak}, Radio = {Lame.RadioReplayGain:X4}, Audiophile = {Lame.AudiophileReplayGain:X4}"); report.Add($" Lame encoding flags = {ConvertTo.ToBitString (Lame.LameFlags, 8)}"); report.Add($" Encoder delay: Start = {Lame.EncoderDelayStart}, End = {Lame.EncoderDelayEnd}"); report.Add($" LAME surround = {Lame.Surround}, LAME preset = {Lame.Preset}"); report.Add($" MP3 gain = {Lame.Mp3Gain}"); report.Add($" Minimum bit rate = {Lame.MinBitRateText}"); report.Add(" Checks:"); report.Add($" Stored: audio header CRC-16 = {Lame.StoredHeaderCrcText}, audio data CRC-16 = {Lame.StoredDataCrcText}"); if (Lame.ActualHeaderCrc != null) { report.Add($" Actual: audio header CRC-16 = {Lame.ActualHeaderCrcText}, audio data CRC-16 = {Lame.ActualDataCrcText}"); } } report.Add(String.Empty); report.Add($"Layout = {Layout}"); }
public override void CalcHashes(Hashes hashFlags, Validations validationFlags) { if (Data.Issues.HasFatal) { return; } if ((hashFlags & Hashes.Intrinsic) != 0) { var buf1 = new byte[27 + 256]; byte[] buf2 = null; Data.PageCount = 0; while (Data.ValidSize < Data.FileSize) { Data.fbs.Position = Data.ValidSize; ++Data.PageCount; var got = Data.fbs.Read(buf1, 0, buf1.Length); if (got < buf1.Length) { IssueModel.Add("Read failed near header", Severity.Fatal); return; } int segmentCount = buf1[26]; UInt32 storedHeaderCRC32 = ConvertTo.FromLit32ToUInt32(buf1, 22); int pageSize = 27 + segmentCount; for (int ix = 27; ix < 27 + segmentCount; ++ix) { pageSize += buf1[ix]; } Data.fbs.Position = Data.ValidSize; if (buf2 == null || buf2.Length < pageSize) { buf2 = new byte[pageSize]; } got = Data.fbs.Read(buf2, 0, pageSize); if (got < pageSize) { IssueModel.Add("Read failed near page " + Data.PageCount, Severity.Fatal); return; } buf2[22] = 0; buf2[23] = 0; buf2[24] = 0; buf2[25] = 0; UInt32 actualHeaderCRC32; var hasher = new Crc32n0Hasher(); hasher.Append(buf2, 0, pageSize); var hash = hasher.GetHashAndReset(); actualHeaderCRC32 = BitConverter.ToUInt32(hash, 0); if (actualHeaderCRC32 != storedHeaderCRC32) { Data.badPage.Add(Data.PageCount.Value); } Data.ValidSize += pageSize; } if (Data.badPage.Count == 0) { Data.CdIssue = IssueModel.Add($"CRC-32 checks successful on {Data.PageCount} pages.", Severity.Advisory, IssueTags.Success); } else { var err = $"CRC-32 checks failed on {Data.badPage.Count} of {Data.PageCount} pages."; Data.CdIssue = IssueModel.Add(err, Severity.Error, IssueTags.Failure); } } base.CalcHashes(hashFlags, validationFlags); }
public Model(Stream stream, string path) : base(path) { base._data = Data = new CueFormat(this, stream, path); SetIgnoredName("Range.wav"); if (Data.FileSize > 512 * 1024) { IssueModel.Add("Oversized file", Severity.Fatal); return; } Data.fBuf = new byte[Data.FileSize]; Data.fbs.Position = 0; if (Data.fbs.Read(Data.fBuf, 0, (int)Data.FileSize) != Data.FileSize) { IssueModel.Add("Read error", Severity.Fatal); return; } Data.Codepage = Encoding.GetEncoding(1252); int fIx = 0, fIx1 = 0, fIx2 = 0, bIxNS = -1, quoteIx1 = -1, quoteIx2 = -1; for (int line = 1;;) { if (fIx < Data.fBuf.Length) { byte ch = Data.fBuf[fIx]; ++fIx; if (ch == (byte)'\r') { fIx2 = fIx < Data.fBuf.Length && Data.fBuf[fIx] == (byte)'\n' ? fIx + 1 : fIx; } else if (ch == (byte)'\n') { fIx2 = fIx; } else { if (ch == '\"') { if (quoteIx1 < 0) { quoteIx1 = fIx; } else if (quoteIx2 < 0) { quoteIx2 = fIx - 1; } } else if (bIxNS < 0 && ch != ' ') { bIxNS = fIx - 1; } continue; } } else { fIx2 = fIx; } if (ConvertTo.StartsWithAscii(Data.fBuf, bIxNS, "CATALOG ")) { Data.Catalog = FormatBase.Cp1252.GetString(Data.fBuf, bIxNS + 8, fIx2 - bIxNS - 8).Trim(null); if (Data.Catalog.Length != 13) { IssueModel.Add("Invalid CATALOG."); } } else if (ConvertTo.StartsWithAscii(Data.fBuf, bIxNS, "FILE ")) { if (quoteIx2 <= quoteIx1) { IssueModel.Add("Malformed FILE."); } else { string quoted = FormatBase.Cp1252.GetString(Data.fBuf, quoteIx1, quoteIx2 - quoteIx1); FilesModel.Add1252(quoted, quoteIx1, quoteIx2 - quoteIx1); } } fIx = fIx1 = bIxNS = fIx2; quoteIx1 = quoteIx2 = -1; ++line; if (fIx >= Data.fBuf.Length) { break; } } }
public Model(Stream stream, string path) { base._data = Data = new IcoFormat(this, stream, path); // Arbitrary sanity limit. if (Data.FileSize > 50000000) { IssueModel.Add("File insanely large", Severity.Fatal); return; } var buf = new byte[Data.FileSize]; stream.Position = 0; var got = stream.Read(buf, 0, (int)Data.FileSize); if (got != Data.FileSize) { IssueModel.Add("Read error", Severity.Fatal); return; } int headerCount = ConvertTo.FromLit16ToInt32(buf, 4); if (headerCount <= 0) { IssueModel.Add("Corrupt header count", Severity.Fatal); return; } int pos = 6; int stop = pos + 16 * headerCount; int actualStart = stop; for (pos = 6; pos < stop; pos += 16) { int width, height, bpp, paletteSize; int storedSize = ConvertTo.FromLit32ToInt32(buf, pos + 8); int storedStart = ConvertTo.FromLit32ToInt32(buf, pos + 12); if (storedStart != actualStart || storedSize <= 0) { IssueModel.Add($"Corrupt header near byte {Data.ValidSize}", Severity.Fatal); return; } bool isPNG = buf[actualStart] == 0x89 && buf[actualStart + 1] == 'P' && buf[actualStart + 2] == 'N' && buf[actualStart + 3] == 'G'; if (isPNG) { width = ConvertTo.FromBig32ToInt32(buf, actualStart + 16); height = ConvertTo.FromBig32ToInt32(buf, actualStart + 20); paletteSize = 0; bpp = buf[actualStart + 24]; } else { width = buf[pos]; if (width == 0) { width = 256; } height = buf[pos + 1]; if (height == 0) { height = 256; } paletteSize = buf[pos + 2]; bpp = buf[pos + 6]; } Data.icons.Add(new IconItem(width, height, paletteSize, bpp, storedStart, storedSize, isPNG)); actualStart += storedSize; } if (actualStart != Data.FileSize) { IssueModel.Add("Incorrect file size"); return; } }
public Model(Stream stream, byte[] hdr, string path) { base._data = Data = new FlvFormat(this, stream, path); var bb = new byte[15]; Data.flags = hdr[4]; if ((Data.flags & 0xA) != 0) { IssueModel.Add("Unexpected flags."); } if ((Data.flags & 5) == 0) { IssueModel.Add("Missing audio and video."); } UInt32 hdrSize = ConvertTo.FromBig32ToUInt32(hdr, 5); if (hdrSize != 9) { IssueModel.Add("Wrong header size."); } Data.mediaPosition = 9; UInt32 actualPrevSize = 0; while (Data.mediaPosition < Data.FileSize) { if (Data.mediaPosition + 15 > Data.FileSize) { IssueModel.Add("File truncated near packet header.", Severity.Fatal); return; } Data.fbs.Position = Data.mediaPosition; var got = Data.fbs.Read(bb, 0, bb.Length); if (got < bb.Length) { IssueModel.Add("Read error", Severity.Fatal); return; } Data.mediaPosition += 15; UInt32 storedPrevSize = ConvertTo.FromBig32ToUInt32(bb, 0); if (storedPrevSize != actualPrevSize) { IssueModel.Add("Bad previous packet size."); } byte packetType = bb[4]; UInt32 packetSize = ConvertTo.FromBig24ToUInt32(bb, 5); actualPrevSize = packetSize + 11; ++Data.PacketCount; Data.mediaPosition += packetSize; } if (Data.mediaPosition > Data.FileSize) { IssueModel.Add("File truncated.", Severity.Fatal); } }
private void ParseAif(Stream stream, byte[] hdr) { Data.GroupId = ConvertTo.FromAsciiToString(hdr, 0, 4); Data.FormType = ConvertTo.FromAsciiToString(hdr, 8, 4); UInt32 gSize = ConvertTo.FromBig32ToUInt32(hdr, 4); if (gSize < 12 || gSize > stream.Length - 8) { IssueModel.Add("File truncated or corrupt.", Severity.Fatal); return; } int hPos = 12; string id0 = ConvertTo.FromAsciiToString(hdr, 12, 4); if (Data.IsCompressed) { if (id0 != "FVER") { IssueModel.Add("Missing 'FVER' chunk."); return; } UInt32 vSize = ConvertTo.FromBig32ToUInt32(hdr, 16); if (vSize != 4) { IssueModel.Add("Bad 'FVER' chunk."); return; } hPos = 24; id0 = ConvertTo.FromAsciiToString(hdr, 24, 4); } if (id0 != "COMM") { IssueModel.Add("Missing 'COMM' chunk."); return; } UInt32 cSize = ConvertTo.FromBig32ToUInt32(hdr, hPos + 4); if (cSize < 18 || cSize > stream.Length - 8) { IssueModel.Add("Bad 'COMM' chunk."); return; } Data.ChannelCount = ConvertTo.FromBig16ToInt32(hdr, hPos + 8); Data.SampleSize = ConvertTo.FromBig16ToInt32(hdr, hPos + 14); ++Data.IffChunkCount; Data.IffSize = gSize; Data.mediaPosition = 0; Data.MediaCount = gSize + 8; Data.ValidSize = hPos + cSize + 8; var hasSSND = false; var buf = new byte[8]; while (Data.ValidSize < Data.MediaCount) { stream.Position = Data.ValidSize; int got = stream.Read(buf, 0, 8); if (got != 8) { IssueModel.Add("Read failed."); return; } cSize = ConvertTo.FromBig32ToUInt32(buf, 4); if (cSize < 8 || Data.ValidSize + cSize > Data.MediaCount - 8) { IssueModel.Add("Bad chunk size or truncated file."); return; } string id = ConvertTo.FromAsciiToString(buf, 0, 4); if (id == "SSND") { if (hasSSND) { IssueModel.Add("Many 'SSND' chunks."); return; } hasSSND = true; } else if (id != "(c) " && id != "ANNO" && id != "AUTH" && id != "NAME") { IssueModel.Add($"Unexpected '{id}' chunk.", Severity.Trivia, IssueTags.StrictWarn); return; } Data.ValidSize += cSize + 8; if ((cSize & 1) != 0) { ++Data.ValidSize; } } if (!hasSSND) { IssueModel.Add("Missing 'SSND' chunk.", Severity.Warning); } }
protected void ParseHashes() { Data.fbs.Position = 0; TextReader tr = new StreamReader(Data.fbs, Data.encoding); for (int line = 1;; ++line) { var lx = tr.ReadLine(); if (lx == null) { return; } lx = lx.TrimStart(); if (lx.Length == 0 || lx[0] == ';') { continue; } if (lx.Length < Data.HashedFiles.HashLength * 2 + 3) { IssueModel.Add($"Too short, line {line}.", Severity.Fatal); return; } // Try typical format with hash first. byte[] hash = null; if (lx[Data.HashedFiles.HashLength * 2] == ' ') { var modeChar = lx[Data.HashedFiles.HashLength * 2 + 1]; for (int modeIx = 1; modeIx < modeChars.Length; ++modeIx) { if (modeChar == modeChars[modeIx]) { hash = ConvertTo.FromHexStringToBytes(lx, 0, Data.HashedFiles.HashLength); if (hash != null) { var targetName = lx.Substring(Data.HashedFiles.HashLength * 2 + 2); HashedModel.Add(targetName, hash, (HashMode)modeIx); break; } } } if (hash != null) { continue; } } // Fall back to layout with name followed by hash. if (lx[lx.Length - Data.HashedFiles.HashLength * 2 - 1] == ' ') { hash = ConvertTo.FromHexStringToBytes(lx, lx.Length - Data.HashedFiles.HashLength * 2, Data.HashedFiles.HashLength); if (hash != null) { var targetName = lx.Substring(0, lx.Length - Data.HashedFiles.HashLength * 2 - 1); HashedModel.Add(targetName, hash, HashMode.Binary); continue; } } IssueModel.Add($"Badly formed, line {line}.", Severity.Fatal); } }
public Model(Stream stream, string path) { ChunksModel = new PngChunk.Vector.Model(); base._data = Data = new PngFormat(this, stream, path); // Arbitrary sanity limit. if (Data.FileSize > 100000000) { IssueModel.Add("File size insanely huge.", Severity.Fatal); return; } Data.fBuf = new byte[(int)Data.FileSize]; var fBuf = Data.fBuf; stream.Position = 0; int got = stream.Read(fBuf, 0, (int)Data.FileSize); if (got < Data.FileSize) { IssueModel.Add("Read failed.", Severity.Fatal); return; } Data.ValidSize = 8; do { UInt32 chunkSize = ConvertTo.FromBig32ToUInt32(fBuf, (int)Data.ValidSize); if (Data.ValidSize + chunkSize + 12 > Data.FileSize) { IssueModel.Add("File is corrupt or truncated.", Severity.Fatal); return; } string type = Encoding.ASCII.GetString(fBuf, (int)Data.ValidSize + 4, 4); UInt32 storedCRC = ConvertTo.FromBig32ToUInt32(fBuf, (int)(Data.ValidSize + chunkSize + 8)); ChunksModel.Add(type, chunkSize, storedCRC); var typeLow = type.ToLower(); switch (typeLow) { case "idat": if (Data.mediaPosition <= 0) { Data.mediaPosition = Data.ValidSize; } break; case "iend": if (Data.MediaCount > 0) { IssueModel.Add("Multiple IEND chunks."); } else { Data.MediaCount = Data.ValidSize - Data.mediaPosition + chunkSize + 0xC; } break; case "ihdr": if (chunkSize < 13) { IssueModel.Add("IHDR chunk is short."); } else if (Data.Width != null) { IssueModel.Add("Multiple IHDR chunks."); } else { Data.Width = ConvertTo.FromBig32ToInt32(fBuf, (int)Data.ValidSize + 8); Data.Height = ConvertTo.FromBig32ToInt32(fBuf, (int)Data.ValidSize + 12); Data.BitDepth = fBuf[(int)Data.ValidSize + 16]; Data.ColorType = fBuf[(int)Data.ValidSize + 17]; Data.CompressionMethod = fBuf[(int)Data.ValidSize + 18]; Data.FilterMethod = fBuf[(int)Data.ValidSize + 19]; Data.InterlaceMethod = fBuf[(int)Data.ValidSize + 20]; } break; case "phys": if (chunkSize < 9) { IssueModel.Add("PHYS chunk is short."); } else if (Data.DotsPerMeter1 != null) { IssueModel.Add("Multiple PHYS chunks."); } else { Data.DotsPerMeter1 = ConvertTo.FromBig32ToInt32(fBuf, (int)Data.ValidSize + 8); Data.DotsPerMeter2 = ConvertTo.FromBig32ToInt32(fBuf, (int)Data.ValidSize + 12); Data.Units = fBuf[(int)Data.ValidSize + 9]; } break; case "text": if (chunkSize > 0x7FFF) { IssueModel.Add("String size too large."); } else { var escaped = new StringBuilder(); for (int ix = (int)Data.ValidSize + 8; ix < (int)Data.ValidSize + 8 + chunkSize; ++ix) { if (fBuf[ix] < ' ' || fBuf[ix] > 0x7F) { escaped.Append($"\\{fBuf[ix]:x2}"); } else { escaped.Append((char)fBuf[ix]); } } Data.texts.Add(escaped.ToString()); } break; case "gama": if (chunkSize < 4) { IssueModel.Add("GAMA chunk is short."); } else if (Data.Gamma != null) { IssueModel.Add("Multiple GAMA chunks."); } else { Data.Gamma = ConvertTo.FromBig32ToUInt32(fBuf, (int)Data.ValidSize + 8) / 100000f; } break; } Data.ValidSize += chunkSize + 0xC; }while (Data.ValidSize < Data.FileSize); if (Data.Width == null) { IssueModel.Add("Missing IHDR chunk."); } if (Data.Chunks.Items[Data.Chunks.Items.Count - 1].Type != "IEND") { IssueModel.Add("Missing IEND chunk."); } if (Data.Width <= 0 || Data.Height <= 0) { IssueModel.Add("Invalid dimensions."); } if (Data.DotsPerMeter1 != Data.DotsPerMeter2) { IssueModel.Add("Density aspect not 1.", Severity.Warning); } if (Data.BitDepth != 1 && Data.BitDepth != 2 && Data.BitDepth != 4 && Data.BitDepth != 8 && Data.BitDepth != 16) { IssueModel.Add($"Invalid bit depth '{Data.BitDepth}'."); } if (Data.CompressionMethod != 0) { IssueModel.Add($"Invalid compression '{Data.CompressionMethod}'."); } if (Data.FilterMethod != 0) { IssueModel.Add($"Invalid filter '{Data.FilterMethod}'."); } if (Data.InterlaceMethod != 0 && Data.InterlaceMethod != 1) { IssueModel.Add($"Invalid interlace '{Data.InterlaceMethod}'."); } }
public Model(Stream stream, byte[] hdr, string path) { base._data = Data = new WavFormat(this, stream, path); ParseRiff(hdr); Data.ActualCRC32 = null; if (Data.Issues.HasFatal) { return; } if (hdr.Length < 0x2C) { IssueModel.Add("File truncated near header", Severity.Fatal); return; } if (Data.IffChunkCount > 1) { IssueModel.Add("Contains multiple RIFF chunks", Severity.Fatal); return; } int hPos = 0x0C; if (hdr[hPos] != 'f' || hdr[hPos + 1] != 'm' || hdr[hPos + 2] != 't' || hdr[hPos + 3] != 0x20) { IssueModel.Add("Missing 'fmt' section", Severity.Fatal); return; } Data.CompCode = hdr[hPos + 8] | hdr[hPos + 9] << 8; Data.ChannelCount = hdr[hPos + 0x0A] | hdr[hPos + 0x0B] << 8; Data.SampleRate = ConvertTo.FromLit32ToUInt32(hdr, hPos + 0x0C); Data.AverageBPS = ConvertTo.FromLit32ToUInt32(hdr, hPos + 0x10); Data.BlockAlign = hdr[hPos + 0x14] | hdr[hPos + 0x15] << 8; Data.BitsPerSample = hdr[hPos + 0x16] | hdr[hPos + 0x17] << 8; if ((hdr[hPos] & 0x80) != 0) { IssueModel.Add("Header size insanely huge", Severity.Fatal); return; } long hdrDataSize = ConvertTo.FromLit32ToInt32(hdr, hPos + 4); long dataPos = hPos + 8 + hdrDataSize; stream.Position = dataPos; var dHdr = new byte[8]; if (stream.Read(dHdr, 0, 8) != 8) { IssueModel.Add("Read failed", Severity.Fatal); return; } if (dHdr[0] != 'd' || dHdr[1] != 'a' || dHdr[2] != 't' || dHdr[3] != 'a') { IssueModel.Add("Missing 'data' section", Severity.Fatal); return; } Data.mediaPosition = dataPos + 8; Data.MediaCount = ConvertTo.FromLit32ToUInt32(dHdr, 4); if (Data.mediaPosition + Data.MediaCount > Data.IffSize) { IssueModel.Add("Invalid data size", Severity.Fatal); return; } Data.HasTags = Data.mediaPosition + Data.MediaCount < Data.IffSize; GetDiagnostics(); }
public Model(Stream stream, string path) { base._data = Data = new JpegFormat(this, stream, path); // Arbitrary choice of 50MB cutoff. if (Data.FileSize > 50000000) { IssueModel.Add("File insanely huge", Severity.Fatal); return; } byte[] buf = new byte[(int)Data.FileSize]; stream.Position = 0; int got = stream.Read(buf, 0, (int)Data.FileSize); if (got != Data.FileSize) { IssueModel.Add("Read error", Severity.Fatal); return; } for (int len = 0, pos = 2; ; pos = pos + len + 2) { if (pos > Data.FileSize - 4) { IssueModel.Add("File truncated", Severity.Fatal); return; } if (buf[pos] != 0xFF) { IssueModel.Add("Missing marker.", Severity.Fatal); return; } if (buf[pos + 1] == 0xD9) { IssueModel.Add("Unexpected EOI marker", Severity.Fatal); return; } // Detect SOS (Start of Stream) marker. if (buf[pos + 1] == 0xDA) { Data.sosPos = pos; break; } len = (buf[pos + 2] << 8) + buf[pos + 3]; if (len < 2) { IssueModel.Add($"Invalid segment length '{len}'.", Severity.Fatal); return; } if (pos + len + 2 >= Data.FileSize) { IssueModel.Add("File truncated.", Severity.Fatal); return; } if (len == 2) { IssueModel.Add("Contains empty segment.", Severity.Trivia); } ++Data.SegmentCount; // Detect application marker: if (buf[pos + 1] >= 0xE0 && buf[pos + 1] <= 0xEF) { string appName = ConvertTo.FromAsciizToString(buf, pos + 4); Data.appNames.Add(appName); if (buf[pos + 1] == 0xE0) { if (buf[pos + 4] != 'J' || buf[pos + 5] != 'F' || buf[pos + 6] != 'I' || buf[pos + 7] != 'F' || buf[pos + 8] != 0) { continue; } if ((Data.Apps & JpegApps.Jfif) != 0) { IssueModel.Add("Contains ghost JFIF segment.", Severity.Advisory); continue; } if (len < 13) { IssueModel.Add("JFIF segment too small.", Severity.Fatal); return; } Data.Apps |= JpegApps.Jfif; Data.VersionMajor = buf[pos + 0x09]; Data.VersionMinor = buf[pos + 0x0A]; Data.Units = (JpegUnits)buf[pos + 0x0B]; Data.DensityX = (buf[pos + 0x0C] << 8) + buf[pos + 0x0D]; Data.DensityY = (buf[pos + 0x0E] << 8) + buf[pos + 0x0F]; Data.ThumbXLen = buf[pos + 0x10]; Data.ThumbYLen = buf[pos + 0x11]; } else if (buf[pos + 1] == 0xE1) { if (buf[pos + 4] == 'E' && buf[pos + 5] == 'x' && buf[pos + 6] == 'i' && buf[pos + 7] == 'f' && buf[pos + 8] == 0) { if ((Data.Apps & JpegApps.Exif) != 0) { IssueModel.Add("Contains ghost Exif segment", Severity.Trivia); continue; } Data.Apps |= JpegApps.Exif; // Additional Exif parsing goes here... } } } } // Detect EOI (end of image) marker: FFD9 for (int pos = (int)Data.sosPos; pos < Data.FileSize - 1; ++pos) { if (buf[pos] == 0xFF && buf[pos + 1] == 0xD9) { Data.ValidSize = pos + 2; Data.eoiPos = pos; break; } } CalcMark(); GetDiagnostics(); }
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()); } }