Преглед изворни кода

Installer: The Kernel parser now extracts character, attack, character growth and weapon data.

Iñigo Valentin пре 3 година
родитељ
комит
f400307aa9

+ 1 - 1
README.md

@@ -1,4 +1,4 @@
-# <img src="doc/v-gears.png" alt="V-Gears" width="50"/> V-Gears
+# <img src="v-gears.png" alt="V-Gears" width="50"/> V-Gears
 
 ## What is V-Gears? 
 

+ 883 - 9
V-Gears-Installer/include/KernelDataInstaller.h

@@ -35,6 +35,60 @@ class KernelDataInstaller{
          */
         ~KernelDataInstaller();
 
+        /**
+         * Reads the command data from the kernel.
+         *
+         * @return The number of commands read.
+         */
+        int ReadCommands();
+
+        /**
+         * Saves command data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteCommands(std::string file);
+
+        /**
+         * Reads the attack data from the kernel.
+         *
+         * @return The number of attacks read.
+         */
+        int ReadAttacks();
+
+        /**
+         * Saves command data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteAttacks(std::string file);
+
+        /**
+         * Reads character data from the kernel.
+         *
+         * @return The number of characters read.
+         */
+        int ReadCharacters();
+
+        /**
+         * Saves character data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteCharacters(std::string file);
+
+        /**
+         * Reads growth data from the kernel.
+         */
+        void ReadGrowth();
+
+        /**
+         * Saves growth data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteGrowth(std::string file);
+
         /**
          * Reads the items from the kernel.
          *
@@ -50,18 +104,18 @@ class KernelDataInstaller{
         void WriteItems(std::string file);
 
         /**
-         * Reads the command from the kernel.
+         * Reads the weapon info from the kernel.
          *
-         * @return The number of items read.
+         * @return The number of weapons read.
          */
-        int ReadCommands();
+        int ReadWeapons();
 
         /**
-         * Saves command data to an xml file
+         * Saves weapon data to an xml file
          *
          * @param file[in] Absolute path to the target XML file.
          */
-        void WriteCommands(std::string file);
+        void WriteWeapons(std::string file);
 
     private:
 
@@ -681,6 +735,198 @@ class KernelDataInstaller{
 
         };
 
+        /**
+         * Data for each attack.
+         *
+         * As found in KERNEL.BIN, file 1, in order, up to camera_multiple. Members after
+         * special_raw are not found literally in KERNEL.BIN, but are derived from other
+         * members.
+         */
+        struct AttackData{
+
+            /**
+             * Probability of the attack to land. 1 byte.
+             */
+            u8 accuracy;
+
+            /**
+             * Impact effect. 1 byte.
+             *
+             * The sprite shown when the attack hits.
+             */
+            u8 impact_effect;
+
+            /**
+             * Index of the animation played by the target when hit. 1 byte.
+             */
+            u8 hurt_anim;
+
+            /**
+             * Unknown data. 1 bytes.
+             */
+            u8 unknown;
+
+            /**
+             * MP cost. 2 bytes.
+             *
+             * Determines the camera movement when the command is executed over a single target.
+             */
+            u16 mp;
+
+            /**
+             * Impact sound. 2 bytes.
+             *
+             * Determines the camera movement when the command is executed over multiple targets.
+             */
+            u16 sound;
+
+            /**
+             * Camera movement ID for single target. 2 bytes.
+             *
+             * Determines the camera movement when the command is executed over a single target.
+             */
+            u16 camera_single;
+
+            /**
+             * Camera movement ID for multiple target. 2 bytes.
+             *
+             * Determines the camera movement when the command is executed over multiple targets.
+             */
+            u16 camera_multiple;
+
+            /**
+             * Targeting mode. 1 byte.
+             *
+             * See {@see target} and {@TargetModes} for more information.
+             */
+            u8 target_raw;
+
+            /**
+             * Atack effect ID. 1 byte.
+             *
+             * Used for animation in battle.
+             */
+            u8 effect;
+
+            /**
+             * Damage formula. 1 byte.
+             *
+             * This byte is divided into two nybbles (four bits). Upper nybble determines what
+             * considerations are made in calculating damage such as physical/magical, allowing
+             * criticals, how to calculate accuracy, etc. There are also three sets of known
+             * formulae that are paired with the upper nybble values. These are selected in the
+             * Lower Nybble and determine how to calculate pre-defense damage
+             */
+            u8 damage_raw;
+
+            /**
+             * The attack power. 1 byte.
+             *
+             * Used for damage/healing calculation.
+             */
+            u8 power;
+
+            /**
+             * The restore type.
+             *
+             * See {@restore_typ} and {@RESTORE_TYPE} for more information.
+             */
+            u8 condition_raw;
+
+            /**
+             * Information about status change mode and chance. 1 bytes.
+             */
+            u8 status_change_raw;
+
+            /**
+             * Special flags. 1 byte.
+             *
+             * @todo Document and retrieve with info from
+             * https://wiki.ffrtt.ru/index.php?title=FF7/Battle/Attack_Special_Effects
+             */
+            u8 additional_effects_raw;
+
+            /**
+             * Special flags. 1 byte.
+             *
+             * @todo Document and retrieve with info from
+             * https://wiki.ffrtt.ru/index.php?title=FF7/Item_data
+             */
+            u8 additional_effects_mod_raw;
+
+            /**
+             * Information about what status can be inflicted or cured. 4 bytes.
+             *
+             * The first six bits are the chance of inflicting/curing (out of 63). If the seventh
+             * bit is set, the status is cured instead of inflicted. If the eighth bit is set, then
+             * the status is toggled (cured if present, inflicted if not present).
+             */
+            u32 status_raw;
+
+            /**
+             * Information about the item elements. 2 bytes.
+             */
+            u16 element_raw;
+
+            /**
+             * Special flags. 2 bytes.
+             *
+             * @todo document and parse with info from
+             * https://wiki.ffrtt.ru/index.php?title=FF7/Battle/Special_Attack_Flags
+             */
+            u16 special_raw;
+
+            /**
+             * Attack ID.
+             *
+             * Not actually in the item file data, it's the order at which it appears.
+             */
+            int id;
+
+            /**
+             * Target selection mode.
+             *
+             * Derived from {@see target_raw}.
+             */
+            Target target;
+
+            /**
+             * The damage formula.
+             *
+             * It's the upper nybble in {@see damage_raw}.
+             */
+            u8 damage_formula;
+
+            /**
+             * The damage modifier.
+             *
+             * It's the lower nybble in {@see damage_raw}.
+             */
+            u8 damage_modifier;
+
+            /**
+             * What the attack restores when used.
+             *
+             * Same as {@see condition_raw}, except for the wrong cases.
+             */
+            RESTORE_TYPE restore_type;
+
+            /**
+             * Status effects.
+             *
+             * Derived from {@see status_change_raw} and {@see status_raw}.
+             */
+            StatusEffect status;
+
+            /**
+             * Elements of the attack.
+             *
+             * Derived from {@see element_raw}.
+             */
+            std::vector<int> elements;
+
+        };
+
         /**
          * Data for each items
          *
@@ -884,14 +1130,642 @@ class KernelDataInstaller{
         };
 
         /**
-         * Items read from the kernel
+         * Materia slot types.
          */
-        std::vector<CommandData> commands_;
+        enum SLOT_TYPE{
+
+            /**
+             * A non linked slot.
+             */
+            SLOT_UNLINKED = 0,
+
+            /**
+             * A linked slot.
+             *
+             * If the slot is in an even position, it's linked to the one in the right. If it's in
+             * an odd position, it;s linked to the one in the Left. Remember that slot positions
+             * are 0-index.
+             */
+            SLOT_LINKED = 1
+        };
 
         /**
-         * Items read from the kernel
+         * Bonus in stat givven by weapons or armor.
          */
-        std::vector<ItemData> items_;
+        struct StatBonus{
+
+            /**
+             * ID of the raised stat.
+             */
+            u8 stat;
+
+            /**
+             * Stat value raise.
+             */
+            u32 bonus;
+        };
+
+        /**
+         * Data for each weapon
+         *
+         * As found in KERNEL.BIN, file 5, in order, up to special. Members after special are not
+         * found literally in KERNEL.BIN, but are derived from other members or found in other
+         * files of the kernel (text files).
+         */
+        struct WeaponData{
+
+            /**
+             * Targeting mode. 1 byte.
+             *
+             * See {@see target} and {@TargetModes} for more information.
+             */
+            u8 target_raw;
+
+            /**
+             * Effect ID, unused (Always 0XFFFF). 1 byte.
+             */
+            u8 unused_0;
+
+            /**
+             * Damage formula. 1 byte.
+             *
+             * This byte is divided into two nybbles (four bits). Upper nybble determines what
+             * considerations are made in calculating damage such as physical/magical, allowing
+             * criticals, how to calculate accuracy, etc. There are also three sets of known
+             * formulae that are paired with the upper nybble values. These are selected in the
+             * Lower Nybble and determine how to calculate pre-defense damage
+             */
+            u8 damage_raw;
+
+            /**
+             * Unused data (Always 0XFFFF). 1 byte.
+             */
+            u8 unused_1;
+
+            /**
+             * The weapon power. 1 byte.
+             *
+             * Used for damage calculation.
+             */
+            u8 power;
+
+            /**
+             * The status effect the weapon induces. 1 byte.
+             */
+            u8 status;
+
+            /**
+             * Weapon materia growth multiplier. 1 byte.
+             */
+            u8 growth;
+
+            /**
+             * Weapon critical chance. 1 byte.
+             */
+            u8 critical;
+
+            /**
+             * Weapon accuracy. 1 byte.
+             */
+            u8 accuracy;
+
+            /**
+             * Weapon battle model. 1 byte.
+             *
+             * This byte is divided into two nybbles (four bits). Upper nybble determines the
+             * attack animation modifier, and it's only used for Barret and Vincent. The lower
+             * nybble is actually the index of the weapon model.
+             */
+            u8 model_raw;
+
+            /**
+             * Unused data (Always 0XFF). 1 byte.
+             */
+            u8 unused_2;
+
+            /**
+             * Consider it unused.
+             *
+             * @todo Verify.
+             */
+            u8 high_sound;
+
+            /**
+             * Camera movement ID. 2 bytes.
+             *
+             * Used for both single and multiple targeted attacks with this weapon. Always 0xFFFF.
+             */
+            u16 camera;
+
+            /**
+             * Characters that can equip the weapon. 2 bytes.
+             *
+             * Contains bits indicating the characters that can equip the weapon {@see character}
+             * for more info.
+             */
+            u16 equip_raw;
+
+            /**
+             * Weapon element id.
+             */
+            u16 element_raw;
+
+            /**
+             * Unused data (Always 0XFFFF). 1 byte.
+             */
+            u8 unused_3;
+
+            /**
+             * Stat the weapon raises.
+             *
+             * {@see stat_bonus} for more info.
+             */
+            u32 stat_raw;
+
+            /**
+             * The amount the raised stats are raised by.
+             *
+             * {@see stat_bonus} for more info.
+             */
+            u32 stat_bonus_raw;
+
+            /**
+             * Materia slots.
+             *
+             * {@see slots} for more info.
+             */
+            u8 slots_raw[8];
+
+            /**
+             * Sound effect id for normal hit.
+             */
+            u8 sound;
+
+            /**
+             * Sound effect id for critical hit.
+             */
+            u8 sound_critical;
+
+            /**
+             * Sound effect id for miss hit.
+             */
+            u8 sound_miss;
+
+            /**
+             * Impcat effect ID. 1 byte.
+             *
+             * Used for animation in battle.
+             */
+            u8 effect;
+
+            /**
+             * Special flags. 2 bytes.
+             *
+             * @todo document and parse with info from
+             * https://wiki.ffrtt.ru/index.php?title=FF7/Battle/Special_Attack_Flags
+             */
+            u16 special_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;
+
+            /**
+             * Weapon ID.
+             *
+             * Not actually in the item file data, it's the order at which it appears.
+             */
+            int id;
+
+            /**
+             * Weapon name.
+             *
+             * Not found in the same file as the rest of the data, but on file 20.
+             */
+            std::string name;
+
+            /**
+             * Weapon description.
+             *
+             * Not found in the same file as the rest of the data, but on file 12.
+             */
+            std::string description;
+
+            /**
+             * Indicates if the weapon can be sold.
+             *
+             * True if the bit 0 in {@see restrict_raw} is 0.
+             */
+            bool sellable;
+
+            /**
+             * Indicates if the weapon can be used in battle.
+             *
+             * True if the bit 2 in {@see restrict_raw} is 0.
+             */
+            bool useable_battle;
+
+            /**
+             * Indicates if the weapon can be used in the menu.
+             *
+             * True if the bit 4 in {@see restrict_raw} is 0.
+             */
+            bool useable_menu;
+
+            /**
+             * Indicates if the weapon can be sold.
+             *
+             * True if the bit 8 in {@see restrict_raw} is 0.
+             */
+            bool throwable;
+
+            /**
+             * Target selection mode.
+             *
+             * Derived from {@see target_raw}.
+             */
+            Target target;
+
+            /**
+             * The damage formula.
+             *
+             * It's the upper nybble in {@see damage_raw}.
+             */
+            u8 damage_formula;
+
+            /**
+             * The damage modifier.
+             *
+             * It's the lower nybble in {@see damage_raw}.
+             */
+            u8 damage_modifier;
+
+            /**
+             * Attack animation ID.
+             *
+             * It's the upper nybble in {@see model_raw}. It's only used for Barret and Vincent.
+             */
+            u8 animation_index;
+
+            /**
+             * Weapon model ID.
+             *
+             * It's the lower nybble in {@see model_raw}.
+             */
+            u8 model;
+
+            /**
+             * 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;
+
+        };
+
+        /**
+         * Character data.
+         *
+         * Contains information about a character. Contained in the file 3 of the kernel, one
+         * entry for each character. Each section is 56 bytes long. The character ID is not
+         * actually in the data, but it's the order at which they appear. The character name is
+         * also not in the dataand  it must be filled manually.
+         */
+        struct CharacterData{
+
+            /**
+             * Strength stat growth curve. 1 byte.
+             */
+            u8 curve_str;
+
+            /**
+             * Vitality stat growth curve. 1 byte.
+             */
+            u8 curve_vit;
+
+            /**
+             * Magic stat growth curve. 1 byte.
+             */
+            u8 curve_mag;
+
+            /**
+             * Spirit stat growth curve. 1 byte.
+             */
+            u8 curve_spr;
+
+            /**
+             * Dexterity stat growth curve. 1 byte.
+             */
+            u8 curve_dex;
+
+            /**
+             * Luck stat growth curve. 1 byte.
+             */
+            u8 curve_lck;
+
+            /**
+             * HP stat growth curve. 1 byte.
+             */
+            u8 curve_hp;
+
+            /**
+             * MP stat growth curve. 1 byte.
+             */
+            u8 curve_mp;
+
+            /**
+             * Experience-for-next-level growth curve. 1 byte.
+             */
+            u8 curve_exp;
+
+            /**
+             * Ignored data. 1 byte.
+             */
+            u8 unused_0;
+
+            /**
+             * Starting level. 1 byte.
+             */
+            u8 initial_level;
+
+            /**
+             * Ignored data. 1 byte.
+             */
+            u8 unused_1;
+
+            /**
+             * 1-1 limit slot command. 1 byte.
+             */
+            u8 limit_1_1;
+
+            /**
+             * 1-2 limit slot command. 1 byte.
+             */
+            u8 limit_1_2;
+
+            /**
+             * 1-3 limit slot command. 1 byte, unused.
+             */
+            u8 limit_1_3;
+
+            /**
+             * 2-1 limit slot command. 1 byte.
+             */
+            u8 limit_2_1;
+
+            /**
+             * 2-2 limit slot command. 1 byte.
+             */
+            u8 limit_2_2;
+
+            /**
+             * 2-3 limit slot command. 1 byte, unused.
+             */
+            u8 limit_2_3;
+
+            /**
+             * 3-1 limit slot command. 1 byte.
+             */
+            u8 limit_3_1;
+
+            /**
+             * 3-2 limit slot command. 1 byte.
+             */
+            u8 limit_3_2;
+
+            /**
+             * 3-3 limit slot command. 1 byte, unused.
+             */
+            u8 limit_3_3;
+
+            /**
+             * 4-1 limit slot command. 1 byte.
+             */
+            u8 limit_4_1;
+
+            /**
+             * 4-2 limit slot command. 1 byte, unused.
+             */
+            u8 limit_4_2;
+
+            /**
+             * 4-3 limit slot command. 1 byte, unused.
+             */
+            u8 limit_4_3;
+
+            /**
+             * Kills required to unlock limit level 2. 2 bytes.
+             */
+            u16 limit_2_kills;
+
+            /**
+             * Kills required to unlock limit level 3. 2 bytes.
+             */
+            u16 limit_3_kills;
+
+            /**
+             * Kills required to unlock limit 1-2. 2 bytes.
+             */
+            u16 limit_1_2_uses;
+
+            /**
+             * Kills required to unlock limit 1-3. 2 bytes, unused.
+             */
+            u16 limit_1_3_uses;
+
+            /**
+             * Kills required to unlock limit 2-2. 2 bytes.
+             */
+            u16 limit_2_2_uses;
+
+            /**
+             * Kills required to unlock limit 2-3. 2 bytes, unused.
+             */
+            u16 limit_2_3_uses;
+
+            /**
+             * Kills required to unlock limit 3-2. 2 bytes.
+             */
+            u16 limit_3_2_uses;
+
+            /**
+             * Kills required to unlock limit 3-3. 2 bytes, unused.
+             */
+            u16 limit_3_3_uses;
+
+            /**
+             * HP divisor for limit level 1.
+             */
+            u32 limit_1_div;
+
+            /**
+             * HP divisor for limit level 2.
+             */
+            u32 limit_2_div;
+
+            /**
+             * HP divisor for limit level 3.
+             */
+            u32 limit_3_div;
+
+            /**
+             * HP divisor for limit level 4.
+             */
+            u32 limit_4_div;
+
+            /**
+             * Character ID.
+             */
+            int id;
+
+            /**
+             * Character default name.
+             */
+            std::string name;
+
+        };
+
+        /**
+         * Types of stat curves.
+         *
+         * Althought any of them can be used for any stat, this is their intended use.
+         */
+        enum STAT_CURVE_TYPE{
+
+            /**
+             * Normal curve, used for primary stat.
+             */
+            CURVE_PRIMARY = 0,
+
+            /**
+             * HP curve. Base values must be multiplied by 40 when calculating.
+             */
+            CURVE_HP,
+
+            /**
+             * MP curve. Base values must be multiplied by 2 when calculating.
+             */
+            CURVE_MP,
+
+            /**
+             * Experience curve. Base must be ignored when calculating. Gradient is quadratic.
+             */
+            CURVE_EXP
+
+        };
+
+        /**
+         * Stat curve.
+         *
+         * determines value growth
+         */
+        struct StatCurve{
+
+            /**
+             * Curve ID.
+             */
+            int id;
+
+            /**
+             * Type of curve.
+             */
+            STAT_CURVE_TYPE type;
+
+            /**
+             * Curve gradients.
+             */
+            u8 gradient[8];
+
+            /**
+             * Curve bases.
+             */
+            u8 base[8];
+
+        };
+
+        /**
+         * Battle data.
+         *
+         * Contains several pieces of data related to character growth. Contained in the file 3 of
+         * the kernel, after the character data (starts at offset 0x01F8). The kernel contains more
+         * data, but it's not needed for V-Gears (random table, scene lookup...)
+         */
+        struct GrowthData{
+
+            /**
+             * Random bonus to primary stats, 12 entries of 1 byte.
+             */
+            u8 bonus_stat[12];
+
+            /**
+             * Random bonus to HP stat, 12 entries of 1 byte.
+             */
+            u8 bonus_hp[12];
+
+            /**
+             * Random bonus to MP stat, 12 entries of 1 byte.
+             */
+            u8 bonus_mp[12];
+
+            /**
+             * Stat curves. 64 curves of 16 bit each.
+             *
+             * 37 for primary stats, 9 for HP, 9 form MP, 9 for experience.
+             */
+            StatCurve curves[64];
+        };
+
+        /**
+         * Items read from the kernel.
+         */
+        std::vector<CommandData> commands_;
+
+        /**
+         * Attacks read from the kernel.
+         */
+        std::vector<AttackData> attacks_;
+
+        /**
+         * Character info read from the kernel.
+         */
+        std::vector<CharacterData> characters_;
+
+        GrowthData growth_;
+
+        /**
+         * Items read from the kernel.
+         */
+        std::vector<ItemData> items_;
+
+        /**
+         * Weapons read from the kernel.
+         */
+        std::vector<WeaponData> weapons_;
 
         /**
          * The kernel file.

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

@@ -101,10 +101,24 @@ int DataInstaller::Progress(){
                 CreateDir("game");
                 kernel_installer_->WriteCommands(output_dir_ + "game/commands.xml");
             }
+            if (kernel_installer_->ReadAttacks() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteAttacks(output_dir_ + "game/attacks.xml");
+            }
+            if (kernel_installer_->ReadCharacters() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteCharacters(output_dir_ + "game/characters.xml");
+            }
             if (kernel_installer_->ReadItems() > 0){
                 CreateDir("game");
                 kernel_installer_->WriteItems(output_dir_ + "game/items.xml");
             }
+            kernel_installer_->ReadGrowth();
+            kernel_installer_->WriteGrowth(output_dir_ + "game/growth.xml");
+            if (kernel_installer_->ReadWeapons() > 0){
+                CreateDir("game");
+                kernel_installer_->WriteWeapons(output_dir_ + "game/weapons.xml");
+            }
             exit(0);
 
             installation_state_ = SPAWN_POINTS_AND_SCALE_FACTORS_INIT;

+ 758 - 1
V-Gears-Installer/src/KernelDataInstaller.cpp

@@ -16,6 +16,7 @@
 
 #include <tinyxml.h>
 #include "KernelDataInstaller.h"
+#include "common/FinalFantasy7/FF7NameLookup.h"
 
 KernelDataInstaller::KernelDataInstaller(std::string path): kernel_(path){}
 
@@ -33,7 +34,9 @@ int KernelDataInstaller::ReadCommands(){
     int desc_offset = 0;
     int command_count = 0;
 
-    std::cout << "|  ID | cur | c_s | c_m| tar |\n";
+    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.
@@ -142,6 +145,509 @@ void KernelDataInstaller::WriteCommands(std::string file){
     xml.SaveFile(file);
 }
 
+int KernelDataInstaller::ReadAttacks(){
+
+    // Empty the item list
+    attacks_.clear();
+
+    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;
+
+        // Read data from the item data section.
+        data.accuracy = attacks_file.readU8();
+        data.impact_effect = attacks_file.readU8();
+        data.hurt_anim = attacks_file.readU8();
+        data.unknown = attacks_file.readU8();
+        data.mp = attacks_file.readU16LE();
+        data.sound = attacks_file.readU16LE();
+        data.camera_single = attacks_file.readU16LE();
+        data.camera_multiple = attacks_file.readU16LE();
+        data.target_raw = attacks_file.readU8();
+        data.effect = attacks_file.readU8();
+        data.damage_raw = attacks_file.readU8();
+        data.power = attacks_file.readU8();
+        data.condition_raw = attacks_file.readU8();
+        data.status_change_raw = attacks_file.readU8();
+        data.additional_effects_raw = attacks_file.readU8();
+        data.additional_effects_mod_raw = attacks_file.readU8();
+        data.status_raw = attacks_file.readU32LE();
+        data.element_raw = attacks_file.readU16LE();
+        data.special_raw = attacks_file.readU16LE();
+
+        // Derive data from the read data.
+        data.id = i;
+        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;
+        else data.target.default_enemy = false;
+        if (data.target_raw == (data.target_raw | 0x04)) data.target.default_multiple = true;
+        else data.target.default_multiple = false;
+        if (data.target_raw == (data.target_raw | 0x08)) data.target.toggle_multiple = true;
+        else data.target.toggle_multiple = false;
+        if (data.target_raw == (data.target_raw | 0x10)) data.target.fixed_row = true;
+        else data.target.fixed_row = false;
+        if (data.target_raw == (data.target_raw | 0x20)) data.target.short_range = true;
+        else data.target.short_range = false;
+        if (data.target_raw == (data.target_raw | 0x40)) data.target.default_enemy = true;
+        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.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;
+            case 2: data.restore_type = RESTORE_STATUS; break;
+            default: data.restore_type = RESTORE_NONE;
+        }
+        // In KERNEL.BIN, items that don't induce statuses actually have all statuses marked, and
+        // a chance of 255. If it's the case, skip statuses altogether.
+        if (data.status.chance <= 64){
+            // TODO: Im fairly sure that this can be done with a single-line loop...
+            // ... but I don't know how
+            if (data.status_raw == (data.status_raw | 0x00000001))
+                data.status.status.push_back(DEATH);
+            if (data.status_raw == (data.status_raw | 0x00000002))
+                data.status.status.push_back(NEAR_DEATH);
+            if (data.status_raw == (data.status_raw | 0x00000004))
+                data.status.status.push_back(SLEEP);
+            if (data.status_raw == (data.status_raw | 0x00000008))
+                data.status.status.push_back(POISONED);
+            if (data.status_raw == (data.status_raw | 0x00000010))
+                data.status.status.push_back(SADNESS);
+            if (data.status_raw == (data.status_raw | 0x00000020))
+                data.status.status.push_back(FURY);
+            if (data.status_raw == (data.status_raw | 0x00000040))
+                data.status.status.push_back(CONFU);
+            if (data.status_raw == (data.status_raw | 0x00000080))
+                data.status.status.push_back(SILENCE);
+            if (data.status_raw == (data.status_raw | 0x00000100))
+                data.status.status.push_back(HASTE);
+            if (data.status_raw == (data.status_raw | 0x00000200))
+                data.status.status.push_back(SLOW);
+            if (data.status_raw == (data.status_raw | 0x00000400))
+                data.status.status.push_back(STOP);
+            if (data.status_raw == (data.status_raw | 0x00000800))
+                data.status.status.push_back(FROG);
+            if (data.status_raw == (data.status_raw | 0x00001000))
+                data.status.status.push_back(SMALL);
+            if (data.status_raw == (data.status_raw | 0x00002000))
+                data.status.status.push_back(SLOW_NUMB);
+            if (data.status_raw == (data.status_raw | 0x00004000))
+                data.status.status.push_back(PETRIFY);
+            if (data.status_raw == (data.status_raw | 0x00008000))
+                data.status.status.push_back(REGEN);
+            if (data.status_raw == (data.status_raw | 0x00010000))
+                data.status.status.push_back(BARRIER);
+            if (data.status_raw == (data.status_raw | 0x00020000))
+                data.status.status.push_back(M_BARRIER);
+            if (data.status_raw == (data.status_raw | 0x00040000))
+                data.status.status.push_back(REFLECT);
+            if (data.status_raw == (data.status_raw | 0x00080000))
+                data.status.status.push_back(DUAL);
+            if (data.status_raw == (data.status_raw | 0x00100000))
+                data.status.status.push_back(SHIELD);
+            if (data.status_raw == (data.status_raw | 0x00200000))
+                data.status.status.push_back(D_SENTENCE);
+            if (data.status_raw == (data.status_raw | 0x00400000))
+                data.status.status.push_back(MANIPULATE);
+            if (data.status_raw == (data.status_raw | 0x00800000))
+                data.status.status.push_back(BERSERK);
+            if (data.status_raw == (data.status_raw | 0x01000000))
+                data.status.status.push_back(PEERLESS);
+            if (data.status_raw == (data.status_raw | 0x02000000))
+                data.status.status.push_back(PARALYSIS);
+            if (data.status_raw == (data.status_raw | 0x04000000))
+                data.status.status.push_back(DARKNESS);
+            if (data.status_raw == (data.status_raw | 0x08000000))
+                data.status.status.push_back(DUAL_DRAIN);
+            if (data.status_raw == (data.status_raw | 0x10000000))
+                data.status.status.push_back(DEATH_FORCE);
+            if (data.status_raw == (data.status_raw | 0x20000000))
+                data.status.status.push_back(RESIST);
+            if (data.status_raw == (data.status_raw | 0x40000000))
+                data.status.status.push_back(LUCKY_GIRL);
+            if (data.status_raw == (data.status_raw | 0x80000000))
+                data.status.status.push_back(IMPRISONED);
+        }
+        // Get elements
+        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 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;
+}
+
+void KernelDataInstaller::WriteAttacks(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);
+}
+
+int KernelDataInstaller::ReadCharacters(){
+
+    // TODO: Do something about young Cloud and Sephiroth.
+
+    // Empty the item list
+    characters_.clear();
+
+    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;
+
+        // Read data from the item data section.
+        data.curve_str = characters_file.readU8();
+        data.curve_vit = characters_file.readU8();
+        data.curve_mag = characters_file.readU8();
+        data.curve_spr = characters_file.readU8();
+        data.curve_dex = characters_file.readU8();
+        data.curve_lck = characters_file.readU8();
+        data.curve_hp = characters_file.readU8();
+        data.curve_mp = characters_file.readU8();
+        data.curve_exp = characters_file.readU8();
+        data.unused_0 = characters_file.readU8();
+        data.initial_level = characters_file.readU8();
+        data.unused_1 = characters_file.readU8();
+        data.limit_1_1 = characters_file.readU8();
+        data.limit_1_2 = characters_file.readU8();
+        data.limit_1_3 = characters_file.readU8();
+        data.limit_2_1 = characters_file.readU8();
+        data.limit_2_2 = characters_file.readU8();
+        data.limit_2_3 = characters_file.readU8();
+        data.limit_3_1 = characters_file.readU8();
+        data.limit_3_2 = characters_file.readU8();
+        data.limit_3_3 = characters_file.readU8();
+        data.limit_4_1 = characters_file.readU8();
+        data.limit_4_2 = characters_file.readU8();
+        data.limit_4_3 = characters_file.readU8();
+        data.limit_2_kills = characters_file.readU16LE();
+        data.limit_3_kills = characters_file.readU16LE();
+        data.limit_1_2_uses = characters_file.readU16LE();
+        data.limit_1_3_uses = characters_file.readU16LE();
+        data.limit_2_2_uses = characters_file.readU16LE();
+        data.limit_2_3_uses = characters_file.readU16LE();
+        data.limit_3_2_uses = characters_file.readU16LE();
+        data.limit_3_3_uses = characters_file.readU16LE();
+        data.limit_1_div = characters_file.readU32LE();
+        data.limit_2_div = characters_file.readU32LE();
+        data.limit_3_div = characters_file.readU32LE();
+        data.limit_4_div = characters_file.readU32LE();
+
+        // Derive data from the read data.
+        data.id = i;
+        data.name = VGears::NameLookup::CharName(i);
+
+        // 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;
+}
+
+void KernelDataInstaller::WriteCharacters(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("attacks"));
+    for (CharacterData character : characters_){
+        std::unique_ptr<TiXmlElement> xml_character(new TiXmlElement("attack"));
+        xml_character->SetAttribute("id", character.id);
+        xml_character->SetAttribute("name", character.name);
+        xml_character->SetAttribute("initial_level", character.initial_level);
+        // Curves.
+        std::unique_ptr<TiXmlElement> xml_curves(new TiXmlElement("curves"));
+        std::unique_ptr<TiXmlElement> xml_curve_str(new TiXmlElement("curve"));
+        xml_curve_str->SetAttribute("stat", "strength");
+        xml_curve_str->SetAttribute("id", character.curve_str);
+        xml_curves->LinkEndChild(xml_curve_str.release());
+        std::unique_ptr<TiXmlElement> xml_curve_vit(new TiXmlElement("curve"));
+        xml_curve_vit->SetAttribute("stat", "vitality");
+        xml_curve_vit->SetAttribute("id", character.curve_vit);
+        xml_curves->LinkEndChild(xml_curve_vit.release());
+        std::unique_ptr<TiXmlElement> xml_curve_mag(new TiXmlElement("curve"));
+        xml_curve_mag->SetAttribute("stat", "magic");
+        xml_curve_mag->SetAttribute("id", character.curve_mag);
+        xml_curves->LinkEndChild(xml_curve_mag.release());
+        std::unique_ptr<TiXmlElement> xml_curve_spr(new TiXmlElement("curve"));
+        xml_curve_spr->SetAttribute("stat", "spirit");
+        xml_curve_spr->SetAttribute("id", character.curve_spr);
+        xml_curves->LinkEndChild(xml_curve_spr.release());
+        std::unique_ptr<TiXmlElement> xml_curve_dex(new TiXmlElement("curve"));
+        xml_curve_dex->SetAttribute("stat", "dexterity");
+        xml_curve_dex->SetAttribute("id", character.curve_dex);
+        xml_curves->LinkEndChild(xml_curve_dex.release());
+        std::unique_ptr<TiXmlElement> xml_curve_lck(new TiXmlElement("curve"));
+        xml_curve_lck->SetAttribute("stat", "luck");
+        xml_curve_lck->SetAttribute("id", character.curve_lck);
+        xml_curves->LinkEndChild(xml_curve_lck.release());
+        std::unique_ptr<TiXmlElement> xml_curve_hp(new TiXmlElement("curve"));
+        xml_curve_hp->SetAttribute("stat", "hp");
+        xml_curve_hp->SetAttribute("id", character.curve_hp);
+        xml_curves->LinkEndChild(xml_curve_hp.release());
+        std::unique_ptr<TiXmlElement> xml_curve_mp(new TiXmlElement("curve"));
+        xml_curve_mp->SetAttribute("stat", "mp");
+        xml_curve_mp->SetAttribute("id", character.curve_mp);
+        xml_curves->LinkEndChild(xml_curve_mp.release());
+        std::unique_ptr<TiXmlElement> xml_curve_exp(new TiXmlElement("curve"));
+        xml_curve_exp->SetAttribute("stat", "experience");
+        xml_curve_exp->SetAttribute("id", character.curve_exp);
+        xml_curves->LinkEndChild(xml_curve_exp.release());
+        xml_character->LinkEndChild(xml_curves.release());
+        // Limits.
+        std::unique_ptr<TiXmlElement> xml_limits(new TiXmlElement("limits"));
+        // Limit level 1.
+        std::unique_ptr<TiXmlElement> xml_limit_1(new TiXmlElement("level"));
+        xml_limit_1->SetAttribute("level", 1);
+        xml_limit_1->SetAttribute("kills", 0);
+        xml_limit_1->SetAttribute("divisor", character.limit_1_div);
+        std::unique_ptr<TiXmlElement> xml_limit_1_1(new TiXmlElement("limit"));
+        xml_limit_1_1->SetAttribute("level", 1);
+        xml_limit_1_1->SetAttribute("uses", 0);
+        xml_limit_1_1->SetAttribute("command", character.limit_1_1);
+        xml_limit_1->LinkEndChild(xml_limit_1_1.release());
+        if (character.limit_1_2 < 255){ // Some characters only have 1 limit per level.
+            std::unique_ptr<TiXmlElement> xml_limit_1_2(new TiXmlElement("limit"));
+            xml_limit_1_2->SetAttribute("level", 2);
+            xml_limit_1_2->SetAttribute("uses", character.limit_1_2_uses);
+            xml_limit_1_2->SetAttribute("command", character.limit_1_2);
+            xml_limit_1->LinkEndChild(xml_limit_1_2.release());
+        }
+        xml_limits->LinkEndChild(xml_limit_1.release());
+        // Limit level 2.
+        if (character.limit_2_kills < 65535){
+            std::unique_ptr<TiXmlElement> xml_limit_2(new TiXmlElement("level"));
+            xml_limit_2->SetAttribute("level", 2);
+            xml_limit_2->SetAttribute("kills", character.limit_2_kills);
+            xml_limit_2->SetAttribute("divisor", character.limit_2_div);
+            std::unique_ptr<TiXmlElement> xml_limit_2_1(new TiXmlElement("limit"));
+            xml_limit_2_1->SetAttribute("level", 1);
+            xml_limit_2_1->SetAttribute("uses", 0);
+            xml_limit_2_1->SetAttribute("command", character.limit_2_1);
+            xml_limit_2->LinkEndChild(xml_limit_2_1.release());
+            if (character.limit_2_2 < 255){ // Some characters only have 1 limit per level.
+                std::unique_ptr<TiXmlElement> xml_limit_2_2(new TiXmlElement("limit"));
+                xml_limit_2_2->SetAttribute("level", 2);
+                xml_limit_2_2->SetAttribute("uses", character.limit_2_2_uses);
+                xml_limit_2_2->SetAttribute("command", character.limit_2_2);
+                xml_limit_2->LinkEndChild(xml_limit_2_2.release());
+            }
+            xml_limits->LinkEndChild(xml_limit_2.release());
+        }
+        // Limit level 3.
+        if (character.limit_3_kills < 65535 && character.limit_3_kills > 0){
+            std::unique_ptr<TiXmlElement> xml_limit_3(new TiXmlElement("level"));
+            xml_limit_3->SetAttribute("level", 3);
+            xml_limit_3->SetAttribute("kills", character.limit_3_kills);
+            xml_limit_3->SetAttribute("divisor", character.limit_3_div);
+            std::unique_ptr<TiXmlElement> xml_limit_3_1(new TiXmlElement("limit"));
+            xml_limit_3_1->SetAttribute("level", 1);
+            xml_limit_3_1->SetAttribute("uses", 0);
+            xml_limit_3_1->SetAttribute("command", character.limit_3_1);
+            xml_limit_3->LinkEndChild(xml_limit_3_1.release());
+            if (character.limit_3_2 < 255){ // Some characters only have 1 limit per level.
+                std::unique_ptr<TiXmlElement> xml_limit_3_2(new TiXmlElement("limit"));
+                xml_limit_3_2->SetAttribute("level", 2);
+                xml_limit_3_2->SetAttribute("uses", character.limit_3_2_uses);
+                xml_limit_3_2->SetAttribute("command", character.limit_3_2);
+                xml_limit_3->LinkEndChild(xml_limit_3_2.release());
+            }
+            xml_limits->LinkEndChild(xml_limit_3.release());
+        }
+        // Limit level 4.
+        if (character.limit_4_1 < 255){
+            std::unique_ptr<TiXmlElement> xml_limit_4(new TiXmlElement("level"));
+            xml_limit_4->SetAttribute("level", 4);
+            xml_limit_4->SetAttribute("kills", 0);
+            xml_limit_4->SetAttribute("divisor", character.limit_4_div);
+            std::unique_ptr<TiXmlElement> xml_limit_4_1(new TiXmlElement("limit"));
+            xml_limit_4_1->SetAttribute("level", 1);
+            xml_limit_4_1->SetAttribute("uses", 0);
+            xml_limit_4_1->SetAttribute("command", character.limit_4_1);
+            xml_limit_4->LinkEndChild(xml_limit_4_1.release());
+            xml_limits->LinkEndChild(xml_limit_4.release());
+        }
+
+        xml_character->LinkEndChild(xml_limits.release());
+
+        container->LinkEndChild(xml_character.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}
+
+void KernelDataInstaller::ReadGrowth(){
+
+    File growth_file = kernel_.ExtractGZip(KERNEL_BATTLE_AND_GROWTH_DATA);
+    growth_file.SetOffset(0x01F8); //Skip character data.
+
+    for (int i = 0; i < 12; i ++) growth_.bonus_stat[i] = growth_file.readU8();
+    for (int i = 0; i < 12; i ++) growth_.bonus_hp[i] = growth_file.readU8();
+    for (int i = 0; i < 12; i ++) growth_.bonus_mp[i] = growth_file.readU8();
+    for (int i = 0; i < 64; i ++){
+        growth_.curves[i].id = i;
+        if (i >=0 && i < 37) growth_.curves[i].type = CURVE_PRIMARY;
+        else if (i >=37 && i < 46) growth_.curves[i].type = CURVE_HP;
+        else if (i >=46 && i < 55) growth_.curves[i].type = CURVE_MP;
+        else growth_.curves[i].type = CURVE_EXP;
+        for (int j = 0; j < 8; j ++){
+            growth_.curves[i].gradient[j] = growth_file.readU8();
+            growth_.curves[i].base[j] = growth_file.readU8();
+        }
+    }
+
+}
+
+void KernelDataInstaller::WriteGrowth(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("growth"));
+    // Bonus.
+    std::unique_ptr<TiXmlElement> xml_bonuses(new TiXmlElement("stat_bonus"));
+    for (int i = 0; i < 12; i ++){
+        std::unique_ptr<TiXmlElement> xml_bonus(new TiXmlElement("bonus"));
+        xml_bonus->SetAttribute("stat", "primary");
+        xml_bonus->SetAttribute("value", growth_.bonus_stat[i]);
+        xml_bonuses->LinkEndChild(xml_bonus.release());
+    }
+    for (int i = 0; i < 12; i ++){
+        std::unique_ptr<TiXmlElement> xml_bonus(new TiXmlElement("bonus"));
+        xml_bonus->SetAttribute("stat", "hp");
+        xml_bonus->SetAttribute("value", growth_.bonus_hp[i]);
+        xml_bonuses->LinkEndChild(xml_bonus.release());
+    }
+    for (int i = 0; i < 12; i ++){
+        std::unique_ptr<TiXmlElement> xml_bonus(new TiXmlElement("bonus"));
+        xml_bonus->SetAttribute("stat", "mp");
+        xml_bonus->SetAttribute("value", growth_.bonus_mp[i]);
+        xml_bonuses->LinkEndChild(xml_bonus.release());
+    }
+    container->LinkEndChild(xml_bonuses.release());
+    // Curves
+    std::unique_ptr<TiXmlElement> xml_curves(new TiXmlElement("curves"));
+    for (int i = 0; i < 64; i ++){
+        std::unique_ptr<TiXmlElement> xml_curve(new TiXmlElement("curve"));
+        xml_curve->SetAttribute("id", growth_.curves[i].id);
+        xml_curve->SetAttribute("type", growth_.curves[i].type);
+        for (int j = 0; j < 8; j ++){
+            std::unique_ptr<TiXmlElement> xml_curve_pair(new TiXmlElement("pair"));
+            xml_curve_pair->SetAttribute("id", j);
+            xml_curve_pair->SetAttribute("gradient", growth_.curves[i].gradient[j]);
+            xml_curve_pair->SetAttribute("base", growth_.curves[i].base[j]);
+            xml_curve->LinkEndChild(xml_curve_pair.release());
+        }
+        xml_curves->LinkEndChild(xml_curve.release());
+    }
+    container->LinkEndChild(xml_curves.release());
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}
+
 int KernelDataInstaller::ReadItems(){
 
     // Empty the item list
@@ -155,6 +661,7 @@ int KernelDataInstaller::ReadItems(){
     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 ++){
@@ -444,3 +951,253 @@ void KernelDataInstaller::WriteItems(std::string file){
     xml.LinkEndChild(container.release());
     xml.SaveFile(file);
 }
+
+int KernelDataInstaller::ReadWeapons(){
+
+    // Empty the item list
+    weapons_.clear();
+
+    File weapons_file = kernel_.ExtractGZip(KERNEL_WEAPON_DATA);
+    File weapons_name_file = kernel_.ExtractGZip(KERNEL_WEAPON_NAMES);
+    File weapons_desc_file = kernel_.ExtractGZip(KERNEL_WEAPON_DESCRIPTIONS);
+    int name_offset = 0;
+    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;
+
+        // 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 = weapons_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = weapons_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 = weapons_desc_file.readU16LE();
+        while (ch != 0xFF){
+            ch = weapons_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 = (weapons_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 = weapons_desc_file.GetU8(desc_offset + 1) & 63;
+
+                    for (int i = 0; i < comp_size; i ++){
+                        data.description += ENGLISH_CHARS[
+                          weapons_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 item data section.
+        data.target_raw = weapons_file.readU8();
+        data.unused_0 = weapons_file.readU8();
+        data.damage_raw = weapons_file.readU8();
+        data.unused_1 = weapons_file.readU8();
+        data.power = weapons_file.readU8();
+        data.status = weapons_file.readU8();
+        data.growth = weapons_file.readU8();
+        data.critical = weapons_file.readU8();
+        data.accuracy = weapons_file.readU8();
+        data.model_raw = weapons_file.readU8();
+        data.unused_2 = weapons_file.readU8();
+        data.high_sound = weapons_file.readU8();
+        data.camera = weapons_file.readU16LE();
+        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 < 8; i ++) data.slots_raw[i] = weapons_file.readU8();
+        data.sound = weapons_file.readU8();
+        data.sound_critical = weapons_file.readU8();
+        data.sound_miss = weapons_file.readU8();
+        data.effect = weapons_file.readU8();
+        data.special_raw = weapons_file.readU16LE();
+        data.restrict_raw = weapons_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));
+        data.throwable = !(data.restrict_raw & (8 << 0));
+        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;
+        else data.target.default_enemy = false;
+        if (data.target_raw == (data.target_raw | 0x04)) data.target.default_multiple = true;
+        else data.target.default_multiple = false;
+        if (data.target_raw == (data.target_raw | 0x08)) data.target.toggle_multiple = true;
+        else data.target.toggle_multiple = false;
+        if (data.target_raw == (data.target_raw | 0x10)) data.target.fixed_row = true;
+        else data.target.fixed_row = false;
+        if (data.target_raw == (data.target_raw | 0x20)) data.target.short_range = true;
+        else data.target.short_range = false;
+        if (data.target_raw == (data.target_raw | 0x40)) data.target.default_enemy = true;
+        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.animation_index = data.model_raw >> 4; // Upper nybble.
+        data.model = data.model_raw & 15; // Lower nybble.
+        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);
+        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 < 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);
+            }
+        }
+        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.
+        weapons_.push_back(data);
+        weapon_count ++;
+    }
+
+    return weapon_count;
+}
+
+void KernelDataInstaller::WriteWeapons(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("weapons"));
+    for (WeaponData weapon : weapons_){
+        std::unique_ptr<TiXmlElement> xml_weapon(new TiXmlElement("weapon"));
+        xml_weapon->SetAttribute("id", weapon.id);
+        xml_weapon->SetAttribute("name", weapon.name);
+        xml_weapon->SetAttribute("description", weapon.description);
+        xml_weapon->SetAttribute("camera", weapon.camera);
+        xml_weapon->SetAttribute("sell", weapon.sellable);
+        xml_weapon->SetAttribute("battle", weapon.useable_battle);
+        xml_weapon->SetAttribute("menu", weapon.useable_menu);
+        xml_weapon->SetAttribute("throw", weapon.throwable);
+        xml_weapon->SetAttribute("effect", weapon.effect);
+        xml_weapon->SetAttribute("dmg_formula", weapon.damage_formula);
+        xml_weapon->SetAttribute("dmg_modifier", weapon.damage_modifier);
+        xml_weapon->SetAttribute("power", weapon.power);
+        xml_weapon->SetAttribute("growth", weapon.growth);
+        xml_weapon->SetAttribute("accuracy", weapon.accuracy);
+        xml_weapon->SetAttribute("critical", weapon.critical);
+        xml_weapon->SetAttribute("model", weapon.model);
+        xml_weapon->SetAttribute("animation", weapon.animation_index);
+        xml_weapon->SetAttribute("target_select", weapon.target.selection_enabled);
+        xml_weapon->SetAttribute("target_default_enemy", weapon.target.default_enemy);
+        xml_weapon->SetAttribute("target_default_multiple", weapon.target.default_multiple);
+        xml_weapon->SetAttribute("target_toggle_multiple", weapon.target.toggle_multiple);
+        xml_weapon->SetAttribute("target_fixed", weapon.target.fixed_row);
+        xml_weapon->SetAttribute("target_short_range", weapon.target.short_range);
+        xml_weapon->SetAttribute("target_all", weapon.target.all_rows);
+        xml_weapon->SetAttribute("target_random", weapon.target.random);
+        // Statuses.
+        if (weapon.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", weapon.status);
+            xml_status->LinkEndChild(xml_status_status.release());
+            xml_weapon->LinkEndChild(xml_status.release());
+        }
+        // Add elements.
+        if (weapon.elements.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_elements(new TiXmlElement("elements"));
+            for (int s : weapon.elements){
+                std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("element"));
+                xml_element->SetAttribute("id", s);
+                xml_elements->LinkEndChild(xml_element.release());
+            }
+            xml_weapon->LinkEndChild(xml_elements.release());
+        }
+        if (weapon.materia_slots.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_slots(new TiXmlElement("slots"));
+            for (u8 slot : weapon.materia_slots){
+                std::unique_ptr<TiXmlElement> xml_slot(new TiXmlElement("slot"));
+                xml_slot->SetAttribute("type", slot);
+                xml_slots->LinkEndChild(xml_slot.release());
+            }
+            xml_weapon->LinkEndChild(xml_slots.release());
+        }
+        if (weapon.equip.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_characters(new TiXmlElement("characters"));
+            for (int s : weapon.equip){
+                std::unique_ptr<TiXmlElement> xml_character(new TiXmlElement("character"));
+                xml_character->SetAttribute("id", s);
+                xml_characters->LinkEndChild(xml_character.release());
+            }
+            xml_weapon->LinkEndChild(xml_characters.release());
+        }
+        container->LinkEndChild(xml_weapon.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}

+ 10 - 0
V-Gears/include/common/File.h

@@ -156,6 +156,16 @@ class File{
          */
         u32 GetCurrentOffset();
 
+        /**
+         * Sets the file offset for reading.
+         *
+         * If the offset is larger than the file size, the offset will be set to the end of the
+         * file
+         *
+         * @param[in] offset The new offset.
+         */
+        void SetOffset(u32 offset);
+
     protected:
 
         /**

+ 0 - 1
V-Gears/include/common/FinalFantasy7/FF7NameLookup.h

@@ -245,7 +245,6 @@ namespace VGears{
                         const auto id = GetInt(node, "Id");
                         const auto name = GetString(node, "Name");
                         character_ids_[id] = name;
-                        node = node->NextSibling();
                     }
                     node = node->NextSibling();
                 }

+ 5 - 2
V-Gears/src/common/File.cpp

@@ -103,6 +103,9 @@ u32 File::readU32LE(){
     return data;
 }
 
-u32 File::GetCurrentOffset(){
-    return offset_;
+u32 File::GetCurrentOffset(){return offset_;}
+
+void File::SetOffset(u32 offset){
+    if (offset < buffer_size_) offset_ = offset;
+    else offset_ = buffer_size_ - 1;
 }