]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/skincache.cpp
fce3c1a14f4547ea29df520d0e278ae2d54ae34b
[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         StringOutputStream cleaned( 256 );
42         cleaned << PathCleaned( token );
43         name = cleaned.c_str();
44 }
45
46 class Doom3ModelSkin
47 {
48 typedef std::map<CopiedString, CopiedString> Remaps;
49 Remaps m_remaps;
50 public:
51 bool parseTokens( Tokeniser& tokeniser ){
52         RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "{" ) );
53         tokeniser.nextLine();
54         for (;; )
55         {
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                 }
64                 else if ( string_equal( token, "model" ) ) {
65                         //const char* model =
66                         tokeniser.getToken();
67                 }
68                 else
69                 {
70                         CopiedString from, to;
71                         parseShaderName( from, token );
72
73                         tokeniser.nextLine(); // hack to handle badly formed skins
74
75                         parseShaderName( to, tokeniser.getToken() );
76
77                         if ( !string_equal( from.c_str(), to.c_str() ) ) {
78                                 m_remaps.insert( Remaps::value_type( from, to ) );
79                         }
80                 }
81                 tokeniser.nextLine();
82         }
83 }
84 const char* getRemap( const char* name ) const {
85         Remaps::const_iterator i = m_remaps.find( name );
86         if ( i != m_remaps.end() ) {
87                 return ( *i ).second.c_str();
88         }
89         return "";
90 }
91 void forEachRemap( const SkinRemapCallback& callback ) const {
92         for ( Remaps::const_iterator i = m_remaps.begin(); i != m_remaps.end(); ++i )
93         {
94                 callback( SkinRemap( ( *i ).first.c_str(), ( *i ).second.c_str() ) );
95         }
96 }
97 };
98
99 class GlobalSkins
100 {
101 public:
102 typedef std::map<CopiedString, Doom3ModelSkin> SkinMap;
103 SkinMap m_skins;
104 Doom3ModelSkin g_nullSkin;
105
106 Doom3ModelSkin& getSkin( const char* name ){
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         tokeniser.nextLine();
117         for (;; )
118         {
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         StringOutputStream relativeName( 64 );
142         relativeName << "skins/" << name;
143         ArchiveTextFile* file = GlobalFileSystem().openTextFile( relativeName.c_str() );
144         if ( file != 0 ) {
145                 globalOutputStream() << "parsing skins from " << makeQuoted( name ) << "\n";
146                 {
147                         Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser( file->getInputStream() );
148                         parseTokens( tokeniser );
149                         tokeniser.release();
150                 }
151                 file->release();
152         }
153         else
154         {
155                 globalErrorStream() << "failed to open " << makeQuoted( name ) << "\n";
156         }
157 }
158
159 typedef MemberCaller1<GlobalSkins, const char*, &GlobalSkins::parseFile> ParseFileCaller;
160
161 void construct(){
162         GlobalFileSystem().forEachFile( "skins/", "skin", ParseFileCaller( *this ) );
163 }
164
165 void destroy(){
166         m_skins.clear();
167 }
168
169 void realise(){
170         construct();
171 }
172 void unrealise(){
173         destroy();
174 }
175 };
176
177 GlobalSkins g_skins;
178
179
180 class Doom3ModelSkinCacheElement : public ModelSkin
181 {
182 ModuleObservers m_observers;
183 Doom3ModelSkin* m_skin;
184 public:
185 Doom3ModelSkinCacheElement() : m_skin( 0 ){
186 }
187 void attach( ModuleObserver& observer ){
188         m_observers.attach( observer );
189         if ( realised() ) {
190                 observer.realise();
191         }
192 }
193 void detach( ModuleObserver& observer ){
194         if ( realised() ) {
195                 observer.unrealise();
196         }
197         m_observers.detach( observer );
198 }
199 bool realised() const {
200         return m_skin != 0;
201 }
202 void realise( const char* name ){
203         ASSERT_MESSAGE( !realised(), "Doom3ModelSkinCacheElement::realise: already realised" );
204         m_skin = &g_skins.getSkin( name );
205         m_observers.realise();
206 }
207 void unrealise(){
208         ASSERT_MESSAGE( realised(), "Doom3ModelSkinCacheElement::unrealise: not realised" );
209         m_observers.unrealise();
210         m_skin = 0;
211 }
212 const char* getRemap( const char* name ) const {
213         ASSERT_MESSAGE( realised(), "Doom3ModelSkinCacheElement::getRemap: not realised" );
214         return m_skin->getRemap( name );
215 }
216 void forEachRemap( const SkinRemapCallback& callback ) const {
217         ASSERT_MESSAGE( realised(), "Doom3ModelSkinCacheElement::forEachRemap: not realised" );
218         m_skin->forEachRemap( callback );
219 }
220 };
221
222 class Doom3ModelSkinCache : public ModelSkinCache, public ModuleObserver
223 {
224 class CreateDoom3ModelSkin
225 {
226 Doom3ModelSkinCache& m_cache;
227 public:
228 explicit CreateDoom3ModelSkin( Doom3ModelSkinCache& cache )
229         : m_cache( cache ){
230 }
231 Doom3ModelSkinCacheElement* construct( const CopiedString& name ){
232         Doom3ModelSkinCacheElement* skin = new Doom3ModelSkinCacheElement;
233         if ( m_cache.realised() ) {
234                 skin->realise( name.c_str() );
235         }
236         return skin;
237 }
238 void destroy( Doom3ModelSkinCacheElement* skin ){
239         if ( m_cache.realised() ) {
240                 skin->unrealise();
241         }
242         delete skin;
243 }
244 };
245
246 typedef HashedCache<CopiedString, Doom3ModelSkinCacheElement, HashString, std::equal_to<CopiedString>, CreateDoom3ModelSkin> Cache;
247 Cache m_cache;
248 bool m_realised;
249
250 public:
251 typedef ModelSkinCache Type;
252 STRING_CONSTANT( Name, "*" );
253 ModelSkinCache* getTable(){
254         return this;
255 }
256
257 Doom3ModelSkinCache() : m_cache( CreateDoom3ModelSkin( *this ) ), m_realised( false ){
258         GlobalFileSystem().attach( *this );
259 }
260 ~Doom3ModelSkinCache(){
261         GlobalFileSystem().detach( *this );
262 }
263
264 ModelSkin& capture( const char* name ){
265         return *m_cache.capture( name );
266 }
267 void release( const char* name ){
268         m_cache.release( name );
269 }
270
271 bool realised() const {
272         return m_realised;
273 }
274 void realise(){
275         g_skins.realise();
276         m_realised = true;
277         for ( Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i )
278         {
279                 ( *i ).value->realise( ( *i ).key.c_str() );
280         }
281 }
282 void unrealise(){
283         m_realised = false;
284         for ( Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i )
285         {
286                 ( *i ).value->unrealise();
287         }
288         g_skins.unrealise();
289 }
290 };
291
292 class Doom3ModelSkinCacheDependencies : public GlobalFileSystemModuleRef, public GlobalScripLibModuleRef
293 {
294 };
295
296 typedef SingletonModule<Doom3ModelSkinCache, Doom3ModelSkinCacheDependencies> Doom3ModelSkinCacheModule;
297
298 Doom3ModelSkinCacheModule g_Doom3ModelSkinCacheModule;
299
300 void Doom3ModelSkinCacheModule_selfRegister( ModuleServer& server ){
301         g_Doom3ModelSkinCacheModule.selfRegister();
302 }