245 lines
6.4 KiB
C++
245 lines
6.4 KiB
C++
|
|
#include "modarchive.h"
|
||
|
|
|
||
|
|
//#include <archive_entry.h>
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
#include <QDebug>
|
||
|
|
|
||
|
|
#define BLOCKSIZE 10240
|
||
|
|
|
||
|
|
Archive::Archive(const QString &arcpath, QObject *parent)
|
||
|
|
: QObject{parent},
|
||
|
|
m_stream{NULL},
|
||
|
|
m_archive{NULL},
|
||
|
|
m_arcpath{arcpath},
|
||
|
|
m_suicidal{false}
|
||
|
|
{
|
||
|
|
if( !this->open() )
|
||
|
|
return;
|
||
|
|
|
||
|
|
qDebug() << "Archive:" << arcpath;
|
||
|
|
while( ar_parse_entry(m_archive) )
|
||
|
|
{
|
||
|
|
const char *entryname = ar_entry_get_name(m_archive);
|
||
|
|
if( strlen(entryname) <= 0 )
|
||
|
|
continue;
|
||
|
|
/*
|
||
|
|
if( (st->st_mode & S_IFMT) == S_IFDIR )
|
||
|
|
continue;
|
||
|
|
*/
|
||
|
|
time64_t filetime = ar_entry_get_filetime(m_archive) / 100000000;
|
||
|
|
|
||
|
|
ArchiveCacheEntry *cacheEntry = new ArchiveCacheEntry(this);
|
||
|
|
cacheEntry->m_offset = ar_entry_get_offset(m_archive);
|
||
|
|
memset( (void*)&cacheEntry->m_stat, 0, sizeof(struct stat) );
|
||
|
|
cacheEntry->m_stat.st_size = ar_entry_get_size(m_archive);
|
||
|
|
cacheEntry->m_stat.st_mode = S_IFREG | 0755;
|
||
|
|
cacheEntry->m_stat.st_nlink = 1;
|
||
|
|
cacheEntry->m_stat.st_uid = geteuid();
|
||
|
|
cacheEntry->m_stat.st_gid = getegid();
|
||
|
|
cacheEntry->m_stat.st_atim.tv_sec = filetime;
|
||
|
|
cacheEntry->m_stat.st_mtim.tv_sec = filetime;
|
||
|
|
cacheEntry->m_stat.st_ctim.tv_sec = filetime;
|
||
|
|
QString lpath = QString(entryname).toLower();
|
||
|
|
m_cache.insert( lpath, cacheEntry );
|
||
|
|
//qDebug() << "\tAdding" << lpath << "/" << cacheEntry->m_stat.st_size << "bytes at offset" << cacheEntry->m_offset;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Archive::~Archive()
|
||
|
|
{
|
||
|
|
for( ArchiveCacheEntry *ent : m_cache.values() )
|
||
|
|
ent->deleteLater();
|
||
|
|
|
||
|
|
close();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Archive::open()
|
||
|
|
{
|
||
|
|
m_stream = ar_open_file(m_arcpath.toStdString().c_str());
|
||
|
|
if( !m_stream ) {
|
||
|
|
qDebug() << "Failed to open archive stream:" << m_arcpath;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( m_arcpath.endsWith("7z", Qt::CaseInsensitive) )
|
||
|
|
m_archive = ar_open_7z_archive(m_stream);
|
||
|
|
else if( m_arcpath.endsWith("rar", Qt::CaseInsensitive) )
|
||
|
|
m_archive = ar_open_rar_archive(m_stream);
|
||
|
|
else if( m_arcpath.endsWith("zip", Qt::CaseInsensitive) )
|
||
|
|
m_archive = ar_open_zip_archive(m_stream, false);
|
||
|
|
else {
|
||
|
|
qDebug() << "Unsupported archive type:" << m_arcpath;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( !m_archive )
|
||
|
|
qDebug() << "Failed to open archive:" << m_arcpath;
|
||
|
|
return NULL != m_archive;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Archive::close()
|
||
|
|
{
|
||
|
|
if( !m_archive )
|
||
|
|
return false;
|
||
|
|
|
||
|
|
m_archive = NULL;
|
||
|
|
m_stream = NULL;
|
||
|
|
|
||
|
|
ar_close_archive(m_archive);
|
||
|
|
ar_close(m_stream);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Archive::contains(const QString &path)
|
||
|
|
{
|
||
|
|
QString lpath = path.toLower();
|
||
|
|
return m_cache.contains(lpath);
|
||
|
|
}
|
||
|
|
|
||
|
|
QMap< QString, ArchiveCacheEntry * > *Archive::getEntries()
|
||
|
|
{
|
||
|
|
return &m_cache;
|
||
|
|
}
|
||
|
|
|
||
|
|
QByteArray *Archive::getEntry(const QString &path)
|
||
|
|
{
|
||
|
|
QString lpath = path.toLower();
|
||
|
|
if( !m_cache.contains(lpath) ) {
|
||
|
|
qDebug() << " 918273918273918273 NOT FOUND:" << lpath;
|
||
|
|
return NULL; // Not found.
|
||
|
|
}
|
||
|
|
else if( m_cache[lpath]->m_loading ) {
|
||
|
|
//qDebug() << QString("getEntry(%1) -> (Still Loading...)\n").arg(path);
|
||
|
|
qDebug() << "Archive::getEntry: Already loading, chill.";
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
else if( m_cache[lpath]->m_cached ) {
|
||
|
|
//qDebug() << QString("getEntry(%1) -> CACHED\n").arg(path);
|
||
|
|
m_cache[lpath]->m_lastAccess = time(NULL);
|
||
|
|
m_cache[lpath]->m_accessCount++;
|
||
|
|
m_cache[lpath]->m_locked = true;
|
||
|
|
m_cache[lpath]->m_mutex.lock();
|
||
|
|
return &m_cache[lpath]->m_data;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_cache[lpath]->m_loading = true;
|
||
|
|
m_mutex.lock();
|
||
|
|
qDebug() << "Cheaty skip to" << m_cache[lpath]->m_offset << "bytes...";
|
||
|
|
if( !ar_parse_entry_at(m_archive, m_cache[lpath]->m_offset) ) {
|
||
|
|
qDebug() << "Archive::getEntry: ar_parse_entry_at failed!";
|
||
|
|
m_cache[lpath]->m_loading = false;
|
||
|
|
m_mutex.unlock();
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
//int64_t entrysize = archive_entry_size(entry);
|
||
|
|
QByteArray obuf;
|
||
|
|
char buff[BLOCKSIZE * 256];
|
||
|
|
size_t len = sizeof(buff);
|
||
|
|
if( len > (size_t)m_cache[lpath]->m_stat.st_size )
|
||
|
|
len = m_cache[lpath]->m_stat.st_size;
|
||
|
|
|
||
|
|
while( ar_entry_uncompress(m_archive, buff, len ) ) {
|
||
|
|
obuf.append(buff, len);
|
||
|
|
len = m_cache[lpath]->m_stat.st_size - obuf.length();
|
||
|
|
if( len <= 0 )
|
||
|
|
break;
|
||
|
|
if( len > sizeof(buff) )
|
||
|
|
len = sizeof(buff);
|
||
|
|
}
|
||
|
|
m_mutex.unlock();
|
||
|
|
|
||
|
|
if( 0 > obuf.length() ) {
|
||
|
|
m_cache[lpath]->m_loading = false;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_cache[lpath]->m_data = obuf;
|
||
|
|
m_cache[lpath]->m_cached = true;
|
||
|
|
m_cache[lpath]->m_accessCount++;
|
||
|
|
m_cache[lpath]->m_loading = false;
|
||
|
|
m_cache[lpath]->m_lastAccess = time(NULL);
|
||
|
|
m_cache[lpath]->m_locked = true;
|
||
|
|
|
||
|
|
m_cache[lpath]->m_mutex.lock();
|
||
|
|
return &m_cache[lpath]->m_data;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Archive::releaseEntry(const QString &path)
|
||
|
|
{
|
||
|
|
QString lpath = path.toLower();
|
||
|
|
if( !m_cache.contains(lpath) ) {
|
||
|
|
qDebug() << " releaseEntry: ---1231231 NOT FOUND:" << lpath;
|
||
|
|
return; // Not found.
|
||
|
|
}
|
||
|
|
/*
|
||
|
|
m_cache[lpath]->m_cached = false;
|
||
|
|
m_cache[lpath]->m_mutex.unlock();
|
||
|
|
m_cache[lpath]->m_locked = false;
|
||
|
|
m_cache[lpath]->m_data.clear();
|
||
|
|
*/
|
||
|
|
m_cache[lpath]->m_mutex.unlock();
|
||
|
|
m_cache[lpath]->m_locked = false;
|
||
|
|
|
||
|
|
if( m_suicidal )
|
||
|
|
checkIfLifeIsWorthIt();
|
||
|
|
}
|
||
|
|
|
||
|
|
ArchiveCacheEntry *Archive::cache(const QString &path)
|
||
|
|
{
|
||
|
|
QString lpath = path.toLower();
|
||
|
|
if( !m_cache.contains(lpath) )
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
return m_cache[lpath];
|
||
|
|
}
|
||
|
|
|
||
|
|
void Archive::cleanCache(time_t olderThan)
|
||
|
|
{
|
||
|
|
//return;
|
||
|
|
QStringList keys = m_cache.keys();
|
||
|
|
for( const QString &k : keys )
|
||
|
|
{
|
||
|
|
if( !m_cache[k]->m_cached )
|
||
|
|
continue;
|
||
|
|
|
||
|
|
if( m_cache[k]->m_loading )
|
||
|
|
continue;
|
||
|
|
|
||
|
|
if( m_cache[k]->m_locked )
|
||
|
|
continue;
|
||
|
|
|
||
|
|
if( m_cache[k]->m_lastAccess > olderThan )
|
||
|
|
continue;
|
||
|
|
|
||
|
|
qDebug() << "Archive::cleanCache: Clearing cache entry for:" << k;
|
||
|
|
m_cache[k]->m_mutex.lock();
|
||
|
|
m_cache[k]->m_cached = false;
|
||
|
|
m_cache[k]->m_data.clear();
|
||
|
|
m_cache[k]->m_mutex.unlock();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Archive::killme()
|
||
|
|
{
|
||
|
|
m_suicidal = true;
|
||
|
|
checkIfLifeIsWorthIt();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Archive::checkIfLifeIsWorthIt()
|
||
|
|
{
|
||
|
|
for( QString k : m_cache.keys() )
|
||
|
|
{
|
||
|
|
if( m_cache[k]->m_loading )
|
||
|
|
return;
|
||
|
|
|
||
|
|
if( m_cache[k]->m_locked )
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
deleteLater();
|
||
|
|
}
|