Change mainmenu texture handling + small misc changes
authorKahrl <kahrl@gmx.net>
Tue, 20 Aug 2013 20:38:14 +0000 (22:38 +0200)
committerKahrl <kahrl@gmx.net>
Tue, 10 Sep 2013 22:08:56 +0000 (00:08 +0200)
Texture names must now be escaped in formspec elements image[],
background[], image_button[], image_button_exit[].

Instead of special-case handling of texture loading (and unloading
which was missing) in guiFormSpecMenu.cpp, use the newly created
ISimpleTextureSource interface which is a minimal subset of
ITextureSource. There is an implementation of this interface
used by GUIEngine (MenuTextureSource).

Fix an off-by-one bug in unescape_string; it caused requests for a
texture called "\0".

builtin/gamemgr.lua
builtin/mainmenu.lua
builtin/mm_menubar.lua
builtin/modstore.lua
src/game.cpp
src/guiEngine.cpp
src/guiEngine.h
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/tile.h
src/util/string.h

index 8409bff0335ced4e9f03a668b623d7c29918c96e..7a5e9790fa0bc67c307e4b33a765b920c2b69d79 100644 (file)
@@ -225,7 +225,8 @@ function gamemgr.tab()
                if current_game.menuicon_path ~= nil and
                        current_game.menuicon_path ~= "" then
                        retval = retval .. 
-                               "image[5.8,-0.25;2,2;" .. current_game.menuicon_path .. "]"
+                               "image[5.8,-0.25;2,2;" ..
+                               engine.formspec_escape(current_game.menuicon_path) .. "]"
                end
                
                retval = retval ..
@@ -251,7 +252,8 @@ function gamemgr.dialog_edit_game()
                if current_game.menuicon_path ~= nil and
                        current_game.menuicon_path ~= "" then
                        retval = retval .. 
-                               "image[5.25,0;2,2;" .. current_game.menuicon_path .. "]"                        
+                               "image[5.25,0;2,2;" ..
+                               engine.formspec_escape(current_game.menuicon_path) .. "]"
                end
                
                retval = retval .. 
index 787a5e28057e0dab44a3c6f837e3f8e47aaf8557..5a1b6e9294271bab9929a67047a3f76c8a5015fa 100644 (file)
@@ -1047,16 +1047,17 @@ function tabbuilder.tab_texture_packs()
        return  retval ..
                        menu.render_texture_pack_list(list) ..
                        ";" .. index .. "]" ..
-                       "image[0.65,0.25;4.0,3.7;"..(screenfile or no_screenshot).."]"..
+                       "image[0.65,0.25;4.0,3.7;"..engine.formspec_escape(screenfile or no_screenshot).."]"..
                        "textarea[1.0,3.25;3.7,1.5;;"..engine.formspec_escape(infotext or "")..";]"
 end
 
 --------------------------------------------------------------------------------
 function tabbuilder.tab_credits()
+       local logofile = menu.defaulttexturedir .. "logo.png"
        return  "vertlabel[0,-0.5;CREDITS]" ..
                        "label[0.5,3;Minetest " .. engine.get_version() .. "]" ..
                        "label[0.5,3.3;http://minetest.net]" .. 
-                       "image[0.5,1;" .. menu.defaulttexturedir .. "logo.png]" ..
+                       "image[0.5,1;" .. engine.formspec_escape(logofile) .. "]" ..
                        "textlist[3.5,-0.25;8.5,5.8;list_credits;" ..
                        "#FFFF00" .. fgettext("Core Developers") .."," ..
                        "Perttu Ahola (celeron55) <celeron55@gmail.com>,"..
index faba92a8f65b7ec5f493534dca05e5e25d618828..c3ddbb2896faaea9af9b02e01b42dd0660917755 100644 (file)
@@ -51,7 +51,8 @@ function menubar.refresh()
 
                        menubar.formspec = menubar.formspec ..
                                "image_button[" .. buttonpos ..  ",5.7;1.3,1.3;"  ..
-                               gamemgr.games[i].menuicon_path .. ";" .. btn_name .. ";;true;false]"
+                               engine.formspec_escape(gamemgr.games[i].menuicon_path) .. ";" ..
+                               btn_name .. ";;true;false]"
                else
                
                        local part1 = gamemgr.games[i].id:sub(1,5)
@@ -75,4 +76,4 @@ function menubar.refresh()
                
                table.insert(menubar.buttons,toadd)
        end
-end
\ No newline at end of file
+end
index 2f967c9b16a2a50f248d4aad56b60762dc486603..43f7759ad0fdff9a95292a9bbdd4e0a0d09cdc63 100644 (file)
@@ -227,7 +227,7 @@ function modstore.getmodlist(list)
                        end
                        
                        retval = retval .. "image[0,".. screenshot_ypos .. ";3,2;" .. 
-                                       list.data[i].texturename .. "]"
+                                       engine.formspec_escape(list.data[i].texturename) .. "]"
                        
                        --title + author
                        retval = retval .."label[2.75," .. screenshot_ypos .. ";" .. 
index 5d836578110f92face3d4425eade6c968b483941..f313ae28f77f664b4f784e88880611e37d6862db 100644 (file)
@@ -1720,7 +1720,7 @@ void the_game(
                        GUIFormSpecMenu *menu =
                                new GUIFormSpecMenu(device, guiroot, -1,
                                        &g_menumgr,
-                                       &client, gamedef);
+                                       &client, gamedef, tsrc);
 
                        InventoryLocation inventoryloc;
                        inventoryloc.setCurrentPlayer();
@@ -2259,7 +2259,7 @@ void the_game(
                                                GUIFormSpecMenu *menu =
                                                                new GUIFormSpecMenu(device, guiroot, -1,
                                                                                &g_menumgr,
-                                                                               &client, gamedef);
+                                                                               &client, gamedef, tsrc);
                                                menu->setFormSource(current_formspec);
                                                menu->setTextDest(current_textdest);
                                                menu->drop();
@@ -2755,7 +2755,7 @@ void the_game(
                                        GUIFormSpecMenu *menu =
                                                new GUIFormSpecMenu(device, guiroot, -1,
                                                        &g_menumgr,
-                                                       &client, gamedef);
+                                                       &client, gamedef, tsrc);
                                        menu->setFormSpec(meta->getString("formspec"),
                                                        inventoryloc);
                                        menu->setFormSource(new NodeMetadataFormSource(
index f00cd039c59c3634d0f1d2af235996b179bdf15a..547f393a49ef76375433ee1da8b775ce1990c96b 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiMainMenu.h"
 #include "sound.h"
 #include "sound_openal.h"
+#include "clouds.h"
 
 #include <IGUIStaticText.h>
 #include <ICameraSceneNode.h>
@@ -36,6 +37,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <curl/curl.h>
 #endif
 
+/******************************************************************************/
+/** TextDestGuiEngine                                                         */
 /******************************************************************************/
 TextDestGuiEngine::TextDestGuiEngine(GUIEngine* engine)
 {
@@ -54,6 +57,38 @@ void TextDestGuiEngine::gotText(std::wstring text)
        m_engine->getScriptIface()->handleMainMenuEvent(wide_to_narrow(text));
 }
 
+/******************************************************************************/
+/** MenuTextureSource                                                         */
+/******************************************************************************/
+MenuTextureSource::MenuTextureSource(video::IVideoDriver *driver)
+{
+       m_driver = driver;
+}
+
+/******************************************************************************/
+MenuTextureSource::~MenuTextureSource()
+{
+       for (std::set<std::string>::iterator it = m_to_delete.begin();
+                       it != m_to_delete.end(); ++it) {
+               const char *tname = (*it).c_str();
+               video::ITexture *texture = m_driver->getTexture(tname);
+               m_driver->removeTexture(texture);
+       }
+}
+
+/******************************************************************************/
+video::ITexture* MenuTextureSource::getTexture(const std::string &name, u32 *id)
+{
+       if(id)
+               *id = 0;
+       if(name.empty())
+               return NULL;
+       m_to_delete.insert(name);
+       return m_driver->getTexture(name.c_str());
+}
+
+/******************************************************************************/
+/** MenuMusicFetcher                                                          */
 /******************************************************************************/
 void MenuMusicFetcher::fetchSounds(const std::string &name,
                        std::set<std::string> &dst_paths,
@@ -74,6 +109,8 @@ void MenuMusicFetcher::fetchSounds(const std::string &name,
                dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg");
 }
 
+/******************************************************************************/
+/** GUIEngine                                                                 */
 /******************************************************************************/
 GUIEngine::GUIEngine(  irr::IrrlichtDevice* dev,
                                                gui::IGUIElement* parent,
@@ -86,6 +123,7 @@ GUIEngine::GUIEngine(        irr::IrrlichtDevice* dev,
        m_menumanager(menumgr),
        m_smgr(smgr),
        m_data(data),
+       m_texture_source(NULL),
        m_sound_manager(NULL),
        m_formspecgui(0),
        m_buttonhandler(0),
@@ -105,6 +143,9 @@ GUIEngine::GUIEngine(       irr::IrrlichtDevice* dev,
        // is deleted by guiformspec!
        m_buttonhandler = new TextDestGuiEngine(this);
 
+       //create texture source
+       m_texture_source = new MenuTextureSource(m_device->getVideoDriver());
+
        //create soundmanager
        MenuMusicFetcher soundfetcher;
 #if USE_SOUND
@@ -132,7 +173,8 @@ GUIEngine::GUIEngine(       irr::IrrlichtDevice* dev,
                                                                -1,
                                                                m_menumanager,
                                                                0 /* &client */,
-                                                               0 /* gamedef */);
+                                                               0 /* gamedef */,
+                                                               m_texture_source);
 
        m_menu->allowClose(false);
        m_menu->lockSize(true,v2u32(800,600));
@@ -264,11 +306,13 @@ GUIEngine::~GUIEngine()
 
        m_irr_toplefttext->setText(L"");
 
-       //initialize texture pointers
+       //clean up texture pointers
        for (unsigned int i = 0; i < TEX_LAYER_MAX; i++) {
                if (m_textures[i] != 0)
                        driver->removeTexture(m_textures[i]);
        }
+
+       delete m_texture_source;
        
        if (m_cloud.clouds)
                m_cloud.clouds->drop();
index 6b7d3b6ed11ef24eaac418cbdc06c3985d8cbb75..4844593954c98fcf679cc12ed48049eac8b83c4e 100644 (file)
@@ -25,17 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 /******************************************************************************/
 #include "irrlichttypes.h"
 #include "modalMenu.h"
-#include "clouds.h"
 #include "guiFormSpecMenu.h"
 #include "sound.h"
+#include "tile.h"
 
 /******************************************************************************/
 /* Typedefs and macros                                                        */
 /******************************************************************************/
-#define MAX_MENUBAR_BTN_COUNT  10
-#define MAX_MENUBAR_BTN_ID             256
-#define MIN_MENUBAR_BTN_ID             (MAX_MENUBAR_BTN_ID - MAX_MENUBAR_BTN_COUNT)
-
 /** texture layer ids */
 typedef enum {
        TEX_LAYER_BACKGROUND = 0,
@@ -50,8 +46,8 @@ typedef enum {
 /******************************************************************************/
 class GUIEngine;
 class MainMenuScripting;
+class Clouds;
 struct MainMenuData;
-struct SimpleSoundSpec;
 
 /******************************************************************************/
 /* declarations                                                               */
@@ -66,6 +62,7 @@ public:
         * @param engine the engine data is transmitted for further processing
         */
        TextDestGuiEngine(GUIEngine* engine);
+
        /**
         * receive fields transmitted by guiFormSpecMenu
         * @param fields map containing formspec field elements currently active
@@ -77,18 +74,58 @@ public:
         * @param text textual representation of event
         */
        void gotText(std::wstring text);
+
 private:
        /** target to transmit data to */
        GUIEngine* m_engine;
 };
 
+/** GUIEngine specific implementation of ISimpleTextureSource */
+class MenuTextureSource : public ISimpleTextureSource
+{
+public:
+       /**
+        * default constructor
+        * @param driver the video driver to load textures from
+        */
+       MenuTextureSource(video::IVideoDriver *driver);
+
+       /**
+        * destructor, removes all loaded textures
+        */
+       virtual ~MenuTextureSource();
+
+       /**
+        * get a texture, loading it if required
+        * @param name path to the texture
+        * @param id receives the texture ID, always 0 in this implementation
+        */
+       video::ITexture* getTexture(const std::string &name, u32 *id = NULL);
+
+private:
+       /** driver to get textures from */
+       video::IVideoDriver *m_driver;
+       /** set of texture names to delete */
+       std::set<std::string> m_to_delete;
+};
+
+/** GUIEngine specific implementation of OnDemandSoundFetcher */
 class MenuMusicFetcher: public OnDemandSoundFetcher
 {
-       std::set<std::string> m_fetched;
 public:
+       /**
+        * get sound file paths according to sound name
+        * @param name sound name
+        * @param dst_paths receives possible paths to sound files
+        * @param dst_datas receives binary sound data (not used here)
+        */
        void fetchSounds(const std::string &name,
                        std::set<std::string> &dst_paths,
                        std::set<std::string> &dst_datas);
+
+private:
+       /** set of fetched sound names */
+       std::set<std::string> m_fetched;
 };
 
 /** implementation of main menu based uppon formspecs */
@@ -150,6 +187,8 @@ private:
        scene::ISceneManager*   m_smgr;
        /** pointer to data beeing transfered back to main game handling */
        MainMenuData*                   m_data;
+       /** pointer to texture source */
+       ISimpleTextureSource*   m_texture_source;
        /** pointer to soundmanager*/
        ISoundManager*                  m_sound_manager;
 
@@ -167,7 +206,7 @@ private:
        bool                                    m_startgame;
 
        /** scripting interface */
-       MainMenuScripting*                      m_script;
+       MainMenuScripting*              m_script;
 
        /** script basefolder */
        std::string                             m_scriptdir;
index bc33143a46887f134b63cc402e00814a08fbe64c..28cb6740e3d8a2dee8ff44252cb3bb19d2062e98 100644 (file)
@@ -69,12 +69,14 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
                gui::IGUIElement* parent, s32 id,
                IMenuManager *menumgr,
                InventoryManager *invmgr,
-               IGameDef *gamedef
+               IGameDef *gamedef,
+               ISimpleTextureSource *tsrc
 ):
        GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
        m_device(dev),
        m_invmgr(invmgr),
        m_gamedef(gamedef),
+       m_tsrc(tsrc),
        m_form_src(NULL),
        m_text_dst(NULL),
        m_selected_item(NULL),
@@ -483,7 +485,7 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element) {
        if (parts.size() == 3) {
                std::vector<std::string> v_pos = split(parts[0],',');
                std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = parts[2];
+               std::string name = unescape_string(parts[2]);
 
                MY_CHECKPOS("image",0);
                MY_CHECKGEOM("image",1);
@@ -504,7 +506,7 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element) {
 
        if (parts.size() == 2) {
                std::vector<std::string> v_pos = split(parts[0],',');
-               std::string name = parts[1];
+               std::string name = unescape_string(parts[1]);
 
                MY_CHECKPOS("image",0);
 
@@ -605,7 +607,7 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) {
        if (parts.size() == 3) {
                std::vector<std::string> v_pos = split(parts[0],',');
                std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = parts[2];
+               std::string name = unescape_string(parts[2]);
 
                MY_CHECKPOS("background",0);
                MY_CHECKGEOM("background",1);
@@ -769,11 +771,6 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) {
                if (str_initial_selection != "")
                        e->setSelected(stoi(str_initial_selection.c_str())-1);
 
-               //if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
-               //      e->setSelected(data->listbox_selections[fname_w]);
-               //}
-
-               //m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
                m_fields.push_back(spec);
                return;
        }
@@ -1149,6 +1146,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,std:
                if(data->bp_set != 2)
                        errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
 
+               image_name = unescape_string(image_name);
+               pressed_image_name = unescape_string(pressed_image_name);
                label = unescape_string(label);
 
                std::wstring wlabel = narrow_to_wide(label.c_str());
@@ -1165,24 +1164,10 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,std:
 
                video::ITexture *texture = 0;
                video::ITexture *pressed_texture = 0;
-               //if there's no gamedef specified try to get direct
-               //TODO check for possible texture leak
-               if (m_gamedef != 0) {
-                       texture = m_gamedef->tsrc()->getTexture(image_name);
-                       if ((parts.size() == 8)) {
-                               pressed_texture = m_gamedef->tsrc()->getTexture(pressed_image_name);
-                       }
-               } else {
-                       if (fs::PathExists(image_name)) {
-                               texture = Environment->getVideoDriver()->getTexture(image_name.c_str());
-                               m_Textures.push_back(texture);
-                       }
-                       if (fs::PathExists(pressed_image_name)) {
-                               pressed_texture = Environment->getVideoDriver()->getTexture(pressed_image_name.c_str());
-                               m_Textures.push_back(pressed_texture);
-                       }
-               }
-               if (parts.size() < 8)
+               texture = m_tsrc->getTexture(image_name);
+               if (parts.size() == 8)
+                       pressed_texture = m_tsrc->getTexture(pressed_image_name);
+               else
                        pressed_texture = texture;
 
                gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
@@ -1797,15 +1782,7 @@ void GUIFormSpecMenu::drawMenu()
        for(u32 i=0; i<m_backgrounds.size(); i++)
        {
                const ImageDrawSpec &spec = m_backgrounds[i];
-               video::ITexture *texture = 0;
-
-               if (m_gamedef != 0)
-                       texture = m_gamedef->tsrc()->getTexture(spec.name);
-               else
-               {
-                       texture = driver->getTexture(spec.name.c_str());
-                       m_Textures.push_back(texture);
-               }
+               video::ITexture *texture = m_tsrc->getTexture(spec.name);
 
                if (texture != 0) {
                        // Image size on screen
@@ -1847,15 +1824,8 @@ void GUIFormSpecMenu::drawMenu()
        for(u32 i=0; i<m_images.size(); i++)
        {
                const ImageDrawSpec &spec = m_images[i];
-               video::ITexture *texture = 0;
+               video::ITexture *texture = m_tsrc->getTexture(spec.name);
 
-               if (m_gamedef != 0)
-                       texture = m_gamedef->tsrc()->getTexture(spec.name);
-               else
-               {
-                       texture = driver->getTexture(spec.name.c_str());
-                       m_Textures.push_back(texture);
-               }
                if (texture != 0) {
                        const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
                        // Image size on screen
index 73c21b72decab7f2ebb3fac5043272746bfc43ab..c244e458f08105141e2fa3f6a85cf3acf5dad119 100644 (file)
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class IGameDef;
 class InventoryManager;
+class ISimpleTextureSource;
 
 typedef enum {
        f_Button,
@@ -176,7 +177,8 @@ public:
                        gui::IGUIElement* parent, s32 id,
                        IMenuManager *menumgr,
                        InventoryManager *invmgr,
-                       IGameDef *gamedef
+                       IGameDef *gamedef,
+                       ISimpleTextureSource *tsrc
                        );
 
        ~GUIFormSpecMenu();
@@ -245,6 +247,7 @@ protected:
        irr::IrrlichtDevice* m_device;
        InventoryManager *m_invmgr;
        IGameDef *m_gamedef;
+       ISimpleTextureSource *m_tsrc;
 
        std::string m_formspec_string;
        InventoryLocation m_current_inventory_location;
@@ -302,8 +305,6 @@ private:
                bool key_escape;
        } fs_key_pendig;
 
-       std::vector<video::ITexture *> m_Textures;
-
        fs_key_pendig current_keys_pending;
 
        // Determine whether listbox click was double click
index 23c2143506b1fdafdf0e6eb20f19823b2dc0b5ed..90e180a48283e91f2b3b17e4690e22b7c82037cf 100644 (file)
@@ -82,22 +82,27 @@ struct TextureFromMeshParams
        TextureSource creates and caches textures.
 */
 
-class ITextureSource
+class ISimpleTextureSource
+{
+public:
+       ISimpleTextureSource(){}
+       virtual ~ISimpleTextureSource(){}
+       virtual video::ITexture* getTexture(
+                       const std::string &name, u32 *id = NULL) = 0;
+};
+
+class ITextureSource : public ISimpleTextureSource
 {
 public:
        ITextureSource(){}
        virtual ~ITextureSource(){}
-       virtual u32 getTextureId(const std::string &name){return 0;}
-       virtual u32 getTextureIdDirect(const std::string &name){return 0;}
-       virtual std::string getTextureName(u32 id){return "";}
-       virtual video::ITexture* getTexture(u32 id){return NULL;}
+       virtual u32 getTextureId(const std::string &name)=0;
+       virtual u32 getTextureIdDirect(const std::string &name)=0;
+       virtual std::string getTextureName(u32 id)=0;
+       virtual video::ITexture* getTexture(u32 id)=0;
        virtual video::ITexture* getTexture(
-                       const std::string &name, u32 *id = NULL){
-               if(id) *id = 0;
-               return NULL;
-       }
-       virtual IrrlichtDevice* getDevice()
-               {return NULL;}
+                       const std::string &name, u32 *id = NULL)=0;
+       virtual IrrlichtDevice* getDevice()=0;
        virtual bool isKnownSourceImage(const std::string &name)=0;
        virtual video::ITexture* generateTextureFromMesh(
                        const TextureFromMeshParams &params)=0;
@@ -108,23 +113,20 @@ class IWritableTextureSource : public ITextureSource
 public:
        IWritableTextureSource(){}
        virtual ~IWritableTextureSource(){}
-       virtual u32 getTextureId(const std::string &name){return 0;}
-       virtual u32 getTextureIdDirect(const std::string &name){return 0;}
-       virtual std::string getTextureName(u32 id){return "";}
-       virtual video::ITexture* getTexture(u32 id){return NULL;}
+       virtual u32 getTextureId(const std::string &name)=0;
+       virtual u32 getTextureIdDirect(const std::string &name)=0;
+       virtual std::string getTextureName(u32 id)=0;
+       virtual video::ITexture* getTexture(u32 id)=0;
        virtual video::ITexture* getTexture(
-                       const std::string &name, u32 *id = NULL){
-               if(id) *id = 0;
-               return NULL;
-       }
-       virtual IrrlichtDevice* getDevice(){return NULL;}
+                       const std::string &name, u32 *id = NULL)=0;
+       virtual IrrlichtDevice* getDevice()=0;
        virtual bool isKnownSourceImage(const std::string &name)=0;
+       virtual video::ITexture* generateTextureFromMesh(
+                       const TextureFromMeshParams &params)=0;
 
        virtual void processQueue()=0;
        virtual void insertSourceImage(const std::string &name, video::IImage *img)=0;
        virtual void rebuildImagesAndTextures()=0;
-       virtual video::ITexture* generateTextureFromMesh(
-                       const TextureFromMeshParams &params)=0;
 };
 
 IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
index d9390e33c5527a82f2e5593456e56cd660ff777a..7531600e37ebe2bfb3ca6d1bc58c6ec1c7aa60b1 100644 (file)
@@ -307,7 +307,7 @@ inline std::string unescape_string(std::string &s)
 {
        std::string res;
        
-       for (size_t i = 0; i <= s.length(); i++) {
+       for (size_t i = 0; i < s.length(); i++) {
                if (s[i] == '\\')
                        i++;
                res += s[i];