misc/quazip/quazipfile.cpp
author sheepluva
Wed, 25 Apr 2012 08:23:48 +0200
changeset 6928 9562ace15141
parent 5752 ea95ee97c805
child 7889 57b117d441b9
permissions -rw-r--r--
nemo said I was boring for removing the "hello" randomizer on room join. That was actually an accident, fixed by this commit. Now find an actual reason to call me boring (there are many to choose from :P) nemo!

/*
Copyright (C) 2005-2011 Sergey A. Tachenov

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser 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 Lesser
General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

See COPYING file for the full LGPL text.

Original ZIP package is copyrighted by Gilles Vollant, see
quazip/(un)zip.h files for details, basically it's zlib license.
 **/

#include "quazipfile.h"

using namespace std;

class QuaZipFilePrivate {
  friend class QuaZipFile;
  private:
    QuaZipFile *q;
    QuaZip *zip;
    QString fileName;
    QuaZip::CaseSensitivity caseSensitivity;
    bool raw;
    qint64 writePos;
    // these two are for writing raw files
    ulong uncompressedSize;
    quint32 crc;
    bool internal;
    int zipError;
    inline void resetZipError() const {setZipError(UNZ_OK);}
    // const, but sets zipError!
    void setZipError(int zipError) const;
    inline QuaZipFilePrivate(QuaZipFile *q):
      q(q), zip(NULL), internal(true), zipError(UNZ_OK) {}
    inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName):
      q(q), internal(true), zipError(UNZ_OK)
      {
        zip=new QuaZip(zipName);
      }
    inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName,
        QuaZip::CaseSensitivity cs):
      q(q), internal(true), zipError(UNZ_OK)
      {
        zip=new QuaZip(zipName);
        this->fileName=fileName;
        this->caseSensitivity=cs;
      }
    inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip):
      q(q), zip(zip), internal(false), zipError(UNZ_OK) {}
    inline ~QuaZipFilePrivate()
    {
      if (internal)
        delete zip;
    }
};

QuaZipFile::QuaZipFile():
  p(new QuaZipFilePrivate(this))
{
}

QuaZipFile::QuaZipFile(QObject *parent):
  QIODevice(parent),
  p(new QuaZipFilePrivate(this))
{
}

QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent):
  QIODevice(parent),
  p(new QuaZipFilePrivate(this, zipName))
{
}

QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName,
    QuaZip::CaseSensitivity cs, QObject *parent):
  QIODevice(parent),
  p(new QuaZipFilePrivate(this, zipName, fileName, cs))
{
}

QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent):
  QIODevice(parent),
  p(new QuaZipFilePrivate(this, zip))
{
}

QuaZipFile::~QuaZipFile()
{
  if (isOpen())
    close();
  delete p;
}

QString QuaZipFile::getZipName() const
{
  return p->zip==NULL ? QString() : p->zip->getZipName();
}

QString QuaZipFile::getActualFileName()const
{
  p->setZipError(UNZ_OK);
  if (p->zip == NULL || (openMode() & WriteOnly))
    return QString();
  QString name=p->zip->getCurrentFileName();
  if(name.isNull())
    p->setZipError(p->zip->getZipError());
  return name;
}

void QuaZipFile::setZipName(const QString& zipName)
{
  if(isOpen()) {
    qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name");
    return;
  }
  if(p->zip!=NULL && p->internal)
    delete p->zip;
  p->zip=new QuaZip(zipName);
  p->internal=true;
}

void QuaZipFile::setZip(QuaZip *zip)
{
  if(isOpen()) {
    qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP");
    return;
  }
  if(p->zip!=NULL && p->internal)
    delete p->zip;
  p->zip=zip;
  p->fileName=QString();
  p->internal=false;
}

void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs)
{
  if(p->zip==NULL) {
    qWarning("QuaZipFile::setFileName(): call setZipName() first");
    return;
  }
  if(!p->internal) {
    qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip");
    return;
  }
  if(isOpen()) {
    qWarning("QuaZipFile::setFileName(): can not set file name for already opened file");
    return;
  }
  p->fileName=fileName;
  p->caseSensitivity=cs;
}

void QuaZipFilePrivate::setZipError(int zipError) const
{
  QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const
  fakeThis->zipError=zipError;
  if(zipError==UNZ_OK)
    q->setErrorString(QString());
  else
    q->setErrorString(q->tr("ZIP/UNZIP API error %1").arg(zipError));
}

bool QuaZipFile::open(OpenMode mode)
{
  return open(mode, NULL);
}

bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password)
{
  p->resetZipError();
  if(isOpen()) {
    qWarning("QuaZipFile::open(): already opened");
    return false;
  }
  if(mode&Unbuffered) {
    qWarning("QuaZipFile::open(): Unbuffered mode is not supported");
    return false;
  }
  if((mode&ReadOnly)&&!(mode&WriteOnly)) {
    if(p->internal) {
      if(!p->zip->open(QuaZip::mdUnzip)) {
        p->setZipError(p->zip->getZipError());
        return false;
      }
      if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) {
        p->setZipError(p->zip->getZipError());
        p->zip->close();
        return false;
      }
    } else {
      if(p->zip==NULL) {
        qWarning("QuaZipFile::open(): zip is NULL");
        return false;
      }
      if(p->zip->getMode()!=QuaZip::mdUnzip) {
        qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
            (int)mode, (int)p->zip->getMode());
        return false;
      }
      if(!p->zip->hasCurrentFile()) {
        qWarning("QuaZipFile::open(): zip does not have current file");
        return false;
      }
    }
    p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password));
    if(p->zipError==UNZ_OK) {
      setOpenMode(mode);
      p->raw=raw;
      return true;
    } else
      return false;
  }
  qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
  return false;
}

bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info,
    const char *password, quint32 crc,
    int method, int level, bool raw,
    int windowBits, int memLevel, int strategy)
{
  zip_fileinfo info_z;
  p->resetZipError();
  if(isOpen()) {
    qWarning("QuaZipFile::open(): already opened");
    return false;
  }
  if((mode&WriteOnly)&&!(mode&ReadOnly)) {
    if(p->internal) {
      qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach");
      return false;
    }
    if(p->zip==NULL) {
      qWarning("QuaZipFile::open(): zip is NULL");
      return false;
    }
    if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) {
      qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
          (int)mode, (int)p->zip->getMode());
      return false;
    }
    info_z.tmz_date.tm_year=info.dateTime.date().year();
    info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1;
    info_z.tmz_date.tm_mday=info.dateTime.date().day();
    info_z.tmz_date.tm_hour=info.dateTime.time().hour();
    info_z.tmz_date.tm_min=info.dateTime.time().minute();
    info_z.tmz_date.tm_sec=info.dateTime.time().second();
    info_z.dosDate = 0;
    info_z.internal_fa=(uLong)info.internalAttr;
    info_z.external_fa=(uLong)info.externalAttr;
    p->setZipError(zipOpenNewFileInZip3(p->zip->getZipFile(),
          p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z,
          info.extraLocal.constData(), info.extraLocal.length(),
          info.extraGlobal.constData(), info.extraGlobal.length(),
          p->zip->getCommentCodec()->fromUnicode(info.comment).constData(),
          method, level, (int)raw,
          windowBits, memLevel, strategy,
          password, (uLong)crc));
    if(p->zipError==UNZ_OK) {
      p->writePos=0;
      setOpenMode(mode);
      p->raw=raw;
      if(raw) {
        p->crc=crc;
        p->uncompressedSize=info.uncompressedSize;
      }
      return true;
    } else
      return false;
  }
  qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
  return false;
}

bool QuaZipFile::isSequential()const
{
  return true;
}

qint64 QuaZipFile::pos()const
{
  if(p->zip==NULL) {
    qWarning("QuaZipFile::pos(): call setZipName() or setZip() first");
    return -1;
  }
  if(!isOpen()) {
    qWarning("QuaZipFile::pos(): file is not open");
    return -1;
  }
  if(openMode()&ReadOnly)
    return unztell(p->zip->getUnzFile());
  else
    return p->writePos;
}

bool QuaZipFile::atEnd()const
{
  if(p->zip==NULL) {
    qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first");
    return false;
  }
  if(!isOpen()) {
    qWarning("QuaZipFile::atEnd(): file is not open");
    return false;
  }
  if(openMode()&ReadOnly)
    return unzeof(p->zip->getUnzFile())==1;
  else
    return true;
}

qint64 QuaZipFile::size()const
{
  if(!isOpen()) {
    qWarning("QuaZipFile::atEnd(): file is not open");
    return -1;
  }
  if(openMode()&ReadOnly)
    return p->raw?csize():usize();
  else
    return p->writePos;
}

qint64 QuaZipFile::csize()const
{
  unz_file_info info_z;
  p->setZipError(UNZ_OK);
  if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
  p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
  if(p->zipError!=UNZ_OK)
    return -1;
  return info_z.compressed_size;
}

qint64 QuaZipFile::usize()const
{
  unz_file_info info_z;
  p->setZipError(UNZ_OK);
  if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
  p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
  if(p->zipError!=UNZ_OK)
    return -1;
  return info_z.uncompressed_size;
}

bool QuaZipFile::getFileInfo(QuaZipFileInfo *info)
{
  if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false;
  p->zip->getCurrentFileInfo(info);
  p->setZipError(p->zip->getZipError());
  return p->zipError==UNZ_OK;
}

void QuaZipFile::close()
{
  p->resetZipError();
  if(p->zip==NULL||!p->zip->isOpen()) return;
  if(!isOpen()) {
    qWarning("QuaZipFile::close(): file isn't open");
    return;
  }
  if(openMode()&ReadOnly)
    p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile()));
  else if(openMode()&WriteOnly)
    if(isRaw()) p->setZipError(zipCloseFileInZipRaw(p->zip->getZipFile(), p->uncompressedSize, p->crc));
    else p->setZipError(zipCloseFileInZip(p->zip->getZipFile()));
  else {
    qWarning("Wrong open mode: %d", (int)openMode());
    return;
  }
  if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen);
  else return;
  if(p->internal) {
    p->zip->close();
    p->setZipError(p->zip->getZipError());
  }
}

qint64 QuaZipFile::readData(char *data, qint64 maxSize)
{
  p->setZipError(UNZ_OK);
  qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize);
  if(bytesRead<0) p->setZipError((int)bytesRead);
  return bytesRead;
}

qint64 QuaZipFile::writeData(const char* data, qint64 maxSize)
{
  p->setZipError(ZIP_OK);
  p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize));
  if(p->zipError!=ZIP_OK) return -1;
  else {
    p->writePos+=maxSize;
    return maxSize;
  }
}

QString QuaZipFile::getFileName() const
{
  return p->fileName;
}

QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const
{
  return p->caseSensitivity;
}

bool QuaZipFile::isRaw() const
{
  return p->raw;
}

int QuaZipFile::getZipError() const
{
  return p->zipError;
}