Browse Source

Installer: Improved kernel parser for item data. Most item values are now correctly read and written to a XML file.

Iñigo Valentin 3 years ago
parent
commit
92929b7a81

+ 2 - 0
V-Gears-Installer/CMakeLists.txt

@@ -93,6 +93,7 @@ set(HEADER_FILES
     include/ModelsAndAnimationsDb.h
     include/ScopedLgp.h
     include/SpawnPointDb.h
+    include/KernelDataInstaller.h
 )
 
 
@@ -165,6 +166,7 @@ set(SOURCE_FILES
     src/decompiler/world/WorldEngine.cpp
     src/ModelsAndAnimationsDb.cpp
     src/ScopedLgp.cpp
+    src/KernelDataInstaller.cpp
 )
 
 # For SUDM. TODO: Add elses.

+ 3 - 3
V-Gears-Installer/include/DataInstaller.h

@@ -23,9 +23,9 @@
 #include "data/VGearsTriggersFile.h"
 #include "data/VGearsFLevelFile.h"
 #include "FieldTextWriter.h"
+#include "KernelDataInstaller.h"
 #include "ModelsAndAnimationsDb.h"
 #include "ScopedLgp.h"
-#include "KernelFile.h"
 #include "SpawnPointDb.h"
 
 // TODO: Separate classes in files.
@@ -241,9 +241,9 @@ class DataInstaller{
         std::unique_ptr<ScopedLgp> fields_lgp_;
 
         /**
-         * BIN/GZ Kernel files.
+         * The installer for kernel data.
          */
-        std::unique_ptr<KernelFile> kernel_bin_;
+        std::unique_ptr<KernelDataInstaller> kernel_installer_;
 
         /**
          * LGP archive with texture data.

+ 51 - 153
V-Gears-Installer/include/KernelFile.h → V-Gears-Installer/include/KernelDataInstaller.h

@@ -15,15 +15,42 @@
 
 #pragma once
 
-#include <tinyxml.h>
 #include "common/BinGZipFile.h"
 #include "common/TypeDefine.h"
 #include "Characters.h"
 
-class KernelFile{
+class KernelDataInstaller{
 
     public:
 
+        /**
+         * Constructor.
+         *
+         * @param[in] path Absolute path to the KERNEL.BIN file
+         */
+        KernelDataInstaller(std::string path);
+
+        /**
+         * Destructor.
+         */
+        ~KernelDataInstaller();
+
+        /**
+         * Reads the items from the kernel.
+         *
+         * @return The number of items read.
+         */
+        int ReadItems();
+
+        /**
+         * Saves item data to an xml file
+         *
+         * @param file[in] Absolute path to the target XML file.
+         */
+        void WriteItems(std::string file);
+
+    private:
+
         enum KERNEL_SECTIONS{
 
             /**
@@ -291,6 +318,14 @@ class KernelFile{
              */
             bool default_multiple;
 
+            /**
+             * The selection can change between groups and individuals.
+             *
+             * When true, the target can be changed between groups and individuals When false,
+             * only {@see default_multiple} determines if the target is an individual or a group.
+             */
+            bool toggle_multiple;
+
             /**
              * The target group can be changed.
              *
@@ -496,7 +531,7 @@ class KernelFile{
             /**
              * The character has deathforce.
              */
-            DEATHFORCE,
+            DEATH_FORCE,
 
             /**
              * The character has resist.
@@ -638,7 +673,7 @@ class KernelFile{
             /**
              * Special flags. 1 byte.
              *
-             * @tod Document and retrieve with info from
+             * @todo Document and retrieve with info from
              * https://wiki.ffrtt.ru/index.php?title=FF7/Battle/Attack_Special_Effects
              */
             u8 additional_effects_raw;
@@ -646,13 +681,17 @@ class KernelFile{
             /**
              * Special flags. 1 byte.
              *
-             * @todop Document and retrieve with info from
+             * @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.
+             * 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;
 
@@ -755,155 +794,14 @@ class KernelFile{
 
         };
 
-        KernelFile(std::string path): kernel_(path){}
-
-        ~KernelFile(){}
-
-        int ReadItems(){
-
-            // Empty the item list
-            items_.clear();
-
-            //kernel_ = BinGZipFile(folder_path + "/KERNEL.BIN");
-            //BinGZipFile kernel_ = BinGZipFile(folder_path + "/KERNEL.BIN");
-            //std::cout << "KERNEL FILES " << kernel_.GetNumberOfFiles() << "\n";
-            File items_file = kernel_.ExtractGZip(KERNEL_ITEM_DATA);
-            File items_name_file = kernel_.ExtractGZip(KERNEL_ITEM_NAMES);
-            File items_desc_file = kernel_.ExtractGZip(KERNEL_ITEM_DESCRIPTIONS);
-            int name_offset = 0;
-            int desc_offset = 0;
-            int item_count = 0;
-
-            std::cout <<
-              "|  ID | cam | sel | bat | men | tar | eff | dmg | pow | con | sch | eff | efm | sta | elm |  spe  |\n";
-
-            for (int i = 0; i < 128; i ++){
-
-                ItemData 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 = items_name_file.readU16LE();
-                while (ch != 0xFF){
-                    ch = 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 = items_desc_file.readU16LE();
-                while (ch != 0xFF){
-                    ch = 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 = (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 = items_desc_file.GetU8(desc_offset + 1) & 63;
-
-                            for (int i = 0; i < comp_size; i ++){
-                                data.description += ENGLISH_CHARS[
-                                  items_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.unknown_0 = items_file.readU32LE();
-                //std::cout << "1st read " << data.unknown_0 << "\n";
-                data.unknown_1 = items_file.readU32LE();
-                data.camera = items_file.readU16LE();
-                data.restrict_raw = items_file.readU16LE();
-                data.target_raw = items_file.readU8();
-                data.effect = items_file.readU8();
-                data.damage_raw = items_file.readU8();
-                data.power = items_file.readU8();
-                data.condition_raw = items_file.readU8();
-                data.status_change_raw = items_file.readU8();
-                data.additional_effects_raw = items_file.readU8();
-                data.additional_effects_mod_raw = items_file.readU8();
-                data.status_raw = items_file.readU32LE();
-                data.element_raw = items_file.readU16LE();
-                data.special_raw = items_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));
-
-                // Add to the list of items.
-                items_.push_back(data);
-                item_count ++;
-
-                printf(
-                  "| %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %3d | %5d | ",
-                  i, data.camera,
-                  data.sellable, data.useable_battle, data.useable_menu, 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, data.element_raw, data.special_raw
-                );
-                std::cout << data.name << ": " << data.description << std::endl;
-
-
-            }
-
-            std::cout << "READ ITEMS END" << std::endl;
-            return item_count;
-        }
-
-        void WriteItems(std::string file){
-            std::cout << "WRITE ITEMS START" << std::endl;
-            TiXmlDocument xml;
-            std::unique_ptr<TiXmlElement> container(new TiXmlElement("items"));
-            for (ItemData item : items_){
-                std::unique_ptr<TiXmlElement> xml_item(new TiXmlElement("item"));
-                xml_item->SetAttribute("id", item.id);
-                xml_item->SetAttribute("name", item.name);
-                xml_item->SetAttribute("description", item.description);
-                xml_item->SetAttribute("sell", item.sellable);
-                xml_item->SetAttribute("battle", item.useable_battle);
-                xml_item->SetAttribute("menu", item.useable_menu);
-                xml_item->SetAttribute("effect", item.effect);
-                xml_item->SetAttribute("dmg_formula", item.damage_formula);
-                xml_item->SetAttribute("dmg_modifier", item.damage_modifier);
-                xml_item->SetAttribute("power", item.power);
-                container->LinkEndChild(xml_item.release());
-
-            }
-            xml.LinkEndChild(container.release());
-            xml.SaveFile(file);
-            std::cout << "WRITE ITEMS END" << std::endl;
-        }
-
-    private:
-
+        /**
+         * Items read from the kernel
+         */
         std::vector<ItemData> items_;
 
-
+        /**
+         * The kernel file.
+         */
         BinGZipFile kernel_;
 
-        //BinGZipFile kernel_2_;*/
-
 };

+ 6 - 4
V-Gears-Installer/src/DataInstaller.cpp

@@ -94,12 +94,14 @@ int DataInstaller::Progress(){
             fields_lgp_ = std::make_unique<ScopedLgp>(
               application_.getRoot(), input_dir_ + "field/flevel.lgp", "LGP", "FFVIIFields"
             );
-            kernel_bin_ = std::make_unique<KernelFile>(input_dir_ += "kernel/KERNEL.BIN");
-            if (kernel_bin_->ReadItems() > 0){
+            kernel_installer_ = std::make_unique<KernelDataInstaller>(
+              input_dir_ += "kernel/KERNEL.BIN"
+            );
+            if (kernel_installer_->ReadItems() > 0){
                 CreateDir("game");
-                kernel_bin_->WriteItems(output_dir_ + "game/items.xml");
+                kernel_installer_->WriteItems(output_dir_ + "game/items.xml");
             }
-            //exit(0);
+            exit(0);
 
             installation_state_ = SPAWN_POINTS_AND_SCALE_FACTORS_INIT;
             // TODO: DEBUG:

+ 293 - 0
V-Gears-Installer/src/KernelDataInstaller.cpp

@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+
+
+#include <tinyxml.h>
+#include "KernelDataInstaller.h"
+
+KernelDataInstaller::KernelDataInstaller(std::string path): kernel_(path){}
+
+KernelDataInstaller::~KernelDataInstaller(){}
+
+int KernelDataInstaller::ReadItems(){
+
+    // Empty the item list
+    items_.clear();
+
+    File items_file = kernel_.ExtractGZip(KERNEL_ITEM_DATA);
+    File items_name_file = kernel_.ExtractGZip(KERNEL_ITEM_NAMES);
+    File items_desc_file = kernel_.ExtractGZip(KERNEL_ITEM_DESCRIPTIONS);
+    int name_offset = 0;
+    int desc_offset = 0;
+    int item_count = 0;
+
+    std::cout <<
+      "|  ID | tar | eff | dmg | pow | con | sch | eff | efm | sta | elm |  spe  |\n";
+
+    for (int i = 0; i < 128; i ++){
+
+        ItemData 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 = items_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = 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 = items_desc_file.readU16LE();
+        while (ch != 0xFF){
+            ch = 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 = (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 = items_desc_file.GetU8(desc_offset + 1) & 63;
+
+                    for (int i = 0; i < comp_size; i ++){
+                        data.description += ENGLISH_CHARS[
+                          items_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.unknown_0 = items_file.readU32LE();
+        data.unknown_1 = items_file.readU32LE();
+        data.camera = items_file.readU16LE();
+        data.restrict_raw = items_file.readU16LE();
+        data.target_raw = items_file.readU8();
+        data.effect = items_file.readU8();
+        data.damage_raw = items_file.readU8();
+        data.power = items_file.readU8();
+        data.condition_raw = items_file.readU8();
+        data.status_change_raw = items_file.readU8();
+        data.additional_effects_raw = items_file.readU8();
+        data.additional_effects_mod_raw = items_file.readU8();
+        data.status_raw = items_file.readU32LE();
+        data.element_raw = items_file.readU16LE();
+        data.special_raw = items_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));
+        // 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.
+        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.party_mp = data.condition_raw & (1 << 0);
+        data.condition.party_status = data.condition_raw & (2 << 0); // ... last bit.
+
+        data.status.chance = 64 - (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;
+
+
+        // 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.
+        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;
+}
+
+void KernelDataInstaller::WriteItems(std::string file){
+    TiXmlDocument xml;
+    std::unique_ptr<TiXmlElement> container(new TiXmlElement("items"));
+    for (ItemData item : items_){
+        std::unique_ptr<TiXmlElement> xml_item(new TiXmlElement("item"));
+        xml_item->SetAttribute("id", item.id);
+        xml_item->SetAttribute("name", item.name);
+        xml_item->SetAttribute("description", item.description);
+        xml_item->SetAttribute("camera", item.camera);
+        xml_item->SetAttribute("sell", item.sellable);
+        xml_item->SetAttribute("battle", item.useable_battle);
+        xml_item->SetAttribute("menu", item.useable_menu);
+        xml_item->SetAttribute("effect", item.effect);
+        xml_item->SetAttribute("dmg_formula", item.damage_formula);
+        xml_item->SetAttribute("dmg_modifier", item.damage_modifier);
+        xml_item->SetAttribute("power", item.power);
+        //if (item.status.status.size() > 0 && item.status.chance <= 64){
+        if (item.status.status.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_status(new TiXmlElement("statuses"));
+            xml_status->SetAttribute("mode", item.status.mode);
+            xml_status->SetAttribute("chance", item.status.chance);
+            for (int s : item.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_item->LinkEndChild(xml_status.release());
+        }
+        // Add elements.
+        // In KERNEL.BIN, Non elemental item actually have all elements marked. Don't print them.
+        //if (item.elements.size() > 0 && item.elements.size() < 16){
+        if (item.elements.size() > 0){
+            std::unique_ptr<TiXmlElement> xml_elements(new TiXmlElement("elements"));
+            for (int s : item.elements){
+                std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("element"));
+                xml_element->SetAttribute("id", s);
+                xml_elements->LinkEndChild(xml_element.release());
+            }
+            xml_item->LinkEndChild(xml_elements.release());
+        }
+
+        container->LinkEndChild(xml_item.release());
+
+    }
+    xml.LinkEndChild(container.release());
+    xml.SaveFile(file);
+}