public static FileData RebuildSample(FileData file, SortedList <int, TrackData> tracks, Dictionary <string, AttachmentData> attachments, FileInfo srsFile, DirectoryInfo outDir) { uint crc = Crc32.StartValue; using (EbmlReader rdr = new EbmlReader(srsFile.FullName, EbmlReadMode.SRS)) using (FileStream fsOut = new FileStream(Path.Combine(outDir.FullName, file.Name), FileMode.Create)) { string currentAttachment = null; int clustercount = 0; while (rdr.Read()) { // the ReSample element is the only part of the SRS file we don't want copied into the new sample. if (rdr.ElementType == EbmlElementType.ReSample) { rdr.SkipContents(); continue; } fsOut.Write(rdr.Element.RawHeader, 0, rdr.Element.RawHeader.Length); crc = Crc32.GetCrc(crc, rdr.Element.RawHeader); switch (rdr.ElementType) { case EbmlElementType.Segment: case EbmlElementType.BlockGroup: case EbmlElementType.AttachmentList: case EbmlElementType.Attachment: // these elements have no useful info of their own, but we want to step into them to examine their children rdr.MoveToChild(); break; case EbmlElementType.Cluster: // simple progress indicator since this can take a while (cluster is good because they're about 1mb each) Console.Write("\b{0}", Program.spinners[clustercount++ % Program.spinners.Length]); rdr.MoveToChild(); break; case EbmlElementType.AttachedFileName: byte[] buff = rdr.ReadContents(); fsOut.Write(buff, 0, buff.Length); crc = Crc32.GetCrc(crc, buff); currentAttachment = Encoding.UTF8.GetString(buff); break; case EbmlElementType.AttachedFileData: AttachmentData attachment = attachments[currentAttachment]; // restore data from extracted attachments buff = new byte[rdr.Element.Length]; attachment.AttachmentFile.Read(buff, 0, buff.Length); fsOut.Write(buff, 0, buff.Length); crc = Crc32.GetCrc(crc, buff); if ((file.Flags & FileData.FileDataFlags.AttachmentsRemoved) != 0) { rdr.MoveToChild(); // really means do nothing in this case } else { rdr.SkipContents(); } break; case EbmlElementType.Block: TrackData track = tracks[rdr.Block.TrackNumber]; // restore data from extracted tracks buff = new byte[rdr.Block.Length]; track.TrackFile.Read(buff, 0, buff.Length); fsOut.Write(rdr.Block.RawBlockHeader, 0, rdr.Block.RawBlockHeader.Length); crc = Crc32.GetCrc(crc, rdr.Block.RawBlockHeader); fsOut.Write(buff, 0, buff.Length); crc = Crc32.GetCrc(crc, buff); rdr.MoveToChild(); // really means do nothing in this case break; default: // anything not caught above is considered metadata, so we copy it as is buff = rdr.ReadContents(); fsOut.Write(buff, 0, buff.Length); crc = Crc32.GetCrc(crc, buff); break; } } } Console.Write('\b'); FileData newFile = new FileData(Path.Combine(outDir.FullName, file.Name)); newFile.Crc32 = ~crc; return(newFile); }
public static void ExtractSampleStreams(SortedList <int, TrackData> tracks, Dictionary <string, AttachmentData> attachments, FileData file, FileInfo inFile, DirectoryInfo outDir) { Stream fs; if (RarFileNameComparer.IsRarFile(inFile.Name)) { fs = new RarStream(inFile.FullName); } else { fs = inFile.OpenRead(); } using (EbmlReader rdr = new EbmlReader(fs, EbmlReadMode.MKV)) { long startOffset = long.MaxValue; foreach (TrackData track in tracks.Values) { if (track.MatchOffset > 0) { startOffset = Math.Min(track.MatchOffset, startOffset); } } string currentAttachment = null; int clustercount = 0; bool done = false; while (rdr.Read() && !done) { switch (rdr.ElementType) { case EbmlElementType.Segment: case EbmlElementType.AttachmentList: case EbmlElementType.Attachment: case EbmlElementType.BlockGroup: rdr.MoveToChild(); break; case EbmlElementType.Cluster: // simple progress indicator since this can take a while (cluster is good because they're about 1mb each) Console.Write("\b{0}", Program.spinners[clustercount++ % Program.spinners.Length]); // in extract mode, we know the first data offset we're looking for, so skip any clusters before that if (rdr.Element.ElementStartPos + rdr.Element.RawHeader.Length + rdr.Element.Length < startOffset) { rdr.SkipContents(); } else { rdr.MoveToChild(); } break; case EbmlElementType.AttachedFileName: currentAttachment = Encoding.UTF8.GetString(rdr.ReadContents()); if (!attachments.ContainsKey(currentAttachment)) { attachments.Add(currentAttachment, new AttachmentData() { Name = currentAttachment }); } break; case EbmlElementType.AttachedFileData: AttachmentData attachment = attachments[currentAttachment]; attachment.Size = rdr.Element.Length; // in extract mode, extract all attachments in case we need them later if (attachment.AttachmentFile == null) { attachment.AttachmentFile = new FileStream(Path.Combine(outDir.FullName, attachment.Name), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 0x10000, FileOptions.DeleteOnClose); } byte[] buff = rdr.ReadContents(); attachment.AttachmentFile.Write(buff, 0, buff.Length); attachment.AttachmentFile.Seek(0, SeekOrigin.Begin); break; case EbmlElementType.Block: TrackData track = tracks[rdr.Block.TrackNumber]; if (rdr.Block.ElementStartPos + rdr.Block.RawHeader.Length + rdr.Block.RawBlockHeader.Length + rdr.Block.Length > track.MatchOffset) { if (track.TrackFile == null) { track.TrackFile = new FileStream(Path.Combine(outDir.FullName, inFile.Name + "." + track.TrackNumber.ToString("d3")), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 0x10000, FileOptions.DeleteOnClose); } buff = rdr.ReadContents(); int offset = 0; for (int i = 0; i < rdr.Block.FrameLengths.Length; i++) { if (rdr.Block.ElementStartPos + rdr.Block.RawHeader.Length + rdr.Block.RawBlockHeader.Length + offset >= track.MatchOffset && track.TrackFile.Position < track.DataLength) { track.TrackFile.Write(buff, offset, rdr.Block.FrameLengths[i]); } offset += rdr.Block.FrameLengths[i]; } bool tracksDone = true; foreach (TrackData t in tracks.Values) { if (t.TrackFile == null || t.TrackFile.Length < t.DataLength) { tracksDone = false; break; } } done = tracksDone; } else { rdr.SkipContents(); } break; default: rdr.SkipContents(); break; } } } Console.Write('\b'); }