public ExtractSource(string sourceString, Dictionary <string, string> externalFiles, ThreadLocalZipArchive archive)
 {
     if (sourceString[0] == '$')
     {
         var externalHash = sourceString.Substring(1);
         if (!externalFiles.TryGetValue(externalHash, out _localPath))
         {
             throw new Exception("Could not find external file with hash {externalHash}.");
         }
     }
     else
     {
         _entryName = sourceString;
         _archive   = archive;
     }
 }
        public void Extract(string compressedArchivePath, string outputDirectory, IProgress <ProgressReport> progress)
        {
            using (var archiveStream = CreateTemporaryFileStream())
            {
                // decompress the LZMA stream
                using (var lzmaStream = File.OpenRead(compressedArchivePath))
                {
                    CompressionUtility.Decompress(lzmaStream, archiveStream, progress);
                }

                var archivePath = ((FileStream)archiveStream).Name;

                // reset the uncompressed stream
                archiveStream.Seek(0, SeekOrigin.Begin);

                // read as a zip archive
                using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Read))
                    using (var tlArchive = new ThreadLocalZipArchive(archivePath, archive))
                    {
                        List <ExtractOperation>            extractOperations = new List <ExtractOperation>();
                        Dictionary <string, ExtractSource> sourceCache       = new Dictionary <string, ExtractSource>();

                        // process the index to determine all extraction operations
                        var indexEntry = archive.GetEntry(IndexFileName);
                        using (var indexReader = new StreamReader(indexEntry.Open()))
                        {
                            Dictionary <string, ZipOperation> zipOperations = new Dictionary <string, ZipOperation>(StringComparer.OrdinalIgnoreCase);
                            for (var line = indexReader.ReadLine(); line != null; line = indexReader.ReadLine())
                            {
                                var lineParts = line.Split(pipeSeperator);
                                if (lineParts.Length != 2)
                                {
                                    throw new Exception("Unexpected index line format, too many '|'s.");
                                }

                                string target = lineParts[0];
                                string source = lineParts[1];

                                ExtractSource extractSource;
                                if (!sourceCache.TryGetValue(source, out extractSource))
                                {
                                    sourceCache[source] = extractSource = new ExtractSource(source, _externalFiles, tlArchive);
                                }

                                var zipSeperatorIndex = target.IndexOf("::", StringComparison.OrdinalIgnoreCase);

                                if (zipSeperatorIndex != -1)
                                {
                                    string zipRelativePath = target.Substring(0, zipSeperatorIndex);
                                    string zipEntryName    = target.Substring(zipSeperatorIndex + 2);
                                    string destinationPath = Path.Combine(outputDirectory, zipRelativePath);

                                    // operations on a zip file will be sequential
                                    ZipOperation currentZipOperation;

                                    if (!zipOperations.TryGetValue(destinationPath, out currentZipOperation))
                                    {
                                        extractOperations.Add(currentZipOperation = new ZipOperation(destinationPath));
                                        zipOperations.Add(destinationPath, currentZipOperation);
                                    }
                                    currentZipOperation.AddEntry(zipEntryName, extractSource);
                                }
                                else
                                {
                                    string destinationPath = Path.Combine(outputDirectory, target);
                                    extractOperations.Add(new CopyOperation(extractSource, destinationPath));
                                }
                            }
                        }

                        int opsExecuted = 0;
                        // execute all operations
                        //foreach(var extractOperation in extractOperations)
                        extractOperations.AsParallel().ForAll(extractOperation =>
                        {
                            extractOperation.DoOperation();
                            progress.Report(LocalizableStrings.Expanding, Interlocked.Increment(ref opsExecuted), extractOperations.Count);
                        });
                    }
            }
        }
        public void Extract(string compressedArchivePath, string outputDirectory, IProgress<ProgressReport> progress)
        {
            using (var archiveStream = CreateTemporaryFileStream())
            {
                // decompress the LZMA stream
                using (var lzmaStream = File.OpenRead(compressedArchivePath))
                {
                    CompressionUtility.Decompress(lzmaStream, archiveStream, progress);
                }

                var archivePath = ((FileStream)archiveStream).Name;

                // reset the uncompressed stream
                archiveStream.Seek(0, SeekOrigin.Begin);

                // read as a zip archive
                using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Read))
                using (var tlArchive = new ThreadLocalZipArchive(archivePath, archive))
                {
                    List<ExtractOperation> extractOperations = new List<ExtractOperation>();
                    Dictionary<string, ExtractSource> sourceCache = new Dictionary<string, ExtractSource>();

                    // process the index to determine all extraction operations
                    var indexEntry = archive.GetEntry(IndexFileName);
                    using (var indexReader = new StreamReader(indexEntry.Open()))
                    {
                        Dictionary<string, ZipOperation> zipOperations = new Dictionary<string, ZipOperation>(StringComparer.OrdinalIgnoreCase);
                        for (var line = indexReader.ReadLine(); line != null; line = indexReader.ReadLine())
                        {
                            var lineParts = line.Split(pipeSeperator);
                            if (lineParts.Length != 2)
                            {
                                throw new Exception("Unexpected index line format, too many '|'s.");
                            }

                            string target = lineParts[0];
                            string source = lineParts[1];

                            ExtractSource extractSource;
                            if (!sourceCache.TryGetValue(source, out extractSource))
                            {
                                sourceCache[source] = extractSource = new ExtractSource(source, _externalFiles, tlArchive);
                            }

                            var zipSeperatorIndex = target.IndexOf("::", StringComparison.OrdinalIgnoreCase);

                            if (zipSeperatorIndex != -1)
                            {
                                string zipRelativePath = target.Substring(0, zipSeperatorIndex);
                                string zipEntryName = target.Substring(zipSeperatorIndex + 2);
                                string destinationPath = Path.Combine(outputDirectory, zipRelativePath);

                                // operations on a zip file will be sequential
                                ZipOperation currentZipOperation;

                                if (!zipOperations.TryGetValue(destinationPath, out currentZipOperation))
                                {
                                    extractOperations.Add(currentZipOperation = new ZipOperation(destinationPath));
                                    zipOperations.Add(destinationPath, currentZipOperation);
                                }
                                currentZipOperation.AddEntry(zipEntryName, extractSource);
                            }
                            else
                            {
                                string destinationPath = Path.Combine(outputDirectory, target);
                                extractOperations.Add(new CopyOperation(extractSource, destinationPath));
                            }
                        }
                    }

                    int opsExecuted = 0;
                    // execute all operations
                    //foreach(var extractOperation in extractOperations)
                    extractOperations.AsParallel().ForAll(extractOperation =>
                    {
                        extractOperation.DoOperation();
                        progress.Report("Expanding", Interlocked.Increment(ref opsExecuted), extractOperations.Count);
                    });
                }
            }
        }
 public ExtractSource(string sourceString, Dictionary<string, string> externalFiles, ThreadLocalZipArchive archive)
 {
     if (sourceString[0] == '$')
     {
         var externalHash = sourceString.Substring(1);
         if (!externalFiles.TryGetValue(externalHash, out _localPath))
         {
             throw new Exception("Could not find external file with hash {externalHash}.");
         }
     }
     else
     {
         _entryName = sourceString;
         _archive = archive;
     }
 }