content: /*
   * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package com.itsaky.androidide.zipfs2;
  
  import java.nio.file.Paths;
  import java.util.Collections;
  import java.util.Map;
  
  import static com.itsaky.androidide.zipfs2.ZipConstants.*;
  import static com.itsaky.androidide.zipfs2.ZipUtils.dosToJavaTime;
  import static com.itsaky.androidide.zipfs2.ZipUtils.unixToJavaTime;
  import static com.itsaky.androidide.zipfs2.ZipUtils.winToJavaTime;
  
  /**
   * Print all loc and cen headers of the ZIP file
   *
   * @author Xueming Shen
   */
  public class ZipInfo {
  
      public static void main(String[] args) throws Throwable {
          if (args.length < 1) {
              print("Usage: java ZipInfo zfname");
          } else {
              Map<String, ?> env = Collections.emptyMap();
              ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider()
                                      .newFileSystem(Paths.get(args[0]), env));
              byte[] cen = zfs.cen;
              if (cen == null) {
                  print("zip file is empty%n");
                  return;
              }
              int    pos = 0;
              byte[] buf = new byte[1024];
              int    no = 1;
              while (pos + CENHDR < cen.length) {
                  print("----------------#%d--------------------%n", no++);
                  printCEN(cen, pos);
  
                  // use size CENHDR as the extra bytes to read, just in case the
                  // loc.extra is bigger than the cen.extra, try to avoid to read
                  // twice
                  long len = LOCHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENHDR;
                  if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
                      ZipFileSystem.zerror("read loc header failed");
                  if (LOCEXT(buf) > CENEXT(cen, pos) + CENHDR) {
                      // have to read the second time;
                      len = LOCHDR + LOCNAM(buf) + LOCEXT(buf);
                      if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
                          ZipFileSystem.zerror("read loc header failed");
                  }
                  printLOC(buf);
                  pos += CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos);
              }
              zfs.close();
          }
      }
  
      static void print(String fmt, Object... objs) {
          System.out.printf(fmt, objs);
      }
  
      static void printLOC(byte[] loc) {
          print("%n");
          print("[Local File Header]%n");
          print("    Signature   :   %#010x%n", LOCSIG(loc));
          if (LOCSIG(loc) != LOCSIG) {
             print("    Wrong signature!");
             return;
          }
          print("    Version     :       %#6x    [%d.%d]%n",
                    LOCVER(loc), LOCVER(loc) / 10, LOCVER(loc) % 10);
          print("    Flag        :       %#6x%n", LOCFLG(loc));
          print("    Method      :       %#6x%n", LOCHOW(loc));
          print("    LastMTime   :   %#10x    [%tc]%n",
                LOCTIM(loc), dosToJavaTime(LOCTIM(loc)));
          print("    CRC         :   %#10x%n", LOCCRC(loc));
          print("    CSize       :   %#10x%n", LOCSIZ(loc));
          print("    Size        :   %#10x%n", LOCLEN(loc));
          print("    NameLength  :       %#6x    [%s]%n",
                    LOCNAM(loc), new String(loc, LOCHDR, LOCNAM(loc)));
          print("    ExtraLength :       %#6x%n", LOCEXT(loc));
          if (LOCEXT(loc) != 0)
              printExtra(loc, LOCHDR + LOCNAM(loc), LOCEXT(loc));
      }
  
      static void printCEN(byte[] cen, int off) {
          print("[Central Directory Header]%n");
          print("    Signature   :   %#010x%n", CENSIG(cen, off));
          if (CENSIG(cen, off) != CENSIG) {
             print("    Wrong signature!");
             return;
          }
          print("    VerMadeby   :       %#6x    [%d, %d.%d]%n",
                CENVEM(cen, off), (CENVEM(cen, off) >> 8),
                (CENVEM(cen, off) & 0xff) / 10,
                (CENVEM(cen, off) & 0xff) % 10);
          print("    VerExtract  :       %#6x    [%d.%d]%n",
                CENVER(cen, off), CENVER(cen, off) / 10, CENVER(cen, off) % 10);
          print("    Flag        :       %#6x%n", CENFLG(cen, off));
          print("    Method      :       %#6x%n", CENHOW(cen, off));
          print("    LastMTime   :   %#10x    [%tc]%n",
                CENTIM(cen, off), dosToJavaTime(CENTIM(cen, off)));
          print("    CRC         :   %#10x%n", CENCRC(cen, off));
          print("    CSize       :   %#10x%n", CENSIZ(cen, off));
          print("    Size        :   %#10x%n", CENLEN(cen, off));
          print("    NameLen     :       %#6x    [%s]%n",
                CENNAM(cen, off), new String(cen, off + CENHDR, CENNAM(cen, off)));
          print("    ExtraLen    :       %#6x%n", CENEXT(cen, off));
          if (CENEXT(cen, off) != 0)
              printExtra(cen, off + CENHDR + CENNAM(cen, off), CENEXT(cen, off));
          print("    CommentLen  :       %#6x%n", CENCOM(cen, off));
          print("    DiskStart   :       %#6x%n", CENDSK(cen, off));
          print("    Attrs       :       %#6x%n", CENATT(cen, off));
          print("    AttrsEx     :   %#10x%n", CENATX(cen, off));
          print("    LocOff      :   %#10x%n", CENOFF(cen, off));
  
      }
  
      static long locoff(byte[] cen, int pos) {
          long locoff = CENOFF(cen, pos);
          if (locoff == ZIP64_MINVAL) {    //ZIP64
              int off = pos + CENHDR + CENNAM(cen, pos);
              int end = off + CENEXT(cen, pos);
              while (off + 4 < end) {
                  int tag = SH(cen, off);
                  int sz = SH(cen, off + 2);
                  if (tag != EXTID_ZIP64) {
                      off += 4 + sz;
                      continue;
                  }
                  off += 4;
                  if (CENLEN(cen, pos) == ZIP64_MINVAL)
                      off += 8;
                  if (CENSIZ(cen, pos) == ZIP64_MINVAL)
                      off += 8;
                  return LL(cen, off);
              }
              // should never be here
          }
          return locoff;
      }
  
      static void printExtra(byte[] extra, int off, int len) {
          int end = off + len;
          while (off + 4 <= end) {
              int tag = SH(extra, off);
              int sz = SH(extra, off + 2);
              print("        [tag=0x%04x, sz=%d, data= ", tag, sz);
              if (off + sz > end) {
                  print("    Error: Invalid extra data, beyond extra length");
                  break;
              }
              off += 4;
              for (int i = 0; i < sz; i++)
                  print("%02x ", extra[off + i]);
              print("]%n");
              switch (tag) {
              case EXTID_ZIP64 :
                  print("         ->ZIP64: ");
                  int pos = off;
                  while (pos + 8 <= off + sz) {
                      print(" *0x%x ", LL(extra, pos));
                      pos += 8;
                  }
                  print("%n");
                  break;
              case EXTID_NTFS:
                  print("         ->PKWare NTFS%n");
                  // 4 bytes reserved
                  if (SH(extra, off + 4) !=  0x0001 || SH(extra, off + 6) !=  24)
                      print("    Error: Invalid NTFS sub-tag or subsz");
                  print("            mtime:%tc%n",
                        winToJavaTime(LL(extra, off + 8)));
                  print("            atime:%tc%n",
                        winToJavaTime(LL(extra, off + 16)));
                  print("            ctime:%tc%n",
                        winToJavaTime(LL(extra, off + 24)));
                  break;
              case EXTID_EXTT:
                  print("         ->Info-ZIP Extended Timestamp: flag=%x%n",extra[off]);
                  pos = off + 1 ;
                  while (pos + 4 <= off + sz) {
                      print("            *%tc%n",
                            unixToJavaTime(LG(extra, pos)));
                      pos += 4;
                  }
                  break;
              default:
                  print("         ->[tag=%x, size=%d]%n", tag, sz);
              }
              off += sz;
          }
      }
  }
  