pmqs / Fix-OneDrive-Zip

Fix OneDrive Zip files >4Gig
257 stars 22 forks source link

Cannot find zip signature at the end of the file! #7

Closed Ankit89Git closed 1 year ago

pmqs commented 1 year ago

If my code is outputting that message it means that

  1. It had found the correct data at the start of the Zip file (which is good).

  2. but it is missing a vital part of the zip file at the end of the file.

Is there any chance that the download was terminated early? This error suggests that the file has been truncated.

If you want to dig deeper, get a copy of zipdetails. Run the following and post the output

zipdetails yourfile.zip

If that complains about not finding signatures, run this and post the output

zipdetails --scan yourfile.zip

Also, if you have access to unzip, run unzip -t yourfile.zip. That will test the zip file.

Alternatively, can you give me access to the zip file?

Ankit89Git commented 1 year ago

Hi Paul,

Thank you for your swift response.

Please find the output below inline.

Due to data privacy issues, I won’t be able to share access to the ZIP file. I am downloading files from OneDrive.

Best regards, Ankit

From: Paul Marquess @.> Sent: Thursday, June 1, 2023 12:41 PM To: pmqs/Fix-OneDrive-Zip @.> Cc: Ankit Ambasht @.>; Author @.> Subject: Re: [pmqs/Fix-OneDrive-Zip] Cannot find zip signature at the end of the file! (Issue #7)

If my code is outputting that message it means that

  1. It had found the correct data at the start of the Zip file (which is good).
  2. but it is missing a vital part of the zip file at the end of the file.

Is there any chance that the download was terminated early? This error suggests that the file has been truncated.

If you want to dig deeper, get a copy of zipdetailshttps://github.com/pmqs/IO-Compress/blob/master/bin/zipdetails. Run the following and post the output

zipdetails yourfile.zip

xxx 1966079 at C:\Strawberry\perl\bin/zipdetails line 1899

If that complains about not finding signatures, run this and post the output

zipdetails --scan yourfile.zip

Can't use an undefined value as a subroutine reference at C:\Strawberry\perl\bin/zipdetails line 755

Also, if you have access to unzip, run unzip -t yourfile.zip. That will test the zip file.

'unzip' is not recognized as an internal or external command, operable program or batch file.

Alternatively, can you give me access to the zip file?

— Reply to this email directly, view it on GitHubhttps://github.com/pmqs/Fix-OneDrive-Zip/issues/7#issuecomment-1571484365, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BAHRPU6BV6ROK3FVA45CKJDXJA6CDANCNFSM6AAAAAAYWO4GWU. You are receiving this because you authored the thread.Message ID: @.**@.>>

!/usr/bin/perl

zipdetails

#

Display info on the contents of a Zip file

#

use 5.010; # for unpack "Q<"

BEGIN {

Check for a 32-bit Perl

if (!eval { pack "Q", 1 }) {
    warn "zipdetails requires 64 bit integers, ",
            "this Perl has 32 bit integers.\n";
    exit(1);
}

}

BEGIN { pop @INC if $INC[-1] eq '.' } use strict; use warnings ; no warnings 'portable'; # for unpacking > 2^32 use feature 'state';

use IO::File; use Encode; use Getopt::Long;

my $VERSION = "2.104" ;

use constant MAX32 => 0xFFFFFFFF ;

Compression types

use constant ZIP_CM_STORE => 0 ; use constant ZIP_CM_IMPLODE => 6 ; use constant ZIP_CM_DEFLATE => 8 ; use constant ZIP_CM_BZIP2 => 12 ; use constant ZIP_CM_LZMA => 14 ; use constant ZIP_CM_PPMD => 98 ;

General Purpose Flag

use constant ZIP_GP_FLAG_ENCRYPTED_MASK => (1 << 0) ; use constant ZIP_GP_FLAG_STREAMING_MASK => (1 << 3) ; use constant ZIP_GP_FLAG_PATCHED_MASK => (1 << 5) ; use constant ZIP_GP_FLAG_STRONG_ENCRYPTED_MASK => (1 << 6) ; use constant ZIP_GP_FLAG_LZMA_EOS_PRESENT => (1 << 1) ; use constant ZIP_GP_FLAG_LANGUAGE_ENCODING => (1 << 11) ;

Internal File Attributes

use constant ZIP_IFA_TEXT_MASK => 1;

Signatures for each of the headers

use constant ZIP_LOCAL_HDR_SIG => 0x04034b50; use constant ZIP_DATA_HDR_SIG => 0x08074b50; use constant ZIP_CENTRAL_HDR_SIG => 0x02014b50; use constant ZIP_END_CENTRAL_HDR_SIG => 0x06054b50; use constant ZIP64_END_CENTRAL_REC_HDR_SIG => 0x06064b50; use constant ZIP64_END_CENTRAL_LOC_HDR_SIG => 0x07064b50; use constant ZIP_ARCHIVE_EXTRA_DATA_SIG => 0x08064b50; use constant ZIP_DIGITAL_SIGNATURE_SIG => 0x05054b50;

use constant ZIP_ARCHIVE_EXTRA_DATA_RECORD_SIG => 0x08064b50;

Extra sizes

use constant ZIP_EXTRA_HEADER_SIZE => 2 ; use constant ZIP_EXTRA_MAX_SIZE => 0xFFFF ; use constant ZIP_EXTRA_SUBFIELD_ID_SIZE => 2 ; use constant ZIP_EXTRA_SUBFIELD_LEN_SIZE => 2 ; use constant ZIP_EXTRA_SUBFIELD_HEADER_SIZE => ZIP_EXTRA_SUBFIELD_ID_SIZE + ZIP_EXTRA_SUBFIELD_LEN_SIZE; use constant ZIP_EXTRA_SUBFIELD_MAX_SIZE => ZIP_EXTRA_MAX_SIZE - ZIP_EXTRA_SUBFIELD_HEADER_SIZE;

my %ZIP_CompressionMethods = ( 0 => 'Stored', 1 => 'Shrunk', 2 => 'Reduced compression factor 1', 3 => 'Reduced compression factor 2', 4 => 'Reduced compression factor 3', 5 => 'Reduced compression factor 4', 6 => 'Imploded', 7 => 'Reserved for Tokenizing compression algorithm', 8 => 'Deflated', 9 => 'Enhanced Deflating using Deflate64(tm)', 10 => 'PKWARE Data Compression Library Imploding', 11 => 'Reserved by PKWARE', 12 => 'BZIP2', 13 => 'Reserved by PKWARE', 14 => 'LZMA', 15 => 'Reserved by PKWARE', 16 => 'IBM z/OS CMPSC Compression', 17 => 'Reserved by PKWARE', 18 => 'File is compressed using IBM TERSE (new)', 19 => 'IBM LZ77 z Architecture (PFS)', 93 => 'Zstandard', 94 => 'MP3', 95 => 'XZ', 96 => 'WinZip JPEG Compression', 97 => 'WavPack compressed data', 98 => 'PPMd version I, Rev 1', 99 => 'AES Encryption', );

my %OS_Lookup = ( 0 => "MS-DOS", 1 => "Amiga", 2 => "OpenVMS", 3 => "Unix", 4 => "VM/CMS", 5 => "Atari ST", 6 => "HPFS (OS/2, NT 3.x)", 7 => "Macintosh", 8 => "Z-System", 9 => "CP/M", 10 => "Windows NTFS or TOPS-20", 11 => "MVS or NTFS", 12 => "VSE or SMS/QDOS", 13 => "Acorn RISC OS", 14 => "VFAT", 15 => "alternate MVS", 16 => "BeOS", 17 => "Tandem", 18 => "OS/400", 19 => "OS/X (Darwin)", 30 => "AtheOS/Syllable", );

my %Lookup = ( ZIP_LOCAL_HDR_SIG, \&LocalHeader, ZIP_DATA_HDR_SIG, \&DataHeader, ZIP_CENTRAL_HDR_SIG, \&CentralHeader, ZIP_END_CENTRAL_HDR_SIG, \&EndCentralHeader, ZIP64_END_CENTRAL_REC_HDR_SIG, \&Zip64EndCentralHeader, ZIP64_END_CENTRAL_LOC_HDR_SIG, \&Zip64EndCentralLocator,

# TODO - Archive Encryption Headers & digital signature
#ZIP_ARCHIVE_EXTRA_DATA_RECORD_SIG
#ZIP_DIGITAL_SIGNATURE_SIG
#ZIP_ARCHIVE_EXTRA_DATA_SIG

);

my %Extras = ( 0x0001, ['ZIP64', \&decode_Zip64], 0x0007, ['AV Info', undef], 0x0008, ['Extended Language Encoding', undef], 0x0009, ['OS/2 extended attributes', undef], 0x000a, ['NTFS FileTimes', \&decode_NTFS_Filetimes], 0x000c, ['OpenVMS', undef], 0x000d, ['Unix', undef], 0x000e, ['Stream & Fork Descriptors', undef], 0x000f, ['Patch Descriptor', undef], 0x0014, ['PKCS#7 Store for X.509 Certificates', undef], 0x0015, ['X.509 Certificate ID and Signature for individual file', undef], 0x0016, ['X.509 Certificate ID for Central Directory', undef], 0x0017, ['Strong Encryption Header', undef], 0x0018, ['Record Management Controls', undef], 0x0019, ['PKCS#7 Encryption Recipient Certificate List', undef], 0x0020, ['Reserved for Timestamp record', undef], 0x0021, ['Policy Decryption Key Record', undef], 0x0022, ['Smartcrypt Key Provider Record', undef], 0x0023, ['Smartcrypt Policy Key Data Record', undef],

  # The Header ID mappings defined by Info-ZIP and third parties are:

  0x0065,  ['IBM S/390 attributes - uncompressed', \&decodeMVS],
  0x0066,  ['IBM S/390 attributes - compressed', undef],
  0x07c8,  ['Info-ZIP Macintosh (old, J. Lee)', undef],
  0x2605,  ['ZipIt Macintosh (first version)', undef],
  0x2705,  ['ZipIt Macintosh v 1.3.5 and newer (w/o full filename)', undef],
  0x2805,  ['ZipIt Macintosh v 1.3.5 and newer', undef],
  0x334d,  ["Info-ZIP Macintosh (new, D. Haase's 'Mac3' field)", undef],
  0x4154,  ['Tandem NSK', undef],
  0x4341,  ['Acorn/SparkFS (David Pilling)', undef],
  0x4453,  ['Windows NT security descriptor', \&decode_NT_security],
  0x4690,  ['POSZIP 4690', undef],
  0x4704,  ['VM/CMS', undef],
  0x470f,  ['MVS', undef],
  0x4854,  ['Theos, old inofficial port', undef],
  0x4b46,  ['FWKCS MD5 (see below)', undef],
  0x4c41,  ['OS/2 access control list (text ACL)', undef],
  0x4d49,  ['Info-ZIP OpenVMS (obsolete)', undef],
  0x4d63,  ['Macintosh SmartZIP, by Macro Bambini', undef],
  0x4f4c,  ['Xceed original location extra field', undef],
  0x5356,  ['AOS/VS (binary ACL)', undef],
  0x5455,  ['Extended Timestamp', \&decode_UT],
  0x554e,  ['Xceed unicode extra field', \&decode_Xceed_unicode],
  0x5855,  ['Info-ZIP Unix (original; also OS/2, NT, etc.)', \&decode_UX],
  0x5a4c,  ['ZipArchive Unicode Filename', undef],
  0x5a4d,  ['ZipArchive Offsets Array', undef],
  0x6375,  ['Info-ZIP Unicode Comment', \&decode_up ],
  0x6542,  ['BeOS (BeBox, PowerMac, etc.)', undef],
  0x6854,  ['Theos', undef],
  0x7075,  ['Info-ZIP Unicode Path', \&decode_up ],
  0x756e,  ['ASi Unix', undef],
  0x7441,  ['AtheOS (AtheOS/Syllable attributes)', undef],
  0x7855,  ['Unix Extra type 2', \&decode_Ux],
  0x7875,  ['Unix Extra Type 3', \&decode_ux],
  0x9901,  ['AES Encryption', \&decode_AES],
  0xa11e,  ['Data Stream Alignment', \&decode_DataStreamAlignment],
  0xA220,  ['Open Packaging Growth Hint', \&decode_GrowthHint ],
  0xCAFE,  ['Java Executable', \&decode_Java_exe],
  0xfb4a,  ['SMS/QDOS', undef],

   );

my $FH;

my $ZIP64 = 0 ; my $NIBBLES = 8; my $LocalHeaderCount = 0; my $CentralHeaderCount = 0;

my $START; my $OFFSET = 0 ; my $TRAILING = 0 ; my $PAYLOADLIMIT = 256; my $ZERO = 0 ; my $APK = 0 ; my $START_APK = 0; my $APK_LEN = 0;

my $SEEN = Seen->new();

sub prOff { my $offset = shift; my $s = offset($OFFSET); $OFFSET += $offset; return $s; }

sub offset { my $v = shift ;

sprintf("%0${NIBBLES}X", $v);

}

my ($OFF, $LENGTH, $CONTENT, $TEXT, $VALUE) ;

my $FMT1 ; my $FMT2 ;

sub setupFormat { my $wantVerbose = shift ; my $nibbles = shift;

my $width = '@' . ('>' x ($nibbles -1));
my $space = " " x length($width);

my $fmt ;

if ($wantVerbose) {

    $FMT1 = "
    format STDOUT =

$width $width ^<<<<<<<<<<<^<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \$OFF, \$LENGTH, \$CONTENT, \$TEXT, \$VALUE $space $space ^<<<<<<<<<<<^<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ \$CONTENT, \$TEXT, \$VALUE . ";

    $FMT2 = "
    format STDOUT =

$width $width ^<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \$OFF, \$LENGTH, \$CONTENT, \$TEXT, \$VALUE $space $space ^<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ \$CONTENT, \$TEXT, \$VALUE . " ;

}
else {

    $FMT1 = "
    format STDOUT =

$width ^<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \$OFF, \$TEXT, \$VALUE $space ^<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ \$TEXT, \$VALUE . ";

    $FMT2 = "
format STDOUT =

$width ^<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \$OFF, \$TEXT, \$VALUE $space ^<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ \$TEXT, \$VALUE . " ; }

eval "$FMT1";

$| = 1;

}

sub mySpr { my $format = shift ;

return "" if ! defined $format;
return $format unless @_ ;
return sprintf $format, @_ ;

}

sub out0 { my $size = shift; my $text = shift; my $format = shift;

$OFF     = prOff($size);
$LENGTH  = offset($size) ;
$CONTENT = '...';
$TEXT    = $text;
$VALUE   = mySpr $format,  @_;

write;

skip($FH, $size);

}

sub xDump { my $input = shift;

$input =~ tr/\0-\37\177-\377/./;
return $input;

}

sub hexDump { my $input = shift;

my $out = unpack('H*', $input) ;
$out =~ s#(..)# $1#g ;
$out =~ s/^ //;
$out = uc $out;

return $out;

}

sub hexValue { return sprintf("%X", $_[0]); }

sub out { my $data = shift; my $text = shift; my $format = shift;

my $size = length($data) ;

$OFF     = prOff($size);
$LENGTH  = offset($size) ;
$CONTENT = hexDump($data);
$TEXT    = $text;
$VALUE   = mySpr $format,  @_;

no warnings;

write;

}

sub out1 { my $text = shift; my $format = shift;

$OFF     = '';
$LENGTH  = '' ;
$CONTENT = '';
$TEXT    = $text;
$VALUE   = mySpr $format,  @_;

write;

}

sub out2 { my $data = shift ; my $text = shift ; my $format = shift;

my $size = length($data) ;
$OFF     = prOff($size);
$LENGTH  = offset($size);
$CONTENT = hexDump($data);
$TEXT    = $text;
$VALUE   = mySpr $format,  @_;

no warnings;
eval "$FMT2";
write ;
eval "$FMT1";

}

sub Value { my $letter = shift; my @value = @_;

if ($letter eq 'C')
  { return ***@***.***) }
elsif ($letter eq 'v')
  { return ***@***.***) }
elsif ($letter eq 'V')
  { return ***@***.***) }
elsif ($letter eq 'Q<')
  { return ***@***.***) }
else
  { die "here letter $letter"}

}

sub outer { my $name = shift ; my $unpack = shift ; my $size = shift ; my $cb1 = shift ; my $cb2 = shift ;

myRead(my $buff, $size);
my ***@***.***) = unpack $unpack, $buff;
my $hex = Value($unpack,  @value);

if (defined $cb1) {
    my $v ;
    if (ref $cb1 eq 'CODE') {
        $v = ***@***.***) ;
    }
    else {
        $v = $cb1 ;
    }

    $v = "'" . $v unless $v =~ /^'/;
    $v .= "'"     unless $v =~ /'$/;
    $hex .= " $v" ;
}

out $buff, $name, $hex ;

***@***.***)
    if defined $cb2 ;

return $value[0];

}

sub out_C { my $name = shift ; my $cb1 = shift ; my $cb2 = shift ;

outer($name, 'C', 1, $cb1, $cb2);

}

sub out_v { my $name = shift ; my $cb1 = shift ; my $cb2 = shift ;

outer($name, 'v', 2, $cb1, $cb2);

}

sub out_V { my $name = shift ; my $cb1 = shift ; my $cb2 = shift ;

outer($name, 'V', 4, $cb1, $cb2);

}

sub out_Q { my $name = shift ; my $cb1 = shift ; my $cb2 = shift ;

outer($name, 'Q<', 8, $cb1, $cb2);

}

sub outSomeData { my $size = shift; my $message = shift; my $redact = shift ;

if ($size > 0) {
    if ($size > $PAYLOADLIMIT) {
        my $before = $FH->tell();
        out0 $size, $message;
    } else {
        myRead(my $buffer, $size );
        $buffer = "X" x $size
            if $redact;
        out $buffer, $message, xDump $buffer ;
    }
}

}

sub unpackValue_C { Valuev(unpack "C", $[0]); }

sub ValueC { sprintf "%02X", $[0]; }

sub unpackValue_v { Valuev(unpack "v", $[0]); }

sub Valuev { sprintf "%04X", $[0]; }

sub unpackValue_V { ValueV(unpack "V", $[0]); }

sub ValueV { my $v = defined $[0] ? $_[0] : 0; sprintf "%08X", $v; }

sub unpackValueQ { my $v = unpack ("Q<", $[0]); Value_Q($v); }

sub Value_Q { my $v = shift ; sprintf "%016X", $v; }

sub read_Q { my $b ; myRead($b, 8); return ($b, unpack ("Q<" , $b)); }

sub read_V { my $b ; myRead($b, 4); return ($b, unpack ("V", $b)); }

sub read_v { my $b ; myRead($b, 2); return ($b, unpack "v", $b); }

sub read_C { my $b ; myRead($b, 1); return ($b, unpack "C", $b); }

sub seekTo { my $offset = shift ; my $loc = shift ;

$loc = SEEK_SET
    if ! defined $loc ;

$FH->seek($offset, $loc);
$OFFSET = $FH->tell();

}

sub scanForSignature { my %sigs = map { $ => 1 } map { substr $, 2, 2 } # don't want the initial "PK" map { pack "V", $_ } ( ZIP_LOCAL_HDR_SIG , ZIP_DATA_HDR_SIG , ZIP_CENTRAL_HDR_SIG , ZIP_END_CENTRAL_HDR_SIG , ZIP64_END_CENTRAL_REC_HDR_SIG , ZIP64_END_CENTRAL_LOC_HDR_SIG ,

ZIP_ARCHIVE_EXTRA_DATA_SIG ,

            # ZIP_DIGITAL_SIGNATURE_SIG         ,
            # ZIP_ARCHIVE_EXTRA_DATA_RECORD_SIG ,
    );

my $start = $FH->tell();

my $last = '';
my $offset = 0;
my $buffer ;
BUFFER:
while ($FH->read($buffer, 1024 * 1000))
{
    my $combine = $last . $buffer ;

    my $ix = 0;
    while (1)
    {
        $ix = index($combine, "PK", $ix) ;

        if ($ix == -1)
        {
            $last = '';
            next BUFFER;
        }

        my $rest = substr($combine, $ix + 2, 2);

        if (! $sigs{$rest})
        {
            $ix += 2;
            next;
        }

        # possible match
        my $here = $FH->tell();
        seekTo($here - length($combine) + $ix);

        return 1;
    }

    $last = substr($combine, $ix+4);
}

return 0;

}

my $is64In32 = 0;

my $opt_verbose = 0; my $opt_scan = 0; my $opt_Redact = 0; my $opt_utc = 0;

$Getopt::Long::bundling = 1 ;

GetOptions("h" => \&Usage, "v" => \$opt_verbose, "scan" => \$opt_scan, "redact" => \$opt_Redact, "utc" => \$opt_utc, "version" => sub { print "$VERSION\n"; exit }, ) or Usage("Invalid command line option\n");

Usage("No zipfile") unless @ARGV == 1;

my $filename = shift @ARGV;

die "$filename does not exist\n" unless -e $filename ;

die "$filename not a standard file\n" unless -f $filename ;

$FH = IO::File->new( "<$filename" ) or die "Cannot open $filename: $!\n";

my $FILELEN = -s $filename ; $TRAILING = -s $filename ; $NIBBLES = nibbles(-s $filename) ;

die "$filename too short to be a zip file\n" if $FILELEN < 22 ;

setupFormat($opt_verbose, $NIBBLES);

if(0) {

Sanity check that this is a Zip file

my ($buffer, $signature) = read_V();

warn "$filename doesn't look like a zip file\n"
    if $signature != ZIP_LOCAL_HDR_SIG ;
$FH->seek(0, SEEK_SET) ;

}

my @Messages = ();

if ($opt_scan) { my $foundCentralHeader = 0; my $lastEndsAt = 0;

while(scanForSignature())
{
    my $here = $FH->tell();

    my ($buffer, $signature) = read_V();

    # check for an APK header directly before the first central header
    if ($signature == ZIP_CENTRAL_HDR_SIG && $foundCentralHeader == 0)
    {
        $foundCentralHeader = 1;

        ($START_APK, $APK, $APK_LEN) = chckForAPKSigningBlock($FH, $here) ;

        if ($START_APK)
        {
            seekTo($lastEndsAt+4);

            scanApkBlock();
            seekTo($here);
            next;
        }

        seekTo($here + 4);
    }

    my $handler = $Lookup{$signature};
    $handler->($signature, $buffer);

    $lastEndsAt = $FH->tell();

    seekTo($here + 4) ;
}

dislayMessages();
exit;

}

our ($CdExists, $CdOffset, @CentralDirectory) = scanCentralDirectory($FH);

die "No Central Directory records found\n" if ! $CdExists ;

$OFFSET = 0 ; $FH->seek(0, SEEK_SET) ;

outSomeData($START, "PREFIX DATA") if defined $START && $START > 0 ;

my $skippedFrom = 0 ; my $skippedContent = 0 ;

while (1) { last if $FH->eof();

my $here = $FH->tell();
if ($here >= $TRAILING) {
    print "\n" ;
    outSomeData($FILELEN - $TRAILING, "TRAILING DATA");
    last;

}

my ($buffer, $signature) = read_V();

my $handler = $Lookup{$signature};

if (!defined $handler)
{
    if ***@***.***) {
        # Should be at offset that central directory says
        my $locOffset = $CentralDirectory[0][0];
        my $delta = $locOffset - $here ;

        if ($here + 4 == $locOffset ) {
            for (0 .. 3) {
                $FH->ungetc(ord(substr($buffer, $_, 1)))
            }
            outSomeData($delta, "UNEXPECTED PADDING");
            next;
        }
    }

    if ($here < $CdOffset)
    {
        if ($APK)
        {
            scanApkBlock();
            next;
        }

        # next
        #     if scanForSignature() ;

        $skippedFrom = $FH->tell() ;
        $skippedContent = $CdOffset - $skippedFrom ;

        printf "\nWARNING!\nZip local header not found at offset 0x%X\n", $skippedFrom;
        printf "Skipping 0x%x bytes to Central Directory...\n", $skippedContent;

        push @Messages,
            sprintf("Expected Zip header not found at offset 0x%X, ", $skippedFrom) .
            sprintf("skipped 0x%X bytes\n", $skippedContent);

        seekTo($CdOffset);

        next;
    }
    else
    {
        printf "\n\nUnexpected END at offset %08X, value %s\n", $here, Value_V($signature);

        last;
    }
}

$ZIP64 = 0 if $signature != ZIP_DATA_HDR_SIG ;
$handler->($signature, $buffer);

}

dislayMessages();

exit ;

sub dislayMessages { $SEEN->short_summary();

$SEEN->summary();

if ***@***.***)
{
    my $count = scalar @Messages ;
    print "\nWARNINGS\n\n";
    print "* $_\n" for @Messages ;
}

print "Done\n";

}

sub compressionMethod { my $id = shift ; Value_v($id) . " '" . ($ZIP_CompressionMethods{$id} || "Unknown Method") . "'" ; }

sub LocalHeader { my $signature = shift ; my $data = shift ;

my $from_offset = $FH->tell() - 4;

print "\n";
++ $LocalHeaderCount;
my $hexHdrCount = sprintf("%X", $LocalHeaderCount) ;
out $data, "LOCAL HEADER #$hexHdrCount" , Value_V($signature);

my $buffer;

my ($loc, $CDcompressedLength, $cdZip64, $zip64Sizes, $cdIndex, $cdEntryOffset) ;
if (! $opt_scan)
{
    ($loc, $CDcompressedLength, $cdZip64, $zip64Sizes, $cdIndex, $cdEntryOffset) = @{ shift @CentralDirectory } ;
    # my $cdi = sprintf("%X", $cdIndex) ;
    # out1 "CENTRAL HEADER #$cdi", sprintf "Offset %s\n", Value_Q($cdEntryOffset);
}

out_C  "Extract Zip Spec", \&decodeZipVer;
out_C  "Extract OS", \&decodeOS;

my ($bgp, $gpFlag) = read_v();
my ($bcm, $compressedMethod) = read_v();

out $bgp, "General Purpose Flag", Value_v($gpFlag) ;
GeneralPurposeBits($compressedMethod, $gpFlag);

out $bcm, "Compression Method",   compressionMethod($compressedMethod) ;

out_V "Last Mod Time", sub { getTime(_dosToUnixTime($_[0])) };

my $crc                = out_V "CRC";
my $compressedLength   = out_V "Compressed Length";
my $uncompressedLength = out_V "Uncompressed Length";
my $filenameLength     = out_v "Filename Length";
my $extraLength        = out_v "Extra Length";

my $filename ;
myRead($filename, $filenameLength);
outputFilename($filename);

my $cl64 = $compressedLength;
my %ExtraContext = ();
if ($extraLength)
{
    my @z64 = ($uncompressedLength, $compressedLength, 1, 1);
    $ExtraContext{Zip64} = ***@***.*** ;
    $ExtraContext{InCentralDir} = 0;
    walkExtra($extraLength, \%ExtraContext);
}

my @msg ;
# if ($cdZip64 && ! $ZIP64)
# {
#     # Central directory said this was Zip64
#     # some zip files don't have the Zip64 field in the local header
#     # seems to be a streaming issue.
#     push @msg, "Missing Zip64 extra field in Local Header #$hexHdrCount\n";

#     if (! $zip64Sizes)
#     {
#         # Central has a ZIP64 entry that doesn't have sizes
#         # Local doesn't have a Zip 64 at all
#         push @msg, "Unzip may complain about 'overlapped components' #$hexHdrCount\n";
#     }
#     else
#     {
#         $ZIP64 = 1
#     }
# }

my $size = 0;
$size = printAes(\%ExtraContext)
    if $compressedMethod == 99 ;

$size += printLzmaProperties()
    if $compressedMethod == ZIP_CM_LZMA ;

$CDcompressedLength = $compressedLength
    if $opt_scan ;

$CDcompressedLength -= $size;

if ($CDcompressedLength) {
    outSomeData($CDcompressedLength, "PAYLOAD", $opt_Redact) ;
}

if ($compressedMethod == 99) {
    my $auth ;
    myRead($auth, 10);
    out $auth, "AES Auth",  hexDump($auth);
}

print "WARNING: $_"
    for @msg;

push @Messages, @msg ;

# $SEEN->save("LOCAL HEADER #" . sprintf("%X", $LocalHeaderCount), $filename, $from_offset, $from_offset + $CDcompressedLength);

}

sub redactFilename { my $filename = shift;

# Redact everything apart from directory seperators
$filename =~ s([^/])(X)g
    if $opt_Redact;

return $filename;

}

sub outputFilename { my $filename = shift;

$filename = redactFilename($filename) ;

if (length $filename > 256)
{
    my $f = substr($filename, 0, 256) ;
    out $f, "Filename",  "'". $f . "' ...";
}
else
{
    out $filename, "Filename",  "'". $filename . "'";
}

}

sub CentralHeader { my $signature = shift ; my $data = shift ;

my $from_offset = $FH->tell() - 4;

++ $CentralHeaderCount;
print "\n";
out $data, "CENTRAL HEADER #" . sprintf("%X", $CentralHeaderCount) . "", Value_V($signature);
my $buffer;

out_C "Created Zip Spec", \&decodeZipVer;
out_C "Created OS", \&decodeOS;
out_C "Extract Zip Spec", \&decodeZipVer;
out_C "Extract OS", \&decodeOS;

my ($bgp, $gpFlag) = read_v();
my ($bcm, $compressedMethod) = read_v();

out $bgp, "General Purpose Flag", Value_v($gpFlag) ;
GeneralPurposeBits($compressedMethod, $gpFlag);

out $bcm, "Compression Method", compressionMethod($compressedMethod) ;

out_V "Last Mod Time", sub { getTime(_dosToUnixTime($_[0])) };

my $crc                = out_V "CRC";
my $compressedLength   = out_V "Compressed Length";
my $uncompressedLength = out_V "Uncompressed Length";
my $filenameLength     = out_v "Filename Length";
my $extraLength        = out_v "Extra Length";
my $comment_length     = out_v "Comment Length";
my $disk_start         = out_v "Disk Start";
my $int_file_attrib    = out_v "Int File Attributes";

out1 "[Bit 0]",  $int_file_attrib & 1 ? "1 Text Data" : "0 'Binary Data'";

my $ext_file_attrib    = out_V "Ext File Attributes";
out1 "[Bit 0]",  "Read-Only"
    if $ext_file_attrib & 0x01 ;
out1 "[Bit 1]",  "Hidden"
    if $ext_file_attrib & 0x02 ;
out1 "[Bit 2]",  "System"
    if $ext_file_attrib & 0x04 ;
out1 "[Bit 3]",  "Label"
    if $ext_file_attrib & 0x08 ;
out1 "[Bit 4]",  "Directory"
    if $ext_file_attrib & 0x10 ;
out1 "[Bit 5]",  "Archive"
    if $ext_file_attrib & 0x20 ;

my $lcl_hdr_offset     = out_V "Local Header Offset";

my $filename ;
myRead($filename, $filenameLength);
outputFilename($filename);

my %ExtraContext = ( CRC => $crc,
                     LocalHdrOffset   => $lcl_hdr_offset,
                     CompressedLength => $compressedLength);

if ($extraLength)
{
    my @z64 = ($uncompressedLength, $compressedLength, $lcl_hdr_offset, $disk_start);
    $ExtraContext{Zip64} = ***@***.*** ;
    $ExtraContext{InCentralDir} = 1;
    walkExtra($extraLength, \%ExtraContext);
}

if ($comment_length)
{
    my $comment ;
    myRead($comment, $comment_length);
    out $comment, "Comment",  "'". $comment . "'";
}

$SEEN->save("CENTRAL HEADER ref Local #" . sprintf("%X", $CentralHeaderCount), $filename, \%ExtraContext) ;

}

sub decodeZipVer { my $ver = shift ;

my $sHi = int($ver /10) ;
my $sLo = $ver % 10 ;

#out1 "Zip Spec", "$sHi.$sLo";
"$sHi.$sLo";

}

sub decodeOS { my $ver = shift ;

$OS_Lookup{$ver} || "Unknown" ;

}

sub Zip64EndCentralHeader { my $signature = shift ; my $data = shift ;

print "\n";
out $data, "ZIP64 END CENTRAL DIR RECORD", Value_V($signature);

my $buff;
myRead($buff, 8);

out $buff, "Size of record", unpackValue_Q($buff);

my $size  = unpack "Q<", $buff;

out_C  "Created Zip Spec", \&decodeZipVer;
out_C  "Created OS", \&decodeOS;
out_C  "Extract Zip Spec", \&decodeZipVer;
out_C  "Extract OS", \&decodeOS;
out_V  "Number of this disk";
out_V  "Central Dir Disk no";
out_Q  "Entries in this disk";
out_Q  "Total Entries";
out_Q  "Size of Central Dir";
out_Q  "Offset to Central dir";

# TODO -
if ($size != 44)
{
    push @Messages,  "Unsupported Size field in Zip64EndCentralHeader: should be 44, got $size\n"
}

}

sub Zip64EndCentralLocator { my $signature = shift ; my $data = shift ;

print "\n";
out $data, "ZIP64 END CENTRAL DIR LOCATOR", Value_V($signature);

out_V  "Central Dir Disk no";
out_Q  "Offset to Central dir";
out_V  "Total no of Disks";

}

sub EndCentralHeader { my $signature = shift ; my $data = shift ;

print "\n";
out $data, "END CENTRAL HEADER", Value_V($signature);

out_v "Number of this disk";
out_v "Central Dir Disk no";
out_v "Entries in this disk";
out_v "Total Entries";
out_V "Size of Central Dir";
out_V "Offset to Central Dir";
my $comment_length = out_v "Comment Length";

if ($comment_length)
{
    my $comment ;
    myRead($comment, $comment_length);
    out $comment, "Comment", "'$comment'";
}

}

sub DataHeader { my $signature = shift ; my $data = shift ;

print "\n";
out $data, "STREAMING DATA HEADER", Value_V($signature);

out_V "CRC";

if ($ZIP64)
{
    out_Q "Compressed Length" ;
    out_Q "Uncompressed Length" ;
}
else
{
    out_V "Compressed Length" ;
    out_V "Uncompressed Length" ;
}

}

sub GeneralPurposeBits { my $method = shift; my $gp = shift;

out1 "[Bit  0]", "1 'Encryption'" if $gp & ZIP_GP_FLAG_ENCRYPTED_MASK;

my %lookup = (
    0 =>    "Normal Compression",
    1 =>    "Maximum Compression",
    2 =>    "Fast Compression",
    3 =>    "Super Fast Compression");

if ($method == ZIP_CM_DEFLATE)
{
    my $mid = ($gp >> 1) & 0x03 ;

    out1 "[Bits 1-2]", "$mid '$lookup{$mid}'";
}

if ($method == ZIP_CM_LZMA)
{
    if ($gp & ZIP_GP_FLAG_LZMA_EOS_PRESENT) {
        out1 "[Bit 1]", "1 'LZMA EOS Marker Present'" ;
    }
    else {
        out1 "[Bit 1]", "0 'LZMA EOS Marker Not Present'" ;
    }
}

if ($method == ZIP_CM_IMPLODE) # Imploding
{
    out1 "[Bit 1]", ($gp & (1 << 1) ? "1 '8k" : "0 '4k") . " Sliding Dictionary'" ;
    out1 "[Bit 2]", ($gp & (2 << 1) ? "1 '3" : "0 '2"  ) . " Shannon-Fano Trees'" ;
}

out1 "[Bit  3]", "1 'Streamed'"           if $gp & ZIP_GP_FLAG_STREAMING_MASK;
out1 "[Bit  4]", "1 'Enhanced Deflating'" if $gp & 1 << 4;
out1 "[Bit  5]", "1 'Compressed Patched'" if $gp & 1 << 5 ;
out1 "[Bit  6]", "1 'Strong Encryption'"  if $gp & ZIP_GP_FLAG_STRONG_ENCRYPTED_MASK;
out1 "[Bit 11]", "1 'Language Encoding'"  if $gp & ZIP_GP_FLAG_LANGUAGE_ENCODING;
out1 "[Bit 12]", "1 'Pkware Enhanced Compression'"  if $gp & 1 <<12 ;
out1 "[Bit 13]", "1 'Encrypted Central Dir'"  if $gp & 1 <<13 ;

return ();

}

sub seekSet { my $fh = $[0] ; my $size = $[1];

use Fcntl qw(SEEK_SET);
seek($fh, $size, SEEK_SET);

}

sub skip { my $fh = $[0] ; my $size = $[1];

use Fcntl qw(SEEK_CUR);
seek($fh, $size, SEEK_CUR);

}

sub myRead { my $got = \$[0] ; my $size = $[1];

my $wantSize = $size;
$$got = '';

if ($size == 0)
{
    return ;
}

if ($size > 0)
{
    my $buff ;
    my $status = $FH->read($buff, $size);
    return $status
        if $status < 0;
    $$got .= $buff ;
}

my $len = length $$got;
die "Truncated file (got $len, wanted $wantSize): $!\n"
    if length $$got != $wantSize;

}

sub walkExtra { my $XLEN = shift; my $context = shift;

my $buff ;
my $offset = 0 ;

my $id;
my $subLen;
my $payload ;

my $count = 0 ;

while ($offset < $XLEN) {

    ++ $count;

    # Detect if there is not enough data for an extra ID and length.
    # Android zipalign and zipflinger are prime candidates for these
    # non-standard extra sub-fields.
    my $remaining = $XLEN - $offset;
    if ($remaining < ZIP_EXTRA_SUBFIELD_HEADER_SIZE) {
        # There is not enough left.
        # Consume whatever is there and return so parsing
        # can continue.
        myRead($payload, $remaining);
        my $data = hexDump($payload);

        if ($payload =~ /^\x00+$/)
        {
            out $payload, "Null Padding in Extra";
        }
        else
        {
            out $payload, "Malformed Extra Data", $data;
        }

        return undef;
    }

    myRead($id, ZIP_EXTRA_SUBFIELD_ID_SIZE);
    $offset += ZIP_EXTRA_SUBFIELD_ID_SIZE;
    my $lookID = unpack "v", $id ;
    if ($lookID == 0)
    {
        # check for null padding at end of extra
        my $here = $FH->tell();
        my $rest;
        myRead($rest, $XLEN - $offset);
        if ($rest =~ /^\x00+$/)
        {
            out $id . $rest, "Null Padding in Extra";
            return undef;
        }

        seekTo($here);
    }

    my ($who, $decoder) =  @{ $Extras{$lookID} // ['', undef ] };

    $who = "$id: $who"
        if $id =~ /\w\w/ ;

    $who = "'$who'";
    out $id, "Extra ID #" . Value_v($count), unpackValue_v($id) . " $who" ;

    myRead($buff, ZIP_EXTRA_SUBFIELD_LEN_SIZE);
    $offset += ZIP_EXTRA_SUBFIELD_LEN_SIZE;

    $subLen =  unpack("v", $buff);
    out2 $buff, "Length", Value_v($subLen) ;

    return undef
        if $offset + $subLen > $XLEN ;

    if (! defined $decoder)
    {
        if ($subLen)
        {
            myRead($payload, $subLen);
            my $data = hexDump($payload);

            out2 $payload, "Extra Payload", $data;
        }
    }
    else
    {
        $decoder->($subLen, $context) ;
    }

    $offset += $subLen ;
}

return undef ;

}

sub full32 { return $_[0] == MAX32 ; }

sub decode_Zip64 { my $len = shift; my $context = shift;

my $z64Data = $context->{Zip64};
my $inCentralHdr = $context->{InCentralDir} ;
my $inLocalHdr = ! $inCentralHdr ;

$ZIP64 = 1;

if ($inLocalHdr || full32 $z64Data->[0]) {
    out_Q "  Uncompressed Size";
}

if ($inLocalHdr || full32 $z64Data->[1]) {
    $context->{CompressedLength} = out_Q "  Compressed Size";
}

if (full32 $z64Data->[2] ) {
    $context->{LocalHdrOffset} = out_Q "  Offset to Local Dir";
}

if ($z64Data->[3] == 0xFFFF ) {
    out_V "  Disk Number";
}

}

sub Ntfs2Unix { my $m = shift; my $v = shift;

# NTFS offset is 19DB1DED53E8000

my $hex = Value_Q($v) ;
$v -= 0x19DB1DED53E8000 ;
my $ns = ($v % 10000000) * 100;
my $elapse = int ($v/10000000);
return "$hex '" . getT($elapse) .
       " " . sprintf("%0dns'", $ns);

}

sub decode_NTFS_Filetimes { my $len = shift; my $context = shift;

out_V "  Reserved";
out_v "  Tag1";
out_v "  Size1" ;

my ($m, $s1) = read_Q;
out $m, "  Mtime", Ntfs2Unix($m, $s1);

my ($a, $s3) = read_Q;
out $m, "  Atime", Ntfs2Unix($m, $s3);

my ($c, $s2) = read_Q;
out $c, "  Ctime", Ntfs2Unix($m, $s2);

}

sub getT { my $time = shift ;

if ($opt_utc)
 { return scalar gmtime($time) }
else
 { return scalar localtime($time) }

}

sub getTime { my $time = shift ;

return "'" . getT($time) . "'";

}

sub decode_UT { my $len = shift; my $context = shift;

my ($data, $flags) = read_C();

my $f = Value_C $flags;
$f .= " mod"    if $flags & 1;
$f .= " access" if $flags & 2;
$f .= " change" if $flags & 4;

out $data, "  Flags", "'$f'";

-- $len;

if ($flags & 1)
{
    my ($data, $time) = read_V();

    out2 $data, "Mod Time",    Value_V($time) . " " . getTime($time) ;

    $len -= 4 ;
}

  if ($flags & 2 && $len > 0 )
  {
      my ($data, $time) = read_V();

      out2 $data, "Access Time",    Value_V($time) . " " . getTime($time) ;
      $len -= 4 ;
  }

  if ($flags & 4 && $len > 0)
  {
      my ($data, $time) = read_V();

      out2 $data, "Change Time",    Value_V($time) . " " . getTime($time) ;
  }

}

sub decode_AES {

ref https://www.winzip.com/win/es/aes_info.html

my $len = shift;
my $context = shift;

return if $len == 0 ;

my %lookup = ( 1 => "AE-1", 2 => "AE-2");
out_v "  Vendor Version", sub {  $lookup{$_[0]} || "Unknown"  } ;

my $id ;
myRead($id, 2);
out $id, "  Vendor ID", unpackValue_v($id) . " '$id'";

my %strengths = (1 => "128-bit encryption key",
                 2 => "192-bit encryption key",
                 3 => "256-bit encryption key",
                );

my $strength = out_C "  Encryption Strength", sub {$strengths{$_[0]} || "Unknown" } ;

my ($bmethod, $method) = read_v();
out $bmethod, "  Compression Method", compressionMethod($method) ;

$context->{AesStrength} = $strength ;

}

sub decode_GrowthHint { my $len = shift; my $context = shift; my $inCentralHdr = $context->{InCentralDir} ;

return if $len == 0 ;

out_v "  Signature" ;
out_v "  Initial Value";

my $padding;
myRead($padding, $len - 4);
my $data = hexDump($padding);

out2 $padding, "Padding", $data;

}

sub decode_DataStreamAlignment { my $len = shift; my $context = shift; my $inCentralHdr = $context->{InCentralDir} ;

return if $len == 0 ;

my ($data, $alignment) = read_v();

out $data, "  Alignment", Value_v($alignment) ;

my $recompress_value = $alignment & 0x8000 ? 1 : 0;

my $recompressing = $recompress_value ? "True" : "False";
$alignment &= 0x7FFF ;
my $hexAl =  sprintf("%X", $alignment);

out1 "  [Bit   15]",  "$recompress_value    'Recompress $recompressing'";
out1 "  [Bits 0-14]", "$hexAl 'Minimal Alignment $alignment'";

if (! $inCentralHdr && $len - 2 > 0)
{
    my $padding;
    myRead($padding, $len - 2);

    out2 $padding, "Padding", hexDump($padding);
}

}

sub decode_UX { my $len = shift; my $context = shift; my $inCentralHdr = $context->{InCentralDir} ;

return if $len == 0 ;

my ($data, $time) = read_V();
out2 $data, "Access Time",    Value_V($time) . " " . getTime($time) ;

($data, $time) = read_V();
out2 $data, "Mod Time",    Value_V($time) . " " . getTime($time) ;

if (! $inCentralHdr ) {
    out_v "  UID" ;
    out_v "  GID";
}

}

sub decode_Ux { my $len = shift; my $context = shift;

return if $len == 0 ;
out_v "  UID" ;
out_v "  GID";

}

sub decodeLitteEndian { my $value = shift ;

if (length $value == 4)
{
    return Value_V unpack ("V", $value)
}
else {
    # TODO - fix this
    die "unsupported\n";
}

my $got = 0 ;
my $shift = 0;

#hexDump
#reverse
#my @a =unpack "C*", $value;
***@***.*** = reverse @a;
***@***.***);

for (reverse unpack "C*", $value)
{
    $got = ($got << 8) + $_ ;
}

return $got ;

}

sub decode_ux { my $len = shift; my $context = shift;

return if $len == 0 ;
out_C "  Version" ;
my $uidSize = out_C "  UID Size";
myRead(my $data, $uidSize);
out2 $data, "UID", decodeLitteEndian($data);

my $gidSize = out_C "  GID Size";
myRead($data, $gidSize);
out2 $data, "GID", decodeLitteEndian($data);

}

sub decode_Java_exe { my $len = shift; my $context = shift;

}

sub decode_up { my $len = shift; my $context = shift;

out_C "  Version";
out_V "  NameCRC32";

myRead(my $data, $len - 5);

out $data, "  UnicodeName", $data;

}

sub decode_Xceed_unicode { my $len = shift; my $context = shift;

my $data ;

# No public definition available, so guess the fields used for this one
# Data analysis based on output from Fiddler (https://www.telerik.com/fiddler),
# which uses this field

# First 4 bytes appear to be "NUCX" all the time
myRead($data, 4);
out $data, "  ID", $data;

# Next 2 bytes contains a count of the remaining bytes/2
# Probably the number of UTF-16 characters, including the
# optional NULL prefix
out_v "  Length";

# next is a UTF16 encode filename
# sometimes preceeded by a two Null bytes

myRead($data, $len - 6);

my $prefix = substr($data, 0, 2);
if ($prefix eq "\x00\x00")
{
    # Found the Null prefix
    out $prefix, "  Null", '0000';
    substr($data, 0, 2) = '';
}

out $data, "  UTF16LE Name", decode("UTF16LE", $data);

}

sub decode_NT_security { my $len = shift; my $context = shift; my $inCentralHdr = $context->{InCentralDir} ;

out_V "  Uncompressed Size" ;

if (! $inCentralHdr) {

    out_C "  Version" ;

    out_v "  Type";

    out_V "  NameCRC32" ;

    my $plen = $len - 4 - 1 - 2 - 4;
    myRead(my $payload, $plen);
    out $plen, "  Extra Payload", hexDump($payload);
}

}

sub decodeMVS { my $len = shift; my $context = shift;

# data in Big-Endian
myRead(my $data, $len);
my $ID = unpack("N", $data);

if ($ID == 0xE9F3F9F0)
{
    out($data, "  ID", "'Z390'");
    substr($data, 0, 4) = '';
}

out($data, "  Extra Payload", hexDump($data));

}

sub printAes {

ref https://www.winzip.com/win/es/aes_info.html

my $context = shift ;

my %saltSize = (
                    1 => 8,
                    2 => 12,
                    3 => 16,
                );

myRead(my $salt, $saltSize{$context->{AesStrength} });
out $salt, "AES Salt", hexDump($salt);
myRead(my $pwv, 2);
out $pwv, "AES Pwd Ver", hexDump($pwv);

return  $saltSize{$context->{AesStrength}} + 2 + 10;

}

sub printLzmaProperties { my $len = 0;

my $b1;
my $b2;
my $buffer;

myRead($b1, 2);
my ($verHi, $verLow) = unpack ("CC", $b1);

out $b1, "LZMA Version", sprintf("%02X%02X", $verHi, $verLow) . " '$verHi.$verLow'";
my $LzmaPropertiesSize = out_v "LZMA Properties Size";
$len += 4;

my $LzmaInfo = out_C "LZMA Info",  sub { $_[0] == 93 ? "(Default)" : ""};

my $PosStateBits = 0;
my $LiteralPosStateBits = 0;
my $LiteralContextBits = 0;
$PosStateBits = int($LzmaInfo / (9 * 5));
$LzmaInfo -= $PosStateBits * 9 * 5;
$LiteralPosStateBits = int($LzmaInfo / 9);
$LiteralContextBits = $LzmaInfo - $LiteralPosStateBits * 9;

out1 "  PosStateBits",        $PosStateBits;
out1 "  LiteralPosStateBits", $LiteralPosStateBits;
out1 "  LiteralContextBits",  $LiteralContextBits;

out_V "LZMA Dictionary Size";

# TODO - assumption that this is 5
$len += $LzmaPropertiesSize;

skip($FH, $LzmaPropertiesSize - 5)
    if  $LzmaPropertiesSize != 5 ;

return $len;

}

sub readFromOffset {

my $fh = shift;

my $offset = shift;
my $len = shift;

seekTo($offset) ;

my $buffer;
myRead($buffer, $len);
# $fh->read($buffer, $len) == $len
length $buffer == $len
    or return '';

return $buffer;

}

sub chckForAPKSigningBlock { my $fh = shift; my $cdOffset = shift;

# APK Signing Block comes directy before the Central directory

# If offset less than
#
#   len1     8
#   id       4
#   kv with zero len 8
#   len1     8
#   magic   16
#   ----------
#           44

return (0, 0, '')
    if $cdOffset < 44;

# Step 1 - 16 bytes before CD is literal string "APK Sig Block 42"
my $magicOffset = $cdOffset - 16;
my $buffer = readFromOffset($magicOffset, 16);

return (0, 0, '')
    if $buffer ne "APK Sig Block 42" ;

# Step 2 - read the two length fields
#          and check they are identical
$buffer = readFromOffset($cdOffset - 16 - 8, 8);
my $len2 = unpack("Q<", $buffer);

return (0, 0, '')
    if $len2 == 0 ;

my $startApkOffset = $cdOffset -  8 - $len2 ;

$buffer = readFromOffset($startApkOffset, 8);
my $len1 = unpack("Q<", $buffer);

return (0, 0, '')
    if $len1 != $len2;

return ($startApkOffset, $cdOffset - 16 - 8, $buffer);

}

sub scanApkBlock { state $IDs = { 0x7109871a => "APK Signature v2", 0xf05368c0 => "APK Signature v3", 0x42726577 => "Verity Padding Block", # from https://android.googlesource.com/platform/tools/apksig/+/master/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java 0x6dff800d => "Source Stamp", 0x504b4453 => "Dependency Info", 0x71777777 => "APK Channel Block", 0xff3b5998 => "Zero Block", 0x2146444e => "Play Metadata", } ;

seekTo($FH->tell() - 4) ;
print "\nAPK SIGNING BLOCK\n";

scanApkPadding('');
out_Q "Block Length Copy #1";
my $ix = 1;

while ($FH->tell() < $APK - 8)
{
     my ($bytes, $id, $len);
    ($bytes, $len) = read_Q ;
    out $bytes, "ID/Value Length #" . sprintf("%X", $ix), Value_Q($len);

    ($bytes, $id) = read_V;

    out $bytes, "  ID", Value_V($id) . " '" . ($IDs->{$id} // 'Unknown ID') . "'";

    outSomeData($len-4, "  Value");
    ++ $ix;
}

out_Q "Block Length Copy #2";

my $magic ;
myRead($magic, 16);

out $magic, "Magic", qq['$magic'];

}

sub scanApkPadding { my $bytes = shift ;

# padding
my $here = $FH->tell();
my $got;
myRead($got, $APK - $here);
$got = $bytes . $got;
if ($got =~ /^(\x00+)($APK_LEN)?/)
{
    my $len = length $1 ;
    seekTo($here - length($bytes));
    outSomeData($len, "Null Padding");
}
else
{
    seekTo($here);
}

}

sub scanCentralDirectory { my $fh = shift;

my $here = $fh->tell();

# Use cases
# 1 32-bit CD
# 2 64-bit CD

my @CD = ();
my $offset = findCentralDirectoryOffset($fh);

return ()
    if ! defined $offset;

$fh->seek($offset, SEEK_SET) ;

# Now walk the Central Directory Records
my $buffer ;
my $cdIndex = 0;
while ($fh->read($buffer, 46) == 46  &&
       unpack("V", $buffer) == ZIP_CENTRAL_HDR_SIG) {

    my $cdEntryOffset = $fh->tell() - 46;
    ++ $cdIndex ;

    my $compressedLength   = unpack("V", substr($buffer, 20, 4));
    my $uncompressedLength = unpack("V", substr($buffer, 24, 4));
    my $filename_length    = unpack("v", substr($buffer, 28, 2));
    my $extra_length       = unpack("v", substr($buffer, 30, 2));
    my $comment_length     = unpack("v", substr($buffer, 32, 2));
    my $locHeaderOffset    = unpack("V", substr($buffer, 42, 4));
    my $cdZip64 = 0;
    my $zip64Sizes = 0;

    skip($fh, $filename_length ) ;

    if ($extra_length)
    {
        $fh->read(my $extraField, $extra_length) ;
        # $self->smartReadExact(\$extraField, $extra_length);

        # Check for Zip64
        # my SizesExtended = findID("\x01\x00", $extraField);
        my $zip64Extended = findID(0x0001, $extraField);

        if ($zip64Extended)
        {
            $cdZip64 = 1;

            if ($uncompressedLength == MAX32)
            {
                $uncompressedLength = unpack "Q<", substr($zip64Extended, 0, 8, "");
                $zip64Sizes = 1;
            }
            if ($compressedLength == MAX32)
            {
                $compressedLength = unpack "Q<", substr($zip64Extended, 0, 8, "");
                $zip64Sizes = 1;

            }
            if ($locHeaderOffset == MAX32)
            {
                $locHeaderOffset = unpack "Q<", substr($zip64Extended, 0, 8, "");
            }
        }
    }

    my $got = [$locHeaderOffset, $compressedLength, $cdZip64, $zip64Sizes, $cdIndex, $cdEntryOffset] ;

    skip($fh, $comment_length ) ;

    push @CD, $got ;
}

$fh->seek($here, SEEK_SET) ;

# @CD = sort { $a->[0]->cmp($b->[0]) } @CD ;
@CD = sort { $a->[0] <=> $b->[0] } @CD ;

# Set the first Local File Header offset.
$START = $CD[0]->[0]
    if @CD ;

return (1, $offset, @CD);

}

sub offsetFromZip64 { my $fh = shift ; my $here = shift;

$fh->seek($here - 20, SEEK_SET)
# TODO - fix this
    or die "xx $!" ;

my $buffer;
my $got = 0;
($got = $fh->read($buffer, 20)) == 20
# TODO - fix this
    or die "xxx

     $here $got $!" ;

if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_LOC_HDR_SIG ) {
    my $cd64 = unpack "Q<", substr($buffer,  8, 8);

    $fh->seek($cd64, SEEK_SET) ;

    $fh->read($buffer, 4) == 4
    # TODO - fix this
        or die "xxx" ;

    if ( unpack("V", $buffer) == ZIP64_END_CENTRAL_REC_HDR_SIG ) {

        $fh->read($buffer, 8) ==  8
        # TODO - fix this
            or die "xxx" ;
        my $size  = unpack "Q<", $buffer;
        $fh->read($buffer, $size) ==  $size
        # TODO - fix this
            or die "xxx" ;

        my $cd64 =  unpack "Q<", substr($buffer,  36, 8);

        return $cd64 ;
    }

    die "Cannot find 'Zip64 end of central directory record': 0x06054b50\nTry running with --scan option.\n" ;
}

die "Cannot find signature for 'Zip64 end of central directory locator': 0x07064b50 \nTry running with --scan option.\n" ;

}

use constant Pack_ZIP_END_CENTRAL_HDR_SIG => pack("V", ZIP_END_CENTRAL_HDR_SIG);

sub findCentralDirectoryOffset { my $fh = shift ;

# Most common use-case is where there is no comment, so
# know exactly where the end of central directory record
# should be.

$fh->seek(-22, SEEK_END) ;
my $here = $fh->tell();

my $is64bit = $here > MAX32;
my $over64bit = $here  & (~ MAX32);

my $buffer;
$fh->read($buffer, 22) == 22
# TODO - fix this
    or die "xxx" ;

my $zip64 = 0;
my $centralDirOffset ;

if ( unpack("V", $buffer) == ZIP_END_CENTRAL_HDR_SIG ) {
    $centralDirOffset = unpack("V", substr($buffer, 16,  4));
}
else {
    $fh->seek(0, SEEK_END) ;

    my $fileLen = $fh->tell();
    my $want = 0 ;

    while(1) {
        $want += 1024 * 32;
        my $seekTo = $fileLen - $want;
        if ($seekTo < 0 ) {
            $seekTo = 0;
            $want = $fileLen ;
        }
        $fh->seek( $seekTo, SEEK_SET)
        # TODO - fix this
            or die "xxx $!" ;
        my $got;
        ($got = $fh->read($buffer, $want)) == $want
        # TODO - fix this
            or die "xxx $got  $!" ;
        my $pos = rindex( $buffer, Pack_ZIP_END_CENTRAL_HDR_SIG);

        if ($pos >= 0 && $want - $pos > 22) {
            $here = $seekTo + $pos ;
            $centralDirOffset = unpack("V", substr($buffer, $pos + 16,  4));
            my $commentLength = unpack("V", substr($buffer, $pos + 20,  2));
            $commentLength = 0 if ! defined $commentLength ;

            my $expectedEof = $fileLen - $want + $pos + 22 + $commentLength  ;
            # check for trailing data after end of zip
            if ($expectedEof < $fileLen ) {
                $TRAILING = $expectedEof ;
            }
            last ;
        }

        return undef
            if $want == $fileLen;
    }
}

if (full32 $centralDirOffset)
{
    $centralDirOffset = offsetFromZip64($fh, $here)
}
elsif ($is64bit)
{
    # use-case is where a 64-bit zip file doesn't use the 64-bit
    # extensions.
    print "EOCD not 64-bit $centralDirOffset ($here)\n" ;

    push @Messages,
        sprintf "Zip file > 4Gig. Expected 'Offset to Central Dir' to be 0xFFFFFFFF, got 0x%X\n", $centralDirOffset;

    $centralDirOffset += $over64bit;
    $is64In32 = 1;
}

($START_APK, $APK, $APK_LEN) = chckForAPKSigningBlock($fh, $centralDirOffset);

return $centralDirOffset ;

}

sub findID { my $id_want = shift ; my $data = shift;

my $XLEN = length $data ;

my $offset = 0 ;
while ($offset < $XLEN) {

    return undef
        if $offset + ZIP_EXTRA_SUBFIELD_HEADER_SIZE  > $XLEN ;

    my $id = substr($data, $offset, ZIP_EXTRA_SUBFIELD_ID_SIZE);
    $id = unpack("v", $id);
    $offset += ZIP_EXTRA_SUBFIELD_ID_SIZE;

    my $subLen =  unpack("v", substr($data, $offset,
                                        ZIP_EXTRA_SUBFIELD_LEN_SIZE));
    $offset += ZIP_EXTRA_SUBFIELD_LEN_SIZE ;

    return undef
        if $offset + $subLen > $XLEN ;

    return substr($data, $offset, $subLen)
        if $id eq $id_want ;

    $offset += $subLen ;
}

return undef ;

}

sub _dosToUnixTime { my $dt = shift;

my $year = ( ( $dt >> 25 ) & 0x7f ) + 80;
my $mon  = ( ( $dt >> 21 ) & 0x0f ) - 1;
my $mday = ( ( $dt >> 16 ) & 0x1f );

my $hour = ( ( $dt >> 11 ) & 0x1f );
my $min  = ( ( $dt >> 5  ) & 0x3f );
my $sec  = ( ( $dt << 1  ) & 0x3e );

use POSIX 'mktime';

# Force mktime to return a UTC time
local $ENV{TZ} = "UTC";

my $time_t = mktime( $sec, $min, $hour, $mday, $mon, $year, 0, 0, -1 );
return 0 if ! defined $time_t;
return $time_t;

}

sub nibbles { my @nibbles = ( [ 16 => 0x1000000000000000 ], [ 15 => 0x100000000000000 ], [ 14 => 0x10000000000000 ], [ 13 => 0x1000000000000 ], [ 12 => 0x100000000000 ], [ 11 => 0x10000000000 ], [ 10 => 0x1000000000 ], [ 9 => 0x100000000 ], [ 8 => 0x10000000 ], [ 7 => 0x1000000 ], [ 6 => 0x100000 ], [ 5 => 0x10000 ], [ 4 => 0x1000 ], [ 4 => 0x100 ], [ 4 => 0x10 ], [ 4 => 0x1 ], ); my $value = shift ;

for my $pair ***@***.***)
{
    my ($count, $limit) = @{ $pair };

    return $count
        if $value >= $limit ;
}

}

{ package Seen;

sub new
{
    my $class = shift ;

    my %object = ( overlaps     => [],
                   duplicates   => [],
                   detail       => [],
                   duplicate_count  => 0,
                   overlap_count    => 0,
                 ) ;

    bless \%object, $class;
}

sub save
{
    my $self = shift ;
    my $hdrId = shift;
    my $name = shift;
    my $extras = shift;

    my $from_offset = $extras->{LocalHdrOffset};
    my $to_offset = $from_offset ;
    $to_offset += $extras->{CompressedLength} - 1 ;

    my $crc = $extras->{CRC};

    $name = ::redactFilename($name) ;

    for my $entry ( @{ $self->{detail} } )
    {
        if ( $from_offset == $entry->{from} && $to_offset == $entry->{to} && $crc == $entry->{crc})
        {
            $self->{duplicate_count} ++;
            print "$hdrId: '$name' matches with $entry->{str}\n"
        }
        elsif ( ($from_offset >= $entry->{from} && $from_offset <= $entry->{to} ) ||
                ($to_offset   >= $entry->{from} && $to_offset   <= $entry->{to} )
           )
        {
            # die "overlap!"
            # push @{ $self->{overlap} },
            $self->{overlap_count} ++;

            print "$hdrId: '$name' overlaps with $entry->{str}\n";
        }
    }

    # warn "ADD $from_offset $to_offset $hdrId: $name\n";
    push @{ $self->{detail} },
         {
            from => $from_offset,
            to   => $to_offset,
            length => $extras->{CompressedLength},
            id   => $hdrId,
            crc  => $crc,
            name => $name,
            str  => "$hdrId: '$name'",
         } ;
}

sub short_summary
{
    my $self = shift;

    my $duplicates = $self->{duplicate_count};
    push @Messages, "$duplicates duplicate entries"
        if $duplicates;

    my $overlaps = $self->{overlap_count};
    push @Messages, "$overlaps overlap entries"
        if $overlaps;
}

sub summary
{
    my $self = shift ;

    # disable for now
    return;

    print "\n";
    for my $entry ( sort { $a->{from}->cmp($b->{from}) } @{ $self->{detail} } )

    {
        my $from_offset = $entry->{from};
        my $to_offset = $entry->{to};
        my $hdrId = $entry->{id};
        my $name = $entry->{name};
        print "$hdrId\t" . $entry->{from} . "\n";

    }
}

}

sub Usage { if (@) { warn "$\n" for @_ ; warn "\n"; }

die <<EOM;

zipdetails [OPTIONS] file

Display details about the internal structure of a Zip file.

This is zipdetails version $VERSION

OPTIONS -h display help --redact Hide filenames in the output --scan enable scannng mode. Blindly scan the file looking for zip headers Expect false-positives. --utc Display date/time fields in UTC -v Verbose - output more stuff --version Print version number

Copyright (c) 2011-2022 Paul Marquess. All rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. EOM

}

END

=head1 NAME

zipdetails - display the internal structure of zip files

=head1 SYNOPSIS

zipdetails [-v][--scan][--redact][--utc] zipfile.zip
zipdetails -h
zipdetails --version

=head1 DESCRIPTION

This program creates a detailed report on the internal structure of zip files. For each item of metadata within a zip file the program will output

=over 5

=item the offset into the zip file where the item is located.

=item a textual representation for the item.

=item an optional hex dump of the item.

=back

The program assumes a prior understanding of the internal structure of Zip files. You should have a copy of the Zip L<APPNOTE.TXT|http://www.pkware.com/documents/casestudies/APPNOTE.TXT> file at hand to help understand the output from this program.

=head2 Default Behaviour

By default the program expects to be given a well-formed zip file. It will navigate the Zip file by first parsing the zip central directory at the end of the file. If that is found, it will then walk through the zip records starting at the beginning of the file. Any badly formed zip data structures encountered are likely to terminate the program.

If the program finds any structural problems with the zip file it will print a summary at the end of the output report. The set of error cases reported is very much a work in progress, so don't rely on this feature to find all the possible errors in a zip file. If you have suggestions for use-cases where this could be enhanced please consider creating an enhancement request (see L<"SUPPORT">).

Date/time fields are found in zip files are displayed in local time. Use the C<--utc> option to display these fields in Coordinated Universal Time (UTC).

=head2 Scan-Mode

If you do have a potentially corrupt zip file, particulatly where the central directory at the end of the file is absent/incomplete, you can try usng the C<--scan> option to search for zip records that are still present.

When Scan-mode is enabled, the program will walk the zip file from the start, blindly looking for the 4-byte signatures that preceed each of the zip data structures. If it finds any of the recognised signatures it will attempt to dump the associated zip record. For very large zip files, this operation can take a long time to run.

Note that the 4-byte signatures used in zip files can sometimes match with random data stored in the zip file, so care is needed interpreting the results.

=head2 OPTIONS

=over 5

=item -h

Display help

=item --redact

Obscure filenames in the output. Handy for the use case where the zip files contains sensitive data that cannot be shared.

=item --scan

Walk the zip file loking for possible zip records. Can be error-prone. See L<"Scan-Mode">

=item --utc

By default, date/time fields are displayed in local time. Use this option to display them in in Coordinated Universal Time (UTC).

=item -v

Enable Verbose mode. See L<"Verbose Output">.

=item --version

Display version number of the program and exit.

=back

=head2 Default Output

By default zipdetails will output the details of the zip file in three columns.

=over 5

=item Column 1

This contains the offset from the start of the file in hex.

=item Column 2

This contains a textual description of the field.

=item Column 3

If the field contains a numeric value it will be displayed in hex. Zip stores most numbers in little-endian format - the value displayed will have the little-endian encoding removed.

Next, is an optional description of what the value means.

=back

For example, assuming you have a zip file with two entries, like this

$ unzip -l test.zip
Archive:  setup/test.zip
Length      Date    Time    Name
---------  ---------- -----   ----
        6  2021-03-23 18:52   latters.txt
        6  2021-03-23 18:52   numbers.txt
---------                     -------
    12                     2 files

Running C will gives this output

$ zipdetails test.zip

0000 LOCAL HEADER #1       04034B50
0004 Extract Zip Spec      0A '1.0'
0005 Extract OS            00 'MS-DOS'
0006 General Purpose Flag  0000
0008 Compression Method    0000 'Stored'
000A Last Mod Time         5277983D 'Tue Mar 23 19:01:58 2021'
000E CRC                   0F8A149C
0012 Compressed Length     00000006
0016 Uncompressed Length   00000006
001A Filename Length       000B
001C Extra Length          0000
001E Filename              'letters.txt'
0029 PAYLOAD               abcde.

002F LOCAL HEADER #2       04034B50
0033 Extract Zip Spec      0A '1.0'
0034 Extract OS            00 'MS-DOS'
0035 General Purpose Flag  0000
0037 Compression Method    0000 'Stored'
0039 Last Mod Time         5277983D 'Tue Mar 23 19:01:58 2021'
003D CRC                   261DAFE6
0041 Compressed Length     00000006
0045 Uncompressed Length   00000006
0049 Filename Length       000B
004B Extra Length          0000
004D Filename              'numbers.txt'
0058 PAYLOAD               12345.

005E CENTRAL HEADER #1     02014B50
0062 Created Zip Spec      1E '3.0'
0063 Created OS            03 'Unix'
0064 Extract Zip Spec      0A '1.0'
0065 Extract OS            00 'MS-DOS'
0066 General Purpose Flag  0000
0068 Compression Method    0000 'Stored'
006A Last Mod Time         5277983D 'Tue Mar 23 19:01:58 2021'
006E CRC                   0F8A149C
0072 Compressed Length     00000006
0076 Uncompressed Length   00000006
007A Filename Length       000B
007C Extra Length          0000
007E Comment Length        0000
0080 Disk Start            0000
0082 Int File Attributes   0001
     [Bit 0]               1 Text Data
0084 Ext File Attributes   81B40000
0088 Local Header Offset   00000000
008C Filename              'letters.txt'

0097 CENTRAL HEADER #2     02014B50
009B Created Zip Spec      1E '3.0'
009C Created OS            03 'Unix'
009D Extract Zip Spec      0A '1.0'
009E Extract OS            00 'MS-DOS'
009F General Purpose Flag  0000
00A1 Compression Method    0000 'Stored'
00A3 Last Mod Time         5277983D 'Tue Mar 23 19:01:58 2021'
00A7 CRC                   261DAFE6
00AB Compressed Length     00000006
00AF Uncompressed Length   00000006
00B3 Filename Length       000B
00B5 Extra Length          0000
00B7 Comment Length        0000
00B9 Disk Start            0000
00BB Int File Attributes   0001
     [Bit 0]               1 Text Data
00BD Ext File Attributes   81B40000
00C1 Local Header Offset   0000002F
00C5 Filename              'numbers.txt'

00D0 END CENTRAL HEADER    06054B50
00D4 Number of this disk   0000
00D6 Central Dir Disk no   0000
00
pmqs commented 1 year ago

Hi Paul, Thank you for your swift response. Please find the output below inline. Due to data privacy issues, I won’t be able to share access to the ZIP file. I am downloading files from OneDrive. Best regards, Ankit

Hey Ankit

you have just posted a copy of the zipdetails script itself -- I need the output you get when you run it. Also - I noticed that I pointed you to an old copy of the script -- use this version instead zipdetails

Ankit89Git commented 1 year ago

Hi Paul,

How do I save the file as an executable file? It’s currently text file.

Best regards, Ankit

From: Paul Marquess @.> Sent: Thursday, June 1, 2023 1:44 PM To: pmqs/Fix-OneDrive-Zip @.> Cc: Ankit Ambasht @.>; Author @.> Subject: Re: [pmqs/Fix-OneDrive-Zip] Cannot find zip signature at the end of the file! (Issue #7)

Hi Paul, Thank you for your swift response. Please find the output below inline. Due to data privacy issues, I won’t be able to share access to the ZIP file. I am downloading files from OneDrive. Best regards, Ankit

Hey Ankit

you have just posted a copy of the zipdetails script itself -- I need the output you get when you run it. Also - I noticed that I pointed you to an old copy of the script -- use this version instead zipdetailshttps://github.com/pmqs/zipdetails/blob/main/bin/zipdetails

— Reply to this email directly, view it on GitHubhttps://github.com/pmqs/Fix-OneDrive-Zip/issues/7#issuecomment-1571574073, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BAHRPUYOC56ODXWQ5HEKNETXJBFLNANCNFSM6AAAAAAYWO4GWU. You are receiving this because you authored the thread.Message ID: @.**@.>>

Ankit89Git commented 1 year ago

Paul,

I am getting this error message.

Best regards, Ankit

From: Paul Marquess @.> Sent: Thursday, June 1, 2023 1:44 PM To: pmqs/Fix-OneDrive-Zip @.> Cc: Ankit Ambasht @.>; Author @.> Subject: Re: [pmqs/Fix-OneDrive-Zip] Cannot find zip signature at the end of the file! (Issue #7)

Hi Paul, Thank you for your swift response. Please find the output below inline. Due to data privacy issues, I won’t be able to share access to the ZIP file. I am downloading files from OneDrive. Best regards, Ankit

Hey Ankit

you have just posted a copy of the zipdetails script itself -- I need the output you get when you run it. Also - I noticed that I pointed you to an old copy of the script -- use this version instead zipdetailshttps://github.com/pmqs/zipdetails/blob/main/bin/zipdetails

— Reply to this email directly, view it on GitHubhttps://github.com/pmqs/Fix-OneDrive-Zip/issues/7#issuecomment-1571574073, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BAHRPUYOC56ODXWQ5HEKNETXJBFLNANCNFSM6AAAAAAYWO4GWU. You are receiving this because you authored the thread.Message ID: @.**@.>>

pmqs commented 1 year ago

How do I save the file as an executable file? It’s currently text file

The program is a perl script -- it is supposed to be text. You just save it into a file called zipdetails

To run the script you need to have perl available. Not sure what platform you are running, but perl is widely available.

Assuming you have perl installed, run this from a terminal window

perl zipdetails yourfile.zip

If that complains, try with the walk option

perl zipdetails --walk yourfile.zip
pmqs commented 1 year ago

Explain reason for error in dd94a63249744e6fc17b28e5a14f8848d21a8709

Ankit89Git commented 1 year ago

Thanks, Paul for sharing the details.

So, it seems there is no option but to download again. Microsoft should do something about all these large files getting corrupted while downloading from OneDrive.

Best regards, Ankit

From: Paul Marquess @.> Sent: Saturday, June 10, 2023 3:47 AM To: pmqs/Fix-OneDrive-Zip @.> Cc: Ankit Ambasht @.>; Author @.> Subject: Re: [pmqs/Fix-OneDrive-Zip] Cannot find zip signature at the end of the file! (Issue #7)

Explain reason for error in dd94a63https://github.com/pmqs/Fix-OneDrive-Zip/commit/dd94a63249744e6fc17b28e5a14f8848d21a8709

— Reply to this email directly, view it on GitHubhttps://github.com/pmqs/Fix-OneDrive-Zip/issues/7#issuecomment-1585185688, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BAHRPU26BR4OWCHVQED47J3XKOOFNANCNFSM6AAAAAAYWO4GWU. You are receiving this because you authored the thread.Message ID: @.**@.>>

pmqs commented 1 year ago

@Ankit89Git there is one possibility I hadn't thought of.

If you get a chance, could you save the code below in "try.pl" and run it with your perl executable please

# Check for a 32-bit Perl
if (!eval { pack "Q", 1 }) {
    print "fix-onedrive-zip requires 64 bit integers, ",
            "this Perl has 32 bit integers.\n";
}

print "all is ok\n";

this is what I get

$ perl try.pl
all is ok

If you don't get the output all is ok, please get back to me.