remove_detached_inventory: Fix segfault during mod load
[oweals/minetest.git] / src / inventory.cpp
index b4d1b4dd931b30ec1ab55905b42ad2a1faeeb4ed..40dc602d0e855883a1d0a42549bdedb117c1fcda 100644 (file)
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "itemdef.h"
 #include "util/strfnd.h"
+#include "content_mapnode.h" // For loading legacy MaterialItems
+#include "nameidmapping.h" // For loading legacy MaterialItems
 #include "util/serialize.h"
 #include "util/string.h"
 
@@ -31,6 +33,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        ItemStack
 */
 
+static content_t content_translate_from_19_to_internal(content_t c_from)
+{
+       for (const auto &tt : trans_table_19) {
+               if(tt[1] == c_from) {
+                       return tt[0];
+               }
+       }
+       return c_from;
+}
+
 ItemStack::ItemStack(const std::string &name_, u16 count_,
                u16 wear_, IItemDefManager *itemdef) :
        name(itemdef->getAlias(name_)),
@@ -45,9 +57,7 @@ ItemStack::ItemStack(const std::string &name_, u16 count_,
 
 void ItemStack::serialize(std::ostream &os) const
 {
-       DSTACK(FUNCTION_NAME);
-
-       if(empty())
+       if (empty())
                return;
 
        // Check how many parts of the itemstring are needed
@@ -72,8 +82,6 @@ void ItemStack::serialize(std::ostream &os) const
 
 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
 {
-       DSTACK(FUNCTION_NAME);
-
        clear();
 
        // Read name
@@ -85,35 +93,139 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
        if(!tmp.empty())
                throw SerializationError("Unexpected text after item name");
 
-       do { // This loop is just to allow "break;"
-               // The real thing
+       if(name == "MaterialItem")
+       {
+               // Obsoleted on 2011-07-30
+
+               u16 material;
+               is>>material;
+               u16 materialcount;
+               is>>materialcount;
+               // Convert old materials
+               if(material <= 0xff)
+                       material = content_translate_from_19_to_internal(material);
+               if(material > 0xfff)
+                       throw SerializationError("Too large material number");
+               // Convert old id to name
+               NameIdMapping legacy_nimap;
+               content_mapnode_get_name_id_mapping(&legacy_nimap);
+               legacy_nimap.getName(material, name);
+               if(name.empty())
+                       name = "unknown_block";
+               if (itemdef)
+                       name = itemdef->getAlias(name);
+               count = materialcount;
+       }
+       else if(name == "MaterialItem2")
+       {
+               // Obsoleted on 2011-11-16
+
+               u16 material;
+               is>>material;
+               u16 materialcount;
+               is>>materialcount;
+               if(material > 0xfff)
+                       throw SerializationError("Too large material number");
+               // Convert old id to name
+               NameIdMapping legacy_nimap;
+               content_mapnode_get_name_id_mapping(&legacy_nimap);
+               legacy_nimap.getName(material, name);
+               if(name.empty())
+                       name = "unknown_block";
+               if (itemdef)
+                       name = itemdef->getAlias(name);
+               count = materialcount;
+       }
+       else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
+                       || name == "craft" || name == "CraftItem")
+       {
+               // Obsoleted on 2012-01-07
+
+               std::string all;
+               std::getline(is, all, '\n');
+               // First attempt to read inside ""
+               Strfnd fnd(all);
+               fnd.next("\"");
+               // If didn't skip to end, we have ""s
+               if(!fnd.at_end()){
+                       name = fnd.next("\"");
+               } else { // No luck, just read a word then
+                       fnd.start(all);
+                       name = fnd.next(" ");
+               }
+               fnd.skip_over(" ");
+               if (itemdef)
+                       name = itemdef->getAlias(name);
+               count = stoi(trim(fnd.next("")));
+               if(count == 0)
+                       count = 1;
+       }
+       else if(name == "MBOItem")
+       {
+               // Obsoleted on 2011-10-14
+               throw SerializationError("MBOItem not supported anymore");
+       }
+       else if(name == "tool" || name == "ToolItem")
+       {
+               // Obsoleted on 2012-01-07
 
-               // Apply item aliases
+               std::string all;
+               std::getline(is, all, '\n');
+               // First attempt to read inside ""
+               Strfnd fnd(all);
+               fnd.next("\"");
+               // If didn't skip to end, we have ""s
+               if(!fnd.at_end()){
+                       name = fnd.next("\"");
+               } else { // No luck, just read a word then
+                       fnd.start(all);
+                       name = fnd.next(" ");
+               }
+               count = 1;
+               // Then read wear
+               fnd.skip_over(" ");
                if (itemdef)
                        name = itemdef->getAlias(name);
+               wear = stoi(trim(fnd.next("")));
+       }
+       else
+       {
+               do  // This loop is just to allow "break;"
+               {
+                       // The real thing
+
+                       // Apply item aliases
+                       if (itemdef)
+                               name = itemdef->getAlias(name);
+
+                       // Read the count
+                       std::string count_str;
+                       std::getline(is, count_str, ' ');
+                       if (count_str.empty()) {
+                               count = 1;
+                               break;
+                       }
 
-               // Read the count
-               std::string count_str;
-               std::getline(is, count_str, ' ');
-               if (count_str.empty()) {
-                       count = 1;
-                       break;
-               } else {
                        count = stoi(count_str);
-               }
 
-               // Read the wear
-               std::string wear_str;
-               std::getline(is, wear_str, ' ');
-               if (wear_str.empty())
-                       break;
-               else
+                       // Read the wear
+                       std::string wear_str;
+                       std::getline(is, wear_str, ' ');
+                       if(wear_str.empty())
+                               break;
+
                        wear = stoi(wear_str);
 
-               // Read metadata
-               metadata.deSerialize(is);
+                       // Read metadata
+                       metadata.deSerialize(is);
 
-       } while(false);
+                       // In case fields are added after metadata, skip space here:
+                       //std::getline(is, tmp, ' ');
+                       //if(!tmp.empty())
+                       //      throw SerializationError("Unexpected text after metadata");
+
+               } while(false);
+       }
 
        if (name.empty() || count == 0)
                clear();
@@ -135,11 +247,8 @@ std::string ItemStack::getItemString() const
 }
 
 
-ItemStack ItemStack::addItem(const ItemStack &newitem_,
-               IItemDefManager *itemdef)
+ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
 {
-       ItemStack newitem = newitem_;
-
        // If the item is empty or the position invalid, bail out
        if(newitem.empty())
        {
@@ -175,11 +284,10 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_,
        return newitem;
 }
 
-bool ItemStack::itemFits(const ItemStack &newitem_,
+bool ItemStack::itemFits(ItemStack newitem,
                ItemStack *restitem,
                IItemDefManager *itemdef) const
 {
-       ItemStack newitem = newitem_;
 
        // If the item is empty or the position invalid, bail out
        if(newitem.empty())
@@ -203,7 +311,6 @@ bool ItemStack::itemFits(const ItemStack &newitem_,
                newitem.clear();
        }
        // Else the item does not fit fully. Return the rest.
-       // the rest.
        else
        {
                u16 freespace = freeSpace(itemdef);
@@ -212,6 +319,7 @@ bool ItemStack::itemFits(const ItemStack &newitem_,
 
        if(restitem)
                *restitem = newitem;
+
        return newitem.empty();
 }
 
@@ -253,23 +361,17 @@ ItemStack ItemStack::peekItem(u32 peekcount) const
 InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
        m_name(name),
        m_size(size),
-       m_width(0),
        m_itemdef(itemdef)
 {
        clearItems();
 }
 
-InventoryList::~InventoryList()
-{
-}
-
 void InventoryList::clearItems()
 {
        m_items.clear();
 
-       for(u32 i=0; i<m_size; i++)
-       {
-               m_items.push_back(ItemStack());
+       for (u32 i=0; i < m_size; i++) {
+               m_items.emplace_back();
        }
 
        //setDirty(true);
@@ -298,15 +400,10 @@ void InventoryList::serialize(std::ostream &os) const
 
        os<<"Width "<<m_width<<"\n";
 
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               const ItemStack &item = m_items[i];
-               if(item.empty())
-               {
+       for (const auto &item : m_items) {
+               if (item.empty()) {
                        os<<"Empty";
-               }
-               else
-               {
+               } else {
                        os<<"Item ";
                        item.serialize(os);
                }
@@ -324,8 +421,7 @@ void InventoryList::deSerialize(std::istream &is)
        u32 item_i = 0;
        m_width = 0;
 
-       for(;;)
-       {
+       while (is.good()) {
                std::string line;
                std::getline(is, line, '\n');
 
@@ -335,17 +431,14 @@ void InventoryList::deSerialize(std::istream &is)
                std::string name;
                std::getline(iss, name, ' ');
 
-               if(name == "EndInventoryList")
-               {
-                       break;
-               }
+               if (name == "EndInventoryList")
+                       return;
+
                // This is a temporary backwards compatibility fix
-               else if(name == "end")
-               {
-                       break;
-               }
-               else if(name == "Width")
-               {
+               if (name == "end")
+                       return;
+
+               if (name == "Width") {
                        iss >> m_width;
                        if (iss.fail())
                                throw SerializationError("incorrect width property");
@@ -365,6 +458,14 @@ void InventoryList::deSerialize(std::istream &is)
                        m_items[item_i++].clear();
                }
        }
+
+       // Contents given to deSerialize() were not terminated properly: throw error.
+
+       std::ostringstream ss;
+       ss << "Malformatted inventory list. list="
+               << m_name << ", read " << item_i << " of " << getSize()
+               << " ItemStacks." << std::endl;
+       throw SerializationError(ss.str());
 }
 
 InventoryList::InventoryList(const InventoryList &other)
@@ -392,14 +493,9 @@ bool InventoryList::operator == (const InventoryList &other) const
                return false;
        if(m_name != other.m_name)
                return false;
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               ItemStack s1 = m_items[i];
-               ItemStack s2 = other.m_items[i];
-               if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
-                               s1.metadata != s2.metadata)
+       for (u32 i = 0; i < m_items.size(); i++)
+               if (m_items[i] != other.m_items[i])
                        return false;
-       }
 
        return true;
 }
@@ -422,9 +518,8 @@ u32 InventoryList::getWidth() const
 u32 InventoryList::getUsedSlots() const
 {
        u32 num = 0;
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               if(!m_items[i].empty())
+       for (const auto &m_item : m_items) {
+               if (!m_item.empty())
                        num++;
        }
        return num;
@@ -540,23 +635,20 @@ bool InventoryList::roomForItem(const ItemStack &item_) const
        return false;
 }
 
-bool InventoryList::containsItem(const ItemStack &item) const
+bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
 {
        u32 count = item.count;
-       if(count == 0)
+       if (count == 0)
                return true;
-       for(std::vector<ItemStack>::const_reverse_iterator
-                       i = m_items.rbegin();
-                       i != m_items.rend(); ++i)
-       {
-               if(count == 0)
+
+       for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
+               if (count == 0)
                        break;
-               if(i->name == item.name)
-               {
-                       if(i->count >= count)
+               if (i->name == item.name && (!match_meta || (i->metadata == item.metadata))) {
+                       if (i->count >= count)
                                return true;
-                       else
-                               count -= i->count;
+
+                       count -= i->count;
                }
        }
        return false;
@@ -565,15 +657,11 @@ bool InventoryList::containsItem(const ItemStack &item) const
 ItemStack InventoryList::removeItem(const ItemStack &item)
 {
        ItemStack removed;
-       for(std::vector<ItemStack>::reverse_iterator
-                       i = m_items.rbegin();
-                       i != m_items.rend(); ++i)
-       {
-               if(i->name == item.name)
-               {
+       for (auto i = m_items.rbegin(); i != m_items.rend(); ++i) {
+               if (i->name == item.name) {
                        u32 still_to_remove = item.count - removed.count;
                        removed.addItem(i->takeItem(still_to_remove), m_itemdef);
-                       if(removed.count == item.count)
+                       if (removed.count == item.count)
                                break;
                }
        }
@@ -686,9 +774,8 @@ Inventory::~Inventory()
 void Inventory::clear()
 {
        m_dirty = true;
-       for(u32 i=0; i<m_lists.size(); i++)
-       {
-               delete m_lists[i];
+       for (auto &m_list : m_lists) {
+               delete m_list;
        }
        m_lists.clear();
 }
@@ -696,11 +783,8 @@ void Inventory::clear()
 void Inventory::clearContents()
 {
        m_dirty = true;
-       for(u32 i=0; i<m_lists.size(); i++)
-       {
-               InventoryList *list = m_lists[i];
-               for(u32 j=0; j<list->getSize(); j++)
-               {
+       for (InventoryList *list : m_lists) {
+               for (u32 j=0; j<list->getSize(); j++) {
                        list->deleteItem(j);
                }
        }
@@ -726,9 +810,8 @@ Inventory & Inventory::operator = (const Inventory &other)
                m_dirty = true;
                clear();
                m_itemdef = other.m_itemdef;
-               for(u32 i=0; i<other.m_lists.size(); i++)
-               {
-                       m_lists.push_back(new InventoryList(*other.m_lists[i]));
+               for (InventoryList *list : other.m_lists) {
+                       m_lists.push_back(new InventoryList(*list));
                }
        }
        return *this;
@@ -749,9 +832,7 @@ bool Inventory::operator == (const Inventory &other) const
 
 void Inventory::serialize(std::ostream &os) const
 {
-       for(u32 i=0; i<m_lists.size(); i++)
-       {
-               InventoryList *list = m_lists[i];
+       for (InventoryList *list : m_lists) {
                os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
                list->serialize(os);
        }
@@ -763,8 +844,7 @@ void Inventory::deSerialize(std::istream &is)
 {
        clear();
 
-       for(;;)
-       {
+       while (is.good()) {
                std::string line;
                std::getline(is, line, '\n');
 
@@ -773,17 +853,14 @@ void Inventory::deSerialize(std::istream &is)
                std::string name;
                std::getline(iss, name, ' ');
 
-               if(name == "EndInventory")
-               {
-                       break;
-               }
+               if (name == "EndInventory")
+                       return;
+
                // This is a temporary backwards compatibility fix
-               else if(name == "end")
-               {
-                       break;
-               }
-               else if(name == "List")
-               {
+               if (name == "end")
+                       return;
+
+               if (name == "List") {
                        std::string listname;
                        u32 listsize;
 
@@ -800,6 +877,13 @@ void Inventory::deSerialize(std::istream &is)
                        throw SerializationError("invalid inventory specifier: " + name);
                }
        }
+
+       // Contents given to deSerialize() were not terminated properly: throw error.
+
+       std::ostringstream ss;
+       ss << "Malformatted inventory (damaged?). "
+               << m_lists.size() << " lists read." << std::endl;
+       throw SerializationError(ss.str());
 }
 
 InventoryList * Inventory::addList(const std::string &name, u32 size)
@@ -815,15 +899,14 @@ InventoryList * Inventory::addList(const std::string &name, u32 size)
                }
                return m_lists[i];
        }
-       else
-       {
-               //don't create list with invalid name
-               if (name.find(" ") != std::string::npos) return NULL;
 
-               InventoryList *list = new InventoryList(name, size, m_itemdef);
-               m_lists.push_back(list);
-               return list;
-       }
+
+       //don't create list with invalid name
+       if (name.find(' ') != std::string::npos) return NULL;
+
+       InventoryList *list = new InventoryList(name, size, m_itemdef);
+       m_lists.push_back(list);
+       return list;
 }
 
 InventoryList * Inventory::getList(const std::string &name)
@@ -837,9 +920,7 @@ InventoryList * Inventory::getList(const std::string &name)
 std::vector<const InventoryList*> Inventory::getLists()
 {
        std::vector<const InventoryList*> lists;
-       for(u32 i=0; i<m_lists.size(); i++)
-       {
-               InventoryList *list = m_lists[i];
+       for (auto list : m_lists) {
                lists.push_back(list);
        }
        return lists;