From f964252564162f70df2cda6b06b45245d0a383ad Mon Sep 17 00:00:00 2001
From: Andrei Karas <akaras@inbox.ru>
Date: Thu, 27 Aug 2015 16:07:33 +0300
Subject: Move receive code from inventoryhandler into separate file.

---
 src/CMakeLists.txt                   |   6 +
 src/Makefile.am                      |   6 +
 src/net/ea/inventoryhandler.cpp      | 232 +--------
 src/net/ea/inventoryhandler.h        |  28 +-
 src/net/ea/inventoryrecv.cpp         | 257 ++++++++++
 src/net/ea/inventoryrecv.h           |  69 +++
 src/net/eathena/inventoryhandler.cpp | 950 ++---------------------------------
 src/net/eathena/inventoryhandler.h   |  69 ---
 src/net/eathena/inventoryrecv.cpp    | 940 ++++++++++++++++++++++++++++++++++
 src/net/eathena/inventoryrecv.h      |  72 +++
 src/net/tmwa/inventoryhandler.cpp    | 439 +---------------
 src/net/tmwa/inventoryhandler.h      |  19 -
 src/net/tmwa/inventoryrecv.cpp       | 464 +++++++++++++++++
 src/net/tmwa/inventoryrecv.h         |  46 ++
 14 files changed, 1944 insertions(+), 1653 deletions(-)
 create mode 100644 src/net/ea/inventoryrecv.cpp
 create mode 100644 src/net/ea/inventoryrecv.h
 create mode 100644 src/net/eathena/inventoryrecv.cpp
 create mode 100644 src/net/eathena/inventoryrecv.h
 create mode 100644 src/net/tmwa/inventoryrecv.cpp
 create mode 100644 src/net/tmwa/inventoryrecv.h

(limited to 'src')

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 471ff7c3f..7bb471242 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1371,6 +1371,8 @@ SET(SRCS_EVOL
     net/ea/guildrecv.h
     net/ea/inventoryhandler.cpp
     net/ea/inventoryhandler.h
+    net/ea/inventoryrecv.cpp
+    net/ea/inventoryrecv.h
     net/ea/itemhandler.cpp
     net/ea/itemhandler.h
     net/ea/loginhandler.cpp
@@ -1429,6 +1431,8 @@ SET(SRCS_TMWA
     net/tmwa/guildrecv.h
     net/tmwa/inventoryhandler.cpp
     net/tmwa/inventoryhandler.h
+    net/tmwa/inventoryrecv.cpp
+    net/tmwa/inventoryrecv.h
     net/tmwa/itemhandler.cpp
     net/tmwa/itemhandler.h
     net/tmwa/loginhandler.cpp
@@ -1611,6 +1615,8 @@ SET(SRCS_EATHENA
     net/eathena/homunculusrecv.h
     net/eathena/inventoryhandler.cpp
     net/eathena/inventoryhandler.h
+    net/eathena/inventoryrecv.cpp
+    net/eathena/inventoryrecv.h
     net/eathena/itemflags.h
     net/eathena/itemhandler.cpp
     net/eathena/itemhandler.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 9a5839036..493c521fd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1213,6 +1213,8 @@ manaplus_SOURCES += main.cpp \
 	      net/ea/guildrecv.h \
 	      net/ea/inventoryhandler.cpp \
 	      net/ea/inventoryhandler.h \
+	      net/ea/inventoryrecv.cpp \
+	      net/ea/inventoryrecv.h \
 	      net/ea/itemhandler.cpp \
 	      net/ea/itemhandler.h \
 	      net/ea/loginhandler.cpp \
@@ -1272,6 +1274,8 @@ manaplus_SOURCES += \
 	      net/tmwa/guildrecv.h \
 	      net/tmwa/inventoryhandler.cpp \
 	      net/tmwa/inventoryhandler.h \
+	      net/tmwa/inventoryrecv.cpp \
+	      net/tmwa/inventoryrecv.h \
 	      net/tmwa/itemhandler.cpp \
 	      net/tmwa/itemhandler.h \
 	      net/tmwa/loginhandler.cpp \
@@ -1457,6 +1461,8 @@ manaplus_SOURCES += gui/windows/bankwindow.cpp \
 	      net/eathena/homunculusrecv.h \
 	      net/eathena/inventoryhandler.cpp \
 	      net/eathena/inventoryhandler.h \
+	      net/eathena/inventoryrecv.cpp \
+	      net/eathena/inventoryrecv.h \
 	      net/eathena/itemflags.h \
 	      net/eathena/itemhandler.cpp \
 	      net/eathena/itemhandler.h \
diff --git a/src/net/ea/inventoryhandler.cpp b/src/net/ea/inventoryhandler.cpp
index b3821822e..e4409278c 100644
--- a/src/net/ea/inventoryhandler.cpp
+++ b/src/net/ea/inventoryhandler.cpp
@@ -36,6 +36,7 @@
 
 #include "net/ea/eaprotocol.h"
 #include "net/ea/equipbackend.h"
+#include "net/ea/inventoryrecv.h"
 
 #include "utils/delete2.h"
 
@@ -43,50 +44,18 @@
 
 #include "debug.h"
 
-const EquipSlot::Type EQUIP_POINTS[EquipSlot::VECTOREND] =
-{
-    EquipSlot::LEGS_SLOT,               // Lower Headgear
-    EquipSlot::FIGHT1_SLOT,             // Weapon
-    EquipSlot::GLOVES_SLOT,             // Garment
-    EquipSlot::RING2_SLOT,              // Accessory 1
-    EquipSlot::RING1_SLOT,              // Armor
-    EquipSlot::FIGHT2_SLOT,             // Shield
-    EquipSlot::FEET_SLOT,               // Footgear
-    EquipSlot::NECK_SLOT,               // Accessory 2
-    EquipSlot::HEAD_SLOT,               // Upper Headgear
-    EquipSlot::TORSO_SLOT,              // Middle Headgear
-    EquipSlot::EVOL_RING1_SLOT,         // Costume Top Headgear
-    EquipSlot::EVOL_RING2_SLOT,         // Costume Mid Headgear
-    EquipSlot::PROJECTILE_SLOT,         // Costume Low Headgear
-    EquipSlot::COSTUME_ROBE_SLOT,       // Costume Garment/Robe
-    EquipSlot::MISSING1_SLOT,           // Missing slot 1
-    EquipSlot::MISSING2_SLOT,           // Missing slot 2
-    EquipSlot::SHADOW_ARMOR_SLOT,       // Shadow Armor
-    EquipSlot::SHADOW_WEAPON_SLOT,      // Shadow Weapon
-    EquipSlot::SHADOW_SHIELD_SLOT,      // Shadow Shield
-    EquipSlot::SHADOW_SHOES_SLOT,       // Shadow Shoes
-    EquipSlot::SHADOW_ACCESSORY2_SLOT,  // Shadow Accessory 2
-    EquipSlot::SHADOW_ACCESSORY1_SLOT,  // Shadow Accessory 1
-};
-
 namespace Ea
 {
 
-EquipBackend InventoryHandler::mEquips;
-InventoryItems InventoryHandler::mInventoryItems;
-Inventory *InventoryHandler::mStorage = nullptr;
-PickupQueue InventoryHandler::mSentPickups;
-bool InventoryHandler::mDebugInventory = true;
-
 InventoryHandler::InventoryHandler()
 {
-    mEquips.clear();
-    mInventoryItems.clear();
-    mStorage = nullptr;
+    InventoryRecv::mEquips.clear();
+    InventoryRecv::mInventoryItems.clear();
+    InventoryRecv::mStorage = nullptr;
     storageWindow = nullptr;
-    while (!mSentPickups.empty())
-        mSentPickups.pop();
-    mDebugInventory = true;
+    while (!InventoryRecv::mSentPickups.empty())
+        InventoryRecv::mSentPickups.pop();
+    InventoryRecv::mDebugInventory = true;
 }
 
 InventoryHandler::~InventoryHandler()
@@ -97,12 +66,12 @@ InventoryHandler::~InventoryHandler()
         storageWindow = nullptr;
     }
 
-    delete2(mStorage);
+    delete2(InventoryRecv::mStorage);
 }
 
 void InventoryHandler::clear()
 {
-    delete2(mStorage);
+    delete2(InventoryRecv::mStorage);
 }
 
 bool InventoryHandler::canSplit(const Item *const item A_UNUSED) const
@@ -144,179 +113,6 @@ size_t InventoryHandler::getSize(const int type) const
             return 0;
     }
 }
-int InventoryHandler::getSlot(const int eAthenaSlot)
-{
-    if (eAthenaSlot == 0)
-        return EquipSlot::VECTOREND;
-
-    if (eAthenaSlot & 0x8000)
-        return inventoryHandler->getProjectileSlot();
-
-    unsigned int mask = 1;
-    int position = 0;
-    while (!(eAthenaSlot & mask))
-    {
-        mask <<= 1;
-        position++;
-    }
-    return static_cast<int>(EQUIP_POINTS[position]);
-}
-
-void InventoryHandler::processPlayerInventoryRemove(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventoryRemove")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    const int amount = msg.readInt16("amount");
-    if (inventory)
-    {
-        if (Item *const item = inventory->getItem(index))
-        {
-            item->increaseQuantity(-amount);
-            if (item->getQuantity() == 0)
-                inventory->removeItemAt(index);
-            ArrowsListener::distributeEvent();
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventoryRemove")
-}
-
-void InventoryHandler::processPlayerInventoryUse(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventoryUse")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    msg.readInt16("item id");
-    msg.readInt32("id?");
-    const int amount = msg.readInt16("amount");
-    msg.readUInt8("type");
-
-    if (inventory)
-    {
-        if (Item *const item = inventory->getItem(index))
-        {
-            if (amount)
-                item->setQuantity(amount);
-            else
-                inventory->removeItemAt(index);
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventoryUse")
-}
-
-void InventoryHandler::processItemUseResponse(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processItemUseResponse")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    const int amount = msg.readInt16("amount");
-
-    if (msg.readUInt8("result") == 0)
-    {
-        NotifyManager::notify(NotifyTypes::USE_FAILED);
-    }
-    else
-    {
-        if (inventory)
-        {
-            if (Item *const item = inventory->getItem(index))
-            {
-                if (amount)
-                    item->setQuantity(amount);
-                else
-                    inventory->removeItemAt(index);
-            }
-        }
-    }
-    BLOCK_END("InventoryHandler::processItemUseResponse")
-}
-
-void InventoryHandler::processPlayerStorageStatus(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageStatus")
-    /*
-      * This is the closest we get to an "Open Storage" packet from the
-      * server. It always comes after the two SMSG_PLAYER_STORAGE_...
-      * packets that update storage contents.
-      */
-    msg.readInt16("used count");
-    const int size = msg.readInt16("max size");
-
-    if (!mStorage)
-        mStorage = new Inventory(InventoryType::STORAGE, size);
-
-    FOR_EACH (Ea::InventoryItems::const_iterator, it, mInventoryItems)
-    {
-        mStorage->setItem((*it).slot,
-            (*it).id,
-            (*it).type,
-            (*it).quantity,
-            (*it).refine,
-            (*it).color,
-            (*it).identified,
-            (*it).damaged,
-            (*it).favorite,
-            (*it).equip,
-            Equipped_false);
-    }
-    mInventoryItems.clear();
-
-    if (!storageWindow)
-    {
-        CREATEWIDGETV(storageWindow, InventoryWindow, mStorage);
-    }
-    BLOCK_END("InventoryHandler::processPlayerStorageStatus")
-}
-
-void InventoryHandler::processPlayerStorageClose(Net::MessageIn &msg A_UNUSED)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageClose")
-    // Storage access has been closed
-    // Storage window deletes itself
-    if (storageWindow)
-    {
-        storageWindow->unsetInventory();
-        storageWindow->close();
-    }
-    storageWindow = nullptr;
-
-    if (mStorage)
-        mStorage->clear();
-
-    delete2(mStorage);
-    BLOCK_END("InventoryHandler::processPlayerStorageClose")
-}
-
-void InventoryHandler::processPlayerAttackRange(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerAttackRange")
-    const int range = msg.readInt16("range");
-    if (localPlayer)
-        localPlayer->setAttackRange(range);
-    PlayerInfo::setStatBase(Attributes::ATTACK_RANGE, range);
-    PlayerInfo::setStatMod(Attributes::ATTACK_RANGE, 0);
-    BLOCK_END("InventoryHandler::processPlayerAttackRange")
-}
-
-void InventoryHandler::processPlayerArrowEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerArrowEquip")
-    int index = msg.readInt16("index");
-    if (index <= 1)
-        return;
-
-    index -= INVENTORY_OFFSET;
-    mEquips.setEquipment(inventoryHandler->getProjectileSlot(), index);
-    ArrowsListener::distributeEvent();
-    BLOCK_END("InventoryHandler::processPlayerArrowEquip")
-}
-
 void InventoryHandler::destroyStorage()
 {
     BLOCK_START("InventoryHandler::closeStorage")
@@ -334,4 +130,14 @@ void InventoryHandler::forgotStorage()
     storageWindow = nullptr;
 }
 
+void InventoryHandler::pushPickup(const BeingId floorId)
+{
+    InventoryRecv::mSentPickups.push(floorId);
+}
+
+Inventory *InventoryHandler::getStorage() const
+{
+    return InventoryRecv::mStorage;
+}
+
 }  // namespace Ea
diff --git a/src/net/ea/inventoryhandler.h b/src/net/ea/inventoryhandler.h
index 29d24a1ac..3883ab0d4 100644
--- a/src/net/ea/inventoryhandler.h
+++ b/src/net/ea/inventoryhandler.h
@@ -68,40 +68,16 @@ class InventoryHandler notfinal : public Net::InventoryHandler
 
         size_t getSize(const int type) const override final A_WARN_UNUSED;
 
-        void pushPickup(const BeingId floorId)
-        { mSentPickups.push(floorId); }
-
-        static int getSlot(const int eAthenaSlot) A_WARN_UNUSED;
+        void pushPickup(const BeingId floorId);
 
         void destroyStorage() override final;
 
         void forgotStorage() override final;
 
-        Inventory *getStorage() const override final
-        { return mStorage; }
+        Inventory *getStorage() const override final;
 
     protected:
         InventoryHandler();
-
-        static void processPlayerInventoryRemove(Net::MessageIn &msg);
-
-        static void processPlayerInventoryUse(Net::MessageIn &msg);
-
-        static void processItemUseResponse(Net::MessageIn &msg);
-
-        static void processPlayerStorageStatus(Net::MessageIn &msg);
-
-        static void processPlayerStorageClose(Net::MessageIn &msg);
-
-        static void processPlayerAttackRange(Net::MessageIn &msg);
-
-        static void processPlayerArrowEquip(Net::MessageIn &msg);
-
-        static EquipBackend mEquips;
-        static InventoryItems mInventoryItems;
-        static Inventory *mStorage;
-        static PickupQueue mSentPickups;
-        static bool mDebugInventory;
 };
 
 }  // namespace Ea
diff --git a/src/net/ea/inventoryrecv.cpp b/src/net/ea/inventoryrecv.cpp
new file mode 100644
index 000000000..810743d26
--- /dev/null
+++ b/src/net/ea/inventoryrecv.cpp
@@ -0,0 +1,257 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2015  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/ea/inventoryrecv.h"
+
+#include "notifymanager.h"
+
+#include "being/localplayer.h"
+
+#include "enums/equipslot.h"
+
+#include "enums/resources/notifytypes.h"
+
+#include "gui/widgets/createwidget.h"
+
+#include "net/messagein.h"
+
+#include "net/ea/eaprotocol.h"
+#include "net/ea/equipbackend.h"
+
+#include "utils/delete2.h"
+
+#include "listeners/arrowslistener.h"
+
+#include "debug.h"
+
+namespace Ea
+{
+
+namespace InventoryRecv
+{
+    const EquipSlot::Type EQUIP_POINTS[EquipSlot::VECTOREND] =
+    {
+        EquipSlot::LEGS_SLOT,               // Lower Headgear
+        EquipSlot::FIGHT1_SLOT,             // Weapon
+        EquipSlot::GLOVES_SLOT,             // Garment
+        EquipSlot::RING2_SLOT,              // Accessory 1
+        EquipSlot::RING1_SLOT,              // Armor
+        EquipSlot::FIGHT2_SLOT,             // Shield
+        EquipSlot::FEET_SLOT,               // Footgear
+        EquipSlot::NECK_SLOT,               // Accessory 2
+        EquipSlot::HEAD_SLOT,               // Upper Headgear
+        EquipSlot::TORSO_SLOT,              // Middle Headgear
+        EquipSlot::EVOL_RING1_SLOT,         // Costume Top Headgear
+        EquipSlot::EVOL_RING2_SLOT,         // Costume Mid Headgear
+        EquipSlot::PROJECTILE_SLOT,         // Costume Low Headgear
+        EquipSlot::COSTUME_ROBE_SLOT,       // Costume Garment/Robe
+        EquipSlot::MISSING1_SLOT,           // Missing slot 1
+        EquipSlot::MISSING2_SLOT,           // Missing slot 2
+        EquipSlot::SHADOW_ARMOR_SLOT,       // Shadow Armor
+        EquipSlot::SHADOW_WEAPON_SLOT,      // Shadow Weapon
+        EquipSlot::SHADOW_SHIELD_SLOT,      // Shadow Shield
+        EquipSlot::SHADOW_SHOES_SLOT,       // Shadow Shoes
+        EquipSlot::SHADOW_ACCESSORY2_SLOT,  // Shadow Accessory 2
+        EquipSlot::SHADOW_ACCESSORY1_SLOT,  // Shadow Accessory 1
+    };
+
+    EquipBackend mEquips;
+    InventoryItems mInventoryItems;
+    Inventory *mStorage = nullptr;
+    PickupQueue mSentPickups;
+    bool mDebugInventory = true;
+}
+
+void InventoryRecv::processPlayerInventoryRemove(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventoryRemove")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    const int amount = msg.readInt16("amount");
+    if (inventory)
+    {
+        if (Item *const item = inventory->getItem(index))
+        {
+            item->increaseQuantity(-amount);
+            if (item->getQuantity() == 0)
+                inventory->removeItemAt(index);
+            ArrowsListener::distributeEvent();
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventoryRemove")
+}
+
+void InventoryRecv::processPlayerInventoryUse(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventoryUse")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    msg.readInt16("item id");
+    msg.readInt32("id?");
+    const int amount = msg.readInt16("amount");
+    msg.readUInt8("type");
+
+    if (inventory)
+    {
+        if (Item *const item = inventory->getItem(index))
+        {
+            if (amount)
+                item->setQuantity(amount);
+            else
+                inventory->removeItemAt(index);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventoryUse")
+}
+
+void InventoryRecv::processItemUseResponse(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processItemUseResponse")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    const int amount = msg.readInt16("amount");
+
+    if (msg.readUInt8("result") == 0)
+    {
+        NotifyManager::notify(NotifyTypes::USE_FAILED);
+    }
+    else
+    {
+        if (inventory)
+        {
+            if (Item *const item = inventory->getItem(index))
+            {
+                if (amount)
+                    item->setQuantity(amount);
+                else
+                    inventory->removeItemAt(index);
+            }
+        }
+    }
+    BLOCK_END("InventoryRecv::processItemUseResponse")
+}
+
+void InventoryRecv::processPlayerStorageStatus(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageStatus")
+    /*
+      * This is the closest we get to an "Open Storage" packet from the
+      * server. It always comes after the two SMSG_PLAYER_STORAGE_...
+      * packets that update storage contents.
+      */
+    msg.readInt16("used count");
+    const int size = msg.readInt16("max size");
+
+    if (!mStorage)
+        mStorage = new Inventory(InventoryType::STORAGE, size);
+
+    FOR_EACH (Ea::InventoryItems::const_iterator, it, mInventoryItems)
+    {
+        mStorage->setItem((*it).slot,
+            (*it).id,
+            (*it).type,
+            (*it).quantity,
+            (*it).refine,
+            (*it).color,
+            (*it).identified,
+            (*it).damaged,
+            (*it).favorite,
+            (*it).equip,
+            Equipped_false);
+    }
+    mInventoryItems.clear();
+
+    if (!storageWindow)
+    {
+        CREATEWIDGETV(storageWindow, InventoryWindow, mStorage);
+    }
+    BLOCK_END("InventoryRecv::processPlayerStorageStatus")
+}
+
+void InventoryRecv::processPlayerStorageClose(Net::MessageIn &msg A_UNUSED)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageClose")
+    // Storage access has been closed
+    // Storage window deletes itself
+    if (storageWindow)
+    {
+        storageWindow->unsetInventory();
+        storageWindow->close();
+    }
+    storageWindow = nullptr;
+
+    if (mStorage)
+        mStorage->clear();
+
+    delete2(mStorage);
+    BLOCK_END("InventoryRecv::processPlayerStorageClose")
+}
+
+void InventoryRecv::processPlayerAttackRange(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerAttackRange")
+    const int range = msg.readInt16("range");
+    if (localPlayer)
+        localPlayer->setAttackRange(range);
+    PlayerInfo::setStatBase(Attributes::ATTACK_RANGE, range);
+    PlayerInfo::setStatMod(Attributes::ATTACK_RANGE, 0);
+    BLOCK_END("InventoryRecv::processPlayerAttackRange")
+}
+
+void InventoryRecv::processPlayerArrowEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerArrowEquip")
+    int index = msg.readInt16("index");
+    if (index <= 1)
+        return;
+
+    index -= INVENTORY_OFFSET;
+    mEquips.setEquipment(inventoryHandler->getProjectileSlot(), index);
+    ArrowsListener::distributeEvent();
+    BLOCK_END("InventoryRecv::processPlayerArrowEquip")
+}
+
+int InventoryRecv::getSlot(const int eAthenaSlot)
+{
+    if (eAthenaSlot == 0)
+        return EquipSlot::VECTOREND;
+
+    if (eAthenaSlot & 0x8000)
+        return inventoryHandler->getProjectileSlot();
+
+    unsigned int mask = 1;
+    int position = 0;
+    while (!(eAthenaSlot & mask))
+    {
+        mask <<= 1;
+        position++;
+    }
+    return static_cast<int>(EQUIP_POINTS[position]);
+}
+
+}  // namespace Ea
diff --git a/src/net/ea/inventoryrecv.h b/src/net/ea/inventoryrecv.h
new file mode 100644
index 000000000..b5ba2a4df
--- /dev/null
+++ b/src/net/ea/inventoryrecv.h
@@ -0,0 +1,69 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2015  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_EA_INVENTORYRECV_H
+#define NET_EA_INVENTORYRECV_H
+
+#include "localconsts.h"
+
+#include "enums/simpletypes/beingid.h"
+
+#include "net/inventoryhandler.h"
+
+#include "net/ea/inventoryitem.h"
+
+#include <queue>
+
+namespace Net
+{
+    class MessageIn;
+}
+
+namespace Ea
+{
+
+    class EquipBackend;
+
+    typedef std::vector<InventoryItem> InventoryItems;
+    typedef std::queue<BeingId> PickupQueue;
+
+    namespace InventoryRecv
+    {
+        extern EquipBackend mEquips;
+        extern InventoryItems mInventoryItems;
+        extern Inventory *mStorage;
+        extern PickupQueue mSentPickups;
+        extern bool mDebugInventory;
+
+        void processPlayerInventoryRemove(Net::MessageIn &msg);
+        void processPlayerInventoryUse(Net::MessageIn &msg);
+        void processItemUseResponse(Net::MessageIn &msg);
+        void processPlayerStorageStatus(Net::MessageIn &msg);
+        void processPlayerStorageClose(Net::MessageIn &msg);
+        void processPlayerAttackRange(Net::MessageIn &msg);
+        void processPlayerArrowEquip(Net::MessageIn &msg);
+
+        int getSlot(const int eAthenaSlot) A_WARN_UNUSED;
+    }  // namespace InventoryRecv
+}  // namespace Ea
+
+#endif  // NET_EA_INVENTORYRECV_H
diff --git a/src/net/eathena/inventoryhandler.cpp b/src/net/eathena/inventoryhandler.cpp
index fd74ac11a..a6b16c466 100644
--- a/src/net/eathena/inventoryhandler.cpp
+++ b/src/net/eathena/inventoryhandler.cpp
@@ -39,6 +39,9 @@
 
 #include "listeners/arrowslistener.h"
 
+#include "net/ea/inventoryrecv.h"
+
+#include "net/eathena/inventoryrecv.h"
 #include "net/eathena/itemflags.h"
 #include "net/eathena/menu.h"
 #include "net/eathena/messageout.h"
@@ -80,8 +83,6 @@ const EquipSlot::Type EQUIP_CONVERT[] =
 namespace EAthena
 {
 
-Ea::InventoryItems InventoryHandler::mCartItems;
-
 InventoryHandler::InventoryHandler() :
     MessageHandler(),
     Ea::InventoryHandler(),
@@ -134,7 +135,7 @@ InventoryHandler::InventoryHandler() :
     handledMessages = _messages;
     inventoryHandler = this;
 
-    mCartItems.clear();
+    InventoryRecv::mCartItems.clear();
 }
 
 InventoryHandler::~InventoryHandler()
@@ -146,163 +147,163 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
     switch (msg.getId())
     {
         case SMSG_PLAYER_INVENTORY:
-            processPlayerInventory(msg);
+            InventoryRecv::processPlayerInventory(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_ITEMS:
-            processPlayerStorage(msg);
+            InventoryRecv::processPlayerStorage(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_EQUIP:
-            processPlayerStorageEquip(msg);
+            InventoryRecv::processPlayerStorageEquip(msg);
             break;
 
         case SMSG_PLAYER_INVENTORY_ADD:
-            processPlayerInventoryAdd(msg);
+            InventoryRecv::processPlayerInventoryAdd(msg);
             break;
 
         case SMSG_PLAYER_INVENTORY_REMOVE:
-            processPlayerInventoryRemove(msg);
+            Ea::InventoryRecv::processPlayerInventoryRemove(msg);
             break;
 
         case SMSG_PLAYER_INVENTORY_REMOVE2:
-            processPlayerInventoryRemove2(msg);
+            InventoryRecv::processPlayerInventoryRemove2(msg);
             break;
 
         case SMSG_PLAYER_INVENTORY_USE:
-            processPlayerInventoryUse(msg);
+            Ea::InventoryRecv::processPlayerInventoryUse(msg);
             break;
 
         case SMSG_ITEM_USE_RESPONSE:
-            processItemUseResponse(msg);
+            Ea::InventoryRecv::processItemUseResponse(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_STATUS:
-            processPlayerStorageStatus(msg);
+            Ea::InventoryRecv::processPlayerStorageStatus(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_ADD:
-            processPlayerStorageAdd(msg);
+            InventoryRecv::processPlayerStorageAdd(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_REMOVE:
-            processPlayerStorageRemove(msg);
+            InventoryRecv::processPlayerStorageRemove(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_CLOSE:
-            processPlayerStorageClose(msg);
+            Ea::InventoryRecv::processPlayerStorageClose(msg);
             break;
 
         case SMSG_PLAYER_EQUIPMENT:
-            processPlayerEquipment(msg);
+            InventoryRecv::processPlayerEquipment(msg);
             break;
 
         case SMSG_PLAYER_EQUIP:
-            processPlayerEquip(msg);
+            InventoryRecv::processPlayerEquip(msg);
             break;
 
         case SMSG_PLAYER_UNEQUIP:
-            processPlayerUnEquip(msg);
+            InventoryRecv::processPlayerUnEquip(msg);
             break;
 
         case SMSG_PLAYER_ATTACK_RANGE:
-            processPlayerAttackRange(msg);
+            Ea::InventoryRecv::processPlayerAttackRange(msg);
             break;
 
         case SMSG_PLAYER_ARROW_EQUIP:
-            processPlayerArrowEquip(msg);
+            Ea::InventoryRecv::processPlayerArrowEquip(msg);
             break;
 
         case SMSG_PLAYER_USE_CARD:
-            processPlayerUseCard(msg);
+            InventoryRecv::processPlayerUseCard(msg);
             break;
 
         case SMSG_PLAYER_INSERT_CARD:
-            processPlayerInsertCard(msg);
+            InventoryRecv::processPlayerInsertCard(msg);
             break;
 
         case SMSG_PLAYER_ITEM_RENTAL_TIME:
-            processPlayerItemRentalTime(msg);
+            InventoryRecv::processPlayerItemRentalTime(msg);
             break;
 
         case SMSG_PLAYER_ITEM_RENTAL_EXPIRED:
-            processPlayerItemRentalExpired(msg);
+            InventoryRecv::processPlayerItemRentalExpired(msg);
             break;
 
         case SMSG_CART_INFO:
-            processCartInfo(msg);
+            InventoryRecv::processCartInfo(msg);
             break;
 
         case SMSG_CART_REMOVE:
-            processCartRemove(msg);
+            InventoryRecv::processCartRemove(msg);
             break;
 
         case SMSG_PLAYER_CART_ADD:
-            processPlayerCartAdd(msg);
+            InventoryRecv::processPlayerCartAdd(msg);
             break;
 
         case SMSG_PLAYER_CART_EQUIP:
-            processPlayerCartEquip(msg);
+            InventoryRecv::processPlayerCartEquip(msg);
             break;
 
         case SMSG_PLAYER_CART_ITEMS:
-            processPlayerCartItems(msg);
+            InventoryRecv::processPlayerCartItems(msg);
             break;
 
         case SMSG_PLAYER_CART_REMOVE:
-            processPlayerCartRemove(msg);
+            InventoryRecv::processPlayerCartRemove(msg);
             break;
 
         case SMSG_PLAYER_IDENTIFY_LIST:
-            processPlayerIdentifyList(msg);
+            InventoryRecv::processPlayerIdentifyList(msg);
             break;
 
         case SMSG_PLAYER_IDENTIFIED:
-            processPlayerIdentified(msg);
+            InventoryRecv::processPlayerIdentified(msg);
             break;
 
         case SMSG_PLAYER_REFINE:
-            processPlayerRefine(msg);
+            InventoryRecv::processPlayerRefine(msg);
             break;
 
         case SMSG_PLAYER_REPAIR_LIST:
-            processPlayerRepairList(msg);
+            InventoryRecv::processPlayerRepairList(msg);
             break;
 
         case SMSG_PLAYER_REPAIR_EFFECT:
-            processPlayerRepairEffect(msg);
+            InventoryRecv::processPlayerRepairEffect(msg);
             break;
 
         case SMSG_PLAYER_REFINE_LIST:
-            processPlayerRefineList(msg);
+            InventoryRecv::processPlayerRefineList(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_PASSWORD:
-            processPlayerStoragePassword(msg);
+            InventoryRecv::processPlayerStoragePassword(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_PASSWORD_RESULT:
-            processPlayerStoragePasswordResult(msg);
+            InventoryRecv::processPlayerStoragePasswordResult(msg);
             break;
 
         case SMSG_PLAYER_COOKING_LIST:
-            processPlayerCookingList(msg);
+            InventoryRecv::processPlayerCookingList(msg);
             break;
 
         case SMSG_ITEM_DAMAGED:
-            processItemDamaged(msg);
+            InventoryRecv::processItemDamaged(msg);
             break;
 
         case SMSG_PLAYER_FAVORITE_ITEM:
-            processFavoriteItem(msg);
+            InventoryRecv::processFavoriteItem(msg);
             break;
 
         case SMSG_PLAYER_CART_ADD_ERROR:
-            processCartAddError(msg);
+            InventoryRecv::processCartAddError(msg);
             break;
 
         case SMSG_BIND_ITEM:
-            processBindItem(msg);
+            InventoryRecv::processBindItem(msg);
             break;
 
         default:
@@ -431,467 +432,6 @@ void InventoryHandler::favoriteItem(const Item *const item,
     outMsg.writeInt8(favorite, "favorite flag");
 }
 
-void InventoryHandler::processPlayerEquipment(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerEquipment")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    msg.readInt16("len");
-    Equipment *const equipment = PlayerInfo::getEquipment();
-    if (equipment && !equipment->getBackend())
-    {   // look like SMSG_PLAYER_INVENTORY was not received
-        mEquips.clear();
-        equipment->setBackend(&mEquips);
-    }
-    const int number = (msg.getLength() - 4) / 31;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const int itemType = msg.readUInt8("item type");
-        msg.readInt32("location");
-        const int equipType = msg.readInt32("wear state");
-        const uint8_t refine = static_cast<uint8_t>(msg.readInt8("refine"));
-        int cards[4];
-        for (int f = 0; f < 4; f++)
-            cards[f] = msg.readInt16("card");
-        msg.readInt32("hire expire date (?)");
-        msg.readInt16("equip type");
-        msg.readInt16("item sprite number");
-        ItemFlags flags;
-        flags.byte = msg.readUInt8("flags");
-        if (inventory)
-        {
-            inventory->setItem(index,
-                itemId,
-                itemType,
-                1,
-                refine,
-                ItemColorManager::getColorFromCards(&cards[0]),
-                fromBool(flags.bits.isIdentified, Identified),
-                fromBool(flags.bits.isDamaged, Damaged),
-                fromBool(flags.bits.isFavorite, Favorite),
-                Equipm_true,
-                Equipped_false);
-            inventory->setCards(index, cards, 4);
-        }
-
-        if (equipType)
-            mEquips.setEquipment(getSlot(equipType), index);
-    }
-    BLOCK_END("InventoryHandler::processPlayerEquipment")
-}
-
-void InventoryHandler::processPlayerInventoryAdd(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventoryAdd")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    if (PlayerInfo::getEquipment()
-        && !PlayerInfo::getEquipment()->getBackend())
-    {   // look like SMSG_PLAYER_INVENTORY was not received
-        mEquips.clear();
-        PlayerInfo::getEquipment()->setBackend(&mEquips);
-    }
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    int amount = msg.readInt16("count");
-    const int itemId = msg.readInt16("item id");
-    uint8_t identified = msg.readUInt8("identified");
-    const uint8_t damaged = msg.readUInt8("is damaged");
-    const uint8_t refine = msg.readUInt8("refine");
-    int cards[4];
-    for (int f = 0; f < 4; f++)
-        cards[f] = msg.readInt16("card");
-    const int equipType = msg.readInt32("location");
-    const int itemType = msg.readUInt8("item type");
-    const unsigned char err = msg.readUInt8("result");
-    msg.readInt32("hire expire date");
-    msg.readInt16("bind on equip");
-
-    const ItemColor color = ItemColorManager::getColorFromCards(&cards[0]);
-    const ItemInfo &itemInfo = ItemDB::get(itemId);
-    BeingId floorId;
-    if (mSentPickups.empty())
-    {
-        floorId = BeingId_zero;
-    }
-    else
-    {
-        floorId = mSentPickups.front();
-        mSentPickups.pop();
-    }
-
-    if (err)
-    {
-        PickupT pickup;
-        switch (err)
-        {
-            case 1:
-                pickup = Pickup::BAD_ITEM;
-                break;
-            case 2:
-                pickup = Pickup::TOO_HEAVY;
-                break;
-            case 4:
-                pickup = Pickup::INV_FULL;
-                break;
-            case 5:
-                pickup = Pickup::MAX_AMOUNT;
-                break;
-            case 6:
-                pickup = Pickup::TOO_FAR;
-                break;
-            case 7:
-                pickup = Pickup::STACK_AMOUNT;
-                break;
-            default:
-                pickup = Pickup::UNKNOWN;
-                UNIMPLIMENTEDPACKET;
-                break;
-        }
-        if (localPlayer)
-        {
-            localPlayer->pickedUp(itemInfo,
-                0,
-                color,
-                floorId,
-                pickup);
-        }
-    }
-    else
-    {
-        if (localPlayer)
-        {
-            localPlayer->pickedUp(itemInfo,
-                amount,
-                color,
-                floorId,
-                Pickup::OKAY);
-        }
-
-        if (inventory)
-        {
-            const Item *const item = inventory->getItem(index);
-
-            if (item && item->getId() == itemId)
-                amount += item->getQuantity();
-
-            inventory->setItem(index,
-                itemId,
-                itemType,
-                amount,
-                refine,
-                color,
-                fromBool(identified, Identified),
-                fromBool(damaged, Damaged),
-                Favorite_false,
-                fromBool(equipType, Equipm),
-                Equipped_false);
-            inventory->setCards(index, cards, 4);
-        }
-        ArrowsListener::distributeEvent();
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventoryAdd")
-}
-
-void InventoryHandler::processPlayerInventory(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventory")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-    if (PlayerInfo::getEquipment())
-    {
-        // Clear inventory - this will be a complete refresh
-        mEquips.clear();
-        PlayerInfo::getEquipment()->setBackend(&mEquips);
-    }
-
-    if (inventory)
-        inventory->clear();
-
-    msg.readInt16("len");
-
-    const int number = (msg.getLength() - 4) / 24;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const int itemType = msg.readUInt8("item type");
-        const int amount = msg.readInt16("count");
-        msg.readInt32("wear state / equip");
-        int cards[4];
-        for (int f = 0; f < 4; f++)
-            cards[f] = msg.readInt16("card");
-        msg.readInt32("hire expire date (?)");
-        ItemFlags flags;
-        flags.byte = msg.readUInt8("flags");
-
-        if (inventory)
-        {
-            inventory->setItem(index,
-                itemId,
-                itemType,
-                amount,
-                0,
-                ItemColorManager::getColorFromCards(&cards[0]),
-                fromBool(flags.bits.isIdentified, Identified),
-                fromBool(flags.bits.isDamaged, Damaged),
-                fromBool(flags.bits.isFavorite, Favorite),
-                Equipm_false,
-                Equipped_false);
-            inventory->setCards(index, cards, 4);
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventory")
-}
-
-void InventoryHandler::processPlayerStorage(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventory")
-    mInventoryItems.clear();
-
-    msg.readInt16("len");
-    msg.readString(24, "storage name");
-
-    const int number = (msg.getLength() - 4 - 24) / 24;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        const int index = msg.readInt16("item index") - STORAGE_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const int itemType = msg.readUInt8("item type");
-        const int amount = msg.readInt16("count");
-        msg.readInt32("wear state / equip");
-        int cards[4];
-        for (int f = 0; f < 4; f++)
-            cards[f] = msg.readInt16("card");
-        msg.readInt32("hire expire date (?)");
-        ItemFlags flags;
-        flags.byte = msg.readUInt8("flags");
-
-        mInventoryItems.push_back(Ea::InventoryItem(index,
-            itemId,
-            itemType,
-            cards,
-            amount,
-            0,
-            ItemColorManager::getColorFromCards(&cards[0]),
-            fromBool(flags.bits.isIdentified, Identified),
-            fromBool(flags.bits.isDamaged, Damaged),
-            fromBool(flags.bits.isFavorite, Favorite),
-            Equipm_false));
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventory")
-}
-
-void InventoryHandler::processPlayerEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerEquip")
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    const int equipType = msg.readInt32("wear location");
-    msg.readInt16("sprite");
-    const uint8_t flag = msg.readUInt8("result");
-
-    switch (flag)
-    {
-        case 0:
-            mEquips.setEquipment(getSlot(equipType), index);
-            break;
-        case 1:
-            NotifyManager::notify(NotifyTypes::EQUIP_FAILED_LEVEL);
-            break;
-
-        case 2:
-        default:
-            NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
-            break;
-    }
-    BLOCK_END("InventoryHandler::processPlayerEquip")
-}
-
-void InventoryHandler::processPlayerUnEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerUnEquip")
-    msg.readInt16("index");
-    const int equipType = msg.readInt32("wear location");
-    const uint8_t flag = msg.readUInt8("result");
-
-    // +++ need use UNEQUIP_FAILED event
-    if (flag)
-        NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
-    else
-        mEquips.setEquipment(getSlot(equipType), -1);
-    if (equipType & 0x8000)
-        ArrowsListener::distributeEvent();
-    BLOCK_END("InventoryHandler::processPlayerUnEquip")
-}
-
-void InventoryHandler::processPlayerInventoryRemove2(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventoryRemove2")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    // +++ here possible use particle or text/sound effects
-    // for different reasons
-    msg.readInt16("reason");
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    const int amount = msg.readInt16("amount");
-    if (inventory)
-    {
-        if (Item *const item = inventory->getItem(index))
-        {
-            item->increaseQuantity(-amount);
-            if (item->getQuantity() == 0)
-                inventory->removeItemAt(index);
-            ArrowsListener::distributeEvent();
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventoryRemove2")
-}
-
-void InventoryHandler::processPlayerStorageEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageEquip")
-    msg.readInt16("len");
-    const int number = (msg.getLength() - 4 - 24) / 31;
-
-    msg.readString(24, "storage name");
-    for (int loop = 0; loop < number; loop++)
-    {
-        const int index = msg.readInt16("index") - STORAGE_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const int itemType = msg.readUInt8("item type");
-        const int amount = 1;
-        msg.readInt32("location");
-        msg.readInt32("wear state");
-        const uint8_t refine = msg.readUInt8("refine level");
-        int cards[4];
-        for (int f = 0; f < 4; f++)
-            cards[f] = msg.readInt16("card");
-        msg.readInt32("hire expire date");
-        msg.readInt16("bind on equip");
-        msg.readInt16("sprite");
-        ItemFlags flags;
-        flags.byte = msg.readUInt8("flags");
-
-        mInventoryItems.push_back(Ea::InventoryItem(index,
-            itemId,
-            itemType,
-            cards,
-            amount,
-            refine,
-            ItemColorManager::getColorFromCards(&cards[0]),
-            fromBool(flags.bits.isIdentified, Identified),
-            fromBool(flags.bits.isDamaged, Damaged),
-            fromBool(flags.bits.isFavorite, Favorite),
-            Equipm_false));
-    }
-    BLOCK_END("InventoryHandler::processPlayerStorageEquip")
-}
-
-void InventoryHandler::processPlayerStorageAdd(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageAdd")
-    // Move an item into storage
-    const int index = msg.readInt16("index") - STORAGE_OFFSET;
-    const int amount = msg.readInt32("amount");
-    const int itemId = msg.readInt16("item id");
-    const int itemType = msg.readUInt8("type");
-    const unsigned char identified = msg.readUInt8("identify");
-    const Damaged damaged = fromBool(msg.readUInt8("attribute"), Damaged);
-    const uint8_t refine = msg.readUInt8("refine");
-    int cards[4];
-    for (int f = 0; f < 4; f++)
-        cards[f] = msg.readInt16("card");
-
-    const ItemColor color = ItemColorManager::getColorFromCards(&cards[0]);
-    if (Item *const item = mStorage->getItem(index))
-    {
-        item->setId(itemId, color);
-        item->increaseQuantity(amount);
-    }
-    else
-    {
-        if (mStorage)
-        {
-            mStorage->setItem(index,
-                itemId,
-                itemType,
-                amount,
-                refine,
-                color,
-                fromBool(identified, Identified),
-                damaged,
-                Favorite_false,
-                Equipm_false,
-                Equipped_false);
-            mStorage->setCards(index, cards, 4);
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerStorageAdd")
-}
-
-void InventoryHandler::processPlayerUseCard(Net::MessageIn &msg)
-{
-    const Inventory *const inv = PlayerInfo::getInventory();
-    const int index = inventoryHandler->getItemIndex();
-    const Item *item1 = nullptr;
-    if (inv)
-        item1 = inv->getItem(index);
-    SellDialog *const dialog = CREATEWIDGETR(InsertCardDialog,
-        index, item1);
-
-    const int count = (msg.readInt16("len") - 4) / 2;
-    for (int f = 0; f < count; f ++)
-    {
-        const int itemIndex = msg.readInt16("item index") - INVENTORY_OFFSET;
-        if (!inv)
-            continue;
-        const Item *const item = inv->getItem(itemIndex);
-        if (!item)
-            continue;
-        dialog->addItem(item, 0);
-    }
-}
-
-void InventoryHandler::processPlayerInsertCard(Net::MessageIn &msg)
-{
-    const int itemIndex = msg.readInt16("item index") - INVENTORY_OFFSET;
-    const int cardIndex = msg.readInt16("card index") - INVENTORY_OFFSET;
-    if (msg.readUInt8("flag"))
-    {
-        NotifyManager::notify(NotifyTypes::CARD_INSERT_FAILED);
-    }
-    else
-    {
-        NotifyManager::notify(NotifyTypes::CARD_INSERT_SUCCESS);
-        Inventory *const inv = PlayerInfo::getInventory();
-        if (!inv)
-            return;
-        Item *const card = inv->getItem(cardIndex);
-        int cardId = 0;
-        if (card)
-        {
-            cardId = card->getId();
-            card->increaseQuantity(-1);
-            if (card->getQuantity() == 0)
-                inv->removeItemAt(cardIndex);
-        }
-        Item *const item = inv->getItem(itemIndex);
-        if (item)
-        {
-            item->addCard(cardId);
-            item->updateColor();
-            itemPopup->resetPopup();
-        }
-    }
-}
-
 void InventoryHandler::selectEgg(const Item *const item) const
 {
     if (!item)
@@ -902,40 +442,6 @@ void InventoryHandler::selectEgg(const Item *const item) const
     menu = MenuType::Unknown;
 }
 
-void InventoryHandler::processPlayerItemRentalTime(Net::MessageIn &msg)
-{
-    const int id = msg.readInt16("item id");
-    const int seconds = msg.readInt32("seconds");
-    const ItemInfo &info = ItemDB::get(id);
-    const std::string timeStr = timeDiffToString(seconds);
-    NotifyManager::notify(NotifyTypes::RENTAL_TIME_LEFT,
-        // TRANSLATORS: notification message
-        strprintf(_("Left %s rental time for item %s."),
-        timeStr.c_str(), info.getName().c_str()));
-}
-
-void InventoryHandler::processPlayerItemRentalExpired(Net::MessageIn &msg)
-{
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    const int id = msg.readInt16("item id");
-    const ItemInfo &info = ItemDB::get(id);
-
-    NotifyManager::notify(NotifyTypes::RENTAL_TIME_EXPIRED,
-        info.getName());
-    if (inventory)
-    {
-        if (Item *const item = inventory->getItem(index))
-        {
-            item->increaseQuantity(-item->getQuantity());
-            inventory->removeItemAt(index);
-            ArrowsListener::distributeEvent();
-        }
-    }
-}
-
 int InventoryHandler::convertFromServerSlot(const int serverSlot) const
 {
     if (serverSlot < 0 || serverSlot > 15)
@@ -944,372 +450,4 @@ int InventoryHandler::convertFromServerSlot(const int serverSlot) const
     return static_cast<int>(EQUIP_CONVERT[serverSlot]);
 }
 
-void InventoryHandler::processPlayerStorageRemove(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageRemove")
-    // Move an item out of storage
-    const int index = msg.readInt16("index") - STORAGE_OFFSET;
-    const int amount = msg.readInt32("amount");
-    if (mStorage)
-    {
-        if (Item *const item = mStorage->getItem(index))
-        {
-            item->increaseQuantity(-amount);
-            if (item->getQuantity() == 0)
-                mStorage->removeItemAt(index);
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerStorageRemove")
-}
-
-void InventoryHandler::processCartInfo(Net::MessageIn &msg)
-{
-    msg.readInt16("cart items used");
-    const int size = msg.readInt16("max cart items");
-    PlayerInfo::setAttribute(Attributes::CART_TOTAL_WEIGHT,
-        msg.readInt32("cart weight"));
-    PlayerInfo::setAttribute(Attributes::CART_MAX_WEIGHT,
-        msg.readInt32("max cart weight"));
-    if (mCartItems.empty())
-        return;
-
-    Inventory *const inv = PlayerInfo::getCartInventory();
-    if (!inv)
-        return;
-
-    inv->resize(size);
-
-    FOR_EACH (Ea::InventoryItems::const_iterator, it, mCartItems)
-    {
-        inv->setItem((*it).slot,
-            (*it).id,
-            (*it).type,
-            (*it).quantity,
-            (*it).refine,
-            (*it).color,
-            (*it).identified,
-            (*it).damaged,
-            (*it).favorite,
-            (*it).equip,
-            Equipped_false);
-    }
-    mCartItems.clear();
-}
-
-void InventoryHandler::processCartRemove(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-    // +++ need close or clear cart?
-}
-
-void InventoryHandler::processPlayerCartAdd(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerCartAdd")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getCartInventory() : nullptr;
-
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    int amount = msg.readInt32("count");
-    const int itemId = msg.readInt16("item id");
-    const int itemType = msg.readUInt8("item type");
-    uint8_t identified = msg.readUInt8("identified");
-    const Damaged damaged = fromBool(msg.readUInt8("attribute"), Damaged);
-    const uint8_t refine = msg.readUInt8("refine");
-    int cards[4];
-    for (int f = 0; f < 4; f++)
-        cards[f] = msg.readInt16("card");
-
-    if (inventory)
-    {
-        const Item *const item = inventory->getItem(index);
-
-        if (item && item->getId() == itemId)
-            amount += item->getQuantity();
-
-        inventory->setItem(index,
-            itemId,
-            itemType,
-            amount,
-            refine,
-            ItemColorManager::getColorFromCards(&cards[0]),
-            fromBool(identified, Identified),
-            damaged,
-            Favorite_false,
-            Equipm_false,
-            Equipped_false);
-        inventory->setCards(index, cards, 4);
-    }
-    BLOCK_END("InventoryHandler::processPlayerCartAdd")
-}
-
-void InventoryHandler::processPlayerCartEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerCartEquip")
-    msg.readInt16("len");
-    const int number = (msg.getLength() - 4) / 31;
-    for (int loop = 0; loop < number; loop++)
-    {
-        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const int itemType = msg.readUInt8("item type");
-        const int amount = 1;
-        msg.readInt32("location");
-        msg.readInt32("wear state");
-        const uint8_t refine = msg.readUInt8("refine level");
-        int cards[4];
-        for (int f = 0; f < 4; f++)
-            cards[f] = msg.readInt16("card");
-        msg.readInt32("hire expire date");
-        msg.readInt16("bind on equip");
-        msg.readInt16("sprite");
-        ItemFlags flags;
-        flags.byte = msg.readUInt8("flags");
-
-        mCartItems.push_back(Ea::InventoryItem(index,
-            itemId,
-            itemType,
-            cards,
-            amount,
-            refine,
-            ItemColorManager::getColorFromCards(&cards[0]),
-            fromBool(flags.bits.isIdentified, Identified),
-            fromBool(flags.bits.isDamaged, Damaged),
-            fromBool(flags.bits.isFavorite, Favorite),
-            Equipm_false));
-    }
-    BLOCK_END("InventoryHandler::processPlayerCartEquip")
-}
-
-void InventoryHandler::processPlayerCartItems(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerCartItems")
-    mInventoryItems.clear();
-
-    msg.readInt16("len");
-    const int number = (msg.getLength() - 4) / 23;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const int itemType = msg.readUInt8("item type");
-        const int amount = msg.readInt16("count");
-        msg.readInt32("wear state / equip");
-        int cards[4];
-        for (int f = 0; f < 4; f++)
-            cards[f] = msg.readInt16("card");
-        msg.readInt32("hire expire date (?)");
-        ItemFlags flags;
-        flags.byte = msg.readUInt8("flags");
-
-        mCartItems.push_back(Ea::InventoryItem(index,
-            itemId,
-            itemType,
-            cards,
-            amount,
-            0,
-            ItemColorManager::getColorFromCards(&cards[0]),
-            fromBool(flags.bits.isIdentified, Identified),
-            fromBool(flags.bits.isDamaged, Damaged),
-            fromBool(flags.bits.isFavorite, Favorite),
-            Equipm_false));
-    }
-    BLOCK_END("InventoryHandler::processPlayerCartItems")
-}
-
-void InventoryHandler::processPlayerCartRemove(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerCartRemove")
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    const int amount = msg.readInt32("amount");
-
-    Inventory *const inv = PlayerInfo::getCartInventory();
-    if (!inv)
-        return;
-
-    if (Item *const item = inv->getItem(index))
-    {
-        item->increaseQuantity(-amount);
-        if (item->getQuantity() == 0)
-            inv->removeItemAt(index);
-    }
-    BLOCK_END("InventoryHandler::processPlayerCartRemove")
-}
-
-void InventoryHandler::processPlayerIdentifyList(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    menu = MenuType::Identify;
-    const int count = msg.readInt16("len") - 4;
-    for (int f = 0; f < count; f ++)
-        msg.readInt16("inv index");
-}
-
-void InventoryHandler::processPlayerIdentified(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    msg.readInt16("inv index");
-    msg.readUInt8("flag");
-}
-
-void InventoryHandler::processPlayerRefine(Net::MessageIn &msg)
-{
-    const int flag = msg.readInt16("flag");
-    const int index = msg.readInt16("inv index") - INVENTORY_OFFSET;
-    msg.readInt16("refine");
-    const Inventory *const inv = PlayerInfo::getInventory();
-    const Item *item = nullptr;
-    int notifyType;
-    std::string message;
-    std::string itemName;
-    if (inv)
-        item = inv->getItem(index);
-    if (item)
-    {
-        itemName = item->getName();
-    }
-    else
-    {
-        // TRANSLATORS: unknown item
-        itemName = _("Unknown item");
-    }
-    switch (flag)
-    {
-        case 0:
-            notifyType = NotifyTypes::REFINE_SUCCESS;
-            break;
-        case 1:
-            notifyType = NotifyTypes::REFINE_FAILURE;
-            break;
-        case 2:
-            notifyType = NotifyTypes::REFINE_DOWNGRADE;
-            break;
-        default:
-            UNIMPLIMENTEDPACKET;
-            notifyType = NotifyTypes::REFINE_UNKNOWN;
-            break;
-    }
-    NotifyManager::notify(notifyType, itemName);
-}
-
-void InventoryHandler::processPlayerRepairList(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    const int count = (msg.readInt16("len") - 4) / 13;
-    for (int f = 0; f < count; f ++)
-    {
-        msg.readInt16("index");
-        msg.readInt16("item id");
-        msg.readUInt8("refine");
-        for (int d = 0; d < 4; d ++)
-            msg.readInt16("card");
-    }
-    menu = MenuType::RepairWespon;
-}
-
-void InventoryHandler::processPlayerRepairEffect(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    msg.readInt16("item index");
-    msg.readUInt8("flag");
-}
-
-void InventoryHandler::processPlayerRefineList(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    const int count = (msg.readInt16("len") - 4) / 13;
-
-    for (int f = 0; f < count; f ++)
-    {
-        msg.readInt16("item index");
-        msg.readInt16("item id");
-        msg.readUInt8("refine");
-        for (int d = 0; d < 4; d ++)
-            msg.readInt16("card");
-    }
-    menu = MenuType::WeaponeRefine;
-}
-
-void InventoryHandler::processPlayerStoragePassword(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    msg.readInt16("info");
-}
-
-void InventoryHandler::processPlayerStoragePasswordResult(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    msg.readInt16("result");
-    msg.readInt16("error count");
-}
-
-void InventoryHandler::processPlayerCookingList(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    const int count = (msg.readInt16("len") - 6) / 2;
-    msg.readInt16("list type");
-    for (int f = 0; f < count; f ++)
-        msg.readInt16("item id");
-}
-
-void InventoryHandler::processItemDamaged(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    msg.readInt16("position");
-    msg.readBeingId("account id");
-}
-
-void InventoryHandler::processFavoriteItem(Net::MessageIn &msg)
-{
-    UNIMPLIMENTEDPACKET;
-
-    msg.readInt16("item index");
-    msg.readUInt8("favorite (0 - favorite)");
-}
-
-void InventoryHandler::processCartAddError(Net::MessageIn &msg)
-{
-    switch(msg.readUInt8("flag"))
-    {
-        case 0:
-            NotifyManager::notify(NotifyTypes::CART_ADD_WEIGHT_ERROR);
-            break;
-        case 1:
-            NotifyManager::notify(NotifyTypes::CART_ADD_COUNT_ERROR);
-            break;
-        default:
-            break;
-    }
-}
-
-void InventoryHandler::processBindItem(Net::MessageIn &msg)
-{
-    const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
-    const Inventory *const inv = PlayerInfo::getInventory();
-    if (inv)
-    {
-        std::string itemName;
-        const Item *item = inv->getItem(index);
-        if (item)
-        {
-            itemName = item->getName();
-        }
-        else
-        {
-            // TRANSLATORS: unknown item message
-            itemName = _("Unknown item");
-        }
-        NotifyManager::notify(NotifyTypes::BOUND_ITEM, itemName);
-    }
-}
-
 }  // namespace EAthena
diff --git a/src/net/eathena/inventoryhandler.h b/src/net/eathena/inventoryhandler.h
index c593a2c67..b234aada7 100644
--- a/src/net/eathena/inventoryhandler.h
+++ b/src/net/eathena/inventoryhandler.h
@@ -77,75 +77,6 @@ class InventoryHandler final : public MessageHandler,
         int getItemIndex() const override final A_WARN_UNUSED
         { return mItemIndex; }
 
-    protected:
-        static void processPlayerEquipment(Net::MessageIn &msg);
-
-        static void processPlayerInventoryAdd(Net::MessageIn &msg);
-
-        static void processPlayerInventory(Net::MessageIn &msg);
-
-        static void processPlayerStorage(Net::MessageIn &msg);
-
-        static void processPlayerEquip(Net::MessageIn &msg);
-
-        static void processPlayerUnEquip(Net::MessageIn &msg);
-
-        static void processPlayerInventoryRemove2(Net::MessageIn &msg);
-
-        static void processPlayerStorageEquip(Net::MessageIn &msg);
-
-        static void processPlayerStorageAdd(Net::MessageIn &msg);
-
-        static void processPlayerUseCard(Net::MessageIn &msg);
-
-        static void processPlayerInsertCard(Net::MessageIn &msg);
-
-        static void processPlayerItemRentalTime(Net::MessageIn &msg);
-
-        static void processPlayerItemRentalExpired(Net::MessageIn &msg);
-
-        static void processPlayerStorageRemove(Net::MessageIn &msg);
-
-        static void processCartInfo(Net::MessageIn &msg);
-
-        static void processCartRemove(Net::MessageIn &msg);
-
-        static void processPlayerCartAdd(Net::MessageIn &msg);
-
-        static void processPlayerCartEquip(Net::MessageIn &msg);
-
-        static void processPlayerCartItems(Net::MessageIn &msg);
-
-        static void processPlayerCartRemove(Net::MessageIn &msg);
-
-        static void processPlayerIdentifyList(Net::MessageIn &msg);
-
-        static void processPlayerIdentified(Net::MessageIn &msg);
-
-        static void processPlayerRefine(Net::MessageIn &msg);
-
-        static void processPlayerRepairList(Net::MessageIn &msg);
-
-        static void processPlayerRepairEffect(Net::MessageIn &msg);
-
-        static void processPlayerRefineList(Net::MessageIn &msg);
-
-        static void processPlayerStoragePassword(Net::MessageIn &msg);
-
-        static void processPlayerStoragePasswordResult(Net::MessageIn &msg);
-
-        static void processPlayerCookingList(Net::MessageIn &msg);
-
-        static void processItemDamaged(Net::MessageIn &msg);
-
-        static void processFavoriteItem(Net::MessageIn &msg);
-
-        static void processCartAddError(Net::MessageIn &msg);
-
-        static void processBindItem(Net::MessageIn &msg);
-
-        static Ea::InventoryItems mCartItems;
-
     private:
         int mItemIndex;
 };
diff --git a/src/net/eathena/inventoryrecv.cpp b/src/net/eathena/inventoryrecv.cpp
new file mode 100644
index 000000000..186e34cbd
--- /dev/null
+++ b/src/net/eathena/inventoryrecv.cpp
@@ -0,0 +1,940 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2015  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/eathena/inventoryrecv.h"
+
+#include "notifymanager.h"
+#include "itemcolormanager.h"
+
+#include "being/localplayer.h"
+
+#include "enums/equipslot.h"
+
+#include "enums/resources/notifytypes.h"
+
+#include "gui/popups/itempopup.h"
+
+#include "gui/widgets/createwidget.h"
+
+#include "gui/windows/insertcarddialog.h"
+
+#include "listeners/arrowslistener.h"
+
+#include "net/eathena/itemflags.h"
+#include "net/eathena/menu.h"
+#include "net/eathena/messageout.h"
+#include "net/eathena/protocol.h"
+
+#include "net/ea/eaprotocol.h"
+#include "net/ea/equipbackend.h"
+#include "net/ea/inventoryrecv.h"
+
+#include "resources/iteminfo.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "debug.h"
+
+namespace EAthena
+{
+
+namespace InventoryRecv
+{
+    Ea::InventoryItems mCartItems;
+}
+
+void InventoryRecv::processPlayerEquipment(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerEquipment")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    msg.readInt16("len");
+    Equipment *const equipment = PlayerInfo::getEquipment();
+    if (equipment && !equipment->getBackend())
+    {   // look like SMSG_PLAYER_INVENTORY was not received
+        Ea::InventoryRecv::mEquips.clear();
+        equipment->setBackend(&Ea::InventoryRecv::mEquips);
+    }
+    const int number = (msg.getLength() - 4) / 31;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const int itemType = msg.readUInt8("item type");
+        msg.readInt32("location");
+        const int equipType = msg.readInt32("wear state");
+        const uint8_t refine = static_cast<uint8_t>(msg.readInt8("refine"));
+        int cards[4];
+        for (int f = 0; f < 4; f++)
+            cards[f] = msg.readInt16("card");
+        msg.readInt32("hire expire date (?)");
+        msg.readInt16("equip type");
+        msg.readInt16("item sprite number");
+        ItemFlags flags;
+        flags.byte = msg.readUInt8("flags");
+        if (inventory)
+        {
+            inventory->setItem(index,
+                itemId,
+                itemType,
+                1,
+                refine,
+                ItemColorManager::getColorFromCards(&cards[0]),
+                fromBool(flags.bits.isIdentified, Identified),
+                fromBool(flags.bits.isDamaged, Damaged),
+                fromBool(flags.bits.isFavorite, Favorite),
+                Equipm_true,
+                Equipped_false);
+            inventory->setCards(index, cards, 4);
+        }
+
+        if (equipType)
+        {
+            Ea::InventoryRecv::mEquips.setEquipment(
+                Ea::InventoryRecv::getSlot(equipType), index);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerEquipment")
+}
+
+void InventoryRecv::processPlayerInventoryAdd(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventoryAdd")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    if (PlayerInfo::getEquipment()
+        && !PlayerInfo::getEquipment()->getBackend())
+    {   // look like SMSG_PLAYER_INVENTORY was not received
+        Ea::InventoryRecv::mEquips.clear();
+        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
+    }
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    int amount = msg.readInt16("count");
+    const int itemId = msg.readInt16("item id");
+    uint8_t identified = msg.readUInt8("identified");
+    const uint8_t damaged = msg.readUInt8("is damaged");
+    const uint8_t refine = msg.readUInt8("refine");
+    int cards[4];
+    for (int f = 0; f < 4; f++)
+        cards[f] = msg.readInt16("card");
+    const int equipType = msg.readInt32("location");
+    const int itemType = msg.readUInt8("item type");
+    const unsigned char err = msg.readUInt8("result");
+    msg.readInt32("hire expire date");
+    msg.readInt16("bind on equip");
+
+    const ItemColor color = ItemColorManager::getColorFromCards(&cards[0]);
+    const ItemInfo &itemInfo = ItemDB::get(itemId);
+    BeingId floorId;
+    if (Ea::InventoryRecv::mSentPickups.empty())
+    {
+        floorId = BeingId_zero;
+    }
+    else
+    {
+        floorId = Ea::InventoryRecv::mSentPickups.front();
+        Ea::InventoryRecv::mSentPickups.pop();
+    }
+
+    if (err)
+    {
+        PickupT pickup;
+        switch (err)
+        {
+            case 1:
+                pickup = Pickup::BAD_ITEM;
+                break;
+            case 2:
+                pickup = Pickup::TOO_HEAVY;
+                break;
+            case 4:
+                pickup = Pickup::INV_FULL;
+                break;
+            case 5:
+                pickup = Pickup::MAX_AMOUNT;
+                break;
+            case 6:
+                pickup = Pickup::TOO_FAR;
+                break;
+            case 7:
+                pickup = Pickup::STACK_AMOUNT;
+                break;
+            default:
+                pickup = Pickup::UNKNOWN;
+                UNIMPLIMENTEDPACKET;
+                break;
+        }
+        if (localPlayer)
+        {
+            localPlayer->pickedUp(itemInfo,
+                0,
+                color,
+                floorId,
+                pickup);
+        }
+    }
+    else
+    {
+        if (localPlayer)
+        {
+            localPlayer->pickedUp(itemInfo,
+                amount,
+                color,
+                floorId,
+                Pickup::OKAY);
+        }
+
+        if (inventory)
+        {
+            const Item *const item = inventory->getItem(index);
+
+            if (item && item->getId() == itemId)
+                amount += item->getQuantity();
+
+            inventory->setItem(index,
+                itemId,
+                itemType,
+                amount,
+                refine,
+                color,
+                fromBool(identified, Identified),
+                fromBool(damaged, Damaged),
+                Favorite_false,
+                fromBool(equipType, Equipm),
+                Equipped_false);
+            inventory->setCards(index, cards, 4);
+        }
+        ArrowsListener::distributeEvent();
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventoryAdd")
+}
+
+void InventoryRecv::processPlayerInventory(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventory")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+    if (PlayerInfo::getEquipment())
+    {
+        // Clear inventory - this will be a complete refresh
+        Ea::InventoryRecv::mEquips.clear();
+        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
+    }
+
+    if (inventory)
+        inventory->clear();
+
+    msg.readInt16("len");
+
+    const int number = (msg.getLength() - 4) / 24;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const int itemType = msg.readUInt8("item type");
+        const int amount = msg.readInt16("count");
+        msg.readInt32("wear state / equip");
+        int cards[4];
+        for (int f = 0; f < 4; f++)
+            cards[f] = msg.readInt16("card");
+        msg.readInt32("hire expire date (?)");
+        ItemFlags flags;
+        flags.byte = msg.readUInt8("flags");
+
+        if (inventory)
+        {
+            inventory->setItem(index,
+                itemId,
+                itemType,
+                amount,
+                0,
+                ItemColorManager::getColorFromCards(&cards[0]),
+                fromBool(flags.bits.isIdentified, Identified),
+                fromBool(flags.bits.isDamaged, Damaged),
+                fromBool(flags.bits.isFavorite, Favorite),
+                Equipm_false,
+                Equipped_false);
+            inventory->setCards(index, cards, 4);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventory")
+}
+
+void InventoryRecv::processPlayerStorage(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventory")
+    Ea::InventoryRecv::mInventoryItems.clear();
+
+    msg.readInt16("len");
+    msg.readString(24, "storage name");
+
+    const int number = (msg.getLength() - 4 - 24) / 24;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        const int index = msg.readInt16("item index") - STORAGE_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const int itemType = msg.readUInt8("item type");
+        const int amount = msg.readInt16("count");
+        msg.readInt32("wear state / equip");
+        int cards[4];
+        for (int f = 0; f < 4; f++)
+            cards[f] = msg.readInt16("card");
+        msg.readInt32("hire expire date (?)");
+        ItemFlags flags;
+        flags.byte = msg.readUInt8("flags");
+
+        Ea::InventoryRecv::mInventoryItems.push_back(Ea::InventoryItem(
+            index,
+            itemId,
+            itemType,
+            cards,
+            amount,
+            0,
+            ItemColorManager::getColorFromCards(&cards[0]),
+            fromBool(flags.bits.isIdentified, Identified),
+            fromBool(flags.bits.isDamaged, Damaged),
+            fromBool(flags.bits.isFavorite, Favorite),
+            Equipm_false));
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventory")
+}
+
+void InventoryRecv::processPlayerEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerEquip")
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    const int equipType = msg.readInt32("wear location");
+    msg.readInt16("sprite");
+    const uint8_t flag = msg.readUInt8("result");
+
+    switch (flag)
+    {
+        case 0:
+            Ea::InventoryRecv::mEquips.setEquipment(
+                Ea::InventoryRecv::getSlot(equipType), index);
+            break;
+        case 1:
+            NotifyManager::notify(NotifyTypes::EQUIP_FAILED_LEVEL);
+            break;
+
+        case 2:
+        default:
+            NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
+            break;
+    }
+    BLOCK_END("InventoryRecv::processPlayerEquip")
+}
+
+void InventoryRecv::processPlayerUnEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerUnEquip")
+    msg.readInt16("index");
+    const int equipType = msg.readInt32("wear location");
+    const uint8_t flag = msg.readUInt8("result");
+
+    // +++ need use UNEQUIP_FAILED event
+    if (flag)
+    {
+        NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
+    }
+    else
+    {
+        Ea::InventoryRecv::mEquips.setEquipment(
+            Ea::InventoryRecv::getSlot(equipType), -1);
+    }
+    if (equipType & 0x8000)
+        ArrowsListener::distributeEvent();
+    BLOCK_END("InventoryRecv::processPlayerUnEquip")
+}
+
+void InventoryRecv::processPlayerInventoryRemove2(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventoryRemove2")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    // +++ here possible use particle or text/sound effects
+    // for different reasons
+    msg.readInt16("reason");
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    const int amount = msg.readInt16("amount");
+    if (inventory)
+    {
+        if (Item *const item = inventory->getItem(index))
+        {
+            item->increaseQuantity(-amount);
+            if (item->getQuantity() == 0)
+                inventory->removeItemAt(index);
+            ArrowsListener::distributeEvent();
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventoryRemove2")
+}
+
+void InventoryRecv::processPlayerStorageEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageEquip")
+    msg.readInt16("len");
+    const int number = (msg.getLength() - 4 - 24) / 31;
+
+    msg.readString(24, "storage name");
+    for (int loop = 0; loop < number; loop++)
+    {
+        const int index = msg.readInt16("index") - STORAGE_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const int itemType = msg.readUInt8("item type");
+        const int amount = 1;
+        msg.readInt32("location");
+        msg.readInt32("wear state");
+        const uint8_t refine = msg.readUInt8("refine level");
+        int cards[4];
+        for (int f = 0; f < 4; f++)
+            cards[f] = msg.readInt16("card");
+        msg.readInt32("hire expire date");
+        msg.readInt16("bind on equip");
+        msg.readInt16("sprite");
+        ItemFlags flags;
+        flags.byte = msg.readUInt8("flags");
+
+        Ea::InventoryRecv::mInventoryItems.push_back(Ea::InventoryItem(
+            index,
+            itemId,
+            itemType,
+            cards,
+            amount,
+            refine,
+            ItemColorManager::getColorFromCards(&cards[0]),
+            fromBool(flags.bits.isIdentified, Identified),
+            fromBool(flags.bits.isDamaged, Damaged),
+            fromBool(flags.bits.isFavorite, Favorite),
+            Equipm_false));
+    }
+    BLOCK_END("InventoryRecv::processPlayerStorageEquip")
+}
+
+void InventoryRecv::processPlayerStorageAdd(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageAdd")
+    // Move an item into storage
+    const int index = msg.readInt16("index") - STORAGE_OFFSET;
+    const int amount = msg.readInt32("amount");
+    const int itemId = msg.readInt16("item id");
+    const int itemType = msg.readUInt8("type");
+    const unsigned char identified = msg.readUInt8("identify");
+    const Damaged damaged = fromBool(msg.readUInt8("attribute"), Damaged);
+    const uint8_t refine = msg.readUInt8("refine");
+    int cards[4];
+    for (int f = 0; f < 4; f++)
+        cards[f] = msg.readInt16("card");
+
+    const ItemColor color = ItemColorManager::getColorFromCards(&cards[0]);
+    if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
+    {
+        item->setId(itemId, color);
+        item->increaseQuantity(amount);
+    }
+    else
+    {
+        if (Ea::InventoryRecv::mStorage)
+        {
+            Ea::InventoryRecv::mStorage->setItem(index,
+                itemId,
+                itemType,
+                amount,
+                refine,
+                color,
+                fromBool(identified, Identified),
+                damaged,
+                Favorite_false,
+                Equipm_false,
+                Equipped_false);
+            Ea::InventoryRecv::mStorage->setCards(index, cards, 4);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerStorageAdd")
+}
+
+void InventoryRecv::processPlayerUseCard(Net::MessageIn &msg)
+{
+    const Inventory *const inv = PlayerInfo::getInventory();
+    const int index = inventoryHandler->getItemIndex();
+    const Item *item1 = nullptr;
+    if (inv)
+        item1 = inv->getItem(index);
+    SellDialog *const dialog = CREATEWIDGETR(InsertCardDialog,
+        index, item1);
+
+    const int count = (msg.readInt16("len") - 4) / 2;
+    for (int f = 0; f < count; f ++)
+    {
+        const int itemIndex = msg.readInt16("item index") - INVENTORY_OFFSET;
+        if (!inv)
+            continue;
+        const Item *const item = inv->getItem(itemIndex);
+        if (!item)
+            continue;
+        dialog->addItem(item, 0);
+    }
+}
+
+void InventoryRecv::processPlayerInsertCard(Net::MessageIn &msg)
+{
+    const int itemIndex = msg.readInt16("item index") - INVENTORY_OFFSET;
+    const int cardIndex = msg.readInt16("card index") - INVENTORY_OFFSET;
+    if (msg.readUInt8("flag"))
+    {
+        NotifyManager::notify(NotifyTypes::CARD_INSERT_FAILED);
+    }
+    else
+    {
+        NotifyManager::notify(NotifyTypes::CARD_INSERT_SUCCESS);
+        Inventory *const inv = PlayerInfo::getInventory();
+        if (!inv)
+            return;
+        Item *const card = inv->getItem(cardIndex);
+        int cardId = 0;
+        if (card)
+        {
+            cardId = card->getId();
+            card->increaseQuantity(-1);
+            if (card->getQuantity() == 0)
+                inv->removeItemAt(cardIndex);
+        }
+        Item *const item = inv->getItem(itemIndex);
+        if (item)
+        {
+            item->addCard(cardId);
+            item->updateColor();
+            itemPopup->resetPopup();
+        }
+    }
+}
+
+void InventoryRecv::processPlayerItemRentalTime(Net::MessageIn &msg)
+{
+    const int id = msg.readInt16("item id");
+    const int seconds = msg.readInt32("seconds");
+    const ItemInfo &info = ItemDB::get(id);
+    const std::string timeStr = timeDiffToString(seconds);
+    NotifyManager::notify(NotifyTypes::RENTAL_TIME_LEFT,
+        // TRANSLATORS: notification message
+        strprintf(_("Left %s rental time for item %s."),
+        timeStr.c_str(), info.getName().c_str()));
+}
+
+void InventoryRecv::processPlayerItemRentalExpired(Net::MessageIn &msg)
+{
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    const int id = msg.readInt16("item id");
+    const ItemInfo &info = ItemDB::get(id);
+
+    NotifyManager::notify(NotifyTypes::RENTAL_TIME_EXPIRED,
+        info.getName());
+    if (inventory)
+    {
+        if (Item *const item = inventory->getItem(index))
+        {
+            item->increaseQuantity(-item->getQuantity());
+            inventory->removeItemAt(index);
+            ArrowsListener::distributeEvent();
+        }
+    }
+}
+
+void InventoryRecv::processPlayerStorageRemove(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageRemove")
+    // Move an item out of storage
+    const int index = msg.readInt16("index") - STORAGE_OFFSET;
+    const int amount = msg.readInt32("amount");
+    if (Ea::InventoryRecv::mStorage)
+    {
+        if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
+        {
+            item->increaseQuantity(-amount);
+            if (item->getQuantity() == 0)
+                Ea::InventoryRecv::mStorage->removeItemAt(index);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerStorageRemove")
+}
+
+void InventoryRecv::processCartInfo(Net::MessageIn &msg)
+{
+    msg.readInt16("cart items used");
+    const int size = msg.readInt16("max cart items");
+    PlayerInfo::setAttribute(Attributes::CART_TOTAL_WEIGHT,
+        msg.readInt32("cart weight"));
+    PlayerInfo::setAttribute(Attributes::CART_MAX_WEIGHT,
+        msg.readInt32("max cart weight"));
+    if (mCartItems.empty())
+        return;
+
+    Inventory *const inv = PlayerInfo::getCartInventory();
+    if (!inv)
+        return;
+
+    inv->resize(size);
+
+    FOR_EACH (Ea::InventoryItems::const_iterator, it, mCartItems)
+    {
+        inv->setItem((*it).slot,
+            (*it).id,
+            (*it).type,
+            (*it).quantity,
+            (*it).refine,
+            (*it).color,
+            (*it).identified,
+            (*it).damaged,
+            (*it).favorite,
+            (*it).equip,
+            Equipped_false);
+    }
+    mCartItems.clear();
+}
+
+void InventoryRecv::processCartRemove(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+    // +++ need close or clear cart?
+}
+
+void InventoryRecv::processPlayerCartAdd(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerCartAdd")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getCartInventory() : nullptr;
+
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    int amount = msg.readInt32("count");
+    const int itemId = msg.readInt16("item id");
+    const int itemType = msg.readUInt8("item type");
+    uint8_t identified = msg.readUInt8("identified");
+    const Damaged damaged = fromBool(msg.readUInt8("attribute"), Damaged);
+    const uint8_t refine = msg.readUInt8("refine");
+    int cards[4];
+    for (int f = 0; f < 4; f++)
+        cards[f] = msg.readInt16("card");
+
+    if (inventory)
+    {
+        const Item *const item = inventory->getItem(index);
+
+        if (item && item->getId() == itemId)
+            amount += item->getQuantity();
+
+        inventory->setItem(index,
+            itemId,
+            itemType,
+            amount,
+            refine,
+            ItemColorManager::getColorFromCards(&cards[0]),
+            fromBool(identified, Identified),
+            damaged,
+            Favorite_false,
+            Equipm_false,
+            Equipped_false);
+        inventory->setCards(index, cards, 4);
+    }
+    BLOCK_END("InventoryRecv::processPlayerCartAdd")
+}
+
+void InventoryRecv::processPlayerCartEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerCartEquip")
+    msg.readInt16("len");
+    const int number = (msg.getLength() - 4) / 31;
+    for (int loop = 0; loop < number; loop++)
+    {
+        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const int itemType = msg.readUInt8("item type");
+        const int amount = 1;
+        msg.readInt32("location");
+        msg.readInt32("wear state");
+        const uint8_t refine = msg.readUInt8("refine level");
+        int cards[4];
+        for (int f = 0; f < 4; f++)
+            cards[f] = msg.readInt16("card");
+        msg.readInt32("hire expire date");
+        msg.readInt16("bind on equip");
+        msg.readInt16("sprite");
+        ItemFlags flags;
+        flags.byte = msg.readUInt8("flags");
+
+        mCartItems.push_back(Ea::InventoryItem(index,
+            itemId,
+            itemType,
+            cards,
+            amount,
+            refine,
+            ItemColorManager::getColorFromCards(&cards[0]),
+            fromBool(flags.bits.isIdentified, Identified),
+            fromBool(flags.bits.isDamaged, Damaged),
+            fromBool(flags.bits.isFavorite, Favorite),
+            Equipm_false));
+    }
+    BLOCK_END("InventoryRecv::processPlayerCartEquip")
+}
+
+void InventoryRecv::processPlayerCartItems(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerCartItems")
+    Ea::InventoryRecv::mInventoryItems.clear();
+
+    msg.readInt16("len");
+    const int number = (msg.getLength() - 4) / 23;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const int itemType = msg.readUInt8("item type");
+        const int amount = msg.readInt16("count");
+        msg.readInt32("wear state / equip");
+        int cards[4];
+        for (int f = 0; f < 4; f++)
+            cards[f] = msg.readInt16("card");
+        msg.readInt32("hire expire date (?)");
+        ItemFlags flags;
+        flags.byte = msg.readUInt8("flags");
+
+        mCartItems.push_back(Ea::InventoryItem(index,
+            itemId,
+            itemType,
+            cards,
+            amount,
+            0,
+            ItemColorManager::getColorFromCards(&cards[0]),
+            fromBool(flags.bits.isIdentified, Identified),
+            fromBool(flags.bits.isDamaged, Damaged),
+            fromBool(flags.bits.isFavorite, Favorite),
+            Equipm_false));
+    }
+    BLOCK_END("InventoryRecv::processPlayerCartItems")
+}
+
+void InventoryRecv::processPlayerCartRemove(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerCartRemove")
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    const int amount = msg.readInt32("amount");
+
+    Inventory *const inv = PlayerInfo::getCartInventory();
+    if (!inv)
+        return;
+
+    if (Item *const item = inv->getItem(index))
+    {
+        item->increaseQuantity(-amount);
+        if (item->getQuantity() == 0)
+            inv->removeItemAt(index);
+    }
+    BLOCK_END("InventoryRecv::processPlayerCartRemove")
+}
+
+void InventoryRecv::processPlayerIdentifyList(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    menu = MenuType::Identify;
+    const int count = msg.readInt16("len") - 4;
+    for (int f = 0; f < count; f ++)
+        msg.readInt16("inv index");
+}
+
+void InventoryRecv::processPlayerIdentified(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    msg.readInt16("inv index");
+    msg.readUInt8("flag");
+}
+
+void InventoryRecv::processPlayerRefine(Net::MessageIn &msg)
+{
+    const int flag = msg.readInt16("flag");
+    const int index = msg.readInt16("inv index") - INVENTORY_OFFSET;
+    msg.readInt16("refine");
+    const Inventory *const inv = PlayerInfo::getInventory();
+    const Item *item = nullptr;
+    int notifyType;
+    std::string message;
+    std::string itemName;
+    if (inv)
+        item = inv->getItem(index);
+    if (item)
+    {
+        itemName = item->getName();
+    }
+    else
+    {
+        // TRANSLATORS: unknown item
+        itemName = _("Unknown item");
+    }
+    switch (flag)
+    {
+        case 0:
+            notifyType = NotifyTypes::REFINE_SUCCESS;
+            break;
+        case 1:
+            notifyType = NotifyTypes::REFINE_FAILURE;
+            break;
+        case 2:
+            notifyType = NotifyTypes::REFINE_DOWNGRADE;
+            break;
+        default:
+            UNIMPLIMENTEDPACKET;
+            notifyType = NotifyTypes::REFINE_UNKNOWN;
+            break;
+    }
+    NotifyManager::notify(notifyType, itemName);
+}
+
+void InventoryRecv::processPlayerRepairList(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    const int count = (msg.readInt16("len") - 4) / 13;
+    for (int f = 0; f < count; f ++)
+    {
+        msg.readInt16("index");
+        msg.readInt16("item id");
+        msg.readUInt8("refine");
+        for (int d = 0; d < 4; d ++)
+            msg.readInt16("card");
+    }
+    menu = MenuType::RepairWespon;
+}
+
+void InventoryRecv::processPlayerRepairEffect(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    msg.readInt16("item index");
+    msg.readUInt8("flag");
+}
+
+void InventoryRecv::processPlayerRefineList(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    const int count = (msg.readInt16("len") - 4) / 13;
+
+    for (int f = 0; f < count; f ++)
+    {
+        msg.readInt16("item index");
+        msg.readInt16("item id");
+        msg.readUInt8("refine");
+        for (int d = 0; d < 4; d ++)
+            msg.readInt16("card");
+    }
+    menu = MenuType::WeaponeRefine;
+}
+
+void InventoryRecv::processPlayerStoragePassword(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    msg.readInt16("info");
+}
+
+void InventoryRecv::processPlayerStoragePasswordResult(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    msg.readInt16("result");
+    msg.readInt16("error count");
+}
+
+void InventoryRecv::processPlayerCookingList(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    const int count = (msg.readInt16("len") - 6) / 2;
+    msg.readInt16("list type");
+    for (int f = 0; f < count; f ++)
+        msg.readInt16("item id");
+}
+
+void InventoryRecv::processItemDamaged(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    msg.readInt16("position");
+    msg.readBeingId("account id");
+}
+
+void InventoryRecv::processFavoriteItem(Net::MessageIn &msg)
+{
+    UNIMPLIMENTEDPACKET;
+
+    msg.readInt16("item index");
+    msg.readUInt8("favorite (0 - favorite)");
+}
+
+void InventoryRecv::processCartAddError(Net::MessageIn &msg)
+{
+    switch(msg.readUInt8("flag"))
+    {
+        case 0:
+            NotifyManager::notify(NotifyTypes::CART_ADD_WEIGHT_ERROR);
+            break;
+        case 1:
+            NotifyManager::notify(NotifyTypes::CART_ADD_COUNT_ERROR);
+            break;
+        default:
+            break;
+    }
+}
+
+void InventoryRecv::processBindItem(Net::MessageIn &msg)
+{
+    const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
+    const Inventory *const inv = PlayerInfo::getInventory();
+    if (inv)
+    {
+        std::string itemName;
+        const Item *item = inv->getItem(index);
+        if (item)
+        {
+            itemName = item->getName();
+        }
+        else
+        {
+            // TRANSLATORS: unknown item message
+            itemName = _("Unknown item");
+        }
+        NotifyManager::notify(NotifyTypes::BOUND_ITEM, itemName);
+    }
+}
+
+}  // namespace EAthena
diff --git a/src/net/eathena/inventoryrecv.h b/src/net/eathena/inventoryrecv.h
new file mode 100644
index 000000000..ea0bd71fd
--- /dev/null
+++ b/src/net/eathena/inventoryrecv.h
@@ -0,0 +1,72 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2015  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_EATHENA_INVENTORYRECV_H
+#define NET_EATHENA_INVENTORYRECV_H
+
+#include "net/ea/inventoryhandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+    namespace InventoryRecv
+    {
+        extern Ea::InventoryItems mCartItems;
+
+        void processPlayerEquipment(Net::MessageIn &msg);
+        void processPlayerInventoryAdd(Net::MessageIn &msg);
+        void processPlayerInventory(Net::MessageIn &msg);
+        void processPlayerStorage(Net::MessageIn &msg);
+        void processPlayerEquip(Net::MessageIn &msg);
+        void processPlayerUnEquip(Net::MessageIn &msg);
+        void processPlayerInventoryRemove2(Net::MessageIn &msg);
+        void processPlayerStorageEquip(Net::MessageIn &msg);
+        void processPlayerStorageAdd(Net::MessageIn &msg);
+        void processPlayerUseCard(Net::MessageIn &msg);
+        void processPlayerInsertCard(Net::MessageIn &msg);
+        void processPlayerItemRentalTime(Net::MessageIn &msg);
+        void processPlayerItemRentalExpired(Net::MessageIn &msg);
+        void processPlayerStorageRemove(Net::MessageIn &msg);
+        void processCartInfo(Net::MessageIn &msg);
+        void processCartRemove(Net::MessageIn &msg);
+        void processPlayerCartAdd(Net::MessageIn &msg);
+        void processPlayerCartEquip(Net::MessageIn &msg);
+        void processPlayerCartItems(Net::MessageIn &msg);
+        void processPlayerCartRemove(Net::MessageIn &msg);
+        void processPlayerIdentifyList(Net::MessageIn &msg);
+        void processPlayerIdentified(Net::MessageIn &msg);
+        void processPlayerRefine(Net::MessageIn &msg);
+        void processPlayerRepairList(Net::MessageIn &msg);
+        void processPlayerRepairEffect(Net::MessageIn &msg);
+        void processPlayerRefineList(Net::MessageIn &msg);
+        void processPlayerStoragePassword(Net::MessageIn &msg);
+        void processPlayerStoragePasswordResult(Net::MessageIn &msg);
+        void processPlayerCookingList(Net::MessageIn &msg);
+        void processItemDamaged(Net::MessageIn &msg);
+        void processFavoriteItem(Net::MessageIn &msg);
+        void processCartAddError(Net::MessageIn &msg);
+        void processBindItem(Net::MessageIn &msg);
+    }  // namespace InventoryRecv
+}  // namespace EAthena
+
+#endif  // NET_EATHENA_INVENTORYRECV_H
diff --git a/src/net/tmwa/inventoryhandler.cpp b/src/net/tmwa/inventoryhandler.cpp
index 98c0a46b6..99fda9a23 100644
--- a/src/net/tmwa/inventoryhandler.cpp
+++ b/src/net/tmwa/inventoryhandler.cpp
@@ -32,6 +32,9 @@
 
 #include "listeners/arrowslistener.h"
 
+#include "net/ea/inventoryrecv.h"
+
+#include "net/tmwa/inventoryrecv.h"
 #include "net/tmwa/messageout.h"
 #include "net/tmwa/protocol.h"
 
@@ -102,67 +105,67 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
     switch (msg.getId())
     {
         case SMSG_PLAYER_INVENTORY:
-            processPlayerInventory(msg);
+            InventoryRecv::processPlayerInventory(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_ITEMS:
-            processPlayerStorage(msg);
+            InventoryRecv::processPlayerStorage(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_EQUIP:
-            processPlayerStorageEquip(msg);
+            InventoryRecv::processPlayerStorageEquip(msg);
             break;
 
         case SMSG_PLAYER_INVENTORY_ADD:
-            processPlayerInventoryAdd(msg);
+            InventoryRecv::processPlayerInventoryAdd(msg);
             break;
 
         case SMSG_PLAYER_INVENTORY_REMOVE:
-            processPlayerInventoryRemove(msg);
+            Ea::InventoryRecv::processPlayerInventoryRemove(msg);
             break;
 
         case SMSG_PLAYER_INVENTORY_USE:
-            processPlayerInventoryUse(msg);
+            Ea::InventoryRecv::processPlayerInventoryUse(msg);
             break;
 
         case SMSG_ITEM_USE_RESPONSE:
-            processItemUseResponse(msg);
+            Ea::InventoryRecv::processItemUseResponse(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_STATUS:
-            processPlayerStorageStatus(msg);
+            Ea::InventoryRecv::processPlayerStorageStatus(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_ADD:
-            processPlayerStorageAdd(msg);
+            InventoryRecv::processPlayerStorageAdd(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_REMOVE:
-            processPlayerStorageRemove(msg);
+            InventoryRecv::processPlayerStorageRemove(msg);
             break;
 
         case SMSG_PLAYER_STORAGE_CLOSE:
-            processPlayerStorageClose(msg);
+            Ea::InventoryRecv::processPlayerStorageClose(msg);
             break;
 
         case SMSG_PLAYER_EQUIPMENT:
-            processPlayerEquipment(msg);
+            InventoryRecv::processPlayerEquipment(msg);
             break;
 
         case SMSG_PLAYER_EQUIP:
-            processPlayerEquip(msg);
+            InventoryRecv::processPlayerEquip(msg);
             break;
 
         case SMSG_PLAYER_UNEQUIP:
-            processPlayerUnEquip(msg);
+            InventoryRecv::processPlayerUnEquip(msg);
             break;
 
         case SMSG_PLAYER_ATTACK_RANGE:
-            processPlayerAttackRange(msg);
+            Ea::InventoryRecv::processPlayerAttackRange(msg);
             break;
 
         case SMSG_PLAYER_ARROW_EQUIP:
-            processPlayerArrowEquip(msg);
+            Ea::InventoryRecv::processPlayerArrowEquip(msg);
             break;
 
         default:
@@ -254,392 +257,6 @@ void InventoryHandler::favoriteItem(const Item *const item A_UNUSED,
 {
 }
 
-void InventoryHandler::processPlayerEquipment(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerEquipment")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    msg.readInt16("len");
-    Equipment *const equipment = PlayerInfo::getEquipment();
-    if (equipment && !equipment->getBackend())
-    {   // look like SMSG_PLAYER_INVENTORY was not received
-        mEquips.clear();
-        equipment->setBackend(&mEquips);
-    }
-    const int number = (msg.getLength() - 4) / 20;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const uint8_t itemType = msg.readUInt8("item type");
-        const uint8_t identified = msg.readUInt8("identify");
-
-        msg.readInt16("equip type?");
-        const int equipType = msg.readInt16("equip type");
-        msg.readUInt8("attribute");
-        const uint8_t refine = msg.readUInt8("refine");
-        int cards[4];
-        for (int f = 0; f < 4; f++)
-            cards[f] = msg.readInt16("card");
-
-        if (mDebugInventory)
-        {
-            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d",
-                        index, itemId, itemType, identified);
-        }
-
-        if (inventory)
-        {
-            inventory->setItem(index,
-                itemId,
-                itemType,
-                1,
-                refine,
-                ItemColor_one,
-                fromBool(identified, Identified),
-                Damaged_false,
-                Favorite_false,
-                Equipm_true,
-                Equipped_false);
-            inventory->setCards(index, cards, 4);
-        }
-
-        if (equipType)
-            mEquips.setEquipment(getSlot(equipType), index);
-    }
-    BLOCK_END("InventoryHandler::processPlayerEquipment")
-}
-
-void InventoryHandler::processPlayerInventoryAdd(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventoryAdd")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    if (PlayerInfo::getEquipment()
-        && !PlayerInfo::getEquipment()->getBackend())
-    {   // look like SMSG_PLAYER_INVENTORY was not received
-        mEquips.clear();
-        PlayerInfo::getEquipment()->setBackend(&mEquips);
-    }
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    int amount = msg.readInt16("amount");
-    const int itemId = msg.readInt16("item id");
-    const uint8_t identified = msg.readUInt8("identified");
-    msg.readUInt8("attribute");
-    const uint8_t refine = msg.readUInt8("refine");
-    int cards[4];
-    for (int f = 0; f < 4; f++)
-        cards[f] = msg.readInt16("card");
-    const int equipType = msg.readInt16("equip type");
-    const int type = msg.readUInt8("item type");
-
-    const ItemInfo &itemInfo = ItemDB::get(itemId);
-    const unsigned char err = msg.readUInt8("status");
-    BeingId floorId;
-    if (mSentPickups.empty())
-    {
-        floorId = BeingId_zero;
-    }
-    else
-    {
-        floorId = mSentPickups.front();
-        mSentPickups.pop();
-    }
-
-    if (err)
-    {
-        PickupT pickup;
-        switch (err)
-        {
-            case 1:
-                pickup = Pickup::BAD_ITEM;
-                break;
-            case 2:
-                pickup = Pickup::TOO_HEAVY;
-                break;
-            case 3:
-                pickup = Pickup::TOO_FAR;
-                break;
-            case 4:
-                pickup = Pickup::INV_FULL;
-                break;
-            case 5:
-                pickup = Pickup::STACK_FULL;
-                break;
-            case 6:
-                pickup = Pickup::DROP_STEAL;
-                break;
-            default:
-                pickup = Pickup::UNKNOWN;
-                UNIMPLIMENTEDPACKET;
-                break;
-        }
-        if (localPlayer)
-        {
-            localPlayer->pickedUp(itemInfo,
-                0,
-                ItemColor_one,
-                floorId,
-                pickup);
-        }
-    }
-    else
-    {
-        if (localPlayer)
-        {
-            localPlayer->pickedUp(itemInfo,
-                amount,
-                ItemColor_one,
-                floorId,
-                Pickup::OKAY);
-        }
-
-        if (inventory)
-        {
-            const Item *const item = inventory->getItem(index);
-
-            if (item && item->getId() == itemId)
-                amount += item->getQuantity();
-
-            inventory->setItem(index,
-                itemId,
-                type,
-                amount,
-                refine,
-                ItemColor_one,
-                fromBool(identified, Identified),
-                Damaged_false,
-                Favorite_false,
-                fromBool(equipType, Equipm),
-                Equipped_false);
-            inventory->setCards(index, cards, 4);
-        }
-        ArrowsListener::distributeEvent();
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventoryAdd")
-}
-
-void InventoryHandler::processPlayerInventory(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventory")
-    Inventory *const inventory = localPlayer
-        ? PlayerInfo::getInventory() : nullptr;
-
-    if (PlayerInfo::getEquipment())
-    {
-        // Clear inventory - this will be a complete refresh
-        mEquips.clear();
-        PlayerInfo::getEquipment()->setBackend(&mEquips);
-    }
-
-    if (inventory)
-        inventory->clear();
-
-    msg.readInt16("len");
-    const int number = (msg.getLength() - 4) / 18;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        int cards[4];
-        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const uint8_t itemType = msg.readUInt8("item type");
-        const uint8_t identified = msg.readUInt8("identified");
-        const int amount = msg.readInt16("amount");
-        const int arrow = msg.readInt16("arrow");
-        for (int i = 0; i < 4; i++)
-            cards[i] = msg.readInt16("card");
-
-        if (mDebugInventory)
-        {
-            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
-                        "Qty: %d, Cards: %d, %d, %d, %d",
-                        index, itemId, itemType, identified, amount,
-                        cards[0], cards[1], cards[2], cards[3]);
-        }
-
-        // Trick because arrows are not considered equipment
-        const bool isEquipment = arrow & 0x8000;
-
-        if (inventory)
-        {
-            inventory->setItem(index,
-                itemId,
-                itemType,
-                amount,
-                0,
-                ItemColor_one,
-                fromBool(identified, Identified),
-                Damaged_false,
-                Favorite_false,
-                fromBool(isEquipment, Equipm),
-                Equipped_false);
-            inventory->setCards(index, cards, 4);
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventory")
-}
-
-void InventoryHandler::processPlayerStorage(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerInventory")
-    mInventoryItems.clear();
-
-    msg.readInt16("len");
-    const int number = (msg.getLength() - 4) / 18;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        int cards[4];
-        const int index = msg.readInt16("index") - STORAGE_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const uint8_t itemType = msg.readUInt8("item type");
-        const uint8_t identified = msg.readUInt8("identified");
-        const int amount = msg.readInt16("amount");
-        msg.readInt16("arrow");
-        for (int i = 0; i < 4; i++)
-            cards[i] = msg.readInt16("card");
-
-        if (mDebugInventory)
-        {
-            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
-                        "Qty: %d, Cards: %d, %d, %d, %d",
-                        index, itemId, itemType, identified, amount,
-                        cards[0], cards[1], cards[2], cards[3]);
-        }
-
-        mInventoryItems.push_back(Ea::InventoryItem(index,
-            itemId,
-            itemType,
-            cards,
-            amount,
-            0,
-            ItemColor_one,
-            fromBool(identified, Identified),
-            Damaged_false,
-            Favorite_false,
-            Equipm_false));
-    }
-    BLOCK_END("InventoryHandler::processPlayerInventory")
-}
-
-void InventoryHandler::processPlayerEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerEquip")
-    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
-    const int equipType = msg.readInt16("equip type");
-    const uint8_t flag = msg.readUInt8("flag");
-
-    if (!flag)
-        NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
-    else
-        mEquips.setEquipment(getSlot(equipType), index);
-    BLOCK_END("InventoryHandler::processPlayerEquip")
-}
-
-void InventoryHandler::processPlayerUnEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerUnEquip")
-    msg.readInt16("index");
-    const int equipType = msg.readInt16("equip type");
-    const uint8_t flag = msg.readUInt8("flag");
-
-    if (flag)
-        mEquips.setEquipment(getSlot(equipType), -1);
-    if (equipType & 0x8000)
-        ArrowsListener::distributeEvent();
-    BLOCK_END("InventoryHandler::processPlayerUnEquip")
-}
-
-void InventoryHandler::processPlayerStorageEquip(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageEquip")
-    msg.readInt16("len");
-    const int number = (msg.getLength() - 4) / 20;
-
-    for (int loop = 0; loop < number; loop++)
-    {
-        int cards[4];
-        const int index = msg.readInt16("index") - STORAGE_OFFSET;
-        const int itemId = msg.readInt16("item id");
-        const uint8_t itemType = msg.readUInt8("item type");
-        const uint8_t identified = msg.readUInt8("identified");
-        const int amount = 1;
-        msg.readInt16("equip point?");
-        msg.readInt16("another equip point?");
-        msg.readUInt8("attribute (broken)");
-        const uint8_t refine = msg.readUInt8("refine");
-        for (int i = 0; i < 4; i++)
-            cards[i] = msg.readInt16("card");
-
-        if (mDebugInventory)
-        {
-            logger->log("Index: %d, ID: %d, Type: %d, Identified: %u, "
-                "Qty: %d, Cards: %d, %d, %d, %d, Refine: %u",
-                index, itemId, itemType,
-                static_cast<unsigned int>(identified), amount,
-                cards[0], cards[1], cards[2], cards[3],
-                static_cast<unsigned int>(refine));
-        }
-
-        mInventoryItems.push_back(Ea::InventoryItem(index,
-            itemId,
-            itemType,
-            cards,
-            amount,
-            refine,
-            ItemColor_one,
-            fromBool(identified, Identified),
-            Damaged_false,
-            Favorite_false,
-            Equipm_false));
-    }
-    BLOCK_END("InventoryHandler::processPlayerStorageEquip")
-}
-
-void InventoryHandler::processPlayerStorageAdd(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageAdd")
-    // Move an item into storage
-    const int index = msg.readInt16("index") - STORAGE_OFFSET;
-    const int amount = msg.readInt32("amount");
-    const int itemId = msg.readInt16("item id");
-    const unsigned char identified = msg.readUInt8("identified");
-    msg.readUInt8("attribute");
-    const uint8_t refine = msg.readUInt8("refine");
-    int cards[4];
-    for (int f = 0; f < 4; f++)
-        cards[f] = msg.readInt16("card");
-
-    if (Item *const item = mStorage->getItem(index))
-    {
-        item->setId(itemId, ItemColor_one);
-        item->increaseQuantity(amount);
-    }
-    else
-    {
-        if (mStorage)
-        {
-            mStorage->setItem(index,
-                itemId,
-                0,
-                amount,
-                refine,
-                ItemColor_one,
-                fromBool(identified, Identified),
-                Damaged_false,
-                Favorite_false,
-                Equipm_false,
-                Equipped_false);
-            mStorage->setCards(index, cards, 4);
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerStorageAdd")
-}
-
 void InventoryHandler::selectEgg(const Item *const item A_UNUSED) const
 {
 }
@@ -652,22 +269,4 @@ int InventoryHandler::convertFromServerSlot(const int serverSlot) const
     return static_cast<int>(EQUIP_CONVERT[serverSlot]);
 }
 
-void InventoryHandler::processPlayerStorageRemove(Net::MessageIn &msg)
-{
-    BLOCK_START("InventoryHandler::processPlayerStorageRemove")
-    // Move an item out of storage
-    const int index = msg.readInt16("index") - STORAGE_OFFSET;
-    const int amount = msg.readInt16("amount");
-    if (mStorage)
-    {
-        if (Item *const item = mStorage->getItem(index))
-        {
-            item->increaseQuantity(-amount);
-            if (item->getQuantity() == 0)
-                mStorage->removeItemAt(index);
-        }
-    }
-    BLOCK_END("InventoryHandler::processPlayerStorageRemove")
-}
-
 }  // namespace TmwAthena
diff --git a/src/net/tmwa/inventoryhandler.h b/src/net/tmwa/inventoryhandler.h
index bf6badc48..8bbd17a56 100644
--- a/src/net/tmwa/inventoryhandler.h
+++ b/src/net/tmwa/inventoryhandler.h
@@ -76,25 +76,6 @@ class InventoryHandler final : public MessageHandler,
 
         int getItemIndex() const override final A_WARN_UNUSED
         { return 0; }
-
-    protected:
-        static void processPlayerEquipment(Net::MessageIn &msg);
-
-        static void processPlayerInventoryAdd(Net::MessageIn &msg);
-
-        static void processPlayerInventory(Net::MessageIn &msg);
-
-        static void processPlayerStorage(Net::MessageIn &msg);
-
-        static void processPlayerEquip(Net::MessageIn &msg);
-
-        static void processPlayerUnEquip(Net::MessageIn &msg);
-
-        static void processPlayerStorageEquip(Net::MessageIn &msg);
-
-        static void processPlayerStorageAdd(Net::MessageIn &msg);
-
-        static void processPlayerStorageRemove(Net::MessageIn &msg);
 };
 
 }  // namespace TmwAthena
diff --git a/src/net/tmwa/inventoryrecv.cpp b/src/net/tmwa/inventoryrecv.cpp
new file mode 100644
index 000000000..da3eb3f91
--- /dev/null
+++ b/src/net/tmwa/inventoryrecv.cpp
@@ -0,0 +1,464 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2015  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/tmwa/inventoryrecv.h"
+
+#include "notifymanager.h"
+
+#include "enums/equipslot.h"
+
+#include "being/localplayer.h"
+
+#include "enums/resources/notifytypes.h"
+
+#include "listeners/arrowslistener.h"
+
+#include "net/tmwa/messageout.h"
+#include "net/tmwa/protocol.h"
+
+#include "net/ea/eaprotocol.h"
+#include "net/ea/equipbackend.h"
+#include "net/ea/inventoryrecv.h"
+
+#include "debug.h"
+
+namespace TmwAthena
+{
+
+void InventoryRecv::processPlayerEquipment(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerEquipment")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    msg.readInt16("len");
+    Equipment *const equipment = PlayerInfo::getEquipment();
+    if (equipment && !equipment->getBackend())
+    {   // look like SMSG_PLAYER_INVENTORY was not received
+        Ea::InventoryRecv::mEquips.clear();
+        equipment->setBackend(&Ea::InventoryRecv::mEquips);
+    }
+    const int number = (msg.getLength() - 4) / 20;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const uint8_t itemType = msg.readUInt8("item type");
+        const uint8_t identified = msg.readUInt8("identify");
+
+        msg.readInt16("equip type?");
+        const int equipType = msg.readInt16("equip type");
+        msg.readUInt8("attribute");
+        const uint8_t refine = msg.readUInt8("refine");
+        int cards[4];
+        for (int f = 0; f < 4; f++)
+            cards[f] = msg.readInt16("card");
+
+        if (Ea::InventoryRecv::mDebugInventory)
+        {
+            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d",
+                        index, itemId, itemType, identified);
+        }
+
+        if (inventory)
+        {
+            inventory->setItem(index,
+                itemId,
+                itemType,
+                1,
+                refine,
+                ItemColor_one,
+                fromBool(identified, Identified),
+                Damaged_false,
+                Favorite_false,
+                Equipm_true,
+                Equipped_false);
+            inventory->setCards(index, cards, 4);
+        }
+
+        if (equipType)
+        {
+            Ea::InventoryRecv::mEquips.setEquipment(
+                Ea::InventoryRecv::getSlot(equipType), index);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerEquipment")
+}
+
+void InventoryRecv::processPlayerInventoryAdd(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventoryAdd")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    if (PlayerInfo::getEquipment()
+        && !PlayerInfo::getEquipment()->getBackend())
+    {   // look like SMSG_PLAYER_INVENTORY was not received
+        Ea::InventoryRecv::mEquips.clear();
+        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
+    }
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    int amount = msg.readInt16("amount");
+    const int itemId = msg.readInt16("item id");
+    const uint8_t identified = msg.readUInt8("identified");
+    msg.readUInt8("attribute");
+    const uint8_t refine = msg.readUInt8("refine");
+    int cards[4];
+    for (int f = 0; f < 4; f++)
+        cards[f] = msg.readInt16("card");
+    const int equipType = msg.readInt16("equip type");
+    const int type = msg.readUInt8("item type");
+
+    const ItemInfo &itemInfo = ItemDB::get(itemId);
+    const unsigned char err = msg.readUInt8("status");
+    BeingId floorId;
+    if (Ea::InventoryRecv::mSentPickups.empty())
+    {
+        floorId = BeingId_zero;
+    }
+    else
+    {
+        floorId = Ea::InventoryRecv::mSentPickups.front();
+        Ea::InventoryRecv::mSentPickups.pop();
+    }
+
+    if (err)
+    {
+        PickupT pickup;
+        switch (err)
+        {
+            case 1:
+                pickup = Pickup::BAD_ITEM;
+                break;
+            case 2:
+                pickup = Pickup::TOO_HEAVY;
+                break;
+            case 3:
+                pickup = Pickup::TOO_FAR;
+                break;
+            case 4:
+                pickup = Pickup::INV_FULL;
+                break;
+            case 5:
+                pickup = Pickup::STACK_FULL;
+                break;
+            case 6:
+                pickup = Pickup::DROP_STEAL;
+                break;
+            default:
+                pickup = Pickup::UNKNOWN;
+                UNIMPLIMENTEDPACKET;
+                break;
+        }
+        if (localPlayer)
+        {
+            localPlayer->pickedUp(itemInfo,
+                0,
+                ItemColor_one,
+                floorId,
+                pickup);
+        }
+    }
+    else
+    {
+        if (localPlayer)
+        {
+            localPlayer->pickedUp(itemInfo,
+                amount,
+                ItemColor_one,
+                floorId,
+                Pickup::OKAY);
+        }
+
+        if (inventory)
+        {
+            const Item *const item = inventory->getItem(index);
+
+            if (item && item->getId() == itemId)
+                amount += item->getQuantity();
+
+            inventory->setItem(index,
+                itemId,
+                type,
+                amount,
+                refine,
+                ItemColor_one,
+                fromBool(identified, Identified),
+                Damaged_false,
+                Favorite_false,
+                fromBool(equipType, Equipm),
+                Equipped_false);
+            inventory->setCards(index, cards, 4);
+        }
+        ArrowsListener::distributeEvent();
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventoryAdd")
+}
+
+void InventoryRecv::processPlayerInventory(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventory")
+    Inventory *const inventory = localPlayer
+        ? PlayerInfo::getInventory() : nullptr;
+
+    if (PlayerInfo::getEquipment())
+    {
+        // Clear inventory - this will be a complete refresh
+        Ea::InventoryRecv::mEquips.clear();
+        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
+    }
+
+    if (inventory)
+        inventory->clear();
+
+    msg.readInt16("len");
+    const int number = (msg.getLength() - 4) / 18;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        int cards[4];
+        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const uint8_t itemType = msg.readUInt8("item type");
+        const uint8_t identified = msg.readUInt8("identified");
+        const int amount = msg.readInt16("amount");
+        const int arrow = msg.readInt16("arrow");
+        for (int i = 0; i < 4; i++)
+            cards[i] = msg.readInt16("card");
+
+        if (Ea::InventoryRecv::mDebugInventory)
+        {
+            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
+                        "Qty: %d, Cards: %d, %d, %d, %d",
+                        index, itemId, itemType, identified, amount,
+                        cards[0], cards[1], cards[2], cards[3]);
+        }
+
+        // Trick because arrows are not considered equipment
+        const bool isEquipment = arrow & 0x8000;
+
+        if (inventory)
+        {
+            inventory->setItem(index,
+                itemId,
+                itemType,
+                amount,
+                0,
+                ItemColor_one,
+                fromBool(identified, Identified),
+                Damaged_false,
+                Favorite_false,
+                fromBool(isEquipment, Equipm),
+                Equipped_false);
+            inventory->setCards(index, cards, 4);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventory")
+}
+
+void InventoryRecv::processPlayerStorage(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerInventory")
+    Ea::InventoryRecv::mInventoryItems.clear();
+
+    msg.readInt16("len");
+    const int number = (msg.getLength() - 4) / 18;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        int cards[4];
+        const int index = msg.readInt16("index") - STORAGE_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const uint8_t itemType = msg.readUInt8("item type");
+        const uint8_t identified = msg.readUInt8("identified");
+        const int amount = msg.readInt16("amount");
+        msg.readInt16("arrow");
+        for (int i = 0; i < 4; i++)
+            cards[i] = msg.readInt16("card");
+
+        if (Ea::InventoryRecv::mDebugInventory)
+        {
+            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
+                        "Qty: %d, Cards: %d, %d, %d, %d",
+                        index, itemId, itemType, identified, amount,
+                        cards[0], cards[1], cards[2], cards[3]);
+        }
+
+        Ea::InventoryRecv::mInventoryItems.push_back(Ea::InventoryItem(
+            index,
+            itemId,
+            itemType,
+            cards,
+            amount,
+            0,
+            ItemColor_one,
+            fromBool(identified, Identified),
+            Damaged_false,
+            Favorite_false,
+            Equipm_false));
+    }
+    BLOCK_END("InventoryRecv::processPlayerInventory")
+}
+
+void InventoryRecv::processPlayerEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerEquip")
+    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
+    const int equipType = msg.readInt16("equip type");
+    const uint8_t flag = msg.readUInt8("flag");
+
+    if (!flag)
+    {
+        NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
+    }
+    else
+    {
+        Ea::InventoryRecv::mEquips.setEquipment(
+            Ea::InventoryRecv::getSlot(equipType), index);
+    }
+    BLOCK_END("InventoryRecv::processPlayerEquip")
+}
+
+void InventoryRecv::processPlayerUnEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerUnEquip")
+    msg.readInt16("index");
+    const int equipType = msg.readInt16("equip type");
+    const uint8_t flag = msg.readUInt8("flag");
+
+    if (flag)
+    {
+        Ea::InventoryRecv::mEquips.setEquipment(
+            Ea::InventoryRecv::getSlot(equipType), -1);
+    }
+    if (equipType & 0x8000)
+        ArrowsListener::distributeEvent();
+    BLOCK_END("InventoryRecv::processPlayerUnEquip")
+}
+
+void InventoryRecv::processPlayerStorageEquip(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageEquip")
+    msg.readInt16("len");
+    const int number = (msg.getLength() - 4) / 20;
+
+    for (int loop = 0; loop < number; loop++)
+    {
+        int cards[4];
+        const int index = msg.readInt16("index") - STORAGE_OFFSET;
+        const int itemId = msg.readInt16("item id");
+        const uint8_t itemType = msg.readUInt8("item type");
+        const uint8_t identified = msg.readUInt8("identified");
+        const int amount = 1;
+        msg.readInt16("equip point?");
+        msg.readInt16("another equip point?");
+        msg.readUInt8("attribute (broken)");
+        const uint8_t refine = msg.readUInt8("refine");
+        for (int i = 0; i < 4; i++)
+            cards[i] = msg.readInt16("card");
+
+        if (Ea::InventoryRecv::mDebugInventory)
+        {
+            logger->log("Index: %d, ID: %d, Type: %d, Identified: %u, "
+                "Qty: %d, Cards: %d, %d, %d, %d, Refine: %u",
+                index, itemId, itemType,
+                static_cast<unsigned int>(identified), amount,
+                cards[0], cards[1], cards[2], cards[3],
+                static_cast<unsigned int>(refine));
+        }
+
+        Ea::InventoryRecv::mInventoryItems.push_back(Ea::InventoryItem(
+            index,
+            itemId,
+            itemType,
+            cards,
+            amount,
+            refine,
+            ItemColor_one,
+            fromBool(identified, Identified),
+            Damaged_false,
+            Favorite_false,
+            Equipm_false));
+    }
+    BLOCK_END("InventoryRecv::processPlayerStorageEquip")
+}
+
+void InventoryRecv::processPlayerStorageAdd(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageAdd")
+    // Move an item into storage
+    const int index = msg.readInt16("index") - STORAGE_OFFSET;
+    const int amount = msg.readInt32("amount");
+    const int itemId = msg.readInt16("item id");
+    const unsigned char identified = msg.readUInt8("identified");
+    msg.readUInt8("attribute");
+    const uint8_t refine = msg.readUInt8("refine");
+    int cards[4];
+    for (int f = 0; f < 4; f++)
+        cards[f] = msg.readInt16("card");
+
+    if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
+    {
+        item->setId(itemId, ItemColor_one);
+        item->increaseQuantity(amount);
+    }
+    else
+    {
+        if (Ea::InventoryRecv::mStorage)
+        {
+            Ea::InventoryRecv::mStorage->setItem(index,
+                itemId,
+                0,
+                amount,
+                refine,
+                ItemColor_one,
+                fromBool(identified, Identified),
+                Damaged_false,
+                Favorite_false,
+                Equipm_false,
+                Equipped_false);
+            Ea::InventoryRecv::mStorage->setCards(index, cards, 4);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerStorageAdd")
+}
+
+void InventoryRecv::processPlayerStorageRemove(Net::MessageIn &msg)
+{
+    BLOCK_START("InventoryRecv::processPlayerStorageRemove")
+    // Move an item out of storage
+    const int index = msg.readInt16("index") - STORAGE_OFFSET;
+    const int amount = msg.readInt16("amount");
+    if (Ea::InventoryRecv::mStorage)
+    {
+        if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
+        {
+            item->increaseQuantity(-amount);
+            if (item->getQuantity() == 0)
+                Ea::InventoryRecv::mStorage->removeItemAt(index);
+        }
+    }
+    BLOCK_END("InventoryRecv::processPlayerStorageRemove")
+}
+
+}  // namespace TmwAthena
diff --git a/src/net/tmwa/inventoryrecv.h b/src/net/tmwa/inventoryrecv.h
new file mode 100644
index 000000000..3fe506d3a
--- /dev/null
+++ b/src/net/tmwa/inventoryrecv.h
@@ -0,0 +1,46 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2015  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_TMWA_INVENTORYRECV_H
+#define NET_TMWA_INVENTORYRECV_H
+
+#include "net/ea/inventoryhandler.h"
+
+#include "net/tmwa/messagehandler.h"
+
+namespace TmwAthena
+{
+    namespace InventoryRecv
+    {
+        void processPlayerEquipment(Net::MessageIn &msg);
+        void processPlayerInventoryAdd(Net::MessageIn &msg);
+        void processPlayerInventory(Net::MessageIn &msg);
+        void processPlayerStorage(Net::MessageIn &msg);
+        void processPlayerEquip(Net::MessageIn &msg);
+        void processPlayerUnEquip(Net::MessageIn &msg);
+        void processPlayerStorageEquip(Net::MessageIn &msg);
+        void processPlayerStorageAdd(Net::MessageIn &msg);
+        void processPlayerStorageRemove(Net::MessageIn &msg);
+    }  // namespace InventoryRecv
+}  // namespace TmwAthena
+
+#endif  // NET_TMWA_INVENTORYRECV_H
-- 
cgit v1.2.3-70-g09d2