Просмотр исходного кода

Installer: The kernel installer now parses armors, accesories, materia, key items and summon names.
Added an structure for savemaps, and the kernel now partially reads the initial savemap (but nothing is written yet).

Iñigo Valentin 3 лет назад
Родитель
Сommit
0fe00ac541

+ 667 - 6
V-Gears-Installer/include/KernelDataInstaller.h

@@ -18,6 +18,7 @@
 #include "common/BinGZipFile.h"
 #include "common/TypeDefine.h"
 #include "Characters.h"
+#include "SaveMap.h"
 
 class KernelDataInstaller{
 
@@ -117,6 +118,88 @@ class KernelDataInstaller{
          */
         void WriteWeapons(std::string file);
 
+        /**
+         * Reads the armor info from the kernel.
+         *
+         * @return The number of armor read.
+         */
+        int ReadArmors();
+
+        /**
+         * Saves armor data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteArmors(std::string file);
+
+        /**
+         * Reads the accessory info from the kernel.
+         *
+         * @return The number of accessories read.
+         */
+        int ReadAccessories();
+
+        /**
+         * Saves accessory data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteAccessories(std::string file);
+
+        /**
+         * Reads materia info from the kernel.
+         *
+         * @return The number of accessories read.
+         */
+        int ReadMateria();
+
+        /**
+         * Saves materia data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteMateria(std::string file);
+
+        /**
+         * Reads key item info from the kernel.
+         *
+         * @return The number of key items read.
+         */
+        int ReadKeyItems();
+
+        /**
+         * Saves key item data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteKeyItems(std::string file);
+
+        /**
+         * Reads summon name info from the kernel.
+         *
+         * @return The number of summons read.
+         */
+        int ReadSummonNames();
+
+        /**
+         * Saves summon names data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteSummonNames(std::string file);
+
+        /**
+         * Reads the initial savemap from the kernel.
+         */
+        void ReadInitialSaveMap();
+
+        /**
+         * Saves the initial savemap to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteInitialSaveMap(std::string file);
+
     private:
 
         enum KERNEL_SECTIONS{
@@ -1162,7 +1245,7 @@ class KernelDataInstaller{
             /**
              * Stat value raise.
              */
-            u32 bonus;
+            u8 bonus;
         };
 
         /**
@@ -1182,7 +1265,7 @@ class KernelDataInstaller{
             u8 target_raw;
 
             /**
-             * Effect ID, unused (Always 0XFFFF). 1 byte.
+             * Effect ID, unused (Always 0XFF). 1 byte.
              */
             u8 unused_0;
 
@@ -1198,7 +1281,7 @@ class KernelDataInstaller{
             u8 damage_raw;
 
             /**
-             * Unused data (Always 0XFFFF). 1 byte.
+             * Unused data (Always 0XFF). 1 byte.
              */
             u8 unused_1;
 
@@ -1266,7 +1349,7 @@ class KernelDataInstaller{
             u16 equip_raw;
 
             /**
-             * Weapon element id.
+             * Weapon element ids.
              */
             u16 element_raw;
 
@@ -1280,14 +1363,14 @@ class KernelDataInstaller{
              *
              * {@see stat_bonus} for more info.
              */
-            u32 stat_raw;
+            u8 stat_raw[4];
 
             /**
              * The amount the raised stats are raised by.
              *
              * {@see stat_bonus} for more info.
              */
-            u32 stat_bonus_raw;
+            u8 stat_bonus_raw[4];
 
             /**
              * Materia slots.
@@ -1448,6 +1531,557 @@ class KernelDataInstaller{
 
         };
 
+        /**
+         * Data for each armor
+         *
+         * As found in KERNEL.BIN, file 6, in order, up to unknown_3. Members after unknown_3 are
+         * not found literally in KERNEL.BIN, but are derived from other members or found in other
+         * files of the kernel (text files).
+         */
+        struct ArmorData{
+
+            /**
+             * Unknown (Always 0XFF). 1 byte.
+             */
+            u8 unknown_0;
+
+            /**
+             * Elemental resistance mode. 1 byte.
+             *
+             * For all elements in {@see element_raw}:
+             * If 0: Absorbs.
+             * If 1: Nullifies.
+             * If 2: Halves.
+             * If any other value: normal.
+             */
+            u8 element_defense_mode;
+
+            /**
+             * Defense stat. 1 byte.
+             */
+            u8 defense;
+
+            /**
+             * Magic defense stat. 1 byte.
+             */
+            u8 m_defense;
+
+            /**
+             * Evasion stat. 1 byte.
+             */
+            u8 evasion;
+
+            /**
+             * Magic evasion stat. 1 byte.
+             */
+            u8 m_evasion;
+
+            /**
+             * The status effect the armor protects against. 1 byte.
+             */
+            u8 status;
+
+            /**
+             * Unknown (Always 0XFFFF). 1 byte.
+             */
+            u16 unknown_1;
+
+            /**
+             * Materia slots.
+             *
+             * {@see slots} for more info.
+             */
+            u8 slots_raw[8];
+
+            /**
+             * Armor materia growth multiplier. 1 byte.
+             */
+            u8 growth;
+
+            /**
+             * Characters that can equip the armor. 2 bytes.
+             *
+             * Contains bits indicating the characters that can equip the armor {@see character}
+             * for more info.
+             */
+            u16 equip_raw;
+
+            /**
+             * Elements the armor protects agains.
+             */
+            u16 element_raw;
+
+            /**
+             * Unknown (Always 0XFF). 1 byte.
+             */
+            u16 unknown_2;
+
+            /**
+             * Stat the weapon raises.
+             *
+             * {@see stat_bonus} for more info.
+             */
+            u8 stat_raw[4];
+
+            /**
+             * The amount the raised stats are raised by.
+             *
+             * {@see stat_bonus} for more info.
+             */
+            u8 stat_bonus_raw[4];
+
+            /**
+             * Weapon restrictions. 2 bytes.
+             *
+             * Contains bits indicating if the item is {@see selleable}, {@see useable_battle} and
+             * {@see useable_menu}and {@see throwable}.
+             */
+            u16 restrict_raw;
+
+            /**
+             * Unknown (Always 0XFFFF). 1 byte.
+             */
+            u16 unknown_3;
+
+            /**
+             * Armor ID.
+             *
+             * Not actually in the item file data, it's the order at which it appears.
+             */
+            int id;
+
+            /**
+             * Armor name.
+             *
+             * Not found in the same file as the rest of the data, but on file 20.
+             */
+            std::string name;
+
+            /**
+             * Armor description.
+             *
+             * Not found in the same file as the rest of the data, but on file 12.
+             */
+            std::string description;
+
+            /**
+             * Indicates if the armor can be sold.
+             *
+             * True if the bit 0 in {@see restrict_raw} is 0.
+             */
+            bool sellable;
+
+            /**
+             * Indicates if the armor can be used in battle.
+             *
+             * True if the bit 2 in {@see restrict_raw} is 0.
+             */
+            bool useable_battle;
+
+            /**
+             * Indicates if the armor can be used in the menu.
+             *
+             * True if the bit 4 in {@see restrict_raw} is 0.
+             */
+            bool useable_menu;
+
+            /**
+             * The weapon battle model index.
+             *
+             * It's the lower nybble in {@see model_raw}.
+             */
+            std::vector<u8> equip;
+
+            /**
+             * Bonus in stats.
+             *
+             * Derived from {@see stat_raw} and {@stat_bonus_raw}.
+             */
+            std::vector<StatBonus> stat_bonus;
+
+            /**
+             * Materia slots.
+             *
+             * Derived and simplified from {@see slots_raw}.
+             */
+            std::vector<u8> materia_slots;
+
+            /**
+             * Elements of the weapon.
+             *
+             * Derived from {@see element_raw}.
+             */
+            std::vector<int> elements;
+
+        };
+
+        /**
+         * Data for each accessory
+         *
+         * As found in KERNEL.BIN, file 7, in order, up to unknown_3. Members after unknown_3 are
+         * not found literally in KERNEL.BIN, but are derived from other members or found in other
+         * files of the kernel (text files).
+         */
+        struct AccessoryData{
+
+            /**
+             * Stat the weapon raises.
+             *
+             * {@see stat_bonus} for more info.
+             */
+            u8 stat_raw[2];
+
+            /**
+             * The amount the raised stats are raised by.
+             *
+             * {@see stat_bonus} for more info.
+             */
+            u8 stat_bonus_raw[2];
+
+            /**
+             * Elemental resistance mode. 1 byte.
+             *
+             * For all elements in {@see element_raw}:
+             * If 0: Absorbs.
+             * If 1: Nullifies.
+             * If 2: Halves.
+             * If any other value: normal.
+             */
+            u8 element_defense_mode;
+
+            /**
+             * The special status effect the accesory induces.
+             *
+             * 0: Auto haste.
+             * 1: Auto berserk.
+             * 2: Curse.
+             * 3: Auto reflect.
+             * 4: Steal rate +.
+             * 5: Manipulate rate +.
+             * 6: Barrier + MBarrier.
+             * Other: Nothing.
+             */
+            u8 effect;
+
+            /**
+             * Elements the armor protects against. 2 bytes.
+             *
+             * The defense type is defined in {@see element_defense_mode}.
+             */
+            u16 element_raw;
+
+            /**
+             * The status effect the armor protects against. 4 bytes.
+             */
+            u32 status_raw;
+
+            /**
+             * Characters that can equip the armor. 2 bytes.
+             *
+             * Contains bits indicating the characters that can equip the armor {@see character}
+             * for more info.
+             */
+            u16 equip_raw;
+
+            /**
+             * Weapon restrictions. 2 bytes.
+             *
+             * Contains bits indicating if the item is {@see selleable}, {@see useable_battle} and
+             * {@see useable_menu}and {@see throwable}.
+             */
+            u16 restrict_raw;
+
+            /**
+             * Accessory ID.
+             *
+             * Not actually in the item file data, it's the order at which it appears.
+             */
+            int id;
+
+            /**
+             * Accessory name.
+             *
+             * Not found in the same file as the rest of the data, but on file 20.
+             */
+            std::string name;
+
+            /**
+             * Accessory description.
+             *
+             * Not found in the same file as the rest of the data, but on file 12.
+             */
+            std::string description;
+
+            /**
+             * Indicates if the accessory can be sold.
+             *
+             * True if the bit 0 in {@see restrict_raw} is 0.
+             */
+            bool sellable;
+
+            /**
+             * Indicates if the accessory can be used in battle.
+             *
+             * True if the bit 2 in {@see restrict_raw} is 0.
+             */
+            bool useable_battle;
+
+            /**
+             * Indicates if the accessory can be used in the menu.
+             *
+             * True if the bit 4 in {@see restrict_raw} is 0.
+             */
+            bool useable_menu;
+
+            /**
+             * The weapon battle model index.
+             *
+             * It's the lower nybble in {@see model_raw}.
+             */
+            std::vector<u8> equip;
+
+            /**
+             * Bonus in stats.
+             *
+             * Derived from {@see stat_raw} and {@stat_bonus_raw}.
+             */
+            std::vector<StatBonus> stat_bonus;
+
+            /**
+             * Elements of the accesory.
+             *
+             * Derived from {@see element_raw}.
+             */
+            std::vector<int> elements;
+
+            /**
+             * Statuses the accessory protects against.
+             *
+             * Derived from {@see status_raw}.
+             */
+            std::vector<int> status;
+
+        };
+
+        /**
+         * Types of materia.
+         */
+        enum class MATERIA_TYPE{
+
+            /**
+             * Magic materia.
+             */
+            MAGIC = 1,
+
+            /**
+             * Support materia.
+             */
+            SUPPORT,
+
+            /**
+             * Command materia.
+             */
+            COMMAND,
+
+            /**
+             * Independent materia.
+             */
+            INDEPENDENT,
+
+            /**
+             * Summon materia.
+             */
+            SUMMON
+        };
+
+        /**
+         * Stat bonuses given by equipping materia.
+         *
+         * They can be negative.
+         */
+        struct MateriaStatBonus{
+
+            /**
+             * Strength bonus. Absolute.
+             */
+            int str;
+
+            /**
+             * Vitality bonus. Absolute.
+             */
+            int vit;
+
+            /**
+             * Magic bonus. Absolute.
+             */
+            int mag;
+
+            /**
+             * Spirit bonus. Absolute.
+             */
+            int spr;
+
+            /**
+             * Dexterity bonus. Absolute.
+             */
+            int dex;
+
+            /**
+             * Luck bonus. Absolute.
+             */
+            int lck;
+
+            /**
+             * Max HP bonus. Percentile.
+             */
+            int hp;
+
+            /**
+             * Max MP bonus. Percentile.
+             */
+            int mp;
+
+            /**
+             * Indicates if at least one of the stats is changed by the materia.
+             */
+            bool change;
+        };
+
+        /**
+         * Data for each materia.
+         *
+         * As found in KERNEL.BIN, file 8, in order, up to attributes. Members after attributes are
+         * not found literally in KERNEL.BIN, but are derived from other members or found in other
+         * files of the kernel (text files).
+         */
+        struct MateriaData{
+
+            /**
+             * AP amounts at which the materia levels up. 4 x 1 byte.
+             *
+             * It can be used to determine how many levels the materia has.
+             */
+            u16 level_up_ap[4];
+
+            /**
+             * Index of stat modifier. 1 byte.
+             */
+            u8 stat_raw;
+
+            /**
+             * Status effects. 3 bytes, not 4!
+             */
+            u32 status_raw;
+
+            /**
+             * Materia element. 1 byte.
+             */
+            u8 element;
+
+            /**
+             * Materia type. 1 byte.
+             *
+             * 2 nybbles. The upper one is the subtype, and it's ignored in V-Gears. The lower one
+             * the actual type.
+             */
+            u8 type_raw;
+
+            /**
+             * Materia attributes. 6 x 1 byte.
+             *
+             * They are mostly ignored in V-Gears.
+             */
+            u8 attribute[6];
+
+            /**
+             * Accessory ID.
+             *
+             * Not actually in the item file data, it's the order at which it appears.
+             */
+            int id;
+
+            /**
+             * Accessory name.
+             *
+             * Not found in the same file as the rest of the data, but on file 20.
+             */
+            std::string name;
+
+            /**
+             * Accessory description.
+             *
+             * Not found in the same file as the rest of the data, but on file 12.
+             */
+            std::string description;
+
+            /**
+             * Mateira type.
+             *
+             * Derived from {@see type_raw}.
+             */
+            int type;
+
+            /**
+             * Stat bonuses for equipping the materia.
+             *
+             * Derived from {@see stat_raw}.
+             */
+            MateriaStatBonus stats;
+
+            /**
+             * Statuses the materia inflicts.
+             *
+             * Derived from {@see status_raw}.
+             */
+            std::vector<int> status;
+        };
+
+        /**
+         * Data for each key item.
+         *
+         * There is no data file about key items in KERNEL.BIN, there are only names (file 24) and
+         * descriptions (file 16)
+         */
+        struct KeyItemData{
+
+            /**
+             * Key item ID.
+             *
+             * Not actually in KERNEL.BIN, it;s the order at which they appear.
+             */
+            int id;
+
+            /**
+             * Key item name.
+             */
+            std::string name;
+
+            /**
+             * Key item description.
+             */
+            std::string description;
+        };
+
+        /**
+         * List of summon names.
+         *
+         * There is no data file about key items in KERNEL.BIN, there are only names (file 26).
+         */
+        struct SummonNameData{
+
+            /**
+             * Summon ID.
+             *
+             * Not actually in KERNEL.BIN, it;s the order at which they appear.
+             */
+            int id;
+
+            /**
+             * Summon name.
+             */
+            std::string name;
+        };
+
         /**
          * Character data.
          *
@@ -1767,6 +2401,33 @@ class KernelDataInstaller{
          */
         std::vector<WeaponData> weapons_;
 
+        /**
+         * Armors read from the kernel.
+         */
+        std::vector<ArmorData> armors_;
+
+        /**
+         * Accessories read from the kernel.
+         */
+        std::vector<AccessoryData> accessories_;
+
+        /**
+         * Materia read from the kernel.
+         */
+        std::vector<MateriaData> materia_;
+
+        /**
+         * Key items read from the kernel.
+         */
+        std::vector<KeyItemData> key_items_;
+
+        /**
+         * Summon names read from the kernel.
+         */
+        std::vector<SummonNameData> summon_names_;
+
+        SaveMap savemap_;
+
         /**
          * The kernel file.
          */

+ 781 - 0
V-Gears-Installer/include/SaveMap.h

@@ -0,0 +1,781 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears 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.
+ */
+
+#pragma once
+
+#include "common/TypeDefine.h"
+
+/**
+ * The structure of a savemap.
+ */
+struct SaveMap{
+
+    /**
+     * Contains the window colours, grouped by corner. 12 bytes.
+     */
+    struct WindowColor{
+
+        /**
+         * Each corner of the window. Three colour components. 3 bytes.
+         */
+        struct Corner{
+
+            /**
+             * Red component of the corner. 1 byte.
+             */
+            u8 red;
+
+            /**
+             * Green component of the window. 1 byte.
+             */
+            u8 green;
+
+            /**
+             * BLue component of the window. 1 byte.
+             */
+            u8 blue;
+        };
+
+        /**
+         * Top left corner. 3 bytes.
+         */
+        Corner top_left;
+
+        /**
+         * Top right corner. 3 bytes.
+         */
+        Corner top_right;
+
+        /**
+         * Bottom left corner. 3 bytes.
+         */
+        Corner bottom_left;
+
+        /**
+         * Bottom right corner. 3 bytes.
+         */
+        Corner bottom_right;
+    };
+
+    /**
+     * A materia. 4 bytes.
+     *
+     * @todo
+     */
+    struct Materia{
+
+        /**
+         * Materia index. 1 byte.
+         */
+        u8 id;
+
+        /**
+         * Materia AP. 3 bytes, not four.
+         *
+         * For Enemy Skill materia, ap is not the actual AP, but each byte set indicates a learned
+         * skill.
+         */
+        u32 ap;
+
+    };
+
+    /**
+     * Type of inventory item record.
+     */
+    enum class INVENTORY_TYPE{
+
+        /**
+         * A normal item.
+         */
+        ITEM = 0,
+
+        /**
+         * A weapon.
+         */
+        WEAPON,
+
+        /**
+         * An armor.
+         */
+        ARMOR,
+
+        /**
+         * An accessory.
+         */
+        ACCESSORY
+    };
+
+    /**
+     * Items, weapons, armors and accessories in inventory. 2 byte.
+     *
+     * The first 7 bits are the item quantity. The 9 last bits are the index.
+     */
+    struct Item{
+
+        /**
+         * The item bytes, as read from KERNEL.BIN in the save map.
+         *
+         * The first 7 bits are the item quantity. The 9 last bits are the index. All the other
+         * members can be derived from this. Indexex 0-127 are items, 128-255 are weapons, 256-287
+         * are weapons and 288-319 are accessories.
+         */
+        u16 item_raw;
+
+        /**
+         * Item index.
+         */
+        int item;
+
+        /**
+         * Item quantity.
+         */
+        int quantity;
+    };
+
+    /**
+     * Information about the player location in a field map. 7 bytes.
+     */
+    struct FieldPosition{
+
+        /**
+         * X coordinate on the map. 2 bytes.
+         */
+        u16 x;
+
+        /**
+         * Y coordinate on the map. 2 bytes.
+         */
+        u16 y;
+
+        /**
+         * Triangle of the walkmesh. 2 bytes.
+         */
+        u16 triangle;
+
+        /**
+         * Player orientation. 1 byte.
+         */
+        u8 orientation;
+    };
+
+    /**
+     * Character record. 132 bytes.
+     */
+    struct Character{
+
+        /**
+         * Character identifier 1 byte.
+         *
+         * It's not the standard character ID. Used only for Cait Sith and Vincent, to distinguish
+         * them from Young Cloud and Sephiroth. V-Gears mostly ignores this.
+         */
+        u8 identifier;
+
+        /**
+         * Character level. 1 byte.
+         */
+        u8 level;
+
+        /**
+         * Character strength stat. 1 byte.
+         */
+        u8 str;
+
+        /**
+         * Character vitality stat. 1 byte.
+         */
+        u8 vit;
+
+        /**
+         * Character magic stat. 1 byte.
+         */
+        u8 mag;
+
+        /**
+         * Character spirit stat. 1 byte.
+         */
+        u8 spr;
+
+        /**
+         * Character dexterity stat. 1 byte.
+         */
+        u8 dex;
+
+        /**
+         * Character luck stat. 1 byte.
+         */
+        u8 lck;
+
+        /**
+         * Character strength bonus from sources. 1 byte.
+         */
+        u8 str_bonus;
+
+        /**
+         * Character vitality bonus from sources. 1 byte.
+         */
+        u8 vit_bonus;
+
+        /**
+         * Character magic bonus from sources. 1 byte.
+         */
+        u8 mag_bonus;
+
+        /**
+         * Character spirit bonus from sources. 1 byte.
+         */
+        u8 spr_bonus;
+
+        /**
+         * Character dexterity bonus from sources. 1 byte.
+         */
+        u8 dex_bonus;
+
+        /**
+         * Character luck bonus from sources. 1 byte.
+         */
+        u8 lck_bonus;
+
+        /**
+         * Current limit level (1-4). 1 byte.
+         */
+        u8 limit_level;
+
+        /**
+         * Limit bar fill status (0x00: empty, 0xFF, full)
+         */
+        u8 limit_bar;
+
+        /**
+         * Character name. FF Text format, ended in 0xFF. 12 bytes.
+         */
+        u8 name_raw[12];
+
+        /**
+         * Equipped weapon ID. 1 byte.
+         */
+        u8 weapon;
+
+        /**
+         * Equipped armor ID. 1 byte.
+         */
+        u8 armor;
+
+        /**
+         * Equipped accessory ID. 0xFF if none. 1 byte.
+         */
+        u8 accessory;
+
+        /**
+         * Fury/sadness flags. 1 byte.
+         *
+         * 0x10: Sadness.
+         * 0x20: Fury.
+         * Other: None.
+         */
+        u8 status_raw;
+
+        /**
+         * Character row. 1 byte.
+         *
+         * 0xFF: Front row (default).
+         * 0xFE: Back row.
+         */
+        u8 row_raw;
+
+        /**
+         * Progress of the level bar. 1 byte.
+         *
+         * Cosmetic only. V-Gears ignores it.
+         */
+        u8 level_progress;
+
+        /**
+         * Learned limits. 2 bytes.
+         *
+         * XORed values:
+         * 0x0001: 1-1 Learned.
+         * 0x0002: 1-2 Learned.
+         * 0x0008: 2-1 Learned.
+         * 0x0010: 2-2 Learned.
+         * 0x0040: 3-1 Learned.
+         * 0x0080: 3-2 Learned.
+         * 0x0200: 4-1 Learned.
+         */
+        u16 learned_limits_raw;
+
+        /**
+         * Number of monster kills. 2 bytes.
+         */
+        u16 kills;
+
+        /**
+         * Number of times the limit 1-1 has been used. 3 x 2 bytes.
+         *
+         * 0: Uses of limit 1-1.
+         * 1: Uses of limit 2-1.
+         * 2: Uses of limit 3-1.
+         */
+        u16 limit_uses[3];
+
+        /**
+         * Current HP. 2 bytes.
+         */
+        u16 hp;
+
+        /**
+         * Base HP. 2 bytes.
+         *
+         * The max HP before materia alterations.
+         */
+        u16 base_hp;
+
+        /**
+         * Current MP. 2 bytes.
+         */
+        u16 mp;
+
+        /**
+         * Base MP. 2 bytes.
+         *
+         * The max MP before materia alterations.
+         */
+        u16 base_mp;
+
+        /**
+         * Unknown data. 4 bytes.
+         */
+        u32 unknown;
+
+        /**
+         * Max HP. 2 bytes.
+         *
+         * The max HP after materia alterations.
+         */
+        u16 max_hp;
+
+        /**
+         * Max MP. 2 bytes.
+         *
+         * The max MP after materia alterations.
+         */
+        u16 max_mp;
+
+        /**
+         * Total gained experience. 4 bytes.
+         */
+        u32 exp;
+
+        /**
+         * Materia equipped in the weapon. 8 x 4 bytes.
+         */
+        Materia weapon_materia[8];
+
+        /**
+         * Materia equipped in the armor. 8 x 4 bytes.
+         */
+        Materia armor_materia[8];
+
+        /**
+         * Remaining experience to reach next level. 4 bytes.
+         */
+        u32 exp_to_next;
+
+        /**
+         * Character name.
+         *
+         * Derived from {@see name_raw}.
+         */
+        std::string name;
+
+        /**
+         * Indicates if the character is in fury state.
+         *
+         * Derived from {@see status_raw}.
+         */
+        bool fury;
+
+        /**
+         * Indicates if the character is in saddness state.
+         *
+         * Derived from {@see status_raw}.
+         */
+        bool sadness;
+
+        /**
+         * Indicates if the character is in the back row.
+         */
+        bool back_row;
+
+    };
+
+    /**
+     * Each of the memory banks dumped to the savemap. 256 bytes.
+     */
+    struct MemoryBank{
+
+        /**
+         * Bank data. 256 x 1 byte.
+         */
+        u8 byte[256];
+    };
+
+    /**
+     * Selections related to the PHS. 2 bytes.
+     */
+    struct PhsSelection{
+
+        /**
+         * The mask, as in the savemap.
+         *
+         * The first 9 bits, when set, represents characters, sorted by id.
+         */
+        u16 mask;
+
+        /**
+         * Character seelction with the mask applied. Selected character IDs are true.
+         */
+        bool character[9];
+    };
+
+    /**
+     * User defined settings. 21 bytes.
+     */
+    struct UserSettings{
+
+        /**
+         * Key mappings. 16 bytes.
+         */
+        struct KeyMapping{
+
+            /**
+             * L2 key mapping. 1 byte.
+             */
+            u8 l2;
+
+            /**
+             * R2 key mapping. 1 byte.
+             */
+            u8 r2;
+
+            /**
+             * L1 key mapping. 1 byte.
+             */
+            u8 l1;
+
+            /**
+             * R1 key mapping. 1 byte.
+             */
+            u8 r1;
+
+            /**
+             * Triangle key mapping. 1 byte.
+             */
+            u8 triangle;
+
+            /**
+             * Circle key mapping. 1 byte.
+             */
+            u8 circle;
+
+            /**
+             * Cross key mapping. 1 byte.
+             */
+            u8 cross;
+
+            /**
+             * Square key mapping. 1 byte.
+             */
+            u8 square;
+
+            /**
+             * Select key mapping. 1 byte.
+             */
+            u8 select;
+
+            /**
+             * Unknown key mapping. 1 byte.
+             */
+            u8 unknown_0;
+
+            /**
+             * Unknown key mapping. 1 byte.
+             */
+            u8 unknown_1;
+
+            /**
+             * Start key mapping. 1 byte.
+             */
+            u8 start;
+
+            /**
+             * Up key mapping. 1 byte.
+             */
+            u8 up;
+
+            /**
+             * Right key mapping. 1 byte.
+             */
+            u8 right;
+
+            /**
+             * Down key mapping. 1 byte.
+             */
+            u8 down;
+
+            /**
+             * Left key mapping. 1 byte.
+             */
+            u8 left;
+        };
+
+        /**
+         * Battle ATB speed, 0x00: Fast, 0xFF: Slow. 1 byte.
+         */
+        u8 battle_speed;
+
+        /**
+         * Battle message speed, 0x00: Fast, 0xFF: Slow. 1 byte.
+         */
+        u8 battle_message_speed;
+
+        /**
+         * Several XORed settings. 1 byte.
+         *
+         * Sound: mono (0x00); stereo (0x01).
+         * Controller: normal (0x00); customize (0x04).
+         * Cursor: initial (0x00); memory (0x10).
+         * ATB: Active (0x00); Recommended (0x40); Wait (0x80).
+         */
+        u8 mask_1;
+
+        /**
+         * Several XORed settings. 1 byte.
+         *
+         * Camera angle: Auto (0x00); Fix (0x01)
+         * Magic order: Restore attack indirect (0x00)
+         * Magic order: Restore indirect attack (0x04)
+         * Magic order: Attack indirect restore (0x08)
+         * Magic order: Attack restore indirect (0x0C)
+         * Magic order: Indirect restore attack (0x10)
+         * Magic order: Indirect attack restore (0x14)
+         * Extra battle window displaying information: Inactive (0x00); Active (0x40)
+         */
+        u8 mask_2;
+
+        /**
+         * Key mapping. 16 bytes.
+         */
+        KeyMapping mapping;
+
+        /**
+         * Message speed, 0x00: Fast, 0xFF: Slow. 1 byte.
+         */
+        u8 message_speed;
+    };
+
+    // TODO: Maybe the header needs to be skipped.
+    // PSX: 512 bytes.
+    // PC: 9 bytes.
+
+    /**
+     * Savemap checksum. 4 bytes.
+     *
+     * Unused in V-Gears. Never generated.
+     */
+    u32 checksum;
+
+    /**
+     * Bytes used for the save preview. 68 bytes
+     *
+     * Unused in V-Gears, can be skipped.
+     */
+    u8 preview[68];
+
+    /**
+     * Bytes for window color. 12 bytes.
+     */
+    WindowColor window_colour;
+
+    /**
+     * Character records. 9 x 132 bytes.
+     *
+     * 0: Cloud.
+     * 1: Barret.
+     * 2: Tifa.
+     * 3: Aeris.
+     * 4: Red XIII.
+     * 5: Yuffie.
+     * 6: Cait Sith / Young Cloud.
+     * 7: Vincent / Sephiroth.
+     */
+    Character characters[9];
+
+    /**
+     * IDs of characters in the party. 3 x 1 byte.
+     */
+    u8 party[3];
+
+    /**
+     * Unused data. Always 0xFF. 1 byte.
+     */
+    u8 unused_0;
+
+    /**
+     * Inventory list. 320 x 2 bytes,
+     *
+     * It includes items, unequipped weapons, unequipped armor and unequipped accessories.
+     */
+    Item inventory[320];
+
+    /**
+     * Unequipped materia list. 200 x 4 bytes.
+     */
+    Materia materia[200];
+
+    /**
+     * Materia stolen by Yuffie. 48 x 4 bytes.
+     */
+    Materia stolen_materia[48];
+
+    /**
+     * Unknown data. Always 0xFFFFFFFF. 4 bytes.
+     */
+    u32 unknown_0;
+
+    /**
+     * Current Gil. 4 bytes.
+     */
+    u32 gil;
+
+    /**
+     * Total played time, in seconds. 4 bytes.
+     */
+    u32 time;
+
+    /**
+     * Countdown time, in seconds. 4 bytes.
+     */
+    u32 countdown;
+
+    /**
+     * Unknown data. 3 bytes.
+     */
+    u8 unknown_1[3];
+
+    /**
+     * Total played time, fractions of seconds. 4 bytes.
+     *
+     * Ignored by V-Gears.
+     */
+    u32 time_fractions;
+
+    /**
+     * Countdown time, fractions of seconds. 4 bytes.
+     *
+     * Ignored by V-Gears.
+     */
+    u32 countdown_fractions;
+
+    /**
+     * Current game module, redundant. 4 bytes.
+     *
+     * Ignored by V-Gears.
+     */
+    u32 module_redundant;
+
+    /**
+     * Current game module. 4 bytes.
+     *
+     * 1: Game saved in a field.
+     * 3: Game saved in the world map.
+     */
+    u16 module;
+
+    /**
+     * Saving location. 2 bytes.
+     *
+     * @todo Does this indicte the current field? The coordinates in a field? If so, 2 nybbles?
+     */
+    u16 location;
+
+    /**
+     * Unused data. Always 0xFF. 1 byte.
+     */
+    u8 unused_1;
+
+    /**
+     * Position information on a field map. 5 bytes.
+     */
+    FieldPosition field_position;
+
+    /**
+     * Unknown data. 3 bytes.
+     */
+    u8 unknown_2[3];
+
+    /**
+     * Information to seed encounter data.
+     *
+     * V-Gears ignores this.
+     */
+    u8 encounter_step;
+
+    /**
+     * Information to seed encounter data.
+     *
+     * V-Gears ignores this.
+     */
+    u8 encounter_offset;
+
+    /**
+     * Unused data. Always 0xFF. 1 byte.
+     */
+    u8 unused_2;
+
+    /**
+     * Memory banks dumped in the savemap. 5 x 256 bytes.
+     */
+    MemoryBank bank[5];
+
+    /**
+     * Characters locked in the PHS. 2 bytes.
+     *
+     * Locked characters can be switched to or from the party.
+     */
+    PhsSelection phs_lock;
+
+    /**
+     * Characters available in the PHS. 2 bytes.
+     *
+     * Available characters appear in the PHS.
+     */
+    PhsSelection phs_available;
+
+    /**
+     * Unknown data. 48 bytes.
+     */
+    u8 unknown_3[48];
+
+    /**
+     * UserSettings. 21 bytes.
+     */
+    UserSettings user_settings;
+
+    /**
+     * Unknown data. 8 bytes.
+     */
+    u8 unknown_4[8];
+};

+ 20 - 0
V-Gears-Installer/src/DataInstaller.cpp

@@ -119,6 +119,26 @@ int DataInstaller::Progress(){
                 CreateDir("game");
                 kernel_installer_->WriteWeapons(output_dir_ + "game/weapons.xml");
             }
+            if (kernel_installer_->ReadArmors() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteArmors(output_dir_ + "game/armors.xml");
+            }
+            if (kernel_installer_->ReadAccessories() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteAccessories(output_dir_ + "game/accessories.xml");
+            }
+            if (kernel_installer_->ReadMateria() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteMateria(output_dir_ + "game/materia.xml");
+            }
+            if (kernel_installer_->ReadKeyItems() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteKeyItems(output_dir_ + "game/key_items.xml");
+            }
+            if (kernel_installer_->ReadSummonNames() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteSummonNames(output_dir_ + "game/summons.xml");
+            }
             exit(0);
 
             installation_state_ = SPAWN_POINTS_AND_SCALE_FACTORS_INIT;

+ 1091 - 79
V-Gears-Installer/src/KernelDataInstaller.cpp

@@ -34,10 +34,6 @@ int KernelDataInstaller::ReadCommands(){
     int desc_offset = 0;
     int command_count = 0;
 
-    std::cout <<
-      "-- Commands ------------------\n" <<
-      "|  ID | cur | c_s | c_m| tar |\n";
-
     // There are exactly 32 commands. Some of them are blank, so there is not really a
     // reliable exit condition.
     for (int i = 0; i < 32; i ++){
@@ -116,14 +112,6 @@ int KernelDataInstaller::ReadCommands(){
             commands_.push_back(data);
             command_count ++;
         }
-
-        printf(
-          "| %3d | %3d | %3d | %3d | ",
-          i, data.camera_single, data.camera_multiple, data.target_raw
-        );
-        std::cout << data.name << ": " << data.description << std::endl;
-
-
     }
 
     return command_count;
@@ -153,10 +141,6 @@ int KernelDataInstaller::ReadAttacks(){
     File attacks_file = kernel_.ExtractGZip(KERNEL_ATTACK_DATA);
     int attack_count = 0;
 
-    std::cout <<
-      "-- Attacks ---------------------------------------------------------------------\n" <<
-      "|  ID | acc |tar | eff | dmg | pow | con | sch | eff | efm | sta | elm |  spe  |\n";
-
     for (int i = 0; i < 128; i ++){
 
         AttackData data;
@@ -309,15 +293,6 @@ int KernelDataInstaller::ReadAttacks(){
         // Add to the list of items.
         attacks_.push_back(data);
         attack_count ++;
-
-        printf(
-          "| %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %5d |\n",
-          i, data.accuracy, data.target_raw, data.effect, data.damage_raw, data.power, data.condition_raw,
-          data.status_change_raw, data.additional_effects_raw, data.additional_effects_mod_raw,
-          data.status_raw, data.element_raw, data.special_raw
-        );
-
-
     }
 
     return attack_count;
@@ -389,10 +364,6 @@ int KernelDataInstaller::ReadCharacters(){
     File characters_file = kernel_.ExtractGZip(KERNEL_BATTLE_AND_GROWTH_DATA);
     int character_count = 0;
 
-    std::cout <<
-      "-- Chars ----\n" <<
-      "|  ID | lvl |\n";
-
     for (int i = 0; i < 10; i ++){ // 9 characters
 
         CharacterData data;
@@ -442,11 +413,6 @@ int KernelDataInstaller::ReadCharacters(){
         // Add to the list of characters.
         characters_.push_back(data);
         character_count ++;
-
-        printf("| %3d | %3d |", i, data.initial_level);
-        std::cout << data.name << std::endl;
-
-
     }
 
     return character_count;
@@ -660,10 +626,6 @@ int KernelDataInstaller::ReadItems(){
     int desc_offset = 0;
     int item_count = 0;
 
-    std::cout <<
-      "-- Items ------------------------------------------------------------------\n" <<
-      "|  ID | tar | eff | dmg | pow | con | sch | eff | efm | sta | elm |  spe  |\n";
-
     for (int i = 0; i < 128; i ++){
 
         ItemData data;
@@ -736,17 +698,6 @@ int KernelDataInstaller::ReadItems(){
         data.sellable = !(data.restrict_raw & (1 << 0));
         data.useable_battle = !(data.restrict_raw & (2 << 0));
         data.useable_menu = !(data.restrict_raw & (4 << 0));
-        // TODO: XOR, not individual bits
-        /*
-        data.target.selection_enabled = data.target_raw & (0 << 0); // First bit...
-        data.target.default_enemy = data.target_raw & (1 << 0);
-        data.target.default_multiple = data.target_raw & (2 << 0);
-        data.target.toggle_multiple = data.target_raw & (3 << 0);
-        data.target.fixed_row = data.target_raw & (4 << 0);
-        data.target.short_range = data.target_raw & (5 << 0);
-        data.target.all_rows = data.target_raw & (6 << 0);
-        data.target.random = data.target_raw & (7 << 0); // ... last bit.
-        */
         if (data.target_raw == (data.target_raw | 0x01)) data.target.selection_enabled = true;
         else data.target.selection_enabled = false;
         if (data.target_raw == (data.target_raw | 0x02)) data.target.default_enemy = true;
@@ -763,21 +714,14 @@ int KernelDataInstaller::ReadItems(){
         else data.target.all_rows = false;
         if (data.target_raw == (data.target_raw | 0x80)) data.target.random = true;
         else data.target.random = false;
-
-
         data.damage_formula = data.damage_raw >> 4; // Upper nybble.
         data.damage_modifier = data.damage_raw & 15; // Lower nybble.
-        /*data.condition.party_hp = data.condition_raw & (0 << 0); //First bit...
-        data.condition.mp = data.condition_raw & (1 << 0);
-        data.condition.party_status = data.condition_raw & (2 << 0); // ... last bit.*/
-
         data.status.chance = 63 - (data.status_change_raw ^ 63); // Out of 63;
         if (data.status_change_raw == (data.status_change_raw | 0x00000080))
             data.status.mode = TOGGLE;
         else if (data.status_change_raw == (data.status_change_raw | 0x00000040))
             data.status.mode = CURE;
         else data.status.mode = INFLICT;
-
         switch (data.condition_raw){
             case 0: data.restore_type = RESTORE_HP; break;
             case 1: data.restore_type = RESTORE_MP; break;
@@ -878,20 +822,9 @@ int KernelDataInstaller::ReadItems(){
         // If that happens, clear them all.
         if (data.elements.size() == 16) data.elements.clear();
 
-
         // Add to the list of items.
         items_.push_back(data);
         item_count ++;
-
-        printf(
-          "| %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %5d | ",
-          i, data.target_raw, data.effect, data.damage_raw, data.power, data.condition_raw,
-          data.status_change_raw, data.additional_effects_raw, data.additional_effects_mod_raw,
-          data.status_raw, data.element_raw, data.special_raw
-        );
-        std::cout << data.name << ": " << data.description << std::endl;
-
-
     }
 
     return item_count;
@@ -964,10 +897,6 @@ int KernelDataInstaller::ReadWeapons(){
     int desc_offset = 0;
     int weapon_count = 0;
 
-    std::cout <<
-      "-- Weapons ----------------------------------------------------------------\n" <<
-      "|  ID | tar | eff | dmg | pow | con | sch | eff | efm | sta | elm |  spe  |\n";
-
     for (int i = 0; i < 128; i ++){
 
         WeaponData data;
@@ -1018,7 +947,7 @@ int KernelDataInstaller::ReadWeapons(){
         // break the loop.
         if ("" == data.name) break;
 
-        // Read data from the item data section.
+        // Read data from the weapon data section.
         data.target_raw = weapons_file.readU8();
         data.unused_0 = weapons_file.readU8();
         data.damage_raw = weapons_file.readU8();
@@ -1035,8 +964,8 @@ int KernelDataInstaller::ReadWeapons(){
         data.equip_raw = weapons_file.readU16LE();
         data.element_raw = weapons_file.readU16LE();
         data.unused_3 = weapons_file.readU16LE();
-        data.stat_raw = weapons_file.readU32LE();
-        data.stat_bonus_raw = weapons_file.readU32LE();
+        for (int i = 0; i < 4; i ++) data.stat_raw[i] = weapons_file.readU8();
+        for (int i = 0; i < 4; i ++) data.stat_bonus_raw[i] = weapons_file.readU8();
         for (int i = 0; i < 8; i ++) data.slots_raw[i] = weapons_file.readU8();
         data.sound = weapons_file.readU8();
         data.sound_critical = weapons_file.readU8();
@@ -1084,11 +1013,13 @@ int KernelDataInstaller::ReadWeapons(){
         if (data.equip_raw == (data.equip_raw | 0x0100)) data.equip.push_back(9);
         if (data.equip_raw == (data.equip_raw | 0x0200)) data.equip.push_back(10);
         if (data.equip_raw == (data.equip_raw | 0x0400)) data.equip.push_back(11);
-        if (data.stat_raw < 0XFF){
-            StatBonus stat_bonus;
-            stat_bonus.stat = data.stat_raw;
-            stat_bonus.bonus = data.stat_bonus_raw;
-            data.stat_bonus.push_back(stat_bonus);
+        for (int i = 0; i < 4; i ++){
+            if (data.stat_raw[i] < 0XFF && data.stat_bonus_raw[i] > 0){
+                StatBonus stat_bonus;
+                stat_bonus.stat = data.stat_raw[i];
+                stat_bonus.bonus = data.stat_bonus_raw[i];
+                data.stat_bonus.push_back(stat_bonus);
+            }
         }
         for (int i = 0; i < 8; i ++){
             if (data.slots_raw[i] == 1 || data.slots_raw[i] == 5){
@@ -1186,6 +1117,16 @@ void KernelDataInstaller::WriteWeapons(std::string file){
             }
             xml_weapon->LinkEndChild(xml_slots.release());
         }
+        if (weapon.stat_bonus.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_stats(new TiXmlElement("stat_bonuses"));
+            for (StatBonus s : weapon.stat_bonus){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", s.stat);
+                xml_stat->SetAttribute("bonus", s.bonus);
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            xml_weapon->LinkEndChild(xml_stats.release());
+        }
         if (weapon.equip.size() > 0){
             std::unique_ptr<TiXmlElement> xml_characters(new TiXmlElement("characters"));
             for (int s : weapon.equip){
@@ -1201,3 +1142,1074 @@ void KernelDataInstaller::WriteWeapons(std::string file){
     xml.LinkEndChild(container.release());
     xml.SaveFile(file);
 }
+
+int KernelDataInstaller::ReadArmors(){
+
+    // Empty the item list
+    armors_.clear();
+
+    File armor_file = kernel_.ExtractGZip(KERNEL_ARMOR_DATA);
+    armor_file.WriteFile("/home/ivalentin/armor");
+    File armor_name_file = kernel_.ExtractGZip(KERNEL_ARMOR_NAMES);
+    File armor_desc_file = kernel_.ExtractGZip(KERNEL_ARMOR_DESCRIPTIONS);
+    int name_offset = 0;
+    int desc_offset = 0;
+    int armor_count = 0;
+
+    for (int i = 0; i < 32; i ++){ // There are exactly 32.
+
+        ArmorData data;
+
+        // From the current name file offset, read 16 bytes.
+        // This is the pointer to the offset for the item name.
+        // Start reading there until 0xFF is found.
+        data.name = "";
+        u8 ch = 1;
+        name_offset = armor_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = armor_name_file.GetU8(name_offset);
+            if (ch != 0xFF) data.name += ENGLISH_CHARS[ch];
+            name_offset ++;
+        }
+
+        // For descriptions, do the same as for names.
+        data.description = "";
+        ch = 1;
+        desc_offset = armor_desc_file.readU16LE();
+        while (ch != 0xFF){
+            ch = armor_desc_file.GetU8(desc_offset);
+            if (ch != 0xFF){
+                // Warning: If the char is 0xF9, special function!
+                // the next byte (in bits, aaoooooo) means to read aa data at offset oooooo.
+                // This is to compress information in the kernel.
+                if (ch == 0xF9){
+                    // Get how much to read, two upper bits of the next byte.
+                    // Multiply by 2 and add 4. That's just how it is.
+                    u8 comp_size = (armor_desc_file.GetU8(desc_offset + 1) >> 6) * 2 + 4;
+                    // Get new offset, lower six bits of the next byte.
+                    // The offset goes backwards from the position of the 0xF9 byte.
+                    u8 comp_offset = armor_desc_file.GetU8(desc_offset + 1) & 63;
+
+                    for (int i = 0; i < comp_size; i ++){
+                        data.description += ENGLISH_CHARS[
+                          armor_desc_file.GetU8(desc_offset - 1 - comp_offset + i)
+                        ];
+                    }
+                    desc_offset ++;
+                }
+                else data.description += ENGLISH_CHARS[ch];
+            }
+            desc_offset ++;
+        }
+
+        // If the item doesn't have a name, it means all items have been read.
+        // break the loop.
+        if ("" == data.name) break;
+
+        // Read data from the weapon data section.
+        data.unknown_0 = armor_file.readU8();
+        data.element_defense_mode = armor_file.readU8();
+        data.defense = armor_file.readU8();
+        data.m_defense = armor_file.readU8();
+        data.evasion = armor_file.readU8();
+        data.m_evasion = armor_file.readU8();
+        data.status = armor_file.readU8();
+        data.unknown_1 = armor_file.readU16LE();
+        for (int i = 0; i < 8; i ++) data.slots_raw[i] = armor_file.readU8();
+        data.growth = armor_file.readU8();
+        data.equip_raw = armor_file.readU16LE();
+        data.element_raw = armor_file.readU16LE();
+        data.unknown_2 = armor_file.readU16LE();
+        for (int i = 0; i < 4; i ++) data.stat_raw[i] = armor_file.readU8();
+        for (int i = 0; i < 4; i ++) data.stat_bonus_raw[i] = armor_file.readU8();
+        data.restrict_raw = armor_file.readU16LE();
+        data.unknown_3 = armor_file.readU16LE();
+
+        // Derive data from the read data.
+        data.id = i;
+        if (data.growth != 0 && data.growth != 2 && data.growth != 3) data.growth = 1;
+        data.sellable = !(data.restrict_raw & (1 << 0));
+        data.useable_battle = !(data.restrict_raw & (2 << 0));
+        data.useable_menu = !(data.restrict_raw & (4 << 0));
+        if (data.equip_raw == (data.equip_raw | 0x0001)) data.equip.push_back(1);
+        if (data.equip_raw == (data.equip_raw | 0x0002)) data.equip.push_back(2);
+        if (data.equip_raw == (data.equip_raw | 0x0004)) data.equip.push_back(3);
+        if (data.equip_raw == (data.equip_raw | 0x0008)) data.equip.push_back(4);
+        if (data.equip_raw == (data.equip_raw | 0x0010)) data.equip.push_back(5);
+        if (data.equip_raw == (data.equip_raw | 0x0020)) data.equip.push_back(6);
+        if (data.equip_raw == (data.equip_raw | 0x0040)) data.equip.push_back(7);
+        if (data.equip_raw == (data.equip_raw | 0x0080)) data.equip.push_back(8);
+        if (data.equip_raw == (data.equip_raw | 0x0100)) data.equip.push_back(9);
+        if (data.equip_raw == (data.equip_raw | 0x0200)) data.equip.push_back(10);
+        if (data.equip_raw == (data.equip_raw | 0x0400)) data.equip.push_back(11);
+        for (int i = 0; i < 4; i ++){
+            if (data.stat_raw[i] < 0XFF && data.stat_bonus_raw[i] > 0){
+                StatBonus stat_bonus;
+                stat_bonus.stat = data.stat_raw[i];
+                stat_bonus.bonus = data.stat_bonus_raw[i];
+                data.stat_bonus.push_back(stat_bonus);
+            }
+        }
+        for (int i = 0; i < 8; i ++){
+            if (data.slots_raw[i] == 1 || data.slots_raw[i] == 5){
+                data.materia_slots.push_back(SLOT_UNLINKED);
+            }
+            else if (
+              data.slots_raw[i] == 2 || data.slots_raw[i] == 3
+              || data.slots_raw[i] == 6 || data.slots_raw[i] == 7
+            ){
+                data.materia_slots.push_back(SLOT_LINKED);
+            }
+        }
+        // In KERNEL.BIN, items with no elemental defense actually do have all elements marked,
+        // with mode 255. If, that's the case, consider that there are no elements.
+        if (data.element_defense_mode != 0xFF){
+            if (data.element_raw == (data.element_raw | 0x0001)) data.elements.push_back(FIRE);
+            if (data.element_raw == (data.element_raw | 0x0002)) data.elements.push_back(ICE);
+            if (data.element_raw == (data.element_raw | 0x0004)) data.elements.push_back(BOLT);
+            if (data.element_raw == (data.element_raw | 0x0008)) data.elements.push_back(EARTH);
+            if (data.element_raw == (data.element_raw | 0x0010)) data.elements.push_back(POISON);
+            if (data.element_raw == (data.element_raw | 0x0020)) data.elements.push_back(GRAVITY);
+            if (data.element_raw == (data.element_raw | 0x0040)) data.elements.push_back(WATER);
+            if (data.element_raw == (data.element_raw | 0x0080)) data.elements.push_back(WIND);
+            if (data.element_raw == (data.element_raw | 0x0100)) data.elements.push_back(HOLY);
+            if (data.element_raw == (data.element_raw | 0x0200))
+                data.elements.push_back(RESTORATIVE);
+            if (data.element_raw == (data.element_raw | 0x0400)) data.elements.push_back(CUT);
+            if (data.element_raw == (data.element_raw | 0x0800)) data.elements.push_back(HIT);
+            if (data.element_raw == (data.element_raw | 0x1000)) data.elements.push_back(PUNCH);
+            if (data.element_raw == (data.element_raw | 0x2000)) data.elements.push_back(SHOOT);
+            if (data.element_raw == (data.element_raw | 0x4000)) data.elements.push_back(SHOUT);
+            if (data.element_raw == (data.element_raw | 0x8000)) data.elements.push_back(HIDDEN);
+        }
+        // In KERNEL.BIN, Non elemental item actually have all elements marked.
+        // If that happens, clear them all.
+        if (data.elements.size() == 16) data.elements.clear();
+
+        // Add to the list of weapons.
+        armors_.push_back(data);
+        armor_count ++;
+    }
+
+    return armor_count;
+}
+
+void KernelDataInstaller::WriteArmors(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("armors"));
+    for (ArmorData armor : armors_){
+        std::unique_ptr<TiXmlElement> xml_armor(new TiXmlElement("armor"));
+        xml_armor->SetAttribute("id", armor.id);
+        xml_armor->SetAttribute("name", armor.name);
+        xml_armor->SetAttribute("description", armor.description);
+        xml_armor->SetAttribute("sell", armor.sellable);
+        xml_armor->SetAttribute("battle", armor.useable_battle);
+        xml_armor->SetAttribute("menu", armor.useable_menu);
+        xml_armor->SetAttribute("phi_defense", armor.defense);
+        xml_armor->SetAttribute("mag_defense", armor.m_defense);
+        xml_armor->SetAttribute("phi_evasion", armor.evasion);
+        xml_armor->SetAttribute("mag_evasion", armor.m_evasion);
+        xml_armor->SetAttribute("growth", armor.growth);
+        // Statuses.
+        if (armor.status != 0XFF){
+            std::unique_ptr<TiXmlElement> xml_status(new TiXmlElement("statuses"));
+            std::unique_ptr<TiXmlElement> xml_status_status(new TiXmlElement("status"));
+            xml_status_status->SetAttribute("id", armor.status);
+            xml_status->LinkEndChild(xml_status_status.release());
+            xml_armor->LinkEndChild(xml_status.release());
+        }
+        // Add elements.
+        if (armor.elements.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_elements(new TiXmlElement("elements"));
+            for (int s : armor.elements){
+                std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("element"));
+                xml_element->SetAttribute("id", s);
+                xml_element->SetAttribute("mode", armor.element_defense_mode);
+                xml_elements->LinkEndChild(xml_element.release());
+            }
+            xml_armor->LinkEndChild(xml_elements.release());
+        }
+        if (armor.materia_slots.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_slots(new TiXmlElement("slots"));
+            for (u8 slot : armor.materia_slots){
+                std::unique_ptr<TiXmlElement> xml_slot(new TiXmlElement("slot"));
+                xml_slot->SetAttribute("type", slot);
+                xml_slots->LinkEndChild(xml_slot.release());
+            }
+            xml_armor->LinkEndChild(xml_slots.release());
+        }
+        if (armor.stat_bonus.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_stats(new TiXmlElement("stat_bonuses"));
+            for (StatBonus s : armor.stat_bonus){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", s.stat);
+                xml_stat->SetAttribute("bonus", s.bonus);
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            xml_armor->LinkEndChild(xml_stats.release());
+        }
+        if (armor.equip.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_characters(new TiXmlElement("characters"));
+            for (int s : armor.equip){
+                std::unique_ptr<TiXmlElement> xml_character(new TiXmlElement("character"));
+                xml_character->SetAttribute("id", s);
+                xml_characters->LinkEndChild(xml_character.release());
+            }
+            xml_armor->LinkEndChild(xml_characters.release());
+        }
+        container->LinkEndChild(xml_armor.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}
+
+int KernelDataInstaller::ReadAccessories(){
+
+    // Empty the item list
+    accessories_.clear();
+
+    File accessory_file = kernel_.ExtractGZip(KERNEL_ACCESSORY_DATA);
+    File accessory_name_file = kernel_.ExtractGZip(KERNEL_ACCESSORY_NAMES);
+    File accessory_desc_file = kernel_.ExtractGZip(KERNEL_ACCESSORY_DESCRIPTIONS);
+    int name_offset = 0;
+    int desc_offset = 0;
+    int accessory_count = 0;
+
+    for (int i = 0; i < 32; i ++){ // There are exactly 32.
+
+        AccessoryData data;
+
+        // From the current name file offset, read 16 bytes.
+        // This is the pointer to the offset for the item name.
+        // Start reading there until 0xFF is found.
+        data.name = "";
+        u8 ch = 1;
+        name_offset = accessory_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = accessory_name_file.GetU8(name_offset);
+            if (ch != 0xFF) data.name += ENGLISH_CHARS[ch];
+            name_offset ++;
+        }
+
+        // For descriptions, do the same as for names.
+        data.description = "";
+        ch = 1;
+        desc_offset = accessory_desc_file.readU16LE();
+        while (ch != 0xFF){
+            ch = accessory_desc_file.GetU8(desc_offset);
+            if (ch != 0xFF){
+                // Warning: If the char is 0xF9, special function!
+                // the next byte (in bits, aaoooooo) means to read aa data at offset oooooo.
+                // This is to compress information in the kernel.
+                if (ch == 0xF9){
+                    // Get how much to read, two upper bits of the next byte.
+                    // Multiply by 2 and add 4. That's just how it is.
+                    u8 comp_size = (accessory_desc_file.GetU8(desc_offset + 1) >> 6) * 2 + 4;
+                    // Get new offset, lower six bits of the next byte.
+                    // The offset goes backwards from the position of the 0xF9 byte.
+                    u8 comp_offset = accessory_desc_file.GetU8(desc_offset + 1) & 63;
+
+                    for (int i = 0; i < comp_size; i ++){
+                        data.description += ENGLISH_CHARS[
+                          accessory_desc_file.GetU8(desc_offset - 1 - comp_offset + i)
+                        ];
+                    }
+                    desc_offset ++;
+                }
+                else data.description += ENGLISH_CHARS[ch];
+            }
+            desc_offset ++;
+        }
+
+        // If the item doesn't have a name, it means all items have been read.
+        // break the loop.
+        if ("" == data.name) break;
+
+        // Read data from the accessory data section.
+        for (int i = 0; i < 2; i ++) data.stat_raw[i] = accessory_file.readU8();
+        for (int i = 0; i < 2; i ++) data.stat_bonus_raw[i] = accessory_file.readU8();
+        data.element_defense_mode = accessory_file.readU8();
+        data.effect = accessory_file.readU8();
+        data.element_raw = accessory_file.readU16LE();
+        data.status_raw = accessory_file.readU32LE();
+        data.equip_raw = accessory_file.readU16LE();
+        data.restrict_raw = accessory_file.readU16LE();
+
+        // Derive data from the read data.
+        data.id = i;
+        data.sellable = !(data.restrict_raw & (1 << 0));
+        data.useable_battle = !(data.restrict_raw & (2 << 0));
+        data.useable_menu = !(data.restrict_raw & (4 << 0));
+        if (data.status_raw == (data.status_raw | 0x00000001)) data.status.push_back(DEATH);
+        if (data.status_raw == (data.status_raw | 0x00000002)) data.status.push_back(NEAR_DEATH);
+        if (data.status_raw == (data.status_raw | 0x00000004)) data.status.push_back(SLEEP);
+        if (data.status_raw == (data.status_raw | 0x00000008)) data.status.push_back(POISONED);
+        if (data.status_raw == (data.status_raw | 0x00000010)) data.status.push_back(SADNESS);
+        if (data.status_raw == (data.status_raw | 0x00000020)) data.status.push_back(FURY);
+        if (data.status_raw == (data.status_raw | 0x00000040)) data.status.push_back(CONFU);
+        if (data.status_raw == (data.status_raw | 0x00000080)) data.status.push_back(SILENCE);
+        if (data.status_raw == (data.status_raw | 0x00000100)) data.status.push_back(HASTE);
+        if (data.status_raw == (data.status_raw | 0x00000200)) data.status.push_back(SLOW);
+        if (data.status_raw == (data.status_raw | 0x00000400)) data.status.push_back(STOP);
+        if (data.status_raw == (data.status_raw | 0x00000800)) data.status.push_back(FROG);
+        if (data.status_raw == (data.status_raw | 0x00001000)) data.status.push_back(SMALL);
+        if (data.status_raw == (data.status_raw | 0x00002000)) data.status.push_back(SLOW_NUMB);
+        if (data.status_raw == (data.status_raw | 0x00004000)) data.status.push_back(PETRIFY);
+        if (data.status_raw == (data.status_raw | 0x00008000)) data.status.push_back(REGEN);
+        if (data.status_raw == (data.status_raw | 0x00010000)) data.status.push_back(BARRIER);
+        if (data.status_raw == (data.status_raw | 0x00020000)) data.status.push_back(M_BARRIER);
+        if (data.status_raw == (data.status_raw | 0x00040000)) data.status.push_back(REFLECT);
+        if (data.status_raw == (data.status_raw | 0x00080000)) data.status.push_back(DUAL);
+        if (data.status_raw == (data.status_raw | 0x00100000)) data.status.push_back(SHIELD);
+        if (data.status_raw == (data.status_raw | 0x00200000)) data.status.push_back(D_SENTENCE);
+        if (data.status_raw == (data.status_raw | 0x00400000)) data.status.push_back(MANIPULATE);
+        if (data.status_raw == (data.status_raw | 0x00800000)) data.status.push_back(BERSERK);
+        if (data.status_raw == (data.status_raw | 0x01000000)) data.status.push_back(PEERLESS);
+        if (data.status_raw == (data.status_raw | 0x02000000)) data.status.push_back(PARALYSIS);
+        if (data.status_raw == (data.status_raw | 0x04000000)) data.status.push_back(DARKNESS);
+        if (data.status_raw == (data.status_raw | 0x08000000)) data.status.push_back(DUAL_DRAIN);
+        if (data.status_raw == (data.status_raw | 0x10000000)) data.status.push_back(DEATH_FORCE);
+        if (data.status_raw == (data.status_raw | 0x20000000)) data.status.push_back(RESIST);
+        if (data.status_raw == (data.status_raw | 0x40000000)) data.status.push_back(LUCKY_GIRL);
+        if (data.status_raw == (data.status_raw | 0x80000000)) data.status.push_back(IMPRISONED);
+        if (data.equip_raw == (data.equip_raw | 0x0001)) data.equip.push_back(1);
+        if (data.equip_raw == (data.equip_raw | 0x0002)) data.equip.push_back(2);
+        if (data.equip_raw == (data.equip_raw | 0x0004)) data.equip.push_back(3);
+        if (data.equip_raw == (data.equip_raw | 0x0008)) data.equip.push_back(4);
+        if (data.equip_raw == (data.equip_raw | 0x0010)) data.equip.push_back(5);
+        if (data.equip_raw == (data.equip_raw | 0x0020)) data.equip.push_back(6);
+        if (data.equip_raw == (data.equip_raw | 0x0040)) data.equip.push_back(7);
+        if (data.equip_raw == (data.equip_raw | 0x0080)) data.equip.push_back(8);
+        if (data.equip_raw == (data.equip_raw | 0x0100)) data.equip.push_back(9);
+        if (data.equip_raw == (data.equip_raw | 0x0200)) data.equip.push_back(10);
+        if (data.equip_raw == (data.equip_raw | 0x0400)) data.equip.push_back(11);
+        for (int i = 0; i < 2; i ++){
+            if (data.stat_raw[i] < 0XFF && data.stat_bonus_raw[i] > 0){
+                StatBonus stat_bonus;
+                stat_bonus.stat = data.stat_raw[i];
+                stat_bonus.bonus = data.stat_bonus_raw[i];
+                data.stat_bonus.push_back(stat_bonus);
+            }
+        }
+        // In KERNEL.BIN, items with no elemental defense actually do have all elements marked,
+        // with mode 255. If that's the case, consider that there are no elements.
+        if (data.element_defense_mode != 0xFF){
+            if (data.element_raw == (data.element_raw | 0x0001)) data.elements.push_back(FIRE);
+            if (data.element_raw == (data.element_raw | 0x0002)) data.elements.push_back(ICE);
+            if (data.element_raw == (data.element_raw | 0x0004)) data.elements.push_back(BOLT);
+            if (data.element_raw == (data.element_raw | 0x0008)) data.elements.push_back(EARTH);
+            if (data.element_raw == (data.element_raw | 0x0010)) data.elements.push_back(POISON);
+            if (data.element_raw == (data.element_raw | 0x0020)) data.elements.push_back(GRAVITY);
+            if (data.element_raw == (data.element_raw | 0x0040)) data.elements.push_back(WATER);
+            if (data.element_raw == (data.element_raw | 0x0080)) data.elements.push_back(WIND);
+            if (data.element_raw == (data.element_raw | 0x0100)) data.elements.push_back(HOLY);
+            if (data.element_raw == (data.element_raw | 0x0200))
+                data.elements.push_back(RESTORATIVE);
+            if (data.element_raw == (data.element_raw | 0x0400)) data.elements.push_back(CUT);
+            if (data.element_raw == (data.element_raw | 0x0800)) data.elements.push_back(HIT);
+            if (data.element_raw == (data.element_raw | 0x1000)) data.elements.push_back(PUNCH);
+            if (data.element_raw == (data.element_raw | 0x2000)) data.elements.push_back(SHOOT);
+            if (data.element_raw == (data.element_raw | 0x4000)) data.elements.push_back(SHOUT);
+            if (data.element_raw == (data.element_raw | 0x8000)) data.elements.push_back(HIDDEN);
+        }
+        // In KERNEL.BIN, Non elemental item actually have all elements marked.
+        // If that happens, clear them all.
+        if (data.elements.size() == 16) data.elements.clear();
+
+        // Add to the list of weapons.
+        accessories_.push_back(data);
+        accessory_count ++;
+    }
+
+    return accessory_count;
+}
+
+void KernelDataInstaller::WriteAccessories(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("accessories"));
+    for (AccessoryData accessory : accessories_){
+        std::unique_ptr<TiXmlElement> xml_accessory(new TiXmlElement("accessory"));
+        xml_accessory->SetAttribute("id", accessory.id);
+        xml_accessory->SetAttribute("name", accessory.name);
+        xml_accessory->SetAttribute("description", accessory.description);
+        xml_accessory->SetAttribute("sell", accessory.sellable);
+        xml_accessory->SetAttribute("battle", accessory.useable_battle);
+        xml_accessory->SetAttribute("menu", accessory.useable_menu);
+        // Special effect
+        if (accessory.effect != 0XFF){
+            std::unique_ptr<TiXmlElement> xml_status(new TiXmlElement("special_effects"));
+            std::unique_ptr<TiXmlElement> xml_status_status(new TiXmlElement("effect"));
+            xml_status_status->SetAttribute("id", accessory.effect);
+            xml_status->LinkEndChild(xml_status_status.release());
+            xml_accessory->LinkEndChild(xml_status.release());
+        }
+        // Statuses.
+        if (accessory.status.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_status(new TiXmlElement("statuses"));
+            for (int s : accessory.status){
+                std::unique_ptr<TiXmlElement> xml_status_status(new TiXmlElement("status"));
+                xml_status_status->SetAttribute("id", s);
+                xml_status->LinkEndChild(xml_status_status.release());
+            }
+            xml_accessory->LinkEndChild(xml_status.release());
+        }
+        // Add elements.
+        if (accessory.elements.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_elements(new TiXmlElement("elements"));
+            for (int s : accessory.elements){
+                std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("element"));
+                xml_element->SetAttribute("id", s);
+                xml_element->SetAttribute("mode", accessory.element_defense_mode);
+                xml_elements->LinkEndChild(xml_element.release());
+            }
+            xml_accessory->LinkEndChild(xml_elements.release());
+        }
+        if (accessory.stat_bonus.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_stats(new TiXmlElement("stat_bonuses"));
+            for (StatBonus s : accessory.stat_bonus){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", s.stat);
+                xml_stat->SetAttribute("bonus", s.bonus);
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            xml_accessory->LinkEndChild(xml_stats.release());
+        }
+        if (accessory.equip.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_characters(new TiXmlElement("characters"));
+            for (int s : accessory.equip){
+                std::unique_ptr<TiXmlElement> xml_character(new TiXmlElement("character"));
+                xml_character->SetAttribute("id", s);
+                xml_characters->LinkEndChild(xml_character.release());
+            }
+            xml_accessory->LinkEndChild(xml_characters.release());
+        }
+        container->LinkEndChild(xml_accessory.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}
+
+int KernelDataInstaller::ReadMateria(){
+
+    // Empty the item list
+    materia_.clear();
+
+    File materia_file = kernel_.ExtractGZip(KERNEL_MATERIA_DATA);
+    File materia_name_file = kernel_.ExtractGZip(KERNEL_MATERIA_NAMES);
+    File materia_desc_file = kernel_.ExtractGZip(KERNEL_MATERIA_DESCRIPTIONS);
+    int name_offset = 0;
+    int desc_offset = 0;
+    int materia_count = 0;
+
+    for (int i = 0; i < 96; i ++){ // There are exactly 96.
+
+        MateriaData data;
+
+        // From the current name file offset, read 16 bytes.
+        // This is the pointer to the offset for the item name.
+        // Start reading there until 0xFF is found.
+        data.name = "";
+        u8 ch = 1;
+        name_offset = materia_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = materia_name_file.GetU8(name_offset);
+            if (ch != 0xFF) data.name += ENGLISH_CHARS[ch];
+            name_offset ++;
+        }
+
+        // For descriptions, do the same as for names.
+        data.description = "";
+        ch = 1;
+        desc_offset = materia_desc_file.readU16LE();
+        while (ch != 0xFF){
+            ch = materia_desc_file.GetU8(desc_offset);
+            if (ch != 0xFF){
+                // Warning: If the char is 0xF9, special function!
+                // the next byte (in bits, aaoooooo) means to read aa data at offset oooooo.
+                // This is to compress information in the kernel.
+                if (ch == 0xF9){
+                    // Get how much to read, two upper bits of the next byte.
+                    // Multiply by 2 and add 4. That's just how it is.
+                    u8 comp_size = (materia_desc_file.GetU8(desc_offset + 1) >> 6) * 2 + 4;
+                    // Get new offset, lower six bits of the next byte.
+                    // The offset goes backwards from the position of the 0xF9 byte.
+                    u8 comp_offset = materia_desc_file.GetU8(desc_offset + 1) & 63;
+
+                    for (int i = 0; i < comp_size; i ++){
+                        data.description += ENGLISH_CHARS[
+                          materia_desc_file.GetU8(desc_offset - 1 - comp_offset + i)
+                        ];
+                    }
+                    desc_offset ++;
+                }
+                else data.description += ENGLISH_CHARS[ch];
+            }
+            desc_offset ++;
+        }
+
+        // Read data from the materia data section.
+        for (int i = 0; i < 4; i ++) data.level_up_ap[i] = materia_file.readU16LE();
+        data.stat_raw = materia_file.readU8();
+        data.status_raw // WARNING: Status has only three bytes.
+          = materia_file.readU8() + (materia_file.readU8() << 8) + (materia_file.readU8() << 16);
+        data.element = materia_file.readU8();
+        data.type_raw = materia_file.readU8();
+        for (int i = 0; i < 6; i ++) data.attribute[i] = materia_file.readU8();
+
+        // If the materia doesn't have a name, don't bother processing the rest of the data,
+        // it's not going to be added to the list.
+        if ("" == data.name) continue;
+
+        // Derive data from the read data.
+        data.id = i;
+        // Status. 3 bytes, so only the first 24 can be present.
+        if (data.status_raw == (data.status_raw | 0x000001)) data.status.push_back(DEATH);
+        if (data.status_raw == (data.status_raw | 0x000002)) data.status.push_back(NEAR_DEATH);
+        if (data.status_raw == (data.status_raw | 0x000004)) data.status.push_back(SLEEP);
+        if (data.status_raw == (data.status_raw | 0x000008)) data.status.push_back(POISONED);
+        if (data.status_raw == (data.status_raw | 0x000010)) data.status.push_back(SADNESS);
+        if (data.status_raw == (data.status_raw | 0x000020)) data.status.push_back(FURY);
+        if (data.status_raw == (data.status_raw | 0x000040)) data.status.push_back(CONFU);
+        if (data.status_raw == (data.status_raw | 0x000080)) data.status.push_back(SILENCE);
+        if (data.status_raw == (data.status_raw | 0x000100)) data.status.push_back(HASTE);
+        if (data.status_raw == (data.status_raw | 0x000200)) data.status.push_back(SLOW);
+        if (data.status_raw == (data.status_raw | 0x000400)) data.status.push_back(STOP);
+        if (data.status_raw == (data.status_raw | 0x000800)) data.status.push_back(FROG);
+        if (data.status_raw == (data.status_raw | 0x001000)) data.status.push_back(SMALL);
+        if (data.status_raw == (data.status_raw | 0x002000)) data.status.push_back(SLOW_NUMB);
+        if (data.status_raw == (data.status_raw | 0x004000)) data.status.push_back(PETRIFY);
+        if (data.status_raw == (data.status_raw | 0x008000)) data.status.push_back(REGEN);
+        if (data.status_raw == (data.status_raw | 0x010000)) data.status.push_back(BARRIER);
+        if (data.status_raw == (data.status_raw | 0x020000)) data.status.push_back(M_BARRIER);
+        if (data.status_raw == (data.status_raw | 0x040000)) data.status.push_back(REFLECT);
+        if (data.status_raw == (data.status_raw | 0x080000)) data.status.push_back(DUAL);
+        if (data.status_raw == (data.status_raw | 0x100000)) data.status.push_back(SHIELD);
+        if (data.status_raw == (data.status_raw | 0x200000)) data.status.push_back(D_SENTENCE);
+        if (data.status_raw == (data.status_raw | 0x400000)) data.status.push_back(MANIPULATE);
+        if (data.status_raw == (data.status_raw | 0x800000)) data.status.push_back(BERSERK);
+        // Limit type to materia color.
+        data.type = data.type_raw & 15; // Lower nybble.
+        switch (data.type){
+            case 0x09:
+            case 0x0A:
+                data.type = static_cast<int>(MATERIA_TYPE::MAGIC);
+                break;
+            case 0x05:
+                data.type = static_cast<int>(MATERIA_TYPE::SUPPORT);
+                break;
+            case 0x02:
+            case 0x03:
+            case 0x06:
+            case 0x07:
+            case 0x08:
+                data.type = static_cast<int>(MATERIA_TYPE::COMMAND);
+                break;
+            case 0x0B:
+            case 0x0C:
+                data.type = static_cast<int>(MATERIA_TYPE::SUMMON);
+                break;
+            default:
+                data.type = static_cast<int>(MATERIA_TYPE::INDEPENDENT);
+        }
+        // Stat bonuses. Manually done.
+        data.stats.str = 0;
+        data.stats.vit = 0;
+        data.stats.mag = 0;
+        data.stats.spr = 0;
+        data.stats.dex = 0;
+        data.stats.lck = 0;
+        data.stats.hp = 0;
+        data.stats.mp = 0;
+        data.stats.change = false;
+        switch (data.stat_raw){
+            case 0x01:
+                data.stats.change = true;
+                data.stats.str = -2;
+                data.stats.vit = -1;
+                data.stats.mag = 2;
+                data.stats.spr = 1;
+                data.stats.dex = 0;
+                data.stats.lck = 0;
+                data.stats.hp = -5;
+                data.stats.mp = 5;
+                break;
+            case 0x02:
+                data.stats.change = true;
+                data.stats.str = -4;
+                data.stats.vit = -2;
+                data.stats.mag = 4;
+                data.stats.spr = 2;
+                data.stats.hp = -10;
+                data.stats.mp = 10;
+                break;
+            case 0x03:
+                data.stats.change = true;
+                data.stats.dex = 2;
+                data.stats.lck = -2;
+                break;
+            case 0x04:
+                data.stats.change = true;
+                data.stats.str = -1;
+                data.stats.vit = -1;
+                data.stats.mag = 1;
+                data.stats.spr = 1;
+                break;
+            case 0x05:
+                data.stats.change = true;
+                data.stats.str = 1;
+                data.stats.vit = 1;
+                data.stats.mag = -1;
+                data.stats.spr = -1;
+                break;
+            case 0x06:
+                data.stats.change = true;
+                data.stats.vit = 1;
+                break;
+            case 0x07:
+                data.stats.change = true;
+                data.stats.lck = 1;
+                break;
+            case 0x08:
+                data.stats.change = true;
+                data.stats.lck = -1;
+                break;
+            case 0x09:
+                data.stats.change = true;
+                data.stats.dex = -2;
+                break;
+            case 0x0A:
+                data.stats.change = true;
+                data.stats.dex = 2;
+                break;
+            case 0x0B:
+                data.stats.change = true;
+                data.stats.str = -1;
+                data.stats.mag = 1;
+                data.stats.hp = -2;
+                data.stats.mp = 2;
+                break;
+            case 0x0C:
+                data.stats.change = true;
+                data.stats.mag = 1;
+                data.stats.hp = -2;
+                data.stats.mp = 2;
+                break;
+            case 0x0D:
+                data.stats.change = true;
+                data.stats.mag = 1;
+                data.stats.spr = 1;
+                data.stats.hp = -5;
+                data.stats.mp = 5;
+                break;
+            case 0x0E:
+                data.stats.change = true;
+                data.stats.mag = 2;
+                data.stats.spr = 2;
+                data.stats.hp = -10;
+                data.stats.mp = 10;
+                break;
+            case 0x0F:
+                data.stats.change = true;
+                data.stats.mag = 4;
+                data.stats.spr = 4;
+                data.stats.hp = -10;
+                data.stats.mp = 15;
+                break;
+            case 0x10:
+                data.stats.change = true;
+                data.stats.mag = 8;
+                data.stats.spr = 8;
+                data.stats.hp = -10;
+                data.stats.mp = 20;
+                break;
+        }
+
+        // Add to the list of materia.
+        materia_.push_back(data);
+        materia_count ++;
+    }
+
+    return materia_count;
+}
+
+void KernelDataInstaller::WriteMateria(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("materias"));
+    for (MateriaData materia : materia_){
+        std::unique_ptr<TiXmlElement> xml_materia(new TiXmlElement("materia"));
+        xml_materia->SetAttribute("id", materia.id);
+        xml_materia->SetAttribute("name", materia.name);
+        xml_materia->SetAttribute("description", materia.description);
+        xml_materia->SetAttribute("type", materia.type);
+        // Levels.
+        std::unique_ptr<TiXmlElement> xml_levels(new TiXmlElement("levels"));
+        std::unique_ptr<TiXmlElement> xml_level(new TiXmlElement("level"));
+        xml_level->SetAttribute("level", 1);
+        xml_level->SetAttribute("ap", 0);
+        xml_levels->LinkEndChild(xml_level.release());
+        for (int i = 0; i < 4; i ++){
+            if (materia.level_up_ap[i] < 65535){
+                std::unique_ptr<TiXmlElement> xml_level(new TiXmlElement("level"));
+                xml_level->SetAttribute("level", i + 2);
+                xml_level->SetAttribute("ap", materia.level_up_ap[i] * 100);
+                xml_levels->LinkEndChild(xml_level.release());
+            }
+        }
+
+        xml_materia->LinkEndChild(xml_levels.release());
+
+        // Statuses.
+        if (materia.status.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_status(new TiXmlElement("statuses"));
+            for (int s : materia.status){
+                std::unique_ptr<TiXmlElement> xml_status_status(new TiXmlElement("status"));
+                xml_status_status->SetAttribute("id", s);
+                xml_status->LinkEndChild(xml_status_status.release());
+            }
+            xml_materia->LinkEndChild(xml_status.release());
+        }
+        // Add elements.
+        if (materia.element != 0xFF){
+            std::unique_ptr<TiXmlElement> xml_elements(new TiXmlElement("elements"));
+            std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("element"));
+            xml_element->SetAttribute("id", materia.element);
+            xml_elements->LinkEndChild(xml_element.release());
+            xml_materia->LinkEndChild(xml_elements.release());
+        }
+        if (materia.stats.change == true){
+            std::unique_ptr<TiXmlElement> xml_stats(new TiXmlElement("stat_bonuses"));
+            // TODO: Lookup stat IDs.
+            if (materia.stats.str != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 0);
+                xml_stat->SetAttribute("bonus", materia.stats.str);
+                xml_stat->SetAttribute("mode", "abs");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            if (materia.stats.vit != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 1);
+                xml_stat->SetAttribute("bonus", materia.stats.vit);
+                xml_stat->SetAttribute("mode", "abs");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            if (materia.stats.mag != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 2);
+                xml_stat->SetAttribute("bonus", materia.stats.mag);
+                xml_stat->SetAttribute("mode", "abs");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            if (materia.stats.spr != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 3);
+                xml_stat->SetAttribute("bonus", materia.stats.spr);
+                xml_stat->SetAttribute("mode", "abs");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            if (materia.stats.dex != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 4);
+                xml_stat->SetAttribute("bonus", materia.stats.dex);
+                xml_stat->SetAttribute("mode", "abs");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            if (materia.stats.lck != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 5);
+                xml_stat->SetAttribute("bonus", materia.stats.lck);
+                xml_stat->SetAttribute("mode", "abs");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            if (materia.stats.hp != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 6);
+                xml_stat->SetAttribute("bonus", materia.stats.hp);
+                xml_stat->SetAttribute("mode", "rel");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            if (materia.stats.mp != 0){
+                std::unique_ptr<TiXmlElement> xml_stat(new TiXmlElement("stat_bonus"));
+                xml_stat->SetAttribute("stat", 7);
+                xml_stat->SetAttribute("bonus", materia.stats.mp);
+                xml_stat->SetAttribute("mode", "rel");
+                xml_stats->LinkEndChild(xml_stat.release());
+            }
+            xml_materia->LinkEndChild(xml_stats.release());
+        }
+        container->LinkEndChild(xml_materia.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}
+
+int KernelDataInstaller::ReadKeyItems(){
+
+    // Empty the item list
+    key_items_.clear();
+
+    File key_items_name_file = kernel_.ExtractGZip(KERNEL_KEY_ITEM_NAMES);
+    File key_items_desc_file = kernel_.ExtractGZip(KERNEL_KEY_ITEM_DESCRIPTIONS);
+    int name_offset = 0;
+    int desc_offset = 0;
+    int key_item_count = 0;
+
+    for (int i = 0; i < 128; i ++){
+
+        KeyItemData data;
+
+        // From the current name file offset, read 16 bytes.
+        // This is the pointer to the offset for the item name.
+        // Start reading there until 0xFF is found.
+        data.name = "";
+        u8 ch = 1;
+        name_offset = key_items_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = key_items_name_file.GetU8(name_offset);
+            if (ch != 0xFF) data.name += ENGLISH_CHARS[ch];
+            name_offset ++;
+        }
+
+        // For descriptions, do the same as for names.
+        data.description = "";
+        ch = 1;
+        desc_offset = key_items_desc_file.readU16LE();
+        while (ch != 0xFF){
+            ch = key_items_desc_file.GetU8(desc_offset);
+            if (ch != 0xFF){
+                // Warning: If the char is 0xF9, special function!
+                // the next byte (in bits, aaoooooo) means to read aa data at offset oooooo.
+                // This is to compress information in the kernel.
+                if (ch == 0xF9){
+                    // Get how much to read, two upper bits of the next byte.
+                    // Multiply by 2 and add 4. That's just how it is.
+                    u8 comp_size = (key_items_desc_file.GetU8(desc_offset + 1) >> 6) * 2 + 4;
+                    // Get new offset, lower six bits of the next byte.
+                    // The offset goes backwards from the position of the 0xF9 byte.
+                    u8 comp_offset = key_items_desc_file.GetU8(desc_offset + 1) & 63;
+
+                    for (int i = 0; i < comp_size; i ++){
+                        data.description += ENGLISH_CHARS[
+                          key_items_desc_file.GetU8(desc_offset - 1 - comp_offset + i)
+                        ];
+                    }
+                    desc_offset ++;
+                }
+                else data.description += ENGLISH_CHARS[ch];
+            }
+            desc_offset ++;
+        }
+
+        // If the key item doesn't have a name, it means all key items have been read.
+        // break the loop.
+        if ("" == data.name) break;
+
+        // Add to the list of items.
+        data.id = i;
+        key_items_.push_back(data);
+        key_item_count ++;
+    }
+
+    return key_item_count;
+}
+
+void KernelDataInstaller::WriteKeyItems(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("key_items"));
+    for (KeyItemData key_item : key_items_){
+        std::unique_ptr<TiXmlElement> xml_key_item(new TiXmlElement("key_item"));
+        xml_key_item->SetAttribute("id", key_item.id);
+        xml_key_item->SetAttribute("name", key_item.name);
+        xml_key_item->SetAttribute("description", key_item.description);
+        container->LinkEndChild(xml_key_item.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}
+
+int KernelDataInstaller::ReadSummonNames(){
+
+    // Empty the summon list
+    key_items_.clear();
+
+    File summon_name_file = kernel_.ExtractGZip(KERNEL_SUMMON_ATTACK_NAMES);
+    int name_offset = 0;
+    int summon_count = 0;
+
+    for (int i = 0; i < 16; i ++){ // There are exactly 16.
+
+        SummonNameData data;
+
+        // From the current name file offset, read 16 bytes.
+        // This is the pointer to the offset for the item name.
+        // Start reading there until 0xFF is found.
+        data.name = "";
+        u8 ch = 1;
+        name_offset = summon_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = summon_name_file.GetU8(name_offset);
+            if (ch != 0xFF) data.name += ENGLISH_CHARS[ch];
+            name_offset ++;
+        }
+
+        // If the key item doesn't have a name, it means all key items have been read.
+        // break the loop.
+        if ("" == data.name) break;
+
+        // Add to the list of items.
+        data.id = i;
+        summon_names_.push_back(data);
+        summon_count ++;
+    }
+
+    return summon_count;
+}
+
+void KernelDataInstaller::WriteSummonNames(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("summons"));
+    for (SummonNameData summon : summon_names_){
+        std::unique_ptr<TiXmlElement> xml_summon(new TiXmlElement("summon"));
+        xml_summon->SetAttribute("id", summon.id);
+        xml_summon->SetAttribute("name", summon.name);
+        container->LinkEndChild(xml_summon.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}
+
+void KernelDataInstaller::ReadInitialSaveMap(){
+
+    // Empty the item list
+    attacks_.clear();
+
+    File savemap_file = kernel_.ExtractGZip(KERNEL_ATTACK_DATA);
+
+    savemap_.checksum = savemap_file.readU32LE();
+    for (int i = 0; i < 68; i ++) savemap_.preview[i] = savemap_file.readU8();
+    savemap_.window_colour.top_left.red = savemap_file.readU8();
+    savemap_.window_colour.top_left.green = savemap_file.readU8();
+    savemap_.window_colour.top_left.blue = savemap_file.readU8();
+    savemap_.window_colour.top_right.red = savemap_file.readU8();
+    savemap_.window_colour.top_right.green = savemap_file.readU8();
+    savemap_.window_colour.top_right.blue = savemap_file.readU8();
+    savemap_.window_colour.top_left.red = savemap_file.readU8();
+    savemap_.window_colour.bottom_left.green = savemap_file.readU8();
+    savemap_.window_colour.bottom_left.blue = savemap_file.readU8();
+    savemap_.window_colour.bottom_right.red = savemap_file.readU8();
+    savemap_.window_colour.bottom_right.green = savemap_file.readU8();
+    savemap_.window_colour.bottom_right.blue = savemap_file.readU8();
+    for (int i = 0; i < 9; i ++){
+        savemap_.characters[i].identifier = savemap_file.readU8();
+        savemap_.characters[i].level = savemap_file.readU8();
+        savemap_.characters[i].str = savemap_file.readU8();
+        savemap_.characters[i].vit = savemap_file.readU8();
+        savemap_.characters[i].mag = savemap_file.readU8();
+        savemap_.characters[i].spr = savemap_file.readU8();
+        savemap_.characters[i].dex = savemap_file.readU8();
+        savemap_.characters[i].lck = savemap_file.readU8();
+        savemap_.characters[i].str_bonus = savemap_file.readU8();
+        savemap_.characters[i].vit_bonus = savemap_file.readU8();
+        savemap_.characters[i].mag_bonus = savemap_file.readU8();
+        savemap_.characters[i].spr_bonus = savemap_file.readU8();
+        savemap_.characters[i].dex_bonus = savemap_file.readU8();
+        savemap_.characters[i].lck_bonus = savemap_file.readU8();
+        savemap_.characters[i].limit_level = savemap_file.readU8();
+        savemap_.characters[i].limit_bar = savemap_file.readU8();
+        bool name_end = false;
+        savemap_.characters[i].name = "";
+        for (int j = 0; j < 12; j ++){
+            savemap_.characters[i].name_raw[i] = savemap_file.readU8();
+            if (name_end == false){
+                if (savemap_.characters[i].name_raw[i] != 0XFF){
+                    savemap_.characters[i].name
+                      += ENGLISH_CHARS[savemap_.characters[i].name_raw[i]];
+                }
+                else name_end = true;
+            }
+        }
+        savemap_.characters[i].weapon = savemap_file.readU8();
+        savemap_.characters[i].armor = savemap_file.readU8();
+        savemap_.characters[i].accessory = savemap_file.readU8();
+        savemap_.characters[i].status_raw = savemap_file.readU8();
+        if (savemap_.characters[i].status_raw == (savemap_.characters[i].status_raw | 0x10)){
+            savemap_.characters[i].sadness = true;
+            savemap_.characters[i].fury = false;
+        }
+        else if (savemap_.characters[i].status_raw == (savemap_.characters[i].status_raw | 0x20)){
+            savemap_.characters[i].sadness = false;
+            savemap_.characters[i].fury = true;
+        }
+        else{
+            savemap_.characters[i].sadness = false;
+            savemap_.characters[i].fury = false;
+        }
+        savemap_.characters[i].row_raw = savemap_file.readU8();
+        if (savemap_.characters[i].row_raw == 0xFE) savemap_.characters[i].back_row = true;
+        else savemap_.characters[i].back_row = false;
+        savemap_.characters[i].level_progress = savemap_file.readU8();
+        savemap_.characters[i].learned_limits_raw = savemap_file.readU16LE();
+    }
+
+}
+
+void KernelDataInstaller::WriteInitialSaveMap(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("attacks"));
+    for (AttackData attack : attacks_){
+        std::unique_ptr<TiXmlElement> xml_attack(new TiXmlElement("attack"));
+        xml_attack->SetAttribute("id", attack.id);
+        xml_attack->SetAttribute("accuracy", attack.accuracy);
+        xml_attack->SetAttribute("impact_effect", attack.impact_effect);
+        xml_attack->SetAttribute("hurt_anim", attack.hurt_anim);
+        xml_attack->SetAttribute("mp", attack.mp);
+        xml_attack->SetAttribute("sounds", attack.sound);
+        xml_attack->SetAttribute("camera_1", attack.camera_single);
+        xml_attack->SetAttribute("camera_x", attack.camera_multiple);
+        xml_attack->SetAttribute("effect", attack.effect);
+        xml_attack->SetAttribute("dmg_formula", attack.damage_formula);
+        xml_attack->SetAttribute("dmg_modifier", attack.damage_modifier);
+        xml_attack->SetAttribute("power", attack.power);
+        xml_attack->SetAttribute("restore", attack.restore_type);
+        xml_attack->SetAttribute("target_select", attack.target.selection_enabled);
+        xml_attack->SetAttribute("target_default_enemy", attack.target.default_enemy);
+        xml_attack->SetAttribute("target_default_multiple", attack.target.default_multiple);
+        xml_attack->SetAttribute("target_toggle_multiple", attack.target.toggle_multiple);
+        xml_attack->SetAttribute("target_fixed", attack.target.fixed_row);
+        xml_attack->SetAttribute("target_short_range", attack.target.short_range);
+        xml_attack->SetAttribute("target_all", attack.target.all_rows);
+        xml_attack->SetAttribute("target_random", attack.target.random);
+        // Statuses.
+        if (attack.status.status.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_status(new TiXmlElement("statuses"));
+            xml_status->SetAttribute("mode", attack.status.mode);
+            xml_status->SetAttribute("chance", attack.status.chance);
+            for (int s : attack.status.status){
+                std::unique_ptr<TiXmlElement> xml_status_status(new TiXmlElement("status"));
+                xml_status_status->SetAttribute("id", s);
+                xml_status->LinkEndChild(xml_status_status.release());
+            }
+            xml_attack->LinkEndChild(xml_status.release());
+        }
+        // Add elements.
+        if (attack.elements.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_elements(new TiXmlElement("elements"));
+            for (int s : attack.elements){
+                std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("element"));
+                xml_element->SetAttribute("id", s);
+                xml_elements->LinkEndChild(xml_element.release());
+            }
+            xml_attack->LinkEndChild(xml_elements.release());
+        }
+
+        container->LinkEndChild(xml_attack.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}