示例#1
0
            public Model(Stream stream, string path)
            {
                base._data = Data = new GifFormat(this, stream, path);

                // Arbitrary sanity limit.
                if (Data.FileSize > 50000000)
                {
                    IssueModel.Add("File size insanely huge", Severity.Fatal);
                    return;
                }

                Data.fBuf = new byte[(int)Data.FileSize];

                stream.Position = 0;
                int got = stream.Read(Data.fBuf, 0, (int)Data.FileSize);

                if (got < Data.FileSize)
                {
                    IssueModel.Add("Read failed.", Severity.Fatal);
                    return;
                }

                Data.Version = Data.fBuf[4] == '7'? "87a" : "89a";
                Data.Width   = ConvertTo.FromLit16ToInt32(Data.fBuf, 6);
                Data.Height  = ConvertTo.FromLit16ToInt32(Data.fBuf, 8);
            }
示例#2
0
            public Model(Stream stream, byte[] header, string path)
            {
                base._data = Data = new AviFormat(this, stream, path);

                ParseRiff(header);

                var buf = new byte[0xC0];

                stream.Position = 0;
                int got = stream.Read(buf, 0, 0xC0);

                if (got != 0xC0)
                {
                    IssueModel.Add("File is short", Severity.Fatal);
                    return;
                }

                Data.StreamCount = ConvertTo.FromLit32ToInt32(buf, 0x38);
                Data.Width       = ConvertTo.FromLit32ToInt32(buf, 0x40);
                Data.Height      = ConvertTo.FromLit32ToInt32(buf, 0x44);
                Data.Codec       = Encoding.ASCII.GetString(buf, 0xBC, 4).Trim();

                CalcMark();
                GetDiagsForMarkable();
            }
示例#3
0
            private void GetDiagnostics()
            {
                if ((Data.Apps & JpegApps.Jfif) != 0)
                {
                    // This spec is often violated.
                    if (Data.DensityX == 0 || Data.DensityY == 0)
                    {
                        IssueModel.Add($"Invalid JFIF density of {Data.DensityX}x{Data.DensityY}", Severity.Trivia);
                    }

                    if ((int)Data.Units > 2)
                    {
                        IssueModel.Add($"Invalid JFIF units of {Data.Units}", Severity.Warning);
                    }
                }

                if (Data.eoiPos == null)
                {
                    IssueModel.Add("Missing end marker");
                }
                else
                if (Data.ExcessSize == 0)
                {
                    var unparsedSize = Data.FileSize - Data.ValidSize;
                    if (unparsedSize > 0)
                    {
                        IssueModel.Add($"Possible watermark, size={unparsedSize}", Severity.Advisory);
                    }
                }
            }
示例#4
0
 private void GetDiagnostics()
 {
     if (Data.storedHash == null)
     {
         IssueModel.Add("No signature.", Severity.Trivia, IssueTags.Fussy);
     }
 }
示例#5
0
            protected void GetDiagnostics()
            {
                if (Data.Moov == 0)
                {
                    IssueModel.Add("Missing 'moov' section.", Severity.Error);
                }

                if (Data.Mdat == 0)
                {
                    IssueModel.Add("Missing 'mdat' section.", Severity.Error);
                }

                // Wide boxes are rare.
                if (Data.Wide != 0)
                {
                    IssueModel.Add($"Number of wide boxes={Data.Wide}.", Severity.Noise);
                }

                if (Data.ExcessSize == 0)
                {
                    var unparsedSize = Data.FileSize - Data.ValidSize;
                    if (unparsedSize != 0)
                    {
                        IssueModel.Add($"Possible watermark, size={unparsedSize}.", Severity.Advisory);
                    }
                }
            }
示例#6
0
            protected void GetDiagnostics()
            {
                GetRiffDiagnostics();

                if (Data.CompCode != (int)WaveCompression.PCM)
                {
                    IssueModel.Add("Data is not PCM", Severity.Trivia, IssueTags.Substandard);
                }
            }
示例#7
0
            protected void ParseRiff(byte[] hdr)
            {
                var  buf       = new byte[8];
                long chunkSize = ConvertTo.FromLit32ToUInt32(hdr, 4) + 8;

                if (chunkSize % 1 != 0)
                {
                    IssueModel.Add("RIFF has odd sized chunk, some tools don't pad this correctly", Severity.Trivia);
                }

                do
                {
                    if (Data.ValidSize + chunkSize > Data.FileSize)
                    {
                        IssueModel.Add("File truncated", Severity.Fatal);
                        return;
                    }

                    ++Data.RiffChunkCount;
                    Data.RiffSize = Data.ValidSize = Data.ValidSize + chunkSize;

                    if (Data.ValidSize + 8 > Data.FileSize)
                    {
                        // Not enough bytes for a header.
                        return;
                    }

                    try
                    {
                        Data.fbs.Position = Data.ValidSize;
                    }
                    catch (EndOfStreamException)
                    {
                        IssueModel.Add("File truncated or corrupt", Severity.Fatal);
                        return;
                    }
                    var got = Data.fbs.Read(buf, 0, 8);
                    if (got != 8)
                    {
                        IssueModel.Add("Read error", Severity.Fatal);
                        return;
                    }
                    chunkSize = ConvertTo.FromLit32ToUInt32(buf, 4) + 8;
                }while (buf[0] == 'R' || buf[1] == 'I' || buf[2] == 'F' || buf[3] == 'F');

                if (buf[0] == 'J' && buf[1] == 'U' && buf[2] == 'N' && buf[3] == 'K')
                {
                    if (Data.ValidSize + chunkSize > Data.FileSize)
                    {
                        IssueModel.Add("File corrupt or truncated", Severity.Fatal);
                        return;
                    }

                    Data.JunkSize   = chunkSize;
                    Data.ValidSize += Data.JunkSize;
                }
            }
示例#8
0
            public override void CalcHashes(Hashes hashFlags, Validations validationFlags)
            {
                if (base.Data.Issues.HasFatal)
                {
                    return;
                }

                if ((validationFlags & Validations.Exists) != 0)
                {
                    Data.MissingCount = 0;
                    if (Data.Files.Items.Count != 1 || Data.Files.Items[0].Name != Data.IgnoredName)
                    {
                        for (int ix = 0; ix < Data.Files.Items.Count; ++ix)
                        {
                            FileItem item = Data.Files.Items[ix];
                            var      name = item.Name;

                            if (Data.AllowNonFile && (name.StartsWith("http:") || name.StartsWith("https:")))
                            {
                                IssueModel.Add($"Ignoring URL '{name}'.", Severity.Trivia);
                            }
                            else
                            {
                                try
                                {
                                    if (!System.IO.Path.IsPathRooted(name))
                                    {
                                        name = Data.Files.RootDir + System.IO.Path.DirectorySeparatorChar + name;
                                    }
                                    else if (Data.ForbidRooted)
                                    {
                                        IssueModel.Add($"File is rooted: '{item.Name}'.");
                                    }
                                }
                                catch (ArgumentException ex)
                                {
                                    IssueModel.Add($"Malformed file name '{name}': {ex.Message}");
                                    FilesModel.SetIsFound(ix, false);
                                    ++Data.MissingCount;
                                    continue;
                                }

                                // Exists doesn't seem to throw any exceptions, so no try/catch.
                                bool isFound = File.Exists(name);
                                FilesModel.SetIsFound(ix, isFound);
                                if (!isFound)
                                {
                                    IssueModel.Add($"File '{item.Name}' not found.", Severity.Advisory);
                                    ++Data.MissingCount;
                                }
                            }
                        }
                    }
                }

                base.CalcHashes(hashFlags, validationFlags);
            }
示例#9
0
            public void CalcHashWebCheck()
            {
                if (Data.storedHash == null)
                {
                    Severity sev = Data.EacVersionString != null && Data.EacVersionString.StartsWith("1")? Severity.Warning : Severity.Noise;
                    Data.ShIssue = IssueModel.Add("EAC log self-hash not present.", sev, IssueTags.StrictErr);
                }
                else
                {
                    string boundary = "---------------------------" + DateTime.Now.Ticks;
                    string header   = "Content-Disposition: form-data; name=\"LogFile\"; filename=\""
                                      + "SubmittedByConDiags.log\"\r\nContent-Type: application/octet-stream\r\n\r\n";

                    byte[] bndBuf = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
                    byte[] hdrBuf = Encoding.UTF8.GetBytes(header);
                    byte[] tlrBuf = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");

                    var req = (HttpWebRequest)WebRequest.Create("http://www.exactaudiocopy.de/log/check.aspx");
                    req.ContentType = "multipart/form-data; boundary=" + boundary;
                    req.Method      = "POST";
                    req.KeepAlive   = true;
                    req.Credentials = CredentialCache.DefaultCredentials;

                    try
                    {
                        using (var qs = req.GetRequestStream())
                        {
                            qs.Write(bndBuf, 0, bndBuf.Length);
                            qs.Write(hdrBuf, 0, hdrBuf.Length);
                            qs.Write(Data.fBuf, 0, Data.fBuf.Length);
                            qs.Write(tlrBuf, 0, tlrBuf.Length);
                        }

                        using (WebResponse res = req.GetResponse())
                            using (Stream ps = res.GetResponseStream())
                                using (StreamReader rdr = new StreamReader(ps))
                                {
                                    string answer = rdr.ReadLine();
                                    if (answer.Contains("is fine"))
                                    {
                                        Data.ShIssue = IssueModel.Add("EAC log self-hash verify successful.", Severity.Advisory, IssueTags.Success);
                                    }
                                    else if (answer.Contains("incorrect"))
                                    {
                                        Data.ShIssue = IssueModel.Add("EAC log self-hash mismatch, file has been modified.", Severity.Error, IssueTags.Failure);
                                    }
                                    else
                                    {
                                        Data.ShIssue = IssueModel.Add("EAC log self-hash verify attempt returned unknown result.", Severity.Advisory, IssueTags.StrictErr);
                                    }
                                }
                    }
                    catch (Exception ex)
                    { Data.ShIssue = IssueModel.Add("EAC log self-hash verify attempt failed: " + ex.Message.Trim(null), Severity.Warning, IssueTags.StrictErr); }
                }
            }
示例#10
0
            public Model(Stream stream, string path)
            {
                base._data = Data = new DbFormat(this, stream, path);

                // No content diagnostics at this time.
                if (Data.fbs.Length == 0)
                {
                    IssueModel.Add("File is empty.");
                }
            }
示例#11
0
            public void CheckFlacRipTags(IList <FlacFormat> flacs)
            {
                int prevTrackNum = -1;

                foreach (FlacFormat flac in flacs)
                {
                    var trackTag = flac.GetTagValue("TRACKNUMBER");

                    var             integerRegex    = new Regex("^([0-9]+)", RegexOptions.Compiled);
                    MatchCollection reMatches       = integerRegex.Matches(trackTag);
                    string          trackTagCapture = reMatches.Count == 1 ? reMatches[0].Groups[1].ToString() : trackTag;

                    if (int.TryParse(trackTagCapture, out int trackNum))
                    {
                        if (prevTrackNum >= 0 && trackNum != prevTrackNum + 1)
                        {
                            IssueModel.Add($"Gap in TRACKNUMBER tags near '{trackTag}'.", Severity.Error, IssueTags.BadTag);
                        }
                        prevTrackNum = trackNum;
                    }
                }

                TagCheckTextIsSame(flacs, "TRACKTOTAL");
                TagCheckTextIsSame(flacs, "DISCNUMBER");
                TagCheckTextIsSame(flacs, "DISCTOTAL");
                TagCheckTextIsSame(flacs, "DATE");
                TagCheckTextIsSame(flacs, "RELEASE DATE");

                bool?isSameAA = FlacFormat.IsFlacTagsAllSame(flacs, "ALBUMARTIST");

                if (isSameAA == false)
                {
                    IssueModel.Add("ALBUMARTIST tags are inconsistent.", Severity.Warning, IssueTags.BadTag | IssueTags.StrictErr);
                }
                else if (isSameAA == null)
                {
                    bool?isSameArtist = FlacFormat.IsFlacTagsAllSame(flacs, "ARTIST");
                    if (isSameArtist == false)
                    {
                        IssueModel.Add("Inconsistent ARTIST tag or missing ALBUMARTIST tag.", Severity.Warning, IssueTags.BadTag);
                    }
                    else if (isSameArtist == null)
                    {
                        IssueModel.Add("ARTIST is missing.", Severity.Warning, IssueTags.BadTag);
                    }
                }

                TagCheckTextIsSame(flacs, "ALBUM");
                TagCheckTextIsSame(flacs, "ORGANIZATION");
                TagCheckTextIsSame(flacs, "BARCODE");
                TagCheckTextIsSame(flacs, "CATALOGNUMBER");
                TagCheckTextIsSame(flacs, "ALBUMARTISTSORTORDER");
            }
示例#12
0
            public Model(Stream stream, byte[] hdr, string path)
            {
                base._data = Data = new Mpeg2Format(this, stream, path);

                if (Data.FileSize < 12)
                {
                    IssueModel.Add("File truncated near start", Severity.Error);
                    return;
                }

                Data.IsVOB = hdr[3] == 0xBA;

                // TODO parse contents for watermark/truncation
                Data.ValidSize = Data.FileSize;

                long loopStop = Data.FileSize - 36;

                if (loopStop < 8)
                {
                    loopStop = 8;
                }
                for (long ePos = Data.FileSize - 4; ePos > loopStop; --ePos)
                {
                    Data.fbs.Position = ePos;
                    var got = Data.fbs.Read(hdr, 0, 4);
                    if (got != 4)
                    {
                        IssueModel.Add("Read error looking for trailer", Severity.Fatal);
                        return;
                    }

                    if (hdr[0] == 0 && hdr[1] == 0 && hdr[2] == 1 && hdr[3] == 0xB9)
                    {
                        // 20 bytes of zero seem typical .mpg pad, so can't do this watermark calculation
                        Data.trailerPos = ePos;
                        Data.ValidSize  = ePos + 4;
                        break;
                    }
                }

                if (Data.trailerPos < 0)
                {
                    IssueModel.Add("No trailermark found.", Severity.Trivia);
                }
                else
                {
                    long unparsedSize = Data.FileSize - Data.ValidSize;
                    if (unparsedSize != 0)
                    {
                        IssueModel.Add($"Possible watermark, size={unparsedSize}", Severity.Trivia);
                    }
                }
            }
示例#13
0
            protected void CalcMark(bool assumeProbable = false)
            {
                byte[] buf = null;

                long markSize = Data.FileSize - Data.ValidSize;

                if (markSize <= 0)
                {
                    return;
                }

                // 1000 is somewhat arbitrary here.
                if (markSize > 1000)
                {
                    Data.Watermark = Likeliness.Possible;
                }
                else
                {
                    Data.fbs.Position = Data.ValidSize;
                    buf = new byte[(int)markSize];
                    int got = Data.fbs.Read(buf, 0, (int)markSize);
                    if (got != markSize)
                    {
                        IssueModel.Add("Read failure", Severity.Fatal);
                        return;
                    }

                    Data.excess    = null;
                    Data.Watermark = Likeliness.Probable;
                    if (!assumeProbable)
                    {
                        for (int ix = 0; ix < buf.Length; ++ix)
                        {
                            // Fuzzy ID of watermark bytes by checking if ASCII text. Purpose is to
                            // exclude .AVI files that do not have correct encoded sizes - a common issue.
                            var bb = buf[ix];
                            if (bb > 127 || (bb < 32 && bb != 0 && bb != 9 && bb != 0x0A && bb != 0x0D))
                            {
                                Data.Watermark = Likeliness.Possible;
                                break;
                            }
                        }
                    }
                }

                if (Data.Watermark == Likeliness.Probable)
                {
                    Data.excess = buf;
                    var caption = Data.Watermark.ToString() + " watermark, size=" + Data.ExcessSize + ".";
                    var prompt  = "Trim probable watermark of " + Data.ExcessSize + " byte" + (Data.ExcessSize != 1? "s" : "");
                    IssueModel.Add(caption, Severity.Warning, IssueTags.None, prompt, TrimWatermark);
                }
            }
示例#14
0
            protected void GetRiffDiagnostics()
            {
                if (Data.RiffSize <= 12)
                {
                    IssueModel.Add("Missing data", Severity.Error);
                }

                long unparsedSize = Data.FileSize - Data.ValidSize - Data.ExcessSize;

                if (unparsedSize != 0)
                {
                    IssueModel.Add("Unrecognized bytes at end = " + unparsedSize, Severity.Warning);
                }
            }
示例#15
0
            private void TagCheckNumber(string key, bool optional = false)
            {
                string value = Data.GetTagValue(key);

                if (value == null)
                {
                    if (!optional)
                    {
                        IssueModel.Add(key + " tag is missing.", Severity.Warning, IssueTags.BadTag | IssueTags.StrictErr);
                    }
                }
                else if (value.Length == 0 || !Char.IsDigit(value[0]))
                {
                    IssueModel.Add(key + " tag is not a number.", Severity.Warning, IssueTags.BadTag | IssueTags.StrictErr);
                }
            }
示例#16
0
            protected void GetDiagsForMarkable()
            {
                if (Data.RiffSize <= 12)
                {
                    IssueModel.Add("Missing data", Severity.Error);
                }

                if (Data.ExcessSize == 0)
                {
                    var unparsedSize = base.Data.FileSize - base.Data.ValidSize;
                    if (unparsedSize > 0)
                    {
                        IssueModel.Add("Possible watermark, size=" + unparsedSize, Severity.Trivia);
                    }
                }
            }
示例#17
0
            public Model(Stream stream, string path) : base(path)
            {
                base._data = Data = new CueFormat(this, stream, path);

                SetIgnoredName("Range.wav");
                Data.fbs.Position = 0;
                TextReader tr = new StreamReader(Data.fbs, LogBuffer.cp1252);

                for (int line = 1; ; ++line)
                {
                    var lx = tr.ReadLine();
                    if (lx == null)
                    {
                        break;
                    }

                    lx = lx.TrimStart();
                    if (lx.Length == 0)
                    {
                        continue;
                    }

                    if (lx.StartsWith("CATALOG "))
                    {
                        Data.Catalog = lx.Substring(8).Trim();
                        if (Data.Catalog.Length != 13)
                        {
                            IssueModel.Add("Invalid CATALOG.");
                        }
                        continue;
                    }

                    if (lx.Length > 0 && lx.StartsWith("FILE "))
                    {
                        var name = Data.GetQuotedField(lx, 5);
                        if (name.Length == 0)
                        {
                            IssueModel.Add("Missing file name.");
                        }
                        else
                        {
                            FilesModel.Add(name);
                        }
                    }
                }
            }
示例#18
0
            public override void CalcHashes(Hashes hashFlags, Validations validationFlags)
            {
                if (Data.Issues.HasFatal)
                {
                    return;
                }

                if (Data.Lame != null && (hashFlags & Hashes.Intrinsic) != 0 && Data.Lame.ActualDataCrc == null)
                {
                    var hasher = new Crc16rHasher();
                    hasher.Append(Data.fBuf, (int)Data.mediaPosition, Data.Lame.LameHeaderSize);
                    byte[] hash = hasher.GetHashAndReset();
                    LameModel.SetActualHeaderCrc(BitConverter.ToUInt16(hash, 0));

                    hasher.Append(Data.fBuf, (int)Data.mediaPosition + 0xC0, (int)Data.MediaCount - 0xC0);
                    hash = hasher.GetHashAndReset();
                    LameModel.SetActualDataCrc(BitConverter.ToUInt16(hash, 0));

                    if (!Data.IsBadData && !Data.IsBadHeader)
                    {
                        Data.ChIssue = Data.CdIssue = IssueModel.Add("CRC-16 checks successful.", Severity.Noise, IssueTags.Success);
                    }
                    else
                    {
                        if (Data.IsBadHeader)
                        {
                            Data.ChIssue = IssueModel.Add("CRC-16 check failed on audio header.", Severity.Error, IssueTags.Failure);
                        }
                        else
                        {
                            Data.ChIssue = IssueModel.Add("CRC-16 check successful on audio header.", Severity.Noise, IssueTags.Success);
                        }

                        if (Data.IsBadData)
                        {
                            Data.CdIssue = IssueModel.Add("CRC-16 check failed on audio data.", Severity.Error, IssueTags.Failure);
                        }
                        else
                        {
                            Data.CdIssue = IssueModel.Add("CRC-16 check successful on audio data.", Severity.Noise, IssueTags.Success);
                        }
                    }
                }

                base.CalcHashes(hashFlags, validationFlags);
            }
示例#19
0
            public override void CalcHashes(Hashes hashFlags, Validations validationFlags)
            {
                if (Data.Issues.HasFatal)
                {
                    return;
                }

                if ((hashFlags & Hashes.Intrinsic) != 0 && Data.badCrcCount == null)
                {
                    Data.badCrcCount = 0;
                    foreach (var master in Data.layout)
                    {
                        foreach (var cx in master.GetNodeTraces("CRC-32"))
                        {
                            var top  = cx.Peek();
                            var node = top.Node.Nodes[top.Index] as EbmlNodeCRC;
                            if (node.Count > 0)
                            {
                                var hasher = new Crc32rHasher();
                                hasher.Append(Data.fbs, node.Start, node.Count);
                                var hash = hasher.GetHashAndReset();
                                node.ActualCRC32 = BitConverter.ToUInt32(hash, 0);
                                if (node.StoredCRC32 != node.ActualCRC32)
                                {
                                    ++Data.badCrcCount;
                                }
                            }
                        }
                    }

                    if (Data.CrcCount > 0)
                    {
                        if (Data.badCrcCount == 0)
                        {
                            Data.CdIssue = IssueModel.Add("CRC checks successful.", Severity.Noise, IssueTags.Success);
                        }
                        else
                        {
                            Data.CdIssue = IssueModel.Add("CRC check failure.", Severity.Error, IssueTags.Failure);
                        }
                    }
                }

                base.CalcHashes(hashFlags, validationFlags);
            }
示例#20
0
            private void TagCheckDate(string key, bool optional = false)
            {
                string value = Data.GetTagValue(key);

                if (value == null)
                {
                    if (!optional)
                    {
                        IssueModel.Add(key + " tag is missing.", Severity.Warning, IssueTags.BadTag | IssueTags.StrictErr);
                    }
                }
                else if ((value.Length != 4 && value.Length != 10) || (!value.StartsWith("19") && !value.StartsWith("20")) ||
                         !Char.IsDigit(value[2]) || !Char.IsDigit(value[3]))
                {
                    IssueModel.Add(key + " tag not like YYYY or YYYY-MM-DD with YYYY in 1900 to 2099.",
                                   Severity.Warning, IssueTags.BadTag | IssueTags.StrictErr);
                }
            }
示例#21
0
            public override void CalcHashes(Hashes hashFlags, Validations validationFlags)
            {
                if ((hashFlags & Hashes.Intrinsic) != 0 && Data.BadCrcCount == null)
                {
                    Data.BadCrcCount = 0;
                    var hasher = new Crc32rHasher();
                    int pos    = 12;
                    for (int ix = 0; ix < Data.Chunks.Items.Count; ++ix)
                    {
                        PngChunk chunk = Data.Chunks.Items[ix];
                        hasher.Append(Data.fBuf, pos, (int)chunk.Size + 4);
                        byte[] hash      = hasher.GetHashAndReset();
                        UInt32 actualCRC = BitConverter.ToUInt32(hash, 0);
                        ChunksModel.SetActualCRC(ix, actualCRC);

                        if (actualCRC != chunk.StoredCRC)
                        {
                            ++Data.BadCrcCount;
                        }
                        pos += (int)chunk.Size + 12;
                    }

                    var sb = new StringBuilder();
                    sb.Append("CRC checks on ");

                    if (Data.BadCrcCount != 0)
                    {
                        sb.Append(Data.BadCrcCount);
                        sb.Append(" of ");
                        sb.Append(Data.Chunks.Items.Count);
                        sb.Append(" chunks failed.");
                    }
                    else
                    {
                        sb.Append(Data.Chunks.Items.Count);
                        sb.Append(" chunks successful.");
                    }

                    Data.CdIssue = IssueModel.Add(sb.ToString(), Data.BadCrcCount == 0? Severity.Noise : Severity.Error,
                                                  Data.BadCrcCount == 0? IssueTags.Success : IssueTags.Failure);
                }

                base.CalcHashes(hashFlags, validationFlags);
            }
示例#22
0
            private string ParseTracks(string lx)
            {
                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(lx, num);
                    }
                }
                return(lx);
            }
示例#23
0
            public override void CalcHashes(Hashes hashFlags, Validations validationFlags)
            {
                if (IssueModel.Data.HasFatal)
                {
                    return;
                }

                if ((hashFlags & Hashes.PcmMD5) != 0 && Data.actualMediaMD5 == null)
                {
                    try
                    {
                        var hasher = new Md5Hasher();
                        hasher.Append(Data.fbs, Data.mediaPosition, Data.MediaCount);
                        Data.actualMediaMD5 = hasher.GetHashAndReset();
                    }
                    catch (EndOfStreamException)
                    {
                        IssueModel.Add("File truncated near audio.", Severity.Fatal);
                    }
                }

                if ((hashFlags & Hashes.PcmCRC32) != 0 && Data.ActualCRC32 == null)
                {
                    try
                    {
                        var hasher = new Crc32rHasher();
                        hasher.Append(Data.fbs, Data.mediaPosition, Data.MediaCount);
                        var hash = hasher.GetHashAndReset();
                        Data.ActualCRC32 = BitConverter.ToUInt32(hash, 0);
                    }
                    catch (EndOfStreamException)
                    {
                        IssueModel.Add("File truncated near audio.", Severity.Fatal);
                    }
                }

                base.CalcHashes(hashFlags, validationFlags);
            }
示例#24
0
            public void Validate(Hashes hashFlags, IList <FlacFormat> flacs)
            {
                actualFlacs = flacs;

                if (flacs == null || flacs.Count == 0 || flacs.Max(s => s.Issues.MaxSeverity) >= Severity.Error)
                {
                    GetDiagnostics();
                }
                else if (flacs.Count == Data.Files.Items.Count)
                {
                    var m = "Repair bad file reference";
                    if (Data.MissingCount != 1)
                    {
                        m += "s";
                    }
                    GetDiagnostics(m, RepairMissing, (hashFlags & Hashes._Repairing) != 0);
                }
                else if (Data.Files.Items.Count != 1)
                {
                    Data.FcIssue = IssueModel.Add($"Folder contains {flacs.Count} .flac file(s) yet .cue contains {Data.Files.Items.Count} file reference(s).",
                                                  Severity.Error, IssueTags.Failure);
                }
            }
示例#25
0
            public Model(Stream stream, string path)
            {
                base._tracksModel = TracksModel = new LogEacTrack.Vector.Model();
                base._data        = Data = new LogEacFormat(this, stream, path);

                Data.AccurateRip = null;
                Data.RipDate     = String.Empty;

                // Arbitrary limit.
                if (Data.FileSize > 250000)
                {
                    IssueModel.Add("File insanely huge.", Severity.Fatal);
                    return;
                }

                Data.fBuf         = new byte[Data.FileSize];
                Data.fbs.Position = 0;
                var got = Data.fbs.Read(Data.fBuf, 0, (int)Data.FileSize);

                if (got != Data.FileSize)
                {
                    IssueModel.Add("Read failed", Severity.Fatal);
                    return;
                }

                if (got < 2 || Data.fBuf[0] != 0xFF || Data.fBuf[1] != 0xFE)
                {
                    Data.Codepage = Encoding.GetEncoding(1252);
                }
                else
                {
                    Data.Codepage = Encoding.Unicode;
                }

                Parse(new LogBuffer(Data.fBuf, Data.Codepage));
                GetDiagnostics();
            }
示例#26
0
            protected void ReadPlaylist(Encoding codePage)
            {
                long fileSize = Data.FileSize;

                if (fileSize > 512 * 1024)
                {
                    IssueModel.Add("Oversized file", Severity.Fatal);
                    return;
                }

                Data.fBuf         = new byte[fileSize];
                Data.fbs.Position = 0;
                if (Data.fbs.Read(Data.fBuf, 0, (int)fileSize) != fileSize)
                {
                    IssueModel.Add("Read error", Severity.Fatal);
                    return;
                }

                using (TextReader reader = new StreamReader(new MemoryStream(Data.fBuf), codePage))
                {
                    for (;;)
                    {
                        var lx = reader.ReadLine();
                        if (lx == null)
                        {
                            break;
                        }
                        lx = lx.TrimStart();
                        if (lx.Length > 0 && lx[0] != '#')
                        {
                            FilesModel.Add(lx);
                        }
                    }
                }

                Data.Codepage = codePage;
            }
示例#27
0
            public override void CalcHashes(Hashes hashFlags, Validations validationFlags)
            {
                if (Data.Issues.HasFatal)
                {
                    return;
                }

                if ((hashFlags & Hashes.Intrinsic) != 0 && Data.ActualAudioHeaderCRC8 == null)
                {
                    var hasher1 = new Crc8Hasher();
                    hasher1.Append(Data.aHdr);
                    byte[] hash1 = hasher1.GetHashAndReset();
                    Data.ActualAudioHeaderCRC8 = hash1[0];

                    try
                    {
                        var hasher2 = new Crc16nHasher();
                        hasher2.Append(Data.fbs, Data.mediaPosition, Data.FileSize - Data.mediaPosition - 2);
                        byte[] hash2 = hasher2.GetHashAndReset();
                        Data.ActualAudioBlockCRC16 = BitConverter.ToUInt16(hash2, 0);
                    }
                    catch (EndOfStreamException ex)
                    {
                        IssueModel.Add("Read failed while checking audio CRC: " + ex.Message, Severity.Fatal);
                        return;
                    }

                    if (!Data.IsBadDataCRC && !Data.IsBadHeaderCRC)
                    {
                        Data.ChIssue = Data.CdIssue = IssueModel.Add("CRC checks successful on audio header and data.", Severity.Noise, IssueTags.Success);
                    }
                    else
                    {
                        if (Data.IsBadHeaderCRC)
                        {
                            Data.ChIssue = IssueModel.Add("CRC-8 check failed on audio header.", Severity.Error, IssueTags.Failure);
                        }
                        else
                        {
                            Data.ChIssue = IssueModel.Add("CRC-8 check successful on audio header.", Severity.Noise, IssueTags.Success);
                        }

                        if (Data.IsBadDataCRC)
                        {
                            Data.CdIssue = IssueModel.Add("CRC-16 check failed on audio data.", Severity.Error, IssueTags.Failure);
                        }
                        else
                        {
                            Data.CdIssue = IssueModel.Add("CRC-16 check successful on audio data.", Severity.Noise, IssueTags.Success);
                        }
                    }
                }

                if ((hashFlags & Hashes.PcmMD5) != 0 && Data.actualAudioDataMD5 == null)
                {
                    Process px = null;
                    try
                    { px = StartFlac(Data.Path); }
                    catch (Exception ex)
                    { IssueModel.Add("flac executable failed with '" + ex.Message.Trim(null) + "'."); }

                    if (px != null)
                    {
                        using (var br = new BinaryReader(px.StandardOutput.BaseStream))
                        {
                            try
                            {
                                var hasher = new Md5Hasher();
                                hasher.Append(br);
                                var hash = hasher.GetHashAndReset();
                                Data.actualAudioDataMD5 = hash;
                            }
                            catch (EndOfStreamException ex)
                            { IssueModel.Add("Read failed while verifying audio MD5: " + ex.Message, Severity.Fatal); }

                            if (Data.IsBadDataMD5)
                            {
                                Data.CmIssue = IssueModel.Add("MD5 check failed on audio data.", Severity.Error, IssueTags.Failure);
                            }
                            else
                            {
                                Data.CmIssue = IssueModel.Add("MD5 check successful on audio data.", Severity.Noise, IssueTags.Success);
                            }
                        }
                    }
                }

                if ((hashFlags & (Hashes.PcmCRC32 | Hashes._FlacMatch)) != 0 && Data.ActualPcmCRC32 == null)
                {
                    Process px = null;
                    try
                    { px = StartFlac(Data.Path); }
                    catch (Exception ex)
                    { IssueModel.Add("flac executable failed with '" + ex.Message.Trim(null) + "'."); }

                    if (px != null)
                    {
                        using (var br = new BinaryReader(px.StandardOutput.BaseStream))
                        {
                            var hasher = new Crc32rHasher();
                            hasher.Append(br);
                            byte[] hash = hasher.GetHashAndReset();
                            Data.ActualPcmCRC32 = BitConverter.ToUInt32(hash, 0);
                        }
                    }
                }

                if ((hashFlags & (Hashes._FlacTags)) != 0)
                {
                    TagCheckNumber("TRACKNUMBER");
                    TagCheckNumber("TRACKTOTAL", optional: true);
                    TagCheckNumber("DISCNUMBER", true);
                    TagCheckNumber("DISCTOTAL", true);
                    TagCheckDate("DATE");
                    TagCheckDate("RELEASE DATE", optional: true);
                    TagCheckDate("ORIGINAL RELEASE DATE", true);
                    TagCheckText("TITLE");
                    TagCheckText("ARTIST");
                    TagCheckText("ALBUM");
                    TagCheckText("ALBUMARTIST", optional: true);
                    TagCheckText("ALBUMARTISTSORTORDER", true);
                    TagCheckText("ORGANIZATION", true);
                    TagCheckText("BARCODE", true);
                    TagCheckText("CATALOGNUMBER", true);
                }

                base.CalcHashes(hashFlags, validationFlags);
            }
示例#28
0
            private void GetDiagnostics()
            {
                if (Data.MetadataBlockStreamInfoSize != 0x22)
                {
                    IssueModel.Add($"Unexpected Metadata block size of {Data.MetadataBlockStreamInfoSize}", Severity.Advisory);
                }

                if (Data.MinBlockSize < 16)
                {
                    IssueModel.Add("Minimum block size too low");
                }

                if (Data.MinBlockSize > 65535)
                {
                    IssueModel.Add("Maximum block size too high");
                }

                if (Data.RawSampleRate == 0xF)
                {
                    IssueModel.Add("Invalid sample rate");
                }

                if (Data.RawSampleSize == 3 || Data.RawSampleSize == 7)
                {
                    IssueModel.Add($"Use of sample size index {Data.RawSampleSize} is reserved");
                }

                if (Data.RawChannelAssignment >= 0xB)
                {
                    IssueModel.Add($"Use of reserved (undefined) channel assignment {Data.RawChannelAssignment}", Severity.Warning);
                }

                if (Data.RawBlockSize == 0)
                {
                    IssueModel.Add("Block size index 0 use is reserved", Severity.Warning);
                }

                if (Data.RawSampleRate == 0xF)
                {
                    IssueModel.Add("Sample rate index 15 use is invalid");
                }

                if (Data.RawChannelAssignment >= 0xB)
                {
                    IssueModel.Add($"Channel index {Data.RawChannelAssignment} use is reserved", Severity.Warning);
                }

                if (Data.Blocks.Tags.Lines.Count != Data.Blocks.Tags.StoredTagCount)
                {
                    IssueModel.Add("Stored tag count wrong");
                }

                foreach (var lx in Data.Blocks.Tags.Lines)
                {
                    if (lx.IndexOf('=') < 0)
                    {
                        IssueModel.Add("Invalid tag line: " + lx);
                    }

                    // U+FFFD is substituted by .NET when malformed utf8 encountered.
                    if (lx.Contains('\uFFFD'))
                    {
                        IssueModel.Add("Tag with malformed UTF-8 character encoding: " + lx);
                    }

                    if (lx.Any(cu => Char.IsSurrogate(cu)))
                    {
                        IssueModel.Add("Tag contains character(s) beyond the basic multilingual plane (may cause player issues): " + lx, Severity.Trivia);
                    }
                }

                int picPlusPadSize = Data.Blocks.Items.Where(b => b.BlockType == FlacBlockType.Padding || b.BlockType == FlacBlockType.Picture).Sum(b => b.Size);
                int bloat          = picPlusPadSize - MaxPicPlusPadSize;

                if (bloat > 0)
                {
                    var msg = $"Artwork+padding consume {picPlusPadSize} bytes.";

                    repairPadSize = Data.Blocks.PadBlock.Size - bloat;
                    if (repairPadSize < 0)
                    {
                        IssueModel.Add(msg, Severity.Trivia, IssueTags.StrictErr);
                    }
                    else
                    {
                        if (repairPadSize > 4096)
                        {
                            repairPadSize = 4096;
                        }
                        IssueModel.Add(msg, Severity.Trivia, IssueTags.StrictWarn,
                                       $"Trim {Data.Blocks.PadBlock.Size-repairPadSize} bytes of excess padding",
                                       RepairArtPadBloat);
                    }
                }
            }
示例#29
0
            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();
            }
示例#30
0
            public Model(Stream stream, string path)
            {
                base._data = Data = new Mp3Format(this, stream, path);

                if (Data.FileSize > Int32.MaxValue)
                {
                    IssueModel.Add("File is insanely large", Severity.Fatal);
                    return;
                }

                int mediaPos32;
                int mediaCount32;

                byte[] fBuf;

                fBuf = Data.fBuf = new byte[Data.FileSize];
                Data.fbs.Position = 0;
                if (Data.fbs.Read(fBuf, 0, (int)Data.FileSize) != Data.FileSize)
                {
                    IssueModel.Add("Read error", Severity.Fatal);
                    return;
                }

                // Detect ID3v2 tag block.
                if (fBuf[0] == 'I' && fBuf[1] == 'D' && fBuf[2] == '3')
                {
                    Data.id3v2Pos = 0;
                    if (Data.FileSize < 10)
                    {
                        IssueModel.Add("File truncated near ID3v2 header", Severity.Fatal);
                        return;
                    }

                    Data.Id3v2Major    = Data.fBuf[3];
                    Data.Id3v2Revision = Data.fBuf[4];

                    Data.storedId3v2DataSize = ((fBuf[6] & 0x7F) << 21) + ((fBuf[7] & 0x7F) << 14) + ((fBuf[8] & 0x7F) << 7) + (fBuf[9] & 0x7F);
                    if (Data.storedId3v2DataSize == 0 || Data.storedId3v2DataSize + 12 > Data.FileSize)
                    {
                        IssueModel.Add("ID3v2 size of " + Data.storedId3v2DataSize + " is bad or file is truncated", Severity.Fatal);
                        return;
                    }

                    if ((fBuf[6] & 0x80) != 0 || (fBuf[7] & 0x80) != 0 || (fBuf[8] & 0x80) != 0 || (Data.fBuf[9] & 0x80) != 0)
                    {
                        IssueModel.Add("Zero parity error on ID3v2 size");
                    }

                    Data.actualId3v2DataSize = Data.storedId3v2DataSize;

                    // Check for bug in old versions of EAC that 1.5% of time writes ID3v2 size over by 1.
                    if (Data.Id3v2Major == 3)
                    {
                        if ((fBuf[10 + Data.storedId3v2DataSize] & 0xFE) == 0xFA)
                        {
                            if (fBuf[9 + Data.storedId3v2DataSize] == 0xFF)
                            {
                                --Data.actualId3v2DataSize;
                                // Bug bite comfirmed.
                                Data.Id3v2TagRepair = String.Format("0009=0x{0:X2}", Data.actualId3v2DataSize & 0x7F);
                                if ((Data.actualId3v2DataSize & 0x7F) == 0x7F)
                                {
                                    Data.Id3v2TagRepair = String.Format("0008=0x{0:X2}, ", ((Data.actualId3v2DataSize >> 7) & 0x7F)) + Data.Id3v2TagRepair;
                                }
                            }
                        }
                    }

                    Data.ValidSize = 10 + Data.actualId3v2DataSize;
                }

                while (Data.ValidSize < Data.FileSize && fBuf[Data.ValidSize] == 0)
                {
                    ++Data.ValidSize;
                    ++Data.DeadBytes;
                }

                if (Data.FileSize - Data.ValidSize < 0xC0)
                {
                    IssueModel.Add("File appears truncated.", Severity.Fatal);
                    return;
                }

                Data.Header = new Mp3Header(fBuf, (int)Data.ValidSize);

                if (!Data.Header.IsMpegLayer3)
                {
                    IssueModel.Add("ID3v2 tag present but no MP3 marker found.", Severity.Fatal);
                    return;
                }

                if (Data.Header.MpegVersionBits == 1)
                {
                    IssueModel.Add("MP3 marker found but MPEG version is not valid.", Severity.Fatal);
                    return;
                }

                mediaPos32     = (int)Data.ValidSize;
                Data.ValidSize = Data.FileSize;

                // Keep the audio header.
                Data.aBuf = new byte[Data.Header.XingOffset + 0x9C];
                Array.Copy(fBuf, mediaPos32, Data.aBuf, 0, Data.aBuf.Length);

                // Detect Xing/LAME encodes:

                XingModel = Mp3XingBlock.Create(Data.aBuf, Data.Header);
                if (XingModel != null)
                {
                    LameModel = XingModel as Mp3LameBlock.Model;
                    Data.Xing = XingModel.Data;
                    Data.Lame = Data.Xing as Mp3LameBlock;
                }

                // Detect ID3v1 tag block:

                int ePos = (int)Data.FileSize;

                if (ePos >= 130 && fBuf[ePos - 128] == 'T' && fBuf[ePos - 127] == 'A' && fBuf[ePos - 126] == 'G')
                {
                    ePos           -= 128;
                    Data.id3v1Block = new byte[128];
                    Array.Copy(fBuf, ePos, Data.id3v1Block, 0, 128);
                }

                // Detect obsolete Lyrics3v2 block:

                if (ePos >= 15)
                {
                    if (fBuf[ePos - 9] == 'L' && fBuf[ePos - 8] == 'Y' && fBuf[ePos - 7] == 'R' && fBuf[ePos - 6] == 'I' && fBuf[ePos - 5] == 'C' && fBuf[ePos - 4] == 'S' &&
                        fBuf[ePos - 3] == '2' && fBuf[ePos - 2] == '0' && fBuf[ePos - 1] == '0')
                    {
                        int l3size = 0;
                        for (var bi = ePos - 15; bi < ePos - 9; ++bi)
                        {
                            if (fBuf[bi] < '0' || fBuf[bi] > '9')
                            {
                                IssueModel.Add("Invalid Lyrics3v2 length digit.");
                                return;
                            }
                            l3size = l3size * 10 + fBuf[bi] - '0';
                        }

                        Data.Lyrics3Size = l3size + 15;
                        ePos            -= Data.Lyrics3Size;
                        if (ePos < 2)
                        {
                            IssueModel.Add("Invalid Lyrics3v2 length.");
                            return;
                        }
                    }
                }

                // Detect APE tag block:

                if (ePos >= 34)
                {
                    int pos = ePos - 32;
                    if (fBuf[pos] == 'A' && fBuf[pos + 1] == 'P' && fBuf[pos + 2] == 'E' &&
                        fBuf[pos + 3] == 'T' && fBuf[pos + 4] == 'A' && fBuf[pos + 5] == 'G' && fBuf[pos + 6] == 'E' && fBuf[pos + 7] == 'X')
                    {
                        Data.ApeSize = 32 + fBuf[pos + 12] + (fBuf[pos + 13] << 8) + (fBuf[pos + 14] << 16) + (fBuf[pos + 15] << 24);
                        ePos        -= Data.ApeSize;
                    }
                }

                if (ePos <= mediaPos32)
                {
                    IssueModel.Add("Missing audio", Severity.Fatal);
                    return;
                }

                mediaCount32 = ePos - mediaPos32;

                // Detect 2nd, phantom ID3v1 tag block:

                if (Data.Lame != null && mediaCount32 == Data.Lame.LameSize + 128 && Data.HasId3v1 && !Data.HasLyrics3 && !Data.HasApe && ePos > 34)
                {
                    if (fBuf[ePos - 128] == 'T' && fBuf[ePos - 127] == 'A' && fBuf[ePos - 126] == 'G')
                    {
                        ePos           -= 128;
                        mediaCount32   -= 128;
                        Data.ValidSize -= 128;

                        Data.excess     = Data.id3v1Block;
                        Data.id3v1Block = new byte[128];
                        Array.Copy(fBuf, ePos, Data.id3v1Block, 0, 128);
                        Data.Watermark = Likeliness.Probable;
                    }
                }

                Data.mediaPosition = mediaPos32;
                Data.MediaCount    = mediaCount32;

                GetDiagnostics();
            }