Fix invalid listname and listsize not handled correctly in set_size
[oweals/minetest.git] / src / inventory.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "inventory.h"
21 #include "serialization.h"
22 #include "debug.h"
23 #include <sstream>
24 #include "log.h"
25 #include "itemdef.h"
26 #include "strfnd.h"
27 #include "content_mapnode.h" // For loading legacy MaterialItems
28 #include "nameidmapping.h" // For loading legacy MaterialItems
29 #include "util/serialize.h"
30 #include "util/string.h"
31
32 /*
33         ItemStack
34 */
35
36 static content_t content_translate_from_19_to_internal(content_t c_from)
37 {
38         for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
39         {
40                 if(trans_table_19[i][1] == c_from)
41                 {
42                         return trans_table_19[i][0];
43                 }
44         }
45         return c_from;
46 }
47
48 // If the string contains spaces, quotes or control characters, encodes as JSON.
49 // Else returns the string unmodified.
50 static std::string serializeJsonStringIfNeeded(const std::string &s)
51 {
52         for(size_t i = 0; i < s.size(); ++i)
53         {
54                 if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
55                         return serializeJsonString(s);
56         }
57         return s;
58 }
59
60 // Parses a string serialized by serializeJsonStringIfNeeded.
61 static std::string deSerializeJsonStringIfNeeded(std::istream &is)
62 {
63         std::ostringstream tmp_os;
64         bool expect_initial_quote = true;
65         bool is_json = false;
66         bool was_backslash = false;
67         for(;;)
68         {
69                 char c = is.get();
70                 if(is.eof())
71                         break;
72                 if(expect_initial_quote && c == '"')
73                 {
74                         tmp_os << c;
75                         is_json = true;
76                 }
77                 else if(is_json)
78                 {
79                         tmp_os << c;
80                         if(was_backslash)
81                                 was_backslash = false;
82                         else if(c == '\\')
83                                 was_backslash = true;
84                         else if(c == '"')
85                                 break; // Found end of string
86                 }
87                 else
88                 {
89                         if(c == ' ')
90                         {
91                                 // Found end of word
92                                 is.unget();
93                                 break;
94                         }
95                         else
96                         {
97                                 tmp_os << c;
98                         }
99                 }
100                 expect_initial_quote = false;
101         }
102         if(is_json)
103         {
104                 std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
105                 return deSerializeJsonString(tmp_is);
106         }
107         else
108                 return tmp_os.str();
109 }
110
111
112 ItemStack::ItemStack(std::string name_, u16 count_,
113                 u16 wear_, std::string metadata_,
114                 IItemDefManager *itemdef)
115 {
116         name = itemdef->getAlias(name_);
117         count = count_;
118         wear = wear_;
119         metadata = metadata_;
120
121         if(name.empty() || count == 0)
122                 clear();
123         else if(itemdef->get(name).type == ITEM_TOOL)
124                 count = 1;
125 }
126
127 void ItemStack::serialize(std::ostream &os) const
128 {
129         DSTACK(__FUNCTION_NAME);
130
131         if(empty())
132                 return;
133
134         // Check how many parts of the itemstring are needed
135         int parts = 1;
136         if(count != 1)
137                 parts = 2;
138         if(wear != 0)
139                 parts = 3;
140         if(metadata != "")
141                 parts = 4;
142
143         os<<serializeJsonStringIfNeeded(name);
144         if(parts >= 2)
145                 os<<" "<<count;
146         if(parts >= 3)
147                 os<<" "<<wear;
148         if(parts >= 4)
149                 os<<" "<<serializeJsonStringIfNeeded(metadata);
150 }
151
152 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
153 {
154         DSTACK(__FUNCTION_NAME);
155
156         clear();
157
158         // Read name
159         name = deSerializeJsonStringIfNeeded(is);
160
161         // Skip space
162         std::string tmp;
163         std::getline(is, tmp, ' ');
164         if(!tmp.empty())
165                 throw SerializationError("Unexpected text after item name");
166         
167         if(name == "MaterialItem")
168         {
169                 // Obsoleted on 2011-07-30
170
171                 u16 material;
172                 is>>material;
173                 u16 materialcount;
174                 is>>materialcount;
175                 // Convert old materials
176                 if(material <= 0xff)
177                         material = content_translate_from_19_to_internal(material);
178                 if(material > 0xfff)
179                         throw SerializationError("Too large material number");
180                 // Convert old id to name
181                 NameIdMapping legacy_nimap;
182                 content_mapnode_get_name_id_mapping(&legacy_nimap);
183                 legacy_nimap.getName(material, name);
184                 if(name == "")
185                         name = "unknown_block";
186                 name = itemdef->getAlias(name);
187                 count = materialcount;
188         }
189         else if(name == "MaterialItem2")
190         {
191                 // Obsoleted on 2011-11-16
192
193                 u16 material;
194                 is>>material;
195                 u16 materialcount;
196                 is>>materialcount;
197                 if(material > 0xfff)
198                         throw SerializationError("Too large material number");
199                 // Convert old id to name
200                 NameIdMapping legacy_nimap;
201                 content_mapnode_get_name_id_mapping(&legacy_nimap);
202                 legacy_nimap.getName(material, name);
203                 if(name == "")
204                         name = "unknown_block";
205                 name = itemdef->getAlias(name);
206                 count = materialcount;
207         }
208         else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
209                         || name == "craft" || name == "CraftItem")
210         {
211                 // Obsoleted on 2012-01-07
212
213                 std::string all;
214                 std::getline(is, all, '\n');
215                 // First attempt to read inside ""
216                 Strfnd fnd(all);
217                 fnd.next("\"");
218                 // If didn't skip to end, we have ""s
219                 if(!fnd.atend()){
220                         name = fnd.next("\"");
221                 } else { // No luck, just read a word then
222                         fnd.start(all);
223                         name = fnd.next(" ");
224                 }
225                 fnd.skip_over(" ");
226                 name = itemdef->getAlias(name);
227                 count = stoi(trim(fnd.next("")));
228                 if(count == 0)
229                         count = 1;
230         }
231         else if(name == "MBOItem")
232         {
233                 // Obsoleted on 2011-10-14
234                 throw SerializationError("MBOItem not supported anymore");
235         }
236         else if(name == "tool" || name == "ToolItem")
237         {
238                 // Obsoleted on 2012-01-07
239
240                 std::string all;
241                 std::getline(is, all, '\n');
242                 // First attempt to read inside ""
243                 Strfnd fnd(all);
244                 fnd.next("\"");
245                 // If didn't skip to end, we have ""s
246                 if(!fnd.atend()){
247                         name = fnd.next("\"");
248                 } else { // No luck, just read a word then
249                         fnd.start(all);
250                         name = fnd.next(" ");
251                 }
252                 count = 1;
253                 // Then read wear
254                 fnd.skip_over(" ");
255                 name = itemdef->getAlias(name);
256                 wear = stoi(trim(fnd.next("")));
257         }
258         else
259         {
260                 do  // This loop is just to allow "break;"
261                 {
262                         // The real thing
263
264                         // Apply item aliases
265                         name = itemdef->getAlias(name);
266
267                         // Read the count
268                         std::string count_str;
269                         std::getline(is, count_str, ' ');
270                         if(count_str.empty())
271                         {
272                                 count = 1;
273                                 break;
274                         }
275                         else
276                                 count = stoi(count_str);
277
278                         // Read the wear
279                         std::string wear_str;
280                         std::getline(is, wear_str, ' ');
281                         if(wear_str.empty())
282                                 break;
283                         else
284                                 wear = stoi(wear_str);
285
286                         // Read metadata
287                         metadata = deSerializeJsonStringIfNeeded(is);
288
289                         // In case fields are added after metadata, skip space here:
290                         //std::getline(is, tmp, ' ');
291                         //if(!tmp.empty())
292                         //      throw SerializationError("Unexpected text after metadata");
293
294                 } while(false);
295         }
296
297         if(name.empty() || count == 0)
298                 clear();
299         else if(itemdef->get(name).type == ITEM_TOOL)
300                 count = 1;
301 }
302
303 void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
304 {
305         std::istringstream is(str, std::ios::binary);
306         deSerialize(is, itemdef);
307 }
308
309 std::string ItemStack::getItemString() const
310 {
311         // Get item string
312         std::ostringstream os(std::ios::binary);
313         serialize(os);
314         return os.str();
315 }
316
317 ItemStack ItemStack::addItem(const ItemStack &newitem_,
318                 IItemDefManager *itemdef)
319 {
320         ItemStack newitem = newitem_;
321
322         // If the item is empty or the position invalid, bail out
323         if(newitem.empty())
324         {
325                 // nothing can be added trivially
326         }
327         // If this is an empty item, it's an easy job.
328         else if(empty())
329         {
330                 *this = newitem;
331                 newitem.clear();
332         }
333         // If item name differs, bail out
334         else if(name != newitem.name)
335         {
336                 // cannot be added
337         }
338         // If the item fits fully, add counter and delete it
339         else if(newitem.count <= freeSpace(itemdef))
340         {
341                 add(newitem.count);
342                 newitem.clear();
343         }
344         // Else the item does not fit fully. Add all that fits and return
345         // the rest.
346         else
347         {
348                 u16 freespace = freeSpace(itemdef);
349                 add(freespace);
350                 newitem.remove(freespace);
351         }
352
353         return newitem;
354 }
355
356 bool ItemStack::itemFits(const ItemStack &newitem_,
357                 ItemStack *restitem,
358                 IItemDefManager *itemdef) const
359 {
360         ItemStack newitem = newitem_;
361
362         // If the item is empty or the position invalid, bail out
363         if(newitem.empty())
364         {
365                 // nothing can be added trivially
366         }
367         // If this is an empty item, it's an easy job.
368         else if(empty())
369         {
370                 newitem.clear();
371         }
372         // If item name differs, bail out
373         else if(name != newitem.name)
374         {
375                 // cannot be added
376         }
377         // If the item fits fully, delete it
378         else if(newitem.count <= freeSpace(itemdef))
379         {
380                 newitem.clear();
381         }
382         // Else the item does not fit fully. Return the rest.
383         // the rest.
384         else
385         {
386                 u16 freespace = freeSpace(itemdef);
387                 newitem.remove(freespace);
388         }
389
390         if(restitem)
391                 *restitem = newitem;
392         return newitem.empty();
393 }
394
395 ItemStack ItemStack::takeItem(u32 takecount)
396 {
397         if(takecount == 0 || count == 0)
398                 return ItemStack();
399
400         ItemStack result = *this;
401         if(takecount >= count)
402         {
403                 // Take all
404                 clear();
405         }
406         else
407         {
408                 // Take part
409                 remove(takecount);
410                 result.count = takecount;
411         }
412         return result;
413 }
414
415 ItemStack ItemStack::peekItem(u32 peekcount) const
416 {
417         if(peekcount == 0 || count == 0)
418                 return ItemStack();
419
420         ItemStack result = *this;
421         if(peekcount < count)
422                 result.count = peekcount;
423         return result;
424 }
425
426 /*
427         Inventory
428 */
429
430 InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef)
431 {
432         m_name = name;
433         m_size = size;
434         m_width = 0;
435         m_itemdef = itemdef;
436         clearItems();
437         //m_dirty = false;
438 }
439
440 InventoryList::~InventoryList()
441 {
442 }
443
444 void InventoryList::clearItems()
445 {
446         m_items.clear();
447
448         for(u32 i=0; i<m_size; i++)
449         {
450                 m_items.push_back(ItemStack());
451         }
452
453         //setDirty(true);
454 }
455
456 void InventoryList::setSize(u32 newsize)
457 {
458         if(newsize != m_items.size())
459                 m_items.resize(newsize);
460         m_size = newsize;
461 }
462
463 void InventoryList::setWidth(u32 newwidth)
464 {
465         m_width = newwidth;
466 }
467
468 void InventoryList::setName(const std::string &name)
469 {
470         m_name = name;
471 }
472
473 void InventoryList::serialize(std::ostream &os) const
474 {
475         //os.imbue(std::locale("C"));
476         
477         os<<"Width "<<m_width<<"\n";
478
479         for(u32 i=0; i<m_items.size(); i++)
480         {
481                 const ItemStack &item = m_items[i];
482                 if(item.empty())
483                 {
484                         os<<"Empty";
485                 }
486                 else
487                 {
488                         os<<"Item ";
489                         item.serialize(os);
490                 }
491                 os<<"\n";
492         }
493
494         os<<"EndInventoryList\n";
495 }
496
497 void InventoryList::deSerialize(std::istream &is)
498 {
499         //is.imbue(std::locale("C"));
500
501         clearItems();
502         u32 item_i = 0;
503         m_width = 0;
504
505         for(;;)
506         {
507                 std::string line;
508                 std::getline(is, line, '\n');
509
510                 std::istringstream iss(line);
511                 //iss.imbue(std::locale("C"));
512
513                 std::string name;
514                 std::getline(iss, name, ' ');
515
516                 if(name == "EndInventoryList")
517                 {
518                         break;
519                 }
520                 // This is a temporary backwards compatibility fix
521                 else if(name == "end")
522                 {
523                         break;
524                 }
525                 else if(name == "Width")
526                 {
527                         iss >> m_width;
528                         if (iss.fail())
529                                 throw SerializationError("incorrect width property");
530                 }
531                 else if(name == "Item")
532                 {
533                         if(item_i > getSize() - 1)
534                                 throw SerializationError("too many items");
535                         ItemStack item;
536                         item.deSerialize(iss, m_itemdef);
537                         m_items[item_i++] = item;
538                 }
539                 else if(name == "Empty")
540                 {
541                         if(item_i > getSize() - 1)
542                                 throw SerializationError("too many items");
543                         m_items[item_i++].clear();
544                 }
545         }
546 }
547
548 InventoryList::InventoryList(const InventoryList &other)
549 {
550         *this = other;
551 }
552
553 InventoryList & InventoryList::operator = (const InventoryList &other)
554 {
555         m_items = other.m_items;
556         m_size = other.m_size;
557         m_width = other.m_width;
558         m_name = other.m_name;
559         m_itemdef = other.m_itemdef;
560         //setDirty(true);
561
562         return *this;
563 }
564
565 bool InventoryList::operator == (const InventoryList &other)
566 {
567         if(m_size != other.m_size)
568                 return false;
569         if(m_width != other.m_width)
570                 return false;
571         if(m_name != other.m_name)
572                 return false;
573         for(u32 i=0; i<m_items.size(); i++)
574         {
575                 ItemStack s1 = m_items[i];
576                 ItemStack s2 = other.m_items[i];
577                 if(s1.name != s2.name || s1.wear!= s2.wear || s1.count != s2.count ||
578                                 s1.metadata != s2.metadata)
579                         return false;
580         }
581
582         return true;
583 }
584
585 const std::string &InventoryList::getName() const
586 {
587         return m_name;
588 }
589
590 u32 InventoryList::getSize() const
591 {
592         return m_items.size();
593 }
594
595 u32 InventoryList::getWidth() const
596 {
597         return m_width;
598 }
599
600 u32 InventoryList::getUsedSlots() const
601 {
602         u32 num = 0;
603         for(u32 i=0; i<m_items.size(); i++)
604         {
605                 if(!m_items[i].empty())
606                         num++;
607         }
608         return num;
609 }
610
611 u32 InventoryList::getFreeSlots() const
612 {
613         return getSize() - getUsedSlots();
614 }
615
616 const ItemStack& InventoryList::getItem(u32 i) const
617 {
618         assert(i < m_size);
619         return m_items[i];
620 }
621
622 ItemStack& InventoryList::getItem(u32 i)
623 {
624         assert(i < m_size);
625         return m_items[i];
626 }
627
628 ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
629 {
630         if(i >= m_items.size())
631                 return newitem;
632
633         ItemStack olditem = m_items[i];
634         m_items[i] = newitem;
635         //setDirty(true);
636         return olditem;
637 }
638
639 void InventoryList::deleteItem(u32 i)
640 {
641         assert(i < m_items.size());
642         m_items[i].clear();
643 }
644
645 ItemStack InventoryList::addItem(const ItemStack &newitem_)
646 {
647         ItemStack newitem = newitem_;
648
649         if(newitem.empty())
650                 return newitem;
651         
652         /*
653                 First try to find if it could be added to some existing items
654         */
655         for(u32 i=0; i<m_items.size(); i++)
656         {
657                 // Ignore empty slots
658                 if(m_items[i].empty())
659                         continue;
660                 // Try adding
661                 newitem = addItem(i, newitem);
662                 if(newitem.empty())
663                         return newitem; // All was eaten
664         }
665
666         /*
667                 Then try to add it to empty slots
668         */
669         for(u32 i=0; i<m_items.size(); i++)
670         {
671                 // Ignore unempty slots
672                 if(!m_items[i].empty())
673                         continue;
674                 // Try adding
675                 newitem = addItem(i, newitem);
676                 if(newitem.empty())
677                         return newitem; // All was eaten
678         }
679
680         // Return leftover
681         return newitem;
682 }
683
684 ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
685 {
686         if(i >= m_items.size())
687                 return newitem;
688
689         ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
690         //if(leftover != newitem)
691         //      setDirty(true);
692         return leftover;
693 }
694
695 bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
696                 ItemStack *restitem) const
697 {
698         if(i >= m_items.size())
699         {
700                 if(restitem)
701                         *restitem = newitem;
702                 return false;
703         }
704
705         return m_items[i].itemFits(newitem, restitem, m_itemdef);
706 }
707
708 bool InventoryList::roomForItem(const ItemStack &item_) const
709 {
710         ItemStack item = item_;
711         ItemStack leftover;
712         for(u32 i=0; i<m_items.size(); i++)
713         {
714                 if(itemFits(i, item, &leftover))
715                         return true;
716                 item = leftover;
717         }
718         return false;
719 }
720
721 bool InventoryList::containsItem(const ItemStack &item) const
722 {
723         u32 count = item.count;
724         if(count == 0)
725                 return true;
726         for(std::vector<ItemStack>::const_reverse_iterator
727                         i = m_items.rbegin();
728                         i != m_items.rend(); i++)
729         {
730                 if(count == 0)
731                         break;
732                 if(i->name == item.name)
733                 {
734                         if(i->count >= count)
735                                 return true;
736                         else
737                                 count -= i->count;
738                 }
739         }
740         return false;
741 }
742
743 ItemStack InventoryList::removeItem(const ItemStack &item)
744 {
745         ItemStack removed;
746         for(std::vector<ItemStack>::reverse_iterator
747                         i = m_items.rbegin();
748                         i != m_items.rend(); i++)
749         {
750                 if(i->name == item.name)
751                 {
752                         u32 still_to_remove = item.count - removed.count;
753                         removed.addItem(i->takeItem(still_to_remove), m_itemdef);
754                         if(removed.count == item.count)
755                                 break;
756                 }
757         }
758         return removed;
759 }
760
761 ItemStack InventoryList::takeItem(u32 i, u32 takecount)
762 {
763         if(i >= m_items.size())
764                 return ItemStack();
765
766         ItemStack taken = m_items[i].takeItem(takecount);
767         //if(!taken.empty())
768         //      setDirty(true);
769         return taken;
770 }
771
772 ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
773 {
774         if(i >= m_items.size())
775                 return ItemStack();
776
777         return m_items[i].peekItem(peekcount);
778 }
779
780 void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
781 {
782         if(this == dest && i == dest_i)
783                 return;
784
785         // Take item from source list
786         ItemStack item1;
787         if(count == 0)
788                 item1 = changeItem(i, ItemStack());
789         else
790                 item1 = takeItem(i, count);
791
792         if(item1.empty())
793                 return;
794
795         // Try to add the item to destination list
796         u32 oldcount = item1.count;
797         item1 = dest->addItem(dest_i, item1);
798
799         // If something is returned, the item was not fully added
800         if(!item1.empty())
801         {
802                 // If olditem is returned, nothing was added.
803                 bool nothing_added = (item1.count == oldcount);
804
805                 // If something else is returned, part of the item was left unadded.
806                 // Add the other part back to the source item
807                 addItem(i, item1);
808
809                 // If olditem is returned, nothing was added.
810                 // Swap the items
811                 if(nothing_added)
812                 {
813                         // Take item from source list
814                         item1 = changeItem(i, ItemStack());
815                         // Adding was not possible, swap the items.
816                         ItemStack item2 = dest->changeItem(dest_i, item1);
817                         // Put item from destination list to the source list
818                         changeItem(i, item2);
819                 }
820         }
821 }
822
823 /*
824         Inventory
825 */
826
827 Inventory::~Inventory()
828 {
829         clear();
830 }
831
832 void Inventory::clear()
833 {
834         for(u32 i=0; i<m_lists.size(); i++)
835         {
836                 delete m_lists[i];
837         }
838         m_lists.clear();
839 }
840
841 void Inventory::clearContents()
842 {
843         for(u32 i=0; i<m_lists.size(); i++)
844         {
845                 InventoryList *list = m_lists[i];
846                 for(u32 j=0; j<list->getSize(); j++)
847                 {
848                         list->deleteItem(j);
849                 }
850         }
851 }
852
853 Inventory::Inventory(IItemDefManager *itemdef)
854 {
855         m_itemdef = itemdef;
856 }
857
858 Inventory::Inventory(const Inventory &other)
859 {
860         *this = other;
861 }
862
863 Inventory & Inventory::operator = (const Inventory &other)
864 {
865         // Gracefully handle self assignment
866         if(this != &other)
867         {
868                 clear();
869                 m_itemdef = other.m_itemdef;
870                 for(u32 i=0; i<other.m_lists.size(); i++)
871                 {
872                         m_lists.push_back(new InventoryList(*other.m_lists[i]));
873                 }
874         }
875         return *this;
876 }
877
878 bool Inventory::operator == (const Inventory &other)
879 {
880         if(m_lists.size() != other.m_lists.size())
881                 return false;
882
883         for(u32 i=0; i<m_lists.size(); i++)
884         {
885                 if(m_lists[i] != other.m_lists[i])
886                         return false;
887         }
888         return true;
889 }
890
891 void Inventory::serialize(std::ostream &os) const
892 {
893         for(u32 i=0; i<m_lists.size(); i++)
894         {
895                 InventoryList *list = m_lists[i];
896                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
897                 list->serialize(os);
898         }
899
900         os<<"EndInventory\n";
901 }
902
903 void Inventory::deSerialize(std::istream &is)
904 {
905         clear();
906
907         for(;;)
908         {
909                 std::string line;
910                 std::getline(is, line, '\n');
911
912                 std::istringstream iss(line);
913
914                 std::string name;
915                 std::getline(iss, name, ' ');
916
917                 if(name == "EndInventory")
918                 {
919                         break;
920                 }
921                 // This is a temporary backwards compatibility fix
922                 else if(name == "end")
923                 {
924                         break;
925                 }
926                 else if(name == "List")
927                 {
928                         std::string listname;
929                         u32 listsize;
930
931                         std::getline(iss, listname, ' ');
932                         iss>>listsize;
933
934                         InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
935                         list->deSerialize(is);
936
937                         m_lists.push_back(list);
938                 }
939                 else
940                 {
941                         throw SerializationError("invalid inventory specifier");
942                 }
943         }
944 }
945
946 InventoryList * Inventory::addList(const std::string &name, u32 size)
947 {
948         s32 i = getListIndex(name);
949         if(i != -1)
950         {
951                 if(m_lists[i]->getSize() != size)
952                 {
953                         delete m_lists[i];
954                         m_lists[i] = new InventoryList(name, size, m_itemdef);
955                 }
956                 return m_lists[i];
957         }
958         else
959         {
960                 //don't create list with invalid name
961                 if (name.find(" ") != std::string::npos) return NULL;
962
963                 InventoryList *list = new InventoryList(name, size, m_itemdef);
964                 m_lists.push_back(list);
965                 return list;
966         }
967 }
968
969 InventoryList * Inventory::getList(const std::string &name)
970 {
971         s32 i = getListIndex(name);
972         if(i == -1)
973                 return NULL;
974         return m_lists[i];
975 }
976
977 std::vector<const InventoryList*> Inventory::getLists()
978 {
979         std::vector<const InventoryList*> lists;
980         for(u32 i=0; i<m_lists.size(); i++)
981         {
982                 InventoryList *list = m_lists[i];
983                 lists.push_back(list);
984         }
985         return lists;
986 }
987
988 bool Inventory::deleteList(const std::string &name)
989 {
990         s32 i = getListIndex(name);
991         if(i == -1)
992                 return false;
993         delete m_lists[i];
994         m_lists.erase(m_lists.begin() + i);
995         return true;
996 }
997
998 const InventoryList * Inventory::getList(const std::string &name) const
999 {
1000         s32 i = getListIndex(name);
1001         if(i == -1)
1002                 return NULL;
1003         return m_lists[i];
1004 }
1005
1006 const s32 Inventory::getListIndex(const std::string &name) const
1007 {
1008         for(u32 i=0; i<m_lists.size(); i++)
1009         {
1010                 if(m_lists[i]->getName() == name)
1011                         return i;
1012         }
1013         return -1;
1014 }
1015
1016 //END