--- /dev/null
+#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