/// <summary> /// Attempt to extract a stream from an archive /// </summary> /// <param name="entryName">Name of the entry to be extracted</param> /// <param name="realEntry">Output representing the entry name that was found</param> /// <returns>MemoryStream representing the entry, null on error</returns> public override (MemoryStream, string) CopyToStream(string entryName) { MemoryStream ms = new MemoryStream(); string realEntry; try { // Decompress the _filename stream realEntry = Path.GetFileNameWithoutExtension(this.Filename); var xz = new XZStream(File.OpenRead(this.Filename)); // Write the file out byte[] xbuffer = new byte[_bufferSize]; int xlen; while ((xlen = xz.Read(xbuffer, 0, _bufferSize)) > 0) { ms.Write(xbuffer, 0, xlen); ms.Flush(); } // Dispose of the streams xz.Dispose(); } catch (Exception ex) { logger.Error(ex); ms = null; realEntry = null; } return(ms, realEntry); }
/// <summary> /// Generate a list of DatItem objects from the header values in an archive /// </summary> /// <returns>List of DatItem objects representing the found data</returns> public override List <BaseFile> GetChildren() { if (_children == null || _children.Count == 0) { _children = new List <BaseFile>(); string gamename = Path.GetFileNameWithoutExtension(this.Filename); BaseFile possibleTxz = GetTorrentXZFileInfo(); // If it was, then add it to the outputs and continue if (possibleTxz != null && possibleTxz.Filename != null) { _children.Add(possibleTxz); } else { try { // Create a blank item for the entry BaseFile xzEntryRom = new BaseFile(); // Perform a quickscan, if flagged to if (this.AvailableHashes == Hash.CRC) { xzEntryRom.Filename = gamename; using (BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename))) { br.BaseStream.Seek(-8, SeekOrigin.End); xzEntryRom.CRC = br.ReadBytesBigEndian(4); xzEntryRom.Size = br.ReadInt32BigEndian(); } } // Otherwise, use the stream directly else { var xzStream = new XZStream(File.OpenRead(this.Filename)); xzEntryRom = xzStream.GetInfo(hashes: this.AvailableHashes); xzEntryRom.Filename = gamename; xzStream.Dispose(); } // Fill in comon details and add to the list xzEntryRom.Parent = gamename; _children.Add(xzEntryRom); } catch (Exception ex) { logger.Error(ex); return(null); } } } return(_children); }
/// <summary> /// Extracts an zip file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { XZStream?xzStream = null; try { xzStream = new XZStream(fileEntry.Content); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.XZ, fileEntry.FullPath, string.Empty, e.GetType()); } if (xzStream != null) { var newFilename = Path.GetFileNameWithoutExtension(fileEntry.Name); var newFileEntry = new FileEntry(newFilename, xzStream, fileEntry); // SharpCompress does not expose metadata without a full read, so we need to decompress first, // and then abort if the bytes exceeded the governor's capacity. var streamLength = xzStream.Index?.Records?.Select(r => r.UncompressedSize) .Aggregate((ulong?)0, (a, b) => a + b); // BUG: Technically, we're casting a ulong to a long, but we don't expect 9 exabyte steams, so // low risk. if (streamLength.HasValue) { governor.CheckResourceGovernor((long)streamLength.Value); } if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); throw new OverflowException(); } foreach (var extractedFile in Context.Extract(newFileEntry, options, governor)) { yield return(extractedFile); } xzStream.Dispose(); } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Write an input file to a torrent XZ archive /// </summary> /// <param name="inputStream">Input stream to be moved</param> /// <param name="outDir">Output directory to build to</param> /// <param name="rom">DatItem representing the new information</param> /// <returns>True if the archive was written properly, false otherwise</returns> public override bool Write(Stream inputStream, string outDir, Rom rom) { bool success = false; // If the stream is not readable, return if (!inputStream.CanRead) { return(success); } // Make sure the output directory exists if (!Directory.Exists(outDir)) { Directory.CreateDirectory(outDir); } outDir = Path.GetFullPath(outDir); // Now get the Rom info for the file so we have hashes and size rom = new Rom(inputStream.GetInfo(keepReadOpen: true)); // Get the output file name string outfile = Path.Combine(outDir, PathExtensions.GetDepotPath(rom.SHA1, Depth)); outfile = outfile.Replace(".gz", ".xz"); // Check to see if the folder needs to be created if (!Directory.Exists(Path.GetDirectoryName(outfile))) { Directory.CreateDirectory(Path.GetDirectoryName(outfile)); } // If the output file exists, don't try to write again if (!File.Exists(outfile)) { // Compress the input stream XZStream outputStream = new XZStream(FileExtensions.TryCreate(outfile)); inputStream.CopyTo(outputStream); // Dispose of everything outputStream.Dispose(); } return(true); }
/// <summary> /// Attempt to extract a file as an archive /// </summary> /// <param name="outDir">Output directory for archive extraction</param> /// <returns>True if the extraction was a success, false otherwise</returns> public override bool CopyAll(string outDir) { bool encounteredErrors = true; try { // Create the temp directory Directory.CreateDirectory(outDir); // Decompress the _filename stream FileStream outstream = FileExtensions.TryCreate(Path.Combine(outDir, Path.GetFileNameWithoutExtension(this.Filename))); var xz = new XZStream(File.OpenRead(this.Filename)); xz.CopyTo(outstream); // Dispose of the streams outstream.Dispose(); xz.Dispose(); encounteredErrors = false; } catch (EndOfStreamException ex) { // Catch this but don't count it as an error because SharpCompress is unsafe logger.Verbose(ex); } catch (InvalidOperationException ex) { logger.Warning(ex); encounteredErrors = true; } catch (Exception ex) { logger.Error(ex); encounteredErrors = true; } return(encounteredErrors); }