From 2aabed9491fc397217be6e8d3e4a637fd65a0528 Mon Sep 17 00:00:00 2001 From: Thomas Frauendorfer Date: Tue, 9 Feb 2010 22:52:04 +0100 Subject: [PATCH] OTHER: read support for zipped skins For now, zipped skins don't show up in the selection dialog yet But they are loaded when set in the config file --- dir_iterator/archive_read_open_qiodevice.cpp | 148 ++++++++++++++++ dir_iterator/archiveiterator.cpp | 167 +++++++++++++++++++ dir_iterator/archiveiterator.h | 57 +++++++ dir_iterator/archivereaddevice.cpp | 45 +++++ dir_iterator/archivereaddevice.h | 47 ++++++ dir_iterator/diriteratorbase.cpp | 24 ++- dir_iterator/wscript | 7 +- src/skin/skin.cpp | 9 +- 8 files changed, 497 insertions(+), 7 deletions(-) create mode 100644 dir_iterator/archive_read_open_qiodevice.cpp create mode 100644 dir_iterator/archiveiterator.cpp create mode 100644 dir_iterator/archiveiterator.h create mode 100644 dir_iterator/archivereaddevice.cpp create mode 100644 dir_iterator/archivereaddevice.h diff --git a/dir_iterator/archive_read_open_qiodevice.cpp b/dir_iterator/archive_read_open_qiodevice.cpp new file mode 100644 index 0000000..15f6eb5 --- /dev/null +++ b/dir_iterator/archive_read_open_qiodevice.cpp @@ -0,0 +1,148 @@ +/*- + * Copyright (C) 2010 Thomas Frauendorfer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// On Debian, libarchive is compiled with 64bit off_t, but nothing in the +// libarchive headers tells so. In other words: Debian sucks. +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include +#include + +#include + +#if (ARCHIVE_API_VERSION < 2) +typedef __LA_SSIZE_T archive_skip_t; +#elif (ARCHIVE_API_VERSION < 3) +typedef off_t archive_skip_t; +#else +typedef __LA_INT64_T archive_skip_t; +#endif + +static const int READ_MAX = 4096; + +struct read_qiodevice_data { + QIODevice *device; + char buffer[READ_MAX]; +}; + +static int qiodevice_read_close(struct archive *, void *); +static archive_skip_t qiodevice_read_skip(struct archive *, void *, archive_skip_t); +static __LA_SSIZE_T qiodevice_read(struct archive *, void *, const void **); + +int +archive_read_open_qiodevice(struct archive *a, QIODevice *device) +{ + if (device == 0) { + qDebug () << "archive_read_open_qiodevice"; + archive_set_error(a, -1, "Invalid QIODevice"); + return ARCHIVE_FATAL; + } + + if (!device->isOpen() && !device->open(QIODevice::ReadOnly)) { + qDebug () << "archive_read_open_qiodevice"; + archive_set_error(a, -1, "Could not open QIODevice for reading"); + return ARCHIVE_FATAL; + } + + if (!device->isReadable()) { + qDebug () << "archive_read_open_qiodevice"; + archive_set_error(a, -1, "Could not open QIODevice for reading"); + return ARCHIVE_FATAL; + } + + struct read_qiodevice_data *data = new read_qiodevice_data; + if (data == 0) { + qDebug () << "archive_read_open_qiodevice"; + archive_set_error(a, -1, "No Memory"); + return ARCHIVE_FATAL; + } + data->device = device; + + device->reset(); // Seek to begining of file + device->setTextModeEnabled(false); // Binary mode + + return (archive_read_open2(a, data, NULL, qiodevice_read, + qiodevice_read_skip, qiodevice_read_close)); +} + +static int +qiodevice_read_close(struct archive *a, void *v) +{ + const struct read_qiodevice_data *data = static_cast(v); + + (void)a; + if (data != 0) { + delete data; + } + + return ARCHIVE_OK; +} + +static archive_skip_t +qiodevice_read_skip(struct archive *a, void *v, archive_skip_t _request) +{ + const struct read_qiodevice_data *data = static_cast(v); + + (void)a; /* unused */ + const qint64 request = _request; // make sure we work with 64bit values + + + if (data->device->isSequential()) { + return 0; + } + if (request == 0) { + return 0; + } + + const qint64 oldpos = data->device->pos(); + if (data->device->seek(oldpos + request)) { + return request; + } + + // Seek might have failed because we tried to seek beyond the end of a file + qDebug("Seek failed, seeked: %lli", data->device->pos() - oldpos); + + return data->device->pos() - oldpos; +} + +static __LA_SSIZE_T +qiodevice_read(struct archive *a, void *v, const void **buf) +{ + struct read_qiodevice_data *data = static_cast(v); + + (void)a; + + *buf = data->buffer; + qint64 bytes_read = data->device->read(data->buffer, READ_MAX); + + if (bytes_read < 0) { + archive_set_error(a, 1, "Error reading from QIODevice"); + } + + return bytes_read; +} + + diff --git a/dir_iterator/archiveiterator.cpp b/dir_iterator/archiveiterator.cpp new file mode 100644 index 0000000..019cb7a --- /dev/null +++ b/dir_iterator/archiveiterator.cpp @@ -0,0 +1,167 @@ +/** + * This file is a part of Promoe, an XMMS2 Client. + * + * Copyright (C) 2010 XMMS2 Team + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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. + */ + +#include "archiveiterator.h" +#include "archivereaddevice.h" + +#include "promoe_config.h" +#if HAVE_LIBARCHIVE +#include "archive.h" +#include "archive_entry.h" +#endif + +#include + +int archive_read_open_qiodevice(struct archive *a, QIODevice *device); + +ArchiveIterator::ArchiveIterator (const QString &path) : QObject (), + DirIteratorBase (), m_current(NULL) +{ + m_dev = new QFile (path); + m_valid = finish_init (m_dev); +} + +ArchiveIterator::ArchiveIterator (QIODevice *d) : QObject (), + DirIteratorBase (), m_current(NULL), m_dev (NULL) +{ + m_valid = finish_init (d); +} + +bool +ArchiveIterator::finish_init (QIODevice *d) +{ + m_archive_entry = NULL; + m_archive = NULL; + m_atEnd = false; + +#if HAVE_LIBARCHIVE + m_archive = archive_read_new (); + if (m_archive == 0) { + qDebug () << "archive_read_new failed"; + return false; + } + + if (archive_read_support_compression_all (m_archive) != ARCHIVE_OK) { + qDebug () << "archive_read_support_compression_all failed"; + return false; + } + if (archive_read_support_format_all (m_archive) != ARCHIVE_OK) { + qDebug () << "archive_read_support_format_all failed"; + return false; + } + if (archive_read_open_qiodevice (m_archive, d) != ARCHIVE_OK) { + qDebug () << "archive_read_open_qiodevice failed"; + return false; + } + + return true; +#else + return false; +#endif +} + +ArchiveIterator::~ArchiveIterator () +{ +#if HAVE_LIBARCHIVE + if (m_archive != 0) { + archive_read_finish (m_archive); + m_archive = 0; + } +#endif + + if (m_dev != 0) { + delete m_dev; + m_dev = 0; + } +} + +/* + * DirIteratorBase methods + */ + +QString +ArchiveIterator::next () +{ +#if HAVE_LIBARCHIVE + if (!m_valid || m_atEnd) { + return QString (); + } + if (m_current != 0) { + m_current->close (); + m_current->deleteLater (); + } + if (archive_read_next_header (m_archive, &m_archive_entry) != ARCHIVE_OK) { + m_atEnd = true; + return QString (); + } + QString s(archive_entry_pathname (m_archive_entry)); + m_current = new ArchiveReadDevice (this, s); + return s; +#else + return QString (); +#endif +} + +QString +ArchiveIterator::pathName () +{ +#if HAVE_LIBARCHIVE + if (!m_valid || m_atEnd) { + return QString (); + } + QString s(archive_entry_pathname (m_archive_entry)); + return s; +#else + return QString (); +#endif +} + +bool +ArchiveIterator::hasNext () +{ +#if HAVE_LIBARCHIVE + return !m_atEnd; // workaround, as there is no has_next method in libarchive +#else + return false; +#endif +} + +QPointer +ArchiveIterator::entry () +{ +#if HAVE_LIBARCHIVE + return m_current; +#else + return NULL; +#endif +} + +qint64 +ArchiveIterator::readData(char *data, qint64 size) +{ +#if HAVE_LIBARCHIVE + if (!m_valid || m_atEnd) { + qDebug () << "gnarf"; + return -1; + } + qint64 ret = archive_read_data (m_archive, data, size); + return ret; +#else + return -1; +#endif +} + +#include "archiveiterator.moc" diff --git a/dir_iterator/archiveiterator.h b/dir_iterator/archiveiterator.h new file mode 100644 index 0000000..56bfe38 --- /dev/null +++ b/dir_iterator/archiveiterator.h @@ -0,0 +1,57 @@ +/** + * This file is a part of Promoe, an XMMS2 Client. + * + * Copyright (C) 2010 XMMS2 Team + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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. + */ + +#ifndef __ARCHIVEITERATOR_H__ +#define __ARCHIVEITERATOR_H__ + +#include + +#include "diriteratorbase.h" + +struct archive; +struct archive_entry; + +class ArchiveIterator : public QObject, public DirIteratorBase +{ + Q_OBJECT + +public: + ArchiveIterator (const QString &path); + ArchiveIterator (QIODevice *); + ~ArchiveIterator (); + + // DirIteratorBase Methods + QString next (); + QString pathName (); + bool hasNext (); + QPointer entry (); + + // QIODevice Methods + qint64 readData(char*, qint64); + +protected: + bool m_valid; + bool m_atEnd; + + struct archive *m_archive; + struct archive_entry *m_archive_entry; + + QIODevice *m_dev; + QPointer m_current; + bool finish_init (QIODevice *); +}; + +#endif diff --git a/dir_iterator/archivereaddevice.cpp b/dir_iterator/archivereaddevice.cpp new file mode 100644 index 0000000..2a38648 --- /dev/null +++ b/dir_iterator/archivereaddevice.cpp @@ -0,0 +1,45 @@ +/** + * This file is a part of Promoe, an XMMS2 Client. + * + * Copyright (C) 2010 XMMS2 Team + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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. + */ + + +#include "archivereaddevice.h" + +#include "promoe_config.h" + +#include + +ArchiveReadDevice::ArchiveReadDevice (ArchiveIterator *parent, + const QString &name) + : QIODevice (parent), m_iter(parent), m_atEnd(false) +{ + setObjectName (name); + setOpenMode (QIODevice::ReadOnly); +} + +qint64 +ArchiveReadDevice::readData (char* buf, qint64 size) +{ + if (!isOpen ()) { + return -1; + } + qint64 ret = m_iter->readData (buf, size); + if (ret <= 0) { + m_atEnd = true; + } + return ret; +} + +#include "archivereaddevice.moc" diff --git a/dir_iterator/archivereaddevice.h b/dir_iterator/archivereaddevice.h new file mode 100644 index 0000000..f27828d --- /dev/null +++ b/dir_iterator/archivereaddevice.h @@ -0,0 +1,47 @@ +/** + * This file is a part of Promoe, an XMMS2 Client. + * + * Copyright (C) 2010 XMMS2 Team + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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. + */ + +#ifndef __ARCHIVEREADDEVICE_H__ +#define __ARCHIVEREADDEVICE_H__ + +#include + +#include "archiveiterator.h" + +struct archive; +struct archive_entry; + +class ArchiveReadDevice : public QIODevice +{ + Q_OBJECT + +public: + ArchiveReadDevice (ArchiveIterator *parent, const QString &name = QString ()); + ~ArchiveReadDevice () {}; + + // QIODevice Methods + qint64 readData(char*, qint64); + qint64 writeData(const char*, qint64) { return -1; } // read only + + bool atEnd () const { return m_atEnd; } + +protected: + ArchiveIterator *m_iter; + + bool m_atEnd; +}; + +#endif diff --git a/dir_iterator/diriteratorbase.cpp b/dir_iterator/diriteratorbase.cpp index eb4c064..a536ee0 100644 --- a/dir_iterator/diriteratorbase.cpp +++ b/dir_iterator/diriteratorbase.cpp @@ -16,8 +16,9 @@ #include "diriteratorbase.h" #include "diriterator.h" +#include "archiveiterator.h" #include -#include "QFile" +#include "QFileInfo" #include @@ -35,17 +36,32 @@ DirIteratorBase::pixmapEntry () QPixmap p; p.loadFromData(a); + if (p.isNull ()) { + qDebug () << "Size:" << a.size (); + } + return p; } DirIteratorBase * DirIteratorBase::open(const QString &path) { - QDir dir(path); - if (!dir.exists ()) { + QFileInfo info(path); + if (!info.exists ()) { return 0; } - return new DirIterator (dir); + if (info.isDir ()) { + return new DirIterator (path); + } + + if (info.isFile ()) { + ArchiveIterator *iter = new ArchiveIterator (path); + if (iter && !iter->hasNext ()) { + delete iter; + return 0; + } + return iter; + } } diff --git a/dir_iterator/wscript b/dir_iterator/wscript index a1ee7cb..efa57b8 100644 --- a/dir_iterator/wscript +++ b/dir_iterator/wscript @@ -36,20 +36,25 @@ POSSIBILITY OF SUCH DAMAGE. lib_source = """ diriteratorbase.cpp diriterator.cpp + archiveiterator.cpp + archivereaddevice.cpp + archive_read_open_qiodevice.cpp """ def set_options(opt): pass def configure(conf): + conf.check_cfg(package='libarchive', args="--cflags --libs") pass + def build(bld): obj = bld.new_task_gen(features='qt4 cstaticlib cxx') obj.target = 'dir_iterator' obj.install_path = 0 # Don't install obj.includes = '.' obj.source = lib_source - obj.uselib = 'QTCORE QTGUI' + obj.uselib = 'QTCORE QTGUI LIBARCHIVE' obj.export_incdirs = '.' pass diff --git a/src/skin/skin.cpp b/src/skin/skin.cpp index 67a8622..fd2c49d 100644 --- a/src/skin/skin.cpp +++ b/src/skin/skin.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -700,8 +701,12 @@ Skin::setSkin (const QString& path) QPixmap p_numbers; QPixmap p_volume; - while (iter->hasNext ()) { - QString entry = iter->next ().toLower (); + QString entry; + while (!(entry = iter->next ().toLower ()).isEmpty ()) { + if ((entry = QFileInfo (entry).fileName ()).isEmpty ()) { + // workaround to ignore pathes in archives + continue; + } if (entry.endsWith (".txt")) { QPointer d = iter->entry (); if (d == 0)