2010-02-24 6 views
7

Lo que necesito es poder extraer los archivos en un archivo .rar en las secuencias. Estoy creando un caso de prueba para tener una idea de cómo usar unrar source. He estado buscando y retocando por un tiempo, pero no puedo entender cómo usar la biblioteca. Me sorprende que ni siquiera pueda encontrar documentación o un tutorial, teniendo en cuenta qué tan comunes son los archivos .rar.Uso de la biblioteca unrar - extracción de archivos en un buffer de filestream

He progresado un poco por mi cuenta, pero no siempre funciona. Ciertos archivos se extraen correctamente. Otros archivos están mezclados por alguna razón (pero no completamente datos binarios "basura"). Todo lo que sabemos hasta ahora es, por lo general (pero no siempre):

archivos
  • no trabajan tienen fileInfo.Method = 48. Parece que son los archivos que tienen una relación de compresión de 100% - es decir, sin compresión

  • archivos de trabajo tienen fileInfo.Method = 49, 50, 51, 52, o 53, que corresponden a las velocidades de compresión, más rápido, Rápido, Normal, Buena , Mejor

Pero no tengo idea de por qué es así. Todavía no puedo encontrar documentación o un ejemplo de trabajo.

A continuación se muestra la fuente del caso de prueba que tengo hasta ahora y una example rar archive que, cuando se extrae con este programa, tiene archivos que funcionan y no funcionan.

/* put in the same directory as the unrar source files 
* compiling with: 
* make clean 
* make lib 
* g++ rartest.cpp -o rartest libunrar.so -lboost_filesystem 
*/ 

#include <cstring> 
#include <iostream> 
#include <fstream> 

#include <boost/filesystem.hpp> 

#define _UNIX 
#define RARDLL 
#include "dll.hpp" 

using namespace std; 
namespace fs = boost::filesystem; 

//char fileName[100] = "testout0.jpg\0"; 
// 
//// doens't work 
//int PASCAL ProcessDataProc(unsigned char* buffer, int buffLen) { 
// cout << "writing..." << endl; 
// ofstream outFile(fileName); 
// cout << buffLen << endl; 
// cout << outFile.write((const char*)buffer, buffLen) << endl; 
// cout << "done writing..." << endl; 
// fileName[7]++; 
//} 

int CALLBACK CallbackProc(unsigned int msg, long myBuffer, long rarBuffer, long bufferLen) { 
    switch(msg) { 
    case UCM_CHANGEVOLUME: 
     break; 
    case UCM_PROCESSDATA: 
     memcpy((char*)myBuffer, (char*)rarBuffer, bufferLen); 
     break; 
    case UCM_NEEDPASSWORD: 
     break; 
    } 
    return 1; 
} 

int main(int argc, char* argv[]) { 
    if (argc != 2) 
    return 0; 
    ifstream archiveStream(argv[1]); 
    if (!archiveStream.is_open()) 
    cout << "fstream couldn't open file\n"; 

    // declare and set parameters 
    HANDLE rarFile; 
    RARHeaderDataEx fileInfo; 
    RAROpenArchiveDataEx archiveInfo; 
    memset(&archiveInfo, 0, sizeof(archiveInfo)); 
    archiveInfo.CmtBuf = NULL; 
    //archiveInfo.OpenMode = RAR_OM_LIST; 
    archiveInfo.OpenMode = RAR_OM_EXTRACT; 
    archiveInfo.ArcName = argv[1]; 

    // Open file 
    rarFile = RAROpenArchiveEx(&archiveInfo); 
    if (archiveInfo.OpenResult != 0) { 
    RARCloseArchive(rarFile); 
    cout << "unrar couldn't open" << endl; 
    exit(1); 
    } 
    fileInfo.CmtBuf = NULL; 

    cout << archiveInfo.Flags << endl; 

    // loop through archive 
    int numFiles = 0; 
    int fileSize; 
    int RHCode; 
    int PFCode; 
    while(true) { 
    RHCode = RARReadHeaderEx(rarFile, &fileInfo); 
    if (RHCode != 0) break; 

    numFiles++; 
    fs::path path(fileInfo.FileName); 
    fileSize = fileInfo.UnpSize; 

    cout << fileInfo.Method << " " << fileInfo.FileName << " (" << fileInfo.UnpSize << ")" << endl; 

    char fileBuffer[fileInfo.UnpSize]; 

    // not sure what this does 
    //RARSetProcessDataProc(rarFile, ProcessDataProc); 

    // works for some files, but not for others 
    RARSetCallback(rarFile, CallbackProc, (long) &fileBuffer); 
    PFCode = RARProcessFile(rarFile, RAR_TEST, NULL, NULL); 

    // properly extracts to a directory... but I need a stream 
    // and I don't want to write to disk, read it, and delete from disk 
    //PFCode = RARProcessFile(rarFile, RAR_EXTRACT, ".", fileInfo.FileName); 

    // just skips 
    //PFCode = RARProcessFile(rarFile, RAR_SKIP, NULL, NULL); 

    if (PFCode != 0) { 
     RARCloseArchive(rarFile); 
     cout << "error processing this file\n" << endl; 
     exit(1); 
    } 
    ofstream outFile(path.filename().c_str()); 
    outFile.write(fileBuffer, fileSize); 
    } 
    if (RHCode != ERAR_END_ARCHIVE) 
    cout << "error traversing through archive: " << RHCode << endl; 
    RARCloseArchive(rarFile); 

    cout << "num files: " << numFiles << endl; 

} 

actualización:

he encontrado un archivo que parece ser (dice ser?) La documentation, pero de acuerdo con el archivo, yo no estoy haciendo nada malo. Creo que podría verse obligado a recurrir a CRC verificando los buffers e implementando una solución si falla.

fuente de solución (gracias, Denis Krjuchkov!):

/* put in the same directory as the unrar source files 
* compiling with: 
* make clean 
* make lib 
* g++ rartest.cpp -o rartest libunrar.so -lboost_filesystem 
*/ 

#include <cstring> 
#include <iostream> 
#include <fstream> 

#include <boost/filesystem.hpp> 
#include <boost/crc.hpp> 

#define _UNIX 
#define RARDLL 
#include "dll.hpp" 

using namespace std; 
namespace fs = boost::filesystem; 

//char fileName[100] = "testout0.jpg\0"; 
// 
//// doens't work 
//int PASCAL ProcessDataProc(unsigned char* buffer, int buffLen) { 
// cout << "writing..." << endl; 
// ofstream outFile(fileName); 
// cout << buffLen << endl; 
// cout << outFile.write((const char*)buffer, buffLen) << endl; 
// cout << "done writing..." << endl; 
// fileName[7]++; 
//} 

int CALLBACK CallbackProc(unsigned int msg, long myBufferPtr, long rarBuffer, long bytesProcessed) { 
    switch(msg) { 
    case UCM_CHANGEVOLUME: 
     return -1; 
     break; 
    case UCM_PROCESSDATA: 
     memcpy(*(char**)myBufferPtr, (char*)rarBuffer, bytesProcessed); 
     *(char**)myBufferPtr += bytesProcessed; 
     return 1; 
     break; 
    case UCM_NEEDPASSWORD: 
     return -1; 
     break; 
    } 
} 

int main(int argc, char* argv[]) { 
    if (argc != 2) 
    return 0; 
    ifstream archiveStream(argv[1]); 
    if (!archiveStream.is_open()) 
    cout << "fstream couldn't open file\n"; 

    // declare and set parameters 
    RARHANDLE rarFile; // I renamed this macro in dll.hpp for my own purposes 
    RARHANDLE rarFile2; 
    RARHeaderDataEx fileInfo; 
    RAROpenArchiveDataEx archiveInfo; 
    memset(&archiveInfo, 0, sizeof(archiveInfo)); 
    archiveInfo.CmtBuf = NULL; 
    //archiveInfo.OpenMode = RAR_OM_LIST; 
    archiveInfo.OpenMode = RAR_OM_EXTRACT; 
    archiveInfo.ArcName = argv[1]; 

    // Open file 
    rarFile = RAROpenArchiveEx(&archiveInfo); 
    rarFile2 = RAROpenArchiveEx(&archiveInfo); 
    if (archiveInfo.OpenResult != 0) { 
    RARCloseArchive(rarFile); 
    cout << "unrar couldn't open" << endl; 
    exit(1); 
    } 
    fileInfo.CmtBuf = NULL; 

// cout << archiveInfo.Flags << endl; 

    // loop through archive 
    int numFiles = 0; 
    int fileSize; 
    int RHCode; 
    int PFCode; 
    int crcVal; 
    bool workaroundUsed = false; 
    char currDir[2] = "."; 
    char tmpFile[11] = "buffer.tmp"; 
    while(true) { 
    RHCode = RARReadHeaderEx(rarFile, &fileInfo); 
    if (RHCode != 0) break; 
    RARReadHeaderEx(rarFile2, &fileInfo); 

    numFiles++; 
    fs::path path(fileInfo.FileName); 
    fileSize = fileInfo.UnpSize; 
    crcVal = fileInfo.FileCRC; 

    cout << dec << fileInfo.Method << " " << fileInfo.FileName << " (" << fileInfo.UnpSize << ")" << endl; 
    cout << " " << hex << uppercase << crcVal << endl; 

    char fileBuffer[fileSize]; 
    char* bufferPtr = fileBuffer; 

    // not sure what this does 
    //RARSetProcessDataProc(rarFile, ProcessDataProc); 

    // works for some files, but not for others 
    RARSetCallback(rarFile, CallbackProc, (long) &bufferPtr); 
    PFCode = RARProcessFile(rarFile, RAR_TEST, NULL, NULL); 

    // properly extracts to a directory... but I need a stream 
    // and I don't want to write to disk, read it, and delete from disk 
// PFCode = RARProcessFile(rarFile, RAR_EXTRACT, currDir, fileInfo.FileName); 

    // just skips 
    //PFCode = RARProcessFile(rarFile, RAR_SKIP, NULL, NULL); 

    if (PFCode != 0) { 
     RARCloseArchive(rarFile); 
     cout << "error processing this file\n" << endl; 
     exit(1); 
    } 

    // crc check 
    boost::crc_32_type crc32result; 
    crc32result.process_bytes(&fileBuffer, fileSize); 
    cout << " " << hex << uppercase << crc32result.checksum() << endl; 

    // old workaround - crc check always succeeds now! 
    if (crcVal == crc32result.checksum()) { 
     RARProcessFile(rarFile2, RAR_SKIP, NULL, NULL); 
    } 
    else { 
     workaroundUsed = true; 
     RARProcessFile(rarFile2, RAR_EXTRACT, currDir, tmpFile); 
     ifstream inFile(tmpFile); 
     inFile.read(fileBuffer, fileSize); 
    } 

    ofstream outFile(path.filename().c_str()); 
    outFile.write(fileBuffer, fileSize); 
    } 
    if (workaroundUsed) remove(tmpFile); 
    if (RHCode != ERAR_END_ARCHIVE) 
    cout << "error traversing through archive: " << RHCode << endl; 
    RARCloseArchive(rarFile); 

    cout << dec << "num files: " << numFiles << endl; 

} 
+0

Quizás haya un problema con los caracteres EOL (archivo hecho en Windows pero extraído en Unix), pero no estoy tan seguro ... –

+0

Me estoy asegurando de usar el 'buffLen' o' fileSize' correcto al leer/escribiendo a buffers sin embargo. En este punto, estoy a punto de echarle la culpa a la biblioteca unrar. – Kache

Respuesta

6

No estoy familiarizado con unrar, después de leer rápidamente una documentación creo que está asumiendo que CallbackProc se llama exactamente una vez por archivo. Sin embargo, creo que unrar puede llamarlo varias veces. Descomprime algunos datos y luego llama al CallbackProc, luego desempaqueta el siguiente fragmento de datos y nuevamente llama al CallbackProc, el proceso se itera hasta que se procesen todos los datos. Debe recordar cuántos bytes se escriben realmente en el búfer y anexar datos nuevos en el desplazamiento correspondiente.

+0

Definitivamente explica por qué se mezclaron los archivos extraídos pero no todos los datos basura. Volví a leer la documentación y no me dio la impresión de que unrar podría ejecutar la devolución de llamada varias veces por archivo. ¿Cómo lo piensas? Hacer que una devolución de llamada se ejecute periódicamente en el medio de la extracción no me parece muy intuitivo. – Kache

+0

Supongo que la razón es que los archivos en el archivo pueden ser lo suficientemente grandes y no caben en la memoria disponible. Desembalar tales archivos completamente en un buffer sería imposible o al menos ineficiente. –

0

Parece que has publicado alguna fuente de código, pero no hay duda real.

¿ha considerado mirando Rarlabs Feedback Page (que apunta a su forums

También, ver: This Article

+0

No puedo obtener (incluso un 'main()' vacío para compilar. Estoy tratando de descomprimir un archivo en un archivo rar en un búfer, para hacer con lo que quiera. Miré a través de sus enlaces. Rarlabs no tiene soporte para su biblioteca unrar que yo sepa, solo una biblioteca unrar de código abierto sin documentación. – Kache

3

no puedo encontrar ninguna documentación en línea o bien, pero hay ejemplos que puede utilizar:

Vaya a http://www.krugle.com, y en la esquina inferior izquierda de la página, ingrese una palabra clave como RAROpenArchiveEx. Verá los archivos de encabezado y origen de varios proyectos de código abierto ts que hacen uso de la biblioteca unrar.

Eso debería ponerlo en marcha.

+0

Gracias! Echaré un vistazo a algunos de estos. Esperemos que al menos uno de estos extractos directamente a un búfer, y puedo averiguar cómo hacer lo mismo. – Kache

Cuestiones relacionadas