public AtomCallbackArgs(uint type, long size, Stream stream, long startPosition, SequentialStreamReader reader) { Type = type; Size = size; Stream = stream; StartPosition = startPosition; Reader = reader; }
/// <summary> /// Reads atom data from <paramref name="stream"/>, invoking <paramref name="handler"/> for each atom encountered. /// </summary> /// <param name="stream">The stream to read atoms from.</param> /// <param name="handler">A callback function to handle each atom.</param> /// <param name="stopByBytes">The maximum number of bytes to process before discontinuing.</param> public static void ProcessAtoms([NotNull] Stream stream, [NotNull] Action<AtomCallbackArgs> handler, long stopByBytes = -1) { var reader = new SequentialStreamReader(stream); var seriesStartPos = stream.Position; while (stopByBytes == -1 || stream.Position < seriesStartPos + stopByBytes) { var atomStartPos = stream.Position; // Length of the atom's data, in bytes, including size bytes long atomSize; try { atomSize = reader.GetUInt32(); } catch (IOException) { // TODO don't use exception to trap end of stream return; } // Typically four ASCII characters, but may be non-printable. // By convention, lowercase 4CCs are reserved by Apple. var atomType = reader.GetUInt32(); if (atomSize == 1) { // Size doesn't fit in 32 bits so read the 64 bit size here // TODO GetUInt64 (i.e. unsigned) atomSize = reader.GetInt64(); } else { Debug.Assert(atomSize >= 8, "Atom should be at least 8 bytes long"); } var args = new AtomCallbackArgs(atomType, atomSize, stream, atomStartPos, reader); handler(args); if (args.Cancel) return; if (atomSize == 0) return; var toSkip = atomStartPos + atomSize - stream.Position; if (toSkip < 0) throw new Exception("Handler moved stream beyond end of atom"); reader.TrySkip(toSkip); } }