Using unrar library - extract files to stream buffer

I need you to be able to extract files in a .rar file to streams. I am creating a test case to understand how to use the unrar source . I searched and worked for a while, but I cannot figure out how to use the library. I am surprised that I can’t even find documentation or a tutorial for this, given how common .rar archives are.

I made some progress on my own, but it does not always work. Some files are extracted properly. For some reason, some files are confused (but not completely garbage binary data). All that I know so far is usually (but not always):

  • Broken files have fileInfo.Method = 48 . They look like files with a compression ratio of 100%, that is, without compression

  • working files have fileInfo.Method = 49 , 50 , 51 , 52 or 53 , which correspond to compression rates, faster, faster, normal, good, better

But I have no idea why this is so. Still unable to find documentation or working example.

Below is the initial value of the test case, and an example of a rar archive , which, when extracted by this program, has both working and non-working files.

 /* 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; } 

update:

I found a file that looks (claims to be?) Documentation , but according to the file, m did nothing wrong. I think I can get CRC to check the buffers and implement a workaround if it fails.

source of the solution (thanks, Denis Kryuchkov!):

 /* 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; } 
+7
c ++ compression unrar
source share
3 answers

I am not familiar with unrar, after a quick reading of the documentation, I think you assume that CallbackProc is called exactly once for each file. However, I think unrar may call it several times. It unpacks some data, then calls CallbackProc , then unpacks the next piece of data and calls CallbackProc again, the process repeats until all the data has been processed. You must remember how many bytes are actually written to the buffer, and add new data at the appropriate offset.

+6
source share

I can not find any documents on the Internet, but there are examples that you can use:

Go to http://www.krugle.com and in the lower left corner of the page enter a keyword, for example RAROpenArchiveEx . You will see headers and source files from various open source projects that use the unrar library.

That should make you.

+3
source share

You seem to have posted some kind of source code, but no question.

Have you reviewed the Rarlabs Feedback Page (which points to their forums

Also see this article.

0
source share

All Articles