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):
archivosno trabajan tienen
fileInfo.Method = 48
. Parece que son los archivos que tienen una relación de compresión de 100% - es decir, sin compresiónarchivos de trabajo tienen
fileInfo.Method = 49
,50
,51
,52
, o53
, 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;
}
Quizás haya un problema con los caracteres EOL (archivo hecho en Windows pero extraído en Unix), pero no estoy tan seguro ... –
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