/*
    Xunzip for Linux, handles gzip and zip files via zlib.
    Copyright (C) 1998 Tero Koskinen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
*/
// zipHeaderCode.cpp

#include <fstream.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>

#ifdef USE_GTK
#include <gtk/gtk.h>
#else
#include <qapp.h>
#include <qlistbox.h>
#include <ktablistbox.h>
#endif

#include <zlib.h>
#include "zipHeader.h"
#include "utils.h"

#include "itoa.h"
#include "mjono.h"

// #define USE_EXTRA_ITEM

Mjono methodToString(CentralDirRecord & cdr);
int gz_uncompress(gzFile in, FILE *out);

// --------- BasicHeader rutiinit ------------------
BasicHeader::BasicHeader()
{
  myVersion=0;
  myFlag=0;
  myMethod=0;
  myLastModTime=0;
  myLastModDate=0;
  myCrc32=0;
  myCSize=0;
  myUnCSize=0;
  myFilenameLength=0;
  myExtraFieldLength=0;
  myFilename=0;
  myPlace=0;
}

BasicHeader::BasicHeader(const BasicHeader & op)
{
  myVersion=op.getVersion();
  myFlag=op.getPFlag();
  myMethod=op.getMethod();
  myLastModTime=op.getLastModTime();
  myLastModDate=op.getLastModDate();
  myCrc32=op.getCrc32();
  myCSize=op.getCSize();
  myUnCSize=op.getUnCSize();
  myFilenameLength=op.getFilenameLength();
  myExtraFieldLength=op.getExtraFieldLength();
  if (op.getFilename())
	{
	  int apu=strlen(op.getFilename());
	  myFilename=new char[apu];
	  if (myFilename)
		strncpy(myFilename,op.getFilename(),apu);
	}
  
  myPlace=op.getPlace();
}

BasicHeader::~BasicHeader()
{
  if (myFilename!=0)
    {
      delete myFilename;
      myFilename=0;
    }
}

BasicHeader & BasicHeader::operator=(const BasicHeader & op)
{
    if (this==&op)
	    return *this;
    myVersion=op.getVersion();
    myFlag=op.getPFlag();
    myMethod=op.getMethod();
    myLastModTime=op.getLastModTime();
    myLastModDate=op.getLastModDate();
    myCrc32=op.getCrc32();
    myCSize=op.getCSize();
    myUnCSize=op.getUnCSize();
    myFilenameLength=op.getFilenameLength();
    myExtraFieldLength=op.getExtraFieldLength();
	if (op.getFilename())
	  {
		int apu=strlen(op.getFilename());
		myFilename=new char[apu];
		if (myFilename)
		  strncpy(myFilename,op.getFilename(),apu);
	  }
    myPlace=op.getPlace();
    return *this;
};

void BasicHeader::read(ifstream & fin)
{
  // unsigned int inttmp;
  // unsigned long longtmp;
  if(!fin.eof())
    {
      // int status=1;
	  /* fin.read((char*)&inttmp,2); kumma mutta tama ei kelpaa */
	  myPlace=fin.tellg();
	  myVersion=readint(fin);
      myFlag=readint(fin);
      myMethod=readint(fin);
      myLastModTime=readint(fin);
      myLastModDate=readint(fin);
      myCrc32=readlong(fin);
      myCSize=readlong(fin);
      myUnCSize=readlong(fin);
      myFilenameLength=readint(fin);
      myExtraFieldLength=readint(fin);
      if (myFilenameLength>0)
		{
		  myFilename=new char[myFilenameLength+1];
		  if (myFilename)
			{
			  fin.read(myFilename,myFilenameLength);
			  myFilename[myFilenameLength]=0;
			}
		}
      if (myExtraFieldLength>0)
		fin.seekg(myExtraFieldLength,ios::cur); /* let's skip these... */
    }
}

// palautetaan 1, jos loydetty, muutoin 0
int BasicHeader::find(ifstream & fin)
{
  int mode=0; // mika tavu menossa
  char headerSign[5]={ 0x50,0x4b,0x03,0x04,0 };
  while(!fin.eof())
    {
      char ch;
      fin.get(ch);
      if (ch==headerSign[mode])
	mode++;
      else
	mode=0;
      if (mode>=4)
	return 1;
    }
  return 0;
}

// ---------- CentralDirRecord rutiinit --------------
CentralDirRecord::CentralDirRecord():
myFilename(0),
myComment(0)
{
  myVerMadeBy=0;
  myVersion=0;
  myFlag=0;
  myMethod=0;
  myLastModTime=0;
  myLastModDate=0;
  myCrc32=0;
  myCSize=0;
  myUnCSize=0;
  myFilenameLength=0;
  myExtraFieldLength=0;
  myCommentLength=0;
  myDiskNumber=0;
  myInternalAttr=0;
  myExternalAttr=0;
  myRelOffset=0;
}

CentralDirRecord::CentralDirRecord(const CentralDirRecord & op)
{
    myVerMadeBy=op.getVersionMadeBy();
    myVersion=op.getVersion();
    myFlag=op.getPFlag();
    myMethod=op.getMethod();
    myLastModTime=op.getLastModTime();
    myLastModDate=op.getLastModDate();
    myCrc32=op.getCrc32();
    myCSize=op.getCSize();
    myUnCSize=op.getUnCSize();
    myFilenameLength=op.getFilenameLength();
    myExtraFieldLength=op.getExtraFieldLength();
    myCommentLength=op.getCommentLength();
    myDiskNumber=op.getDiskNumber();
    myInternalAttr=op.getInternalAttr();
    myExternalAttr=op.getExternalAttr();
    myRelOffset=op.getRelOffsetLHeader();
	if (op.getFilename())
	  {
		if (myFilename)
		  {
			delete [] myFilename;
			myFilename=0;
		  }
		int apu=strlen(op.getFilename());
		myFilename=new char[apu+1];
		myFilenameLength=apu;
		if (myFilename)
		{
		  tyhjennaTaulukko(myFilename,apu+1);
		  strncpy(myFilename,op.getFilename(),apu);
		}
	  }
	if (op.getComment())
	  {
		int apu=strlen(op.getComment());
		if (myComment)
		  {
			delete myComment;
			myComment=0;
		  }
		myComment=new char[apu];
		if (myComment)
		  strncpy(myComment,op.getComment(),apu);
	  }
	myPlace=op.getPlace();
}

CentralDirRecord::~CentralDirRecord()
{
  if (myFilename)
    delete myFilename;
  if (myComment)
    delete myComment;
}

CentralDirRecord & CentralDirRecord::operator=(const CentralDirRecord & op)
{
    if (this==&op)
	    return *this;

    myVerMadeBy=op.getVersionMadeBy();
    myVersion=op.getVersion();
    myFlag=op.getPFlag();
    myMethod=op.getMethod();
    myLastModTime=op.getLastModTime();
    myLastModDate=op.getLastModDate();
    myCrc32=op.getCrc32();
    myCSize=op.getCSize();
    myUnCSize=op.getUnCSize();
    myFilenameLength=op.getFilenameLength();
    myExtraFieldLength=op.getExtraFieldLength();
    myCommentLength=op.getCommentLength();
    myDiskNumber=op.getDiskNumber();
    myInternalAttr=op.getInternalAttr();
    myExternalAttr=op.getExternalAttr();
    myRelOffset=op.getRelOffsetLHeader();
	if (op.getFilename())
    {
		if (myFilename)
	    {
			delete myFilename;
			myFilename=0;
		}
		unsigned int apu=strlen(op.getFilename());
		myFilename=new char[apu+1];
		myFilenameLength=apu;
		if (apu!=myFilenameLength)
		    cerr << __FUNCTION__ << "(): if (apu!=myFilenameLength)" << endl;
		if (myFilename)
		{
		    tyhjennaTaulukko(myFilename,apu+1);
		    strncpy(myFilename,op.getFilename(),apu);
		}
	}
	if (op.getComment())
	{
		int apu=strlen(op.getComment());
		if (myComment)
		  {
			delete myComment;
			myComment=0;
		  }
		myComment=new char[apu];
		if (myComment)
		{
		    tyhjennaTaulukko(myComment,apu+1);
			strncpy(myComment,op.getComment(),apu);
		}
	}
	myPlace=op.getPlace();
	return *this;
}

void CentralDirRecord::read(ifstream & fin)
{
  // unsigned int inttmp;
  // unsigned long longtmp;
  if(!fin.eof())
    {
	  // char ch;
      // int status=1;
      myPlace=fin.tellg();
      myVerMadeBy=readint(fin);
      myVersion=readint(fin);
      myFlag=readint(fin);
      myMethod=readint(fin);
      myLastModTime=readint(fin);
      myLastModDate=readint(fin);
	  myCrc32=readlong(fin);
      myCSize=readlong(fin);
      myUnCSize=readlong(fin);
      myFilenameLength=readint(fin);
      myExtraFieldLength=readint(fin);
      myCommentLength=readint(fin);
      myDiskNumber=readint(fin);
      myInternalAttr=readint(fin);
      myExternalAttr=readlong(fin);
      myRelOffset=readlong(fin);
      if (myFilenameLength>0)
		{
		  myFilename=new char[myFilenameLength+1];
		  if (myFilename)
		  {
			  tyhjennaTaulukko(myFilename,myFilenameLength+1);
			  fin.read(myFilename,myFilenameLength);
			  myFilename[myFilenameLength]=0;
		  }
		}
      if (myExtraFieldLength>0)
		fin.seekg(myExtraFieldLength,ios::cur); /* let's skip these... */
      if (myCommentLength>0)
		{
		  myComment=new char[myCommentLength+1];
		  if (myComment)
			{
			  fin.read(myComment,myCommentLength);
			  myComment[myCommentLength]=0;
			}
		}
    }
}

// palautetaan 1, jos loydetty, muutoin 0
int CentralDirRecord::find(ifstream & fin)
{
  int mode=0; // mika tavu menossa
  char headerSign[5]={ 0x50,0x4b,0x01,0x02,0 };
  while(!fin.eof())
    {
      char ch;
      fin.get(ch);
      if (ch==headerSign[mode])
	mode++;
      else
	mode=0;
      if (mode>=4)
	return 1;
    }
  return 0;
}


// -------------- ZipHeader rutiinit --------------------
// palautetaan 1,2,4 tai 4 jos loydetty joku header, muutoin 0
int ZipHeader::find(ifstream & fin)
{
  int mode=0; // mika tavu menossa
  char headerSign[3]={ 0x50,0x4b,0 };
  while(1)
    {
      char ch;
      fin.get(ch);
      if (ch==headerSign[mode])
		mode++;
      else
		mode=0;
      if (mode>=2)
		{
		  char a,b;
		  fin.get(a).get(b);
		  if (a==0x01 && b==0x02)
			return ZIP_CENTRAL_DIR_RECORD;
		  else if (a==0x03 && b==0x04)
			return ZIP_BASIC_HEADER;
		  else if (a==0x05 && b==0x06)
			return ZIP_END_OF_CDR;
		  fin.seekg(-2,ios::cur); // palataan 2 takaisin varmuuden vuoksi
		  mode=0;
		}
      if (fin.eof())
		return 0;
    }
  
  return 0;
}

// ------- BasicHeaderList luokan rutiinit  ---------------
int BasicHeaderList::add(BasicHeader & bh)
{
	BasicNode *p;
	p=new BasicNode();
	if (!p)
	    return -1;
	p->h=bh;
	if (!root)
	    root=p;
	else
	    root->setNext(p);
	nodes++;
	return 0;
}

void BasicHeaderList::deleteAll(void)
{
    BasicNode *p;
    BasicNode *o;
    if (root)
    {
	    p=root;
		while(p)
		{
		    o=p->getNext();
			if (p)
			  delete(p);
		    p=o;
			if (!p)
			  break;
		}
		root=0;
		nodes=0;
    }
}

BasicNode *BasicHeaderList::getNode(int index)
{
    BasicNode *p;
    if (!root)
	    return 0;
	p=root;
    for (int a=0;a<index;a++)
	{
	    if (!p->getNext())
		    break;
	    p=p->getNext();
    }
    return p;
}

int CentralDirRecordList::add(CentralDirRecord & cdr)
{
	CDRNode *p;
	p=new CDRNode();
	if (!p)
	    return -1;
	p->h=cdr;
	if (!root)
	    root=p;
	else
	    root->setNext(p);
	nodes++;
	return 0;
}

void CentralDirRecordList::deleteAll(void)
{
    CDRNode *p;
    CDRNode *o;
    if (root)
    {
	    p=root;
		while(p)
		{
		    o=p->getNext();
			if (p)
			  delete(p);
		    p=o;
			if (!p)
			  break;
		}
		root=0;
		nodes=0;
    }
}

CDRNode *CentralDirRecordList::getNode(int index)
{
    CDRNode *p;
    if (!root)
	    return 0;
	p=root;
    for (int a=0;a<index;a++)
	{
	    if (!p->getNext())
		    break;
	    p=p->getNext();
    }
    return p;
}

CDRNode *CentralDirRecordList::getNodeByName(const char *name)
{
    CDRNode *p;
    if (!root)
	    return 0;
	// cout << "Searching file: " << name << "." << endl;
	p=root;
	while(p)
	{
	    if (p)
	    {
		    if (p->h.getFilename())
		    {
			    // cout << "Comparing to: " << p->h.getFilename() << "." << endl;
			    if (!strcmp(p->h.getFilename(),name))
			    {
				    // cout << "Found!" << endl;
				    break;
				}
		    }
		}
		p=p->getNext();
    }
    return p;
}

// ----------- ZipPacket luokan rutiinit --------------------

// apu funktio
Mjono methodToString(CentralDirRecord & cdr)
{
    Mjono apu;
    unsigned long ratio;
    switch(cdr.getMethod())
    {
	    case 0:
		    apu=Mjono(" (stored ");
		    break;
		case 1:
		    apu=Mjono(" (shrunk ");
		    break;
		case 2:
		case 3:
		case 4:
		case 5:
		    apu=Mjono(" (reduced ");
		    break;
		case 6:
		    apu=Mjono(" (imploded ");
		    break;
		case 8:
		    apu=Mjono(" (deflated ");
		    break;
		case 9:
		case 10:
		    apu=Mjono(" (unsupported ");
		    break;
		default:
		    apu=Mjono(" (none ");
		    break;
    }
    if (cdr.getUnCSize()>0)
	    ratio=100-(unsigned long)(((cdr.getCSize()*100)/(cdr.getUnCSize()))+0.5);
	else
	    ratio=0;
    // cout << "getC(): " << cdr.getCSize() << endl;
    // cout << "getUnC(): " << cdr.getUnCSize() << endl;
    // cout << "ratio: " << ratio << endl;
    apu+=itoa(ratio);
    apu+=Mjono("%)");
    return apu;
}

ZipPacket::ZipPacket()
{
    opened=0;
    files=0;
}

ZipPacket::~ZipPacket()
{
    files=0;
    if (opened) // Hmmm... toimiikohan tama..
	    fin.close();
}

#ifdef USE_GTK
int ZipPacket::open(char *file,GtkWidget *lista)
#else
int ZipPacket::open(const char *file,KTabListBox *lista)
#endif
{
    BasicHeader bh;
    CentralDirRecord cdr;
#ifdef USE_GTK
    GtkWidget *list_item;
    GList *dlist;
#else
    QString str;
#endif
    int fl=0;
    int out=0;
    int ok=1;
    Mjono apu;
    
    if (opened)
	    close(lista);
#ifdef USE_GTK
	dlist=NULL;
#endif
    tyhjennaTaulukko(tiedosto,FNAME_MAX);
	strncpy(tiedosto,file,strlen(file));
	fl=strlen(tiedosto);
	if (fl>GZ_SUFFIX_LEN && strcmp(tiedosto+fl-GZ_SUFFIX_LEN,GZ_SUFFIX)==0)
	{
	    gzFile gzin;
	    gzin=gzopen(tiedosto,"rb");
	    if (gzin!=NULL)
	    {
		    char ch;
		    int a=0;
		    gzclose(gzin);
		    a=fl-1;
		    while(tiedosto[a]!='/'){ if (a<0) break; a--; }
		    
		    ch=tiedosto[fl-GZ_SUFFIX_LEN];
		    tiedosto[fl-GZ_SUFFIX_LEN]='\0';
#ifdef USE_GTK
		    list_item=gtk_list_item_new_with_label(tiedosto+a+1);
		    gtk_container_add(GTK_CONTAINER(lista),list_item);
		    gtk_widget_show(list_item);
#else
			lista->insertItem(tiedosto+a+1);
#endif
		    tiedosto[fl-GZ_SUFFIX_LEN]=ch;
		    fileType=2;
		    opened=2;
		    ok=1;
		    return 0;
		}
		fileType=0;
    }
    fin.open(file);
    if (!fin)
    {
	    // cerr << "Cannot open file " << file << endl;
	    return -1;
	}

	while(out==0)
    {
	    int type=zh.find(fin);
	    switch(type)
	    {
		    case 0:
			    out=1;
			    break;
			case 1: // basicheader field
			    ok=1;
			    bh.read(fin);
			    bhList.add(bh);
			    break;
			case 2: // CentralDirRecord field
			    ok=1;
			    fileType=1;
			    cdr.read(fin);
			    cdList.add(cdr);
			    apu=Mjono(cdr.getFilename());
			    apu+=Mjono("\t",1)+methodToString(cdr);
#ifdef USE_GTK
			    list_item=gtk_list_item_new_with_label(apu.AnnaMjono());
			    gtk_container_add(GTK_CONTAINER(lista),list_item);
				gtk_widget_show(list_item);
#else
			    lista->insertItem(apu.AnnaMjono());
#endif
			    break;
			case 3:
				// CentralDirRecordEnd field
				ok=1;
				fileType=1;
				out=1;
				break;
		}
	}
    // opened=1;
    fin.close();
    if (!ok)
	{
		opened=0;
		return -2;
	}
    opened=2;
    return 0;
}

#ifdef USE_GTK
int ZipPacket::close(GtkWidget *lista)
#else
int ZipPacket::close(KTabListBox *lista)
#endif
{
    if (opened)
    {
	    if (fileType==1)
	    {
#ifdef USE_GTK
		    gtk_list_clear_items(GTK_LIST(lista),0,cdList.getNodes());
#else
		    lista->clear();
#endif
		    cdList.deleteAll();
		    bhList.deleteAll();
		}
		else
#ifdef USE_GTK
		    gtk_list_clear_items(GTK_LIST(lista),0,1);
#else
		    lista->clear();
#endif
		files=0;
	}
    if (opened==1)
	    fin.close();

	fileType=0;
	files=0;
	opened=0;
    return 0;
}

int ZipPacket::unZip(const char *path,int sfile)
{
    char filename[FNAME_MAX];
    char fname[FNAME_MAX];
    unsigned char *in;
    int skip=0;
    
    if (sfile>=0)
	    skip=1;
    
    BasicHeader h;
    ZipHeader zh;
    tyhjennaTaulukko(filename,FNAME_MAX);
    if (strlen(path))
	    strcpy(filename,path);
	if (filename[strlen(filename)-1]!='/')
	    strcat(filename,"/");
	if (fileType==1)
	{
	    if (opened==2)
	    {
		    fin.open(tiedosto);
		    if (!fin)
			    return -1;
			opened=1;
		}
	    else if (opened==0)
		    return -1;
	 
	    while (zh.find(fin)==1)
	    {
		    if (sfile>0)
			{
				sfile--;
				continue;
			}
		    int b=0;
		    h.read(fin);
		    tyhjennaTaulukko(fname,FNAME_MAX);
		    b=h.getCSize();
		    if (b>0)
		    {
			    in=new unsigned char[b];
			    tyhjennaTaulukko((char*)in,b);
			    if (in)
			    {
				    fin.read((char*)in,b);
			     // cout << "Tavuja luettu: " << fin.gcount() << endl;
				    if (strlen(filename))
					    strncpy(fname,filename,strlen(filename));
					strncat(fname,(h.getFilename()),strlen(h.getFilename()));
				// cout << "filename: " << fname << endl;
				    makedirs(fname);
				    ofstream fout(fname);
				    if (!fout)
					{
					    fin.close();
						delete in;
						return -1;
				    }	
					else
					{
					    if (h.getMethod()==8)
						    b=inflatePacked(in,h.getCSize(),fout);
						else if (h.getMethod()==0)
					    {
						    fout.write(in,b);
						    b=Z_OK;
					    }
					    else
						{
						    fin.close();
						    delete in;
						    return -3;
					    }
					}
				    if (b!=Z_OK)
				    {
					    char eMessage[FNAME_MAX+30];
					    tyhjennaTaulukko(eMessage,FNAME_MAX+30);
					    strncpy(eMessage,"Error: Cannot uncompress properly file: ",40);
					    strcat(eMessage,filename);
					    // fin.close();
					    // delete in;
					    // return -1;
					    showMessage(eMessage);
				    }
				    delete in;
			    }
		    }
		    if (skip==1)
			    break;
	    }
		opened=2;
	    fin.close();
	}
	else if (fileType==2)
	{
	    if (opened)
	    {
		    int a=0;
		    int status=0;
			a=strlen(tiedosto)-1;
		    while(tiedosto[a]!='/')
			{
			    if (a<1)
				    break;
				a--;
		    }
			strcat(filename,tiedosto+a+1);
		    // cout << "Filename: " << filename << endl;
		    // cout << "Tiedosto: " << tiedosto+a+1 << endl;
		    status=gunzipFile(filename);
		    if (status!=Z_OK)
			    return -1;
	    }
	}
	return 0;
}

#ifdef USE_GTK
int ZipPacket::testcrc(GtkWidget *lista) // palautetaan 0, jos kunnossa, muuten -1
#else
int ZipPacket::testcrc(QListBox *lista)
#endif
{
    unsigned char *buffer;
    unsigned long crc=0;
    ZipHeader zh;
    BasicHeader bh;
#ifdef USE_GTK
    GtkWidget *list_item;
#else
    QString str;
#endif
    if (fileType!=1 || opened==0)
	    return 0;

	if (opened==1)
	{
	    fin.close();
	    opened=2;
	}
		    	    
    crc=crc32(0L,Z_NULL,0);
    fin.open(tiedosto);
    if (!fin)
    {
	    showError("Cannot open file ",16,tiedosto);
	    return 0;
	}
	
	while(zh.find(fin)==1)
	{
	    int status=0;
	    bh.read(fin);
	    buffer=new unsigned char[bh.getCSize()];
	    fin.read(buffer,bh.getCSize());
	    // cout << "Testing...";
	    // cout.flush();
		status=testInflated(buffer,bh.getCSize(),bh.getCrc32());
		// cout << ".." << endl;
	    if (status!=Z_OK)
	    {
		    char eMessage[FNAME_MAX+30];
		    tyhjennaTaulukko(eMessage,FNAME_MAX+30);
		    strcpy(eMessage,bh.getFilename());
		    switch(status)
		    {
			    case -10:
				    strncat(eMessage," (crc error)",12);
				    break;
				case Z_DATA_ERROR:
				    strncat(eMessage," (data error)",13);
				    break;
		    }
#ifdef USE_GTK
		    list_item=gtk_list_item_new_with_label(eMessage);
			gtk_container_add(GTK_CONTAINER(lista),list_item);
			gtk_widget_show(list_item);
#else
			lista->insertItem(eMessage);
#endif
		    // showError("Error in file ",14,bh.getFilename());
		    // cout << "Error in file " << bh.getFilename() << endl;
		}
		delete(buffer);
		if (fin.eof())
		    break;
	}
	fin.close();
	return 0;
}

int ZipPacket::inflatePacked(unsigned char *compr,
    unsigned long comprLen,ofstream & fout)
{
  int i=0;
  int windowBits=0;
  int status=Z_OK;
  unsigned char unCompr[0x8000+1];
  z_stream z;
  z.zalloc=Z_NULL;
  z.zfree=Z_NULL;
  z.opaque=0;
  for (i=0x8000,windowBits=0;!(i&1);i>>=1,++windowBits);
  
  z.avail_out=0x8000;
  z.next_out=unCompr;
  z.next_in=compr;
  z.avail_in=comprLen;
  
  status=inflateInit2(&z,-windowBits);
  if (status!=Z_OK)
    {
      // showMessage("Error initializing...");
      return status;
    }
  while(status!=Z_STREAM_END)
    {
      while(z.avail_out>0)
	{
	  status=inflate(&z,Z_PARTIAL_FLUSH);
	  
	  if (status==Z_STREAM_END)
	    break;
	  if (status!=Z_OK && status!=Z_STREAM_END)
	    {
	      // showMessage("Error at uncompressing");
	      return status;
	    }
	  if (z.avail_in<=0)
	    {
	      z.next_in=compr;
	      z.avail_in=comprLen;
	    }
	}
      // cout << "z.avail_out =" << z.avail_out << endl;
      fout.write(unCompr,0x8000-z.avail_out);
      z.next_out=unCompr;
      z.avail_out=0x8000;
    }
  while(status!=Z_STREAM_END)
    {
      status=inflate(&z,Z_PARTIAL_FLUSH);
      if (status==Z_STREAM_END)
	    break;
      if (status!=Z_OK)
	    return status;
      z.next_out=unCompr;
      z.avail_out=0x8000;
    }
  // cout << "Total out: " << z.total_out;
  status=inflateEnd(&z);
  if (status!=Z_OK)
    {
      // showMessage("Error at end");
      return status;
    }
  // cout << " OK" << endl;
  return Z_OK;
}

int ZipPacket::testInflated(unsigned char *compr,
    unsigned long comprLen,unsigned long original_crc)
{
  int i=0;
  int windowBits=0;
  int status=Z_OK;
  unsigned char unCompr[0x8000+1];
  unsigned long crc;
  z_stream z;
  z.zalloc=Z_NULL;
  z.zfree=Z_NULL;
  z.opaque=0;
  for (i=0x8000,windowBits=0;!(i&1);i>>=1,++windowBits);
  
  crc=crc32(0L,Z_NULL,0);
  
  z.avail_out=0x8000;
  z.next_out=unCompr;
  z.next_in=compr;
  z.avail_in=comprLen;
  
  status=inflateInit2(&z,-windowBits);
  if (status!=Z_OK)
  {
      // showMessage("Error initializing...");
      return status;
  }
  while(status!=Z_STREAM_END)
  {
    while(z.avail_out>0)
	{
	  status=inflate(&z,Z_PARTIAL_FLUSH);
	  if (status==Z_STREAM_END)
	    break;
	  if (status!=Z_OK && status!=Z_STREAM_END)
      {
	      // showMessage("Error at uncompressing");
	      return status;
	  }
	  if (z.avail_in<=0)
	  {
	      z.next_in=compr;
	      z.avail_in=comprLen;
	  }
	}
	if (0x8000-z.avail_out>0)
	    crc=crc32(crc,unCompr,0x8000-z.avail_out);
    // cout << "z.avail_out =" << z.avail_out << endl;
    // fout.write(unCompr,0x8000-z.avail_out);
    z.next_out=unCompr;
    z.avail_out=0x8000;
  }
  while(status!=Z_STREAM_END)
  {
      status=inflate(&z,Z_PARTIAL_FLUSH);
      if (status==Z_STREAM_END)
	    break;
      if (status!=Z_OK)
	    return status;
      z.next_out=unCompr;
      z.avail_out=0x8000;
  }
  // cout << "Crc: " << crc << endl << "Orig crc: " << original_crc << endl;
  // cout << "Total out: " << z.total_out;
  status=inflateEnd(&z);
  if (status!=Z_OK)
  {
      // showMessage("Error at end");
      return status;
  }
  if (crc != original_crc)
      return -10;
  // cout << " OK" << endl;
  return Z_OK;
}

unsigned long ZipPacket::getCSize(const char *name)
{
    CDRNode *p;
    p=cdList.getNodeByName(name);
    if (!p)
	    return 0;
	return p->h.getCSize();
}

unsigned long ZipPacket::getUnCSize(const char *name)
{
	CDRNode *p;
    p=cdList.getNodeByName(name);
    if (!p)
	    return 0;
	return p->h.getUnCSize();
}	

int ZipPacket::gunzipFile(char *filename)
{
    FILE *out;
    gzFile in;
    int len=strlen(filename);
    int status=0;
    
    in=gzopen(tiedosto,"r");
    if (in==NULL)
	    return -Z_ERRNO;
	    
	filename[len-GZ_SUFFIX_LEN]='\0';
	// cout << "gzfilename: " << filename << endl;
	out=fopen(filename,"w");
	if (out==NULL)
	{
	    gzclose(in);
	    return -Z_ERRNO;
	}
	status=gz_uncompress(in,out);
	gzclose(in);
	fclose(out);
	return status;
}

int gz_uncompress(gzFile in, FILE *out)
{
    char buf[4096];
    int len=0;
    // int err;

    for (;;) {
        len = gzread(in, buf, sizeof(buf));
        if (len < 0) return Z_ERRNO;
        if (len == 0) break;

        if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
            return Z_ERRNO;
        }
    }
//    if (fclose(out)) error("failed fclose");

//    if (gzclose(in) != Z_OK) error("failed gzclose");
    return Z_OK;
} 

