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(); }
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); } }