#include "modarchive.h" //#include #include #include #include #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(); }