2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "referencecache.h"
24 #include "debugging/debugging.h"
26 #include "iscenegraph.h"
27 #include "iselection.h"
30 MapModules& ReferenceAPI_getMapModules();
32 ModelModules& ReferenceAPI_getModelModules();
33 #include "ifilesystem.h"
35 #include "ifiletypes.h"
36 #include "ireference.h"
38 #include "qerplugin.h"
42 #include "container/cache.h"
43 #include "container/hashfunc.h"
45 #include "stream/textfilestream.h"
46 #include "nullmodel.h"
48 #include "stream/stringstream.h"
50 #include "moduleobserver.h"
51 #include "moduleobservers.h"
53 #include "mainframe.h"
55 #include "filetypes.h"
58 bool References_Saved();
62 Map_SetModified(g_map, !References_Saved());
66 EntityCreator* g_entityCreator = 0;
68 bool MapResource_loadFile(const MapFormat& format, scene::Node& root, const char* filename)
70 globalOutputStream() << "Open file " << filename << " for read...";
71 TextFileInputStream file(filename);
74 globalOutputStream() << "success\n";
75 ScopeDisableScreenUpdates disableScreenUpdates(path_get_filename_start(filename), "Loading Map");
76 ASSERT_NOTNULL(g_entityCreator);
77 format.readGraph(root, file, *g_entityCreator);
82 globalErrorStream() << "failure\n";
87 NodeSmartReference MapResource_load(const MapFormat& format, const char* path, const char* name)
89 NodeSmartReference root(NewMapRoot(name));
91 StringOutputStream fullpath(256);
92 fullpath << path << name;
94 if(path_is_absolute(fullpath.c_str()))
96 MapResource_loadFile(format, root, fullpath.c_str());
100 globalErrorStream() << "map path is not fully qualified: " << makeQuoted(fullpath.c_str()) << "\n";
106 bool MapResource_saveFile(const MapFormat& format, scene::Node& root, GraphTraversalFunc traverse, const char* filename)
108 //ASSERT_MESSAGE(path_is_absolute(filename), "MapResource_saveFile: path is not absolute: " << makeQuoted(filename));
109 globalOutputStream() << "Open file " << filename << " for write...";
110 TextFileOutputStream file(filename);
113 globalOutputStream() << "success\n";
114 ScopeDisableScreenUpdates disableScreenUpdates(path_get_filename_start(filename), "Saving Map");
115 format.writeGraph(root, traverse, file);
119 globalErrorStream() << "failure\n";
123 bool file_saveBackup(const char* path)
125 if(file_writeable(path))
127 StringOutputStream backup(256);
128 backup << StringRange(path, path_get_extension(path)) << "bak";
130 return (!file_exists(backup.c_str()) || file_remove(backup.c_str())) // remove backup
131 && file_move(path, backup.c_str()); // rename current to backup
134 globalErrorStream() << "map path is not writeable: " << makeQuoted(path) << "\n";
138 bool MapResource_save(const MapFormat& format, scene::Node& root, const char* path, const char* name)
140 StringOutputStream fullpath(256);
141 fullpath << path << name;
143 if(path_is_absolute(fullpath.c_str()))
145 if(!file_exists(fullpath.c_str()) || file_saveBackup(fullpath.c_str()))
147 return MapResource_saveFile(format, root, Map_Traverse, fullpath.c_str());
150 globalErrorStream() << "failed to save a backup map file: " << makeQuoted(fullpath.c_str()) << "\n";
154 globalErrorStream() << "map path is not fully qualified: " << makeQuoted(fullpath.c_str()) << "\n";
160 NodeSmartReference g_nullNode(NewNullNode());
161 NodeSmartReference g_nullModel(g_nullNode);
164 class NullModelLoader : public ModelLoader
167 scene::Node& loadModel(ArchiveFile& file)
175 NullModelLoader g_NullModelLoader;
179 /// \brief Returns the model loader for the model \p type or 0 if the model \p type has no loader module
180 ModelLoader* ModelLoader_forType(const char* type)
182 const char* moduleName = findModuleName(&GlobalFiletypes(), ModelLoader::Name(), type);
183 if(string_not_empty(moduleName))
185 ModelLoader* table = ReferenceAPI_getModelModules().findModule(moduleName);
192 globalErrorStream() << "ERROR: Model type incorrectly registered: \"" << moduleName << "\"\n";
193 return &g_NullModelLoader;
199 NodeSmartReference ModelResource_load(ModelLoader* loader, const char* name)
201 ScopeDisableScreenUpdates disableScreenUpdates(path_get_filename_start(name), "Loading Model");
203 NodeSmartReference model(g_nullModel);
206 ArchiveFile* file = GlobalFileSystem().openFile(name);
210 globalOutputStream() << "Loaded Model: \"" << name << "\"\n";
211 model = loader->loadModel(*file);
216 globalErrorStream() << "Model load failed: \"" << name << "\"\n";
220 model.get().m_isRoot = true;
226 inline hash_t path_hash(const char* path, hash_t previous = 0)
229 return string_hash_nocase(path, previous);
231 return string_hash(path, previous);
237 bool operator()(const CopiedString& path, const CopiedString& other) const
239 return path_equal(path.c_str(), other.c_str());
245 typedef hash_t hash_type;
246 hash_type operator()(const CopiedString& path) const
248 return path_hash(path.c_str());
252 typedef std::pair<CopiedString, CopiedString> ModelKey;
256 bool operator()(const ModelKey& key, const ModelKey& other) const
258 return path_equal(key.first.c_str(), other.first.c_str()) && path_equal(key.second.c_str(), other.second.c_str());
264 typedef hash_t hash_type;
265 hash_type operator()(const ModelKey& key) const
267 return hash_combine(path_hash(key.first.c_str()), path_hash(key.second.c_str()));
271 typedef HashTable<ModelKey, NodeSmartReference, ModelKeyHash, ModelKeyEqual> ModelCache;
272 ModelCache g_modelCache;
273 bool g_modelCache_enabled = true;
275 ModelCache::iterator ModelCache_find(const char* path, const char* name)
277 if(g_modelCache_enabled)
279 return g_modelCache.find(ModelKey(path, name));
281 return g_modelCache.end();
284 ModelCache::iterator ModelCache_insert(const char* path, const char* name, scene::Node& node)
286 if(g_modelCache_enabled)
288 return g_modelCache.insert(ModelKey(path, name), NodeSmartReference(node));
290 return g_modelCache.insert(ModelKey("", ""), g_nullModel);
293 void ModelCache_flush(const char* path, const char* name)
295 ModelCache::iterator i = g_modelCache.find(ModelKey(path, name));
296 if(i != g_modelCache.end())
298 //ASSERT_MESSAGE((*i).value.getCount() == 0, "resource flushed while still in use: " << (*i).key.first.c_str() << (*i).key.second.c_str());
299 g_modelCache.erase(i);
303 void ModelCache_clear()
305 g_modelCache_enabled = false;
306 g_modelCache.clear();
307 g_modelCache_enabled = true;
310 NodeSmartReference Model_load(ModelLoader* loader, const char* path, const char* name, const char* type)
314 return ModelResource_load(loader, name);
318 const char* moduleName = findModuleName(&GlobalFiletypes(), MapFormat::Name(), type);
319 if(string_not_empty(moduleName))
321 const MapFormat* format = ReferenceAPI_getMapModules().findModule(moduleName);
324 return MapResource_load(*format, path, name);
328 globalErrorStream() << "ERROR: Map type incorrectly registered: \"" << moduleName << "\"\n";
334 if(string_not_empty(type))
336 globalErrorStream() << "Model type not supported: \"" << name << "\"\n";
345 bool g_realised = false;
347 // name may be absolute or relative
348 const char* rootPath(const char* name)
350 return GlobalFileSystem().findRoot(
351 path_is_absolute(name)
353 : GlobalFileSystem().findFile(name)
358 struct ModelResource : public Resource
360 NodeSmartReference m_model;
361 const CopiedString m_originalName;
365 ModelLoader* m_loader;
366 ModuleObservers m_observers;
367 std::time_t m_modified;
368 std::size_t m_unrealised;
370 ModelResource(const CopiedString& name) :
371 m_model(g_nullModel),
372 m_originalName(name),
373 m_type(path_get_extension(name.c_str())),
378 m_loader = ModelLoader_forType(m_type.c_str());
391 ASSERT_MESSAGE(!realised(), "ModelResource::~ModelResource: resource reference still realised: " << makeQuoted(m_name.c_str()));
394 ModelResource(const ModelResource&);
396 ModelResource& operator=(const ModelResource&);
398 void setModel(const NodeSmartReference& model)
404 m_model = g_nullModel;
409 if(g_modelCache_enabled)
412 ModelCache::iterator i = ModelCache_find(m_path.c_str(), m_name.c_str());
413 if(i == g_modelCache.end())
415 i = ModelCache_insert(
418 Model_load(m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str())
422 setModel((*i).value);
426 setModel(Model_load(m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str()));
439 ASSERT_MESSAGE(realised(), "resource not realised");
440 if(m_model == g_nullModel)
445 return m_model != g_nullModel;
451 const char* moduleName = findModuleName(GetFileTypeRegistry(), MapFormat::Name(), m_type.c_str());
452 if(string_not_empty(moduleName))
454 const MapFormat* format = ReferenceAPI_getMapModules().findModule(moduleName);
455 if(format != 0 && MapResource_save(*format, m_model.get(), m_path.c_str(), m_name.c_str()))
468 ModelCache_flush(m_path.c_str(), m_name.c_str());
471 scene::Node* getNode()
473 //if(m_model != g_nullModel)
475 return m_model.get_pointer();
479 void setNode(scene::Node* node)
481 ModelCache::iterator i = ModelCache_find(m_path.c_str(), m_name.c_str());
482 if(i != g_modelCache.end())
484 (*i).value = NodeSmartReference(*node);
486 setModel(NodeSmartReference(*node));
490 void attach(ModuleObserver& observer)
496 m_observers.attach(observer);
498 void detach(ModuleObserver& observer)
502 observer.unrealise();
504 m_observers.detach(observer);
508 return m_unrealised == 0;
512 ASSERT_MESSAGE(m_unrealised != 0, "ModelResource::realise: already realised");
513 if(--m_unrealised == 0)
515 m_path = rootPath(m_originalName.c_str());
516 m_name = path_make_relative(m_originalName.c_str(), m_path.c_str());
518 //globalOutputStream() << "ModelResource::realise: " << m_path.c_str() << m_name.c_str() << "\n";
520 m_observers.realise();
525 if(++m_unrealised == 1)
527 m_observers.unrealise();
529 //globalOutputStream() << "ModelResource::unrealise: " << m_path.c_str() << m_name.c_str() << "\n";
535 return Node_getMapFile(m_model) != 0;
539 MapFile* map = Node_getMapFile(m_model);
542 map->setChangedCallback(FreeCaller<MapChanged>());
545 std::time_t modified() const
547 StringOutputStream fullpath(256);
548 fullpath << m_path.c_str() << m_name.c_str();
549 return file_modified(fullpath.c_str());
553 m_modified = modified();
554 MapFile* map = Node_getMapFile(m_model);
560 bool mapSaved() const
562 MapFile* map = Node_getMapFile(m_model);
565 return m_modified == modified() && map->saved();
569 bool isModified() const
571 return ((!string_empty(m_path.c_str()) // had or has an absolute path
572 && m_modified != modified()) // AND disk timestamp changed
573 || !path_equal(rootPath(m_originalName.c_str()), m_path.c_str())); // OR absolute vfs-root changed
586 class HashtableReferenceCache : public ReferenceCache, public ModuleObserver
588 typedef HashedCache<CopiedString, ModelResource, PathHash, PathEqual> ModelReferences;
589 ModelReferences m_references;
590 std::size_t m_unrealised;
592 class ModelReferencesSnapshot
594 ModelReferences& m_references;
595 typedef std::list<ModelReferences::iterator> Iterators;
596 Iterators m_iterators;
598 typedef Iterators::iterator iterator;
599 ModelReferencesSnapshot(ModelReferences& references) : m_references(references)
601 for(ModelReferences::iterator i = m_references.begin(); i != m_references.end(); ++i)
603 m_references.capture(i);
604 m_iterators.push_back(i);
607 ~ModelReferencesSnapshot()
609 for(Iterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i)
611 m_references.release(*i);
616 return m_iterators.begin();
620 return m_iterators.end();
626 typedef ModelReferences::iterator iterator;
628 HashtableReferenceCache() : m_unrealised(1)
634 return m_references.begin();
638 return m_references.end();
643 m_references.clear();
646 Resource* capture(const char* path)
648 //globalOutputStream() << "capture: \"" << path << "\"\n";
649 return m_references.capture(CopiedString(path)).get();
651 void release(const char* path)
653 m_references.release(CopiedString(path));
654 //globalOutputStream() << "release: \"" << path << "\"\n";
657 void setEntityCreator(EntityCreator& entityCreator)
659 g_entityCreator = &entityCreator;
662 bool realised() const
664 return m_unrealised == 0;
668 ASSERT_MESSAGE(m_unrealised != 0, "HashtableReferenceCache::realise: already realised");
669 if(--m_unrealised == 0)
674 ModelReferencesSnapshot snapshot(m_references);
675 for(ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i)
677 ModelReferences::value_type& value = *(*i);
678 if(value.value.count() != 1)
680 value.value.get()->realise();
688 if(++m_unrealised == 1)
693 ModelReferencesSnapshot snapshot(m_references);
694 for(ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i)
696 ModelReferences::value_type& value = *(*i);
697 if(value.value.count() != 1)
699 value.value.get()->unrealise();
709 ModelReferencesSnapshot snapshot(m_references);
710 for(ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i)
712 ModelResource* resource = (*(*i)).value.get();
713 if(!resource->isMap())
723 HashtableReferenceCache g_referenceCache;
727 class ResourceVisitor
730 virtual void visit(const char* name, const char* path, const
734 void SaveReferences()
736 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
737 for(HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i)
744 bool References_Saved()
746 for(HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i)
748 scene::Node* node = (*i).value->getNode();
751 MapFile* map = Node_getMapFile(*node);
752 if(map != 0 && !map->saved())
761 void RefreshReferences()
763 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Refreshing Models");
764 g_referenceCache.refresh();
768 void FlushReferences()
772 g_referenceCache.clear();
775 ReferenceCache& GetReferenceCache()
777 return g_referenceCache;
781 #include "modulesystem/modulesmap.h"
782 #include "modulesystem/singletonmodule.h"
783 #include "modulesystem/moduleregistry.h"
785 class ReferenceDependencies :
786 public GlobalRadiantModuleRef,
787 public GlobalFileSystemModuleRef,
788 public GlobalFiletypesModuleRef
790 ModelModulesRef m_model_modules;
791 MapModulesRef m_map_modules;
793 ReferenceDependencies() :
794 m_model_modules(GlobalRadiant().getRequiredGameDescriptionKeyValue("modeltypes")),
795 m_map_modules(GlobalRadiant().getRequiredGameDescriptionKeyValue("maptypes"))
798 ModelModules& getModelModules()
800 return m_model_modules.get();
802 MapModules& getMapModules()
804 return m_map_modules.get();
808 class ReferenceAPI : public TypeSystemRef
810 ReferenceCache* m_reference;
812 typedef ReferenceCache Type;
813 STRING_CONSTANT(Name, "*");
817 g_nullModel = NewNullModel();
819 GlobalFileSystem().attach(g_referenceCache);
821 m_reference = &GetReferenceCache();
825 GlobalFileSystem().detach(g_referenceCache);
827 g_nullModel = g_nullNode;
829 ReferenceCache* getTable()
835 typedef SingletonModule<ReferenceAPI, ReferenceDependencies> ReferenceModule;
836 typedef Static<ReferenceModule> StaticReferenceModule;
837 StaticRegisterModule staticRegisterReference(StaticReferenceModule::instance());
839 ModelModules& ReferenceAPI_getModelModules()
841 return StaticReferenceModule::instance().getDependencies().getModelModules();
843 MapModules& ReferenceAPI_getMapModules()
845 return StaticReferenceModule::instance().getDependencies().getMapModules();