9#include <qstandardpaths.h>
12#include <QXmlStreamReader>
16#include <QByteArrayMatcher>
21#if QT_CONFIG(mimetype_database)
22# if defined(Q_CC_MSVC_ONLY)
23# pragma section(".qtmimedatabase", read, shared)
24__declspec(allocate(
".qtmimedatabase")) __declspec(align(4096))
25# elif defined(Q_OS_DARWIN)
26__attribute__((section(
"__TEXT,.qtmimedatabase"), aligned(4096)))
27# elif (defined(Q_OF_ELF) || defined(Q_OS_WIN)) && defined(Q_CC_GNU)
28__attribute__((section(
".qtmimedatabase"), aligned(4096)))
31# include "qmimeprovider_database.cpp"
33# ifdef MIME_DATABASE_IS_ZSTD
35# error "MIME database is zstd but no support compiled in!"
39# ifdef MIME_DATABASE_IS_GZIP
41# error "MIME database is zlib but no support compiled in!"
51using namespace Qt::StringLiterals;
61 return qFromBigEndian(*
reinterpret_cast<quint16 *>(data + offset));
65 return qFromBigEndian(*
reinterpret_cast<quint32 *>(data + offset));
69 return reinterpret_cast<
const char *>(data + offset);
80static inline void appendIfNew(QStringList &list,
const QString &str)
82 if (!list.contains(str))
87 :
m_db(db), m_directory(directory)
129 if (!file.open(QIODevice::ReadOnly))
131 data = file.map(0, file.size());
133 const int major = getUint16(0);
134 const int minor = getUint16(2);
135 m_valid = (major == 1 && minor >= 1 && minor <= 2);
137 m_mtime = QFileInfo(file).lastModified(QTimeZone::UTC);
155 return m_cacheFile !=
nullptr;
176bool QMimeBinaryProvider::checkCacheChanged()
178 QFileInfo fileInfo(m_cacheFile->file);
179 if (fileInfo.lastModified(QTimeZone::UTC) > m_cacheFile->m_mtime) {
182 m_cacheFile->reload();
191 const QString cacheFileName = m_directory +
"/mime.cache"_L1;
192 m_cacheFile = std::make_unique<CacheFile>(cacheFileName);
193 m_mimetypeListLoaded =
false;
194 m_mimetypeExtra.clear();
196 if (checkCacheChanged()) {
197 m_mimetypeListLoaded =
false;
198 m_mimetypeExtra.clear();
203 if (!m_cacheFile->isValid())
209 if (!m_mimetypeListLoaded)
211 return m_mimetypeNames.contains(name);
216 if (fileName.isEmpty())
218 Q_ASSERT(m_cacheFile);
221 numMatches = matchGlobList(result, m_cacheFile.get(),
222 m_cacheFile->getUint32(PosLiteralListOffset), fileName);
224 if (numMatches == 0) {
225 const QString lowerFileName = fileName.toLower();
226 const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset);
227 const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset);
228 const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4);
229 if (matchSuffixTree(result, m_cacheFile.get(), numRoots, firstRootOffset, lowerFileName,
230 lowerFileName.size() - 1,
false)) {
232 }
else if (matchSuffixTree(result, m_cacheFile.get(), numRoots, firstRootOffset, fileName,
233 fileName.size() - 1,
true)) {
239 matchGlobList(result, m_cacheFile.get(), m_cacheFile->getUint32(PosGlobListOffset),
243int QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result,
CacheFile *cacheFile,
int off,
244 const QString &fileName)
247 const int numGlobs = cacheFile->getUint32(off);
249 for (
int i = 0; i < numGlobs; ++i) {
250 const int globOffset = cacheFile->getUint32(off + 4 + 12 * i);
251 const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4);
252 const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8);
253 const int weight = flagsAndWeight & 0xff;
254 const bool caseSensitive = flagsAndWeight & 0x100;
255 const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
256 const QString pattern = QLatin1StringView(cacheFile
->getCharStar(globOffset
));
258 const QLatin1StringView mimeType(cacheFile
->getCharStar(mimeTypeOffset
));
260 if (isMimeTypeGlobsExcluded(mimeType))
264 if (glob.matchFileName(fileName)) {
265 result.addMatch(mimeType, weight, pattern);
272bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result,
273 QMimeBinaryProvider::
CacheFile *cacheFile,
int numEntries,
274 int firstOffset,
const QString &fileName,
275 qsizetype charPos,
bool caseSensitiveCheck)
277 QChar fileChar = fileName[charPos];
279 int max = numEntries - 1;
281 const int mid = (min + max) / 2;
282 const int off = firstOffset + 12 * mid;
283 const QChar ch =
char16_t(cacheFile->getUint32(off));
286 else if (ch > fileChar)
290 int numChildren = cacheFile->getUint32(off + 4);
291 int childrenOffset = cacheFile->getUint32(off + 8);
292 bool success =
false;
294 success = matchSuffixTree(result, cacheFile, numChildren, childrenOffset, fileName, charPos, caseSensitiveCheck);
296 for (
int i = 0; i < numChildren; ++i) {
297 const int childOff = childrenOffset + 12 * i;
298 const int mch = cacheFile->getUint32(childOff);
301 const int mimeTypeOffset = cacheFile->getUint32(childOff + 4);
302 const QLatin1StringView mimeType(cacheFile
->getCharStar(mimeTypeOffset
));
303 if (isMimeTypeGlobsExcluded(mimeType))
305 const int flagsAndWeight = cacheFile->getUint32(childOff + 8);
306 const int weight = flagsAndWeight & 0xff;
307 const bool caseSensitive = flagsAndWeight & 0x100;
308 if (caseSensitiveCheck || !caseSensitive) {
309 result.addMatch(mimeType, weight,
310 u'*' + QStringView{ fileName }.mid(charPos + 1),
311 fileName.size() - charPos - 2);
322bool QMimeBinaryProvider::matchMagicRule(QMimeBinaryProvider::
CacheFile *cacheFile,
int numMatchlets,
int firstOffset,
const QByteArray &data)
324 const char *dataPtr = data.constData();
325 const qsizetype dataSize = data.size();
326 for (
int matchlet = 0; matchlet < numMatchlets; ++matchlet) {
327 const int off = firstOffset + matchlet * 32;
328 const int rangeStart = cacheFile->getUint32(off);
329 const int rangeLength = cacheFile->getUint32(off + 4);
331 const int valueLength = cacheFile->getUint32(off + 12);
332 const int valueOffset = cacheFile->getUint32(off + 16);
333 const int maskOffset = cacheFile->getUint32(off + 20);
334 const char *mask = maskOffset ? cacheFile
->getCharStar(maskOffset
) :
nullptr;
336 if (!QMimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile
->getCharStar(valueOffset
), mask))
339 const int numChildren = cacheFile->getUint32(off + 24);
340 const int firstChildOffset = cacheFile->getUint32(off + 28);
341 if (numChildren == 0)
344 if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data))
352 const int magicListOffset = m_cacheFile->getUint32(PosMagicListOffset);
353 const int numMatches = m_cacheFile->getUint32(magicListOffset);
355 const int firstMatchOffset = m_cacheFile->getUint32(magicListOffset + 8);
357 for (
int i = 0; i < numMatches; ++i) {
358 const int off = firstMatchOffset + i * 16;
359 const int numMatchlets = m_cacheFile->getUint32(off + 8);
360 const int firstMatchletOffset = m_cacheFile->getUint32(off + 12);
361 if (matchMagicRule(m_cacheFile.get(), numMatchlets, firstMatchletOffset, data)) {
362 const int mimeTypeOffset = m_cacheFile->getUint32(off + 4);
363 const char *mimeType = m_cacheFile->getCharStar(mimeTypeOffset);
364 const int accuracy =
static_cast<
int>(m_cacheFile->getUint32(off));
367 result.candidate = QString::fromLatin1(mimeType);
375void QMimeBinaryProvider::
addParents(
const QString &mime, QStringList &result)
378 const int parentListOffset = m_cacheFile->getUint32(PosParentListOffset);
379 const int numEntries = m_cacheFile->getUint32(parentListOffset);
382 int end = numEntries - 1;
383 while (begin <= end) {
384 const int medium = (begin + end) / 2;
385 const int off = parentListOffset + 4 + 8 * medium;
386 const int mimeOffset = m_cacheFile->getUint32(off);
387 const char *aMime = m_cacheFile->getCharStar(mimeOffset);
388 const int cmp = qstrcmp(aMime, mimeStr);
391 }
else if (cmp > 0) {
394 const int parentsOffset = m_cacheFile->getUint32(off + 4);
395 const int numParents = m_cacheFile->getUint32(parentsOffset);
396 for (
int i = 0; i < numParents; ++i) {
397 const int parentOffset = m_cacheFile->getUint32(parentsOffset + 4 + 4 * i);
398 const char *aParent = m_cacheFile->getCharStar(parentOffset);
399 const QString strParent = QString::fromLatin1(aParent);
400 appendIfNew(result, strParent);
410 const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset);
411 const int numEntries = m_cacheFile->getUint32(aliasListOffset);
413 int end = numEntries - 1;
414 while (begin <= end) {
415 const int medium = (begin + end) / 2;
416 const int off = aliasListOffset + 4 + 8 * medium;
417 const int aliasOffset = m_cacheFile->getUint32(off);
418 const char *alias = m_cacheFile->getCharStar(aliasOffset);
419 const int cmp = qstrcmp(alias, input);
422 }
else if (cmp > 0) {
425 const int mimeOffset = m_cacheFile->getUint32(off + 4);
426 const char *mimeType = m_cacheFile->getCharStar(mimeOffset);
427 return QLatin1StringView(mimeType);
433void QMimeBinaryProvider::
addAliases(
const QString &name, QStringList &result)
436 const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset);
437 const int numEntries = m_cacheFile->getUint32(aliasListOffset);
438 for (
int pos = 0; pos < numEntries; ++pos) {
439 const int off = aliasListOffset + 4 + 8 * pos;
440 const int mimeOffset = m_cacheFile->getUint32(off + 4);
441 const char *mimeType = m_cacheFile->getCharStar(mimeOffset);
443 if (input == mimeType) {
444 const int aliasOffset = m_cacheFile->getUint32(off);
445 const char *alias = m_cacheFile->getCharStar(aliasOffset);
446 const QString strAlias = QString::fromLatin1(alias);
447 appendIfNew(result, strAlias);
452void QMimeBinaryProvider::loadMimeTypeList()
454 if (!m_mimetypeListLoaded) {
455 m_mimetypeListLoaded =
true;
456 m_mimetypeNames.clear();
459 QFile file(m_directory + QStringView(u"/types"));
460 if (file.open(QIODevice::ReadOnly)) {
462 while (file.readLineInto(&line)) {
463 auto lineView = QByteArrayView(line);
464 if (lineView.endsWith(
'\n'))
466 m_mimetypeNames.insert(QString::fromLatin1(lineView));
475 if (result.isEmpty()) {
476 result.reserve(m_mimetypeNames.size());
477 for (
const QString &name : std::as_const(m_mimetypeNames))
478 result.append(QMimeType(QMimeTypePrivate(name)));
480 for (
const QString &name : std::as_const(m_mimetypeNames))
481 if (std::find_if(result.constBegin(), result.constEnd(), [name](
const QMimeType &mime) ->
bool {
return mime.name() == name; })
482 == result.constEnd())
483 result.append(QMimeType(QMimeTypePrivate(name)));
489 MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name);
490 if (it != m_mimetypeExtra.cend())
491 return it->second.localeComments;
497 MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name);
498 if (it != m_mimetypeExtra.cend())
499 return it->second.hasGlobDeleteAll;
505 MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name);
506 if (it != m_mimetypeExtra.cend())
507 return it->second.globPatterns;
511QMimeBinaryProvider::MimeTypeExtraMap::const_iterator
512QMimeBinaryProvider::loadMimeTypeExtra(
const QString &mimeName)
514#if QT_CONFIG(xmlstreamreader)
515 auto it = m_mimetypeExtra.find(mimeName);
516 if (it == m_mimetypeExtra.cend()) {
520 QString mimeFile = m_directory + u'/' + mimeName.toLower() +
".xml"_L1;
521 if (!QFile::exists(mimeFile))
522 mimeFile = m_directory + u'/' + mimeName +
".xml"_L1;
524 QFile qfile(mimeFile);
525 if (!qfile.open(QFile::ReadOnly))
526 return m_mimetypeExtra.cend();
528 it = m_mimetypeExtra.try_emplace(mimeName).first;
529 MimeTypeExtra &extra = it->second;
532 QXmlStreamReader xml(&qfile);
533 if (xml.readNextStartElement()) {
534 if (xml.name() !=
"mime-type"_L1) {
535 return m_mimetypeExtra.cend();
537 const auto name = xml.attributes().value(
"type"_L1);
539 return m_mimetypeExtra.cend();
540 if (name.compare(mimeName, Qt::CaseInsensitive))
541 qWarning() <<
"Got name" << name <<
"in file" << mimeFile <<
"expected" << mimeName;
543 while (xml.readNextStartElement()) {
544 const auto tag = xml.name();
545 if (tag ==
"comment"_L1) {
546 QString lang = xml.attributes().value(
"xml:lang"_L1).toString();
547 const QString text = xml.readElementText();
548 if (lang.isEmpty()) {
551 extra.localeComments.insert(lang, text);
553 }
else if (tag ==
"glob-deleteall"_L1) {
554 extra.hasGlobDeleteAll =
true;
555 }
else if (tag ==
"glob"_L1) {
556 const QString pattern = xml.attributes().value(
"pattern"_L1).toString();
557 if (mainPattern.isEmpty() && pattern.startsWith(u'*')) {
558 mainPattern = pattern;
560 appendIfNew(extra.globPatterns, pattern);
562 xml.skipCurrentElement();
564 Q_ASSERT(xml.name() ==
"mime-type"_L1);
569 if (!mainPattern.isEmpty() &&
570 (extra.globPatterns.isEmpty() || extra.globPatterns.constFirst() != mainPattern)) {
572 extra.globPatterns.removeAll(mainPattern);
573 extra.globPatterns.prepend(mainPattern);
579 qWarning(
"Cannot load mime type since QXmlStreamReader is not available.");
580 return m_mimetypeExtra.cend();
585QLatin1StringView QMimeBinaryProvider::iconForMime(
CacheFile *cacheFile,
int posListOffset,
588 const int iconsListOffset = cacheFile->getUint32(posListOffset);
589 const int numIcons = cacheFile->getUint32(iconsListOffset);
591 int end = numIcons - 1;
592 while (begin <= end) {
593 const int medium = (begin + end) / 2;
594 const int off = iconsListOffset + 4 + 8 * medium;
595 const int mimeOffset = cacheFile->getUint32(off);
597 const int cmp = qstrcmp(mime, inputMime);
603 const int iconOffset = cacheFile->getUint32(off + 4);
607 return QLatin1StringView();
613 return iconForMime(m_cacheFile.get(), PosIconsListOffset, inputMime);
619 return iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, inputMime);
624#if QT_CONFIG(mimetype_database)
625static QString internalMimeFileName()
627 return QStringLiteral(
"<internal MIME data>");
630QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db, InternalDatabaseEnum)
631 : QMimeProviderBase(db, internalMimeFileName())
633 static_assert(
sizeof(mimetype_database),
"Bundled MIME database is empty");
634 static_assert(
sizeof(mimetype_database) <= MimeTypeDatabaseOriginalSize,
635 "Compressed MIME database is larger than the original size");
636 static_assert(MimeTypeDatabaseOriginalSize <= 16*1024*1024,
637 "Bundled MIME database is too big");
638 const char *data =
reinterpret_cast<
const char *>(mimetype_database);
639 qsizetype size = MimeTypeDatabaseOriginalSize;
641#ifdef MIME_DATABASE_IS_ZSTD
643 std::unique_ptr<
char []> uncompressed(
new char[size]);
644 size = ZSTD_decompress(uncompressed.get(), size, mimetype_database,
sizeof(mimetype_database));
645 Q_ASSERT(!ZSTD_isError(size));
646 data = uncompressed.get();
647#elif defined(MIME_DATABASE_IS_GZIP)
648 std::unique_ptr<
char []> uncompressed(
new char[size]);
650 zs.next_in =
const_cast<Bytef *>(mimetype_database);
651 zs.avail_in =
sizeof(mimetype_database);
652 zs.next_out =
reinterpret_cast<Bytef *>(uncompressed.get());
655 int res = inflateInit2(&zs, MAX_WBITS | 32);
656 Q_ASSERT(res == Z_OK);
657 res = inflate(&zs, Z_FINISH);
658 Q_ASSERT(res == Z_STREAM_END);
659 res = inflateEnd(&zs);
660 Q_ASSERT(res == Z_OK);
662 data = uncompressed.get();
697#if QT_CONFIG(mimetype_database)
698 return m_directory == internalMimeFileName();
706 return m_nameMimeTypeMap.contains(name);
711 auto filterFunc = [
this](
const QString &name) {
return !isMimeTypeGlobsExcluded(name); };
712 m_mimeTypeGlobs.matchingGlobs(fileName, result, filterFunc);
717 for (
const QMimeMagicRuleMatcher &matcher : std::as_const(m_magicMatchers)) {
718 if (matcher.matches(data)) {
719 const int priority = matcher.priority();
720 if (priority > result.accuracy) {
721 result.accuracy = priority;
722 result.candidate = matcher.mimetype();
730 QStringList allFiles;
731 const QString packageDir = m_directory + QStringView(u"/packages");
732 for (
const auto &entry : QDirListing(packageDir, QDirListing::IteratorFlag::FilesOnly
733 | QDirListing::IteratorFlag::ResolveSymlinks))
734 allFiles.emplace_back(packageDir + u'/' + entry.fileName());
736 if (m_allFiles == allFiles)
738 m_allFiles = allFiles;
740 m_nameMimeTypeMap.clear();
744 m_magicMatchers.clear();
748 for (
const QString &file : std::as_const(allFiles))
754 return m_nameMimeTypeMap.value(name).localeComments;
759 return m_nameMimeTypeMap.value(name).hasGlobDeleteAll;
764 return m_nameMimeTypeMap.value(name).globPatterns;
769 return m_nameMimeTypeMap.value(name).iconName;
774 return m_nameMimeTypeMap.value(name).genericIconName;
777void QMimeXMLProvider::load(
const QString &fileName)
779 QString errorMessage;
780 if (!load(fileName, &errorMessage))
781 qWarning(
"QMimeDatabase: Error loading %ls\n%ls", qUtf16Printable(fileName), qUtf16Printable(errorMessage));
784bool QMimeXMLProvider::
load(
const QString &fileName, QString *errorMessage)
786 QFile file(fileName);
787 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
789 *errorMessage =
"Cannot open "_L1 + fileName +
": "_L1 + file.errorString();
794 errorMessage->clear();
797 return parser.parse(&file, fileName, errorMessage);
800#if QT_CONFIG(mimetype_database)
801void QMimeXMLProvider::load(
const char *data, qsizetype len)
804 buffer.setData(QByteArray::fromRawData(data, len));
805 buffer.open(QIODevice::ReadOnly);
806 QString errorMessage;
807 QMimeTypeParser parser(*
this);
808 if (!parser.parse(&buffer, internalMimeFileName(), &errorMessage))
809 qWarning(
"QMimeDatabase: Error loading internal MIME data\n%s", qPrintable(errorMessage));
820 m_nameMimeTypeMap.insert(mt.name, mt);
823void QMimeXMLProvider::
addParents(
const QString &mime, QStringList &result)
825 for (
const QString &parent : m_parents.value(mime)) {
826 if (!result.contains(parent))
827 result.append(parent);
831void QMimeXMLProvider::
addParent(
const QString &child,
const QString &parent)
833 m_parents[child].append(parent);
836void QMimeXMLProvider::
addAliases(
const QString &name, QStringList &result)
839 for (
const auto &[alias, mimeName] : std::as_const(m_aliases).asKeyValueRange()) {
840 if (mimeName == name)
841 appendIfNew(result, alias);
847 return m_aliases.value(name);
850void QMimeXMLProvider::
addAlias(
const QString &alias,
const QString &name)
852 m_aliases.insert(alias, name);
857 if (result.isEmpty()) {
858 for (
auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd();
860 result.append(QMimeType(QMimeTypePrivate(it.value().name)));
863 for (
auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd() ; it != end ; ++it) {
864 const QString newMime = it.key();
865 if (
std::find_if(result.constBegin(), result.constEnd(), [newMime](
const QMimeType &mime) ->
bool {
return mime.name() == newMime; })
866 == result.constEnd())
867 result.append(QMimeType(QMimeTypePrivate(it.value().name)));
874 m_magicMatchers.append(matcher);
void addGlob(const QMimeGlobPattern &glob)
void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override
QString resolveAlias(const QString &name) override
void findByMagic(const QByteArray &data, QMimeMagicResult &result) override
bool knowsMimeType(const QString &name) override
QString icon(const QString &name) override
bool hasGlobDeleteAll(const QString &name) override
void ensureLoaded() override
void addAliases(const QString &name, QStringList &result) override
virtual ~QMimeBinaryProvider()
void addAllMimeTypes(QList< QMimeType > &result) override
void addParents(const QString &mime, QStringList &result) override
QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory)
QStringList globPatterns(const QString &name) override
QString genericIcon(const QString &name) override
bool isInternalDatabase() const override
QMimeTypePrivate::LocaleHash localeComments(const QString &name) override
The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching.
bool isMimeTypeGlobsExcluded(const QString &name) const
QMimeProviderBase * m_overrideProvider
QMimeProviderBase * overrideProvider() const
void setOverrideProvider(QMimeProviderBase *provider)
QMimeDatabasePrivate * m_db
QMimeTypeParser(QMimeXMLProvider &provider)
void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override
void addGlobPattern(const QMimeGlobPattern &glob)
void addAliases(const QString &name, QStringList &result) override
void addAlias(const QString &alias, const QString &name)
QMimeTypePrivate::LocaleHash localeComments(const QString &name) override
QStringList globPatterns(const QString &name) override
bool load(const QString &fileName, QString *errorMessage)
QMimeXMLProvider(QMimeDatabasePrivate *db, const QString &directory)
void ensureLoaded() override
bool knowsMimeType(const QString &name) override
bool isInternalDatabase() const override
QString resolveAlias(const QString &name) override
void addMimeType(const QMimeTypeXMLData &mt)
void findByMagic(const QByteArray &data, QMimeMagicResult &result) override
QString icon(const QString &name) override
void addParents(const QString &mime, QStringList &result) override
void addAllMimeTypes(QList< QMimeType > &result) override
QMimeXMLProvider(QMimeDatabasePrivate *db, InternalDatabaseEnum)
void addMagicMatcher(const QMimeMagicRuleMatcher &matcher)
bool hasGlobDeleteAll(const QString &name) override
void addParent(const QString &child, const QString &parent)
QString genericIcon(const QString &name) override
@ PosGenericIconsListOffset
@ PosReverseSuffixTreeOffset
static void appendIfNew(QStringList &list, const QString &str)
const char * getCharStar(int offset) const
quint16 getUint16(int offset) const
CacheFile(const QString &fileName)
quint32 getUint32(int offset) const