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(); }
protected void ComputeContentHashes(CryptoHasher hasher, Hashes mediaHash = Hashes.None) { System.Diagnostics.Debug.Assert(Data.HashedFiles.HashLength == hasher.HashLength); for (int index = 0; index < Data.HashedFiles.Items.Count; ++index) { HashedFile item = Data.HashedFiles.Items[index]; string msg = null; var targetName = Data.HashedFiles.GetPath(index); try { using (var tfs = new FileStream(targetName, FileMode.Open, FileAccess.Read)) { HashedModel.SetIsFound(index, true); byte[] hash = null; if (item.Style == HashStyle.Media) { if (mediaHash == Hashes.None) { IssueModel.Add("Unexpected media hash on item " + (index + 1) + "."); } else { var hdr = new byte[0x3F]; tfs.Position = 0; int got = tfs.Read(hdr, 0, hdr.Length); Mp3Format.Model fmt = Mp3Format.CreateModel(tfs, hdr, targetName); if (fmt == null) { // Only MP3 supported for now. IssueModel.Add("Unexpected file format."); } else { fmt.CalcHashes(mediaHash, Validations.None); fmt.CloseFile(); hash = fmt.Data.MediaSHA1; } } } else { hasher.Append(tfs); hash = hasher.GetHashAndReset(); } HashedModel.SetActualHash(index, hash); if (item.IsMatch == false) { IssueModel.Add(Data.HasherName + " mismatch on '" + item.FileName + "'."); } } } catch (FileNotFoundException ex) { msg = ex.Message.TrimEnd(null); } catch (IOException ex) { msg = ex.Message.TrimEnd(null); } catch (UnauthorizedAccessException ex) { msg = ex.Message.TrimEnd(null); } if (msg != null) { HashedModel.SetIsFound(index, false); IssueModel.Add(msg); } } string tx = Data.HasherName + " validation of " + Data.HashedFiles.Items.Count + " file"; if (Data.HashedFiles.Items.Count != 1) { tx += "s"; } if (base.Data.Issues.MaxSeverity < Severity.Error) { tx += " successful."; } else { tx += " failed with " + Data.HashedFiles.FoundCount + " found and " + Data.HashedFiles.MatchCount + " matched."; } IssueModel.Add(tx, Severity.Advisory); }