/// <summary> /// ctor: Create class from data returned by the Reader /// </summary> /// <param name="reader">A binary data reader for this type of data - positioned at last page</param> public p4kEndOfCentralDirRecord(p4kRecReader reader) { // sanity check only System.Diagnostics.Trace.Assert(Marshal.SizeOf(typeof(MyRecord)) == RecordLength, "Record size does not match!(" + Marshal.SizeOf(typeof(MyRecord)).ToString( ) + ")"); if (reader.IsOpen( )) { try { long cPos = p4kSignatures.FindSignatureInPage(reader, p4kSignatures.EndOfCentralDirRecord); if (cPos >= 0) { m_recordOffset = cPos; reader.Seek(cPos); m_item = p4kRecReader.ByteToType <MyRecord>(reader.TheReader); m_itemValid = true; } } catch { m_itemValid = false; m_recordOffset = -1; } finally { if (!m_itemValid) { throw new OperationCanceledException(string.Format("EOF - cannot find EndOfCentralDirRecord")); } } } }
/// <summary> /// ctor: Create class from data returned by the Reader; starts reading at current stream position /// </summary> /// <param name="reader">A binary data reader for this type of data - positioned already</param> public p4kDirectoryEntry(p4kRecReader reader) { // sanity check only System.Diagnostics.Trace.Assert(Marshal.SizeOf(typeof(MyRecord)) == RecordLength, "Record size does not match!(" + Marshal.SizeOf(typeof(MyRecord)).ToString( ) + ")"); System.Diagnostics.Trace.Assert(Marshal.SizeOf(typeof(MyZ64ExtraRecord)) == Z64ExtraRecordLength, "Extra Record size does not match!(" + Marshal.SizeOf(typeof(MyZ64ExtraRecord)).ToString( ) + ")"); if (reader.IsOpen( )) { try { // get the next Directory record long cPos = p4kSignatures.FindSignatureInPage(reader, p4kSignatures.CentralDirRecord); if (cPos >= 0) { m_recordOffset = cPos; reader.Seek(cPos); m_item = p4kRecReader.ByteToType <MyRecord>(reader.TheReader); m_itemValid = true; // implicite from Find.. } // get some file attributes if (m_itemValid) { if (m_item.FilenameLength > 0) { ReadFilename(reader); } if (m_item.ExtraFieldLength > 0) { ReadExtradata(reader); // Likely Zip64 extensions } m_fileDateTime = p4kFileTStamp.FromDos(m_item.LastModDate, m_item.LastModTime); // get the file time given in the container // check if standard fields or extension is used for size information if (m_item.CompressedSize < 0xffffffff) { m_fileSizeComp = m_item.CompressedSize; m_fileSizeUnComp = m_item.UncompressedSize; } else { m_fileSizeComp = (long)m_z64Item.CompressedSize; m_fileSizeUnComp = (long)m_z64Item.UncompressedSize; } } } catch (Exception e) { m_itemValid = false; m_recordOffset = -1; } finally { if (!m_itemValid) { throw new OperationCanceledException(string.Format("EOF - cannot find CentralDirRec in this page")); } } } }
/// <summary> /// Read the extra data /// </summary> /// <param name="reader"></param> private void ReadExtradata(p4kRecReader reader) { // first the Zip64 extra record m_z64Item = p4kRecReader.ByteToType <MyZ64ExtraRecord>(reader.TheReader); // then the rest of the extra record (is another item with tag 0x0666 and the rest lenght (ignored) m_extraBytes = new byte[m_item.ExtraFieldLength - Z64ExtraRecordLength]; m_extraBytes = reader.ReadBytes(m_extraBytes.Length); m_extraBytes = null; // dump it ... // now we would be able to read the file content }
// 4.3.6 Overall.ZIP file format: //[local file header 1] //[encryption header 1] //[file data 1] //[data descriptor 1] // . // . // . //[local file header n] //[encryption header n] //[file data n] //[data descriptor n] //[archive decryption header] //[archive extra data record] //[central directory header 1] // . // . // . //[central directory header n] //[zip64 end of central directory record] //[zip64 end of central directory locator] //[end of central directory record] /// <summary> /// Retrieve the file given by the descriptor (from our list) /// and return the content as string /// </summary> /// <param name="file">A file descriptor from this list</param> /// <returns>The content of the file or an empty string</returns> public byte[] GetFile(string p4kFilename, p4kFile file) { if (!File.Exists(p4kFilename)) { return new byte[] { } } ; using (p4kRecReader reader = new p4kRecReader(p4kFilename)) { return(file.GetFile(reader)); } }
/// <summary> /// Return the file related to this entry /// </summary> /// <param name="reader">An open p4k File reader</param> /// <returns>The content of the file or an empty string</returns> public byte[] GetFile(p4kRecReader reader) { if (!m_itemValid) { return new byte[] { } } ; // ERROR cannot.. reader.Seek(FileHeaderOffset); p4kFileHeader p4Kfh = new p4kFileHeader(reader); return(p4Kfh.GetFile(reader)); }
/// <summary> /// Returns the position of the Signature within the stream /// Searches one page from current location starting at the end of the page /// </summary> /// <param name="reader">A positioned reader</param> /// <returns>The position within the stream or -1 if not found</returns> public static long FindSignatureInPageBackwards(p4kRecReader reader, byte[] signature) { long pos = reader.Position; byte[] lPage = reader.GetPage( ); for (int i = lPage.Length - 4; i > 0; i--) { if (lPage.Skip(i).Take(4).SequenceEqual(signature)) { // now this should be the start of the item [end of central directory record] return(pos + i); } } return(-1); // not found... }
public static readonly byte[] EndOfCentralDirRecord = { 0x50, 0x4B, 0x05, 0x06 }; // (0x06054b50) 4.3.16 End of central directory record: /// <summary> /// Returns the position of the Signature within the stream /// Searches one page from current location /// </summary> /// <param name="reader">A positioned reader</param> /// <returns>The position within the stream or -1 if not found</returns> public static long FindSignatureInPage(p4kRecReader reader, byte[] signature) { long pos = reader.Position; byte[] lPage = reader.GetPage( ); for (int i = 0; i < lPage.Length - 4; i++) { if (lPage.Skip(i).Take(4).SequenceEqual(signature)) { // now this should be the start of the item return(pos + i); } } return(-1); // not found... }
/// <summary> /// ctor: Create class from data returned by the Reader /// </summary> /// <param name="reader">A binary data reader for this type of data - positioned at last page</param> public p4kEndOfCentralDirRecord(p4kRecReader reader) { // sanity check only System.Diagnostics.Trace.Assert(Marshal.SizeOf(typeof(MyRecord)) == RecordLength, "Record size does not match!(" + Marshal.SizeOf(typeof(MyRecord)).ToString( ) + ")"); if (reader.IsOpen( )) { try { long thisPos = 0; // 20180804BM- fix when the last page is barely used and the sig is one or more pages before // - backup one or more pages to find end of dir (max 10 times) int tries = 10; while (!m_itemValid && (tries > 0)) { thisPos = reader.Position; long cPos = p4kSignatures.FindSignatureInPage(reader, p4kSignatures.EndOfCentralDirRecord); if (cPos >= 0) { m_recordOffset = cPos; reader.Seek(cPos); m_item = p4kRecReader.ByteToType <MyRecord>(reader.TheReader); m_itemValid = true; } else { // backup one page again reader.Seek(thisPos); reader.BackwardPage( ); tries--; } } } catch { m_itemValid = false; m_recordOffset = -1; } finally { if (!m_itemValid) { throw new OperationCanceledException(string.Format("EOF - cannot find EndOfCentralDirRecord")); } } } }
/// <summary> /// Return the file related to this entry /// </summary> /// <param name="reader">An open p4k File reader</param> /// <returns>The content of the file or an empty string</returns> public byte[] GetFile(p4kRecReader reader) { if (!m_itemValid) { return new byte[] { } } ; // ERROR cannot.. reader.Seek(m_fileOffset); // ?? big files may have trouble here - may be we need to read and write chunks for this // but for now we only want to get XMLs out and that is OK with byte alloc on the heap byte[] fileBytes = new byte[m_fileSizeComp]; fileBytes = reader.ReadBytes(fileBytes.Length); byte[] decompFile = null; if (m_item.CompressionMethod == 0x64) { // this indicates p4k ZStd compression using (var decompressor = new Decompressor( )) { try { decompFile = decompressor.Unwrap(fileBytes); return(decompFile); } catch (ZstdException e) { Console.WriteLine("ZStd - Cannot decode file: " + m_filename); Console.WriteLine("Error: " + e.Message); //Console.ReadLine(); return(new byte[] { }); } } } else { // plain write - might be wrong if another compression was applied.. decompFile = fileBytes; return(decompFile); } }
/// <summary> /// Scans directory entries and return the a file descriptor (string.EndsWith is used) /// </summary> /// <param name="p4kFilename">The p4k file</param> /// <param name="filename">The filename to look for</param> public p4kFile ScanDirectoryFor(string p4kFilename, string filename) { if (!File.Exists(p4kFilename)) { return(null); } using (p4kRecReader reader = new p4kRecReader(p4kFilename)) { // work from the end of the file reader.GotoLastPage( ); m_endOfCentralDirRecord = new p4kEndOfCentralDirRecord(reader); // position first reader.Seek(m_endOfCentralDirRecord.RecordOffset - p4kRecReader.PageSize); m_z64EndOfCentralDirLocator = new p4kZ64EndOfCentralDirLocator(reader); // for the next the position should be found already - seek it reader.Seek(m_z64EndOfCentralDirLocator.Z64EndOfCentralDir); m_z64EndOfCentralDirRecord = new p4kZ64EndOfCentralDirRecord(reader); // now we should have the start of the directory entries... // position first reader.Seek(m_z64EndOfCentralDirRecord.Z64StartOfCentralDir); // loop all file - as per dir reporting for (long i = 0; i < m_z64EndOfCentralDirRecord.NumberOfEntries; i++) { p4kDirectoryEntry de = new p4kDirectoryEntry(reader); if (!string.IsNullOrEmpty(filename) && de.Filename.ToLower( ).EndsWith(filename.ToLower( ))) { var p = new p4kFile(de); // FOUND reader.TheReader.Close( ); return(p); // bail out if found } } } return(null); }
/// <summary> /// Scans directory entries and return the a list of matching file descriptors (string.Contains is used) /// </summary> /// <param name="p4kFilename">The p4k file</param> /// <param name="filenamepart">The filename part to look for</param> public IList <p4kFile> ScanDirectoryContaining(string p4kFilename, string filenamepart) { if (!File.Exists(p4kFilename)) { return(null); } List <p4kFile> fileList = new List <p4kFile>( ); using (p4kRecReader reader = new p4kRecReader(p4kFilename)) { // work from the end of the file reader.GotoLastPage( ); m_endOfCentralDirRecord = new p4kEndOfCentralDirRecord(reader); // position first reader.Seek(m_endOfCentralDirRecord.RecordOffset - p4kRecReader.PageSize); m_z64EndOfCentralDirLocator = new p4kZ64EndOfCentralDirLocator(reader); // for the next the position should be found already - seek it reader.Seek(m_z64EndOfCentralDirLocator.Z64EndOfCentralDir); m_z64EndOfCentralDirRecord = new p4kZ64EndOfCentralDirRecord(reader); // now we should have the start of the directory entries... // position first reader.Seek(m_z64EndOfCentralDirRecord.Z64StartOfCentralDir); // loop all file - as per dir reporting for (long i = 0; i < m_z64EndOfCentralDirRecord.NumberOfEntries; i++) { p4kDirectoryEntry de = new p4kDirectoryEntry(reader); if (!string.IsNullOrEmpty(filenamepart) && de.Filename.ToLower( ).Contains(filenamepart.ToLower( ))) { var p = new p4kFile(de); // FOUND fileList.Add(p); } } } return(fileList); }
/// <summary> /// Get the filename from the data /// </summary> /// <param name="reader">The open and positioned reader</param> private void ReadFilename(p4kRecReader reader) { byte[] fileNameBytes = new byte[m_item.FilenameLength]; fileNameBytes = reader.ReadBytes(m_item.FilenameLength); m_filename = Encoding.ASCII.GetString(fileNameBytes); }
//4.3.7 Local file header: // local file header signature 4 bytes(0x04034b50) // version needed to extract 2 bytes // general purpose bit flag 2 bytes // compression method 2 bytes // last mod file time 2 bytes // last mod file date 2 bytes // crc-32 4 bytes // compressed size 4 bytes // uncompressed size 4 bytes // file name length 2 bytes // extra field length 2 bytes // file name( variable size ) // extra field( variable size ) //4.3.8 File data // Immediately following the local header for a file // SHOULD be placed the compressed or stored data for the file. // If the file is encrypted, the encryption header for the file // SHOULD be placed after the local header and before the file // data. The series of[local file header][encryption header] // [file data][data descriptor] repeats for each file in the // .ZIP archive. // Zero-byte files, directories, and other file types that // contain no content MUST not include file data. // 4.5.3 -Zip64 Extended Information Extra Field(0x0001): // The following is the layout of the zip64 extended // information "extra" block.If one of the size or // offset fields in the Local or Central directory // record is too small to hold the required data, // a Zip64 extended information record is created. // The order of the fields in the zip64 extended // information record is fixed, but the fields MUST // only appear if the corresponding Local or Central // directory record field is set to 0xFFFF or 0xFFFFFFFF. // Note: all fields stored in Intel low - byte / high - byte order. // Value Size Description // ---- - --------------- //(ZIP64)0x0001 2 bytes Tag for this "extra" block type // Size 2 bytes Size of this "extra" block // Original // Size 8 bytes Original uncompressed file size // Compressed // Size 8 bytes Size of compressed data // Relative Header // Offset 8 bytes Offset of local header record // Disk Start // Number 4 bytes Number of the disk on which // this file starts // This entry in the Local header MUST include BOTH original // and compressed file size fields.If encrypting the // central directory and bit 13 of the general purpose bit // flag is set indicating masking, the value stored in the // Local Header for the original file size will be zero. /// <summary> /// ctor: Create class from data returned by the Reader /// </summary> /// <param name="reader">A binary data reader for this type of data</param> public p4kFileHeader(p4kRecReader reader) { // sanity check only System.Diagnostics.Trace.Assert(Marshal.SizeOf(typeof(MyRecord)) == RecordLength, "Record size does not match!(" + Marshal.SizeOf(typeof(MyRecord)).ToString( ) + ")"); System.Diagnostics.Trace.Assert(Marshal.SizeOf(typeof(MyZ64ExtraRecord)) == Z64ExtraRecordLength, "Extra Record size does not match!(" + Marshal.SizeOf(typeof(MyZ64ExtraRecord)).ToString( ) + ")"); if (reader.IsOpen( )) { try { long cPos = reader.Position; do { // Fileheaders are Page aligned - scan to find one reader.AdvancePage( ); // to next page cPos = reader.Position; string cPosS = cPos.ToString("X"); m_recordOffset = cPos; m_item = p4kRecReader.ByteToType <MyRecord>(reader.TheReader); m_itemValid = m_item.ID.SequenceEqual(p4kSignatures.LocalFileHeaderCry); } while ((cPos < reader.Length) && !m_itemValid); // get some file attributes if (m_itemValid) { if (m_item.FilenameLength > 0) { ReadFilename(reader); } if (m_item.ExtraFieldLength > 0) { ReadExtradata(reader); // Likely Zip64 extensions } m_fileDateTime = p4kFileTStamp.FromDos(m_item.LastModDate, m_item.LastModTime); // check if standard fields or extension is used for size information if (m_item.CompressedSize < 0xffffffff) { m_fileSizeComp = m_item.CompressedSize; m_fileSizeUnComp = m_item.UncompressedSize; } else { m_fileSizeComp = (long)m_z64Item.CompressedSize; m_fileSizeUnComp = (long)m_z64Item.UncompressedSize; } // now we would be able to read the file content // but we skip it for now to process the next header m_fileOffset = reader.TheReader.BaseStream.Position; // save position of this item reader.TheReader.BaseStream.Seek(m_fileSizeComp, SeekOrigin.Current); } else { // actually invalid but good manner .. m_recordOffset = -1; m_fileOffset = -1; m_fileSizeComp = 0; m_fileSizeUnComp = 0; } } catch { m_itemValid = false; } finally { if (!m_itemValid) { if (m_item.ID.SequenceEqual(p4kSignatures.CentralDirRecord)) { // read beyond the file entries throw new OperationCanceledException(string.Format("EOF - found Central Directory header {0}", m_item.ID.ToString( ))); } else { // other error throw new NotSupportedException(string.Format("Cannot process fileheader ID {0}", m_item.ID.ToString( ))); } } } } }