Quello che mi serve è essere in grado di estrarre i file in un file .rar negli stream. Sto creando un caso di prova per avere un'idea di come usare unrar source. Ho cercato e armeggiato per un po ', ma non riesco a capire come usare la libreria. Sono sorpreso che non riesca nemmeno a trovare la documentazione o un tutorial per questo, considerando quanto sono comuni gli archivi .rar.Uso della libreria unrar: estrazione di file in un buffer filestream
Ho fatto un po 'di progressi da solo, ma non sempre funziona. Alcuni file vengono estratti correttamente. Altri file sono mescolati per qualche motivo (ma non completamente dati binari "garbage"). Tutto quello che sappiamo finora è, di solito (ma non sempre):
filenon lavorano hanno
fileInfo.Method = 48
. Essi sembrano essere i file che hanno un rapporto di compressione di 100% - vale a dire senza compressionefile di lavoro hanno
fileInfo.Method = 49
,50
,51
,52
o53
, che corrispondono alle velocità di compressione, più veloce, Veloce, Normale, Buono , Best
Ma non ho idea del perché sia così. Ancora non riesci a trovare documentazione o un esempio funzionante.
Di seguito è la fonte del test case che ho finora e un example rar archive che, quando viene estratto con questo programma, ha sia file funzionanti che non funzionanti.
/* 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;
}
aggiornamento:
ho trovato un file che sembra essere (dice di essere?) Il documentation, ma secondo il file, non sto facendo niente di male. Penso che potrei essere costretto a ricorrere a CRC controllando i buffer e implementando una soluzione alternativa se fallisce.
fonte soluzione (grazie, 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;
}
Forse c'è un problema con i caratteri EOL (archivio fatto su Windows ma estratto su Unix), ma non sono così sicuro .. –
Mi sto assicurando di usare il corretto 'buffLen' o' fileSize' durante la lettura/scrivendo ai buffer però. A questo punto, sono quasi pronto a dare la colpa alla libreria Unrar. – Kache