/// <summary> /// Reads an archive file from a given filepath /// </summary> /// <param name="path"></param> /// <param name="hashService"></param> /// <returns></returns> public static Archive ReadArchive(string path, IHashService hashService) { var mapName = "ReadArchive_Map"; var ar = new Archive() { ArchiveAbsolutePath = path }; using var fs = new FileStream(ar.ArchiveAbsolutePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); using var mmf = MemoryMappedFileExtensions.GetMemoryMappedFile(fs, mapName); // read header uint customDataLength; using (var vs = mmf.CreateViewStream(0, Header.EXTENDED_SIZE, MemoryMappedFileAccess.Read)) using (var br = new BinaryReader(vs)) { ar.Header = ReadHeader(br); customDataLength = br.ReadUInt32(); } // read custom try { if (customDataLength != 0) { using var vs = mmf.CreateViewStream(Header.EXTENDED_SIZE, customDataLength, MemoryMappedFileAccess.Read); using var br = new BinaryReader(vs); if (br.BaseStream.Length >= LxrsFooter.MIN_LENGTH) { var lxrs = br.ReadLxrsFooter(); foreach (var s in lxrs.FileInfos) { hashService.AddCustom(s); } } } } catch (UnauthorizedAccessException) { //Console.WriteLine(e); } // read files using (var vs = mmf.CreateViewStream((long)ar.Header.IndexPosition, ar.Header.IndexSize, MemoryMappedFileAccess.Read)) using (var br = new BinaryReader(vs)) { ar.Index = ReadIndex(br, hashService); } foreach (var file in ar.Index.FileEntries.Values) { file.Archive = ar; ar.Files.Add(file.Key, file); } return(ar); }
/// <summary> /// Extracts all files from an archive to a specified directory. /// </summary> /// <param name="ar"></param> /// <param name="outDir"></param> /// <param name="pattern"></param> /// <param name="regex"></param> /// <param name="decompressBuffers"></param> /// <returns></returns> public void ExtractAll(Archive ar, DirectoryInfo outDir, string pattern = "", string regex = "", bool decompressBuffers = false) { var extractedList = new ConcurrentBag <string>(); var failedList = new ConcurrentBag <string>(); var mapName = "ExtractAll_Map"; // check search pattern then regex var finalmatches = ar.Files.Values.Cast <FileEntry>(); var totalInArchiveCount = ar.FileCount; if (!string.IsNullOrEmpty(pattern)) { finalmatches = ar.Files.Values.Cast <FileEntry>().MatchesWildcard(item => item.FileName, pattern); } if (!string.IsNullOrEmpty(regex)) { var searchTerm = new System.Text.RegularExpressions.Regex($@"{regex}"); var queryMatchingFiles = from file in finalmatches let matches = searchTerm.Matches(file.FileName) where matches.Count > 0 select file; finalmatches = queryMatchingFiles; } var finalMatchesList = finalmatches.ToList(); _loggerService.Info($"{ar.ArchiveAbsolutePath}: Found {finalMatchesList.Count}/{totalInArchiveCount} entries to extract."); if (finalMatchesList.Count == 0) { return; } using var fs = new FileStream(ar.ArchiveAbsolutePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); using var mmf = MemoryMappedFileExtensions.GetMemoryMappedFile(fs, mapName); var progress = 0; // check hashes before parallel unbundling for (var i = 0; i < finalMatchesList.Count; i++) { var item = finalMatchesList[i]; _hashService.Contains(item.Key); } Parallel.ForEach(finalMatchesList, info => { var extracted = ExtractSingle(ar, info.NameHash64, outDir, decompressBuffers, mmf); if (extracted != 0) { extractedList.Add(info.FileName); } else { failedList.Add(info.FileName); } Interlocked.Increment(ref progress); _progressService.Report(progress / (float)finalMatchesList.Count); }); //logging var msg = $"{ar.ArchiveAbsolutePath}: Unbundled {extractedList.Count}/{finalMatchesList.Count} entries."; if (extractedList.Count == finalMatchesList.Count) { _loggerService.Success(msg); } else { _loggerService.Info(msg); } }