From d0c270a0b5e9a8cc3e86186b44ddd927ac083e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Ker=C3=A4nen?= Date: Tue, 29 Mar 2016 19:26:50 +0300 Subject: [PATCH] libdoomsday|Resources: Added LumpCatalog: package-aware WAD lump indexer Currently implements a simple WAD lump directory latest-lump-applies logic, operating in any specified package order. --- .../libdoomsday/include/doomsday/LumpCatalog | 1 + .../include/doomsday/resource/lumpcatalog.h | 59 ++++++++++ .../include/doomsday/resource/lumpdirectory.h | 19 ++++ .../libdoomsday/src/resource/lumpcatalog.cpp | 105 ++++++++++++++++++ .../src/resource/lumpdirectory.cpp | 20 +++- 5 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 doomsday/apps/libdoomsday/include/doomsday/LumpCatalog create mode 100644 doomsday/apps/libdoomsday/include/doomsday/resource/lumpcatalog.h create mode 100644 doomsday/apps/libdoomsday/src/resource/lumpcatalog.cpp diff --git a/doomsday/apps/libdoomsday/include/doomsday/LumpCatalog b/doomsday/apps/libdoomsday/include/doomsday/LumpCatalog new file mode 100644 index 0000000000..b27a4ace88 --- /dev/null +++ b/doomsday/apps/libdoomsday/include/doomsday/LumpCatalog @@ -0,0 +1 @@ +#include "resource/lumpcatalog.h" diff --git a/doomsday/apps/libdoomsday/include/doomsday/resource/lumpcatalog.h b/doomsday/apps/libdoomsday/include/doomsday/resource/lumpcatalog.h new file mode 100644 index 0000000000..4b64ff599f --- /dev/null +++ b/doomsday/apps/libdoomsday/include/doomsday/resource/lumpcatalog.h @@ -0,0 +1,59 @@ +/** @file lumpcatalog.h Catalog of lumps from multiple bundles. + * + * @authors Copyright (c) 2016 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBDOOMSDAY_LUMPCATALOG_H +#define LIBDOOMSDAY_LUMPCATALOG_H + +#include "../libdoomsday.h" +#include +#include + +namespace res { + +/** + * Catalog of lumps from multiple bundles. + * + * This is a utility for locating and reading lumps from a set of data bundles. + * It does not cache data: caching should either occur in the File objects or + * a LumpBank that is backed by a LumpCatalog. + */ +class LIBDOOMSDAY_PUBLIC LumpCatalog +{ +public: + LumpCatalog(); + + /** + * Sets the list of packages where data lumps are to be read from. Only data bundle + * packages of Wad and Lump types are used. + * + * @param packageIds List of packages. These are specifiesd in "load order", meaning + * later ones override the contents of earlier ones. + * + * @return @c true, if the list of packages is different than the one set previously. + */ + bool setPackages(de::StringList const &packageIds); + + de::Block read(de::String const &lumpName) const; + +private: + DENG2_PRIVATE(d) +}; + +} // namespace res + +#endif // LIBDOOMSDAY_LUMPCATALOG_H diff --git a/doomsday/apps/libdoomsday/include/doomsday/resource/lumpdirectory.h b/doomsday/apps/libdoomsday/include/doomsday/resource/lumpdirectory.h index f769db915c..42c1316c73 100644 --- a/doomsday/apps/libdoomsday/include/doomsday/resource/lumpdirectory.h +++ b/doomsday/apps/libdoomsday/include/doomsday/resource/lumpdirectory.h @@ -43,6 +43,7 @@ class LIBDOOMSDAY_PUBLIC LumpDirectory }; typedef de::dsize Pos; + static de::dsize const InvalidPos; DENG2_ERROR(OffsetError); @@ -72,8 +73,26 @@ class LIBDOOMSDAY_PUBLIC LumpDirectory */ de::duint32 crc32() const; + /** + * Checks if the lump directory has a specific lump. Performance is O(1) (hashed). + * + * @param lumpName Name of a lump. + * + * @return @c true, if the lump is in the directory. + */ bool has(de::Block const &lumpName) const; + /** + * Finds the entry of a lump in the directory. If there are multiple lumps with + * the same name, this returns the last one in the directory. Performance is O(1) + * (hashed). + * + * @param lumpName Name of a lump. + * + * @return Lump entry information. + */ + LumpDirectory::Pos find(de::Block const &lumpName) const; + de::duint32 lumpSize(de::Block const &lumpName) const; private: diff --git a/doomsday/apps/libdoomsday/src/resource/lumpcatalog.cpp b/doomsday/apps/libdoomsday/src/resource/lumpcatalog.cpp new file mode 100644 index 0000000000..d3421d45bc --- /dev/null +++ b/doomsday/apps/libdoomsday/src/resource/lumpcatalog.cpp @@ -0,0 +1,105 @@ +/** @file lumpcatalog.cpp + * + * @authors Copyright (c) 2016 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "doomsday/resource/lumpcatalog.h" +#include "doomsday/resource/lumpdirectory.h" +#include "doomsday/resource/databundle.h" + +#include +#include +#include +#include + +using namespace de; + +namespace res { + +DENG2_PIMPL(LumpCatalog) +{ + using Found = std::pair; + + StringList packageIds; + QList bundles; /// @todo Should observe for deletion. -jk + + Instance(Public *i) : Base(i) {} + + void updateBundles() + { + bundles.clear(); + + for(auto const &pkg : packageIds) + { + // The package must be available as a file. + if(File const *file = App::packageLoader().select(pkg)) + { + auto const *bundle = file->target().maybeAs(); + if(bundle && bundle->lumpDirectory()) + { + bundles << bundle; + } + } + } + } + + Found findLump(String const &name) const + { + Block const lumpName = name.toLatin1(); + Found found { nullptr, LumpDirectory::InvalidPos }; + + // The last bundle is checked first. + for(int i = bundles.size() - 1; i >= 0; --i) + { + auto const pos = bundles.at(i)->lumpDirectory()->find(lumpName); + if(pos != LumpDirectory::InvalidPos) + { + found = Found(bundles.at(i), pos); + break; + } + } + return found; + } +}; + +LumpCatalog::LumpCatalog() + : d(new Instance(this)) +{} + +bool LumpCatalog::setPackages(StringList const &packageIds) +{ + if(packageIds != d->packageIds) + { + d->packageIds = packageIds; + d->updateBundles(); + return true; + } + return false; +} + +Block LumpCatalog::read(String const &lumpName) const +{ + Block data; + Instance::Found found = d->findLump(lumpName); + if(found.first) + { + auto const &entry = found.first->lumpDirectory()->entry(found.second); + data.copyFrom(*found.first, entry.offset, entry.size); + } + return data; +} + +} // namespace res diff --git a/doomsday/apps/libdoomsday/src/resource/lumpdirectory.cpp b/doomsday/apps/libdoomsday/src/resource/lumpdirectory.cpp index b713839107..e0f892604b 100644 --- a/doomsday/apps/libdoomsday/src/resource/lumpdirectory.cpp +++ b/doomsday/apps/libdoomsday/src/resource/lumpdirectory.cpp @@ -26,12 +26,14 @@ using namespace de; namespace res { +dsize const LumpDirectory::InvalidPos = dsize(-1); + DENG2_PIMPL_NOREF(LumpDirectory) { Type type = Invalid; duint32 crc = 0; QList entries; - QHash index; // points to entries + QHash index; // points to entries void read(IByteArray const &source) { @@ -67,9 +69,9 @@ DENG2_PIMPL_NOREF(LumpDirectory) // Make an index of all the lumps. index.clear(); - for(Entry &entry : entries) + for(int i = 0; i < entries.size(); ++i) { - index.insert(entry.name, &entry); + index.insert(entries.at(i).name, i); } } }; @@ -116,7 +118,7 @@ duint32 LumpDirectory::lumpSize(Block const &lumpName) const auto found = d->index.constFind(lumpName); if(found != d->index.constEnd()) { - return found.value()->size; + return d->entries.at(found.value()).size; } return 0; } @@ -126,4 +128,14 @@ bool LumpDirectory::has(Block const &lumpName) const return d->index.contains(lumpName); } +LumpDirectory::Pos LumpDirectory::find(Block const &lumpName) const +{ + auto found = d->index.constFind(lumpName); + if(found != d->index.constEnd()) + { + return found.value(); + } + return InvalidPos; +} + } // namespace res