Используя unrar библиотеку - извлечение файлов в буфер filestream

То, в чем я нуждаюсь, должно смочь извлечь файлы в .rar файле в потоки. Я создаю тестовый сценарий для получения смысла того, как использовать unrar источник. Я искал и чинил некоторое время, но я не могу выяснить, как пользоваться библиотекой. Я удивлен, что не могу даже найти документацию или учебное руководство для нее, рассмотрев, как общие архивы .rar.

Я сделал немного успехов самостоятельно, но они не всегда работают. Определенные файлы извлечены правильно. Другие файлы смешаны по некоторым причинам (но не полностью двоичные данные "мусора"). Все, что я знаю до сих пор, обычно (но не всегда):

  • не рабочие файлы имеют fileInfo.Method = 48. Они, кажется, файлы, которые имеют степень сжатия 100% - т.е. никакое сжатие

  • рабочие файлы имеют fileInfo.Method = 49, 50, 51, 52, или 53, которые соответствуют скоростям сжатия, Самым Быстрым, Быстрым, Нормальным, Хорошим, Лучше всего

Но я понятия не имею, почему это. Все еще не может найти документацию или рабочий пример.

Ниже источник тестового сценария, который я имею до сих пор и пример rar архив, который при извлечении с этой программой имеет и работу и не рабочие файлы.

/* 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  
#include  
#include  

#include  

#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;

}

обновление:

Я нашел файл, который, кажется, (утверждает, что был?) документация, но согласно файлу, я не делаю ничего плохого. Я думаю, что мог бы быть вынужден обратиться к CRC, проверяющему буферы и реализующему обходное решение, если он перестал работать.

источник решения (спасибо, 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  
#include  
#include  

#include  
#include    

#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
задан 22 revs, 2 users 100% 9 August 2013 в 11:52
поделиться

3 ответа

Я не знаком с unrar, быстро прочитав документацию, я думаю, вы предполагаете, что CallbackProc вызывается ровно один раз для каждого файла. Однако я думаю, что unrar может вызывать его несколько раз. Он распаковывает некоторые данные, затем вызывает CallbackProc , затем распаковывает следующий блок данных и снова вызывает CallbackProc , процесс повторяется до тех пор, пока не будут обработаны все данные. Вы должны помнить, сколько байты фактически записываются в буфер и добавляют новые данные по соответствующему смещению.

6
ответ дан 7 December 2019 в 03:15
поделиться

Похоже, вы опубликовали исходный код, но на самом деле вопросов нет.

Думали ли вы о том, чтобы заглянуть на страницу отзывов Rarlabs (которая указывает на их форумы

). Также см .: Эта статья

0
ответ дан 7 December 2019 в 03:15
поделиться

Я тоже не могу найти никаких документов в Интернете, но есть примеров , которые вы можете использовать:

Перейдите на http://www.krugle.com и в левом нижнем углу страницы введите ключевое слово, например RAROpenArchiveEx . Вы увидите заголовочные и исходные файлы из различных проектов с открытым исходным кодом, в которых используется библиотека unrar.

Это должно вас подтолкнуть.

3
ответ дан 7 December 2019 в 03:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: