From 544da439e81ff78ec102b754e16b6cc0a28a6d0a Mon Sep 17 00:00:00 2001
From: KirieZ <guilherme.menaldo@outlook.com>
Date: Sun, 30 Jul 2017 13:45:41 -0300
Subject: Implementation of RoDEX

---
 conf/map/battle/feature.conf             |  10 +
 doc/sample/npc_rodex.txt                 |  39 ++
 doc/script_commands.txt                  |  23 ++
 sql-files/main.sql                       |  55 ++-
 sql-files/upgrades/2017-03-15--14-29.sql |  74 ++++
 sql-files/upgrades/index.txt             |   1 +
 src/char/HPMchar.c                       |   1 +
 src/char/Makefile.in                     |   4 +-
 src/char/char.c                          |   4 +
 src/char/char.h                          |   2 +
 src/char/int_rodex.c                     | 536 ++++++++++++++++++++++++++
 src/char/int_rodex.h                     |  47 +++
 src/char/inter.c                         |   6 +-
 src/char/mapif.c                         |  21 ++
 src/char/mapif.h                         |   9 +
 src/common/mmo.h                         |  47 +++
 src/map/HPMmap.c                         |   1 +
 src/map/Makefile.in                      |   4 +-
 src/map/battle.c                         |   2 +
 src/map/battle.h                         |   3 +
 src/map/clif.c                           | 516 +++++++++++++++++++++++++
 src/map/clif.h                           |  28 ++
 src/map/date.c                           |  14 +
 src/map/date.h                           |   1 +
 src/map/intif.c                          | 232 +++++++++++-
 src/map/intif.h                          |  12 +
 src/map/map.c                            |   5 +
 src/map/packets.h                        |  34 +-
 src/map/packets_struct.h                 | 212 +++++++++++
 src/map/pc.c                             |   3 +
 src/map/pc.h                             |   9 +
 src/map/rodex.c                          | 621 +++++++++++++++++++++++++++++++
 src/map/rodex.h                          |  85 +++++
 src/map/script.c                         | 181 +++++++++
 src/map/status.c                         |   7 +
 src/map/status.h                         |   5 +-
 vcproj-11/char-server.vcxproj            |   2 +
 vcproj-11/char-server.vcxproj.filters    |   6 +
 vcproj-11/map-server.vcxproj             |   2 +
 vcproj-11/map-server.vcxproj.filters     |   6 +
 vcproj-12/char-server.vcxproj            |   2 +
 vcproj-12/char-server.vcxproj.filters    |   6 +
 vcproj-12/map-server.vcxproj             |   2 +
 vcproj-12/map-server.vcxproj.filters     |   6 +
 vcproj-14/char-server.vcxproj            |   2 +
 vcproj-14/char-server.vcxproj.filters    |   6 +
 vcproj-14/map-server.vcxproj             |   2 +
 vcproj-14/map-server.vcxproj.filters     |   6 +
 48 files changed, 2877 insertions(+), 25 deletions(-)
 create mode 100644 doc/sample/npc_rodex.txt
 create mode 100644 sql-files/upgrades/2017-03-15--14-29.sql
 create mode 100644 src/char/int_rodex.c
 create mode 100644 src/char/int_rodex.h
 create mode 100644 src/map/rodex.c
 create mode 100644 src/map/rodex.h

diff --git a/conf/map/battle/feature.conf b/conf/map/battle/feature.conf
index 003e4c75a..cdb9913ea 100644
--- a/conf/map/battle/feature.conf
+++ b/conf/map/battle/feature.conf
@@ -55,4 +55,14 @@ features: {
 	// Requires: 2014-10-22bRagexe or later
 	// Disabled by default while test version is out; enable at your own risk -- the mean dev.
 	roulette: false
+
+	// Enabled RoDEX (Note 1)
+	// Requires: 2015-05-13aRagexe or later
+	rodex: true
+
+	// Allow usage of "Account Mail" box in RoDEX?
+	// Requires: 2016-03-16aRagexeRE or later
+	// This is disabled in client-side in some client versions
+	// Disabled by default
+	rodex_use_accountmail: false
 }
diff --git a/doc/sample/npc_rodex.txt b/doc/sample/npc_rodex.txt
new file mode 100644
index 000000000..a808a1aa9
--- /dev/null
+++ b/doc/sample/npc_rodex.txt
@@ -0,0 +1,39 @@
+//===== Hercules Script =======================================
+//= Sample: RoDEX Send Mail
+//===== By: ==================================================
+//= KirieZ
+//===== Current Version: =====================================
+//= 20170322
+//===== Description: =========================================
+//= Demonstrates sending mails through RoDEX.
+//============================================================
+
+prontera,150,150,4	script	Rodex Mail	1_M_01,{
+	// Sends a messsage to attached player from "Rodex Test", with title "Rodex Test1",
+	// with message "Hello World, How are You?", 1000 Zenies and 5 Red Potions attached
+	rodex_sendmail(
+		getcharid(CHAR_ID_CHAR), "Rodex Test", "Rodex Test1", "Hello World, How are You?",
+		1000,
+		Red_Potion, 5
+	);
+
+	// Sends a messsage to attached player "Account Box" from "Rodex Test", with title "Rodex Test3",
+	// with message "Hello World, How are You?", 1000 Zenies and 5 Red Potions attached
+	rodex_sendmail_acc(
+		getcharid(CHAR_ID_CHAR), "Rodex Test", "Rodex Test3", "Hello World, How are You?",
+		1000,
+		Red_Potion, 5
+	);
+
+	// Sends a messsage to attached player "Account Box" from "Rodex Test", with title "Rodex Test6",
+	// with message "Hello World, How are You?", 1000 Zenies, 5 Red Potions and
+	// 1 +10 Knife[3] with 3 Poring Cards (Broken)
+	rodex_sendmail2(
+		getcharid(CHAR_ID_CHAR), "Rodex Test", "Rodex Test6", "Hello World, How are You?",
+		1000,
+		Red_Potion, 5, 0, 0, 0, 0, 0, 0,
+		Knife, 1, 10, 1, Poring_Card, Poring_Card, Poring_Card, 0
+	);
+
+	end;
+}
diff --git a/doc/script_commands.txt b/doc/script_commands.txt
index 9148e023c..779cd271c 100644
--- a/doc/script_commands.txt
+++ b/doc/script_commands.txt
@@ -8908,6 +8908,29 @@ Directions are the same as NPC sprite facing directions: (DIR_ constants).
 
 This will open a book item at the specified page.
 
+---------------------------------------
+
+*rodex_sendmail(<char id>, "<sender name>", "<title>", "<body>"{, <zeny>{, <item1>, <amount1>{, <item2>, <amount2>{, <item3>, <amount3>{, <item4>, <amount4>{, <item5>, <amount5>}}}}}})
+*rodex_sendmail_acc(<account id>, "<sender name>", "<title>", "<body>"{, <zeny>{, <item1>, <amount1>{, <item2>, <amount2>{, <item3>, <amount3>{, <item4>, <amount4>{, <item5>, <amount5>}}}}}})
+
+This will send a mail using the RoDEX mail system, in newer clients the message will be marked as a NPC mail (that you can't reply).
+If items and zeny are specified, they will be added as attachments to the message.
+
+For examples of usage, see /doc/sample/npc_rodex.txt
+
+---------------------------------------
+
+*rodex_sendmail2(<char id>, "<sender name>", "<title>", "<body>"{, <zeny>{, <item1>, <amount1>, <refine1>, <attribute1>, <card0_1>, <card1_1>, <card2_1>, <card3_1>{,<item2>, <amount2>, <refine2>, <attribute2>, <card0_2>, <card1_2>, <card2_2>, <card3_2>{,<item3>, <amount3>, <refine3>, <attribute3>, <card0_3>, <card1_3>, <card2_3>, <card3_3>{,<item4>, <amount4>, <refine4>, <attribute4>, <card0_4>, <card1_4>, <card2_4>, <card3_4>{,<item5>, <amount5>, <refine5>, <attribute5>, <card0_5>, <card1_5>, <card2_5>, <card3_5>}}}}}})
+*rodex_sendmail_acc2(<account id>, "<sender name>", "<title>", "<body>"{, <zeny>{, <item1>, <amount1>, <refine1>, <attribute1>, <card0_1>, <card1_1>, <card2_1>, <card3_1>{,<item2>, <amount2>, <refine2>, <attribute2>, <card0_2>, <card1_2>, <card2_2>, <card3_2>{,<item3>, <amount3>, <refine3>, <attribute3>, <card0_3>, <card1_3>, <card2_3>, <card3_3>{,<item4>, <amount4>, <refine4>, <attribute4>, <card0_4>, <card1_4>, <card2_4>, <card3_4>{,<item5>, <amount5>, <refine5>, <attribute5>, <card0_5>, <card1_5>, <card2_5>, <card3_5>}}}}}})
+
+These are more flexible versions of rodex_sendmail and rodex_sendmail_acc.
+
+This will send a mail using the RoDEX mail system, in newer clients the message will be marked as a NPC mail (that you can't reply).
+If items and zeny are specified, they will be added as attachments to the message.
+Check getitem2 command for more information of the extra parameters.
+
+For examples of usage, see /doc/sample/npc_rodex.txt
+
 ---------------------------------------
 //=====================================
 7 - Instance-Related Commands
diff --git a/sql-files/main.sql b/sql-files/main.sql
index 3edbdbc3b..1e1fdbc38 100644
--- a/sql-files/main.sql
+++ b/sql-files/main.sql
@@ -882,7 +882,7 @@ INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1475526420); -- 2016-10-0
 INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1477434595); -- 2016-10-26--10-29.sql
 INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1488454834); -- 2017-03-02--11-40.sql
 INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1488744559); -- 2017-03-05--08-09.sql
-
+INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1489588190); -- 2017-03-15--14-29.sql
 --
 -- Table structure for table `storage`
 --
@@ -916,3 +916,56 @@ CREATE TABLE IF NOT EXISTS `storage` (
   PRIMARY KEY (`id`),
   KEY `account_id` (`account_id`)
 ) ENGINE=MyISAM;
+
+CREATE TABLE IF NOT EXISTS `rodex_items` (
+	`id` INT(11) NOT NULL AUTO_INCREMENT,
+	`mail_id` BIGINT(20) NOT NULL DEFAULT '0',
+	`nameid` INT(11) NOT NULL DEFAULT '0',
+	`amount` INT(11) NOT NULL DEFAULT '0',
+	`equip` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+	`identify` SMALLINT(6) NOT NULL DEFAULT '0',
+	`refine` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+	`attribute` TINYINT(4) NOT NULL DEFAULT '0',
+	`card0` SMALLINT(11) NOT NULL DEFAULT '0',
+	`card1` SMALLINT(11) NOT NULL DEFAULT '0',
+	`card2` SMALLINT(11) NOT NULL DEFAULT '0',
+	`card3` SMALLINT(11) NOT NULL DEFAULT '0',
+	`opt_idx0` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val0` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx1` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val1` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx2` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val2` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx3` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val3` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx4` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val4` SMALLINT(5) NOT NULL DEFAULT '0',
+	`expire_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+	`bound` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
+	`unique_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
+	PRIMARY KEY (`id`),
+  KEY `mail_id` (`mail_id`)
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `rodex_mail` (
+	`mail_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`sender_name` VARCHAR(30) NOT NULL,
+	`sender_id` INT(11) NOT NULL,
+	`receiver_name` VARCHAR(30) NOT NULL,
+	`receiver_id` INT(11) NOT NULL,
+  `receiver_accountid` INT(11) NOT NULL,
+	`title` VARCHAR(50) NOT NULL,
+	`body` VARCHAR(510) NOT NULL,
+	`zeny` BIGINT(20) NOT NULL,
+	`type` TINYINT(8) UNSIGNED NOT NULL,
+	`is_read` TINYINT(8) NOT NULL,
+	`send_date` INT(11) NOT NULL,
+	`expire_date` INT(11) NOT NULL,
+	`weight` INT(11) NOT NULL,
+	PRIMARY KEY (`mail_id`),
+  KEY `sender_id` (`sender_id`),
+  KEY `receiver_id` (`receiver_id`),
+  KEY `receiver_accountid` (`receiver_accountid`),
+  KEY `send_date` (`send_date`),
+  KEY `expire_date` (`expire_date`)
+) ENGINE=MyISAM;
diff --git a/sql-files/upgrades/2017-03-15--14-29.sql b/sql-files/upgrades/2017-03-15--14-29.sql
new file mode 100644
index 000000000..6e6044738
--- /dev/null
+++ b/sql-files/upgrades/2017-03-15--14-29.sql
@@ -0,0 +1,74 @@
+#1489588190
+
+-- This file is part of Hercules.
+-- http://herc.ws - http://github.com/HerculesWS/Hercules
+--
+-- Copyright (C) 2017  Hercules Dev Team
+--
+-- Hercules 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 3 of the License, or
+-- (at your option) 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/>.
+
+CREATE TABLE IF NOT EXISTS `rodex_items` (
+	`id` INT(11) NOT NULL AUTO_INCREMENT,
+	`mail_id` BIGINT(20) NOT NULL DEFAULT '0',
+	`nameid` INT(11) NOT NULL DEFAULT '0',
+	`amount` INT(11) NOT NULL DEFAULT '0',
+	`equip` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+	`identify` SMALLINT(6) NOT NULL DEFAULT '0',
+	`refine` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+	`attribute` TINYINT(4) NOT NULL DEFAULT '0',
+	`card0` SMALLINT(11) NOT NULL DEFAULT '0',
+	`card1` SMALLINT(11) NOT NULL DEFAULT '0',
+	`card2` SMALLINT(11) NOT NULL DEFAULT '0',
+	`card3` SMALLINT(11) NOT NULL DEFAULT '0',
+	`opt_idx0` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val0` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx1` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val1` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx2` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val2` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx3` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val3` SMALLINT(5) NOT NULL DEFAULT '0',
+	`opt_idx4` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+	`opt_val4` SMALLINT(5) NOT NULL DEFAULT '0',
+	`expire_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+	`bound` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
+	`unique_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
+	PRIMARY KEY (`id`),
+  KEY `mail_id` (`mail_id`)
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `rodex_mail` (
+	`mail_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`sender_name` VARCHAR(30) NOT NULL COLLATE 'utf8_unicode_ci',
+	`sender_id` INT(11) NOT NULL,
+	`receiver_name` VARCHAR(30) NOT NULL COLLATE 'utf8_unicode_ci',
+	`receiver_id` INT(11) NOT NULL,
+	`receiver_accountid` INT(11) NOT NULL,
+	`title` VARCHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
+	`body` VARCHAR(510) NOT NULL COLLATE 'utf8_unicode_ci',
+	`zeny` BIGINT(20) NOT NULL,
+	`type` TINYINT(8) UNSIGNED NOT NULL,
+	`is_read` TINYINT(8) NOT NULL,
+	`send_date` INT(11) NOT NULL,
+	`expire_date` INT(11) NOT NULL,
+	`weight` INT(11) NOT NULL,
+	PRIMARY KEY (`mail_id`),
+  KEY `sender_id` (`sender_id`),
+  KEY `receiver_id` (`receiver_id`),
+  KEY `receiver_accountid` (`receiver_accountid`),
+  KEY `send_date` (`send_date`),
+  KEY `expire_date` (`expire_date`)
+) ENGINE=MyISAM;
+
+INSERT INTO `sql_updates` (`timestamp`, `ignored`) VALUES (1489588190 , 'No');
diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt
index d56697a40..553e071ee 100644
--- a/sql-files/upgrades/index.txt
+++ b/sql-files/upgrades/index.txt
@@ -39,3 +39,4 @@
 2016-10-26--10-29.sql
 2017-03-02--11-40.sql
 2017-03-05--08-09.sql
+2017-03-15--14-29.sql
diff --git a/src/char/HPMchar.c b/src/char/HPMchar.c
index e0f81c61f..3a74f443d 100644
--- a/src/char/HPMchar.c
+++ b/src/char/HPMchar.c
@@ -36,6 +36,7 @@
 #include "char/int_party.h"
 #include "char/int_pet.h"
 #include "char/int_quest.h"
+#include "char/int_rodex.h"
 #include "char/int_storage.h"
 #include "char/loginif.h"
 #include "char/mapif.h"
diff --git a/src/char/Makefile.in b/src/char/Makefile.in
index 7806ce2bb..1a7d067a4 100644
--- a/src/char/Makefile.in
+++ b/src/char/Makefile.in
@@ -42,11 +42,11 @@ MT19937AR_H = $(MT19937AR_D)/mt19937ar.h
 
 CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_auction.c int_elemental.c int_guild.c \
          int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \
-         int_quest.c int_storage.c pincode.c
+         int_quest.c int_rodex.c int_storage.c pincode.c
 CHAR_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(CHAR_C)))
 CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_auction.h int_elemental.h int_guild.h \
          int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \
-         int_quest.h int_storage.h pincode.h
+         int_quest.h int_rodex.h int_storage.h pincode.h
 CHAR_PH =
 
 HAVE_MYSQL=@HAVE_MYSQL@
diff --git a/src/char/char.c b/src/char/char.c
index f66108c00..4687d4d6b 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -34,6 +34,7 @@
 #include "char/int_party.h"
 #include "char/int_pet.h"
 #include "char/int_quest.h"
+#include "char/int_rodex.h"
 #include "char/int_storage.h"
 #include "char/inter.h"
 #include "char/loginif.h"
@@ -97,6 +98,8 @@ char auction_db[256] = "auction"; // Auctions System
 char friend_db[256] = "friends";
 char hotkey_db[256] = "hotkey";
 char quest_db[256] = "quest";
+char rodex_db[256] = "rodex_mail";
+char rodex_item_db[256] = "rodex_items";
 char homunculus_db[256] = "homunculus";
 char skill_homunculus_db[256] = "skill_homunculus";
 char mercenary_db[256] = "mercenary";
@@ -6412,6 +6415,7 @@ void char_load_defaults(void)
 	inter_pet_defaults();
 	inter_quest_defaults();
 	inter_storage_defaults();
+	inter_rodex_defaults();
 	inter_defaults();
 	geoip_defaults();
 }
diff --git a/src/char/char.h b/src/char/char.h
index 6b081e536..3689690c7 100644
--- a/src/char/char.h
+++ b/src/char/char.h
@@ -328,6 +328,8 @@ extern char pet_db[256];
 extern char mail_db[256];
 extern char auction_db[256];
 extern char quest_db[256];
+extern char rodex_db[256];
+extern char rodex_item_db[256];
 extern char homunculus_db[256];
 extern char skill_homunculus_db[256];
 extern char mercenary_db[256];
diff --git a/src/char/int_rodex.c b/src/char/int_rodex.c
new file mode 100644
index 000000000..12ab54290
--- /dev/null
+++ b/src/char/int_rodex.c
@@ -0,0 +1,536 @@
+/**
+ * This file is part of Hercules.
+ * http://herc.ws - http://github.com/HerculesWS/Hercules
+ *
+ * Copyright (C) 2017 Hercules Dev Team
+ *
+ * Hercules 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 3 of the License, or
+ * (at your option) 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/>.
+ */
+#define HERCULES_CORE
+
+#include "int_rodex.h"
+
+#include "char/char.h"
+#include "char/inter.h"
+#include "char/mapif.h"
+#include "common/db.h"
+#include "common/memmgr.h"
+#include "common/mmo.h"
+#include "common/nullpo.h"
+#include "common/showmsg.h"
+#include "common/socket.h"
+#include "common/sql.h"
+#include "common/strlib.h"
+#include "common/timer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+struct inter_rodex_interface inter_rodex_s;
+struct inter_rodex_interface *inter_rodex;
+
+// Loads new mails of this char_id/account_id
+static int inter_rodex_fromsql(int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails)
+{
+	int i, count = 0;
+	struct rodex_message msg = { 0 };
+	struct SqlStmt *stmt;
+	struct SqlStmt *stmt_items;
+
+	nullpo_retr(-1, mails);
+
+	stmt = SQL->StmtMalloc(inter->sql_handle);
+
+	switch (opentype) {
+	case RODEX_OPENTYPE_MAIL:
+		if (SQL_ERROR == SQL->StmtPrepare(stmt,
+			"SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`,"
+			"`title`, `body`, `zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`"
+			"FROM `%s` WHERE `expire_date` > '%d' AND `receiver_id` = '%d' AND `mail_id` > '%"PRId64"'"
+			"ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), char_id, mail_id)
+			) {
+			SqlStmt_ShowDebug(stmt);
+			SQL->StmtFree(stmt);
+			return -1;
+		}
+		break;
+
+	case RODEX_OPENTYPE_ACCOUNT:
+		if (SQL_ERROR == SQL->StmtPrepare(stmt,
+			"SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`,"
+			"`title`, `body`, `zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`"
+			"FROM `%s` WHERE "
+			"`expire_date` > '%d' AND `receiver_accountid` = '%d' AND `mail_id` > '%"PRId64"'"
+			"ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), account_id, mail_id)
+			) {
+			SqlStmt_ShowDebug(stmt);
+			SQL->StmtFree(stmt);
+			return -1;
+		}
+		break;
+
+	case RODEX_OPENTYPE_RETURN:
+		if (SQL_ERROR == SQL->StmtPrepare(stmt,
+			"SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`,"
+			"`title`, `body`, `zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`"
+			"FROM `%s` WHERE (`sender_id` = '%d' AND `expire_date` <= '%d' AND `send_date` + '%d' > '%d' AND `mail_id` > '%"PRId64"')"
+			"ORDER BY `mail_id` ASC", rodex_db, char_id, (int)time(NULL), 2 * RODEX_EXPIRE, (int)time(NULL), mail_id)
+			) {
+			SqlStmt_ShowDebug(stmt);
+			SQL->StmtFree(stmt);
+			return -1;
+		}
+		break;
+	}
+
+	if (SQL_ERROR == SQL->StmtExecute(stmt)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT64, &msg.id, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_STRING, &msg.sender_name, sizeof(msg.sender_name), NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &msg.sender_id, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &msg.receiver_name, sizeof(msg.receiver_name), NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &msg.receiver_id, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &msg.receiver_accountid, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_STRING, &msg.title, sizeof(msg.title), NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_STRING, &msg.body, sizeof(msg.body), NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT, &msg.zeny, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT8, &msg.type, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_INT8, &msg.is_read, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_INT, &msg.send_date, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_INT, &msg.expire_date, 0, NULL, NULL)
+		|| SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_INT, &msg.weight, 0, NULL, NULL)
+		) {
+		SqlStmt_ShowDebug(stmt);
+		SQL->StmtFree(stmt);
+		return -1;
+	}
+
+	stmt_items = SQL->StmtMalloc(inter->sql_handle);
+	if (stmt_items == NULL) {
+		SQL->StmtFreeResult(stmt);
+		SQL->StmtFree(stmt);
+		return -1;
+	}
+
+	// Read mails
+	while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) {
+		struct item it = { 0 };
+
+		if (msg.type & MAIL_TYPE_ITEM) {
+			if (SQL_ERROR == SQL->StmtPrepare(stmt_items, "SELECT `nameid`, `amount`, `equip`, `identify`,"
+				"`refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `opt_idx0`, `opt_val0`,"
+				"`opt_idx1`, `opt_val1`, `opt_idx2`, `opt_val2`, `opt_idx3`, `opt_val3`, `opt_idx4`, `opt_val4`,"
+				"`expire_time`, `bound`, `unique_id`"
+				"FROM `%s` WHERE mail_id = '%"PRId64"' ORDER BY `mail_id` ASC", rodex_item_db, msg.id)
+				|| SQL_ERROR == SQL->StmtExecute(stmt_items)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 0, SQLDT_INT, &it.nameid, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 1, SQLDT_INT, &it.amount, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 2, SQLDT_UINT, &it.equip, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 3, SQLDT_INT8, &it.identify, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 4, SQLDT_INT8, &it.refine, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 5, SQLDT_INT8, &it.attribute, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 6, SQLDT_INT16, &it.card[0], 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 7, SQLDT_INT16, &it.card[1], 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 8, SQLDT_INT16, &it.card[2], 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9, SQLDT_INT16, &it.card[3], 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 10, SQLDT_INT16, &it.option[0].index, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 11, SQLDT_INT16, &it.option[0].value, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 12, SQLDT_INT16, &it.option[1].index, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 13, SQLDT_INT16, &it.option[1].value, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 14, SQLDT_INT16, &it.option[2].index, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 15, SQLDT_INT16, &it.option[2].value, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 16, SQLDT_INT16, &it.option[3].index, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 17, SQLDT_INT16, &it.option[3].value, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 18, SQLDT_INT16, &it.option[4].index, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 19, SQLDT_INT16, &it.option[4].value, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 20, SQLDT_INT, &it.expire_time, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 21, SQLDT_UINT8, &it.bound, 0, NULL, NULL)
+				|| SQL_ERROR == SQL->StmtBindColumn(stmt_items, 22, SQLDT_UINT64, &it.unique_id, 0, NULL, NULL)) {
+				SqlStmt_ShowDebug(stmt_items);
+			}
+
+			for (i = 0; i < RODEX_MAX_ITEM && SQL_SUCCESS == SQL->StmtNextRow(stmt_items); ++i) {
+				msg.items[i].item = it;
+				msg.items_count++;
+			}
+		}
+
+		if (msg.items_count == 0) {
+			msg.type &= ~MAIL_TYPE_ITEM;
+		}
+
+		if (msg.zeny == 0) {
+			msg.type &= ~MAIL_TYPE_ZENY;
+		}
+
+		msg.opentype = opentype;
+#if PACKETVER < 20160601
+		// NPC Message Type isn't supported in old clients
+		msg.type &= ~MAIL_TYPE_NPC;
+#endif
+
+		++count;
+		VECTOR_ENSURE(*mails, 1, 1);
+		VECTOR_PUSH(*mails, msg);
+		memset(&msg, 0, sizeof(struct rodex_message));
+	}
+
+	SQL->StmtFreeResult(stmt);
+	SQL->StmtFreeResult(stmt_items);
+
+	SQL->StmtFree(stmt);
+	SQL->StmtFree(stmt_items);
+
+	ShowInfo("rodex load complete from DB - id: %d (total: %d)\n", char_id, count);
+	return count;
+}
+
+// Checks if user has non-read mails
+static bool inter_rodex_hasnew(int char_id, int account_id)
+{
+	int count = 0;
+	char *data;
+	
+	if (SQL_ERROR == SQL->Query(inter->sql_handle, 
+		"SELECT count(*) FROM `%s` WHERE ("
+		"(`expire_date` > '%d' AND (`receiver_id` = '%d' OR `receiver_accountid` = '%d')) OR"
+		"(`sender_id` = '%d' AND `expire_date` <= '%d' AND `send_date` + '%d' > '%d')"
+		") AND (`is_read` = 0 OR (`type` > 0 AND `type` != 8))",
+		rodex_db, (int)time(NULL), char_id, account_id,
+		char_id, (int)time(NULL), 2 * RODEX_EXPIRE, (int)time(NULL))
+		) {
+		Sql_ShowDebug(inter->sql_handle);
+		return -1;
+	}
+	
+	if (SQL_SUCCESS != SQL->NextRow(inter->sql_handle))
+		return false;
+
+	SQL->GetData(inter->sql_handle, 0, &data, NULL);
+	count = atoi(data);
+	SQL->FreeResult(inter->sql_handle);
+
+	return count > 0;
+}
+
+/// Checks player name and retrieves some data
+static bool inter_rodex_checkname(const char *name, int *target_char_id, short *target_class, int *target_level)
+{
+	char esc_name[NAME_LENGTH * 2 + 1];
+	bool found = false;
+
+	nullpo_retr(false, name);
+	nullpo_retr(false, target_char_id);
+	nullpo_retr(false, target_class);
+	nullpo_retr(false, target_level);
+
+	// Try to find the Dest Char by Name
+	SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+	if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `char_id`, `class`, `base_level` FROM `%s` WHERE `name` = '%s'", char_db, esc_name)) {
+		Sql_ShowDebug(inter->sql_handle);
+	} else {
+		if (SQL_SUCCESS == SQL->NextRow(inter->sql_handle)) {
+			char *data;
+			SQL->GetData(inter->sql_handle, 0, &data, NULL); *target_char_id = atoi(data);
+			SQL->GetData(inter->sql_handle, 1, &data, NULL); *target_class = (short)atoi(data);
+			SQL->GetData(inter->sql_handle, 2, &data, NULL); *target_level = atoi(data);
+			found = true;
+		}
+	}
+	SQL->FreeResult(inter->sql_handle);
+
+	return found;
+}
+
+/// Stores a single message in the database.
+/// Returns the message's ID if successful (or 0 if it fails).
+int64 inter_rodex_savemessage(struct rodex_message* msg)
+{
+	char sender_name[NAME_LENGTH * 2 + 1];
+	char receiver_name[NAME_LENGTH * 2 + 1];
+	char body[RODEX_BODY_LENGTH * 2 + 1];
+	char title[RODEX_TITLE_LENGTH * 2 + 1];
+	int i;
+
+	nullpo_retr(false, msg);
+
+	SQL->EscapeStringLen(inter->sql_handle, sender_name, msg->sender_name, strnlen(msg->sender_name, NAME_LENGTH));
+	SQL->EscapeStringLen(inter->sql_handle, receiver_name, msg->receiver_name, strnlen(msg->receiver_name, NAME_LENGTH));
+	SQL->EscapeStringLen(inter->sql_handle, body, msg->body, strnlen(msg->body, RODEX_BODY_LENGTH));
+	SQL->EscapeStringLen(inter->sql_handle, title, msg->title, strnlen(msg->title, RODEX_TITLE_LENGTH));
+
+	if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`, `title`, `body`,"
+		"`zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`) VALUES "
+		"('%s', '%d', '%s', '%d', '%d', '%s', '%s', '%"PRId64"', '%d', '%d', '%d', '%d', '%d')",
+		rodex_db, sender_name, msg->sender_id, receiver_name, msg->receiver_id, msg->receiver_accountid,
+		title, body, msg->zeny, msg->type, msg->is_read == true ? 1 : 0, msg->send_date, msg->expire_date, msg->weight)) {
+		Sql_ShowDebug(inter->sql_handle);
+		return 0;
+	}
+
+	msg->id = (int64)SQL->LastInsertId(inter->sql_handle);
+
+	for (i = 0; i < RODEX_MAX_ITEM; ++i) {
+		// Should we use statement here? [KIRIEZ]
+		struct item *it = &msg->items[i].item;
+		if (it->nameid == 0)
+			continue;
+
+		if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`mail_id`, `nameid`, `amount`, `equip`, `identify`,"
+			"`refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `opt_idx0`, `opt_val0`, `opt_idx1`, `opt_val1`, `opt_idx2`,"
+			"`opt_val2`, `opt_idx3`, `opt_val3`, `opt_idx4`, `opt_val4`,`expire_time`, `bound`, `unique_id`) VALUES "
+			"('%"PRId64"', '%d', '%d', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%u', '%"PRIu64"')",
+			rodex_item_db, msg->id, it->nameid, it->amount, it->equip, it->identify, it->refine, it->attribute, it->card[0], it->card[1], it->card[2], it->card[3],
+			it->option[0].index, it->option[0].value, it->option[1].index, it->option[1].value, it->option[2].index, it->option[2].value, it->option[3].index,
+			it->option[3].value, it->option[4].index, it->option[4].value, it->expire_time, it->bound, it->unique_id)
+			) {
+			Sql_ShowDebug(inter->sql_handle);
+			continue;
+		}
+	}
+
+	return msg->id;
+}
+
+/*==========================================
+ * Inbox Request
+ *------------------------------------------*/
+void mapif_rodex_sendinbox(int fd, int char_id, int8 opentype, int8 flag, int count, struct rodex_maillist *mails)
+{
+	int per_packet = (UINT16_MAX - 15) / sizeof(struct rodex_message);
+	int sent = 0;
+	nullpo_retv(mails);
+	Assert_retv(char_id > 0);
+	Assert_retv(count >= 0);
+
+	do {
+		int i = 15, j, size, limit;
+		bool is_last = true;
+
+		if (count <= per_packet) {
+			size = count * sizeof(struct rodex_message) + 15;
+			limit = count;
+			is_last = true;
+		} else {
+			int to_send = count - sent;
+			limit = min(to_send, per_packet);
+			if (limit != to_send) {
+				is_last = false;
+			}
+			size = limit * sizeof(struct rodex_message) + 15;
+		}
+
+		WFIFOHEAD(fd, size);
+		WFIFOW(fd, 0) = 0x3895;
+		WFIFOW(fd, 2) = size;
+		WFIFOL(fd, 4) = char_id;
+		WFIFOB(fd, 8) = opentype;
+		WFIFOB(fd, 9) = flag;
+		WFIFOB(fd, 10) = is_last;
+		WFIFOL(fd, 11) = count;
+		for (j = 0; j < limit; ++j, ++sent, i += sizeof(struct rodex_message)) {
+			memcpy(WFIFOP(fd, i), &VECTOR_INDEX(*mails, sent), sizeof(struct rodex_message));
+		}
+		WFIFOSET(fd, size);
+	} while (sent < count);
+}
+
+void mapif_parse_rodex_requestinbox(int fd)
+{
+	int count;
+	int char_id = RFIFOL(fd,2);
+	int account_id = RFIFOL(fd, 6);
+	int8 flag = RFIFOB(fd, 10);
+	int8 opentype = RFIFOB(fd, 11);
+	int64 mail_id = RFIFOQ(fd, 12);
+	struct rodex_maillist mails = { 0 };
+
+	VECTOR_INIT(mails);
+	if (flag == 0)
+		count = inter_rodex->fromsql(char_id, account_id, opentype, 0, &mails);
+	else
+		count = inter_rodex->fromsql(char_id, account_id, opentype, mail_id, &mails);
+	mapif->rodex_sendinbox(fd, char_id, opentype, flag, count, &mails);
+	VECTOR_CLEAR(mails);
+}
+
+/*==========================================
+* Checks if there are new mails
+*------------------------------------------*/
+void mapif_rodex_sendhasnew(int fd, int char_id, bool has_new)
+{
+	Assert_retv(char_id > 0);
+
+	WFIFOHEAD(fd, 7);
+	WFIFOW(fd, 0) = 0x3896;
+	WFIFOL(fd, 2) = char_id;
+	WFIFOB(fd, 6) = has_new;
+	WFIFOSET(fd, 7);
+}
+
+void mapif_parse_rodex_checkhasnew(int fd)
+{
+	int char_id = RFIFOL(fd, 2);
+	int account_id = RFIFOL(fd, 6);
+	bool has_new;
+
+	Assert_retv(account_id >= START_ACCOUNT_NUM && account_id <= END_ACCOUNT_NUM);
+	Assert_retv(char_id >= START_CHAR_NUM);
+
+	has_new = inter_rodex->hasnew(char_id, account_id);
+	mapif->rodex_sendhasnew(fd, char_id, has_new);
+}
+
+/*==========================================
+ * Update/Delete mail
+ *------------------------------------------*/
+void mapif_parse_rodex_updatemail(int fd)
+{
+	int64 mail_id = RFIFOL(fd, 2);
+	int8 flag = RFIFOB(fd, 10);
+
+	Assert_retv(mail_id > 0);
+	Assert_retv(flag >= 0 && flag <= 3);
+
+	switch (flag) {
+	case 0: // Read
+		if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `is_read` = 1 WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id))
+			Sql_ShowDebug(inter->sql_handle);
+		break;
+	
+	case 1: // Get Zeny
+		if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~2) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id))
+			Sql_ShowDebug(inter->sql_handle);
+		break;
+	
+	case 2: // Get Items
+		if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_item_db, mail_id))
+			Sql_ShowDebug(inter->sql_handle);
+		if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~4) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id))
+			Sql_ShowDebug(inter->sql_handle);
+		break;
+
+	case 3: // Delete Mail
+		if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id))
+			Sql_ShowDebug(inter->sql_handle);
+		if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_item_db, mail_id))
+			Sql_ShowDebug(inter->sql_handle);
+		break;
+	}
+}
+
+/*==========================================
+ * Send Mail
+ *------------------------------------------*/
+void mapif_rodex_send(int fd, int sender_id, int receiver_id, int receiver_accountid, bool result)
+{
+	Assert_retv(sender_id >= 0);
+	Assert_retv(receiver_id + receiver_accountid > 0);
+	
+	WFIFOHEAD(fd,15);
+	WFIFOW(fd,0) = 0x3897;
+	WFIFOL(fd,2) = sender_id;
+	WFIFOL(fd,6) = receiver_id;
+	WFIFOL(fd,10) = receiver_accountid;
+	WFIFOB(fd,14) = result;
+	WFIFOSET(fd,15);
+}
+
+void mapif_parse_rodex_send(int fd)
+{
+	struct rodex_message msg = { 0 };
+
+	if (RFIFOW(fd,2) != 4 + sizeof(struct rodex_message))
+		return;
+
+	memcpy(&msg, RFIFOP(fd,4), sizeof(struct rodex_message));
+	if (msg.receiver_id > 0 || msg.receiver_accountid > 0)
+		msg.id = inter_rodex->savemessage(&msg);
+
+	mapif->rodex_send(fd, msg.sender_id, msg.receiver_id, msg.receiver_accountid, msg.id > 0 ? true : false);
+}
+
+/*------------------------------------------
+ * Check Player
+ *------------------------------------------*/
+void mapif_rodex_checkname(int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char name[NAME_LENGTH])
+{
+	nullpo_retv(name);
+	Assert_retv(reqchar_id > 0);
+	Assert_retv(target_char_id >= 0);
+
+	WFIFOHEAD(fd, 16 + NAME_LENGTH);
+	WFIFOW(fd, 0) = 0x3898;
+	WFIFOL(fd, 2) = reqchar_id;
+	WFIFOL(fd, 6) = target_char_id;
+	WFIFOW(fd, 10) = target_class;
+	WFIFOL(fd, 12) = target_level;
+	safestrncpy(WFIFOP(fd, 16), name, NAME_LENGTH);
+	WFIFOSET(fd, 16 + NAME_LENGTH);
+}
+
+void mapif_parse_rodex_checkname(int fd)
+{
+	int reqchar_id = RFIFOL(fd, 2);
+	char name[NAME_LENGTH];
+	int target_char_id, target_level;
+	short target_class;
+
+	safestrncpy(name, RFIFOP(fd, 6), NAME_LENGTH);
+
+	if (inter_rodex->checkname(name, &target_char_id, &target_class, &target_level) == true)
+		mapif->rodex_checkname(fd, reqchar_id, target_char_id, target_class, target_level, name);
+	else
+		mapif->rodex_checkname(fd, reqchar_id, 0, 0, 0, name);
+}
+
+/*==========================================
+ * Packets From Map Server
+ *------------------------------------------*/
+int inter_rodex_parse_frommap(int fd)
+{
+	switch(RFIFOW(fd,0))
+	{
+		case 0x3095: mapif->parse_rodex_requestinbox(fd); break;
+		case 0x3096: mapif->parse_rodex_checkhasnew(fd); break;
+		case 0x3097: mapif->parse_rodex_updatemail(fd); break;
+		case 0x3098: mapif->parse_rodex_send(fd); break;
+		case 0x3099: mapif->parse_rodex_checkname(fd); break;
+		default:
+			return 0;
+	}
+	return 1;
+}
+
+int inter_rodex_sql_init(void)
+{
+	return 1;
+}
+
+void inter_rodex_sql_final(void)
+{
+	return;
+}
+
+void inter_rodex_defaults(void)
+{
+	inter_rodex = &inter_rodex_s;
+
+	inter_rodex->savemessage = inter_rodex_savemessage;
+	inter_rodex->parse_frommap = inter_rodex_parse_frommap;
+	inter_rodex->sql_init = inter_rodex_sql_init;
+	inter_rodex->sql_final = inter_rodex_sql_final;
+	inter_rodex->fromsql = inter_rodex_fromsql;
+	inter_rodex->hasnew = inter_rodex_hasnew;
+	inter_rodex->checkname = inter_rodex_checkname;
+}
diff --git a/src/char/int_rodex.h b/src/char/int_rodex.h
new file mode 100644
index 000000000..801ebcb89
--- /dev/null
+++ b/src/char/int_rodex.h
@@ -0,0 +1,47 @@
+/**
+ * This file is part of Hercules.
+ * http://herc.ws - http://github.com/HerculesWS/Hercules
+ *
+ * Copyright (C) 2017 Hercules Dev Team
+ *
+ * Hercules 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 3 of the License, or
+ * (at your option) 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 CHAR_INT_RODEX_H
+#define CHAR_INT_RODEX_H
+
+#include "common/mmo.h"
+#include "common/db.h"
+
+struct item;
+
+/**
+ * inter_rodex interface
+ **/
+struct inter_rodex_interface {
+	int (*sql_init) (void);
+	void (*sql_final) (void);
+	int (*parse_frommap) (int fd);
+	int (*fromsql) (int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails);
+	bool (*hasnew) (int char_id, int account_id);
+	bool (*checkname) (const char *name, int *target_char_id, short *target_class, int *target_level);
+	int64 (*savemessage) (struct rodex_message* msg);
+};
+
+#ifdef HERCULES_CORE
+void inter_rodex_defaults(void);
+#endif // HERCULES_CORE
+
+HPShared struct inter_rodex_interface *inter_rodex;
+
+#endif /* CHAR_INT_RODEX_H */
diff --git a/src/char/inter.c b/src/char/inter.c
index cfbc0fd4d..557ee5313 100644
--- a/src/char/inter.c
+++ b/src/char/inter.c
@@ -33,6 +33,7 @@
 #include "char/int_party.h"
 #include "char/int_pet.h"
 #include "char/int_quest.h"
+#include "char/int_rodex.h"
 #include "char/int_storage.h"
 #include "char/mapif.h"
 #include "common/cbasetypes.h"
@@ -76,7 +77,7 @@ int inter_recv_packet_length[] = {
 	 6,-1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,    // 3060-  Quest system [Kevin] [Inkfish]
 	-1,10, 6,-1,  0, 0, 0, 0,  0, 0, 0, 0, -1,10,  6,-1,    // 3070-  Mercenary packets [Zephyrus], Elemental packets [pakpil]
 	48,14,-1, 6,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,    // 3080-
-	-1,10,-1, 6,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,    // 3090-  Homunculus packets [albator]
+	-1,10,-1, 6,  0, 20,10,11, -1,6 + NAME_LENGTH, 0, 0,  0, 0,  0, 0,    // 3090-  Homunculus packets [albator], RoDEX packets
 };
 
 struct WisData {
@@ -974,6 +975,7 @@ int inter_init_sql(const char *file)
 	inter_elemental->sql_init();
 	inter_mail->sql_init();
 	inter_auction->sql_init();
+	inter_rodex->sql_init();
 
 	geoip->init();
 	inter->msg_config_read("conf/messages.conf", false);
@@ -994,6 +996,7 @@ void inter_final(void)
 	inter_elemental->sql_final();
 	inter_mail->sql_final();
 	inter_auction->sql_final();
+	inter_rodex->sql_final();
 
 	geoip->final(true);
 	inter->do_final_msg();
@@ -1416,6 +1419,7 @@ int inter_parse_frommap(int fd)
 		  || inter_mail->parse_frommap(fd)
 		  || inter_auction->parse_frommap(fd)
 		  || inter_quest->parse_frommap(fd)
+		  || inter_rodex->parse_frommap(fd)
 		   )
 			break;
 		else
diff --git a/src/char/mapif.c b/src/char/mapif.c
index 5fff96ba8..b71171e23 100644
--- a/src/char/mapif.c
+++ b/src/char/mapif.c
@@ -26,6 +26,7 @@
 #include "char/int_auction.h"
 #include "char/int_guild.h"
 #include "char/int_homun.h"
+#include "char/int_rodex.h"
 #include "common/cbasetypes.h"
 #include "common/mmo.h"
 #include "common/random.h"
@@ -182,6 +183,16 @@ void mapif_quest_save_ack(int fd, int char_id, bool success);
 int mapif_parse_quest_save(int fd);
 void mapif_send_quests(int fd, int char_id, struct quest *tmp_questlog, int num_quests);
 int mapif_parse_quest_load(int fd);
+/* RoDEX */
+int mapif_parse_rodex_requestinbox(int fd);
+void mapif_rodex_sendinbox(int fd, int char_id, int8 opentype, int8 flag, int count, struct rodex_maillist *mails);
+int mapif_parse_rodex_checkhasnew(int fd);
+void mapif_rodex_sendhasnew(int fd, int char_id, bool has_new);
+int mapif_parse_rodex_updatemail(int fd);
+int mapif_parse_rodex_send(int fd);
+void mapif_rodex_send(int fd, int sender_id, int receiver_id, int receiver_accountid, bool result);
+int mapif_parse_rodex_checkname(int fd);
+void mapif_rodex_checkname(int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char name[NAME_LENGTH]);
 int mapif_load_guild_storage(int fd,int account_id,int guild_id, char flag);
 int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, int fail);
 int mapif_parse_LoadGuildStorage(int fd);
@@ -363,6 +374,16 @@ void mapif_defaults(void) {
 	mapif->parse_quest_save = mapif_parse_quest_save;
 	mapif->send_quests = mapif_send_quests;
 	mapif->parse_quest_load = mapif_parse_quest_load;
+	/* RoDEX */
+	mapif->parse_rodex_requestinbox = mapif_parse_rodex_requestinbox;
+	mapif->rodex_sendinbox = mapif_rodex_sendinbox;
+	mapif->parse_rodex_checkhasnew = mapif_parse_rodex_checkhasnew;
+	mapif->rodex_sendhasnew = mapif_rodex_sendhasnew;
+	mapif->parse_rodex_updatemail = mapif_parse_rodex_updatemail;
+	mapif->parse_rodex_send = mapif_parse_rodex_send;
+	mapif->rodex_send = mapif_rodex_send;
+	mapif->parse_rodex_checkname = mapif_parse_rodex_checkname;
+	mapif->rodex_checkname = mapif_rodex_checkname;
 	mapif->load_guild_storage = mapif_load_guild_storage;
 	mapif->save_guild_storage_ack = mapif_save_guild_storage_ack;
 	mapif->parse_LoadGuildStorage = mapif_parse_LoadGuildStorage;
diff --git a/src/char/mapif.h b/src/char/mapif.h
index 07fbed6c6..353da7ab0 100644
--- a/src/char/mapif.h
+++ b/src/char/mapif.h
@@ -176,6 +176,15 @@ struct mapif_interface {
 	int (*parse_quest_save) (int fd);
 	void (*send_quests) (int fd, int char_id, struct quest *tmp_questlog, int num_quests);
 	int (*parse_quest_load) (int fd);
+	int(*parse_rodex_requestinbox) (int fd);
+	void(*rodex_sendinbox) (int fd, int char_id, int8 opentype, int8 flag, int count, struct rodex_maillist *mails);
+	int(*parse_rodex_checkhasnew) (int fd);
+	void(*rodex_sendhasnew) (int fd, int char_id, bool has_new);
+	int(*parse_rodex_updatemail) (int fd);
+	int(*parse_rodex_send) (int fd);
+	void(*rodex_send) (int fd, int sender_id, int receiver_id, int receiver_accountid, bool result);
+	int(*parse_rodex_checkname) (int fd);
+	void(*rodex_checkname) (int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char name[NAME_LENGTH]);
 	int (*load_guild_storage) (int fd, int account_id, int guild_id, char flag);
 	int (*save_guild_storage_ack) (int fd, int account_id, int guild_id, int fail);
 	int (*parse_LoadGuildStorage) (int fd);
diff --git a/src/common/mmo.h b/src/common/mmo.h
index 276e0eb08..9bb9837ab 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -271,6 +271,13 @@
 #endif
 STATIC_ASSERT(MAX_ITEM_OPTIONS <= 5, "This value is limited by the client and database layout and should only be increased if you know the consequences.");
 
+// RoDEX
+#define RODEX_TITLE_LENGTH (40 + 1)
+#define RODEX_BODY_LENGTH (500 + 1)
+#define RODEX_MAX_ITEM (5)
+#define RODEX_EXPIRE (1 * 15 * 24 * 60 * 60)
+#define RODEX_MAIL_PER_PAGE 7
+
 // The following system marks a different job ID system used by the map server,
 // which makes a lot more sense than the normal one. [Skotlex]
 // These marks the "level" of the job.
@@ -809,6 +816,46 @@ enum fame_list_type {
 	RANKTYPE_PK         = 3, //Not supported yet
 };
 
+struct rodex_message {
+	int64 id;
+	int sender_id;
+	char sender_name[NAME_LENGTH];
+	int receiver_id;
+	int receiver_accountid;
+	char receiver_name[NAME_LENGTH];
+	char title[RODEX_TITLE_LENGTH];
+	char body[RODEX_BODY_LENGTH];
+	struct {
+		struct item item;
+		int idx;
+		
+	} items[RODEX_MAX_ITEM];
+	int64 zeny;
+	uint8 type;
+	int8 opentype;
+	bool is_read;
+	bool is_deleted;
+	int send_date;
+	int expire_date;
+	int weight;
+	int items_count;
+};
+
+VECTOR_STRUCT_DECL(rodex_maillist, struct rodex_message);
+
+enum rodex_opentype {
+	RODEX_OPENTYPE_MAIL = 0,
+	RODEX_OPENTYPE_ACCOUNT = 1,
+	RODEX_OPENTYPE_RETURN = 2,
+};
+
+enum MAIL_TYPE {
+	MAIL_TYPE_TEXT = 0x0,
+	MAIL_TYPE_ZENY = 0x2,
+	MAIL_TYPE_ITEM = 0x4,
+	MAIL_TYPE_NPC = 0x8
+};
+
 /**
  * Guild Basic Information
  * It is used to request changes via intif_guild_change_basicinfo in map-server and to
diff --git a/src/map/HPMmap.c b/src/map/HPMmap.c
index 84544976a..381dbf599 100644
--- a/src/map/HPMmap.c
+++ b/src/map/HPMmap.c
@@ -78,6 +78,7 @@
 #include "map/pc_groups.h"
 #include "map/pet.h"
 #include "map/quest.h"
+#include "map/rodex.h"
 #include "map/script.h"
 #include "map/searchstore.h"
 #include "map/skill.h"
diff --git a/src/map/Makefile.in b/src/map/Makefile.in
index c01826bfb..55712cc69 100644
--- a/src/map/Makefile.in
+++ b/src/map/Makefile.in
@@ -44,14 +44,14 @@ MAP_C = atcommand.c battle.c battleground.c buyingstore.c channel.c chat.c \
         chrif.c clif.c date.c duel.c elemental.c guild.c homunculus.c HPMmap.c \
         instance.c intif.c irc-bot.c itemdb.c log.c mail.c map.c mapreg_sql.c \
         mercenary.c mob.c npc.c npc_chat.c party.c path.c pc.c pc_groups.c \
-        pet.c quest.c script.c searchstore.c skill.c status.c storage.c \
+        pet.c quest.c rodex.c script.c searchstore.c skill.c status.c storage.c \
         trade.c unit.c vending.c
 MAP_OBJ = $(addprefix obj_sql/, $(patsubst %c,%o,$(MAP_C)))
 MAP_H = atcommand.h battle.h battleground.h buyingstore.h channel.h chat.h \
         chrif.h clif.h date.h duel.h elemental.h guild.h homunculus.h HPMmap.h \
         instance.h intif.h irc-bot.h itemdb.h log.h mail.h map.h mapreg.h \
         mercenary.h mob.h npc.h packets.h packets_struct.h party.h path.h \
-        pc.h pc_groups.h pet.h quest.h script.h searchstore.h skill.h \
+        pc.h pc_groups.h pet.h quest.h rodex.h script.h searchstore.h skill.h \
         status.h storage.h trade.h unit.h vending.h
 MAP_PH =
 
diff --git a/src/map/battle.c b/src/map/battle.c
index 57a74bba4..b5ad62407 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -7335,6 +7335,8 @@ static const struct battle_data {
 	{ "monster_eye_range_bonus",            &battle_config.mob_eye_range_bonus,             0,      0,      10,             },
 	{ "prevent_logout_trigger",             &battle_config.prevent_logout_trigger,          0xE,    0,      0xF,            },
 	{ "boarding_halter_speed",              &battle_config.boarding_halter_speed,           25,     0,      100,            },
+	{ "features/rodex",                     &battle_config.feature_rodex,                   1,      0,      1,              },
+	{ "features/rodex_use_accountmail",     &battle_config.feature_rodex_use_accountmail,   0,      0,      1,              },
 };
 #ifndef STATS_OPT_OUT
 /**
diff --git a/src/map/battle.h b/src/map/battle.h
index a73c6dc44..e6129ca7c 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -555,6 +555,9 @@ struct Battle_Config {
 
 	int prevent_logout_trigger;
 	int boarding_halter_speed;
+
+	int feature_rodex;
+	int feature_rodex_use_accountmail;
 };
 
 /* criteria for battle_config.idletime_critera */
diff --git a/src/map/clif.c b/src/map/clif.c
index bb4d53a99..c13a3ea80 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -46,6 +46,7 @@
 #include "map/pc.h"
 #include "map/pet.h"
 #include "map/quest.h"
+#include "map/rodex.h"
 #include "map/script.h"
 #include "map/skill.h"
 #include "map/status.h"
@@ -19225,6 +19226,494 @@ unsigned short clif_parse_cmd_optional( int fd, struct map_session_data *sd ) {
 	return cmd;
 }
 
+/*==========================================
+ * RoDEX
+ *------------------------------------------*/
+
+void clif_parse_rodex_open_write_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_open_write_mail(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_OPEN_WRITE_MAIL *rPacket = RFIFOP(fd, 0);
+	int8 result = (rodex->isenabled() == true) ? 1 : 0;
+	
+	clif->rodex_open_write_mail(fd, rPacket->receiveName, result);
+}
+
+void clif_rodex_open_write_mail(int fd, const char *receiver_name, int8 result)
+{
+#if PACKETVER >= 20140416
+	struct PACKET_ZC_ACK_OPEN_WRITE_MAIL *sPacket = NULL;
+	
+	nullpo_retv(receiver_name);
+
+	WFIFOHEAD(fd, sizeof(*sPacket));
+	sPacket = WFIFOP(fd, 0);
+	sPacket->PacketType = rodexopenwrite;
+	strncpy(sPacket->receiveName, receiver_name, NAME_LENGTH);
+	sPacket->result = result;
+	WFIFOSET(fd, sizeof(*sPacket));
+#endif
+}
+
+void clif_parse_rodex_add_item(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_add_item(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_ADD_ITEM_TO_MAIL *rPacket = RFIFOP(fd, 0);
+	int16 idx = rPacket->index - 2;
+
+	rodex->add_item(sd, idx, (int16)rPacket->count);
+}
+
+void clif_rodex_add_item_result(struct map_session_data *sd, int16 idx, int16 amount, int8 result)
+{
+#if PACKETVER >= 20140416
+	struct PACKET_ZC_ADD_ITEM_TO_MAIL *packet;
+	int fd, j;
+
+	nullpo_retv(sd);
+	if (idx < 0 || idx >= MAX_INVENTORY)
+		result = RODEX_ADD_ITEM_FATAL_ERROR;
+
+	fd = sd->fd;
+
+	WFIFOHEAD(fd, sizeof(*packet));
+	packet = WFIFOP(fd, 0);
+	memset(packet, 0x0, sizeof(*packet));
+	packet->PacketType = rodexadditem;
+	packet->result = result;
+
+	if (result != RODEX_ADD_ITEM_SUCCESS) { //No need to continue building the packet if it failed
+		WFIFOSET(fd, sizeof(*packet));
+		return;
+	}
+
+	packet->index = idx + 2;
+	packet->count = amount;
+	packet->ITID = sd->status.inventory[idx].nameid;
+	packet->type = itemtype(sd->inventory_data[idx]->type);
+	packet->IsIdentified = sd->status.inventory[idx].identify ? 1 : 0;
+	packet->IsDamaged = (sd->status.inventory[idx].attribute & ATTR_BROKEN) != 0 ? 1 : 0;
+	packet->refiningLevel = sd->status.inventory[idx].refine;
+	for (j = 0; j < ARRAYLENGTH(packet->slot.card); ++j)
+		packet->slot.card[j] = sd->status.inventory[idx].card[j];
+	for (j = 0; j < MAX_ITEM_OPTIONS; ++j) {
+		packet->optionData[j].index = sd->status.inventory[idx].option[j].index;
+		packet->optionData[j].param = sd->status.inventory[idx].option[j].param;
+		packet->optionData[j].value = sd->status.inventory[idx].option[j].value;
+	}
+	packet->weight = sd->rodex.tmp.weight / 10;
+	WFIFOSET(fd, sizeof(*packet));
+#endif
+}
+
+void clif_parse_rodex_remove_item(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_remove_item(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_REMOVE_ITEM_MAIL *rPacket = RFIFOP(fd, 0);
+	int16 idx = rPacket->index - 2;
+
+	rodex->remove_item(sd, idx, (int16)rPacket->cnt);
+}
+
+void clif_rodex_remove_item_result(struct map_session_data *sd, int16 idx, int16 amount)
+{
+#if PACKETVER >= 20140521
+	struct PACKET_ZC_ACK_REMOVE_ITEM_MAIL *packet;
+	int fd;
+
+	nullpo_retv(sd);
+	Assert_retv(idx >= 0 && idx < MAX_INVENTORY);
+
+	fd = sd->fd;
+
+	WFIFOHEAD(fd, sizeof(*packet));
+	packet = WFIFOP(fd, 0);
+	packet->PacketType = rodexremoveitem;
+	packet->result = (amount < 0) ? 0 : 1;
+	packet->cnt = (amount < 0) ? 0 : sd->status.inventory[idx].amount - amount;
+	packet->index = idx + 2;
+	packet->weight = sd->rodex.tmp.weight / 10;
+	WFIFOSET(fd, sizeof(*packet));
+#endif
+}
+
+void clif_parse_rodex_checkname(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_checkname(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_CHECKNAME *rPacket = RFIFOP(fd, 0);
+	int char_id = 0, base_level = 0;
+	short class = 0;
+	char name[NAME_LENGTH];
+	
+	safestrncpy(name, rPacket->Name, NAME_LENGTH);
+
+	rodex->check_player(sd, name, &base_level, &char_id, &class);
+}
+
+void clif_rodex_checkname_result(struct map_session_data *sd, int char_id, short class_, int base_level, const char *name)
+{
+#if PACKETVER >= 20140521
+	struct PACKET_ZC_CHECKNAME *sPacket;
+	int fd;
+
+	nullpo_retv(sd);
+	nullpo_retv(name);
+
+	fd = sd->fd;
+	WFIFOHEAD(fd, sizeof(*sPacket));
+	sPacket = WFIFOP(fd, 0);
+	sPacket->PacketType = rodexcheckplayer;
+	if (char_id == 0) {
+		sPacket->CharId = 0;
+		WFIFOSET(fd, sizeof(*sPacket));
+		return;
+	}
+	sPacket->CharId = char_id;
+	sPacket->Class = class_;
+	sPacket->BaseLevel = base_level;
+#if PACKETVER >= 20160316
+	strncpy(sPacket->Name, name, NAME_LENGTH);
+#endif
+	WFIFOSET(fd, sizeof(*sPacket));
+#endif
+}
+
+void clif_parse_rodex_send_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_send_mail(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_SEND_MAIL *rPacket = RFIFOP(fd, 0);
+	int8 result;
+
+	if (rPacket->TextcontentsLength + rPacket->Titlelength > rPacket->PacketLength - sizeof(*rPacket)) {
+		result = RODEX_SEND_MAIL_FATAL_ERROR;
+	} else if (rPacket->TextcontentsLength > RODEX_BODY_LENGTH || rPacket->Titlelength > RODEX_TITLE_LENGTH) {
+		result = RODEX_SEND_MAIL_FATAL_ERROR;
+	} else {
+		char rname[NAME_LENGTH] = "";
+		char title[RODEX_TITLE_LENGTH] = "";
+		char body[RODEX_BODY_LENGTH] = "";
+
+		safestrncpy(rname, rPacket->receiveName, NAME_LENGTH);
+		safestrncpy(title, rPacket->string, RODEX_TITLE_LENGTH);
+		safestrncpy(body, &rPacket->string[rPacket->Titlelength], RODEX_BODY_LENGTH);
+
+		result = rodex->send_mail(sd, rname, body, title, rPacket->zeny);
+	}
+
+	if (result != RODEX_SEND_MAIL_SUCCESS)
+		clif->rodex_send_mail_result(fd, sd, result);
+	rodex->clean(sd, 1);
+}
+
+void clif_rodex_send_mail_result(int fd, struct map_session_data *sd, int8 result)
+{
+#if PACKETVER >= 20131230
+	struct PACKET_ZC_WRITE_MAIL_RESULT *sPacket;
+
+	WFIFOHEAD(fd, sizeof(*sPacket));
+	sPacket = WFIFOP(fd, 0);
+	sPacket->PacketType = rodexwriteresult;
+	sPacket->result = result;
+	WFIFOSET(fd, sizeof(*sPacket));
+#endif
+}
+
+void clif_rodex_send_maillist(int fd, struct map_session_data *sd, int8 open_type, int64 page_start)
+{
+#if PACKETVER >= 20131218
+	struct PACKET_ZC_MAIL_LIST *packet;
+	struct maillistinfo *inner;
+	int16 size = sizeof(*packet);
+	int8 count = 0;
+
+	nullpo_retv(sd);
+
+	WFIFOHEAD(fd, sizeof(*packet) + (sizeof(*inner) + RODEX_TITLE_LENGTH) * 7);
+	packet = WFIFOP(fd, 0);
+	packet->PacketType = ((page_start == (VECTOR_LENGTH(sd->rodex.messages) - 1)) ? rodexmailList : rodexnextpage);
+	packet->opentype = open_type;
+
+	inner = WFIFOP(fd, size);
+
+	while (page_start >= 0 && count < RODEX_MAIL_PER_PAGE) {
+		struct rodex_message *msg = &VECTOR_INDEX(sd->rodex.messages, page_start);
+		--page_start;
+
+		if (msg->is_deleted)
+			continue;
+
+		inner->MailID = msg->id;
+		inner->Isread = msg->is_read == true ? 1 : 0;
+		inner->type = msg->type;
+		inner->regDateTime = (int)time(NULL) - msg->send_date;
+		inner->expireDateTime = msg->expire_date - (int)time(NULL);
+		if (open_type == RODEX_OPENTYPE_RETURN) {
+			inner->expireDateTime += RODEX_EXPIRE;
+		}
+		inner->Titlelength = (int16)strlen(msg->title) + 1;
+		if (open_type != RODEX_OPENTYPE_RETURN) {
+			strncpy(inner->SenderName, msg->sender_name, sizeof(msg->sender_name));
+		} else {
+			strncpy(inner->SenderName, msg->receiver_name, sizeof(msg->receiver_name));
+		}
+		strncpy(inner->title, msg->title, inner->Titlelength);
+		size += sizeof(*inner) + inner->Titlelength;
+		inner = WFIFOP(fd, size);
+		++count;
+	}
+
+	packet->PacketLength = size;
+	packet->cnt = count;
+	packet->IsEnd = page_start > 0 ? 0 : 1;
+	WFIFOSET(fd, size);
+#endif
+}
+
+void clif_rodex_send_refresh(int fd, struct map_session_data *sd, int8 open_type, int count)
+{
+#if PACKETVER >= 20131218
+	struct PACKET_ZC_MAIL_LIST *packet;
+	struct maillistinfo *inner;
+	int16 size = sizeof(*packet);
+	int i, j;
+
+	nullpo_retv(sd);
+
+	WFIFOHEAD(fd, sizeof(*packet) + (sizeof(*inner) + RODEX_TITLE_LENGTH) * 7);
+	packet = WFIFOP(fd, 0);
+	packet->PacketType = rodexmailList;
+	packet->opentype = open_type;
+
+	inner = WFIFOP(fd, size);
+
+	i = VECTOR_LENGTH(sd->rodex.messages) - 1;
+	j = count;
+	while (i >= 0 && j > 0) {
+		struct rodex_message *msg = &VECTOR_INDEX(sd->rodex.messages, i);
+		--i;
+
+		if (msg->is_deleted)
+			continue;
+
+		inner->MailID = msg->id;
+		inner->Isread = msg->is_read == true ? 1 : 0;
+		inner->type = msg->type;
+		inner->regDateTime = (int)time(NULL) - msg->send_date;
+		inner->expireDateTime = msg->expire_date - (int)time(NULL);
+		if (open_type == RODEX_OPENTYPE_RETURN) {
+			inner->expireDateTime += RODEX_EXPIRE;
+		}
+		inner->Titlelength = (int16)strlen(msg->title) + 1;
+		if (open_type != RODEX_OPENTYPE_RETURN) {
+			strncpy(inner->SenderName, msg->sender_name, sizeof(msg->sender_name));
+		} else {
+			strncpy(inner->SenderName, msg->receiver_name, sizeof(msg->receiver_name));
+		}
+		strncpy(inner->title, msg->title, inner->Titlelength);
+		size += sizeof(*inner) + inner->Titlelength;
+		inner = WFIFOP(fd, size);
+		--j;
+	}
+
+	packet->PacketLength = size;
+	packet->cnt = count;
+	packet->IsEnd = 1;
+	WFIFOSET(fd, size);
+#endif
+}
+
+void clif_parse_rodex_next_maillist(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_next_maillist(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_NEXT_MAIL_LIST *packet = RFIFOP(fd, 0);
+
+	rodex->next_page(sd, packet->opentype, packet->Lower_MailID);
+}
+
+void clif_parse_rodex_read_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_read_mail(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_READ_MAIL *rPacket = RFIFOP(fd, 0);
+
+	rodex->read_mail(sd, rPacket->MailID);
+}
+
+void clif_rodex_read_mail(struct map_session_data *sd, int8 opentype, struct rodex_message *msg)
+{
+#if PACKETVER >= 20131223
+	struct PACKET_ZC_READ_MAIL *sPacket;
+	struct mail_item *item;
+	int fd, i, body_len, size;
+
+	nullpo_retv(sd);
+	nullpo_retv(msg);
+	
+	fd = sd->fd;
+	body_len = (int)strlen(msg->body) + 1;
+	size = sizeof(*sPacket);
+
+	WFIFOHEAD(fd, sizeof(*sPacket) + body_len + (sizeof(*item) * RODEX_MAX_ITEM));
+	sPacket = WFIFOP(fd, 0);
+	sPacket->PacketType = rodexread;
+	sPacket->opentype = opentype;
+	sPacket->MailID = msg->id;
+	sPacket->TextcontentsLength = body_len;
+	sPacket->zeny = msg->zeny;
+	sPacket->ItemCnt = msg->items_count;
+	strncpy(WFIFOP(fd, size), msg->body, body_len);
+	size += body_len;
+	for (i = 0; i < RODEX_MAX_ITEM; ++i) {
+		struct item *it = &msg->items[i].item;
+		int j, k;
+
+		if (it->nameid == 0) {
+			continue;
+		}
+
+		item = WFIFOP(fd, size);
+		memset(item, 0x0, sizeof(*item));
+		item->ITID = it->nameid;
+		item->count = it->amount;
+		item->type = itemtype(itemdb->search(it->nameid)->type);
+		item->IsIdentified = it->identify ? 1 : 0;
+		item->IsDamaged = (it->attribute & ATTR_BROKEN) != 0 ? 1 : 0;
+		item->refiningLevel = it->refine;
+		for (k = 0; k < MAX_SLOTS; ++k) {
+			item->slot.card[k] = it->card[k];
+		}
+		for (j = 0; j < MAX_ITEM_OPTIONS; ++j) {
+			item->optionData[j].index = it->option[j].index;
+			item->optionData[j].value = it->option[j].value;
+		}
+
+		size += sizeof(*item);
+	}
+	sPacket->PacketLength = size;
+	WFIFOSET(fd, size);
+#endif
+}
+
+void clif_parse_rodex_delete_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_delete_mail(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_DELETE_MAIL *rPacket = RFIFOP(fd, 0);
+
+	rodex->delete_mail(sd, rPacket->MailID);
+}
+
+void clif_rodex_delete_mail(struct map_session_data *sd, int8 opentype, int64 mail_id)
+{
+#if PACKETVER >= 20131218
+	struct PACKET_ZC_ACK_DELETE_MAIL *sPacket;
+	int fd;
+
+	nullpo_retv(sd);
+
+	fd = sd->fd;
+
+	WFIFOHEAD(fd, sizeof(*sPacket));
+	sPacket = WFIFOP(fd, 0);
+	sPacket->PacketType = rodexdelete;
+	sPacket->opentype = opentype;
+	sPacket->MailID = mail_id;
+	WFIFOSET(fd, sizeof(*sPacket));
+#endif
+}
+
+void clif_parse_rodex_request_zeny(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_request_zeny(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_ZENY_FROM_MAIL *rPacket = RFIFOP(fd, 0);
+	
+	rodex->get_zeny(sd, rPacket->opentype, rPacket->MailID);
+}
+
+void clif_rodex_request_zeny(struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result)
+{
+#if PACKETVER >= 20140409
+	struct PACKET_ZC_ACK_ZENY_FROM_MAIL *sPacket;
+	int fd;
+
+	nullpo_retv(sd);
+
+	fd = sd->fd;
+
+	WFIFOHEAD(fd, sizeof(*sPacket));
+	sPacket = WFIFOP(fd, 0);
+	sPacket->PacketType = rodexgetzeny;
+	sPacket->MailID = mail_id;
+	sPacket->opentype = opentype;
+	sPacket->result = result;
+	WFIFOSET(fd, sizeof(*sPacket));
+#endif
+}
+
+void clif_parse_rodex_request_items(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_request_items(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_ITEM_FROM_MAIL *rPacket = RFIFOP(fd, 0);
+
+	rodex->get_items(sd, rPacket->opentype, rPacket->MailID);
+}
+
+void clif_rodex_request_items(struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result)
+{
+#if PACKETVER >= 20140409
+	struct PACKET_ZC_ACK_ITEM_FROM_MAIL *sPacket;
+	int fd;
+
+	nullpo_retv(sd);
+
+	fd = sd->fd;
+	
+	WFIFOHEAD(fd, sizeof(*sPacket));
+	sPacket = WFIFOP(fd, 0);
+	sPacket->PacketType = rodexgetitem;
+	sPacket->MailID = mail_id;
+	sPacket->opentype = opentype;
+	sPacket->result = result;
+	WFIFOSET(fd, sizeof(*sPacket));
+#endif
+}
+
+void clif_rodex_icon(int fd, bool show)
+{
+#if PACKETVER >= 20140716
+	WFIFOHEAD(fd, 3);
+	WFIFOW(fd, 0) = rodexicon;
+	WFIFOB(fd, 2) = (show == true ? 1 : 0);
+	WFIFOSET(fd, 3);
+#endif
+}
+
+void clif_parse_rodex_refresh_maillist(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_refresh_maillist(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_REFRESH_MAIL_LIST *packet = RFIFOP(fd, 0);
+	rodex->refresh(sd, packet->opentype, packet->Upper_MailID);
+}
+
+void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd)
+{
+	const struct PACKET_CZ_REQ_OPEN_MAIL *packet = RFIFOP(fd, 0);
+	rodex->open(sd, packet->opentype);
+	rodex->clean(sd, 1);
+}
+
+void clif_parse_rodex_close_mailbox(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_close_mailbox(int fd, struct map_session_data *sd)
+{
+	rodex->clean(sd, 0);
+	intif->rodex_checkhasnew(sd);
+}
+
+void clif_parse_rodex_cancel_write_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_rodex_cancel_write_mail(int fd, struct map_session_data *sd)
+{
+	rodex->clean(sd, 1);
+}
+
 /*==========================================
  * Main client packet processing function
  *------------------------------------------*/
@@ -20265,4 +20754,31 @@ void clif_defaults(void) {
 	clif->dressroom_open = clif_dressroom_open;
 	clif->pOneClick_ItemIdentify = clif_parse_OneClick_ItemIdentify;
 	clif->get_bl_name = clif_get_bl_name;
+	/* RODEX */
+	clif->pRodexOpenWriteMail = clif_parse_rodex_open_write_mail;
+	clif->rodex_open_write_mail = clif_rodex_open_write_mail;
+	clif->pRodexAddItem = clif_parse_rodex_add_item;
+	clif->rodex_add_item_result = clif_rodex_add_item_result;
+	clif->pRodexRemoveItem = clif_parse_rodex_remove_item;
+	clif->rodex_remove_item_result = clif_rodex_remove_item_result;
+	clif->pRodexSendMail = clif_parse_rodex_send_mail;
+	clif->rodex_send_mail_result = clif_rodex_send_mail_result;
+	clif->rodex_send_maillist = clif_rodex_send_maillist;
+	clif->rodex_send_refresh = clif_rodex_send_refresh;
+	clif->pRodexReadMail = clif_parse_rodex_read_mail;
+	clif->rodex_read_mail = clif_rodex_read_mail;
+	clif->pRodexNextMaillist = clif_parse_rodex_next_maillist;
+	clif->pRodexCloseMailbox = clif_parse_rodex_close_mailbox;
+	clif->pRodexCancelWriteMail = clif_parse_rodex_cancel_write_mail;
+	clif->pRodexOpenMailbox = clif_parse_rodex_open_mailbox;
+	clif->pRodexCheckName = clif_parse_rodex_checkname;
+	clif->rodex_checkname_result = clif_rodex_checkname_result;
+	clif->pRodexDeleteMail = clif_parse_rodex_delete_mail;
+	clif->rodex_delete_mail = clif_rodex_delete_mail;
+	clif->pRodexRefreshMaillist = clif_parse_rodex_refresh_maillist;
+	clif->pRodexRequestZeny = clif_parse_rodex_request_zeny;
+	clif->rodex_request_zeny = clif_rodex_request_zeny;
+	clif->pRodexRequestItems = clif_parse_rodex_request_items;
+	clif->rodex_request_items = clif_rodex_request_items;
+	clif->rodex_icon = clif_rodex_icon;
 }
diff --git a/src/map/clif.h b/src/map/clif.h
index b34be81a3..ac8cc8d35 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -1365,6 +1365,34 @@ struct clif_interface {
 	void (*pSelectCart) (int fd, struct map_session_data *sd);
 
 	const char *(*get_bl_name) (const struct block_list *bl);
+
+	/* RoDEX */
+	void (*pRodexOpenWriteMail) (int fd, struct map_session_data *sd);
+	void (*rodex_open_write_mail) (int fd, const char *receiver_name, int8 result);
+	void (*pRodexAddItem) (int fd, struct map_session_data *sd);
+	void (*rodex_add_item_result) (struct map_session_data *sd, int16 idx, int16 amount, int8 result);
+	void (*pRodexRemoveItem) (int fd, struct map_session_data *sd);
+	void (*rodex_remove_item_result) (struct map_session_data *sd, int16 idx, int16 amount);
+	void (*pRodexSendMail) (int fd, struct map_session_data *sd);
+	void (*rodex_send_mail_result) (int fd, struct map_session_data *sd, int8 result);
+	void (*rodex_send_maillist) (int fd, struct map_session_data *sd, int8 open_type, int64 page_start);
+	void (*rodex_send_refresh) (int fd, struct map_session_data *sd, int8 open_type, int count);
+	void (*pRodexReadMail) (int fd, struct map_session_data *sd);
+	void (*rodex_read_mail) (struct map_session_data *sd, int8 opentype, struct rodex_message *msg);
+	void (*pRodexNextMaillist) (int fd, struct map_session_data *sd);
+	void (*pRodexCloseMailbox) (int fd, struct map_session_data *sd);
+	void (*pRodexCancelWriteMail) (int fd, struct map_session_data *sd);
+	void (*pRodexOpenMailbox) (int fd, struct map_session_data *sd);
+	void (*pRodexCheckName) (int fd, struct map_session_data *sd);
+	void (*rodex_checkname_result) (struct map_session_data *sd, int char_id, short class_, int base_level, const char *name);
+	void (*pRodexDeleteMail) (int fd, struct map_session_data *sd);
+	void (*rodex_delete_mail) (struct map_session_data *sd, int8 opentype, int64 mail_id);
+	void (*pRodexRefreshMaillist) (int fd, struct map_session_data *sd);
+	void (*pRodexRequestZeny) (int fd, struct map_session_data *sd);
+	void (*rodex_request_zeny) (struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result);
+	void (*pRodexRequestItems) (int fd, struct map_session_data *sd);
+	void (*rodex_request_items) (struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result);
+	void (*rodex_icon) (int fd, bool show);
 };
 
 #ifdef HERCULES_CORE
diff --git a/src/map/date.c b/src/map/date.c
index 20ab9fe95..b06ec18d6 100644
--- a/src/map/date.c
+++ b/src/map/date.c
@@ -84,6 +84,20 @@ int date_get_dayofweek(void)
 	return lt->tm_wday;
 }
 
+// Returns YYYYMMDD of now
+int date_get_date(void)
+{
+	time_t t;
+	struct tm * lt;
+	t = time(NULL);
+	lt = localtime(&t);
+
+	return
+		(lt->tm_year + 1900) * 10000 +
+		(lt->tm_mon + 1) * 100 +
+		(lt->tm_mday);
+}
+
 /*==========================================
  * Star gladiator related checks
  *------------------------------------------*/
diff --git a/src/map/date.h b/src/map/date.h
index ac0a3a7fa..f4f119ade 100644
--- a/src/map/date.h
+++ b/src/map/date.h
@@ -32,6 +32,7 @@ int date_get_hour(void);
 int date_get_min(void);
 int date_get_sec(void);
 int date_get_dayofweek(void);
+int date_get_date(void);
 
 bool is_day_of_sun(void);
 bool is_day_of_moon(void);
diff --git a/src/map/intif.c b/src/map/intif.c
index 61d2e3633..de862b4dd 100644
--- a/src/map/intif.c
+++ b/src/map/intif.c
@@ -38,6 +38,7 @@
 #include "map/pc.h"
 #include "map/pet.h"
 #include "map/quest.h"
+#include "map/rodex.h"
 #include "map/storage.h"
 #include "common/memmgr.h"
 #include "common/nullpo.h"
@@ -2399,6 +2400,218 @@ void intif_parse_Itembound_ack(int fd) {
 		gstor->lock = 0; //Unlock now that operation is completed
 #endif
 }
+
+/*==========================================
+* RoDEX System
+*==========================================*/
+
+/*------------------------------------------
+ * Mail List
+ *------------------------------------------*/
+
+// Rodex Inbox Request
+// char_id: char_id
+// account_id: account_id (used by account mail)
+// flag: 0 - Open/Refresh ; 1 = Next Page
+int intif_rodex_requestinbox(int char_id, int account_id, int8 flag, int8 opentype, int64 mail_id)
+{
+	if (intif->CheckForCharServer())
+		return 0;
+
+	WFIFOHEAD(inter_fd, 20);
+	WFIFOW(inter_fd, 0) = 0x3095;
+	WFIFOL(inter_fd, 2) = char_id;
+	WFIFOL(inter_fd, 6) = account_id;
+	WFIFOL(inter_fd, 10) = flag;
+	WFIFOB(inter_fd, 11) = opentype;
+	WFIFOQ(inter_fd, 12) = mail_id;
+	WFIFOSET(inter_fd, 20);
+
+	return 0;
+}
+
+void intif_parse_RequestRodexOpenInbox(int fd)
+{
+	struct map_session_data *sd;
+	int8 opentype = RFIFOB(fd, 8);
+	int8 flag = RFIFOB(fd, 9);
+	int8 is_end = RFIFOB(fd, 10);
+	int count = RFIFOL(fd, 11);
+	int i, j;
+
+	sd = map->charid2sd(RFIFOL(fd, 4));
+
+	if (sd == NULL) // user is not online anymore
+		return;
+
+	sd->rodex.total = count;
+	if (RFIFOW(fd, 2) - 15 != sd->rodex.total * sizeof(struct rodex_message)) {
+		ShowError("intif_parse_RodexInboxOpenReceived: data size mismatch %d != %"PRIuS"\n", RFIFOW(fd, 2) - 15, sd->rodex.total * sizeof(struct rodex_message));
+		return;
+	}
+
+	if (flag == 0)
+		VECTOR_CLEAR(sd->rodex.messages);
+
+	for (i = 0, j = 15; i < count; ++i, j += sizeof(struct rodex_message)) {
+		struct rodex_message msg = { 0 };
+		VECTOR_ENSURE(sd->rodex.messages, 1, 1);
+		memcpy(&msg, RFIFOP(fd, j), sizeof(struct rodex_message));
+		VECTOR_PUSH(sd->rodex.messages, msg);
+	}
+
+	if (is_end == true) {
+		if (flag == 0)
+			clif->rodex_send_maillist(sd->fd, sd, opentype, VECTOR_LENGTH(sd->rodex.messages) - 1);
+		else
+			clif->rodex_send_refresh(sd->fd, sd, opentype, count);
+	}
+}
+
+/*------------------------------------------
+ * Notifications
+ *------------------------------------------*/
+int intif_rodex_hasnew(struct map_session_data *sd)
+{
+	nullpo_retr(0, sd);
+
+	if (intif->CheckForCharServer())
+		return 0;
+
+	WFIFOHEAD(inter_fd, 10);
+	WFIFOW(inter_fd, 0) = 0x3096;
+	WFIFOL(inter_fd, 2) = sd->status.char_id;
+	WFIFOL(inter_fd, 6) = sd->status.account_id;
+	WFIFOSET(inter_fd, 10);
+
+	return 0;
+}
+
+void intif_parse_RodexNotifications(int fd)
+{
+	struct map_session_data *sd;
+	bool has_messages;
+
+	sd = map->charid2sd(RFIFOL(fd, 2));
+	has_messages = RFIFOB(fd, 6);
+
+	if (sd == NULL) // user is not online anymore
+		return;
+
+	clif->rodex_icon(sd->fd, has_messages);
+}
+
+/*------------------------------------------
+ * Update Mail
+ *------------------------------------------*/
+
+/// Updates a mail
+/// flag:
+///		0 - user Read
+///		1 - user got Zeny 
+///		2 - user got Items
+///		3 - delete
+int intif_rodex_updatemail(int64 mail_id, int8 flag)
+{
+	if (intif->CheckForCharServer())
+		return 0;
+
+	WFIFOHEAD(inter_fd, 11);
+	WFIFOW(inter_fd, 0) = 0x3097;
+	WFIFOQ(inter_fd, 2) = mail_id;
+	WFIFOB(inter_fd, 10) = flag;
+	WFIFOSET(inter_fd, 11);
+
+	return 0;
+}
+
+/*------------------------------------------
+ * Send Mail
+ *------------------------------------------*/
+int intif_rodex_sendmail(struct rodex_message *msg)
+{
+	if (intif->CheckForCharServer())
+		return 0;
+
+	nullpo_retr(0, msg);
+
+	WFIFOHEAD(inter_fd, 4 + sizeof(struct rodex_message));
+	WFIFOW(inter_fd, 0) = 0x3098;
+	WFIFOW(inter_fd, 2) = 4 + sizeof(struct rodex_message);
+	memcpy(WFIFOP(inter_fd, 4), msg, sizeof(struct rodex_message));
+	WFIFOSET(inter_fd, 4 + sizeof(struct rodex_message));
+
+	return 0;
+}
+
+void intif_parse_RodexSendMail(int fd)
+{
+	struct map_session_data *ssd = NULL, *rsd = NULL;
+	int sender_id = RFIFOL(fd, 2);
+	int receiver_id = RFIFOL(fd, 6);
+	int receiver_accountid = RFIFOL(fd, 10);
+
+	if (sender_id > 0)
+		ssd = map->charid2sd(sender_id);
+
+	if (receiver_id > 0)
+		rsd = map->charid2sd(receiver_id);
+	else if (receiver_accountid > 0)
+		rsd = map->id2sd(receiver_accountid);
+
+	rodex->send_mail_result(ssd, rsd, RFIFOL(fd, 14));
+}
+
+/*------------------------------------------
+ * Check Player
+ *------------------------------------------*/
+int intif_rodex_checkname(struct map_session_data *sd, const char *name)
+{
+	if (intif->CheckForCharServer())
+		return 0;
+
+	nullpo_retr(0, sd);
+	nullpo_retr(0, name);
+
+	WFIFOHEAD(inter_fd, 6 + NAME_LENGTH);
+	WFIFOW(inter_fd, 0) = 0x3099;
+	WFIFOL(inter_fd, 2) = sd->status.char_id;
+	safestrncpy(WFIFOP(inter_fd, 6), name, NAME_LENGTH);
+	WFIFOSET(inter_fd, 6 + NAME_LENGTH);
+
+	return 0;
+}
+
+void intif_parse_RodexCheckName(int fd)
+{
+	struct map_session_data *sd = NULL;
+	int reqchar_id = RFIFOL(fd, 2);
+	int target_char_id = RFIFOL(fd, 6);
+	short target_class = RFIFOW(fd, 10);
+	int target_level = RFIFOL(fd, 12);
+	char name[NAME_LENGTH];
+
+	safestrncpy(name, RFIFOP(inter_fd, 16), NAME_LENGTH);
+
+	if (reqchar_id <= 0)
+		return;
+	
+	sd = map->charid2sd(reqchar_id);
+
+	if (sd == NULL) // User is not online anymore
+		return;
+
+	if (target_char_id == 0) {
+		clif->rodex_checkname_result(sd, 0, 0, 0, name);
+		return;
+	}
+
+	sd->rodex.tmp.receiver_id = target_char_id;
+	strncpy(sd->rodex.tmp.receiver_name, name, NAME_LENGTH);
+
+	clif->rodex_checkname_result(sd, target_char_id, target_class, target_level, name);
+}
+
 //-----------------------------------------------------------------
 // Communication from the inter server
 // Return a 0 (false) if there were any errors.
@@ -2509,6 +2722,12 @@ int intif_parse(int fd)
 		case 0x3891: intif->pRecvHomunculusData(fd); break;
 		case 0x3892: intif->pSaveHomunculusOk(fd); break;
 		case 0x3893: intif->pDeleteHomunculusOk(fd); break;
+
+		// RoDEX
+		case 0x3895: intif->pRequestRodexOpenInbox(fd); break;
+		case 0x3896: intif->pRodexHasNew(fd); break;
+		case 0x3897: intif->pRodexSendMail(fd); break;
+		case 0x3898: intif->pRodexCheckName(fd); break;
 	default:
 		ShowError("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0));
 		return 0;
@@ -2534,7 +2753,7 @@ void intif_defaults(void) {
 		-1, 7, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0, //0x3860  Quests [Kevin] [Inkfish]
 		-1, 3, 3, 0,  0, 0, 0, 0,  0, 0, 0, 0, -1, 3,  3, 0, //0x3870  Mercenaries [Zephyrus] / Elemental [pakpil]
 		12,-1, 7, 3,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0, //0x3880
-		-1,-1, 7, 3,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0, //0x3890  Homunculus [albator]
+		-1,-1, 7, 3,  0,-1, 7, 15,16 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, 0, //0x3890  Homunculus [albator] / RoDEX [KirieZ]
 	};
 
 	intif = &intif_s;
@@ -2616,6 +2835,12 @@ void intif_defaults(void) {
 	intif->elemental_request = intif_elemental_request;
 	intif->elemental_delete = intif_elemental_delete;
 	intif->elemental_save = intif_elemental_save;
+	// RoDEX
+	intif->rodex_requestinbox = intif_rodex_requestinbox;
+	intif->rodex_checkhasnew = intif_rodex_hasnew;
+	intif->rodex_updatemail = intif_rodex_updatemail;
+	intif->rodex_sendmail = intif_rodex_sendmail;
+	intif->rodex_checkname = intif_rodex_checkname;
 	/* @accinfo */
 	intif->request_accinfo = intif_request_accinfo;
 	/* */
@@ -2687,4 +2912,9 @@ void intif_defaults(void) {
 	intif->pRecvHomunculusData = intif_parse_RecvHomunculusData;
 	intif->pSaveHomunculusOk = intif_parse_SaveHomunculusOk;
 	intif->pDeleteHomunculusOk = intif_parse_DeleteHomunculusOk;
+	/* RoDEX */
+	intif->pRequestRodexOpenInbox = intif_parse_RequestRodexOpenInbox;
+	intif->pRodexHasNew = intif_parse_RodexNotifications;
+	intif->pRodexSendMail = intif_parse_RodexSendMail;
+	intif->pRodexCheckName = intif_parse_RodexCheckName;
 }
diff --git a/src/map/intif.h b/src/map/intif.h
index 3d6a52440..4bca5f167 100644
--- a/src/map/intif.h
+++ b/src/map/intif.h
@@ -37,6 +37,7 @@ struct s_elemental;
 struct s_homunculus;
 struct s_mercenary;
 struct s_pet;
+struct rodex_message;
 
 /**
  * Defines
@@ -131,6 +132,12 @@ struct intif_interface {
 	int (*elemental_request) (int ele_id, int char_id);
 	int (*elemental_delete) (int ele_id);
 	int (*elemental_save) (struct s_elemental *ele);
+	// RoDEX
+	int(*rodex_requestinbox) (int char_id, int account_id, int8 flag, int8 opentype, int64 mail_id);
+	int(*rodex_checkhasnew) (struct map_session_data *sd);
+	int(*rodex_updatemail) (int64 mail_id, int8 flag);
+	int(*rodex_sendmail) (struct rodex_message *msg);
+	int(*rodex_checkname) (struct map_session_data *sd, const char *name);
 	/* @accinfo */
 	void (*request_accinfo) (int u_fd, int aid, int group_lv, char* query);
 	/* */
@@ -200,6 +207,11 @@ struct intif_interface {
 	void (*pRecvHomunculusData) (int fd);
 	void (*pSaveHomunculusOk) (int fd);
 	void (*pDeleteHomunculusOk) (int fd);
+	/* RoDEX */
+	void(*pRequestRodexOpenInbox) (int fd);
+	void(*pRodexHasNew) (int fd);
+	void(*pRodexSendMail) (int fd);
+	void(*pRodexCheckName) (int fd);
 };
 
 #ifdef HERCULES_CORE
diff --git a/src/map/map.c b/src/map/map.c
index 17156f631..f8aebcfa6 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -55,6 +55,7 @@
 #include "map/skill.h"
 #include "map/status.h"
 #include "map/storage.h"
+#include "map/rodex.h"
 #include "map/trade.h"
 #include "map/unit.h"
 #include "common/HPM.h"
@@ -1915,6 +1916,7 @@ int map_quit(struct map_session_data *sd) {
 	}
 
 	npc->script_event(sd, NPCE_LOGOUT);
+	rodex->clean(sd, 0);
 
 	//Unit_free handles clearing the player related data,
 	//map->quit handles extra specific data which is related to quitting normally
@@ -6012,6 +6014,7 @@ int do_final(void) {
 	elemental->final();
 	map->list_final();
 	vending->final();
+	rodex->final();
 
 	HPM_map_do_final();
 
@@ -6208,6 +6211,7 @@ void map_load_defaults(void) {
 	path_defaults();
 	quest_defaults();
 	npc_chat_defaults();
+	rodex_defaults();
 }
 /**
  * --run-once handler
@@ -6525,6 +6529,7 @@ int do_init(int argc, char *argv[])
 	bg->init(minimal);
 	duel->init(minimal);
 	vending->init(minimal);
+	rodex->init(minimal);
 
 	if (map->scriptcheck) {
 		bool failed = map->extra_scripts_count > 0 ? false : true;
diff --git a/src/map/packets.h b/src/map/packets.h
index 0a774561d..c471e8a6b 100644
--- a/src/map/packets.h
+++ b/src/map/packets.h
@@ -2858,7 +2858,7 @@ packet(0x96e,-1,clif->ackmergeitems);
 // new packets
 	packet(0x09e7,2); // ZC_NOTIFY_UNREAD_RODEX
 	packet(0x09e8,18,clif->pDull/*,XXX*/); // CZ_OPEN_RODEXBOX
-	packet(0x09e9,2,clif->pDull/*,XXX*/); // CZ_CLOSE_RODEXBOX
+	packet(0x09e9,2,clif->pRodexCloseMailbox); // CZ_CLOSE_RODEXBOX
 	packet(0x09ed,-1); // ZC_ACK_SEND_RODEX
 	packet(0x09ee,-1,clif->pDull/*,XXX*/); // CZ_REQ_NEXT_RODEX
 // changed packet sizes
@@ -2902,13 +2902,13 @@ packet(0x96e,-1,clif->ackmergeitems);
 // new packets
 	packet(0x09ea,10,clif->pDull/*,XXX*/); // CZ_REQ_READ_RODEX
 	packet(0x09eb,14); // ZC_ACK_READ_RODEX
-	packet(0x09ef,11,clif->pDull/*,XXX*/); // CZ_REQ_REFRESH_RODEX
+	packet(0x09ef,11,clif->pRodexRefreshMaillist); // CZ_REQ_REFRESH_RODEX
 	packet(0x09f0,-1); // ZC_ACK_RODEX_LIST
-	packet(0x09f5,11,clif->pDull/*,XXX*/); // CZ_REQ_DELETE_RODEX
+	packet(0x09f5,11,clif->pRodexDeleteMail); // CZ_REQ_DELETE_RODEX
 	packet(0x09f6,11); // ZC_ACK_DELETE_RODEX
 // changed packet sizes
 	packet(0x09e8,10,clif->pDull/*,XXX*/); // CZ_OPEN_RODEXBOX
-	packet(0x09ee,11,clif->pDull/*,XXX*/); // CZ_REQ_NEXT_RODEX
+	packet(0x09ee,11,clif->pRodexNextMaillist); // CZ_REQ_NEXT_RODEX
 #endif
 
 // 2013-12-23cRagexe - Yommy
@@ -2948,7 +2948,7 @@ packet(0x96e,-1,clif->ackmergeitems);
 #if PACKETVER >= 20131223
 // new packets
 // changed packet sizes
-	packet(0x09ea,11,clif->pDull/*,XXX*/); // CZ_REQ_READ_RODEX
+	packet(0x09ea,11,clif->pRodexReadMail); // CZ_REQ_READ_RODEX
 	packet(0x09eb,24); // ZC_ACK_READ_RODEX
 #endif
 
@@ -2988,7 +2988,7 @@ packet(0x96e,-1,clif->ackmergeitems);
 // 2013-12-30aRagexe
 #if PACKETVER >= 20131230
 // new packets
-	packet(0x09ec,-1,clif->pDull/*,XXX*/); // CZ_REQ_SEND_RODEX
+	packet(0x09ec,-1,clif->pRodexSendMail); // CZ_REQ_SEND_RODEX
 	packet(0x09ed,3); // ZC_ACK_SEND_RODEX
 	packet(0x09f7,75); // ZC_PROPERTY_HOMUN_2
 // changed packet sizes
@@ -3110,7 +3110,7 @@ packet(0x96e,-1,clif->ackmergeitems);
 // new packets
 	packet(0x0a02,4); // ZC_DRESSROOM_OPEN
 // changed packet sizes
-	packet(0x09e8,11,clif->pDull/*,XXX*/); // CZ_OPEN_RODEXBOX
+	packet(0x09e8,11,clif->pRodexOpenMailbox); // CZ_OPEN_RODEXBOX
 #endif
 
 // 2014-02-19aRagexeRE
@@ -3204,11 +3204,11 @@ packet(0x96e,-1,clif->ackmergeitems);
 // 2014-03-26cRagexeRE
 #if PACKETVER >= 20140326
 // changed packet sizes
-	packet(0x09f1,11,clif->pDull/*,XXX*/); // CZ_REQ_ZENY_FROM_RODEX
+	packet(0x09f1,11,clif->pRodexRequestZeny); // CZ_REQ_ZENY_FROM_RODEX
 	packet(0x09f2,4); // ZC_ACK_ZENY_FROM_RODEX
-	packet(0x09f3,11,clif->pDull/*,XXX*/); // CZ_REQ_ITEM_FROM_RODEX
+	packet(0x09f3,11,clif->pRodexRequestItems); // CZ_REQ_ITEM_FROM_RODEX
 	packet(0x09f4,4); // ZC_ACK_ITEM_FROM_RODEX
-	packet(0x0a03,2,clif->pDull/*,XXX*/); // CZ_REQ_CANCEL_WRITE_RODEX
+	packet(0x0a03,2,clif->pRodexCancelWriteMail); // CZ_REQ_CANCEL_WRITE_RODEX
 	packet(0x0a07,6); // ZC_ACK_REMOVE_RODEX_ITEM
 	packet(0x0a08,7,clif->pDull/*,XXX*/); // CZ_REQ_OPEN_WRITE_RODEX
 #endif
@@ -3298,14 +3298,14 @@ packet(0x96e,-1,clif->ackmergeitems);
 // 2014-04-16aRagexeRE
 #if PACKETVER >= 20140416
 // new packets
-	packet(0x0a04,6,clif->pDull/*,XXX*/); // CZ_REQ_ADD_ITEM_RODEX
+	packet(0x0a04,6,clif->pRodexAddItem); // CZ_REQ_ADD_ITEM_RODEX
 	packet(0x0a12,27); // ZC_ACK_OPEN_WRITE_RODEX
-	packet(0x0a13,2,clif->pDull/*,XXX*/); // CZ_CHECK_RECEIVE_CHARACTER_NAME
+	packet(0x0a13,2,clif->pRodexCheckName); // CZ_CHECK_RECEIVE_CHARACTER_NAME
 // changed packet sizes
 	packet(0x0a05,48); // ZC_ACK_ADD_ITEM_RODEX
-	packet(0x0a06,6,clif->pDull/*,XXX*/); // CZ_REQ_REMOVE_RODEX_ITEM
+	packet(0x0a06,6,clif->pRodexRemoveItem); // CZ_REQ_REMOVE_RODEX_ITEM
 	packet(0x0a07,7); // ZC_ACK_REMOVE_RODEX_ITEM
-	packet(0x0a08,26,clif->pDull/*,XXX*/); // CZ_REQ_OPEN_WRITE_RODEX
+	packet(0x0a08,26,clif->pRodexOpenWriteMail); // CZ_REQ_OPEN_WRITE_RODEX
 #endif
 
 // 2014-04-23aRagexeRE
@@ -3313,7 +3313,7 @@ packet(0x96e,-1,clif->ackmergeitems);
 // new packets
 	packet(0x0a14,6); // ZC_CHECK_RECEIVE_CHARACTER_NAME
 // changed packet sizes
-	packet(0x0a13,26,clif->pDull/*,XXX*/); // CZ_CHECK_RECEIVE_CHARACTER_NAME
+	packet(0x0a13,26,clif->pRodexCheckName); // CZ_CHECK_RECEIVE_CHARACTER_NAME
 #endif
 
 // 2014-04-30aRagexeRE
@@ -4818,7 +4818,7 @@ packet(0x96e,-1,clif->ackmergeitems);
 // new packets
 	packet(0x0a6c,7,clif->pDull/*,XXX*/);
 	packet(0x0a6d,-1);
-	packet(0x0a6e,-1);
+	packet(0x0a6e,-1,clif->pRodexSendMail); // CZ_RODEX_SEND_MAIL
 	packet(0x0a6f,-1);
 // changed packet sizes
 #endif
@@ -4971,7 +4971,7 @@ packet(0x96e,-1,clif->ackmergeitems);
 	packet(0x0a79,-1);
 	packet(0x0a7b,-1);
 	packet(0x0a7c,-1);
-	packet(0x0a7d,-1);
+	packet(0x0a7d,-1); // ZC_RODEX_MAILLIST
 // changed packet sizes
 #endif
 
diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h
index 796ea577c..6e0ddf814 100644
--- a/src/map/packets_struct.h
+++ b/src/map/packets_struct.h
@@ -307,6 +307,27 @@ enum packet_headers {
 #else // PACKETVER < 20141022
 	questListType = 0x2b1, ///< ZC_ALL_QUEST_LIST
 #endif // PACKETVER >= 20141022
+	/* Rodex */
+	rodexicon = 0x09E7,
+	rodexread = 0x09EB,
+	rodexwriteresult = 0x09ED,
+	rodexnextpage = 0x09F0,
+	rodexgetzeny = 0x09F2,
+	rodexgetitem = 0x09F4,
+	rodexdelete = 0x09F6,
+	rodexadditem = 0x0A05,
+	rodexremoveitem = 0x0A07,
+	rodexopenwrite = 0x0A12,
+#if PACKETVER < 20160600
+	rodexmailList = 0x09F0,
+#else // PACKETVER >= 20160600
+	rodexmailList = 0x0A7D,
+#endif
+#if PACKETVER < 20160316
+	rodexcheckplayer = 0x0A14,
+#else // PACKETVER >= 20160316
+	rodexcheckplayer = 0x0A51,
+#endif
 };
 
 #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
@@ -1247,6 +1268,197 @@ struct packet_whisper_message {
 	char message[];
 } __attribute__((packed));
 
+/* RoDEX */
+struct PACKET_CZ_ADD_ITEM_TO_MAIL {
+	int16 PacketType;
+	int16 index;
+	int16 count;
+} __attribute__((packed));
+
+struct PACKET_ZC_ADD_ITEM_TO_MAIL {
+	int16 PacketType;
+	int8 result;
+	int16 index;
+	int16 count;
+	uint16 ITID;
+	int8 type;
+	int8 IsIdentified;
+	int8 IsDamaged;
+	int8 refiningLevel;
+	struct EQUIPSLOTINFO slot;
+	struct ItemOptions optionData[MAX_ITEM_OPTIONS];
+	int16 weight;
+	int8 unknow[5];
+} __attribute__((packed));
+
+struct mail_item {
+	int16 count;
+	uint16 ITID;
+	int8 IsIdentified;
+	int8 IsDamaged;
+	int8 refiningLevel;
+	struct EQUIPSLOTINFO slot;
+	int8 unknow1[4];
+	int8 type;
+	int8 unknown[4];
+	struct ItemOptions optionData[MAX_ITEM_OPTIONS];
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_OPEN_WRITE_MAIL {
+	int16 PacketType;
+	char receiveName[NAME_LENGTH];
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_OPEN_WRITE_MAIL {
+	int16 PacketType;
+	char receiveName[NAME_LENGTH];
+	int8 result;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_REMOVE_ITEM_MAIL {
+	int16 PacketType;
+	int16 index;
+	uint16 cnt;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_REMOVE_ITEM_MAIL {
+	int16 PacketType;
+	int8 result;
+	int16 index;
+	uint16 cnt;
+	int16 weight;
+} __attribute__((packed));
+
+struct PACKET_CZ_SEND_MAIL {
+	int16 PacketType;
+	int16 PacketLength;
+	char receiveName[24];
+	char senderName[24];
+	int64 zeny;
+	int16 Titlelength;
+	int16 TextcontentsLength;
+#if PACKETVER > 20160600
+	int32 receiver_char_id;
+#endif // PACKETVER > 20160600
+	char string[];
+} __attribute__((packed));
+
+struct PACKET_ZC_WRITE_MAIL_RESULT {
+	int16 PacketType;
+	int8 result;
+} __attribute__((packed));
+
+struct PACKET_CZ_CHECKNAME {
+	int16 PacketType;
+	char Name[24];
+} __attribute__((packed));
+
+struct PACKET_ZC_CHECKNAME {
+	int16 PacketType;
+	int32 CharId;
+	int16 Class;
+	int16 BaseLevel;
+#if PACKETVER >= 20160316
+	char Name[24];
+#endif
+} __attribute__((packed));
+
+struct PACKET_ZC_NOTIFY_UNREADMAIL {
+	int16 PacketType;
+	char result;
+} __attribute__((packed));
+
+struct maillistinfo {
+	int64 MailID;
+	int8 Isread;
+	uint8 type;
+	char SenderName[24];
+	int32 regDateTime;
+	int32 expireDateTime;
+	int16 Titlelength;
+	char title[];
+} __attribute__((packed));
+
+struct PACKET_ZC_MAIL_LIST {
+	int16 PacketType;
+	int16 PacketLength;
+	int8 opentype;
+	int8 cnt;
+	int8 IsEnd;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_NEXT_MAIL_LIST {
+	int16 PacketType;
+	int8 opentype;
+	int64 Lower_MailID;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_OPEN_MAIL {
+	int16 PacketType;
+	int8 opentype;
+	int64 Upper_MailID;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_READ_MAIL {
+	int16 PacketType;
+	int8 opentype;
+	int64 MailID;
+} __attribute__((packed));
+
+struct PACKET_ZC_READ_MAIL {
+	int16 PacketType;
+	int16 PacketLength;
+	int8 opentype;
+	int64 MailID;
+	int16 TextcontentsLength;
+	int64 zeny;
+	int8 ItemCnt;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_DELETE_MAIL {
+	int16 PacketType;
+	int8 opentype;
+	int64 MailID;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_DELETE_MAIL {
+	int16 PacketType;
+	int8 opentype;
+	int64 MailID;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_REFRESH_MAIL_LIST {
+	int16 PacketType;
+	int8 opentype;
+	int64 Upper_MailID;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_ZENY_FROM_MAIL {
+	int16 PacketType;
+	int64 MailID;
+	int8 opentype;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_ZENY_FROM_MAIL {
+	int16 PacketType;
+	int64 MailID;
+	int8 opentype;
+	int8 result;
+} __attribute__((packed));
+
+struct PACKET_CZ_REQ_ITEM_FROM_MAIL {
+	int16 PacketType;
+	int64 MailID;
+	int8 opentype;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_ITEM_FROM_MAIL {
+	int16 PacketType;
+	int64 MailID;
+	int8 opentype;
+	int8 result;
+} __attribute__((packed));
+
 #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
 #pragma pack(pop)
 #endif // not NetBSD < 6 / Solaris
diff --git a/src/map/pc.c b/src/map/pc.c
index a925b523c..f332feea8 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -1165,6 +1165,8 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
 	}
 
 	memcpy(&sd->status, st, sizeof(*st));
+	memset(&sd->rodex, 0x0, sizeof(sd->rodex));
+	VECTOR_INIT(sd->rodex.messages);
 
 	if (st->sex != sd->status.sex) {
 		clif->authfail_fd(sd->fd, 0);
@@ -1517,6 +1519,7 @@ int pc_reg_received(struct map_session_data *sd)
 
 	intif->Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox
 	intif->request_questlog(sd);
+	intif->rodex_checkhasnew(sd);
 
 	if (sd->state.connect_new == 0 && sd->fd) { //Character already loaded map! Gotta trigger LoadEndAck manually.
 		sd->state.connect_new = 1;
diff --git a/src/map/pc.h b/src/map/pc.h
index 04fd98b24..c94ec6099 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -28,6 +28,7 @@
 #include "map/log.h" // struct e_log_pick_type
 #include "map/map.h" // RC_MAX, ELE_MAX
 #include "map/pc_groups.h" // GroupSettings
+#include "map/rodex.h"
 #include "map/script.h" // struct reg_db
 #include "map/searchstore.h"  // struct s_search_store_info
 #include "map/status.h" // enum sc_type, OPTION_*
@@ -499,6 +500,14 @@ END_ZEROED_BLOCK;
 		bool changed; // if true, should sync with charserver on next mailbox request
 	} mail;
 
+	// RoDEX
+	struct {
+		struct rodex_message tmp;
+		struct rodex_maillist messages;
+		int total;
+		bool new_mail;
+	} rodex;
+
 	// Quest log system
 	int num_quests;          ///< Number of entries in quest_log
 	int avail_quests;        ///< Number of Q_ACTIVE and Q_INACTIVE entries in quest log (index of the first Q_COMPLETE entry)
diff --git a/src/map/rodex.c b/src/map/rodex.c
new file mode 100644
index 000000000..03191ab22
--- /dev/null
+++ b/src/map/rodex.c
@@ -0,0 +1,621 @@
+/**
+ * This file is part of Hercules.
+ * http://herc.ws - http://github.com/HerculesWS/Hercules
+ *
+ * Copyright (C) 2017 Hercules Dev Team
+ *
+ * Hercules 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 3 of the License, or
+ * (at your option) 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/>.
+ */
+#define HERCULES_CORE
+
+#include "rodex.h"
+
+#include "map/battle.h"
+#include "map/date.h"
+#include "map/intif.h"
+#include "map/itemdb.h"
+#include "map/pc.h"
+
+#include "common/nullpo.h"
+#include "common/sql.h"
+#include "common/memmgr.h"
+
+
+// NOTE : These values are hardcoded into the client
+// Cost of each Attached Item
+#define ATTACHITEM_COST 2500
+// Percent of Attached Zeny that will be paid as Tax
+#define ATTACHZENY_TAX 2
+// Maximun number of messages that can be sent in one day
+#define DAILY_MAX_MAILS 100
+
+struct rodex_interface rodex_s;
+struct rodex_interface *rodex;
+
+/// Checks if RoDEX System is enabled in the server
+/// Returns true if it's enabled, false otherwise
+bool rodex_isenabled(void)
+{
+	if (battle_config.feature_rodex == 1)
+		return true;
+
+	return false;
+}
+
+/// Checks and refreshes the user daily number of Stamps
+/// @param sd : The player who's being checked
+void rodex_refresh_stamps(struct map_session_data *sd)
+{
+	int today = date_get_date();
+	nullpo_retv(sd);
+
+	// Note : Weirdly, iRO starts this with maximum messages of the day and decrements
+	//        but our clients starts this at 0 and increments
+	if (sd->sc.data[SC_DAILYSENDMAILCNT] == NULL) {
+		sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, today, 0, INFINITE_DURATION);
+	} else {
+		int sc_date = sd->sc.data[SC_DAILYSENDMAILCNT]->val1;
+		if (sc_date != today) {
+			sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, today, 0, INFINITE_DURATION);
+		}
+	}
+}
+
+/// Attaches an item to a message being written
+/// @param sd : The player who's writting
+/// @param idx : the inventory idx of the item
+/// @param amount : Amount of the item to be attached
+void rodex_add_item(struct map_session_data *sd, int16 idx, int16 amount)
+{
+	int i;
+	bool is_stack = false;
+
+	nullpo_retv(sd);
+
+	if (idx < 0 || idx >= MAX_INVENTORY) {
+		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
+		return;
+	}
+
+	if (amount < 0 || amount > sd->status.inventory[idx].amount) {
+		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
+		return;
+	}
+
+	if (!pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
+		!itemdb_canmail(&sd->status.inventory[idx], pc_get_group_level(sd)) ||
+		(sd->status.inventory[idx].bound && !pc_can_give_bound_items(sd))) {
+		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_NOT_TRADEABLE);
+		return;
+	}
+
+	if (itemdb->isstackable(sd->status.inventory[idx].nameid) == 1) {
+		for (i = 0; i < RODEX_MAX_ITEM; ++i) {
+			if (sd->rodex.tmp.items[i].idx == idx) {
+				if (sd->status.inventory[idx].nameid == sd->rodex.tmp.items[i].item.nameid &&
+					sd->status.inventory[idx].unique_id == sd->rodex.tmp.items[i].item.unique_id) {
+					is_stack = true;
+					break;
+				}
+			}
+		}
+
+		if (i == RODEX_MAX_ITEM && sd->rodex.tmp.items_count < RODEX_MAX_ITEM) {
+			ARR_FIND(0, RODEX_MAX_ITEM, i, sd->rodex.tmp.items[i].idx == 0);
+		}
+	} else if (sd->rodex.tmp.items_count < RODEX_MAX_ITEM) {
+		ARR_FIND(0, RODEX_MAX_ITEM, i, sd->rodex.tmp.items[i].idx == 0);
+	} else {
+		i = RODEX_MAX_ITEM;
+	}
+
+	if (i == RODEX_MAX_ITEM) {
+		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_NO_SPACE);
+		return;
+	}
+	
+	if (sd->rodex.tmp.items[i].item.amount + amount > sd->status.inventory[idx].amount) {
+		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
+		return;
+	}
+
+	if (sd->rodex.tmp.weight + sd->inventory_data[idx]->weight * amount > RODEX_WEIGHT_LIMIT) {
+		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
+		return;
+	}
+
+	sd->rodex.tmp.items[i].idx = idx;
+	sd->rodex.tmp.weight += sd->inventory_data[idx]->weight * amount;
+	if (is_stack == false) {
+		sd->rodex.tmp.items[i].item = sd->status.inventory[idx];
+		sd->rodex.tmp.items[i].item.amount = amount;
+		sd->rodex.tmp.items_count++;
+	} else {
+		sd->rodex.tmp.items[i].item.amount += amount;
+	}
+	sd->rodex.tmp.type |= MAIL_TYPE_ITEM;
+
+	clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_SUCCESS);
+}
+
+/// Removes an item attached to a message being writen
+/// @param sd : The player who's writting the message
+/// @param idx : The index of the item
+/// @param amount : How much to remove
+void rodex_remove_item(struct map_session_data *sd, int16 idx, int16 amount)
+{
+	int i;
+	struct item *it;
+	struct item_data *itd;
+
+	nullpo_retv(sd);
+	Assert_retv(idx >= 0 && idx < MAX_INVENTORY);
+
+	for (i = 0; i < RODEX_MAX_ITEM; ++i) {
+		if (sd->rodex.tmp.items[i].idx == idx)
+			break;
+	}
+
+	if (i == RODEX_MAX_ITEM) {
+		clif->rodex_remove_item_result(sd, idx, -1);
+		return;
+	}
+
+	it = &sd->rodex.tmp.items[i].item;
+
+	if (amount <= 0 || amount > it->amount) {
+		clif->rodex_remove_item_result(sd, idx, -1);
+		return;
+	}
+
+	itd = itemdb->search(it->nameid);
+
+	if (amount == it->amount) {
+		sd->rodex.tmp.weight -= itd->weight * amount;
+		sd->rodex.tmp.items_count--;
+		if (sd->rodex.tmp.items_count < 1) {
+			sd->rodex.tmp.type &= ~MAIL_TYPE_ITEM;
+		}
+		memset(&sd->rodex.tmp.items[i], 0x0, sizeof(sd->rodex.tmp.items[0]));
+		clif->rodex_remove_item_result(sd, idx, 0);
+		return;
+	}
+
+	it->amount -= amount;
+	sd->rodex.tmp.weight -= itd->weight * amount;
+	
+	clif->rodex_remove_item_result(sd, idx, it->amount);
+}
+
+/// Request if character with given name exists and returns information about him
+/// @param sd : The player who's requesting
+/// @param name : The name of the character to check
+/// @param base_level : Reference to return the character base level, if he exists
+/// @param char_id : Reference to return the character id, if he exists
+/// @param class : Reference to return the character class id, if he exists
+void rodex_check_player(struct map_session_data *sd, const char *name, int *base_level, int *char_id, short *class)
+{
+	intif->rodex_checkname(sd, name);
+}
+
+/// Sends a Mail to an character
+/// @param sd : The player who's sending
+/// @param receiver_name : The name of the character who's receiving the message
+/// @param body : Mail message
+/// @param title : Mail Title
+/// @param zeny : Amount of zeny attached
+/// Returns result code:
+/// 	RODEX_SEND_MAIL_SUCCESS = 0,
+///		RODEX_SEND_MAIL_FATAL_ERROR = 1,
+///		RODEX_SEND_MAIL_COUNT_ERROR = 2,
+///		RODEX_SEND_MAIL_ITEM_ERROR = 3,
+///		RODEX_SEND_MAIL_RECEIVER_ERROR = 4
+int rodex_send_mail(struct map_session_data *sd, const char *receiver_name, const char *body, const char *title, int64 zeny)
+{
+	int i;
+	int64 total_zeny;
+
+	nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, sd);
+	nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, receiver_name);
+	nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, body);
+	nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, title);
+
+	if (zeny < 0) {
+		rodex->clean(sd, 1);
+		return RODEX_SEND_MAIL_FATAL_ERROR;
+	}
+
+	total_zeny = zeny + sd->rodex.tmp.items_count * ATTACHITEM_COST + (2 * zeny)/100;
+	
+	if (strcmp(receiver_name, sd->rodex.tmp.receiver_name) != 0) {
+		rodex->clean(sd, 1);
+		return RODEX_SEND_MAIL_RECEIVER_ERROR;
+	}
+
+	if (total_zeny > sd->status.zeny || total_zeny < 0) {
+		rodex->clean(sd, 1);
+		return RODEX_SEND_MAIL_FATAL_ERROR;
+	}
+
+	rodex_refresh_stamps(sd);
+
+	if (sd->sc.data[SC_DAILYSENDMAILCNT] != NULL) {
+		if (sd->sc.data[SC_DAILYSENDMAILCNT]->val2 >= DAILY_MAX_MAILS) {
+			rodex->clean(sd, 1);
+			return RODEX_SEND_MAIL_COUNT_ERROR;
+		}
+
+		sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, sd->sc.data[SC_DAILYSENDMAILCNT]->val1, sd->sc.data[SC_DAILYSENDMAILCNT]->val2 + 1, INFINITE_DURATION);
+	} else {
+		sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, date_get_date(), 1, INFINITE_DURATION);
+	}
+	
+	for (i = 0; i < RODEX_MAX_ITEM; i++) {
+		int16 idx = sd->rodex.tmp.items[i].idx;
+
+		if (sd->rodex.tmp.items[i].item.nameid == 0)
+			continue;
+
+		if (sd->rodex.tmp.items[i].item.nameid != sd->status.inventory[idx].nameid
+			|| sd->rodex.tmp.items[i].item.unique_id != sd->status.inventory[idx].unique_id
+			|| sd->rodex.tmp.items[i].item.amount > sd->status.inventory[idx].amount
+			|| sd->rodex.tmp.items[i].item.amount < 1) {
+			rodex->clean(sd, 1);
+			return RODEX_SEND_MAIL_ITEM_ERROR;
+		}
+	}
+
+	if (total_zeny > 0 && pc->payzeny(sd, (int)total_zeny, LOG_TYPE_MAIL, NULL) != 0) {
+		rodex->clean(sd, 1);
+		return RODEX_SEND_MAIL_FATAL_ERROR;
+	}
+
+	for (i = 0; i < RODEX_MAX_ITEM; i++) {
+		int16 idx = sd->rodex.tmp.items[i].idx;
+
+		if (sd->rodex.tmp.items[i].item.nameid == 0) {
+			continue;
+		}
+
+		if (pc->delitem(sd, idx, sd->rodex.tmp.items[i].item.amount, 0, DELITEM_NORMAL, LOG_TYPE_MAIL) != 0) {
+			rodex->clean(sd, 1);
+			return RODEX_SEND_MAIL_ITEM_ERROR;
+		}
+	}
+
+	sd->rodex.tmp.zeny = zeny;
+	sd->rodex.tmp.is_read = false;
+	sd->rodex.tmp.is_deleted = false;
+	sd->rodex.tmp.send_date = (int)time(NULL);
+	sd->rodex.tmp.expire_date = (int)time(NULL) + RODEX_EXPIRE;
+	if (strlen(sd->rodex.tmp.body) > 0)
+		sd->rodex.tmp.type |= MAIL_TYPE_TEXT;
+	if (sd->rodex.tmp.zeny > 0)
+		sd->rodex.tmp.type |= MAIL_TYPE_ZENY;
+	sd->rodex.tmp.sender_id = sd->status.char_id;
+	strncpy(sd->rodex.tmp.sender_name, sd->status.name, NAME_LENGTH);
+	strncpy(sd->rodex.tmp.title, title, RODEX_TITLE_LENGTH);
+	strncpy(sd->rodex.tmp.body, body, RODEX_BODY_LENGTH);
+
+	intif->rodex_sendmail(&sd->rodex.tmp);
+	return RODEX_SEND_MAIL_SUCCESS; // this will not inform client of the success yet. (see rodex_send_mail_result)
+}
+
+/// The result of a message send, called by char-server
+/// @param ssd : Sender's sd
+/// @param rsd : Receiver's sd
+/// @param result : Message sent (true) or failed (false)
+void rodex_send_mail_result(struct map_session_data *ssd, struct map_session_data *rsd, bool result)
+{
+	if (ssd != NULL) {
+		rodex->clean(ssd, 1);
+		if (result == false) {
+			clif->rodex_send_mail_result(ssd->fd, ssd, RODEX_SEND_MAIL_FATAL_ERROR);
+			return;
+		}
+
+		clif->rodex_send_mail_result(ssd->fd, ssd, RODEX_SEND_MAIL_SUCCESS);
+	}
+
+	if (rsd != NULL) {
+		clif->rodex_icon(rsd->fd, true);
+		clif_disp_onlyself(rsd, "You've got a new mail!");
+	}
+	return;
+}
+
+/// Retrieves one message from character
+/// @param sd : Character
+/// @param mail_id : Mail ID that's being retrieved
+/// Returns the message
+struct rodex_message *rodex_get_mail(struct map_session_data *sd, int64 mail_id)
+{
+	int i;
+	struct rodex_message *msg;
+
+	nullpo_retr(NULL, sd);
+
+	ARR_FIND(0, VECTOR_LENGTH(sd->rodex.messages), i, VECTOR_INDEX(sd->rodex.messages, i).id == mail_id && VECTOR_INDEX(sd->rodex.messages, i).is_deleted != true);
+	if (i == VECTOR_LENGTH(sd->rodex.messages))
+		return NULL;
+
+	msg = &VECTOR_INDEX(sd->rodex.messages, i);
+	
+	return msg;
+}
+
+/// Request to read a mail by its ID
+/// @param sd : Who's reading
+/// @param mail_id : Mail ID to be read
+void rodex_read_mail(struct map_session_data *sd, int64 mail_id)
+{
+	struct rodex_message *msg;
+
+	nullpo_retv(sd);
+
+	msg = rodex->get_mail(sd, mail_id);
+	nullpo_retv(msg);
+
+	if (msg->is_read == false) {
+		intif->rodex_updatemail(msg->id, 0);
+		msg->is_read = true;
+	}
+
+	clif->rodex_read_mail(sd, msg->opentype, msg);
+}
+
+/// Deletes a mail
+/// @param sd : Who's deleting
+/// @param mail_id : Mail ID to be deleted
+void rodex_delete_mail(struct map_session_data *sd, int64 mail_id)
+{
+	struct rodex_message *msg;
+
+	nullpo_retv(sd);
+
+	msg = rodex->get_mail(sd, mail_id);
+	nullpo_retv(msg);
+
+	msg->is_deleted = true;
+	intif->rodex_updatemail(msg->id, 3);
+
+	clif->rodex_delete_mail(sd, msg->opentype, msg->id);
+}
+
+/// Gets attached zeny
+/// @param sd : Who's getting
+/// @param mail_id : Mail ID that we're getting zeny from
+void rodex_get_zeny(struct map_session_data *sd, int8 opentype, int64 mail_id)
+{
+	struct rodex_message *msg;
+
+	nullpo_retv(sd);
+
+	msg = rodex->get_mail(sd, mail_id);
+
+	if (msg == NULL) {
+		clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR);
+		return;
+	}
+
+	if ((int64)sd->status.zeny + msg->zeny > MAX_ZENY) {
+		clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_LIMIT_ERROR);
+		return;
+	}
+
+	if (pc->getzeny(sd, (int)msg->zeny, LOG_TYPE_MAIL, NULL) != 0) {
+		clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR);
+		return;
+	}
+
+	msg->zeny = 0;
+	intif->rodex_updatemail(mail_id, 1);
+
+	clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_SUCCESS);
+}
+
+/// Gets attached item
+/// @param sd : Who's getting
+/// @param mail_id : Mail ID that we're getting items from
+void rodex_get_items(struct map_session_data *sd, int8 opentype, int64 mail_id)
+{
+	struct rodex_message *msg;
+	int weight = 0;
+	int empty_slots = 0, required_slots;
+	int i;
+
+	nullpo_retv(sd);
+
+	msg = rodex->get_mail(sd, mail_id);
+
+	if (msg == NULL) {
+		clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FATAL_ERROR);
+		return;
+	}
+
+	if (msg->items_count < 1) {
+		clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FATAL_ERROR);
+		return;
+	}
+
+	for (i = 0; i < RODEX_MAX_ITEM; ++i) {
+		if (msg->items[i].item.nameid != 0) {
+			weight += itemdb->search(msg->items[i].item.nameid)->weight * msg->items[i].item.amount;
+		}
+	}
+
+	if ((sd->weight + weight > sd->max_weight)) {
+		clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR);
+		return;
+	}
+
+	required_slots = msg->items_count;
+	for (i = 0; i < MAX_INVENTORY; ++i) {
+		if (sd->status.inventory[i].nameid == 0) {
+			empty_slots++;
+		} else if (itemdb->isstackable(sd->status.inventory[i].nameid) == 1) {
+			int j;
+			ARR_FIND(0, msg->items_count, j, sd->status.inventory[i].nameid == msg->items[j].item.nameid);
+			if (j < msg->items_count) {
+				struct item_data *idata = itemdb->search(sd->status.inventory[i].nameid);
+
+				if ((idata->stack.inventory && sd->status.inventory[i].amount + msg->items[i].item.amount > idata->stack.amount) ||
+					sd->status.inventory[i].amount + msg->items[i].item.amount > MAX_AMOUNT) {
+					clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR);
+					return;
+				}
+
+				required_slots--;
+			}
+		}
+	}
+
+	if (empty_slots < required_slots) {
+		clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR);
+		return;
+	}
+
+	for (i = 0; i < RODEX_MAX_ITEM; ++i) {
+		struct item *it = &msg->items[i].item;
+
+		if (it->nameid == 0) {
+			continue;
+		}
+
+		if (pc->additem(sd, it, it->amount, LOG_TYPE_MAIL) != 0) {
+			clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR);
+			intif->rodex_updatemail(mail_id, 2);
+			return;
+		} else {
+			memset(it, 0x0, sizeof(*it));
+		}
+	}
+
+	intif->rodex_updatemail(mail_id, 2);
+
+	clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEMS_SUCCESS);
+}
+
+/// Cleans user's RoDEX related data
+/// - should be called everytime we're going to stop using rodex in this character
+/// @param sd : Target to clean
+/// @param flag :
+///		0 - clear everything
+///		1 - clear tmp only
+void rodex_clean(struct map_session_data *sd, int8 flag)
+{
+	nullpo_retv(sd);
+
+	if (flag == 0)
+		VECTOR_CLEAR(sd->rodex.messages);
+
+	memset(&sd->rodex.tmp, 0x0, sizeof(sd->rodex.tmp));
+}
+
+/// User request to open rodex, load mails from char-server
+/// @param sd : Who's requesting
+/// @param open_type : Box Type (see RODEX_OPENTYPE)
+void rodex_open(struct map_session_data *sd, int8 open_type)
+{
+	nullpo_retv(sd);
+	if (open_type == RODEX_OPENTYPE_ACCOUNT && battle_config.feature_rodex_use_accountmail == false)
+		open_type = RODEX_OPENTYPE_MAIL;
+
+	intif->rodex_requestinbox(sd->status.char_id, sd->status.account_id, 0, open_type, 0);
+}
+
+/// User request to read next page of mails
+/// @param sd : Who's requesting
+/// @param open_type : Box Type (see RODEX_OPENTYPE)
+/// @param last_mail_id : The last mail from the current page
+void rodex_next_page(struct map_session_data *sd, int8 open_type, int64 last_mail_id)
+{
+	int64 msg_count, page_start = 0;
+	nullpo_retv(sd);
+	
+	if (open_type == RODEX_OPENTYPE_ACCOUNT && battle_config.feature_rodex_use_accountmail == false) {
+		// Should not happen
+		open_type = RODEX_OPENTYPE_MAIL;
+		rodex->open(sd, open_type);
+		return;
+	}
+
+	msg_count = VECTOR_LENGTH(sd->rodex.messages);
+
+	if (last_mail_id > 0) {
+		// Find where the page starts
+		ARR_FIND(0, msg_count, page_start, VECTOR_INDEX(sd->rodex.messages, page_start).id == last_mail_id);
+		if (page_start > 0 && page_start < msg_count) {
+			--page_start; // Valid page, get first item of next page
+		} else {
+			page_start = msg_count - 1; // Should not happen, invalid lower_id given
+		}
+		clif->rodex_send_maillist(sd->fd, sd, open_type, page_start);
+	}
+}
+
+/// User's request to refresh his mail box
+/// @param sd : Who's requesting
+/// @param open_type : Box Type (See RODEX_OPENTYPE)
+/// @param first_mail_id : The first mail id known by client, currently unused
+void rodex_refresh(struct map_session_data *sd, int8 open_type, int64 first_mail_id)
+{
+	nullpo_retv(sd);
+	if (open_type == RODEX_OPENTYPE_ACCOUNT && battle_config.feature_rodex_use_accountmail == false)
+		open_type = RODEX_OPENTYPE_MAIL;
+
+	// Some clients sends the first mail id it currently has and expects to receive
+	// a list of newer mails, other clients sends first mail id as 0 and expects
+	// to receive the first page (as if opening the box)
+	if (first_mail_id > 0) {
+		intif->rodex_requestinbox(sd->status.char_id, sd->status.account_id, 1, open_type, first_mail_id);
+	} else {
+		intif->rodex_requestinbox(sd->status.char_id, sd->status.account_id, 0, open_type, first_mail_id);
+	}
+}
+
+void do_init_rodex(bool minimal)
+{
+	if (minimal)
+		return;
+}
+
+void do_final_rodex(void)
+{
+
+}
+
+void rodex_defaults(void)
+{
+	rodex = &rodex_s;
+
+	rodex->init = do_init_rodex;
+	rodex->final = do_final_rodex;
+
+	rodex->open = rodex_open;
+	rodex->next_page = rodex_next_page;
+	rodex->refresh = rodex_refresh;
+	rodex->isenabled = rodex_isenabled;
+	rodex->add_item = rodex_add_item;
+	rodex->remove_item = rodex_remove_item;
+	rodex->check_player = rodex_check_player;
+	rodex->send_mail = rodex_send_mail;
+	rodex->send_mail_result = rodex_send_mail_result;
+	rodex->get_mail = rodex_get_mail;
+	rodex->read_mail = rodex_read_mail;
+	rodex->delete_mail = rodex_delete_mail;
+	rodex->get_zeny = rodex_get_zeny;
+	rodex->get_items = rodex_get_items;
+	rodex->clean = rodex_clean;
+}
diff --git a/src/map/rodex.h b/src/map/rodex.h
new file mode 100644
index 000000000..18ecd91d6
--- /dev/null
+++ b/src/map/rodex.h
@@ -0,0 +1,85 @@
+/**
+ * This file is part of Hercules.
+ * http://herc.ws - http://github.com/HerculesWS/Hercules
+ *
+ * Copyright (C) 2017 Hercules Dev Team
+ *
+ * Hercules 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 3 of the License, or
+ * (at your option) 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 MAP_RODEX_H
+#define MAP_RODEX_H
+
+#include "common/mmo.h"
+#include "common/hercules.h"
+
+#define RODEX_WEIGHT_LIMIT (2000 * 10)
+
+struct rodex_message;
+
+enum rodex_add_item {
+	RODEX_ADD_ITEM_SUCCESS = 0,
+	RODEX_ADD_ITEM_WEIGHT_ERROR = 1,
+	RODEX_ADD_ITEM_FATAL_ERROR = 2,
+	RODEX_ADD_ITEM_NO_SPACE = 3,
+	RODEX_ADD_ITEM_NOT_TRADEABLE = 4,
+};
+
+enum rodex_send_mail {
+	RODEX_SEND_MAIL_SUCCESS = 0,
+	RODEX_SEND_MAIL_FATAL_ERROR = 1,
+	RODEX_SEND_MAIL_COUNT_ERROR = 2,
+	RODEX_SEND_MAIL_ITEM_ERROR = 3,
+	RODEX_SEND_MAIL_RECEIVER_ERROR = 4
+};
+
+enum rodex_get_zeny {
+	RODEX_GET_ZENY_SUCCESS = 0,
+	RODEX_GET_ZENY_FATAL_ERROR = 1,
+	RODEX_GET_ZENY_LIMIT_ERROR = 2
+};
+
+enum rodex_get_items {
+	RODEX_GET_ITEMS_SUCCESS = 0,
+	RODEX_GET_ITEM_FATAL_ERROR = 1,
+	RODEX_GET_ITEM_FULL_ERROR = 2,
+};
+
+struct rodex_interface {
+	void (*init) (bool minimal);
+	void (*final) (void);
+
+	bool (*isenabled) (void);
+	void (*open) (struct map_session_data *sd, int8 open_type);
+	void (*next_page) (struct map_session_data *sd, int8 open_type, int64 last_mail_id);
+	void (*refresh) (struct map_session_data *sd, int8 open_type, int64 first_mail_id);
+	void (*add_item) (struct map_session_data *sd, int16 idx, int16 amount);
+	void (*remove_item) (struct map_session_data *sd, int16 idx, int16 amount);
+	void (*check_player) (struct map_session_data *sd, const char *name, int *base_level, int *char_id, short *class);
+	int (*send_mail) (struct map_session_data *sd, const char *receiver_name, const char *body, const char *title, int64 zeny);
+	void (*send_mail_result) (struct map_session_data *ssd, struct map_session_data *rsd, bool result);
+	struct rodex_message *(*get_mail) (struct map_session_data *sd, int64 mail_id);
+	void (*read_mail) (struct map_session_data *sd, int64 mail_id);
+	void (*get_zeny) (struct map_session_data *sd, int8 opentype, int64 mail_id);
+	void (*get_items) (struct map_session_data *sd, int8 opentype, int64 mail_id);
+	void (*delete_mail) (struct map_session_data *sd, int64 mail_id);
+	void (*clean) (struct map_session_data *sd, int8 flag);
+};
+
+#ifdef HERCULES_CORE
+void rodex_defaults(void);
+#endif
+
+HPShared struct rodex_interface *rodex;
+
+#endif
diff --git a/src/map/script.c b/src/map/script.c
index 31af59891..8722fcdbe 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -23372,6 +23372,181 @@ BUILDIN(navigateto)
 #endif
 }
 
+bool rodex_sendmail_sub(struct script_state* st, struct rodex_message *msg)
+{
+	const char *sender_name, *title, *body;
+
+	if (!strcmp(script->getfuncname(st), "rodex_sendmail_acc2"))
+		msg->receiver_accountid = script_getnum(st, 2);
+	else
+		msg->receiver_id = script_getnum(st, 2);
+
+	sender_name = script_getstr(st, 3);
+	if (strlen(sender_name) >= NAME_LENGTH) {
+		ShowError("script:rodex_sendmail: Sender name must not be bigger than %d!\n", NAME_LENGTH - 1);
+		return false;
+	}
+	safestrncpy(msg->sender_name, sender_name, NAME_LENGTH);
+
+	title = script_getstr(st, 4);
+	if (strlen(title) >= RODEX_TITLE_LENGTH) {
+		ShowError("script:rodex_sendmail: Mail Title must not be bigger than %d!\n", RODEX_TITLE_LENGTH - 1);
+		return false;
+	}
+	safestrncpy(msg->title, title, RODEX_TITLE_LENGTH);
+
+	body = script_getstr(st, 5);
+	if (strlen(body) >= MAIL_BODY_LENGTH) {
+		ShowError("script:rodex_sendmail: Mail Message must not be bigger than %d!\n", RODEX_BODY_LENGTH - 1);
+		return false;
+	}
+	safestrncpy(msg->body, body, MAIL_BODY_LENGTH);
+
+	if (script_hasdata(st, 6)) {
+		msg->zeny = script_getnum(st, 6);
+		if (msg->zeny < 0 || msg->zeny > MAX_ZENY) {
+			ShowError("script:rodex_sendmail: Invalid Zeny value %"PRId64"!\n", msg->zeny);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+BUILDIN(rodex_sendmail)
+{
+	struct rodex_message msg = { 0 };
+	int item_count = 0, i = 0, param = 7;
+
+	// Common parameters - sender/message/zeny
+	if (rodex_sendmail_sub(st, &msg) == false)
+		return false;
+	
+	// Item list
+	while (i < RODEX_MAX_ITEM && script_hasdata(st, param)) {
+		struct item_data *idata;
+
+		if (!script_hasdata(st, param + 1)) {
+			ShowError("script:rodex_sendmail: Missing Item %d amount!\n", (i + 1));
+			return false;
+		}
+
+		++item_count;
+		if (data_isstring(script_getdata(st, param)) == false) {
+			int itemid = script_getnum(st, param);
+
+			if (itemdb->exists(itemid) == false) {
+				ShowError("script:rodex_sendmail: Unknown item ID %d.\n", itemid);
+				return false;
+			}
+
+			idata = itemdb->search(itemid);
+		}
+		else {
+			ShowError("script:rodex_sendmail: Item %d must be passed as Number.\n", (i + 1));
+			return false;
+		}
+
+		msg.items[i].item.nameid = idata->nameid;
+		msg.items[i].item.amount = script_getnum(st, (param + 1));
+		msg.items[i].item.identify = 1;
+		
+		++i;
+		param += 2;
+	}
+	msg.items_count = item_count;
+
+	msg.type = MAIL_TYPE_NPC;
+	if (msg.zeny > 0)
+		msg.type |= MAIL_TYPE_ZENY;
+	if (msg.items_count > 0)
+		msg.type |= MAIL_TYPE_ITEM;
+	msg.send_date = (int)time(NULL);
+	msg.expire_date = (int)time(NULL) + RODEX_EXPIRE;
+
+	intif->rodex_sendmail(&msg);
+
+	return true;
+}
+
+BUILDIN(rodex_sendmail2)
+{
+	struct rodex_message msg = { 0 };
+	int item_count = 0, i = 0, param = 7;
+
+	// Common parameters - sender/message/zeny
+	if (rodex_sendmail_sub(st, &msg) == false)
+		return false;
+
+	// Item list
+	while (i < RODEX_MAX_ITEM && script_hasdata(st, param)) {
+		struct item_data *idata;
+		int j;
+
+		// Tests
+		if (!script_hasdata(st, param + 1)) {
+			ShowError("script:rodex_sendmail: Missing Item %d amount!\n", (i + 1));
+			return false;
+		}
+		if (!script_hasdata(st, param + 2)) {
+			ShowError("script:rodex_sendmail: Missing Item %d refine!\n", (i + 1));
+			return false;
+		}
+		if (!script_hasdata(st, param + 3)) {
+			ShowError("script:rodex_sendmail: Missing Item %d attribute!\n", (i + 1));
+			return false;
+		}
+		for (j = 0; j < MAX_SLOTS; ++j) {
+			if (!script_hasdata(st, param + 4 + j)) {
+				ShowError("script:rodex_sendmail: Missing Item %d card %d!\n", (i + 1), j);
+				return false;
+			}
+		}
+
+		// Set data to message
+		++item_count;
+		if (data_isstring(script_getdata(st, param)) == false) {
+			int itemid = script_getnum(st, param);
+
+			if (itemdb->exists(itemid) == false) {
+				ShowError("script:rodex_sendmail: Unknown item ID %d.\n", itemid);
+				return false;
+			}
+
+			idata = itemdb->search(itemid);
+		} else {
+			ShowError("script:rodex_sendmail: Item %d must be passed as Number.\n", (i + 1));
+			return false;
+		}
+
+		msg.items[i].item.nameid = idata->nameid;
+		msg.items[i].item.amount = script_getnum(st, (param + 1));
+		msg.items[i].item.refine = script_getnum(st, (param + 2));
+		msg.items[i].item.attribute = script_getnum(st, (param + 3));
+		msg.items[i].item.identify = 1;
+
+		for (j = 0; j < MAX_SLOTS; ++j) {
+			msg.items[i].item.card[j] = script_getnum(st, param + 4 + j);
+		}
+		
+		++i;
+		param += 4 + MAX_SLOTS;
+	}
+	msg.items_count = item_count;
+
+	msg.type = MAIL_TYPE_NPC;
+	if (msg.zeny > 0)
+		msg.type |= MAIL_TYPE_ZENY;
+	if (msg.items_count > 0)
+		msg.type |= MAIL_TYPE_ITEM;
+	msg.send_date = (int)time(NULL);
+	msg.expire_date = (int)time(NULL) + RODEX_EXPIRE;
+
+	intif->rodex_sendmail(&msg);
+
+	return true;
+}
+
 /**
  * Adds a built-in script function.
  *
@@ -24071,6 +24246,12 @@ void script_parse_builtin(void) {
 		BUILDIN_DEF(showscript, "s?"),
 		BUILDIN_DEF(mergeitem,""),
 		BUILDIN_DEF(getcalendartime, "ii??"),
+
+		// -- RoDEX
+		BUILDIN_DEF(rodex_sendmail, "isss???????????"),
+		BUILDIN_DEF2(rodex_sendmail, "rodex_sendmail_acc", "isss???????????"),
+		BUILDIN_DEF(rodex_sendmail2, "isss?????????????????????????????????????????"),
+		BUILDIN_DEF2(rodex_sendmail2, "rodex_sendmail_acc2", "isss?????????????????????????????????????????"),
 		BUILDIN_DEF(_,"s"),
 		BUILDIN_DEF2(_, "_$", "s"),
 	};
diff --git a/src/map/status.c b/src/map/status.c
index 166b33e6f..c98857736 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -1019,6 +1019,9 @@ void initChangeTables(void)
 	// Summoner
 	status->dbs->IconChangeTable[SC_SPRITEMABLE] = SI_SPRITEMABLE;
 
+	// RoDEX
+	status->dbs->IconChangeTable[SC_DAILYSENDMAILCNT] = SI_DAILYSENDMAILCNT;
+
 	// Other SC which are not necessarily associated to skills.
 	status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION1] |= SCB_ASPD;
 	status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION2] |= SCB_ASPD;
@@ -8246,6 +8249,7 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
 			case SC_TURNKICK_READY:
 			case SC_DODGE_READY:
 			case SC_PUSH_CART:
+			case SC_DAILYSENDMAILCNT:
 				tick = INFINITE_DURATION;
 				break;
 
@@ -9865,6 +9869,9 @@ int status_get_val_flag(enum sc_type type)
 		case SC_OVERLAPEXPUP:
 			val_flag |= 1;
 			break;
+		case SC_DAILYSENDMAILCNT:
+			val_flag |= 1 | 2;
+			break;
 	}
 	return val_flag;
 }
diff --git a/src/map/status.h b/src/map/status.h
index 3c0a87175..0482c7cbd 100644
--- a/src/map/status.h
+++ b/src/map/status.h
@@ -842,6 +842,9 @@ typedef enum sc_type {
 	SC_FRESHSHRIMP,
 	
 	SC_DRESS_UP,
+
+	// Rodex
+	SC_DAILYSENDMAILCNT = 824,
 #ifndef SC_MAX
 	SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
 #endif
@@ -1691,7 +1694,7 @@ enum si_type {
 	//SI_SHOW_NPCHPBAR                       = 821,
 	SI_FLOWERSMOKE                           = 822,
 	SI_FSTONE                                = 823,
-	//SI_DAILYSENDMAILCNT                    = 824,
+	SI_DAILYSENDMAILCNT                      = 824,
 	//SI_QSCARABA                            = 825,
 	SI_LJOSALFAR                             = 826,
 	//SI_PAD_READER_KNIGHT                   = 827,
diff --git a/vcproj-11/char-server.vcxproj b/vcproj-11/char-server.vcxproj
index 7107d2e54..17c1ab847 100644
--- a/vcproj-11/char-server.vcxproj
+++ b/vcproj-11/char-server.vcxproj
@@ -140,6 +140,7 @@
     <ClInclude Include="..\3rdparty\libconfig\strbuf.h" />
     <ClInclude Include="..\3rdparty\libconfig\wincompat.h" />
     <ClInclude Include="..\3rdparty\mt19937ar\mt19937ar.h" />
+    <ClInclude Include="..\src\char\int_rodex.h" />
     <ClInclude Include="..\src\common\atomic.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\conf.h" />
@@ -201,6 +202,7 @@
     <ClCompile Include="..\3rdparty\libconfig\scanner.c" />
     <ClCompile Include="..\3rdparty\libconfig\strbuf.c" />
     <ClCompile Include="..\3rdparty\mt19937ar\mt19937ar.c" />
+    <ClCompile Include="..\src\char\int_rodex.c" />
     <ClCompile Include="..\src\common\conf.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\console.c" />
diff --git a/vcproj-11/char-server.vcxproj.filters b/vcproj-11/char-server.vcxproj.filters
index 2299a2402..ba5914a89 100644
--- a/vcproj-11/char-server.vcxproj.filters
+++ b/vcproj-11/char-server.vcxproj.filters
@@ -136,6 +136,9 @@
     <ClCompile Include="..\src\common\des.c">
       <Filter>common</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\char\int_rodex.c">
+      <Filter>char</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\common\cbasetypes.h">
@@ -321,6 +324,9 @@
     <ClInclude Include="..\src\config\classes\general.h">
       <Filter>config</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\char\int_rodex.h">
+      <Filter>char</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="common">
diff --git a/vcproj-11/map-server.vcxproj b/vcproj-11/map-server.vcxproj
index 98d5ddbf3..c09d5293a 100644
--- a/vcproj-11/map-server.vcxproj
+++ b/vcproj-11/map-server.vcxproj
@@ -207,6 +207,7 @@
     <ClInclude Include="..\src\config\renewal.h" />
     <ClInclude Include="..\src\config\secure.h" />
     <ClInclude Include="..\src\config\classes\general.h" />
+    <ClInclude Include="..\src\map\rodex.h" />
     <ClInclude Include="..\src\map\script.h" />
     <ClInclude Include="..\src\map\searchstore.h" />
     <ClInclude Include="..\src\map\skill.h" />
@@ -277,6 +278,7 @@
     <ClCompile Include="..\src\map\pc_groups.c" />
     <ClCompile Include="..\src\map\pet.c" />
     <ClCompile Include="..\src\map\quest.c" />
+    <ClCompile Include="..\src\map\rodex.c" />
     <ClCompile Include="..\src\map\script.c" />
     <ClCompile Include="..\src\map\searchstore.c" />
     <ClCompile Include="..\src\map\skill.c" />
diff --git a/vcproj-11/map-server.vcxproj.filters b/vcproj-11/map-server.vcxproj.filters
index 13192d108..e6b89b3e6 100644
--- a/vcproj-11/map-server.vcxproj.filters
+++ b/vcproj-11/map-server.vcxproj.filters
@@ -205,6 +205,9 @@
     <ClCompile Include="..\src\map\channel.c">
       <Filter>map</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\map\rodex.c">
+      <Filter>map</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\map\atcommand.h">
@@ -462,6 +465,9 @@
     <ClInclude Include="..\src\config\secure.h">
       <Filter>config</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\map\rodex.h">
+      <Filter>map</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="common">
diff --git a/vcproj-12/char-server.vcxproj b/vcproj-12/char-server.vcxproj
index 0c72ef671..02457b05f 100644
--- a/vcproj-12/char-server.vcxproj
+++ b/vcproj-12/char-server.vcxproj
@@ -139,6 +139,7 @@
     <ClInclude Include="..\3rdparty\libconfig\strbuf.h" />
     <ClInclude Include="..\3rdparty\libconfig\wincompat.h" />
     <ClInclude Include="..\3rdparty\mt19937ar\mt19937ar.h" />
+    <ClInclude Include="..\src\char\int_rodex.h" />
     <ClInclude Include="..\src\common\atomic.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\conf.h" />
@@ -200,6 +201,7 @@
     <ClCompile Include="..\3rdparty\libconfig\scanner.c" />
     <ClCompile Include="..\3rdparty\libconfig\strbuf.c" />
     <ClCompile Include="..\3rdparty\mt19937ar\mt19937ar.c" />
+    <ClCompile Include="..\src\char\int_rodex.c" />
     <ClCompile Include="..\src\common\conf.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\console.c" />
diff --git a/vcproj-12/char-server.vcxproj.filters b/vcproj-12/char-server.vcxproj.filters
index 2299a2402..ba5914a89 100644
--- a/vcproj-12/char-server.vcxproj.filters
+++ b/vcproj-12/char-server.vcxproj.filters
@@ -136,6 +136,9 @@
     <ClCompile Include="..\src\common\des.c">
       <Filter>common</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\char\int_rodex.c">
+      <Filter>char</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\common\cbasetypes.h">
@@ -321,6 +324,9 @@
     <ClInclude Include="..\src\config\classes\general.h">
       <Filter>config</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\char\int_rodex.h">
+      <Filter>char</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="common">
diff --git a/vcproj-12/map-server.vcxproj b/vcproj-12/map-server.vcxproj
index ae441f152..49822db0d 100644
--- a/vcproj-12/map-server.vcxproj
+++ b/vcproj-12/map-server.vcxproj
@@ -207,6 +207,7 @@
     <ClInclude Include="..\src\config\renewal.h" />
     <ClInclude Include="..\src\config\secure.h" />
     <ClInclude Include="..\src\config\classes\general.h" />
+    <ClInclude Include="..\src\map\rodex.h" />
     <ClInclude Include="..\src\map\script.h" />
     <ClInclude Include="..\src\map\searchstore.h" />
     <ClInclude Include="..\src\map\skill.h" />
@@ -277,6 +278,7 @@
     <ClCompile Include="..\src\map\pc_groups.c" />
     <ClCompile Include="..\src\map\pet.c" />
     <ClCompile Include="..\src\map\quest.c" />
+    <ClCompile Include="..\src\map\rodex.c" />
     <ClCompile Include="..\src\map\script.c" />
     <ClCompile Include="..\src\map\searchstore.c" />
     <ClCompile Include="..\src\map\skill.c" />
diff --git a/vcproj-12/map-server.vcxproj.filters b/vcproj-12/map-server.vcxproj.filters
index 13192d108..e6b89b3e6 100644
--- a/vcproj-12/map-server.vcxproj.filters
+++ b/vcproj-12/map-server.vcxproj.filters
@@ -205,6 +205,9 @@
     <ClCompile Include="..\src\map\channel.c">
       <Filter>map</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\map\rodex.c">
+      <Filter>map</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\map\atcommand.h">
@@ -462,6 +465,9 @@
     <ClInclude Include="..\src\config\secure.h">
       <Filter>config</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\map\rodex.h">
+      <Filter>map</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="common">
diff --git a/vcproj-14/char-server.vcxproj b/vcproj-14/char-server.vcxproj
index 542b675bb..f2fc4682c 100644
--- a/vcproj-14/char-server.vcxproj
+++ b/vcproj-14/char-server.vcxproj
@@ -138,6 +138,7 @@
     <ClInclude Include="..\3rdparty\libconfig\strbuf.h" />
     <ClInclude Include="..\3rdparty\libconfig\wincompat.h" />
     <ClInclude Include="..\3rdparty\mt19937ar\mt19937ar.h" />
+    <ClInclude Include="..\src\char\int_rodex.h" />
     <ClInclude Include="..\src\common\atomic.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\conf.h" />
@@ -199,6 +200,7 @@
     <ClCompile Include="..\3rdparty\libconfig\scanner.c" />
     <ClCompile Include="..\3rdparty\libconfig\strbuf.c" />
     <ClCompile Include="..\3rdparty\mt19937ar\mt19937ar.c" />
+    <ClCompile Include="..\src\char\int_rodex.c" />
     <ClCompile Include="..\src\common\conf.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\console.c" />
diff --git a/vcproj-14/char-server.vcxproj.filters b/vcproj-14/char-server.vcxproj.filters
index 2299a2402..ba5914a89 100644
--- a/vcproj-14/char-server.vcxproj.filters
+++ b/vcproj-14/char-server.vcxproj.filters
@@ -136,6 +136,9 @@
     <ClCompile Include="..\src\common\des.c">
       <Filter>common</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\char\int_rodex.c">
+      <Filter>char</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\common\cbasetypes.h">
@@ -321,6 +324,9 @@
     <ClInclude Include="..\src\config\classes\general.h">
       <Filter>config</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\char\int_rodex.h">
+      <Filter>char</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="common">
diff --git a/vcproj-14/map-server.vcxproj b/vcproj-14/map-server.vcxproj
index e446060f0..e20d13462 100644
--- a/vcproj-14/map-server.vcxproj
+++ b/vcproj-14/map-server.vcxproj
@@ -205,6 +205,7 @@
     <ClInclude Include="..\src\config\renewal.h" />
     <ClInclude Include="..\src\config\secure.h" />
     <ClInclude Include="..\src\config\classes\general.h" />
+    <ClInclude Include="..\src\map\rodex.h" />
     <ClInclude Include="..\src\map\script.h" />
     <ClInclude Include="..\src\map\searchstore.h" />
     <ClInclude Include="..\src\map\skill.h" />
@@ -275,6 +276,7 @@
     <ClCompile Include="..\src\map\pc_groups.c" />
     <ClCompile Include="..\src\map\pet.c" />
     <ClCompile Include="..\src\map\quest.c" />
+    <ClCompile Include="..\src\map\rodex.c" />
     <ClCompile Include="..\src\map\script.c" />
     <ClCompile Include="..\src\map\searchstore.c" />
     <ClCompile Include="..\src\map\skill.c" />
diff --git a/vcproj-14/map-server.vcxproj.filters b/vcproj-14/map-server.vcxproj.filters
index 13192d108..e6b89b3e6 100644
--- a/vcproj-14/map-server.vcxproj.filters
+++ b/vcproj-14/map-server.vcxproj.filters
@@ -205,6 +205,9 @@
     <ClCompile Include="..\src\map\channel.c">
       <Filter>map</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\map\rodex.c">
+      <Filter>map</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\map\atcommand.h">
@@ -462,6 +465,9 @@
     <ClInclude Include="..\src\config\secure.h">
       <Filter>config</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\map\rodex.h">
+      <Filter>map</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="common">
-- 
cgit v1.2.3-70-g09d2