]> git.tdb.fi Git - libs/datafile.git/blobdiff - source/packcollection.h
Add an intelligent packed collection class
[libs/datafile.git] / source / packcollection.h
diff --git a/source/packcollection.h b/source/packcollection.h
new file mode 100644 (file)
index 0000000..946271a
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef MSP_DATAFILE_PACKCOLLECTION_H_
+#define MSP_DATAFILE_PACKCOLLECTION_H_
+
+#include "collection.h"
+#include "objectloader.h"
+
+namespace Msp {
+namespace DataFile {
+
+/**
+A collection class that loads data from pack files.  As opposed to plain
+collection files, pack files are composed from a number of logical files.  They
+also contain a directory with a list of objects contained in the pack and which
+logical files they are in.  This allows the pack to be loaded in a piecewise
+manner instead of all at once.
+
+It's possible for a pack file to contain plain collection files as well.  When
+an object from such a file is requested, the entire sub-collection it is stored
+in is loaded.
+*/
+class PackCollection: public Collection
+{
+private:
+       class File;
+       struct Object;
+
+       typedef std::map<std::string, const Object *> ObjectMap;
+
+       class Pack
+       {
+       public:
+               class Loader: public ObjectLoader<Pack>
+               {
+               public:
+                       Loader(Pack &);
+               private:
+                       void file(const std::string &);
+               };
+
+       private:
+               std::string filename;
+               unsigned base_offset;
+               std::list<File> files;
+
+       public:
+               Pack(const std::string &);
+
+               const std::string &get_filename() const { return filename; }
+               unsigned get_base_offset() const { return base_offset; }
+
+               void collect_objects(ObjectMap &) const;
+       };
+
+       class File
+       {
+       public:
+               class Loader: public ObjectLoader<File>
+               {
+               public:
+                       Loader(File &);
+               private:
+                       virtual void finish();
+                       void object(const std::string &, const std::string &);
+               };
+
+       private:
+               const Pack &pack;
+               std::string filename;
+               unsigned offset;
+               unsigned length;
+               bool collection;
+               std::list<Object> objects;
+               bool loaded;
+
+       public:
+               File(const Pack &, const std::string &);
+
+               RefPtr<IO::Base> open() const;
+               const std::string &get_filename() const { return filename; }
+               std::string get_full_name() const;
+               bool is_collection() const { return collection; }
+
+               void set_loaded();
+               bool is_loaded() const { return loaded; }
+
+               void collect_objects(ObjectMap &) const;
+       };
+
+       class Object
+       {
+       private:
+               File &file;
+               std::string name;
+               std::string keyword;
+
+       public:
+               Object(File &, const std::string &, const std::string &);
+
+               File &get_file() const { return file; }
+               const std::string &get_name() const { return name; }
+               const std::string &get_keyword() const { return keyword; }
+       };
+
+       std::list<Pack> packs;
+       ObjectMap objects;
+
+public:
+       /** Adds a pack file to the collection.  The directory is read immediately,
+       and packed objects are loaded as they are needed. */
+       void add_pack_file(const std::string &);
+
+protected:
+       template<typename T>
+       CollectionItemType<T> &add_type()
+       {
+               return Collection::add_type<T>().creator(&PackCollection::create<T>);
+       }
+
+private:
+       template<typename T>
+       T *create(const std::string &name)
+       {
+               ObjectMap::iterator i = objects.find(name);
+               if(i==objects.end())
+                       return 0;
+
+               File &file = i->second->get_file();
+               if(file.is_loaded())
+                       return 0;
+               file.set_loaded();
+
+               RefPtr<IO::Base> in = file.open();
+               Parser parser(*in, file.get_full_name());
+               if(file.is_collection())
+               {
+                       Loader ldr(*this);
+                       ldr.load(parser);
+                       return 0;
+               }
+               else
+               {
+                       RefPtr<T> item = new T;
+                       ItemLoader<T> ldr(*item, *this);
+                       ldr.load(parser);
+                       return item.release();
+               }
+       }
+};
+
+} // namespace DataFile
+} // namespace Msp
+
+#endif