Page 1 of 1

Extracting archive from c++

PostPosted: 08 Oct 2011, 17:37
by Edward_Lii
Hello everyone,

I'm currently working on an addon-manager for MeAndMyShadow.
Downloading levels works fine, and downloading levelpacks and themes too.
But the problem is that I want the levelpacks and themes to be packed in an archive like a .zip or .tar.gz, etc..

How can I extract the files from the archive from within c++ to a destination folder?
What is the best way to do it?

Thanks in advance.

Re: Extracting archive from c++

PostPosted: 12 Oct 2011, 07:48
by Tuxide
I'm not too sure what dependencies you're using, but the Crystal Space engine allows you to mount a zip archive as if it was a filesystem and access its contents. Others probably do too.

Re: Extracting archive from c++

PostPosted: 12 Oct 2011, 16:50
by Edward_Lii
Hello Tuxide,

Tuxide {l Wrote}:I'm not too sure what dependencies you're using, but the Crystal Space engine allows you to mount a zip archive as if it was a filesystem and access its contents. Others probably do too.

I've looked at how Supertuxkart does it and saw that Irrlicht had such a system you mentioned.
The problem is that AFAIK sdl doesn't have such system, so I'll have to look at some third party library.

I'm currently looking in libarchive.
EDIT: I've got a working system using libarchive. :)

Re: Extracting archive from c++

PostPosted: 14 Oct 2011, 20:34
by qubodup
Care to share how? (Just a link to the code file in the online-readable repo would be enough if available)

Re: Extracting archive from c++

PostPosted: 15 Oct 2011, 06:37
by Edward_Lii
Hello qubodup,

qubodup {l Wrote}:Care to share how? (Just a link to the code file in the online-readable repo would be enough if available)

Of course, I'll try my best explaining it. (Here's the source: Addons.cpp)

To make use of libarchive we need to include these two header files:
{l Code}: {l Select All Code}
#include <archive.h>
#include <archive_entry.h>


Now the actual coding starts.
We create two archives, one for the file we want to extract and the for the destination we want to extract to.
Note that the name archive doesn't mean it is an archive, it can also be to disk.
{l Code}: {l Select All Code}
archive *file;
archive *dest;


Since an archive can be multiple things we need to define them:
{l Code}: {l Select All Code}
//This will be the archive we want to extract, so we make it a archive_[b]read[/b]_new();
file = archive_read_new();
//This will be the destination, so we make it a archive_[b]write_disk[/b]_new();
//If we leave out [b]_disk[/b] it will save write the files into another archive.
dest = archive_write_disk_new();

archive_write_disk_set_options(dest, ARCHIVE_EXTRACT_TIME);
archive_read_support_format_zip(file);  //We configure which archive files are supported.


Now comes the actual reading.
ProcessFileName is a method in meandmyshadow which translates some strings like %DATA%/themes/... to ./themes/.
{l Code}: {l Select All Code}
//Now read the archive.
if(archive_read_open_file(file, ProcessFileName(path).c_str(), 10240)) {
   cerr<<"Error while reading archive "+path<<endl;
}


After opening the file we loop it's entries and write them to dest.
First we try to read the next header(files in the archive).
Then we check if there was an error or we reached the end of the file.
{l Code}: {l Select All Code}
//Now write every entry to disk.
int status;
archive_entry *entry;

while(true) {
   status=archive_read_next_header(file,&entry);
   if(status==ARCHIVE_EOF){
      break;
   }
   if(status!=ARCHIVE_OK){
      cerr<<"Error while reading archive "+path<<endl;
   }


That's the reading of the archive entries, now follows the writing.
First we override the pathname of the entry, this way we can write them to a file/location we want.
Again some checking if everything went ok.
{l Code}: {l Select All Code}
   archive_entry_set_pathname(entry,(ProcessFileName(destination) + archive_entry_pathname(entry)).c_str());
      
   status=archive_write_header(dest,entry);
   if(status!=ARCHIVE_OK){
      cerr<<"Error while extracting archive "+path<<endl;
   }else{
      copyData(file, dest);
      status=archive_write_finish_entry(dest);
      if(status!=ARCHIVE_OK){
         cerr<<"Error while extracting archive "+path<<endl;
      }
   }
}


And finally we close the archive:
{l Code}: {l Select All Code}
archive_read_close(file);
archive_read_finish(file);


And here follows the copyData function.
There are only two lines of code that actually perform to coping:

archive_read_data_block(file, &buff, &size, &offset);
archive_write_data_block(dest, buff, size, offset);
{l Code}: {l Select All Code}
void Addons::copyData(archive *file, archive *dest) {
   int status;
   const void *buff;
   size_t size;
   off_t offset;

   while(true) {
      status=archive_read_data_block(file, &buff, &size, &offset);
      if(status==ARCHIVE_EOF){
         return;
      }
      if(status!=ARCHIVE_OK){
         cerr<<"Error while writing data to disk."<<endl;
         return;
      }
      status=archive_write_data_block(dest, buff, size, offset);
      if(status!=ARCHIVE_OK) {
         cerr<<"Error while writing data to disk."<<endl;
         return;
      }
   }
}


That's all that needs to be done! :)