From 06f5a06f679dec69d5d9a2502a2c498d8469e3cb Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Fri, 1 Nov 2013 08:24:14 +0100 Subject: [PATCH] Filesystem: Optimize RGSSAD file enumeration Finally got around to nuking that ugly pile of shit that was previously there for PhysFS file enumeration because filepath cache generation with unencrypted game files + archive + RTP has started taking around 6 seconds. Thank $DEITY. --- src/filesystem.cpp | 77 +++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/src/filesystem.cpp b/src/filesystem.cpp index d4266ca..6f3ea5d 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -27,6 +27,7 @@ #include "physfs.h" #include +#include #include #include "stdio.h" @@ -67,8 +68,14 @@ typedef QList QByteList; struct RGSS_archiveData { PHYSFS_Io *archiveIo; + + /* Maps: file path + * to: entry data */ QHash entryHash; - QHash dirHash; + + /* Maps: directory path, + * to: list of contained entries */ + QHash > dirHash; }; static bool @@ -344,6 +351,9 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite) uint32_t magic = RGSS_MAGIC; + /* Top level entry list */ + QSet &topLevel = data->dirHash["."]; + while (true) { /* Read filename length, @@ -377,13 +387,33 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite) data->entryHash.insert(nameBuf, entry); - /* Test for new folder */ + /* Check for top level entries */ + for (i = 0; i < nameLen; ++i) + { + bool slash = nameBuf[i] == '/'; + if (!slash && i+1 < nameLen) + continue; + + if (slash) + nameBuf[i] = '\0'; + + topLevel.insert(nameBuf); + + if (slash) + nameBuf[i] = '/'; + } + + /* Check for more entries */ for (i = nameLen; i > 0; i--) if (nameBuf[i] == '/') { nameBuf[i] = '\0'; - if (!data->dirHash.contains(nameBuf)) - data->dirHash.insert(nameBuf, true); + + const char *dir = nameBuf; + const char *entry = &nameBuf[i+1]; + + QSet &entryList = data->dirHash[dir]; + entryList.insert(entry); } io->seek(io, entry.offset + entry.size); @@ -399,34 +429,16 @@ RGSS_enumerateFiles(void *opaque, const char *dirname, { RGSS_archiveData *data = static_cast(opaque); - char dirBuf[512]; - QByteList keys = data->entryHash.keys(); - keys += data->dirHash.keys(); + QByteArray _dirname(dirname); - Q_FOREACH (const QByteArray &filename, keys) - { - /* Get the filename directory part */ - strncpy(dirBuf, filename.constData(), sizeof(dirBuf)); + if (!data->dirHash.contains(_dirname)) + return; - /* Extract path and basename */ - const char *dirpath = "."; - char *basename = dirBuf; + const QSet &entries = data->dirHash.value(_dirname); - for (int i = filename.size(); i >= 0; i--) - if (dirBuf[i] == '/') - { - dirBuf[i] = '\0'; - dirpath = dirBuf; - - basename = &dirBuf[i+1]; - - break; - } - - /* Compare to provided dirname */ - if (strcmp(dirpath, dirname) == 0) - cb(callbackdata, origdir, basename); - } + QSet::const_iterator iter; + for (iter = entries.begin(); iter != entries.end(); ++iter) + cb(callbackdata, origdir, iter->constData()); } static PHYSFS_Io* @@ -716,12 +728,13 @@ static void cacheEnumCB(void *d, const char *origdir, else strncpy(buf, fname, sizeof(buf)); - QByteArray mixedCase = buf; + QByteArray mixedCase(buf); - QByteArray lowerCase = mixedCase; - for (char *p = lowerCase.data(); *p; ++p) + for (char *p = buf; *p; ++p) *p = tolower(*p); + QByteArray lowerCase(buf); + p->pathCache.insert(lowerCase, mixedCase); PHYSFS_enumerateFilesCallback(mixedCase.constData(), cacheEnumCB, p);