]> git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/skincache.cpp
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / plugins / entity / skincache.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 #include "skincache.h"
23
24 #include "ifilesystem.h"
25 #include "iscriplib.h"
26 #include "iarchive.h"
27 #include "modelskin.h"
28
29 #include <map>
30
31 #include "stream/stringstream.h"
32 #include "generic/callback.h"
33 #include "container/cache.h"
34 #include "container/hashfunc.h"
35 #include "os/path.h"
36 #include "moduleobservers.h"
37 #include "modulesystem/singletonmodule.h"
38 #include "stringio.h"
39
40 void parseShaderName(CopiedString &name, const char *token)
41 {
42     StringOutputStream cleaned(256);
43     cleaned << PathCleaned(token);
44     name = cleaned.c_str();
45 }
46
47 class Doom3ModelSkin {
48     typedef std::map<CopiedString, CopiedString> Remaps;
49     Remaps m_remaps;
50 public:
51     bool parseTokens(Tokeniser &tokeniser)
52     {
53         RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "{"));
54         tokeniser.nextLine();
55         for (;;) {
56             const char *token = tokeniser.getToken();
57             if (token == 0) {
58                 return false;
59             }
60             if (string_equal(token, "}")) {
61                 tokeniser.nextLine();
62                 return true;
63             } else if (string_equal(token, "model")) {
64                 //const char* model =
65                 tokeniser.getToken();
66             } else {
67                 CopiedString from, to;
68                 parseShaderName(from, token);
69
70                 tokeniser.nextLine(); // hack to handle badly formed skins
71
72                 parseShaderName(to, tokeniser.getToken());
73
74                 if (!string_equal(from.c_str(), to.c_str())) {
75                     m_remaps.insert(Remaps::value_type(from, to));
76                 }
77             }
78             tokeniser.nextLine();
79         }
80     }
81
82     const char *getRemap(const char *name) const
83     {
84         Remaps::const_iterator i = m_remaps.find(name);
85         if (i != m_remaps.end()) {
86             return (*i).second.c_str();
87         }
88         return "";
89     }
90
91     void forEachRemap(const SkinRemapCallback &callback) const
92     {
93         for (Remaps::const_iterator i = m_remaps.begin(); i != m_remaps.end(); ++i) {
94             callback(SkinRemap((*i).first.c_str(), (*i).second.c_str()));
95         }
96     }
97 };
98
99 class GlobalSkins {
100 public:
101     typedef std::map<CopiedString, Doom3ModelSkin> SkinMap;
102     SkinMap m_skins;
103     Doom3ModelSkin g_nullSkin;
104
105     Doom3ModelSkin &getSkin(const char *name)
106     {
107         SkinMap::iterator i = m_skins.find(name);
108         if (i != m_skins.end()) {
109             return (*i).second;
110         }
111
112         return g_nullSkin;
113     }
114
115     bool parseTokens(Tokeniser &tokeniser)
116     {
117         tokeniser.nextLine();
118         for (;;) {
119             const char *token = tokeniser.getToken();
120             if (token == 0) {
121                 // end of token stream
122                 return true;
123             }
124             if (!string_equal(token, "skin")) {
125                 Tokeniser_unexpectedError(tokeniser, token, "skin");
126                 return false;
127             }
128             const char *other = tokeniser.getToken();
129             if (other == 0) {
130                 Tokeniser_unexpectedError(tokeniser, token, "#string");
131                 return false;
132             }
133             CopiedString name;
134             parseShaderName(name, other);
135             Doom3ModelSkin &skin = m_skins[name];
136             RETURN_FALSE_IF_FAIL(skin.parseTokens(tokeniser));
137         }
138     }
139
140     void parseFile(const char *name)
141     {
142         StringOutputStream relativeName(64);
143         relativeName << "skins/" << name;
144         ArchiveTextFile *file = GlobalFileSystem().openTextFile(relativeName.c_str());
145         if (file != 0) {
146             globalOutputStream() << "parsing skins from " << makeQuoted(name) << "\n";
147             {
148                 Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(file->getInputStream());
149                 parseTokens(tokeniser);
150                 tokeniser.release();
151             }
152             file->release();
153         } else {
154             globalErrorStream() << "failed to open " << makeQuoted(name) << "\n";
155         }
156     }
157
158     typedef MemberCaller<GlobalSkins, void(const char *), &GlobalSkins::parseFile> ParseFileCaller;
159
160     void construct()
161     {
162         GlobalFileSystem().forEachFile("skins/", "skin", ParseFileCaller(*this));
163     }
164
165     void destroy()
166     {
167         m_skins.clear();
168     }
169
170     void realise()
171     {
172         construct();
173     }
174
175     void unrealise()
176     {
177         destroy();
178     }
179 };
180
181 GlobalSkins g_skins;
182
183
184 class Doom3ModelSkinCacheElement : public ModelSkin {
185     ModuleObservers m_observers;
186     Doom3ModelSkin *m_skin;
187 public:
188     Doom3ModelSkinCacheElement() : m_skin(0)
189     {
190     }
191
192     void attach(ModuleObserver &observer)
193     {
194         m_observers.attach(observer);
195         if (realised()) {
196             observer.realise();
197         }
198     }
199
200     void detach(ModuleObserver &observer)
201     {
202         if (realised()) {
203             observer.unrealise();
204         }
205         m_observers.detach(observer);
206     }
207
208     bool realised() const
209     {
210         return m_skin != 0;
211     }
212
213     void realise(const char *name)
214     {
215         ASSERT_MESSAGE(!realised(), "Doom3ModelSkinCacheElement::realise: already realised");
216         m_skin = &g_skins.getSkin(name);
217         m_observers.realise();
218     }
219
220     void unrealise()
221     {
222         ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::unrealise: not realised");
223         m_observers.unrealise();
224         m_skin = 0;
225     }
226
227     const char *getRemap(const char *name) const
228     {
229         ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::getRemap: not realised");
230         return m_skin->getRemap(name);
231     }
232
233     void forEachRemap(const SkinRemapCallback &callback) const
234     {
235         ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::forEachRemap: not realised");
236         m_skin->forEachRemap(callback);
237     }
238 };
239
240 class Doom3ModelSkinCache : public ModelSkinCache, public ModuleObserver {
241     class CreateDoom3ModelSkin {
242         Doom3ModelSkinCache &m_cache;
243     public:
244         explicit CreateDoom3ModelSkin(Doom3ModelSkinCache &cache)
245                 : m_cache(cache)
246         {
247         }
248
249         Doom3ModelSkinCacheElement *construct(const CopiedString &name)
250         {
251             Doom3ModelSkinCacheElement *skin = new Doom3ModelSkinCacheElement;
252             if (m_cache.realised()) {
253                 skin->realise(name.c_str());
254             }
255             return skin;
256         }
257
258         void destroy(Doom3ModelSkinCacheElement *skin)
259         {
260             if (m_cache.realised()) {
261                 skin->unrealise();
262             }
263             delete skin;
264         }
265     };
266
267     typedef HashedCache<CopiedString, Doom3ModelSkinCacheElement, HashString, std::equal_to<CopiedString>, CreateDoom3ModelSkin> Cache;
268     Cache m_cache;
269     bool m_realised;
270
271 public:
272     typedef ModelSkinCache Type;
273
274     STRING_CONSTANT(Name, "*");
275
276     ModelSkinCache *getTable()
277     {
278         return this;
279     }
280
281     Doom3ModelSkinCache() : m_cache(CreateDoom3ModelSkin(*this)), m_realised(false)
282     {
283         GlobalFileSystem().attach(*this);
284     }
285
286     ~Doom3ModelSkinCache()
287     {
288         GlobalFileSystem().detach(*this);
289     }
290
291     ModelSkin &capture(const char *name)
292     {
293         return *m_cache.capture(name);
294     }
295
296     void release(const char *name)
297     {
298         m_cache.release(name);
299     }
300
301     bool realised() const
302     {
303         return m_realised;
304     }
305
306     void realise()
307     {
308         g_skins.realise();
309         m_realised = true;
310         for (Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i) {
311             (*i).value->realise((*i).key.c_str());
312         }
313     }
314
315     void unrealise()
316     {
317         m_realised = false;
318         for (Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i) {
319             (*i).value->unrealise();
320         }
321         g_skins.unrealise();
322     }
323 };
324
325 class Doom3ModelSkinCacheDependencies : public GlobalFileSystemModuleRef, public GlobalScripLibModuleRef {
326 };
327
328 typedef SingletonModule<Doom3ModelSkinCache, Doom3ModelSkinCacheDependencies> Doom3ModelSkinCacheModule;
329
330 Doom3ModelSkinCacheModule g_Doom3ModelSkinCacheModule;
331
332 void Doom3ModelSkinCacheModule_selfRegister(ModuleServer &server)
333 {
334     g_Doom3ModelSkinCacheModule.selfRegister();
335 }