Extracting archive from c++

Extracting archive from c++

Postby Edward_Lii » 08 Oct 2011, 17:37

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.
From,
Edward_Lii
User avatar
Edward_Lii
MnMS Moderator
 
Posts: 777
Joined: 20 Dec 2010, 16:46

Re: Extracting archive from c++

Postby Tuxide » 12 Oct 2011, 07:48

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.
Crossfire and Wesnoth played a game of chess. It started out as an open game.
User avatar
Tuxide
 
Posts: 41
Joined: 04 Dec 2009, 04:37

Re: Extracting archive from c++

Postby Edward_Lii » 12 Oct 2011, 16:50

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. :)
From,
Edward_Lii
User avatar
Edward_Lii
MnMS Moderator
 
Posts: 777
Joined: 20 Dec 2010, 16:46

Re: Extracting archive from c++

Postby qubodup » 14 Oct 2011, 20:34

Care to share how? (Just a link to the code file in the online-readable repo would be enough if available)
User avatar
qubodup
Global Moderator
 
Posts: 1671
Joined: 08 Nov 2009, 22:52
Location: Berlin, Germany

Re: Extracting archive from c++

Postby Edward_Lii » 15 Oct 2011, 06:37

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! :)
From,
Edward_Lii
User avatar
Edward_Lii
MnMS Moderator
 
Posts: 777
Joined: 20 Dec 2010, 16:46

Who is online

Users browsing this forum: No registered users and 1 guest