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'); }
public static void FindSampleStreams(SortedList <int, TrackData> tracks, FileInfo inFile) { Stream fs; if (RarFileNameComparer.IsRarFile(inFile.Name)) { fs = new RarStream(inFile.FullName); } else { fs = inFile.OpenRead(); } using (EbmlReader rdr = new EbmlReader(fs, EbmlReadMode.MKV)) { int clustercount = 0; bool done = false; while (rdr.Read() && !done) { switch (rdr.ElementType) { case EbmlElementType.Segment: 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]); rdr.MoveToChild(); break; case EbmlElementType.Block: if (!tracks.ContainsKey(rdr.Block.TrackNumber)) { tracks.Add(rdr.Block.TrackNumber, new TrackData() { TrackNumber = (ushort)rdr.Block.TrackNumber }); } TrackData track = tracks[rdr.Block.TrackNumber]; // it's possible the sample didn't require or contain data for all tracks in the main file // if that happens, we obviously don't want to try to match the data if (track.SignatureBytes != null && (track.MatchOffset == 0 || track.CheckBytes.Length < track.SignatureBytes.Length)) { // here, the data we're looking for might not start in the first frame (lace) of the block, so we need to check them all byte[] buff = rdr.ReadContents(); int offset = 0; for (int i = 0; i < rdr.Block.FrameLengths.Length; i++) { if (track.CheckBytes != null && track.CheckBytes.Length < track.SignatureBytes.Length) { byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, rdr.Block.FrameLengths[i] + track.CheckBytes.Length)]; Buffer.BlockCopy(track.CheckBytes, 0, checkBytes, 0, track.CheckBytes.Length); Buffer.BlockCopy(buff, offset, checkBytes, track.CheckBytes.Length, checkBytes.Length - track.CheckBytes.Length); if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length)) { track.CheckBytes = checkBytes; } else { // it was only a partial match. start over track.CheckBytes = null; track.MatchOffset = 0; track.MatchLength = 0; } } // this is a bit weird, but if we had a false positive match going and discovered it above, we check this frame again // to see if it's the start of a new match (rare problem, but it can happen with subtitles especially) if (track.CheckBytes == null) { byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, rdr.Block.FrameLengths[i])]; Buffer.BlockCopy(buff, offset, checkBytes, 0, checkBytes.Length); if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length)) { track.CheckBytes = checkBytes; track.MatchOffset = rdr.Block.ElementStartPos + rdr.Block.RawHeader.Length + rdr.Block.RawBlockHeader.Length + offset; track.MatchLength = Math.Min(track.DataLength, rdr.Block.FrameLengths[i]); } } else { track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.Block.FrameLengths[i]); } offset += rdr.Block.FrameLengths[i]; } } else if (track.MatchLength < track.DataLength) { track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.Element.Length); rdr.SkipContents(); bool tracksDone = true; foreach (TrackData t in tracks.Values) { if (t.MatchLength < t.DataLength) { tracksDone = false; break; } } done = tracksDone; } else { rdr.SkipContents(); } break; default: rdr.SkipContents(); break; } } } Console.Write('\b'); }
public static void ExtractSampleStreams(SortedList <int, TrackData> tracks, FileData file, FileInfo inFile, DirectoryInfo outDir) { Stream fs; if (RarFileNameComparer.IsRarFile(inFile.Name)) { fs = new RarStream(inFile.FullName); } else { fs = inFile.OpenRead(); } using (RiffReader rdr = new RiffReader(fs, RiffReadMode.AVI)) { long startOffset = long.MaxValue; foreach (TrackData track in tracks.Values) { if (track.MatchOffset > 0) { startOffset = Math.Min(track.MatchOffset, startOffset); } } int blockcount = 0; bool done = false; while (rdr.Read() && !done) { if (rdr.ChunkType == RiffChunkType.List) { rdr.MoveToChild(); } else // normal chunk { if (rdr.ChunkType == RiffChunkType.Movi) { if (++blockcount % 15 == 0) { Console.Write("\b{0}", Program.spinners[blockcount % Program.spinners.Length]); } if (!tracks.ContainsKey(rdr.MoviChunk.StreamNumber)) { tracks.Add(rdr.MoviChunk.StreamNumber, new TrackData()); } TrackData track = tracks[rdr.MoviChunk.StreamNumber]; if (rdr.MoviChunk.ChunkStartPos + rdr.MoviChunk.RawHeader.Length + rdr.MoviChunk.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); } if (track.TrackFile.Position < track.DataLength) { if (rdr.MoviChunk.ChunkStartPos + rdr.MoviChunk.RawHeader.Length >= track.MatchOffset) { track.TrackFile.Write(rdr.ReadContents(), 0, (int)rdr.MoviChunk.Length); } else { int chunkOffset = (int)(track.MatchOffset - (rdr.MoviChunk.ChunkStartPos + rdr.MoviChunk.RawHeader.Length)); track.TrackFile.Write(rdr.ReadContents(), chunkOffset, (int)rdr.MoviChunk.Length - chunkOffset); } } bool tracksDone = true; foreach (TrackData t in tracks.Values) { if (t.TrackFile == null || t.TrackFile.Length < t.DataLength) { tracksDone = false; break; } } done = tracksDone; } rdr.SkipContents(); } else { rdr.SkipContents(); } } } } Console.Write('\b'); }
static int CreateReconstructionFile(List <FileInfo> inFiles, DirectoryInfo inFolder, List <string> storeFiles, string srrName, bool savePaths) { using (FileStream srrfs = new FileStream(srrName, FileMode.Create)) { BinaryWriter bw = new BinaryWriter(srrfs, Encoding.ASCII); bw.Write(new SrrHeaderBlock(appName).RawData); // we store copies of any files included in the storeFiles list in the .srr using a "store block". any SFV files used are also included. foreach (FileInfo fi in inFiles) { if (fi.Extension.ToLower() == ".sfv") { storeFiles.Add(fi.FullName); } } SrrStoredFileBlock storeBlock = null; foreach (string fileName in storeFiles) { string searchName = fileName; if (!Path.IsPathRooted(searchName)) { searchName = Path.Combine(inFolder.FullName, fileName); } DirectoryInfo searchDir = new DirectoryInfo(Path.GetDirectoryName(searchName)); if (searchDir.Exists) { foreach (FileInfo storeFile in searchDir.GetFiles(Path.GetFileName(searchName))) { string fName = savePaths ? MakePathRelative(storeFile.FullName, inFolder.FullName) : storeFile.Name; Console.WriteLine("Storing file: {0}", fName); storeBlock = new SrrStoredFileBlock(fName, (int)storeFile.Length); if (savePaths) { storeBlock.Flags |= (ushort)SrrStoredFileBlock.FlagValues.PathsSaved; } using (FileStream storefs = storeFile.OpenRead()) storefs.Read(storeBlock.RawData, storeBlock.FileOffset, (int)storeFile.Length); bw.Write(storeBlock.RawData); } } } List <string> rarFiles = new List <string>(); foreach (FileInfo inFile in inFiles) { if (inFile.Extension.ToLower() == ".sfv") { using (SfvReader sfvReader = new SfvReader(inFile.FullName)) { List <string> sfvRarFiles = new List <string>(); SfvEntry sfvEntry; while ((sfvEntry = sfvReader.Read()) != null) { if (RarFileNameComparer.IsRarFile(sfvEntry.FileName)) { sfvRarFiles.Add(Path.Combine(inFile.DirectoryName, sfvEntry.FileName)); } else { ReportError(string.Format("Warning: Non-RAR file referenced in SFV: {0}\n\tThis file cannot be recreated unless it is stored using -s", sfvEntry.FileName)); continue; } } sfvRarFiles.Sort(new RarFileNameComparer()); rarFiles.AddRange(sfvRarFiles); } } else { bool oldNameFormat = false; RarBlock block = null; using (RarReader rdr = new RarReader(inFile.FullName, RarReadMode.RAR)) while ((block = rdr.Read()) != null) { if (block is RarVolumeHeaderBlock && ((block.Flags & (ushort)RarVolumeHeaderBlock.FlagValues.Volume) != 0)) { if ((block.Flags & (ushort)RarVolumeHeaderBlock.FlagValues.FirstVolume) == 0) { throw new InvalidDataException("You must start with the first volume from a RAR set"); } oldNameFormat = (block.Flags & (ushort)RarVolumeHeaderBlock.FlagValues.NewNumbering) == 0; } } string nextFileName = inFile.FullName; while (File.Exists(nextFileName)) { rarFiles.Add(Path.Combine(inFile.DirectoryName, nextFileName)); nextFileName = RarFileNameFinder.FindNextFileName(nextFileName, oldNameFormat); } } } foreach (string fileName in rarFiles) { if (!File.Exists(fileName)) { ReportError(string.Format("Referenced file not found: {0}", fileName)); srrfs.Close(); File.Delete(srrName); return(2); } string fName = savePaths ? MakePathRelative(fileName, inFolder.FullName) : Path.GetFileName(fileName); Console.WriteLine("Processing file: {0}", fName); SrrRarFileBlock rarBlock = new SrrRarFileBlock(fName); if (savePaths) { rarBlock.Flags |= (ushort)SrrRarFileBlock.FlagValues.PathsSaved; } bw.Write(rarBlock.RawData); using (RarReader rarReader = new RarReader(fileName, RarReadMode.RAR)) { RarBlock block; while ((block = rarReader.Read()) != null) { if (verbose) { Console.WriteLine("\tBlock Type: 0x{0:x2}", block.RawType); Console.WriteLine("\tBlock Size: {0}", block.RawData.Length); } if (block is RarPackedFileBlock) { RarPackedFileBlock fileData = (RarPackedFileBlock)block; if (verbose) { Console.WriteLine("\t\tCompression Type: 0x{0:x2}", fileData.CompressionMethod); Console.WriteLine("\t\tPacked Data Size: {0:n0}", fileData.PackedSize); Console.WriteLine("\t\tFile Size: {0:n0}", fileData.UnpackedSize); Console.WriteLine("\t\tFile Name: {0}", fileData.FileName); } if (fileData.CompressionMethod != 0x30) { ReportError(string.Format("Archive uses unsupported compression method: {0}", fileName)); srrfs.Close(); File.Delete(srrName); return(3); } } else if (block is RarRecoveryBlock) { RarRecoveryBlock subData = (RarRecoveryBlock)block; if (verbose & subData.RecoverySectors > 0) { Console.WriteLine("\t\tRecovery Record Size: {0:n0}", subData.PackedSize); Console.WriteLine("\t\tRecovery Sectors: {0:n0}", subData.RecoverySectors); Console.WriteLine("\t\tProtected Sectors: {0:n0}", subData.DataSectors); } } // store the raw data for any blocks found bw.Write(block.RawData); } } } } Console.WriteLine("\nReconstruction file successfully created: {0}", srrName); return(0); }
public static void FindSampleStreams(SortedList <int, TrackData> tracks, FileInfo inFile) { Stream fs; if (RarFileNameComparer.IsRarFile(inFile.Name)) { fs = new RarStream(inFile.FullName); } else { fs = inFile.OpenRead(); } using (RiffReader rdr = new RiffReader(fs, RiffReadMode.AVI)) { int blockcount = 0; bool done = false; while (rdr.Read() && !done) { if (rdr.ChunkType == RiffChunkType.List) { rdr.MoveToChild(); } else // normal chunk { if (rdr.ChunkType == RiffChunkType.Movi) { if (++blockcount % 15 == 0) { Console.Write("\b{0}", Program.spinners[blockcount % Program.spinners.Length]); } int trackno = rdr.MoviChunk.StreamNumber; if (!tracks.ContainsKey(trackno)) { tracks.Add(trackno, new TrackData()); } TrackData track = tracks[trackno]; track.TrackNumber = (byte)trackno; if (track.MatchOffset == 0 || track.CheckBytes.Length < track.SignatureBytes.Length) { // it's possible the sample didn't require or contain data for all tracks in the main file // if that happens, we obviously don't want to try to match the data if (track.SignatureBytes != null) { if (track.CheckBytes != null && track.CheckBytes.Length < track.SignatureBytes.Length) { byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, rdr.MoviChunk.Length + track.CheckBytes.Length)]; track.CheckBytes.CopyTo(checkBytes, 0); Buffer.BlockCopy(rdr.ReadContents(), 0, checkBytes, track.CheckBytes.Length, checkBytes.Length - track.CheckBytes.Length); if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length)) { track.CheckBytes = checkBytes; } else { // it was only a partial match. start over track.CheckBytes = null; track.MatchOffset = 0; track.MatchLength = 0; } } // this is a bit weird, but if we had a false positive match going and discovered it above, we check this frame again // to see if it's the start of a new match (probably will never happen with AVI, but it does in MKV, so just in case...) if (track.CheckBytes == null) { byte[] chunkBytes = rdr.ReadContents(); byte searchByte = track.SignatureBytes[0]; int foundPos = -1; while ((foundPos = Array.IndexOf <byte>(chunkBytes, searchByte, foundPos + 1)) > -1) { byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, chunkBytes.Length - foundPos)]; Buffer.BlockCopy(chunkBytes, foundPos, checkBytes, 0, checkBytes.Length); if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length)) { track.CheckBytes = checkBytes; track.MatchOffset = rdr.Chunk.ChunkStartPos + rdr.Chunk.RawHeader.Length + foundPos; track.MatchLength = Math.Min(track.DataLength, chunkBytes.Length - foundPos); break; } } } else { track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.MoviChunk.Length); } } } else if (track.MatchLength < track.DataLength) { track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.MoviChunk.Length); bool tracksDone = true; foreach (TrackData t in tracks.Values) { if (t.MatchLength < t.DataLength) { tracksDone = false; break; } } done = tracksDone; } rdr.SkipContents(); } else { rdr.SkipContents(); } } } } Console.Write('\b'); }