예제 #1
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();
            }
예제 #2
0
            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);
                }
            }