/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "skincache.h" #include "ifilesystem.h" #include "iscriplib.h" #include "iarchive.h" #include "modelskin.h" #include #include "stream/stringstream.h" #include "generic/callback.h" #include "container/cache.h" #include "container/hashfunc.h" #include "os/path.h" #include "moduleobservers.h" #include "modulesystem/singletonmodule.h" #include "stringio.h" void parseShaderName(CopiedString& name, const char* token) { StringOutputStream cleaned(256); cleaned << PathCleaned(token); name = cleaned.c_str(); } class Doom3ModelSkin { typedef std::map Remaps; Remaps m_remaps; public: bool parseTokens(Tokeniser& tokeniser) { RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "{")); tokeniser.nextLine(); for(;;) { const char* token = tokeniser.getToken(); if(token == 0) { return false; } if(string_equal(token, "}")) { tokeniser.nextLine(); return true; } else if(string_equal(token, "model")) { //const char* model = tokeniser.getToken(); } else { CopiedString from, to; parseShaderName(from, token); tokeniser.nextLine(); // hack to handle badly formed skins parseShaderName(to, tokeniser.getToken()); if(!string_equal(from.c_str(), to.c_str())) { m_remaps.insert(Remaps::value_type(from, to)); } } tokeniser.nextLine(); } } const char* getRemap(const char* name) const { Remaps::const_iterator i = m_remaps.find(name); if(i != m_remaps.end()) { return (*i).second.c_str(); } return ""; } void forEachRemap(const SkinRemapCallback& callback) const { for(Remaps::const_iterator i = m_remaps.begin(); i != m_remaps.end(); ++i) { callback(SkinRemap((*i).first.c_str(), (*i).second.c_str())); } } }; class GlobalSkins { public: typedef std::map SkinMap; SkinMap m_skins; Doom3ModelSkin g_nullSkin; Doom3ModelSkin& getSkin(const char* name) { SkinMap::iterator i = m_skins.find(name); if(i != m_skins.end()) { return (*i).second; } return g_nullSkin; } bool parseTokens(Tokeniser& tokeniser) { tokeniser.nextLine(); for(;;) { const char* token = tokeniser.getToken(); if(token == 0) { // end of token stream return true; } if(!string_equal(token, "skin")) { Tokeniser_unexpectedError(tokeniser, token, "skin"); return false; } const char* other = tokeniser.getToken(); if(other == 0) { Tokeniser_unexpectedError(tokeniser, token, "#string"); return false; } CopiedString name; parseShaderName(name, other); Doom3ModelSkin& skin = m_skins[name]; RETURN_FALSE_IF_FAIL(skin.parseTokens(tokeniser)); } } void parseFile(const char* name) { StringOutputStream relativeName(64); relativeName << "skins/" << name; ArchiveTextFile* file = GlobalFileSystem().openTextFile(relativeName.c_str()); if(file != 0) { globalOutputStream() << "parsing skins from " << makeQuoted(name) << "\n"; { Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(file->getInputStream()); parseTokens(tokeniser); tokeniser.release(); } file->release(); } else { globalErrorStream() << "failed to open " << makeQuoted(name) << "\n"; } } typedef MemberCaller1 ParseFileCaller; void construct() { GlobalFileSystem().forEachFile("skins/", "skin", ParseFileCaller(*this)); } void destroy() { m_skins.clear(); } void realise() { construct(); } void unrealise() { destroy(); } }; GlobalSkins g_skins; class Doom3ModelSkinCacheElement : public ModelSkin { ModuleObservers m_observers; Doom3ModelSkin* m_skin; public: Doom3ModelSkinCacheElement() : m_skin(0) { } void attach(ModuleObserver& observer) { m_observers.attach(observer); if(realised()) { observer.realise(); } } void detach(ModuleObserver& observer) { if(realised()) { observer.unrealise(); } m_observers.detach(observer); } bool realised() const { return m_skin != 0; } void realise(const char* name) { ASSERT_MESSAGE(!realised(), "Doom3ModelSkinCacheElement::realise: already realised"); m_skin = &g_skins.getSkin(name); m_observers.realise(); } void unrealise() { ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::unrealise: not realised"); m_observers.unrealise(); m_skin = 0; } const char* getRemap(const char* name) const { ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::getRemap: not realised"); return m_skin->getRemap(name); } void forEachRemap(const SkinRemapCallback& callback) const { ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::forEachRemap: not realised"); m_skin->forEachRemap(callback); } }; class Doom3ModelSkinCache : public ModelSkinCache, public ModuleObserver { class CreateDoom3ModelSkin { Doom3ModelSkinCache& m_cache; public: explicit CreateDoom3ModelSkin(Doom3ModelSkinCache& cache) : m_cache(cache) { } Doom3ModelSkinCacheElement* construct(const CopiedString& name) { Doom3ModelSkinCacheElement* skin = new Doom3ModelSkinCacheElement; if(m_cache.realised()) { skin->realise(name.c_str()); } return skin; } void destroy(Doom3ModelSkinCacheElement* skin) { if(m_cache.realised()) { skin->unrealise(); } delete skin; } }; typedef HashedCache, CreateDoom3ModelSkin> Cache; Cache m_cache; bool m_realised; public: typedef ModelSkinCache Type; STRING_CONSTANT(Name, "*"); ModelSkinCache* getTable() { return this; } Doom3ModelSkinCache() : m_cache(CreateDoom3ModelSkin(*this)), m_realised(false) { GlobalFileSystem().attach(*this); } ~Doom3ModelSkinCache() { GlobalFileSystem().detach(*this); } ModelSkin& capture(const char* name) { return *m_cache.capture(name); } void release(const char* name) { m_cache.release(name); } bool realised() const { return m_realised; } void realise() { g_skins.realise(); m_realised = true; for(Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i) { (*i).value->realise((*i).key.c_str()); } } void unrealise() { m_realised = false; for(Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i) { (*i).value->unrealise(); } g_skins.unrealise(); } }; class Doom3ModelSkinCacheDependencies : public GlobalFileSystemModuleRef, public GlobalScripLibModuleRef { }; typedef SingletonModule Doom3ModelSkinCacheModule; Doom3ModelSkinCacheModule g_Doom3ModelSkinCacheModule; void Doom3ModelSkinCacheModule_selfRegister(ModuleServer& server) { g_Doom3ModelSkinCacheModule.selfRegister(); }