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.
This commit is contained in:
Jonas Kulla 2013-11-01 08:24:14 +01:00
parent 28ff5cd33c
commit 06f5a06f67
1 changed files with 45 additions and 32 deletions

View File

@ -27,6 +27,7 @@
#include "physfs.h" #include "physfs.h"
#include <QHash> #include <QHash>
#include <QSet>
#include <QByteArray> #include <QByteArray>
#include "stdio.h" #include "stdio.h"
@ -67,8 +68,14 @@ typedef QList<QByteArray> QByteList;
struct RGSS_archiveData struct RGSS_archiveData
{ {
PHYSFS_Io *archiveIo; PHYSFS_Io *archiveIo;
/* Maps: file path
* to: entry data */
QHash<QByteArray, RGSS_entryData> entryHash; QHash<QByteArray, RGSS_entryData> entryHash;
QHash<QByteArray, bool> dirHash;
/* Maps: directory path,
* to: list of contained entries */
QHash<QByteArray, QSet<QByteArray> > dirHash;
}; };
static bool static bool
@ -344,6 +351,9 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
uint32_t magic = RGSS_MAGIC; uint32_t magic = RGSS_MAGIC;
/* Top level entry list */
QSet<QByteArray> &topLevel = data->dirHash["."];
while (true) while (true)
{ {
/* Read filename length, /* Read filename length,
@ -377,13 +387,33 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
data->entryHash.insert(nameBuf, entry); 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--) for (i = nameLen; i > 0; i--)
if (nameBuf[i] == '/') if (nameBuf[i] == '/')
{ {
nameBuf[i] = '\0'; nameBuf[i] = '\0';
if (!data->dirHash.contains(nameBuf))
data->dirHash.insert(nameBuf, true); const char *dir = nameBuf;
const char *entry = &nameBuf[i+1];
QSet<QByteArray> &entryList = data->dirHash[dir];
entryList.insert(entry);
} }
io->seek(io, entry.offset + entry.size); io->seek(io, entry.offset + entry.size);
@ -399,34 +429,16 @@ RGSS_enumerateFiles(void *opaque, const char *dirname,
{ {
RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque); RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque);
char dirBuf[512]; QByteArray _dirname(dirname);
QByteList keys = data->entryHash.keys();
keys += data->dirHash.keys();
Q_FOREACH (const QByteArray &filename, keys) if (!data->dirHash.contains(_dirname))
{ return;
/* Get the filename directory part */
strncpy(dirBuf, filename.constData(), sizeof(dirBuf));
/* Extract path and basename */ const QSet<QByteArray> &entries = data->dirHash.value(_dirname);
const char *dirpath = ".";
char *basename = dirBuf;
for (int i = filename.size(); i >= 0; i--) QSet<QByteArray>::const_iterator iter;
if (dirBuf[i] == '/') for (iter = entries.begin(); iter != entries.end(); ++iter)
{ cb(callbackdata, origdir, iter->constData());
dirBuf[i] = '\0';
dirpath = dirBuf;
basename = &dirBuf[i+1];
break;
}
/* Compare to provided dirname */
if (strcmp(dirpath, dirname) == 0)
cb(callbackdata, origdir, basename);
}
} }
static PHYSFS_Io* static PHYSFS_Io*
@ -716,12 +728,13 @@ static void cacheEnumCB(void *d, const char *origdir,
else else
strncpy(buf, fname, sizeof(buf)); strncpy(buf, fname, sizeof(buf));
QByteArray mixedCase = buf; QByteArray mixedCase(buf);
QByteArray lowerCase = mixedCase; for (char *p = buf; *p; ++p)
for (char *p = lowerCase.data(); *p; ++p)
*p = tolower(*p); *p = tolower(*p);
QByteArray lowerCase(buf);
p->pathCache.insert(lowerCase, mixedCase); p->pathCache.insert(lowerCase, mixedCase);
PHYSFS_enumerateFilesCallback(mixedCase.constData(), cacheEnumCB, p); PHYSFS_enumerateFilesCallback(mixedCase.constData(), cacheEnumCB, p);