// Read header data private void readHeader(BinaryReader source) { source.BaseStream.Seek(4, SeekOrigin.Begin); // File size int magicNumber = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); if (magicNumber != AA_MAGIC_NUMBER) { return; } isValid = true; int tocSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); source.BaseStream.Seek(4, SeekOrigin.Current); // Even FFMPeg doesn't know what this integer is // The table of contents describes the layout of the file as triples of integers (<section>, <offset>, <length>) toc = new Dictionary <int, Tuple <long, long> >(); for (int i = 0; i < tocSize; i++) { int section = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); long offset = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); long size = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); Tuple <long, long> data = new Tuple <long, long>(offset, size); toc[section] = data; if (TOC_AUDIO == section) { audioSize = size; } } }
/// <summary> /// Reads ID and size of a local chunk and returns them in a dedicated structure _without_ reading nor skipping the data /// </summary> /// <param name="source">Source where to read header information</param> /// <returns>Local chunk header information</returns> private ChunkHeader seekNextChunkHeader(BinaryReader source, long limit) { ChunkHeader header = new ChunkHeader(); byte[] aByte = new byte[1]; source.BaseStream.Read(aByte, 0, 1); // In case previous field size is not correctly documented, tries to advance to find a suitable first character for an ID while (!((aByte[0] == 40) || ((64 < aByte[0]) && (aByte[0] < 91))) && source.BaseStream.Position < limit) { source.BaseStream.Read(aByte, 0, 1); } if (source.BaseStream.Position < limit) { source.BaseStream.Seek(-1, SeekOrigin.Current); // Chunk ID header.ID = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); // Chunk size header.Size = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); } else { header.ID = ""; } return(header); }
/// <summary> /// Read WMA-like formatted fields starting at the given reader's current position, and stopping after the given size /// </summary> /// <param name="source">Source to read the fields from</param> /// <param name="atomDataSize">Max size of the zone to read</param> /// <returns>List of the detected metadata fields</returns> public static IList <KeyValuePair <string, string> > ReadFields(BinaryReader source, long atomDataSize) { IList <KeyValuePair <string, string> > result = new List <KeyValuePair <string, string> >(); long initialPos = source.BaseStream.Position; long pos = initialPos; while (pos < initialPos + atomDataSize) { int fieldSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); int stringDataSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); string fieldName = Utils.Latin1Encoding.GetString(source.ReadBytes(stringDataSize)); source.BaseStream.Seek(4, SeekOrigin.Current); stringDataSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); string fieldValue; int fieldType = StreamUtils.DecodeBEInt16(source.ReadBytes(2)); if (19 == fieldType) // Numeric { fieldValue = source.ReadInt64() + ""; } else { fieldValue = Utils.StripEndingZeroChars(Encoding.Unicode.GetString(source.ReadBytes(stringDataSize - 6))); } result.Add(new KeyValuePair <string, string>(fieldName, fieldValue)); source.BaseStream.Seek(pos + fieldSize, SeekOrigin.Begin); pos += fieldSize; } return(result); }
private static int readInt32(BinaryReader r, bool isBigEndian) { if (isBigEndian) { return(StreamUtils.DecodeBEInt32(r.ReadBytes(4))); } else { return(r.ReadInt32()); } }
private void readCover(BinaryReader source, long offset, PictureInfo.PIC_TYPE pictureType) { source.BaseStream.Seek(offset, SeekOrigin.Begin); int pictureSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); int picOffset = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); source.BaseStream.Seek(picOffset, SeekOrigin.Begin); PictureInfo picInfo = PictureInfo.fromBinaryData(source.BaseStream, pictureSize, pictureType, getImplementedTagType(), TOC_COVER_ART); tagData.Pictures.Add(picInfo); }
// Read header data private void readHeader(BinaryReader source) { uint fileSize = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); int magicNumber = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); if (magicNumber != AA_MAGIC_NUMBER) { return; } tagExists = true; AudioDataOffset = source.BaseStream.Position - 4; int tocSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); source.BaseStream.Seek(4, SeekOrigin.Current); // Even FFMPeg doesn't know what this integer is // The table of contents describes the layout of the file as triples of integers (<section>, <offset>, <length>) toc = new Dictionary <int, Tuple <uint, uint> >(); for (int i = 0; i < tocSize; i++) { int section = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); uint tocEntryOffset = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); uint tocEntrySize = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); Tuple <uint, uint> data = new Tuple <uint, uint>(tocEntryOffset, tocEntrySize); toc[section] = data; structureHelper.AddZone(tocEntryOffset, (int)tocEntrySize, section.ToString()); structureHelper.AddIndex(source.BaseStream.Position - 8, tocEntryOffset, false, section.ToString()); if (TOC_AUDIO == section) { AudioDataOffset = tocEntryOffset; AudioDataSize = tocEntrySize; } if (TOC_CONTENT_TAGS == section) { structureHelper.AddSize(source.BaseStream.Position - 4, tocEntrySize, section.ToString()); structureHelper.AddSize(0, fileSize, section.ToString()); } if (TOC_COVER_ART == section) { structureHelper.AddSize(source.BaseStream.Position - 4, tocEntrySize, section.ToString()); structureHelper.AddIndex(source.BaseStream.Position - 8, tocEntryOffset, false, section.ToString()); structureHelper.AddSize(0, fileSize, section.ToString()); } } }
private void readTags(BinaryReader source, long offset, long size, ReadTagParams readTagParams) { source.BaseStream.Seek(offset, SeekOrigin.Begin); int nbTags = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); for (int i = 0; i < nbTags; i++) { source.BaseStream.Seek(1, SeekOrigin.Current); // No idea what this byte is int keyLength = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); int valueLength = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); string key = Encoding.UTF8.GetString(source.ReadBytes(keyLength)); string value = Encoding.UTF8.GetString(source.ReadBytes(valueLength)).Trim(); SetMetaField(key, value, readTagParams.ReadAllMetaFrames); if ("codec".Equals(key)) { codec = value; } } }
// ---------- SPECIFIC MEMBERS public static VorbisMetaDataBlockPicture ReadMetadataBlockPicture(Stream s) { VorbisMetaDataBlockPicture result = new VorbisMetaDataBlockPicture(); int stringLen; BinaryReader r = new BinaryReader(s); result.nativePicCode = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.picType = ID3v2.DecodeID3v2PictureType(result.nativePicCode); stringLen = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.mimeType = Utils.Latin1Encoding.GetString(r.ReadBytes(stringLen)); stringLen = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.description = Encoding.UTF8.GetString(r.ReadBytes(stringLen)); result.width = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.height = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.colorDepth = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.colorNum = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.picDataLength = StreamUtils.DecodeBEInt32(r.ReadBytes(4)); result.picDataOffset = 4 + 4 + result.mimeType.Length + 4 + result.description.Length + 4 + 4 + 4 + 4 + 4; return(result); }
/// <inheritdoc/> protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams) { byte[] header; string trigger; IList <MidiTrack> tracks = new List <MidiTrack>(); resetData(); // Ignores everything (comments) written before the MIDI header StreamUtils.FindSequence(source.BaseStream, Utils.Latin1Encoding.GetBytes(MIDI_FILE_HEADER)); // Ready to read header data... header = source.ReadBytes(10); if ((header[0] != 0) || (header[1] != 0) || (header[2] != 0) || (header[3] != 6) ) { Logging.LogDelegator.GetLogDelegate()(Log.LV_ERROR, "Wrong MIDI header"); return(false); } type = header[5]; // MIDI STRUCTURE TYPE // 0 - single-track // 1 - multiple tracks, synchronous // 2 - multiple tracks, asynchronous if (type > 1) { Logging.LogDelegator.GetLogDelegate()(Log.LV_WARNING, "SMF type 2 MIDI files are partially supported; results may be approximate"); } tagExists = true; timebase = (header[8] << 8) + header[9]; tempo = 0; // maybe (hopefully!) overwritten by parseTrack int trackSize; int nbTrack = 0; comment = new StringBuilder(""); AudioDataOffset = source.BaseStream.Position; AudioDataSize = sizeInfo.FileSize - AudioDataOffset; // Ready to read track data... while (source.BaseStream.Position < sizeInfo.FileSize - 4) { trigger = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); if (trigger != MIDI_TRACK_HEADER) { source.BaseStream.Seek(-3, SeekOrigin.Current); if (!StreamUtils.FindSequence(source.BaseStream, Utils.Latin1Encoding.GetBytes(MIDI_TRACK_HEADER))) { break; } } // trackSize is stored in big endian -> needs inverting trackSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); tracks.Add(parseTrack(source.ReadBytes(trackSize), nbTrack)); nbTrack++; } this.tracks = tracks; if (comment.Length > 0) { comment.Remove(comment.Length - 1, 1); } tagData.IntegrateValue(TagData.TAG_FIELD_COMMENT, comment.ToString()); duration = getDuration(); bitrate = (double)sizeInfo.FileSize / duration; return(true); }
// ---------- SUPPORT METHODS private bool readWAV(Stream source, ReadTagParams readTagParams) { bool result = true; uint riffChunkSize; long riffChunkSizePos; byte[] data = new byte[4]; source.Seek(0, SeekOrigin.Begin); // Read header source.Read(data, 0, 4); string str = Utils.Latin1Encoding.GetString(data); if (str.Equals(HEADER_RIFF)) { _isLittleEndian = true; } else if (str.Equals(HEADER_RIFX)) { _isLittleEndian = false; } else { return(false); } // Force creation of FileStructureHelper with detected endianness structureHelper = new FileStructureHelper(isLittleEndian); id3v2StructureHelper = new FileStructureHelper(isLittleEndian); riffChunkSizePos = source.Position; source.Read(data, 0, 4); if (isLittleEndian) { riffChunkSize = StreamUtils.DecodeUInt32(data); } else { riffChunkSize = StreamUtils.DecodeBEUInt32(data); } // Format code source.Read(data, 0, 4); str = Utils.Latin1Encoding.GetString(data); if (!str.Equals(FORMAT_WAVE)) { return(false); } string subChunkId; uint chunkSize; long chunkDataPos; bool foundSample = false; bool foundBext = false; bool foundInfo = false; bool foundIXml = false; // Sub-chunks loop while (source.Position < riffChunkSize + 8) { // Chunk ID source.Read(data, 0, 4); if (0 == data[0]) // Sometimes data segment ends with a parasite null byte { source.Seek(-3, SeekOrigin.Current); source.Read(data, 0, 4); } subChunkId = Utils.Latin1Encoding.GetString(data); // Chunk size source.Read(data, 0, 4); if (isLittleEndian) { chunkSize = StreamUtils.DecodeUInt32(data); } else { chunkSize = StreamUtils.DecodeBEUInt32(data); } chunkDataPos = source.Position; if (subChunkId.Equals(CHUNK_FORMAT)) { source.Read(data, 0, 2); if (isLittleEndian) { formatId = StreamUtils.DecodeUInt16(data); } else { formatId = StreamUtils.DecodeBEUInt16(data); } source.Read(data, 0, 2); if (isLittleEndian) { channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeUInt16(data)); } else { channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeBEUInt16(data)); } source.Read(data, 0, 4); if (isLittleEndian) { sampleRate = StreamUtils.DecodeUInt32(data); } else { sampleRate = StreamUtils.DecodeBEUInt32(data); } source.Read(data, 0, 4); if (isLittleEndian) { bytesPerSecond = StreamUtils.DecodeUInt32(data); } else { bytesPerSecond = StreamUtils.DecodeBEUInt32(data); } source.Seek(2, SeekOrigin.Current); // BlockAlign source.Read(data, 0, 2); if (isLittleEndian) { bitsPerSample = StreamUtils.DecodeUInt16(data); } else { bitsPerSample = StreamUtils.DecodeBEUInt16(data); } } else if (subChunkId.Equals(CHUNK_DATA)) { headerSize = riffChunkSize - chunkSize; } else if (subChunkId.Equals(CHUNK_FACT)) { source.Read(data, 0, 4); if (isLittleEndian) { sampleNumber = StreamUtils.DecodeInt32(data); } else { sampleNumber = StreamUtils.DecodeBEInt32(data); } } else if (subChunkId.Equals(CHUNK_SAMPLE)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundSample = true; tagExists = true; SampleTag.FromStream(source, this, readTagParams); } else if (subChunkId.Equals(CHUNK_BEXT)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundBext = true; tagExists = true; BextTag.FromStream(source, this, readTagParams); } else if (subChunkId.Equals(CHUNK_INFO)) { // Purpose of the list should be INFO source.Read(data, 0, 4); string purpose = Utils.Latin1Encoding.GetString(data, 0, 4); if (purpose.Equals(InfoTag.PURPOSE_INFO)) { structureHelper.AddZone(source.Position - 12, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundInfo = true; tagExists = true; InfoTag.FromStream(source, this, readTagParams, chunkSize); } } else if (subChunkId.Equals(CHUNK_IXML)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundIXml = true; tagExists = true; IXmlTag.FromStream(source, this, readTagParams, chunkSize); } else if (subChunkId.Equals(CHUNK_ID3)) { id3v2Offset = source.Position; // Zone is already added by Id3v2.Read id3v2StructureHelper.AddZone(id3v2Offset - 8, (int)(chunkSize + 8), subChunkId); id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); } source.Seek(chunkDataPos + chunkSize, SeekOrigin.Begin); } // Add zone placeholders for future tag writing if (readTagParams.PrepareForWriting) { if (!foundSample) { structureHelper.AddZone(source.Position, 0, CHUNK_SAMPLE); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_SAMPLE); } if (!foundBext) { structureHelper.AddZone(source.Position, 0, CHUNK_BEXT); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_BEXT); } if (!foundInfo) { structureHelper.AddZone(source.Position, 0, CHUNK_INFO); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_INFO); } if (!foundIXml) { structureHelper.AddZone(source.Position, 0, CHUNK_IXML); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_IXML); } } // ID3 zone should be set as the very last one for Windows to be able to read the LIST INFO zone properly if (-1 == id3v2Offset) { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(source.Position, 0, CHUNK_ID3); id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_ID3); } } return(result); }
public static ImageProperties GetImageProperties(byte[] imageData, ImageFormat format = ImageFormat.Undefined) { ImageProperties props = new ImageProperties(); if (ImageFormat.Undefined.Equals(format)) { format = GetImageFormatFromPictureHeader(imageData); } if (format.Equals(ImageFormat.Unsupported)) { return(props); } props.NumColorsInPalette = 0; props.Format = format; using (MemoryStream s = new MemoryStream(imageData)) using (BinaryReader r = new BinaryReader(s)) { long limit = (long)Math.Round(s.Length * 0.25); // TODO - test and adjust limit switch (format) { case (ImageFormat.Tiff): bool isBigEndian = (0x4D == r.ReadByte()); s.Seek(3, SeekOrigin.Current); // Skip the rest of the signature long IFDOffset = readInt32(r, isBigEndian); s.Seek(IFDOffset, SeekOrigin.Begin); int nbIFDEntries = readInt16(r, isBigEndian); long initialPos = s.Position; int IFDtag, IFDFieldType, IFDNbValues, IFDValue; int photometricInterpretation = 0; int bitsPerSample = 0; int samplesPerPixel = 0; for (int i = 0; i < nbIFDEntries; i++) { IFDtag = readInt16(r, isBigEndian); IFDFieldType = readInt16(r, isBigEndian); IFDNbValues = readInt32(r, isBigEndian); IFDValue = readInt32(r, isBigEndian); switch (IFDtag) { // Common properties case (0x0100): props.Width = IFDValue; break; case (0x0101): props.Height = IFDValue; break; // Specific properties case (0x0106): // PhotometricInterpretation photometricInterpretation = IFDValue; if (IFDValue < 2) { props.ColorDepth = 1; // Bilevel image } else if (2 == IFDValue) { props.ColorDepth = 24; // RGB full color image } // NB : A value of 3 would indicate a palette-color image, but has no effect here break; case (0x0102): // BitsPerSample bitsPerSample = IFDValue; break; case (0x0115): // SamplesPerPixel samplesPerPixel = IFDValue; break; } } if (photometricInterpretation < 2) // Bilevel { props.ColorDepth = bitsPerSample; } else if (2 == photometricInterpretation) // RGB { props.ColorDepth = 8 * samplesPerPixel; } else if (3 == photometricInterpretation) // Palette { props.ColorDepth = 8 * samplesPerPixel; props.NumColorsInPalette = bitsPerSample; } break; case (ImageFormat.Gif): byte[] GraphicControlExtensionBlockSignature = new byte[2] { 0x21, 0xf9 }; props.ColorDepth = 24; // 1 byte for each component s.Seek(3, SeekOrigin.Current); // Skip GIF signature string version = Utils.Latin1Encoding.GetString(r.ReadBytes(3)); s.Seek(4, SeekOrigin.Current); // Skip logical screen descriptors byte globalPaletteUse = r.ReadByte(); if (((globalPaletteUse & 0x80) >> 7) > 0) // File uses a global color palette { props.NumColorsInPalette = 2 << (globalPaletteUse & 0x07); } /* * v89a means that the first image block should follow the first graphic control extension block * (which may in turn be located after an application extension block if the GIF is animated) * * => The simplest way to get to the 1st image block is to look for the 1st * graphic control extension block, and to skip it */ if ("89a".Equals(version)) { initialPos = s.Position; if (StreamUtils.FindSequence(s, GraphicControlExtensionBlockSignature)) { s.Seek(6, SeekOrigin.Current); } else { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid v89a GIF file; no graphic control extension block found"); // GIF is malformed; trying to find the image block directly s.Seek(initialPos, SeekOrigin.Begin); if (StreamUtils.FindSequence(s, new byte[1] { 0x2c })) { s.Seek(-1, SeekOrigin.Current); } } } // At this point, we should be at the very beginning of the first image block if (0x2c == r.ReadByte()) { s.Seek(4, SeekOrigin.Current); // Skip image position descriptors props.Width = r.ReadInt16(); props.Height = r.ReadInt16(); // No global palette is set => try and find information in the local palette of the 1st image block if (0 == props.NumColorsInPalette) { props.NumColorsInPalette = (int)Math.Pow(2, ((globalPaletteUse & 0x0F) << 4) + 1); } } else { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Error parsing GIF file; image block not found"); } break; case (ImageFormat.Bmp): // Skip useless information s.Seek(18, SeekOrigin.Begin); props.Width = r.ReadInt32(); props.Height = r.ReadInt32(); s.Seek(2, SeekOrigin.Current); // Planes props.ColorDepth = r.ReadInt16(); // No support for BMP color palettes, as they seem to be exotic (and ATL has no use of this information) break; case (ImageFormat.Png): byte[] intData = new byte[4]; byte[] IHDRChunkSignature = Utils.Latin1Encoding.GetBytes("IHDR"); byte[] PaletteChunkSignature = Utils.Latin1Encoding.GetBytes("PLTE"); // Skip header s.Seek(8, SeekOrigin.Begin); // Scroll chunks until we find IHDR (that should be the first one to appear, but who knows...) if (0 == findPngChunk(s, IHDRChunkSignature, limit)) { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid PNG file; no IHDR chunk found"); } else { // Read IHDR chunk s.Read(intData, 0, 4); props.Width = StreamUtils.DecodeBEInt32(intData); s.Read(intData, 0, 4); props.Height = StreamUtils.DecodeBEInt32(intData); props.ColorDepth = r.ReadByte(); int colorType = r.ReadByte(); if (3 == colorType) // PNG file uses a palette { s.Seek(7, SeekOrigin.Current); // 3 last useful data + ending chunk CRC uint paletteChunkSize = findPngChunk(s, PaletteChunkSignature, limit); if (0 == paletteChunkSize) { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid PNG file; palette declared, but no PLTE chunk found"); } else { props.NumColorsInPalette = (int)Math.Floor(paletteChunkSize / 3.0); } } else { props.NumColorsInPalette = 0; } } break; case (ImageFormat.Jpeg): byte[] shortData = new byte[2]; byte[] SOF0FrameSignature = new byte[2] { 0xFF, 0xC0 }; /* * We just need to reach the SOF0 frame descripting the actual picture * * In order to handle JPEG files that contain multiple SOF0 frames (see test suite), * the simplest way of proceeding is to look for all SOF0 frames in the first 25% of the file, * and then read the very last one */ long lastPos = 0; while (StreamUtils.FindSequence(s, SOF0FrameSignature, limit)) { lastPos = s.Position; } if (0 == lastPos) { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid JPEG file; no SOF0 frame found"); } else { // Skip frame length s.Seek(2, SeekOrigin.Current); bitsPerSample = r.ReadByte(); s.Read(shortData, 0, 2); props.Height = StreamUtils.DecodeBEUInt16(shortData); s.Read(shortData, 0, 2); props.Width = StreamUtils.DecodeBEUInt16(shortData); byte nbComponents = r.ReadByte(); props.ColorDepth = bitsPerSample * nbComponents; } break; } } return(props); }
private static Int32 readInt32(BinaryReader r, Boolean isBigEndian) { return(isBigEndian ? StreamUtils.DecodeBEInt32(r.ReadBytes(4)) : r.ReadInt32()); }
protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams) { bool result = false; long position; resetData(); source.BaseStream.Seek(0, SeekOrigin.Begin); if (AIFF_CONTAINER_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { // Container chunk size long containerChunkPos = source.BaseStream.Position; int containerChunkSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); if (containerChunkPos + containerChunkSize + 4 != source.BaseStream.Length) { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Header size is incoherent with file size"); } // Form type format = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); if (format.Equals(FORMTYPE_AIFF) || format.Equals(FORMTYPE_AIFC)) { isValid = true; StringBuilder commentStr = new StringBuilder(""); long soundChunkPosition = 0; long soundChunkSize = 0; // Header size included bool nameFound = false; bool authorFound = false; bool copyrightFound = false; bool commentsFound = false; long limit = Math.Min(containerChunkPos + containerChunkSize + 4, source.BaseStream.Length); int annotationIndex = 0; int commentIndex = 0; while (source.BaseStream.Position < limit) { ChunkHeader header = seekNextChunkHeader(source, limit); position = source.BaseStream.Position; if (header.ID.Equals(CHUNKTYPE_COMMON)) { short channels = StreamUtils.DecodeBEInt16(source.ReadBytes(2)); switch (channels) { case 1: channelsArrangement = MONO; break; case 2: channelsArrangement = STEREO; break; case 3: channelsArrangement = ISO_3_0_0; break; case 4: channelsArrangement = ISO_2_2_0; break; // Specs actually allow both 2/2.0 and LRCS case 6: channelsArrangement = LRLcRcCS; break; default: channelsArrangement = UNKNOWN; break; } numSampleFrames = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); sampleSize = (uint)StreamUtils.DecodeBEInt16(source.ReadBytes(2)); // This sample size is for uncompressed data only byte[] byteArray = source.ReadBytes(10); Array.Reverse(byteArray); double aSampleRate = StreamUtils.ExtendedToDouble(byteArray); if (format.Equals(FORMTYPE_AIFC)) { compression = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); } else // AIFF <=> no compression { compression = COMPRESSION_NONE; } if (aSampleRate > 0) { sampleRate = (int)Math.Round(aSampleRate); duration = (double)numSampleFrames * 1000.0 / sampleRate; if (!compression.Equals(COMPRESSION_NONE)) // Sample size is specific to selected compression method { if (compression.ToLower().Equals("fl32")) { sampleSize = 32; } else if (compression.ToLower().Equals("fl64")) { sampleSize = 64; } else if (compression.ToLower().Equals("alaw")) { sampleSize = 8; } else if (compression.ToLower().Equals("ulaw")) { sampleSize = 8; } } if (duration > 0) { bitrate = sampleSize * numSampleFrames * channelsArrangement.NbChannels / duration; } } } else if (header.ID.Equals(CHUNKTYPE_SOUND)) { soundChunkPosition = source.BaseStream.Position - 8; soundChunkSize = header.Size + 8; } else if (header.ID.Equals(CHUNKTYPE_NAME) || header.ID.Equals(CHUNKTYPE_AUTHOR) || header.ID.Equals(CHUNKTYPE_COPYRIGHT)) { structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID); tagExists = true; if (header.ID.Equals(CHUNKTYPE_NAME)) { nameFound = true; } if (header.ID.Equals(CHUNKTYPE_AUTHOR)) { authorFound = true; } if (header.ID.Equals(CHUNKTYPE_COPYRIGHT)) { copyrightFound = true; } SetMetaField(header.ID, Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size)), readTagParams.ReadAllMetaFrames); } else if (header.ID.Equals(CHUNKTYPE_ANNOTATION)) { annotationIndex++; structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + annotationIndex); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + annotationIndex); if (commentStr.Length > 0) { commentStr.Append(Settings.InternalValueSeparator); } commentStr.Append(Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size))); tagExists = true; } else if (header.ID.Equals(CHUNKTYPE_COMMENTS)) { commentIndex++; structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + commentIndex); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + commentIndex); tagExists = true; commentsFound = true; ushort numComs = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)); for (int i = 0; i < numComs; i++) { CommentData cmtData = new CommentData(); cmtData.Timestamp = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); cmtData.MarkerId = StreamUtils.DecodeBEInt16(source.ReadBytes(2)); // Comments length ushort comLength = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)); MetaFieldInfo comment = new MetaFieldInfo(getImplementedTagType(), header.ID + commentIndex); comment.Value = Utils.Latin1Encoding.GetString(source.ReadBytes(comLength)); comment.SpecificData = cmtData; tagData.AdditionalFields.Add(comment); // Only read general purpose comments, not those linked to a marker if (0 == cmtData.MarkerId) { if (commentStr.Length > 0) { commentStr.Append(Settings.InternalValueSeparator); } commentStr.Append(comment.Value); } } } else if (header.ID.Equals(CHUNKTYPE_ID3TAG)) { id3v2Offset = source.BaseStream.Position; // Zone is already added by Id3v2.Read id3v2StructureHelper.AddZone(id3v2Offset - 8, header.Size + 8, CHUNKTYPE_ID3TAG); id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG); } source.BaseStream.Position = position + header.Size; if (header.ID.Equals(CHUNKTYPE_SOUND) && header.Size % 2 > 0) { source.BaseStream.Position += 1; // Sound chunk size must be even } } tagData.IntegrateValue(TagData.TAG_FIELD_COMMENT, commentStr.ToString().Replace("\0", " ").Trim()); if (-1 == id3v2Offset) { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(soundChunkPosition + soundChunkSize, 0, CHUNKTYPE_ID3TAG); id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG); } } // Add zone placeholders for future tag writing if (readTagParams.PrepareForWriting) { if (!nameFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_NAME); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_NAME); } if (!authorFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_AUTHOR); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_AUTHOR); } if (!copyrightFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COPYRIGHT); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COPYRIGHT); } if (!commentsFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COMMENTS); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COMMENTS); } } result = true; } } return(result); }
protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams) { bool result = true; int maxPatterns = -1; byte nbSamples; string readString; StringBuilder comment = new StringBuilder(""); Sample sample; IList <IList <int> > pattern; IList <int> row; resetData(); // == TITLE == readString = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); if (readString.Equals(SIG_POWERPACKER)) { throw new InvalidDataException("MOD files compressed with PowerPacker are not supported yet"); } tagExists = true; // Restart from beginning, else parser might miss empty titles source.BaseStream.Seek(0, SeekOrigin.Begin); // Title = max first 20 chars; null-terminated string title = StreamUtils.ReadNullTerminatedStringFixed(source, System.Text.Encoding.ASCII, 20); if (readTagParams.PrepareForWriting) { structureHelper.AddZone(0, 20, new byte[20] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, ZONE_TITLE); } tagData.IntegrateValue(TagData.TAG_FIELD_TITLE, title.Trim()); AudioDataOffset = source.BaseStream.Position; AudioDataSize = sizeInfo.FileSize - AudioDataOffset; // == SAMPLES == nbSamples = detectNbSamples(source); string charOne = Utils.Latin1Encoding.GetString(new byte[] { 1 }); for (int i = 0; i < nbSamples; i++) { sample = new Sample(); sample.Name = StreamUtils.ReadNullTerminatedStringFixed(source, System.Text.Encoding.ASCII, 22).Trim(); sample.Name = sample.Name.Replace("\0", ""); sample.Name = sample.Name.Replace(charOne, ""); sample.Size = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)) * 2; sample.Finetune = source.ReadSByte(); sample.Volume = source.ReadByte(); sample.RepeatOffset = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)) * 2; sample.RepeatLength = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)) * 2; FSamples.Add(sample); } // == SONG == nbValidPatterns = source.ReadByte(); source.BaseStream.Seek(1, SeekOrigin.Current); // Controversial byte; no real use here for (int i = 0; i < 128; i++) { FPatternTable.Add(source.ReadByte()); // Pattern table } // File format tag formatTag = Utils.Latin1Encoding.GetString(source.ReadBytes(4)).Trim(); if (modFormats.ContainsKey(formatTag)) { nbChannels = modFormats[formatTag].NbChannels; trackerName = modFormats[formatTag].Name; } else // Default { nbChannels = NB_CHANNELS_DEFAULT; LogDelegator.GetLogDelegate()(Log.LV_WARNING, "MOD format tag '" + formatTag + "'not recognized"); } // == PATTERNS == // Some extra information about the "FLT8" -type MOD's: // // These MOD's have 8 channels, still the format isn't the same as the // other 8 channel formats ("OCTA", "CD81", "8CHN"): instead of storing // ONE 8-track pattern, it stores TWO 4-track patterns per logical pattern. // i.e. The first 4 channels of the first logical pattern are stored in // the first physical 4-channel pattern (size 1kb) whereas channel 5 until // channel 8 of the first logical pattern are stored as the SECOND physical // 4-channel pattern. Got it? ;-). // If you convert all the 4 channel patterns to 8 channel patterns, do not // forget to divide each pattern nr by 2 in the pattern sequence table! foreach (byte b in FPatternTable) { maxPatterns = Math.Max(maxPatterns, b); } for (int p = 0; p < maxPatterns + 1; p++) // Patterns loop { FPatterns.Add(new List <IList <int> >()); pattern = FPatterns[FPatterns.Count - 1]; // Rows loop for (int l = 0; l < MAX_ROWS; l++) { pattern.Add(new List <int>()); row = pattern[pattern.Count - 1]; for (int c = 0; c < nbChannels; c++) // Channels loop { row.Add(StreamUtils.DecodeBEInt32(source.ReadBytes(4))); } // end channels loop } // end rows loop } // end patterns loop // == Computing track properties duration = calculateDuration(); foreach (Sample aSample in FSamples) { if (aSample.Name.Length > 0) { comment.Append(aSample.Name).Append(Settings.InternalValueSeparator); } } if (comment.Length > 0) { comment.Remove(comment.Length - 1, 1); } tagData.IntegrateValue(TagData.TAG_FIELD_COMMENT, comment.ToString()); bitrate = sizeInfo.FileSize / duration; return(result); }
private static int readInt32(BinaryReader r, bool isBigEndian) { return isBigEndian ? StreamUtils.DecodeBEInt32(r.ReadBytes(4)) : r.ReadInt32(); }
public void StreamUtils_Exceptions() { Assert.IsFalse(StreamUtils.ArrEqualsArr(new byte[1], new byte[2])); Assert.IsFalse(StreamUtils.StringEqualsArr(".", new char[2])); try { StreamUtils.DecodeBEUInt16(new byte[1]); Assert.Fail(); } catch { } try { StreamUtils.DecodeUInt16(new byte[1]); Assert.Fail(); } catch { } try { StreamUtils.DecodeInt16(new byte[1]); Assert.Fail(); } catch { } try { StreamUtils.DecodeBEInt16(new byte[1]); Assert.Fail(); } catch { } try { StreamUtils.DecodeBEInt24(new byte[2]); Assert.Fail(); } catch { } try { StreamUtils.DecodeBEUInt24(new byte[2]); Assert.Fail(); } catch { } try { StreamUtils.EncodeBEUInt24(0x01FFFFFF); Assert.Fail(); } catch { } try { StreamUtils.DecodeBEUInt32(new byte[3]); Assert.Fail(); } catch { } try { StreamUtils.DecodeUInt32(new byte[3]); Assert.Fail(); } catch { } try { StreamUtils.DecodeBEInt32(new byte[3]); Assert.Fail(); } catch { } try { StreamUtils.DecodeInt32(new byte[3]); Assert.Fail(); } catch { } try { StreamUtils.DecodeUInt64(new byte[7]); Assert.Fail(); } catch { } try { StreamUtils.DecodeBEInt64(new byte[7]); Assert.Fail(); } catch { } try { StreamUtils.DecodeSynchSafeInt(new byte[6]); Assert.Fail(); } catch { } try { StreamUtils.DecodeSynchSafeInt32(new byte[6]); Assert.Fail(); } catch { } try { StreamUtils.EncodeSynchSafeInt(1, 0); Assert.Fail(); } catch { } try { StreamUtils.EncodeSynchSafeInt(1, 6); Assert.Fail(); } catch { } try { StreamUtils.ReadBits(new BinaryReader(new MemoryStream()), 0, 0); Assert.Fail(); } catch { } try { StreamUtils.ReadBits(new BinaryReader(new MemoryStream()), 0, 33); Assert.Fail(); } catch { } }