private unsafe Dictionary <DbPackage, IdxEntry[]> ReadDictionary(Int32 entriesCount, IdxStruct *idxPtr) { // Count files in packages Dictionary <Int32, Int32> fileNumbers = new Dictionary <Int32, Int32>(_mftContent.Packages.Count); for (Int32 i = 0; i < entriesCount; i++) { Int32 packageIndex = idxPtr[i].Package; if (fileNumbers.TryGetValue(packageIndex, out Int32 count)) { fileNumbers[packageIndex] = count + 1; } else { fileNumbers[packageIndex] = 1; } } // Prepare dictionary Dictionary <DbPackage, IdxEntry[]> dic = new Dictionary <DbPackage, IdxEntry[]>(fileNumbers.Count); foreach (var pair in fileNumbers) { Int32 packageIndex = pair.Key; Int32 fileCount = pair.Value; DbPackage package = _mftContent.Packages[packageIndex]; IdxEntry[] idxEntries = new IdxEntry[fileCount]; dic.Add(package, idxEntries); } // Fill dicrionary for (Int32 i = 0; i < entriesCount; i++) { IdxStruct idxEntry = idxPtr[i]; DbPackage package = _mftContent.Packages[idxEntry.Package]; IdxEntry[] idxEntries = dic[package]; Int32 fileIndex = IndexInPackage(idxEntry.Package, idxEntries); idxEntries[fileIndex] = ToEntry(i, in idxEntry); } Int32 IndexInPackage(Int32 packageNumber, IdxEntry[] array) { return(array.Length - fileNumbers[packageNumber]--); } return(dic); }
public void Enqueue(DbPackage package, IdxEntry entry) { if (_disposed) { throw new ObjectDisposedException("DbExtractor already disposed."); } if (_exceptions.Count > 0) { Dispose(); } _semaphore.WaitOne(); lock (_queue) { _queue.Enqueue(Tuple.Create(package, entry)); Monitor.Pulse(_queue); } }
private void Extract(DbPackage package, IdxEntry entry, ThreadContext context) { if (context.Package != package) { context.Stream?.Dispose(); context.Stream = File.OpenRead(package.FullPath); context.Package = package; } Byte[] buff = context.Buffer; if (buff == null) { buff = new Byte[entry.UncompressedSize]; context.Buffer = buff; } else if (buff.Length < entry.UncompressedSize) { Array.Resize(ref buff, entry.UncompressedSize); context.Buffer = buff; } FileStream inputStream = context.Stream; if (inputStream.Position != entry.Offset) { inputStream.Position = entry.Offset; } if (entry.IsCompressed) { unsafe { fixed(Byte *ptr = buff) { Int32 readedSize = context.Decompressor.ReadCompressedFile(entry.CompressedSize, ptr, inputStream); if (readedSize != entry.UncompressedSize) { throw new EndOfStreamException($"Failed to decompress the file {package.Name}:{entry.Offset}"); } } } } else { Int32 offset = 0; Int32 size = entry.UncompressedSize; while (size > 0) { Int32 readed = inputStream.Read(buff, offset, size); if (readed == 0 && size > 0) { throw new EndOfStreamException($"Failed to copy the file {package.Name}:{entry.Offset}"); } size -= readed; offset += readed; } } String directoryPath = Path.Combine(_outputDirectory, package.Name); if (entry.CompressedSize < 8) { throw new NotSupportedException($"Compressed size is too small: {entry.CompressedSize}"); } UInt16 version = UInt16.MaxValue; void CheckVersion(String name, Int32 supported) { version = UInt16.Parse(Encoding.ASCII.GetString(buff, 2, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); if (version != supported) { throw new NotSupportedException($"Invalid {name} version: {version}. Expected: {supported}"); } } String GetOutputPath(String tag) { DbRecordId recordId = new DbRecordId(entry.ResourceId); return(Path.Combine(directoryPath, Rename && DbNames.TryGetRecordName(recordId, out var name) ? $"{recordId}_{name}.{tag.ToLowerInvariant()}" : $"{recordId}.{tag.ToLowerInvariant()}")); } String outputPath; ITarget target; String magicTag = Encoding.ASCII.GetString(buff, 0, 2); switch (magicTag) { case "LV": CheckVersion(magicTag, _version.LV); outputPath = GetOutputPath(magicTag); target = CopyTarget.Instance; break; case "AM": CheckVersion(magicTag, _version.AM); if (Convert) { outputPath = GetOutputPath("tiff"); target = ImageTarget.Instance; } else { outputPath = GetOutputPath(magicTag); target = CopyTarget.Instance; } break; case "GV": CheckVersion(magicTag, _version.GV); outputPath = GetOutputPath(magicTag); target = GVTarget.Instance; target = CopyTarget.Instance; break; case "TX": CheckVersion(magicTag, _version.TX); if (Convert) { outputPath = GetOutputPath("txt"); target = TextTarget.Instance; } else { outputPath = GetOutputPath(magicTag); target = CopyTarget.Instance; } break; case "CH": CheckVersion(magicTag, _version.CH); outputPath = GetOutputPath(magicTag); target = CopyTarget.Instance; break; case "IL": CheckVersion(magicTag, _version.IL); outputPath = GetOutputPath(magicTag); target = ILTarget.Instance; target = CopyTarget.Instance; break; case "VS": outputPath = GetOutputPath("vssf"); if (Convert) { outputPath = GetOutputPath("mp3"); target = SoundTarget.Instance; } else { outputPath = GetOutputPath(magicTag); target = CopyTarget.Instance; } break; default: { if (Encoding.ASCII.GetString(buff, 4, 4) != "moov") { throw new NotSupportedException($"Invalid magic number: {Encoding.ASCII.GetString(buff, 0, 8)}"); } directoryPath = _outputDirectory; outputPath = Path.Combine(directoryPath, $"{entry.ResourceId:X8}_{package.Name}.qt"); target = CopyTarget.Instance; break; } } if (!Convert) { target = CopyTarget.Instance; } Directory.CreateDirectory(directoryPath); ArraySegment <Byte> segment = new ArraySegment <Byte>(buff, 0, entry.UncompressedSize); target.Write(segment, outputPath, version); }