2011-01-26 18 views
17

Wikipedia has an excellent description of the ZIP file format, pero la estructura del "directorio central" me resulta confusa. Específicamente esto:¿Cómo se puede encontrar el inicio del "Directorio central" en archivos zip?

Este orden permite que se cree un archivo ZIP en una sola pasada, pero generalmente se descomprime leyendo primero el directorio central al final.

El problema es que incluso el encabezado final para el directorio central es de longitud variable. ¿Cómo, entonces, alguien puede obtener el inicio del directorio central para analizar?

(Ah, y lo hice pasar algún tiempo buscando en APPNOTE.TXT en vano antes de venir aquí y preguntar: P)

Respuesta

9

Mis condolencias, la lectura de la descripción de Wikipedia me da la impresión muy fuerte que lo que necesita hacer una buena cantidad de adivinar + verificar trabajo:

Caza hacia atrás desde el final para la etiqueta de final de directorio 0x06054b50, busque 16 bytes para encontrar la compensación para la etiqueta de inicio de directorio 0x02014b50, y espero que sea eso. Podría hacer algunas comprobaciones de cordura, como buscar la longitud de comentario y las etiquetas de cadena de comentario después de la etiqueta de fin de directorio, pero parece que los decodificadores Zip funcionan porque las personas no ponen caracteres divertidos en sus comentarios zip, nombres de archivo, etc. adelante. Basado enteramente en la página de wikipedia, de todos modos.

+1

espero que nadie utiliza '0x06054b50' en la sección de comentarios: P – Tower

+1

Suena como que sería mucho más fácil si hubiera un encabezado pequeño por adelantado con una etiqueta y 8 bytes para el desplazamiento del CD. El software ZIP tendría que volver atrás y establecer esos bytes en la parte frontal del archivo una vez que supiera cuál era el desplazamiento (parte de la belleza del CD se encuentra al final del archivo, usted recopila la información necesaria al momento de archivar) pero luego encontrarlo sería un juego de niños. Otra desventaja es que solo sería a prueba de futuro para el número de bytes reservados a menos que se usen (y se esperen) diferentes encabezados para diferenciar ZIP32 de ZIP64 (y ZIP128, etc.). – KeithS

6

Estaba implementando la compatibilidad de archivos zip hace algún tiempo, y busco los últimos kilobytes para un final de la firma de directorio central (4 bytes). Eso funciona bastante bien, hasta que alguien ponga 50kb de texto en el comentario (lo que es poco probable que suceda. Para estar absolutamente seguro, puede buscar los últimos 64kb + unos pocos bytes, ya que el tamaño del comentario es de 16 bits). Después de eso, busco el localizador de dir central zip64, eso es más fácil ya que tiene una estructura fija.

0

En caso de que alguien por ahí todavía tenga dificultades con este problema, eche un vistazo al repositorio que alojé en GitHub que contiene mi proyecto y que podría responder sus preguntas.

Zip file reader Básicamente lo que hace es descargar la parte central directory del archivo .zip que reside en el final del archivo. Luego leerá cada nombre de archivo y carpeta con su ruta desde los bytes e imprimiéndolo en la consola.

He hecho comentarios sobre los pasos más complicados en mi código fuente.

El programa solo puede funcionar hasta unos archivos .zip de 4 GB. Después de eso, tendrá que hacer algunos cambios en el tamaño de la VM y tal vez más.

Enjoy :)

1

Aquí es una solución justa que he tenido que rodar a cabo en caso que alguien necesita esto. Esto implica agarrar el directorio central.

En mi caso, no quería ninguna de las características de compresión que se ofrecen en cualquiera de las soluciones zip. Solo quería saber sobre los contenidos. El siguiente código devolverá un ZipArchive de una lista de cada entrada en el zip.

También utiliza una cantidad mínima de acceso a archivos y asignación de memoria.

TinyZip.CPP

#include "TinyZip.h" 
#include <cstdio> 

namespace TinyZip 
{ 
#define VALID_ZIP_SIGNATURE 0x04034b50 
#define CENTRAL_DIRECTORY_EOCD 0x06054b50 //signature 
#define CENTRAL_DIRECTORY_ENTRY_SIGNATURE 0x02014b50 
#define PTR_OFFS(type, mem, offs) *((type*)(mem + offs)) //SHOULD BE OK 

    typedef struct { 
     unsigned int signature : 32; 
     unsigned int number_of_disk : 16; 
     unsigned int disk_where_cd_starts : 16; 
     unsigned int number_of_cd_records : 16; 
     unsigned int total_number_of_cd_records : 16; 
     unsigned int size_of_cd : 32; 
     unsigned int offset_of_start : 32; 
     unsigned int comment_length : 16; 
    } ZipEOCD; 

    ZipArchive* ZipArchive::GetArchive(const char *filepath) 
    { 
     FILE *pFile = nullptr; 
#ifdef WIN32 
     errno_t err; 
     if ((err = fopen_s(&pFile, filepath, "rb")) == 0) 
#else 
     if ((pFile = fopen(filepath, "rb")) == NULL) 
#endif 
     { 
      int fileSignature = 0; 
      //Seek to start and read zip header 
      fread(&fileSignature, sizeof(int), 1, pFile); 
      if (fileSignature != VALID_ZIP_SIGNATURE) return false; 

      //Grab the file size 
      long fileSize = 0; 
      long currPos = 0; 

      fseek(pFile, 0L, SEEK_END); 
      fileSize = ftell(pFile); 
      fseek(pFile, 0L, SEEK_SET); 

      //Step back the size of the ZipEOCD 
      //If it doesn't have any comments, should get an instant signature match 
      currPos = fileSize; 
      int signature = 0; 
      while (currPos > 0) 
      { 
       fseek(pFile, currPos, SEEK_SET); 
       fread(&signature, sizeof(int), 1, pFile); 
       if (signature == CENTRAL_DIRECTORY_EOCD) 
       { 
        break; 
       } 
       currPos -= sizeof(char); //step back one byte 
      } 

      if (currPos != 0) 
      { 
       ZipEOCD zipOECD; 
       fseek(pFile, currPos, SEEK_SET); 
       fread(&zipOECD, sizeof(ZipEOCD), 1, pFile); 

       long memBlockSize = fileSize - zipOECD.offset_of_start; 

       //Allocate zip archive of size 
       ZipArchive *pArchive = new ZipArchive(memBlockSize); 

       //Read in the whole central directory (also includes the ZipEOCD...) 
       fseek(pFile, zipOECD.offset_of_start, SEEK_SET); 
       fread((void*)pArchive->m_MemBlock, memBlockSize - 10, 1, pFile); 
       long currMemBlockPos = 0; 
       long currNullTerminatorPos = -1; 
       while (currMemBlockPos < memBlockSize) 
       { 
        int sig = PTR_OFFS(int, pArchive->m_MemBlock, currMemBlockPos); 
        if (sig != CENTRAL_DIRECTORY_ENTRY_SIGNATURE) 
        { 
         if (sig == CENTRAL_DIRECTORY_EOCD) return pArchive; 
         return nullptr; //something went wrong 
        } 

        if (currNullTerminatorPos > 0) 
        { 
         pArchive->m_MemBlock[currNullTerminatorPos] = '\0'; 
         currNullTerminatorPos = -1; 
        } 

        const long offsToFilenameLen = 28; 
        const long offsToFieldLen = 30; 
        const long offsetToFilename = 46; 

        int filenameLength = PTR_OFFS(int, pArchive->m_MemBlock, currMemBlockPos + offsToFilenameLen); 
        int extraFieldLen = PTR_OFFS(int, pArchive->m_MemBlock, currMemBlockPos + offsToFieldLen); 
        const char *pFilepath = &pArchive->m_MemBlock[currMemBlockPos + offsetToFilename]; 
        currNullTerminatorPos = (currMemBlockPos + offsetToFilename) + filenameLength; 
        pArchive->m_Entries.push_back(pFilepath); 

        currMemBlockPos += (offsetToFilename + filenameLength + extraFieldLen); 
       } 

       return pArchive; 
      } 
     } 
     return nullptr; 
    } 

    ZipArchive::ZipArchive(long size) 
    { 
     m_MemBlock = new char[size]; 
    } 

    ZipArchive::~ZipArchive() 
    { 
     delete[] m_MemBlock; 
    } 

    const std::vector<const char*> &ZipArchive::GetEntries() 
    { 
     return m_Entries; 
    } 
} 

TinyZip.h

#ifndef __TinyZip__ 
#define __TinyZip__ 

#include <vector> 
#include <string> 

namespace TinyZip 
{ 
    class ZipArchive 
    { 
    public: 
     ZipArchive(long memBlockSize); 
     ~ZipArchive(); 

     static ZipArchive* GetArchive(const char *filepath); 

     const std::vector<const char*> &GetEntries(); 

    private: 
     std::vector<const char*> m_Entries; 
     char *m_MemBlock; 
    }; 

} 


#endif 

Uso:

TinyZip::ZipArchive *pArchive = TinyZip::ZipArchive::GetArchive("Scripts_unencrypt.pak"); 
if (pArchive != nullptr) 
{ 
    const std::vector<const char*> entries = pArchive->GetEntries(); 
    for (auto entry : entries) 
    { 
     //do stuff 
    } 
} 
+1

1. ¿En qué parte del libro de reglas dice esto? 2. Mis disculpas por no haber leído completamente el libro sobre los estándares del compilador. ¿Es muy difícil para ti cambiar eso? ¿Te has roto las manos? 3. ¿Desea que le explique el uso de punteros? 4. Sí lo hace, ¿no comprende mi código? 5. ¿Eres estúpido? – Asheh

Cuestiones relacionadas