/// <summary> /// Generate a list of DatItem objects from the header values in an archive /// </summary> /// <param name="omitFromScan">Hash representing the hashes that should be skipped</param> /// <param name="date">True if entry dates should be included, false otherwise (default)</param> /// <returns>List of DatItem objects representing the found data</returns> /// <remarks>TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually</remarks> public override List <BaseFile> GetChildren(Hash omitFromScan = Hash.DeepHashes, bool date = false) { List <BaseFile> found = new List <BaseFile>(); string gamename = Path.GetFileNameWithoutExtension(_filename); try { ZipFile zf = new ZipFile(); ZipReturn zr = zf.Open(_filename, new FileInfo(_filename).LastWriteTime.Ticks, true); if (zr != ZipReturn.ZipGood) { throw new Exception(ZipFile.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.EntriesCount; i++) { // Open the read stream zr = zf.OpenReadStream(i, false, out Stream readStream, out ulong streamsize, out SabreTools.Library.Data.CompressionMethod cm, out uint lastMod); // If we get a read error, log it and continue if (zr != ZipReturn.ZipGood) { Globals.Logger.Warning("An error occurred while reading archive {0}: Zip Error - {1}", _filename, zr); continue; } // If the entry ends with a directory separator, continue to the next item, if any if (zf.Entries[i].FileName.EndsWith(Path.DirectorySeparatorChar.ToString()) || zf.Entries[i].FileName.EndsWith(Path.AltDirectorySeparatorChar.ToString()) || zf.Entries[i].FileName.EndsWith(Path.PathSeparator.ToString())) { continue; } // If secure hashes are disabled, do a quickscan if (omitFromScan == Hash.SecureHashes) { string newname = zf.Entries[i].FileName; long newsize = (long)zf.Entries[i].UncompressedSize; byte[] newcrc = zf.Entries[i].CRC.Reverse().ToArray(); string convertedDate = Utilities.ConvertMsDosTimeFormatToDateTime(zf.Entries[i].LastMod).ToString("yyyy/MM/dd hh:mm:ss"); found.Add(new BaseFile { Filename = newname, Size = newsize, CRC = newcrc, Date = (date ? convertedDate : null), Parent = gamename, }); } // Otherwise, use the stream directly else { BaseFile zipEntryRom = Utilities.GetStreamInfo(readStream, (long)zf.Entries[i].UncompressedSize, omitFromScan: omitFromScan, keepReadOpen: true); zipEntryRom.Filename = zf.Entries[i].FileName; zipEntryRom.Parent = gamename; string convertedDate = Utilities.ConvertMsDosTimeFormatToDateTime(zf.Entries[i].LastMod).ToString("yyyy/MM/dd hh:mm:ss"); zipEntryRom.Date = (date ? convertedDate : null); found.Add(zipEntryRom); } } // Dispose of the archive zr = zf.CloseReadStream(); zf.Close(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return(null); } return(found); }
/// <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); // Extract all files to the temp directory ZipFile zf = new ZipFile(); ZipReturn zr = zf.Open(_filename, new FileInfo(_filename).LastWriteTime.Ticks, true); if (zr != ZipReturn.ZipGood) { throw new Exception(ZipFile.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.EntriesCount && zr == ZipReturn.ZipGood; i++) { // Open the read stream zr = zf.OpenReadStream(i, false, out Stream readStream, out ulong streamsize, out SabreTools.Library.Data.CompressionMethod cm, out uint lastMod); // Create the rest of the path, if needed if (!String.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.Entries[i].FileName))) { Directory.CreateDirectory(Path.Combine(outDir, Path.GetDirectoryName(zf.Entries[i].FileName))); } // If the entry ends with a directory separator, continue to the next item, if any if (zf.Entries[i].FileName.EndsWith(Path.DirectorySeparatorChar.ToString()) || zf.Entries[i].FileName.EndsWith(Path.AltDirectorySeparatorChar.ToString()) || zf.Entries[i].FileName.EndsWith(Path.PathSeparator.ToString())) { continue; } FileStream writeStream = Utilities.TryCreate(Path.Combine(outDir, zf.Entries[i].FileName)); // If the stream is smaller than the buffer, just run one loop through to avoid issues if (streamsize < _bufferSize) { byte[] ibuffer = new byte[streamsize]; int ilen = readStream.Read(ibuffer, 0, (int)streamsize); writeStream.Write(ibuffer, 0, ilen); writeStream.Flush(); } // Otherwise, we do the normal loop else { int realBufferSize = (streamsize < _bufferSize ? (int)streamsize : _bufferSize); byte[] ibuffer = new byte[realBufferSize]; int ilen; while ((ilen = readStream.Read(ibuffer, 0, realBufferSize)) > 0) { writeStream.Write(ibuffer, 0, ilen); writeStream.Flush(); } } zr = zf.CloseReadStream(); writeStream.Dispose(); } zf.Close(); encounteredErrors = false; } catch (EndOfStreamException) { // Catch this but don't count it as an error because SharpCompress is unsafe } catch (InvalidOperationException) { encounteredErrors = true; } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); encounteredErrors = true; } return(encounteredErrors); }
/// <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 = null; try { ZipFile zf = new ZipFile(); ZipReturn zr = zf.Open(_filename, new FileInfo(_filename).LastWriteTime.Ticks, true); if (zr != ZipReturn.ZipGood) { throw new Exception(ZipFile.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.EntriesCount && zr == ZipReturn.ZipGood; i++) { if (zf.Entries[i].FileName.Contains(entryName)) { // Open the read stream realEntry = zf.Entries[i].FileName; zr = zf.OpenReadStream(i, false, out Stream readStream, out ulong streamsize, out SabreTools.Library.Data.CompressionMethod cm, out uint lastMod); // If the stream is smaller than the buffer, just run one loop through to avoid issues if (streamsize < _bufferSize) { byte[] ibuffer = new byte[streamsize]; int ilen = readStream.Read(ibuffer, 0, (int)streamsize); ms.Write(ibuffer, 0, ilen); ms.Flush(); } // Otherwise, we do the normal loop else { byte[] ibuffer = new byte[_bufferSize]; int ilen; while (streamsize > _bufferSize) { ilen = readStream.Read(ibuffer, 0, _bufferSize); ms.Write(ibuffer, 0, ilen); ms.Flush(); streamsize -= _bufferSize; } ilen = readStream.Read(ibuffer, 0, (int)streamsize); ms.Write(ibuffer, 0, ilen); ms.Flush(); } zr = zf.CloseReadStream(); } } zf.Dispose(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); ms = null; realEntry = null; } return(ms, realEntry); }