From: Mikko Rasa Date: Thu, 15 Nov 2012 19:04:09 +0000 (+0200) Subject: Redesign automatic object loading X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=b1bc256;p=libs%2Fdatafile.git Redesign automatic object loading Replace the Collection subclasses with a new concept of CollectionSources. This permits more than one way of loading objects. --- diff --git a/source/collection.cpp b/source/collection.cpp index 5ac12ab..bf1c5ed 100644 --- a/source/collection.cpp +++ b/source/collection.cpp @@ -11,26 +11,9 @@ Collection::~Collection() delete *i; } -void Collection::add_future(const string &name) +void Collection::add_source(CollectionSource &s) { - for(TypeList::const_iterator i=types.begin(); i!=types.end(); ++i) - if((*i)->match_name(name)) - { - insert_unique(items, name, (*i)->create_future()); - return; - } -} - -void Collection::add_future_with_keyword(const string &name, const string &keyword) -{ - for(TypeList::const_iterator i=types.begin(); i!=types.end(); ++i) - if((*i)->get_keyword()==keyword) - { - insert_unique(items, name, (*i)->create_future()); - return; - } - - throw runtime_error("Collection::add_future_with_keyword"); + sources.push_back(&s); } diff --git a/source/collection.h b/source/collection.h index f79e567..ec7cde3 100644 --- a/source/collection.h +++ b/source/collection.h @@ -4,8 +4,12 @@ #include #include #include +#include "collectionsource.h" #include "loader.h" +/* XXX This file is a big mess with too many things in it. However, the +dependencies between those things make it difficult to split up. */ + namespace Msp { namespace DataFile { @@ -39,12 +43,8 @@ While this class can be instantiated by itself and used for storing objects, loading requires that a subclass defines the supported types. See the add_type method for details. -Collections also support a notion of "future objects". These are objects which -are known to be possible to load, but loading them is deferred to the first -time they are requested. - -Other classes are available to provide refined ways of loading objects from -files. See DirectoryCollection and PackCollection. +Collections can have sources for loading objects on demand. Automatic loading +only works on a non-const Collection. See class CollectionSource for details. */ class Collection { @@ -82,9 +82,11 @@ protected: private: typedef std::map ItemMap; typedef std::list TypeList; + typedef std::list SourceList; TypeList types; ItemMap items; + SourceList sources; Collection(const Collection &); Collection &operator=(const Collection &); @@ -100,62 +102,19 @@ public: if(!item) throw std::invalid_argument("Collection::add(item)"); - typedef RefPtr::Type> RPNCT; - - ItemMap::iterator i = items.find(name); - if(i!=items.end()) - { - if(i->second.check_type()) - { - // Replace a future object placeholder - RPNCT &ptr = i->second.value(); - if(!ptr) - { - ptr = item; - return; - } - } - - throw key_error(typeid(ItemMap)); - } - - items.insert(ItemMap::value_type(name, RPNCT(item))); - } - -protected: - /** Adds the name of a future object to the collection. The object itself - will be loaded on first access. The calling subclass should be prepared to - create the object on request. */ - template - void add_future(const std::string &name) - { - RefPtr::Type> ptr(0); - insert_unique(items, name, ptr); + insert_unique(items, name, RefPtr::Type>(item)); } - /** Adds the name of a future object, guessing its type. If a type matching - the name can't be found, nothing is done. */ - void add_future(const std::string &name); - - /** Adds the name of a future object, using a keyword to determine its type. - The keyword must be known to the collection. */ - void add_future_with_keyword(const std::string &name, const std::string &); - -public: /// Gets a typed object from the collection. template T &get(const std::string &name) const { - typedef typename RemoveConst::Type NCT; - - T *ptr = get_item(items, name).value >().get(); - if(!ptr) - throw key_error(typeid(ItemMap)); - return *ptr; + return *get_item(items, name).value::Type> >(); } - /** Gets a typed object from the collection. If the name is not found in - and a creator for the item type is defined, it is invoked. */ + /** Gets a typed object from the collection. If the name is not found, + automatic creation with the type's creator function (if defined) or from + sources (if present) is attempted. */ template T &get(const std::string &); @@ -168,22 +127,27 @@ private: for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i) if(i->second.check_type()) { - T *ptr = i->second.value().get(); - if(ptr) + if(objects) + objects->push_back(i->second.value().get()); + if(names) + names->push_back(i->first); + } + + if(future_names) + if(CollectionItemTypeBase *type = get_type()) + { + for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i) { - if(objects) - objects->push_back(ptr); - if(names) - names->push_back(i->first); + std::list available_names = (*i)->get_names(*type); + for(std::list::iterator j=available_names.begin(); j!=available_names.end(); ++j) + if(!items.count(*j)) + future_names->push_back(*j); } - else if(future_names) - future_names->push_back(i->first); } } public: - /** Returns a list of the names of loaded objects of one type in the - collection. */ + /// Returns a list of the names of objects of one type in the collection. template std::list get_names() const { @@ -192,8 +156,8 @@ public: return result; } - /** Returns a list of the names of objects of one type in the collection, - including any future objects. */ + /** Returns a list of the names of objects of one type in the collection or + available from sources. */ template std::list get_names() { @@ -202,7 +166,7 @@ public: return result; } - /// Returns a list of loaded objects of one type in the collection. + /// Returns a list of objects of one type in the collection. template std::list get_list() const { @@ -211,8 +175,8 @@ public: return result; } - /** Returns a list of objects of one type in the collection. Any future - objects of that type are loaded and returned in the list. */ + /** Returns a list of objects of one type, loading them from sources if + necessary. */ template std::list get_list() { @@ -230,24 +194,31 @@ private: { ItemMap::const_iterator i = items.find(name); if(i==items.end()) - return false; + { + if(CollectionItemTypeBase *type = get_type()) + { + for(SourceList::const_iterator j=sources.begin(); j!=sources.end(); ++j) + if((*j)->is_loadable(*type, name)) + return 2; + } + return 0; + } typedef RefPtr::Type> RPNCT; if(!i->second.check_type()) - return false; + return 0; - T *ptr = i->second.value().get(); - return ptr ? 1 : 2; + return 1; } public: - /// Checks whether a typed object exists and is loaded in the collection. + /// Checks whether a typed object exists in the collection. template bool contains(const std::string &name) const { return get_status(name)==1; } - /** Checks whether a typed object exists in the collection, as either a - loaded or future object. */ + /** Checks whether a typed object exists in the collection or is loadable + from a source. */ template bool contains(const std::string &name) { return get_status(name)>0; } @@ -272,6 +243,12 @@ protected: can be used to define how objects of that type can be loaded. */ template CollectionItemType &add_type(); + + /** Returns the descriptor for a type, or null if one isn't defined. */ + template + CollectionItemTypeBase *get_type() const; + + void add_source(CollectionSource &); }; template @@ -323,7 +300,7 @@ public: virtual void add_to_loader(Collection::Loader &) const = 0; virtual bool can_create() const = 0; virtual void create_item(Collection &, const std::string &) const = 0; - virtual Variant create_future() const = 0; + virtual void load_item(Collection &, Parser &, const std::string &) const = 0; template bool check_type() const @@ -373,7 +350,6 @@ private: virtual ~StoreBase() { } virtual void store(Collection &, const std::string &, T *) = 0; - virtual Variant create_future() const = 0; virtual void add_to_loader(Collection::Loader &, const std::string &) = 0; }; @@ -385,9 +361,6 @@ private: virtual void store(Collection &coll, const std::string &name, T *obj) { coll.add(name, static_cast(obj)); } - virtual Variant create_future() const - { return RefPtr(0); } - virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd) { loader.add(kwd, &Collection::Loader::item); } }; @@ -466,32 +439,42 @@ public: store->store(coll, name, obj); } - virtual Variant create_future() const - { return store->create_future(); } + virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const + { + RefPtr obj = new T; + Collection::ItemLoader ldr(*obj, coll); + ldr.load(parser); + store->store(coll, name, obj.get()); + obj.release(); + } }; template T &Collection::get(const std::string &name) { - typedef typename RemoveConst::Type NCT; + typedef RefPtr::Type> RPNCT; ItemMap::iterator i = items.find(name); if(i!=items.end()) + return *i->second.value(); + + if(CollectionItemTypeBase *type = get_type()) { - NCT *ptr = i->second.value >().get(); - if(ptr) - return *ptr; + bool loaded = false; + if(type->can_create()) + { + type->create_item(*this, name); + loaded = items.count(name); + } + for(SourceList::iterator j=sources.begin(); (!loaded && j!=sources.end()); ++j) + { + (*j)->load(*this, *type, name); + loaded = items.count(name); + } } - for(TypeList::iterator j=types.begin(); j!=types.end(); ++j) - if((*j)->can_create() && (*j)->check_type()) - (*j)->create_item(*this, name); - - NCT *ptr = get_item(items, name).value >().get(); - if(!ptr) - throw key_error(typeid(ItemMap)); - return *ptr; + return *get_item(items, name).value(); } template @@ -502,6 +485,15 @@ CollectionItemType &Collection::add_type() return *type; } +template +CollectionItemTypeBase *Collection::get_type() const +{ + for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j) + if((*j)->check_type::Type>()) + return *j; + return 0; +} + } // namespace DataFile } // namespace Msp diff --git a/source/collectionsource.h b/source/collectionsource.h new file mode 100644 index 0000000..61bb115 --- /dev/null +++ b/source/collectionsource.h @@ -0,0 +1,39 @@ +#ifndef COLLECTIONSOURCE_H_ +#define COLLECTIONSOURCE_H_ + +#include +#include + +namespace Msp { +namespace DataFile { + +class Collection; +class CollectionItemTypeBase; + +/** +Provides automatically loadable objects for collections. This is a base class; +see DirectorySource and PackSource for concrete classes. +*/ +class CollectionSource +{ +public: + typedef std::list NameList; + +protected: + CollectionSource() { } + +public: + /** Determines */ + virtual bool is_loadable(const CollectionItemTypeBase &type, const std::string &name) const = 0; + + /** Returns the names of loadable objects of a specific type. Implementors + should use the keyword and/or suffixes of the type to */ + virtual NameList get_names(const CollectionItemTypeBase &type) const = 0; + + virtual void load(Collection &, const CollectionItemTypeBase &, const std::string &) const = 0; +}; + +} // namespace DataFile +} // namespace Msp + +#endif diff --git a/source/directorycollection.cpp b/source/directorycollection.cpp deleted file mode 100644 index 4413ca5..0000000 --- a/source/directorycollection.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include "directorycollection.h" - -using namespace std; - -namespace Msp { -namespace DataFile { - -DirectoryCollection::DirectoryCollection() -{ - set_directory("."); -} - -void DirectoryCollection::set_directory(const FS::Path &d) -{ - dirs.clear(); - add_directory(d); -} - -void DirectoryCollection::add_directory(const FS::Path &d) -{ - dirs.push_back(d); -} - -void DirectoryCollection::load_names() -{ - for(list::const_iterator i=dirs.begin(); i!=dirs.end(); ++i) - { - list names = FS::list_files(*i); - for(list::const_iterator j=names.begin(); j!=names.end(); ++j) - add_future(*j); - } -} - -bool DirectoryCollection::lookup_file(const string &name, FS::Path &result) const -{ - for(list::const_iterator i=dirs.begin(); i!=dirs.end(); ++i) - { - FS::Path file_path = *i/name; - if(FS::exists(file_path)) - { - result = file_path; - return true; - } - } - - return false; -} - -} // namespace DataFile -} // namespace Msp diff --git a/source/directorycollection.h b/source/directorycollection.h deleted file mode 100644 index a796054..0000000 --- a/source/directorycollection.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef MSP_DATAFILE_DIRECTORYCOLLECTION_H_ -#define MSP_DATAFILE_DIRECTORYCOLLECTION_H_ - -#include -#include "collection.h" - -namespace Msp { -namespace DataFile { - -/** -A Collection that can automatically load items from files in a directory. -*/ -class DirectoryCollection: public Collection -{ -private: - std::list dirs; - -public: - DirectoryCollection(); - -protected: - void set_directory(const FS::Path &); - void add_directory(const FS::Path &); - - /** Examines the names of files in the designated directories and adds any - applicable ones as future objects. */ - void load_names(); - - template - CollectionItemType &add_type() - { - return Collection::add_type().creator(&DirectoryCollection::create); - } - -private: - template - T *create(const std::string &name) - { - FS::Path file; - if(lookup_file(name, file)) - { - RefPtr item = new T; - ItemLoader ldr(*item, *this); - IO::BufferedFile in(file.str()); - Parser parser(in, file.str()); - ldr.load(parser); - return item.release(); - } - else - return 0; - } - -protected: - bool lookup_file(const std::string &, FS::Path &) const; -}; - -} // namespace DataFile -} // namespace Msp - -#endif diff --git a/source/directorysource.cpp b/source/directorysource.cpp new file mode 100644 index 0000000..4ee3b80 --- /dev/null +++ b/source/directorysource.cpp @@ -0,0 +1,73 @@ +#include +#include +#include "collection.h" +#include "directorysource.h" + +using namespace std; + +namespace Msp { +namespace DataFile { + +DirectorySource::DirectorySource() +{ + set_directory("."); +} + +void DirectorySource::set_directory(const FS::Path &d) +{ + dirs.clear(); + add_directory(d); +} + +void DirectorySource::add_directory(const FS::Path &d) +{ + dirs.push_back(d); +} + +bool DirectorySource::is_loadable(const CollectionItemTypeBase &, const string &name) const +{ + FS::Path path; + return lookup_file(name, path); +} + +CollectionSource::NameList DirectorySource::get_names(const CollectionItemTypeBase &type) const +{ + NameList names; + for(list::const_iterator i=dirs.begin(); i!=dirs.end(); ++i) + { + list files = FS::list_files(*i); + for(list::const_iterator j=files.begin(); j!=files.end(); ++j) + if(type.match_name(*j)) + names.push_back(*j); + } + return names; +} + +void DirectorySource::load(Collection &coll, const CollectionItemTypeBase &type, const string &name) const +{ + FS::Path file; + if(lookup_file(name, file)) + { + IO::BufferedFile in(file.str()); + Parser parser(in, file.str()); + type.load_item(coll, parser, name); + } +} + +bool DirectorySource::lookup_file(const string &name, FS::Path &result) const +{ + for(list::const_iterator i=dirs.begin(); i!=dirs.end(); ++i) + { + FS::Path file_path = *i/name; + if(FS::exists(file_path)) + { + result = file_path; + return true; + } + } + + return false; +} + +} // namespace DataFile +} // namespace Msp diff --git a/source/directorysource.h b/source/directorysource.h new file mode 100644 index 0000000..cd82ece --- /dev/null +++ b/source/directorysource.h @@ -0,0 +1,34 @@ +#ifndef MSP_DATAFILE_DIRECTORYSOURCE_H_ +#define MSP_DATAFILE_DIRECTORYSOURCE_H_ + +#include +#include "collectionsource.h" + +namespace Msp { +namespace DataFile { + +/** +A source that load items from files in a directory. +*/ +class DirectorySource: public CollectionSource +{ +private: + std::list dirs; + +public: + DirectorySource(); + + void set_directory(const FS::Path &); + void add_directory(const FS::Path &); + + virtual bool is_loadable(const CollectionItemTypeBase &, const std::string &) const; + virtual NameList get_names(const CollectionItemTypeBase &) const; + virtual void load(Collection &, const CollectionItemTypeBase &, const std::string &) const; + + bool lookup_file(const std::string &, FS::Path &) const; +}; + +} // namespace DataFile +} // namespace Msp + +#endif diff --git a/source/packcollection.cpp b/source/packcollection.cpp deleted file mode 100644 index 6d348db..0000000 --- a/source/packcollection.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include "packcollection.h" - -using namespace std; - -namespace Msp { -namespace DataFile { - -void PackCollection::add_pack_file(const string &fn) -{ - packs.push_back(Pack(fn)); - Pack &pack = packs.back(); - load(pack, fn); - - ObjectMap pack_objs; - pack.collect_objects(pack_objs); - for(ObjectMap::const_iterator i=pack_objs.begin(); i!=pack_objs.end(); ++i) - { - if(i->second->get_keyword().empty()) - add_future(i->first); - else - add_future_with_keyword(i->first, i->second->get_keyword()); - } - - objects.insert(pack_objs.begin(), pack_objs.end()); -} - - -PackCollection::Pack::Pack(const string &fn): - filename(fn), - base_offset(0) -{ } - -void PackCollection::Pack::collect_objects(ObjectMap &objs) const -{ - for(list::const_iterator i=files.begin(); i!=files.end(); ++i) - i->collect_objects(objs); -} - - -PackCollection::File::File(const Pack &p, const string &fn): - pack(p), - filename(fn), - offset(0), - length(0), - collection(false), - loaded(false) -{ } - -RefPtr PackCollection::File::open() const -{ - RefPtr io_file = new IO::BufferedFile(pack.get_filename()); - io_file->seek(pack.get_base_offset()+offset, IO::S_BEG); - return io_file; -} - -string PackCollection::File::get_full_name() const -{ - return format("%s/%s", pack.get_filename(), filename); -} - -void PackCollection::File::set_loaded() -{ - loaded = true; -} - -void PackCollection::File::collect_objects(ObjectMap &objs) const -{ - for(list::const_iterator i=objects.begin(); i!=objects.end(); ++i) - objs[i->get_name()] = &*i; -} - - -PackCollection::Object::Object(File &f, const string &n, const string &k): - file(f), - name(n), - keyword(k) -{ } - - -PackCollection::Pack::Loader::Loader(Pack &p): - ObjectLoader(p) -{ - add("file", &Loader::file); - add("base_offset", &Pack::base_offset); -} - -void PackCollection::Pack::Loader::file(const string &fn) -{ - obj.files.push_back(File(obj, fn)); - load_sub(obj.files.back()); -} - - -PackCollection::File::Loader::Loader(File &f): - ObjectLoader(f) -{ - add("object", &Loader::object); - add("slice", &File::offset, &File::length); -} - -void PackCollection::File::Loader::finish() -{ - if(!obj.collection) - { - PackCollection::Object ob(obj, obj.filename, string()); - obj.objects.push_back(ob); - } -} - -void PackCollection::File::Loader::object(const string &name, const string &kwd) -{ - obj.objects.push_back(PackCollection::Object(obj, name, kwd)); - obj.collection = true; -} - -} // namespace DataFile -} // namespace Msp diff --git a/source/packcollection.h b/source/packcollection.h deleted file mode 100644 index 946271a..0000000 --- a/source/packcollection.h +++ /dev/null @@ -1,153 +0,0 @@ -#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 ObjectMap; - - class Pack - { - public: - class Loader: public ObjectLoader - { - public: - Loader(Pack &); - private: - void file(const std::string &); - }; - - private: - std::string filename; - unsigned base_offset; - std::list 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 - { - 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 objects; - bool loaded; - - public: - File(const Pack &, const std::string &); - - RefPtr 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 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 - CollectionItemType &add_type() - { - return Collection::add_type().creator(&PackCollection::create); - } - -private: - template - 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 in = file.open(); - Parser parser(*in, file.get_full_name()); - if(file.is_collection()) - { - Loader ldr(*this); - ldr.load(parser); - return 0; - } - else - { - RefPtr item = new T; - ItemLoader ldr(*item, *this); - ldr.load(parser); - return item.release(); - } - } -}; - -} // namespace DataFile -} // namespace Msp - -#endif diff --git a/source/packsource.cpp b/source/packsource.cpp new file mode 100644 index 0000000..c06b270 --- /dev/null +++ b/source/packsource.cpp @@ -0,0 +1,163 @@ +#include +#include "collection.h" +#include "packsource.h" + +using namespace std; + +namespace Msp { +namespace DataFile { + +void PackSource::add_pack_file(const string &fn) +{ + packs.push_back(Pack(fn)); + Pack &pack = packs.back(); + DataFile::load(pack, fn); + + pack.collect_objects(objects); +} + +bool PackSource::is_loadable(const CollectionItemTypeBase &type, const string &name) const +{ + ObjectMap::const_iterator i = objects.find(name); + if(i==objects.end()) + return false; + + // If the object has a keyword, it must match that of the type + if(!i->second->get_keyword().empty() && i->second->get_keyword()!=type.get_keyword()) + return false; + + return true; +} + +CollectionSource::NameList PackSource::get_names(const CollectionItemTypeBase &type) const +{ + NameList names; + for(ObjectMap::const_iterator i=objects.begin(); i!=objects.end(); ++i) + { + if(!i->second->get_keyword().empty()) + { + if(i->second->get_keyword()!=type.get_keyword()) + continue; + } + else if(!type.match_name(i->first)) + continue; + + names.push_back(i->first); + } + + return names; +} + +void PackSource::load(Collection &coll, const CollectionItemTypeBase &type, const string &name) const +{ + ObjectMap::const_iterator i = objects.find(name); + if(i==objects.end()) + return; + + File &file = i->second->get_file(); + if(file.is_loaded()) + return; + file.set_loaded(); + + RefPtr in = file.open(); + Parser parser(*in, file.get_full_name()); + if(file.is_collection()) + { + Collection::Loader ldr(coll); + ldr.load(parser); + } + else + type.load_item(coll, parser, name); +} + + +PackSource::Pack::Pack(const string &fn): + filename(fn), + base_offset(0) +{ } + +void PackSource::Pack::collect_objects(ObjectMap &objs) const +{ + for(list::const_iterator i=files.begin(); i!=files.end(); ++i) + i->collect_objects(objs); +} + + +PackSource::File::File(const Pack &p, const string &fn): + pack(p), + filename(fn), + offset(0), + length(0), + collection(false), + loaded(false) +{ } + +RefPtr PackSource::File::open() const +{ + RefPtr io_file = new IO::BufferedFile(pack.get_filename()); + io_file->seek(pack.get_base_offset()+offset, IO::S_BEG); + return io_file; +} + +string PackSource::File::get_full_name() const +{ + return format("%s/%s", pack.get_filename(), filename); +} + +void PackSource::File::set_loaded() +{ + loaded = true; +} + +void PackSource::File::collect_objects(ObjectMap &objs) const +{ + for(list::const_iterator i=objects.begin(); i!=objects.end(); ++i) + objs[i->get_name()] = &*i; +} + + +PackSource::Object::Object(File &f, const string &n, const string &k): + file(f), + name(n), + keyword(k) +{ } + + +PackSource::Pack::Loader::Loader(Pack &p): + ObjectLoader(p) +{ + add("file", &Loader::file); + add("base_offset", &Pack::base_offset); +} + +void PackSource::Pack::Loader::file(const string &fn) +{ + obj.files.push_back(File(obj, fn)); + load_sub(obj.files.back()); +} + + +PackSource::File::Loader::Loader(File &f): + ObjectLoader(f) +{ + add("object", &Loader::object); + add("slice", &File::offset, &File::length); +} + +void PackSource::File::Loader::finish() +{ + if(!obj.collection) + { + PackSource::Object ob(obj, obj.filename, string()); + obj.objects.push_back(ob); + } +} + +void PackSource::File::Loader::object(const string &name, const string &kwd) +{ + obj.objects.push_back(PackSource::Object(obj, name, kwd)); + obj.collection = true; +} + +} // namespace DataFile +} // namespace Msp diff --git a/source/packsource.h b/source/packsource.h new file mode 100644 index 0000000..e2c84a0 --- /dev/null +++ b/source/packsource.h @@ -0,0 +1,121 @@ +#ifndef MSP_DATAFILE_PACKSOURCE_H_ +#define MSP_DATAFILE_PACKSOURCE_H_ + +#include +#include "collectionsource.h" +#include "objectloader.h" + +namespace Msp { +namespace DataFile { + +/** +A source 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 an index 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 PackSource: public CollectionSource +{ +private: + class File; + struct Object; + + typedef std::map ObjectMap; + + class Pack + { + public: + class Loader: public ObjectLoader + { + public: + Loader(Pack &); + private: + void file(const std::string &); + }; + + private: + std::string filename; + unsigned base_offset; + std::list 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 + { + 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 objects; + bool loaded; + + public: + File(const Pack &, const std::string &); + + RefPtr 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 packs; + ObjectMap objects; + +public: + /// Adds a pack file to load objects from. The index is read immediately. + void add_pack_file(const std::string &); + + + virtual bool is_loadable(const CollectionItemTypeBase &, const std::string &) const; + virtual NameList get_names(const CollectionItemTypeBase &) const; + virtual void load(Collection &, const CollectionItemTypeBase &, const std::string &) const; +}; + +} // namespace DataFile +} // namespace Msp + +#endif