Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/core/Loader.cc @ 8987

Last change on this file since 8987 was 8830, checked in by landauf, 13 years ago

added some output (user and internal) throughout the initialization of the game, graphics, and game states

  • Property svn:eol-style set to native
File size: 16.4 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "Loader.h"
30
31#include <sstream>
32#include <tinyxml/ticpp.h>
33#include <boost/scoped_ptr.hpp>
34
35#include "util/Output.h"
36#include "util/Exception.h"
37#include "util/StringUtils.h"
38#include "BaseObject.h"
39#include "Iterator.h"
40#include "ObjectList.h"
41#include "LuaState.h"
42#include "Namespace.h"
43#include "Resource.h"
44#include "XMLFile.h"
45
46namespace orxonox
47{
48    std::vector<std::pair<const XMLFile*, ClassTreeMask> > Loader::files_s;
49    ClassTreeMask Loader::currentMask_s;
50
51    bool Loader::open(const XMLFile* file, const ClassTreeMask& mask, bool bVerbose)
52    {
53        Loader::add(file, mask);
54        return Loader::load(file, mask, bVerbose);
55    }
56
57    void Loader::close()
58    {
59        Loader::unload();
60        Loader::files_s.clear();
61    }
62
63    void Loader::close(const XMLFile* file)
64    {
65        Loader::unload(file);
66        Loader::remove(file);
67    }
68
69    void Loader::add(const XMLFile* file, const ClassTreeMask& mask)
70    {
71        if (!file)
72            return;
73        Loader::files_s.insert(Loader::files_s.end(), std::pair<const XMLFile*, ClassTreeMask>(file, mask));
74    }
75
76    void Loader::remove(const XMLFile* file)
77    {
78        if (!file)
79            return;
80        for (std::vector<std::pair<const XMLFile*, ClassTreeMask> >::iterator it = Loader::files_s.begin(); it != Loader::files_s.end(); ++it)
81        {
82            if (it->first == file)
83            {
84                Loader::files_s.erase(it);
85                break;
86            }
87        }
88    }
89
90    /**
91    @brief
92        Loads all opened files, while conforming to the restrictions given by the input ClassTreeMask.
93    @param mask
94        A ClassTreeMask, which defines which types of classes are loaded and which aren't.
95    @param bVerbose
96        Whether the loader is verbose (prints its progress in a low output level) or not.
97    @return
98        Returns true if successful.
99    */
100    bool Loader::load(const ClassTreeMask& mask, bool bVerbose)
101    {
102        bool success = true;
103        for (std::vector<std::pair<const XMLFile*, ClassTreeMask> >::iterator it = Loader::files_s.begin(); it != Loader::files_s.end(); ++it)
104            if (!Loader::load(it->first, it->second * mask, bVerbose))
105                success = false;
106
107        return success;
108    }
109
110    void Loader::unload(const ClassTreeMask& mask)
111    {
112        for (ObjectList<BaseObject>::iterator it = ObjectList<BaseObject>::begin(); it != ObjectList<BaseObject>::end(); )
113        {
114            if (mask.isIncluded(it->getIdentifier()))
115                (it++)->destroy();
116            else
117                ++it;
118        }
119    }
120
121    /**
122    @brief
123        Reloads all opened files, while conforming to the restrictions given by the input ClassTreeMask.
124    @param mask
125        A ClassTreeMask, which defines which types of classes are reloaded and which aren't.
126    @param bVerbose
127        Whether the loader is verbose (prints its progress in a low output level) or not.
128    @return
129        Returns true if successful.
130    */
131    bool Loader::reload(const ClassTreeMask& mask, bool bVerbose)
132    {
133        Loader::unload(mask);
134        return Loader::load(mask, bVerbose);
135    }
136
137    /**
138    @brief
139        Loads the input file, while conforming to the restrictions given by the input ClassTreeMask.
140    @param file
141        The file to be loaded.
142    @param mask
143        A ClassTreeMask, which defines which types of classes are loaded and which aren't.
144    @param bVerbose
145        Whether the loader is verbose (prints its progress in a low output level) or not.
146    @param bRemoveLuaTags
147        If true lua tags are just ignored and removed. The default is false.
148    @return
149        Returns true if successful.
150    */
151    bool Loader::load(const XMLFile* file, const ClassTreeMask& mask, bool bVerbose, bool bRemoveLuaTags)
152    {
153        if (!file)
154            return false;
155
156        Loader::currentMask_s = file->getMask() * mask;
157
158        std::string xmlInput;
159        if (file->getLuaSupport() && !bRemoveLuaTags)
160        {
161            // Use the LuaState to replace the XML tags (calls our function)
162            scoped_ptr<LuaState> luaState(new LuaState());
163            luaState->setIncludeParser(&Loader::replaceLuaTags);
164            luaState->includeFile(file->getFilename());
165            xmlInput = luaState->getOutput().str();
166        }
167        else
168        {
169            shared_ptr<ResourceInfo> info = Resource::getInfo(file->getFilename());
170            if (info == NULL)
171            {
172                orxout(user_error, context::loader) << "Could not find XML file '" << file->getFilename() << "'." << endl;
173                return false;
174            }
175            xmlInput = Resource::open(file->getFilename())->getAsString();
176
177            if (bRemoveLuaTags)
178            {
179                // Remove all Lua code.
180                // Note: we only need this to speed up parsing of level files at the
181                // start of the program.
182                // Assumption: the LevelInfo tag does not use Lua scripting
183                xmlInput = removeLuaTags(xmlInput);
184            }
185        }
186
187        try
188        {
189            if(bVerbose)
190            {
191                orxout(user_info) << "Start loading " << file->getFilename() << "..." << endl;
192                orxout(internal_info, context::loader) << "Mask: " << Loader::currentMask_s << endl;
193            }
194            else
195            {
196                orxout(verbose, context::loader) << "Start loading " << file->getFilename() << "..." << endl;
197                orxout(verbose_more, context::loader) << "Mask: " << Loader::currentMask_s << endl;
198            }
199
200            ticpp::Document xmlfile(file->getFilename());
201            xmlfile.Parse(xmlInput, true);
202
203            ticpp::Element rootElement;
204            rootElement.SetAttribute("name", "root");
205            rootElement.SetAttribute("bAutogenerated", true);
206
207            for (ticpp::Iterator<ticpp::Element> child = xmlfile.FirstChildElement(false); child != child.end(); child++)
208                rootElement.InsertEndChild(*child);
209
210            orxout(verbose, context::loader) << "  creating root-namespace..." << endl;
211            Namespace* rootNamespace = new Namespace(0);
212            rootNamespace->setLoaderIndentation("    ");
213            rootNamespace->setFile(file);
214            rootNamespace->setNamespace(rootNamespace);
215            rootNamespace->setRoot(true);
216            rootNamespace->XMLPort(rootElement, XMLPort::LoadObject);
217
218            if(bVerbose)
219                orxout(user_info) << "Finished loading " << file->getFilename() << '.' << endl;
220            else
221                orxout(verbose, context::loader) << "Finished loading " << file->getFilename() << '.' << endl;
222
223            orxout(verbose, context::loader) << "Namespace-tree:" << '\n' << rootNamespace->toString("  ") << endl;
224
225            return true;
226        }
227        catch (ticpp::Exception& ex)
228        {
229            orxout(user_error, context::loader) << endl;
230            orxout(user_error, context::loader) << "An XML-error occurred in Loader.cc while loading " << file->getFilename() << ':' << endl;
231            orxout(user_error, context::loader) << ex.what() << endl;
232            orxout(user_error, context::loader) << "Loading aborted." << endl;
233            return false;
234        }
235        catch (Exception& ex)
236        {
237            orxout(user_error, context::loader) << endl;
238            orxout(user_error, context::loader) << "A loading-error occurred in Loader.cc while loading " << file->getFilename() << ':' << endl;
239            orxout(user_error, context::loader) << ex.what() << endl;
240            orxout(user_error, context::loader) << "Loading aborted." << endl;
241            return false;
242        }
243        catch (...)
244        {
245            orxout(user_error, context::loader) << endl;
246            orxout(user_error, context::loader) << "An error occurred in Loader.cc while loading " << file->getFilename() << ':' << endl;
247            orxout(user_error, context::loader) << Exception::handleMessage() << endl;
248            orxout(user_error, context::loader) << "Loading aborted." << endl;
249            return false;
250        }
251    }
252
253    void Loader::unload(const XMLFile* file, const ClassTreeMask& mask)
254    {
255        if (!file)
256            return;
257        for (ObjectList<BaseObject>::iterator it = ObjectList<BaseObject>::begin(); it; )
258        {
259            if ((it->getFile() == file) && mask.isIncluded(it->getIdentifier()))
260                (it++)->destroy();
261            else
262                ++it;
263        }
264    }
265
266    /**
267    @brief
268        Reloads the input file, while conforming to the restrictions given by the input ClassTreeMask.
269    @param file
270        The file to be reloaded.
271    @param mask
272        A ClassTreeMask, which defines which types of classes are reloaded and which aren't.
273    @param bVerbose
274        Whether the loader is verbose (prints its progress in a low output level) or not.
275    @return
276        Returns true if successful.
277    */
278    bool Loader::reload(const XMLFile* file, const ClassTreeMask& mask, bool bVerbose)
279    {
280        Loader::unload(file, mask);
281        return Loader::load(file, mask, bVerbose);
282    }
283
284    bool Loader::getLuaTags(const std::string& text, std::map<size_t, bool>& luaTags)
285    {
286        // fill map with all Lua tags
287        {
288            size_t pos = 0;
289            while ((pos = text.find("<?lua", pos)) != std::string::npos)
290                luaTags[pos++] = true;
291        }
292        {
293            size_t pos = 0;
294            while ((pos = text.find("?>", pos)) != std::string::npos)
295                luaTags[pos++] = false;
296        }
297
298        // erase all tags from the map that are between two quotes
299        {
300            std::map<size_t, bool>::iterator it = luaTags.begin();
301            std::map<size_t, bool>::iterator it2 = it;
302            bool bBetweenQuotes = false;
303            size_t pos = 0;
304            while ((pos = getNextQuote(text, pos)) != std::string::npos)
305            {
306                while ((it != luaTags.end()) && (it->first < pos))
307                {
308                    if (bBetweenQuotes)
309                    {
310                        it2++;
311                        if (it->second && !(it2->second) && it2->first < pos)
312                            it = ++it2;
313                        else
314                            luaTags.erase(it++);
315                    }
316                    else
317                        ++it;
318                }
319                bBetweenQuotes = !bBetweenQuotes;
320                pos++;
321            }
322        }
323
324        // check whether on every opening <?lua tag a closing ?> tag follows
325        {
326            bool expectedValue = true;
327            for (std::map<size_t, bool>::iterator it = luaTags.begin(); it != luaTags.end(); ++it)
328            {
329                if (it->second == expectedValue)
330                    expectedValue = !expectedValue;
331                else
332                {
333                    expectedValue = false;
334                    break;
335                }
336            }
337            if (!expectedValue)
338            {
339                orxout(internal_error, context::loader) << "Error in level file" << endl;
340                // TODO: error handling
341                return false;
342            }
343        }
344
345        return true;
346    }
347
348    std::string Loader::replaceLuaTags(const std::string& text)
349    {
350        // create a map with all lua tags
351        std::map<size_t, bool> luaTags;
352        if (!getLuaTags(text, luaTags))
353            return "";
354
355        // Use a stringstream object to speed up the parsing
356        std::ostringstream output;
357
358        // cut the original string into pieces and put them together with print() instead of lua tags
359        {
360            std::map<size_t, bool>::iterator it = luaTags.begin();
361            bool bInPrintFunction = true;
362            size_t start = 0;
363            size_t end = 0;
364
365            do
366            {
367                if (it != luaTags.end())
368                    end = (it++)->first;
369                else
370                    end = std::string::npos;
371
372                unsigned int equalSignCounter = 0;
373
374                if (bInPrintFunction)
375                {
376                    // count ['='[ and ]'='] and replace tags with print([[ and ]])
377                    const std::string& temp = text.substr(start, end - start);
378                    {
379                    size_t pos = 0;
380                    while ((pos = temp.find('[', pos)) != std::string::npos)
381                    {
382                        unsigned int tempCounter = 1;
383                        size_t tempPos = pos++;
384                        while (temp[++tempPos] == '=')
385                        {
386                            tempCounter++;
387                        }
388                        if (temp[tempPos] != '[')
389                        {
390                            tempCounter = 0;
391                        }
392                        else if (tempCounter == 0)
393                        {
394                            tempCounter = 1;
395                        }
396                        if (tempCounter > equalSignCounter)
397                            equalSignCounter = tempCounter;
398                        }
399                    }
400                    {
401                        size_t pos = 0;
402                        while ((pos = temp.find(']', pos)) != std::string::npos)
403                        {
404                            unsigned int tempCounter = 1;
405                            size_t tempPos = pos++;
406                            while (temp[++tempPos] == '=')
407                            {
408                                tempCounter++;
409                            }
410                            if (temp[tempPos] != ']')
411                            {
412                                tempCounter = 0;
413                            }
414                            else if (tempCounter == 0)
415                            {
416                                tempCounter = 1;
417                            }
418                            if (tempCounter > equalSignCounter)
419                                equalSignCounter = tempCounter;
420                        }
421                    }
422                    std::string equalSigns;
423                    for (unsigned int i = 0; i < equalSignCounter; i++)
424                    {
425                        equalSigns += '=';
426                    }
427                    output << "print([" + equalSigns + '[' + temp + ']' + equalSigns +"])";
428                    start = end + 5;
429                }
430                else
431                {
432                    output << text.substr(start, end - start);
433                    start = end + 2;
434                }
435
436                bInPrintFunction = !bInPrintFunction;
437            }
438            while (end != std::string::npos);
439        }
440
441        return output.str();
442    }
443
444    std::string Loader::removeLuaTags(const std::string& text)
445    {
446        // create a map with all lua tags
447        std::map<size_t, bool> luaTags;
448        if (!getLuaTags(text, luaTags))
449            return "";
450
451        // Use a stringstream object to speed up the concatenation
452        std::ostringstream output;
453
454        // cut the original string into pieces and only write the non Lua parts
455        std::map<size_t, bool>::iterator it = luaTags.begin();
456        bool bLuaCode = false;
457        size_t start = 0;
458        size_t end = 0;
459
460        do
461        {
462            if (it != luaTags.end())
463                end = (it++)->first;
464            else
465                end = std::string::npos;
466
467            if (!bLuaCode)
468            {
469                output << text.substr(start, end - start);
470                start = end + 5;
471            }
472            else
473                start = end + 2;
474
475            bLuaCode = !bLuaCode;
476        }
477        while (end != std::string::npos);
478
479        return output.str();
480    }
481}
Note: See TracBrowser for help on using the repository browser.