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

The kernel installer now extracts materia data with all of their attributes. Materia menu improved.

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

+ 72 - 5
V-Gears-Installer/include/KernelDataInstaller.h

@@ -260,9 +260,9 @@ class KernelDataInstaller{
             KERNEL_COMMAND_DESCRIPTIONS,
 
             /**
-             * Magic descriptions section in KERNEL.BIN.
+             * Attack descriptions section in KERNEL.BIN.
              */
-            KERNEL_MAGIC_DESCRIPTIONS,
+            KERNEL_ATTACK_DESCRIPTIONS,
 
             /**
              * Item descriptions section in KERNEL.BIN.
@@ -300,9 +300,9 @@ class KernelDataInstaller{
             KERNEL_COMMAND_NAMES,
 
             /**
-             * Magic Names section in KERNEL.BIN.
+             * Atack Names section in KERNEL.BIN.
              */
-            KERNEL_MAGIC_NAMES,
+            KERNEL_ATTACK_NAMES,
 
             /**
              * Item Names section in KERNEL.BIN.
@@ -971,6 +971,20 @@ class KernelDataInstaller{
              */
             int id;
 
+            /**
+             * Attack name.
+             *
+             * Not found in the same file as the rest of the data, but on file 18.
+             */
+            std::string name;
+
+            /**
+             * Attack description.
+             *
+             * Not found in the same file as the rest of the data, but on file 10.
+             */
+            std::string description;
+
             /**
              * Target selection mode.
              *
@@ -2020,12 +2034,19 @@ class KernelDataInstaller{
             std::string description;
 
             /**
-             * Mateira type.
+             * Materia type.
              *
              * Derived from {@see type_raw}.
              */
             int type;
 
+            /**
+             * Materia type.
+             *
+             * Derived from {@see type_raw}.
+             */
+            int sub_type;
+
             /**
              * Stat bonuses for equipping the materia.
              *
@@ -2039,6 +2060,42 @@ class KernelDataInstaller{
              * Derived from {@see status_raw}.
              */
             std::vector<int> status;
+
+            /**
+             * Commands the materia enables in battle.
+             *
+             * Derived from {@see type_raw}.
+             */
+            std::vector<int> command;
+
+            /**
+             * Attacks the materia enables in battle, at each level.
+             *
+             * Derived from {@see type_raw} and {@see attribute}.
+             */
+            int attack[6];
+
+            /**
+             * Number of times per battle an attack can be used, per level.
+             *
+             * -1 means infinite times. Derived from {@see type_raw} and {@see attribute}.
+             */
+            int attack_times[6];
+
+            /**
+             * Ability confered by the materia.
+             *
+             * Can be provided by independent and independent materia.
+             */
+            std::string ability;
+
+            /**
+             * Parameter for the ability, varies per level.
+             *
+             * It can be the number of times the ability can be executed, or the chance to
+             * activate...
+             */
+            int ability_parameters[5];
         };
 
         /**
@@ -2465,4 +2522,14 @@ class KernelDataInstaller{
          */
         u32 prices_[416];
 
+        /**
+         * Attack names, required for other sections.
+         */
+        std::string attack_names_[128];
+
+        /**
+         * Command names, required for other sections.
+         */
+        std::string command_names_[128];
+
 };

+ 275 - 2
V-Gears-Installer/src/KernelDataInstaller.cpp

@@ -125,6 +125,7 @@ int KernelDataInstaller::ReadCommands(){
 
         // Add to the list of items.
         if (data.name != ""){
+            command_names_[i] = data.name;
             commands_.push_back(data);
             command_count ++;
         }
@@ -153,11 +154,59 @@ int KernelDataInstaller::ReadAttacks(){
     attacks_.clear();
 
     File attacks_file = kernel_.ExtractGZip(KERNEL_ATTACK_DATA);
+    File attacks_name_file = kernel_.ExtractGZip(KERNEL_ATTACK_NAMES);
+    File attacks_desc_file = kernel_.ExtractGZip(KERNEL_ATTACK_DESCRIPTIONS);
     int attack_count = 0;
 
     for (int i = 0; i < 128; i ++){
 
         AttackData data;
+        int name_offset;
+        int desc_offset;
+
+        // 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 = attacks_name_file.readU16LE();
+        while (ch != 0xFF){
+            ch = attacks_name_file.GetU8(name_offset);
+            if (ch != 0xFF) data.name += ENGLISH_CHARS[ch];
+            name_offset ++;
+        }
+        boost::replace_all(data.name, "\"", "'");
+
+        // For descriptions, do the same as for names.
+        data.description = "";
+        ch = 1;
+        desc_offset = attacks_desc_file.readU16LE();
+        while (ch != 0xFF){
+            ch = attacks_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 = (attacks_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 = attacks_desc_file.GetU8(desc_offset + 1) & 63;
+
+                    for (int c = 0; c < comp_size; c ++){
+                        data.description += ENGLISH_CHARS[
+                          attacks_desc_file.GetU8(desc_offset - 1 - comp_offset + c)
+                        ];
+                    }
+                    desc_offset ++;
+                }
+                else data.description += ENGLISH_CHARS[ch];
+            }
+            desc_offset ++;
+        }
+        boost::replace_all(data.description, "\"", "'");
 
         // Read data from the item data section.
         data.accuracy = attacks_file.readU8();
@@ -306,6 +355,7 @@ int KernelDataInstaller::ReadAttacks(){
 
         // Add to the list of items.
         attacks_.push_back(data);
+        attack_names_[i] = data.name;
         attack_count ++;
     }
 
@@ -318,6 +368,8 @@ void KernelDataInstaller::WriteAttacks(std::string file_name){
     if (!file) return;
     for (AttackData attack : attacks_){
         file << "Game.Attacks[" << attack.id << "] = {\n"
+          << "    name = \"" << attack.name << "\",\n"
+          << "    description = \"" << attack.description << "\",\n"
           << "    accuracy = " << static_cast<int>(attack.accuracy) << ",\n"
           << "    impact_effect = " << static_cast<int>(attack.impact_effect) << ",\n"
           << "    hurt_anim = " << static_cast<int>(attack.hurt_anim) << ",\n"
@@ -1551,6 +1603,7 @@ int KernelDataInstaller::ReadMateria(){
         if (data.status_raw == (data.status_raw | 0x800000)) data.status.push_back(BERSERK);
         // Limit type to materia color.
         data.type = data.type_raw & 15; // Lower nybble.
+        data.sub_type = data.type_raw & 15; // Upper nyble.
         switch (data.type){
             case 0x09:
             case 0x0A:
@@ -1686,6 +1739,73 @@ int KernelDataInstaller::ReadMateria(){
                 break;
         }
 
+        // Attributtes. Very hardcoded!
+        /*u8 sub_type = data.type_raw >> 4;
+        u8 type = data.type_raw & 15;
+        switch (type){
+            case 0x09: // Magic, normal.
+            case 0x0A: // Magic, high tier.
+                // Always enable magic command.
+                // Each attribute is an attack unlocked each level.
+                data.command.push_back(2);
+                for (int a = 0; a < 6; a ++){
+                    if (data.attribute[i] == 0xFF) data.attack[i] = -1;
+                    else data.attack[i] = data.attribute[i];
+                    data.attack_times[i] = -1;
+                }
+                break;
+            case 0x05: //Support materia.
+                    switch (data.attribute[i]){
+                        case 0x54: data.ability = "Materia.ABILITY.COMMAND_COUNTER"; break;
+                        case 0x55: data.ability = "Materia.ABILITY.MAGIC_COUNTER"; break;
+                        case 0x56: data.ability = "Materia.ABILITY.SNEAK_ATTACK"; break;
+                        case 0x58: data.ability = "Materia.ABILITY.MP_TURBO"; break;
+                        case 0x59: data.ability = "Materia.ABILITY.HP_ABSORB"; break;
+                        case 0x5A: data.ability = "Materia.ABILITY.MP_ABSORB"; break;
+                        case 0x5C: data.ability = "Materia.ABILITY.ADDED_CUT"; break;
+                        case 0x5D: data.ability = "Materia.ABILITY.STEAL_AS_WELL"; break;
+                        case 0x5E: data.ability = "Materia.ABILITY.ELEMENTAL"; break;
+                        case 0x5F: data.ability = "Materia.ABILITY.ADDED_EFFECT"; break;
+                    }
+                    for (int a = 1; a < 6; a ++){
+                        if (data.attribute[i] != 0xFF)
+                            data.ability_parameters[a - 1] = data.attribute[i];
+                    }
+                break;
+            case 0x02: // Command, non-existent.
+                for (int a = 0; a < 6; a ++)
+                    if (data.attribute[i] != 0xFF) data.command.push_back(data.attribute[i]);
+                break;
+            case 0x03: // Command, W commands.
+                for (int a = 0; a < 6; a ++)
+                    if (data.attribute[i] != 0xFF) data.command.push_back(data.attribute[i]);
+                break;
+            case 0x06: // Command, replace attack, different by level.
+                for (int a = 0; a < 6; a ++)
+                    if (data.attribute[i] != 0xFF) data.command.push_back(data.attribute[i]);
+                break;
+            case 0x07: // E. Skill
+                data.command.push_back(13); break; // E.Skill.
+                break;
+            case 0x08: // Adds commands. Can be different by level.
+                for (int a = 0; a < 6; a ++)
+                    if (data.attribute[i] != 0xFF) data.command.push_back(data.attribute[i]);
+                break;
+            case 0x0B: // Summon, normal.
+            case 0x0C: // Summon, high tier.
+                // Always enable Summon command (3), always enable attack in attribute 0.
+                // Attributes 1-5 are the times the summon can be used per battle. They are always
+                // 1-5.
+                data.command.push_back(3);
+                //data.attack.push_back(data.attribute[i]);
+                for (int a = 1; a <= 6; a ++){
+                    data.attack_times[a - 1] = a;
+                }
+                break;
+            //default:
+                //data.type = static_cast<int>(MATERIA_TYPE::INDEPENDENT);
+        }*/
+
         // Add to the list of materia.
         materia_.push_back(data);
         materia_count ++;
@@ -1704,6 +1824,20 @@ void KernelDataInstaller::WriteMateria(std::string file_name){
           << "    name = \"" << materia.name << "\",\n"
           << "    description = \"" << materia.description << "\",\n"
           << "    type = " << materia.type << ",\n"
+
+
+        // TODO Remove from here...
+          << "    sub_type = " << (int)materia.sub_type << ",\n"
+          << "    type_raw = " << (int)materia.type_raw << ",\n"
+          << "    type_raw_upp = " << (int)(materia.type_raw >> 4) << ",\n"
+          << "    type_raw_low = " << (int)(materia.type_raw & 15) << ",\n"
+          << "    attributtes = {";
+        for (int i = 0; i < 6; i ++)
+            file << (int) materia.attribute[i] << ", ";
+        file << "},\n"
+        // TODO ... until here
+
+
           << "    levels_ap = {[1] = 0, ";
         for (int i = 0; i < 4; i ++){
             if (materia.level_up_ap[i] < 65535)
@@ -1726,8 +1860,147 @@ void KernelDataInstaller::WriteMateria(std::string file_name){
         if (materia.stats.lck != 0) file << "\n        lck = " << materia.stats.lck << ",";
         if (materia.stats.hp != 0) file << "\n        hp = " << materia.stats.hp << ",";
         if (materia.stats.mp != 0) file << "\n        mp = " << materia.stats.mp << ",";
-        if (materia.stats.change) file << "\n    }\n";
-        else file << "}\n";
+        if (materia.stats.change) file << "\n    },\n";
+        else file << "},\n";
+
+        // Type-dependant
+        if (materia.type == static_cast<int>(MATERIA_TYPE::MAGIC)){
+            file << "    magic = {\n";
+            for (int a = 0; a < 6; a ++){
+                if (materia.attribute[a] != 0xFF){
+                    file << "        [" << a + 1 << "] = " << static_cast<int>(materia.attribute[a])
+                      << ", -- " << attack_names_[static_cast<int>(materia.attribute[a])] << "\n";
+                }
+            }
+            file << "    }\n";
+        }
+        else if (materia.type == static_cast<int>(MATERIA_TYPE::SUMMON)){
+            file << "    summon = " << static_cast<int>(materia.attribute[0]) << ", -- "
+              << attack_names_[static_cast<int>(materia.attribute[0])] << "\n";
+            file << "    times = {";
+            for (int a = 1; a < 6; a ++)
+                file << "[" << a << "] = " << static_cast<int>(materia.attribute[a]) << ", ";
+            file << "}\n";
+        }
+        else if (materia.type == static_cast<int>(MATERIA_TYPE::COMMAND)){
+            switch (materia.sub_type){
+                case 0x02: // Command, replace attack.
+                    file << "    command = {\n";
+                    for (int a = 0; a < 6; a ++){
+                        if (materia.attribute[a] != 0xFF){
+                            file << "        [" << a + 1 << "] = "
+                              << static_cast<int>(materia.attribute[a]) << ", -- "
+                              << command_names_[static_cast<int>(materia.attribute[a])] << "\n";
+                        }
+                    }
+                    file << "    },\n    command_replace = 1 -- Attack\n";
+                    break;
+                case 0x03: // Command, W commands.
+                    file << "    command = {\n";
+                    for (int a = 0; a < 6; a ++){
+                        if (materia.attribute[a] != 0xFF){
+                            file << "        [" << a + 1 << "] = "
+                              << static_cast<int>(materia.attribute[a]) << ", -- "
+                              << command_names_[static_cast<int>(materia.attribute[a])] << "\n";
+                        }
+                    }
+                    file << "    },\n    command_replace = "
+                      << static_cast<int>(materia.attribute[0] - 19) << " -- "
+                      << command_names_[static_cast<int>(materia.attribute[0] - 19)] << "\n";
+                    break;
+                case 0x06: // Normal command.
+                case 0x07: // E.Skill.
+                    file << "    command = {\n";
+                        for (int a = 0; a < 6; a ++){
+                            if (materia.attribute[a] != 0xFF){
+                                file << "        [" << a + 1 << "] = "
+                                  << static_cast<int>(materia.attribute[a]) << ", -- "
+                                  << command_names_[static_cast<int>(materia.attribute[a])] << "\n";
+                            }
+                        }
+                        file << "    }\n";
+                    break;
+                case 0x08: // Master command.
+                    // TODO
+                    break;
+            }
+        }
+        else if (materia.type == static_cast<int>(MATERIA_TYPE::SUPPORT)){
+            file << "    link = {\n        id = ";
+            switch (materia.attribute[0]){
+                case 0x51: file << "Materia.LINK.ALL,\n"; break;
+                case 0x54: file << "Materia.LINK.COMMAND_COUNTER,\n"; break;
+                case 0x55: file << "Materia.LINK.MAGIC_COUNTER,\n"; break;
+                case 0x56: file << "Materia.LINK.SNEAK_ATTACK,\n"; break;
+                case 0x57: file << "Materia.LINK.FINAL_ATTACK,\n"; break;
+                case 0x58: file << "Materia.LINK.MP_TURBO,\n"; break;
+                case 0x59: file << "Materia.LINK.MP_ABSORB,\n"; break;
+                case 0x5A: file << "Materia.LINK.HP_ABSORB,\n"; break;
+                case 0x5C: file << "Materia.LINK.ADDED_CUT,\n"; break;
+                case 0x5D: file << "Materia.LINK.STEAL_AS_WELL,\n"; break;
+                case 0x5E: file << "Materia.LINK.ELEMENTAL,\n"; break;
+                case 0x5F: file << "Materia.LINK.ADDED_EFFECT,\n"; break;
+                case 0x63: file << "Materia.LINK.QUADRA_MAGIC,\n"; break;
+                default: file << "nil,\n"; break;
+            }
+            file << "        param = {";
+            for (int a = 1; a < 6; a ++){
+                if (materia.attribute[a] != 0xFF){
+                    file << "[" << a << "] = " << static_cast<int>(materia.attribute[a]) << ", ";
+                }
+            }
+            file << "}\n    }\n";
+        }
+        else if (materia.type == static_cast<int>(MATERIA_TYPE::INDEPENDENT)){
+            bool invert_attrs = false;
+            file << "    ability = {\n        id = ";
+            switch (materia.sub_type){
+                case 0x00: // Character effects
+                    switch (materia.attribute[0]){
+                        case 0x02: file << "Materia.ABILITY.MAG_PLUS,\n"; break;
+                        case 0x04: file << "Materia.ABILITY.DEX_PLUS,\n"; break;
+                        case 0x05: file << "Materia.ABILITY.LCK_PLUS,\n"; break;
+                        case 0x08: file << "Materia.ABILITY.HP_PLUS,\n"; break;
+                        case 0x09: file << "Materia.ABILITY.MP_PLUS,\n"; break;
+                        case 0x0A: file << "Materia.ABILITY.EXP_PLUS,\n"; break;
+                        case 0x0B: file << "Materia.ABILITY.COVER,\n"; break;
+                        case 0x0C: file << "Materia.ABILITY.UNDERWATER,\n"; break;
+                        case 0x50: file << "Materia.ABILITY.LONG_RANGE,\n"; break;
+                        case 0x53: file << "Materia.ABILITY.COUNTER_ATTACK,\n"; break;
+                        case 0x62: file << "Materia.ABILITY.HP_MP,\n"; break;
+                        default: file << "nil,\n"; break;
+                    }
+                    break;
+                case 0x01: // Party effects
+                    switch (materia.attribute[0]){
+                        case 0x00: file << "Materia.ABILITY.MONEY_PLUS,\n"; break;
+                        case 0x01: // Enemy Away / Enemy lure / Chocobo lure
+                            if (materia.id == 7)
+                                file << "Materia.ABILITY.ENCOUNTER_DOWN,\n";
+                            else if (materia.id == 8)
+                                file << "Materia.ABILITY.ENCOUNTER_UP,\n";
+                            else if (materia.id == 9)
+                                file << "Materia.ABILITY.ENCOUNTER_CHOCOBO,\n";
+                            else file << "nil,\n";
+                            break;
+                        case 0x03: file << "Materia.ABILITY.PREEMPTIVE,\n"; break;
+                        default: file << "nil,\n"; break;
+                    }
+                    break;
+                case 0x04:
+                    file << "Materia.ABILITY.MEGA_ALL,\n";
+                    break;
+                default: file << "nil,\n"; break;
+            }
+            file << "        param = {";
+                for (int a = 1; a < 6; a ++){
+                    if (materia.attribute[a] != 0xFF){
+                        file << "[" << a << "] = "
+                          << static_cast<int>(materia.attribute[a]) << ", ";
+                    }
+                }
+                file << "}\n    }\n";
+        }
         file << "}\n";
     }
     file.close();

BIN
data/data/fonts/ffvii_digits.png


+ 3 - 0
data/data/fonts/ffvii_digits.xml

@@ -10,4 +10,7 @@
     <char name="8" x="64" y="0" width="7" height="8" pre="0" post="0" />
     <char name="9" x="72" y="0" width="7" height="8" pre="0" post="0" />
     <char name=" " x="80" y="0" width="7" height="8" pre="0" post="0" />
+    <char name="-" x="88" y="0" width="7" height="8" pre="0" post="0" />
+    <char name="+" x="96" y="0" width="7" height="8" pre="0" post="0" />
+    <char name="%" x="104" y="0" width="7" height="8" pre="0" post="0" />
 </font>

+ 157 - 0
data/data/screens/materia_menu/materia_menu.xml

@@ -0,0 +1,157 @@
+<screen name="MateriaMenu" script="scripts/menu/materia_menu.lua" visible="false">
+    <widget name="Container" width="40%" height="40%" x="0" y="0" align="left" valign="top" scale="2.5 2.5" visible="true">
+        <widget name="Character" width="100%" height="75" visible="true">
+            <prototype name="Window"/>
+            <sprite name="Portrait" image="images/other/choco.png" x="10" y="12" width="48" height="58" visible="true" />
+            <widget name="Data" x="66" y="14" width="100%-66" height="100%-14" visible="true">
+                <prototype name="MenuCharacterData" />
+            </widget>
+            <sprite name="WpnIcon" image="images/icons/item_weapon_0.png" x="170" y="12" width="14" height="14" visible="true" />
+            <sprite name="ArmIcon" image="images/icons/item_armor.png" x="170" y="42" width="14" height="14" visible="true" />
+            <text_area name="WpnLbl" x="188" y="13" font="FFVIIFont" visible="true" />
+            <text_area name="ArmLbl" x="188" y="43" font="FFVIIFont" visible="true" />
+            <text_area name="Check" text_name="MateriaMenuCheck" x="180" y="26" font="FFVIIFont" visible="true" />
+            <text_area name="Order" text_name="MateriaMenuOrder" x="180" y="56" font="FFVIIFont" visible="true" />
+            <widget name="WeaponSlots" x="230" y="24" width="230" height="14" visible="true">
+                <sprite name="Slot1" image="images/icons/materia_slot.png" x="0" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot2" image="images/icons/materia_slot.png" x="15" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link1" image="images/icons/materia_slot_link.png" x="10" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Slot3" image="images/icons/materia_slot.png" x="30" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot4" image="images/icons/materia_slot.png" x="45" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link2" image="images/icons/materia_slot_link.png" x="40" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Slot5" image="images/icons/materia_slot.png" x="60" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot6" image="images/icons/materia_slot.png" x="75" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link3" image="images/icons/materia_slot_link.png" x="70" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Slot7" image="images/icons/materia_slot.png" x="90" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot8" image="images/icons/materia_slot.png" x="105" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link4" image="images/icons/materia_slot_link.png" x="100" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Mat1" image="images/icons/materia_3.png" x="2" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat2" image="images/icons/materia_3.png" x="17" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat3" image="images/icons/materia_3.png" x="32" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat4" image="images/icons/materia_3.png" x="47" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat5" image="images/icons/materia_3.png" x="62" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat6" image="images/icons/materia_3.png" x="77" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat7" image="images/icons/materia_3.png" x="92" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat8" image="images/icons/materia_3.png" x="107" y="2" width="10" height="10" visible="true"/>
+            </widget>
+            <widget name="ArmorSlots" x="230" y="54" width="230" height="14" visible="true">
+                <sprite name="Slot1" image="images/icons/materia_slot.png" x="0" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot2" image="images/icons/materia_slot.png" x="15" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link1" image="images/icons/materia_slot_link.png" x="10" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Slot3" image="images/icons/materia_slot.png" x="30" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot4" image="images/icons/materia_slot.png" x="45" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link2" image="images/icons/materia_slot_link.png" x="40" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Slot5" image="images/icons/materia_slot.png" x="60" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot6" image="images/icons/materia_slot.png" x="75" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link3" image="images/icons/materia_slot_link.png" x="70" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Slot7" image="images/icons/materia_slot.png" x="90" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Slot8" image="images/icons/materia_slot.png" x="105" y="0" width="14" height="14" visible="true"/>
+                <sprite name="Link4" image="images/icons/materia_slot_link.png" x="100" y="0" width="8" height="14" visible="true"/>
+                <sprite name="Mat1" image="images/icons/materia_3.png" x="2" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat2" image="images/icons/materia_3.png" x="17" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat3" image="images/icons/materia_3.png" x="32" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat4" image="images/icons/materia_3.png" x="47" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat5" image="images/icons/materia_3.png" x="62" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat6" image="images/icons/materia_3.png" x="77" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat7" image="images/icons/materia_3.png" x="92" y="2" width="10" height="10" visible="true"/>
+                <sprite name="Mat8" image="images/icons/materia_3.png" x="107" y="2" width="10" height="10" visible="true"/>
+            </widget>
+            <sprite name="Cursor" image="images/icons/cursor.png" x="155" y="27" width="24" height="17" visible="true">
+                <animation name="Position1-1" length="0" x="0:155" y="0:27" />
+                <animation name="Position1-2" length="0" x="0:210" y="0:27" />
+                <animation name="Position1-3" length="0" x="0:225" y="0:27" />
+                <animation name="Position1-4" length="0" x="0:240" y="0:27" />
+                <animation name="Position1-5" length="0" x="0:255" y="0:27" />
+                <animation name="Position1-6" length="0" x="0:270" y="0:27" />
+                <animation name="Position1-7" length="0" x="0:285" y="0:27" />
+                <animation name="Position1-8" length="0" x="0:300" y="0:27" />
+                <animation name="Position1-9" length="0" x="0:315" y="0:27" />
+                <animation name="Position2-1" length="0" x="0:155" y="0:57" />
+                <animation name="Position2-2" length="0" x="0:210" y="0:57" />
+                <animation name="Position2-3" length="0" x="0:225" y="0:57" />
+                <animation name="Position2-4" length="0" x="0:240" y="0:57" />
+                <animation name="Position2-5" length="0" x="0:255" y="0:57" />
+                <animation name="Position2-6" length="0" x="0:270" y="0:57" />
+                <animation name="Position2-7" length="0" x="0:285" y="0:57" />
+                <animation name="Position2-8" length="0" x="0:300" y="0:57" />
+                <animation name="Position2-9" length="0" x="0:315" y="0:57" />
+            </sprite>
+        </widget>
+        <widget name="Title" x="100%-86" y="0" width="86" height="24" visible="true">
+            <prototype name="Window" />
+            <text_area name="Title" x="16" y="6" text_name="MateriaMenuTitle" font="FFVIIFont" visible="true" />
+        </widget>
+        <widget name="Help" x="0" y="75" width="100%" height="24" visible="true">
+            <prototype name="Window" />
+            <text_area name="Help" x="16" y="6" font="FFVIIFont" visible="true" />
+        </widget>
+        <widget name="Details" x="0" y="94" width="230" height="100%-99" visible="true">
+            <prototype name="Window" />
+            <widget name="Materia" x="0" y ="0" width="100%" height="100%" visible="true">
+                <sprite name="Icon" image="images/icons/materia_3.png" x="5" y="5" width="18" height="18" visible="true"/>
+                <text_area name="Name" x="29" y="9" font="FFVIIFont" visible="true" />
+                <widget name="Stars" x="100%-90" y="8" width="86" height="15" visible="true">
+                    <sprite name="Star1" image="images/icons/materia_star_1.png" x="5" y="0" width="15" height="15" visible="true"/>
+                    <sprite name="Star2" image="images/icons/materia_star_1.png" x="21" y="0" width="15" height="15" visible="true"/>
+                    <sprite name="Star3" image="images/icons/materia_star_1.png" x="37" y="0" width="15" height="15" visible="true"/>
+                    <sprite name="Star4" image="images/icons/materia_star_1.png" x="53" y="0" width="15" height="15" visible="true"/>
+                    <sprite name="Star5" image="images/icons/materia_star_1.png" x="69" y="0" width="15" height="15" visible="true"/>
+                </widget>
+                <text_area name="LblAP" text_name="MateriaMenuAP" x="100%-90" y="28" font="FFVIIFont" visible="true" />
+                <text_area name="LblAPNext" text_name="MateriaMenuAPNext" x="100%-90" y="42" font="FFVIIFont" visible="true" />
+                <text_area name="AP" text_name="MateriaMenuAP" x="100%-50" y="30" font="FFVIIMenuDigits" visible="true" />
+                <text_area name="APNext" text_name="MateriaMenuAPNext" x="100%-50" y="44" font="FFVIIMenuDigits" visible="true" />
+                <text_area name="LblAbilities" text_name="MateriaMenuAbilities" x="10" y="65" font="FFVIIFont" visible="true" />
+                <text_area name="LblEffects" text_name="MateriaMenuEffects" x="50%+10" y="65" font="FFVIIFont" visible="true" />
+                <widget name="Abilities" x="20" y="80" width="50%-20" height="100%-80" visible="true">
+                    <text_area name="Ability1" text_name="MateriaMenuAbilities" x="0" y="0" width="100%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Ability2" text_name="MateriaMenuAbilities" x="0" y="15" width="100%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Ability3" text_name="MateriaMenuAbilities" x="0" y="30" width="100%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Ability4" text_name="MateriaMenuAbilities" x="0" y="45" width="100%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Ability5" text_name="MateriaMenuAbilities" x="0" y="60" width="100%" height="15" font="FFVIIFont" visible="true" />
+                </widget>
+                <widget name="Effects" x="50%+20" y="80" width="50%-20" height="100%-80" visible="true">
+                    <text_area name="Stat1" text_name="MateriaMenuEffects" x="0" y="0" width="60%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Stat2" text_name="MateriaMenuEffects" x="0" y="15" width="60%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Stat3" text_name="MateriaMenuEffects" x="0" y="30" width="60%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Stat4" text_name="MateriaMenuEffects" x="0" y="45" width="60%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Stat5" text_name="MateriaMenuEffects" x="0" y="60" width="60%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Stat6" text_name="MateriaMenuEffects" x="0" y="75" width="60%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Stat7" text_name="MateriaMenuEffects" x="0" y="90" width="60%" height="15" font="FFVIIFont" visible="true" />
+                    <text_area name="Val1" text_name="MateriaMenuAP" x="60%" y="2" width="30%" height="15" font="FFVIIMenuDigits" visible="true" />
+                    <text_area name="Val2" text_name="MateriaMenuAP" x="60%" y="17" width="30%" height="15" font="FFVIIMenuDigits" visible="true" />
+                    <text_area name="Val3" text_name="MateriaMenuAP" x="60%" y="32" width="30%" height="15" font="FFVIIMenuDigits" visible="true" />
+                    <text_area name="Val4" text_name="MateriaMenuAP" x="60%" y="47" width="30%" height="15" font="FFVIIMenuDigits" visible="true" />
+                    <text_area name="Val5" text_name="MateriaMenuAP" x="60%" y="62" width="30%" height="15" font="FFVIIMenuDigits" visible="true" />
+                    <text_area name="Val6" text_name="MateriaMenuAP" x="60%" y="77" width="30%" height="15" font="FFVIIMenuDigits" visible="true" />
+                    <text_area name="Val7" text_name="MateriaMenuAP" x="60%" y="92" width="30%" height="15" font="FFVIIMenuDigits" visible="true" />
+                </widget>
+            </widget>
+        </widget>
+        <widget name="List" x="230" y="94" width="100%-230" height="100%-99" visible="true">
+            <prototype name="Window" />
+            <text_area name="Item1" x="15" y="4%" font="FFVIIFont" visible="true" />
+            <text_area name="Item2" x="15" y="13%" font="FFVIIFont" visible="true" />
+            <text_area name="Item3" x="15" y="22%" font="FFVIIFont" visible="true" />
+            <text_area name="Item4" x="15" y="31%" font="FFVIIFont" visible="true" />
+            <text_area name="Item5" x="15" y="40%" font="FFVIIFont" visible="true" />
+            <text_area name="Item6" x="15" y="49%" font="FFVIIFont" visible="true" />
+            <text_area name="Item7" x="15" y="58%" font="FFVIIFont" visible="true" />
+            <text_area name="Item8" x="15" y="67%" font="FFVIIFont" visible="true" />
+            <text_area name="Item9" x="15" y="76%" font="FFVIIFont" visible="true" />
+            <text_area name="Item10" x="15" y="85%" font="FFVIIFont" visible="true" />
+            <sprite name="Cursor" image="images/icons/cursor.png" x="-10" y="8" width="24" height="17" visible="true">
+                <animation name="Position1" length="0" y="0:4%+3" />
+                <animation name="Position2" length="0" y="0:13%+3" />
+                <animation name="Position3" length="0" y="0:22%+3" />
+                <animation name="Position4" length="0" y="0:31%+3" />
+                <animation name="Position5" length="0" y="0:40%+3" />
+                <animation name="Position6" length="0" y="0:49%+3" />
+                <animation name="Position7" length="0" y="0:58%+3" />
+                <animation name="Position8" length="0" y="0:67%+3" />
+                <animation name="Position9" length="0" y="0:76%+3" />
+                <animation name="Position10" length="0" y="0:85%+3" />
+            </sprite>
+        </widget>
+    </widget>
+</screen>

+ 51 - 0
data/data/scripts/data.lua

@@ -118,6 +118,57 @@ Inventory.ORDER = {
 
 Materia = {}
 
+--- Types of materia
+Materia.TYPE = {
+    MAGIC = 1,
+    SUPPORT = 2,
+    COMMAND = 3,
+    INDEPENDENT = 4,
+    SUMMON = 5
+}
+
+--- Abilities confered by independent materia.
+Materia.ABILITY = {
+    STR_PLUS = 0,
+    VIT_PLUS = 1,
+    MAG_PLUS = 2,
+    SPR_PLUS = 3,
+    DEX_PLUS = 4,
+    LCK_PLUS = 5,
+    HP_PLUS = 8,
+    MP_PLUS = 9,
+    EXP_PLUS = 10,
+    COVER = 11,
+    UNDERWATER = 12,
+    LONG_RANGE = 80,
+    COUNTER_ATTACK = 83,
+    HP_MP = 98,
+    MEGA_ALL = 99,
+    MONEY_PLUS = 100,
+    PREEMPTIVE = 103,
+    ENC_DOWN = 107,
+    ENC_UP = 108,
+    ENC_CHOCOBO = 109
+}
+
+--- Abilities confered by support materia.
+--
+-- They are dependant on the linked materia.
+Materia.LINK = {
+    ALL = 81,
+    COMMAND_COUNTER = 84,
+    MAGIC_COUNTER = 85,
+    SNEAK_ATTACK = 86,
+    FINAL_ATTACK = 87,
+    MP_TURBO = 88,
+    MP_TURBO = 89,
+    MP_TURBO = 90,
+    ADDED_CUT = 92,
+    STEAL_AS_WELL = 93,
+    ELEMENTAL = 94,
+    ADDED_EFFECT = 95,
+    QUADRA_MAGIC = 99
+}
 
 
 FFVII = {}

+ 10 - 0
data/data/scripts/debug_data.lua

@@ -46,3 +46,13 @@ Inventory.money = 1234567
 Inventory.sort(Inventory.ORDER.NAME)
 Characters[0].stats.hp.current = 12345
 Characters[0].stats.hp.base = 54321
+Characters[0].armor.id = 268
+Characters[0].armor.materia = {
+    [0] = {id = 62, ap = 5340},
+    [2] = {id = 72, ap = 5},
+    [3] = {id = 78, ap = 520},
+    [4] = {id = 36, ap = 0},
+    [5] = {id = 36, ap = 50000},
+    [6] = {id = 0, ap = 0},
+    [7] = {id = 26, ap = 0},
+}

+ 575 - 0
data/data/scripts/menu/materia_menu.lua

@@ -0,0 +1,575 @@
+if UiContainer == nil then UiContainer = {} end
+
+--- The item menu.
+UiContainer.MateriaMenu = {
+
+    --- Current header cursor row (1: Check/Weapon, 2: Order/Armor).
+    header_position_row = 1,
+
+    --- Current header cursor position (1: Check/Order, 2-9: Weapon/Armor slots).
+    header_position = 1,
+
+    --- Current header cursor max row.
+    header_position_row_total = 2,
+
+    --- Current header cursor max position.
+    header_position_total = 9,
+
+    --- ID of the character the menu is open for.
+    char_id = -1,
+
+    list_position = 1,
+
+    list_position_total = 10,
+
+    list_first_visible = 1,
+
+    --- Run when the menu is creaded.
+    --
+    -- It does nothing.
+    on_start = function(self)
+        return 0
+    end,
+
+    --- Handles button events.
+    --
+    -- For the current submenu, handles directional keys, enter and escape events.
+    -- @param button Pressed button string key. "Up", "Left", "Enter" and "Escape" are handled.
+    -- @param event Trigger event. Normally, "Press".
+    on_button = function(self, button, event)
+        if UiContainer.current_menu == "materia" then
+            if UiContainer.current_submenu == "" then
+                -- TODO: Handle L1/R1 (change character)
+                -- TODO: Handle square: Materia menu
+                if button == "Escape" and event == "Press" then
+                    UiContainer.current_menu = "main"
+                    script:request_end_sync(Script.UI, "MateriaMenu", "hide", 0)
+                elseif button == "Down" then
+                    self.header_position_row = self.header_position_row + 1
+                    if self.header_position_row > self.header_position_row_total then
+                        self.header_position_row = 1
+                    end
+                    ui_manager:get_widget("MateriaMenu.Container.Character.Cursor"):set_default_animation("Position" .. self.header_position_row .. "-" .. self.header_position)
+                    self.populate_details(self, false)
+                elseif button == "Up" then
+                    self.header_position_row = self.header_position_row - 1
+                    if self.header_position_row < 1 then
+                        self.header_position_row = self.header_position_row_total
+                    end
+                    ui_manager:get_widget("MateriaMenu.Container.Character.Cursor"):set_default_animation("Position" .. self.header_position_row .. "-" .. self.header_position)
+                    self.populate_details(self, false)
+                elseif button == "Right" then
+                    self.header_position = self.header_position + 1
+                    if self.header_position > self.header_position_total then
+                        self.header_position = 1
+                    end
+                    ui_manager:get_widget("MateriaMenu.Container.Character.Cursor"):set_default_animation("Position" .. self.header_position_row .. "-" .. self.header_position)
+                    self.populate_details(self, false)
+                elseif button == "Left" then
+                    self.header_position = self.header_position - 1
+                    if self.header_position < 1 then
+                        self.header_position = self.header_position_total
+                    end
+                    ui_manager:get_widget("MateriaMenu.Container.Character.Cursor"):set_default_animation("Position" .. self.header_position_row .. "-" .. self.header_position)
+                    self.populate_details(self, false)
+                elseif button == "Enter" then
+                    --[[if self.equip_position == 1 then
+                        self.selecting_slot = Inventory.ITEM_TYPE.WEAPON
+                    elseif self.equip_position == 2 then
+                        self.selecting_slot = Inventory.ITEM_TYPE.ARMOR
+                    elseif self.equip_position == 3 then
+                        self.selecting_slot = Inventory.ITEM_TYPE.ACCESSORY
+                    end
+                    self.submenu_select(self)]]
+                else
+                    return 0
+                end
+            end
+        end
+        return 0
+    end,
+
+    --- Opens the materia menu.
+    --
+    -- Populates and updates displayed data before opening.
+    show = function(self)
+        self.char_id = UiContainer.current_character_id
+        ui_manager:get_widget("MateriaMenu"):set_visible(true)
+        UiContainer.current_menu = "materia"
+        UiContainer.current_submenu = ""
+        self.header_position_row = 1
+        self.header_position = 1
+        self.submenu_none(self)
+        return 0;
+    end,
+
+    --- Enters or returns to the main equip menu.
+    submenu_none = function(self)
+        UiContainer.current_submenu = ""
+        UiContainer.populate_character_data("MateriaMenu.Container.Character", Characters[self.char_id])
+        ui_manager:get_widget("MateriaMenu.Container.Character.Portrait"):set_image("images/characters/" .. tostring(self.char_id) .. ".png")
+        self.populate_equip(self)
+        -- TODO: Do something for chars 9 and 10
+
+
+        --[[self.load_availables(self)
+        self.populate_details(self, false)
+        self.populate_current_stats(self)
+        self.calculate_stat_diffs(self)
+        -- Hide stat arrows and difs
+        ui_manager:get_widget("EquipMenu.Container.Stats.AtkDif"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.AccDif"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.DefDif"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.EvaDif"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.MAtkDif"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.MDefDif"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.MEvaDif"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.AtkArw"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.AccArw"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.DefArw"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.EvaArw"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.MAtkArw"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.MDefArw"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.Stats.MEvaArw"):set_visible(false)
+        ui_manager:get_widget("EquipMenu.Container.List.Cursor"):set_visible(false)
+        for i = 1, self.list_position_total do
+            ui_manager:get_widget("EquipMenu.Container.List.Item" .. tostring(i)):set_text("")
+        end]]
+    end,
+
+    populate_equip = function(self)
+        local weapon = Game.Items[Characters[self.char_id].weapon.id]
+        local armor = Game.Items[Characters[self.char_id].armor.id]
+        ui_manager:get_widget("MateriaMenu.Container.Character.WpnLbl"):set_text(weapon.name)
+        ui_manager:get_widget("MateriaMenu.Container.Character.ArmLbl"):set_text(armor.name)
+        for i = 1, 8 do
+            -- Show/hide slots
+            -- TODO: Change image for no-growth slots?
+            if #(weapon.slots) >= i then
+                ui_manager:get_widget("MateriaMenu.Container.Character.WeaponSlots.Slot" .. tostring(i)):set_visible(true)
+            else
+                ui_manager:get_widget("MateriaMenu.Container.Character.WeaponSlots.Slot" .. tostring(i)):set_visible(false)
+            end
+            if #(armor.slots) >= i then
+                ui_manager:get_widget("MateriaMenu.Container.Character.ArmorSlots.Slot" .. tostring(i)):set_visible(true)
+            else
+                ui_manager:get_widget("MateriaMenu.Container.Character.ArmorSlots.Slot" .. tostring(i)):set_visible(false)
+            end
+            -- Show/hide linkers
+            if i % 2 ~= 0 then
+                if #(weapon.slots) > i  and weapon.slots[i] == 1 then
+                    ui_manager:get_widget("MateriaMenu.Container.Character.WeaponSlots.Link" .. tostring(math.floor(i / 2) + 1)):set_visible(true)
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Character.WeaponSlots.Link" .. tostring(math.floor(i / 2) + 1)):set_visible(false)
+                end
+                if #(armor.slots) > i  and armor.slots[i] == 1 then
+                    ui_manager:get_widget("MateriaMenu.Container.Character.ArmorSlots.Link" .. tostring(math.floor(i / 2) + 1)):set_visible(true)
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Character.ArmorSlots.Link" .. tostring(math.floor(i / 2) + 1)):set_visible(false)
+                end
+            end
+            -- Show/hide/set materia
+            if Characters[self.char_id].weapon.materia[i - 1] ~= nil then
+                local mat_type =  Game.Materia[Characters[self.char_id].weapon.materia[i - 1].id].type
+                ui_manager:get_widget("MateriaMenu.Container.Character.WeaponSlots.Mat" .. tostring(i)):set_image("images/icons/materia_" .. tostring(mat_type) .. ".png")
+                ui_manager:get_widget("MateriaMenu.Container.Character.WeaponSlots.Mat" .. tostring(i)):set_visible(true)
+            else
+                ui_manager:get_widget("MateriaMenu.Container.Character.WeaponSlots.Mat" .. tostring(i)):set_visible(false)
+            end
+            if Characters[self.char_id].armor.materia[i - 1] ~= nil then
+                local mat_type =  Game.Materia[Characters[self.char_id].armor.materia[i - 1].id].type
+                ui_manager:get_widget("MateriaMenu.Container.Character.ArmorSlots.Mat" .. tostring(i)):set_image("images/icons/materia_" .. tostring(mat_type) .. ".png")
+                ui_manager:get_widget("MateriaMenu.Container.Character.ArmorSlots.Mat" .. tostring(i)):set_visible(true)
+            else
+                ui_manager:get_widget("MateriaMenu.Container.Character.ArmorSlots.Mat" .. tostring(i)):set_visible(false)
+            end
+        end
+    end,
+
+    --- Initializzes the item selection submenu
+    --[[submenu_select = function(self)
+        -- Populate the list
+        self.list_first_visible = 1
+        self.list_position = 1
+        self.list_item_selected = 1
+        if self.populate_item_list(self) == false then
+            print("BEEP")
+            return
+        end
+        UiContainer.current_submenu = "item_select"
+        ui_manager:get_widget("EquipMenu.Container.List.Cursor"):set_visible(true)
+        ui_manager:get_widget("EquipMenu.Container.List.Cursor"):set_default_animation("Position" .. self.list_position)
+        -- TODO: Description and details of first item
+        self.populate_details(self, true)
+        self.calculate_stat_diffs(self)
+    end,]]
+
+    --[[populate_item_list = function(self)
+        local list
+        if self.selecting_slot == Inventory.ITEM_TYPE.WEAPON then
+            list = self.avail_weapons
+        elseif self.selecting_slot == Inventory.ITEM_TYPE.ARMOR then
+            list = self.avail_armors
+        elseif self.selecting_slot == Inventory.ITEM_TYPE.ACCESSORY then
+            list = self.avail_accessories
+        else
+            for i = 1, self.list_position_total do
+                ui_manager:get_widget("EquipMenu.Container.List.Item" .. tostring(i)):set_text("")
+            end
+            return false
+        end
+        if list == nil or #(list) == 0 then
+            -- No items for the list
+            return false
+        end
+        for i = 1, self.list_position_total do
+            if #(list) >= i + self.list_first_visible then
+                local name = Game.Items[list[i + self.list_first_visible] ].name
+                ui_manager:get_widget("EquipMenu.Container.List.Item" .. tostring(i)):set_text(name)
+            else
+                ui_manager:get_widget("EquipMenu.Container.List.Item" .. tostring(i)):set_text("")
+            end
+        end
+    end,]]
+
+    --- Loads the list of equipment available for the current character.
+    --
+    -- Currently equiped items are not added, even if more of them are in the inventory.
+    --[[load_availables = function(self)
+        self.avail_weapons = {}
+        self.avail_armors = {}
+        self.avail_accessories = {}
+        for _, item in ipairs(Inventory) do
+            if item.item ~= nil then
+                local item_type = Game.Items[item.item].type
+                if item_type == Inventory.ITEM_TYPE.WEAPON or item_type == Inventory.ITEM_TYPE.ARMOR or item_type == Inventory.ITEM_TYPE.ACCESSORY then
+                    local equip = Game.Items[item.item].users
+                    for _, ch in ipairs(equip) do
+                        if ch == self.char_id then
+                            if item_type == Inventory.ITEM_TYPE.WEAPON then
+                                if item.item ~= Characters[self.char_id].weapon.id then
+                                    table.insert(self.avail_weapons, item.item)
+                                end
+                            elseif item_type == Inventory.ITEM_TYPE.ARMOR then
+                                if item.item ~= Characters[self.char_id].armor.id then
+                                    table.insert(self.avail_armors, item.item)
+                                end
+                            elseif item_type == Inventory.ITEM_TYPE.ACCESSORY then
+                                if item.item ~= Characters[self.char_id].accessory then
+                                    table.insert(self.avail_accessories, item.item)
+                                end
+                            end
+                        end
+                    end
+                end
+            end
+        end
+    end,]]
+
+    --[[populate_current_stats = function(self)
+        -- TODO: Cap at 255?
+        local stats = Characters[self.char_id].stats
+        ui_manager:get_widget("EquipMenu.Container.Stats.Atk"):set_text(UiContainer.pad_value(stats.atk, 4))
+        ui_manager:get_widget("EquipMenu.Container.Stats.Acc"):set_text(UiContainer.pad_value(stats.acc, 4))
+        ui_manager:get_widget("EquipMenu.Container.Stats.Def"):set_text(UiContainer.pad_value(stats.def, 4))
+        ui_manager:get_widget("EquipMenu.Container.Stats.Eva"):set_text(UiContainer.pad_value(stats.eva, 4))
+        ui_manager:get_widget("EquipMenu.Container.Stats.MAtk"):set_text(UiContainer.pad_value(stats.matk, 4))
+        ui_manager:get_widget("EquipMenu.Container.Stats.MDef"):set_text(UiContainer.pad_value(stats.mdef, 4))
+        ui_manager:get_widget("EquipMenu.Container.Stats.MEva"):set_text(UiContainer.pad_value(stats.meva, 4))
+    end,]]
+
+    --- Hiddes the materia details.
+    --
+    -- Must be called when the cursor goes into an empty slot
+    hide_details = function(self)
+        -- TODO
+    end,
+
+    --- Show details of an enemy skill materia.
+    --
+    -- Must be called when the cursor goes into an empty slot
+    populate_e_skill_details = function(self)
+        -- TODO
+    end,
+
+    --- Populated the details of the selected header item (check, materia, or none).
+    --
+    -- @param from_list If true, data will be read from the item higlighted in the list on the
+    -- right. If false, data wil be red from the currently equipped items.
+    populate_details = function(self, from_list)
+        local id
+        local ap
+        local ap_next
+        local level
+        local max_level
+        local master = false
+        local materia
+        if from_list == false then
+            if self.header_position_row == 1 then
+                -- Weapon materia
+                local weapon_id = Characters[self.char_id].weapon.id
+                local weapon = Game.Items[weapon_id]
+                if #(weapon.slots) < self.header_position -1 or Characters[self.char_id].weapon.materia[self.header_position - 2] == nil then
+                    -- No slot or no materia in slot. Clear and exit
+                    self.hide_details(self)
+                    return 0
+                end
+                id = Characters[self.char_id].weapon.materia[self.header_position - 2].id
+                ap = Characters[self.char_id].weapon.materia[self.header_position - 2].ap
+                materia = Game.Materia[id]
+            elseif self.header_position_row == 2 then
+                -- Armor materia
+                local armor_id = Characters[self.char_id].armor.id
+                local armor = Game.Items[armor_id]
+                if #(armor.slots) < self.header_position - 1 or Characters[self.char_id].armor.materia[self.header_position - 2] == nil then
+                    -- No slot or no materia in slot. Clear and exit
+                    self.hide_details(self)
+                    return 0
+                end
+                id = Characters[self.char_id].armor.materia[self.header_position - 2].id
+                ap = Characters[self.char_id].armor.materia[self.header_position - 2].ap
+                materia = Game.Materia[id]
+            else
+                -- This should not happen, but just in case.
+                self.hide_details(self)
+                return 0
+            end
+        else
+            -- TODO: Get materia data from list
+        end
+
+        -- Get level and max level, and check for master.
+        level = 0
+        for lv, lv_ap in ipairs(materia.levels_ap) do
+            if lv_ap > ap then
+                break
+            end
+            level = level + 1
+        end
+        max_level = #(materia.levels_ap)
+        if level == max_level then
+            master = true
+            ap_next = 0
+        else
+            ap_next = materia.levels_ap[level + 1] - ap
+        end
+        -- Start displaying data
+        ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Icon"):set_image("images/icons/materia_" .. tostring(materia.type) .. ".png")
+        ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Name"):set_text(materia.name)
+        for l = 1, 5 do
+            if l > max_level then
+                -- No such level, hide star.
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Stars.Star" .. tostring(l)):set_visible(false)
+            else
+                if l <= level then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Stars.Star" .. tostring(l)):set_image("images/icons/materia_star_" .. tostring(materia.type) .. ".png")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Stars.Star" .. tostring(l)):set_image("images/icons/materia_star_empty_" .. tostring(materia.type) .. ".png")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Stars.Star" .. tostring(l)):set_visible(true)
+            end
+        end
+        ui_manager:get_widget("MateriaMenu.Container.Details.Materia.AP"):set_text(UiContainer.pad_value(ap, 6))
+        ui_manager:get_widget("MateriaMenu.Container.Details.Materia.APNext"):set_text(UiContainer.pad_value(ap_next, 6))
+        --ui_manager:get_widget("MateriaMenu.Container.Details.Materia.AP"):set_text(UiContainer.pad_value(200000, 6))
+        --ui_manager:get_widget("MateriaMenu.Container.Details.Materia.APNext"):set_text(UiContainer.pad_value(30000, 6))
+
+        -- Hide all attacks
+        for a = 1, 5 do
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(a)):set_visible(false)
+        end
+
+        -- Show attacks, vary by type
+        if materia.type == Materia.TYPE.MAGIC then
+            local attack_index = 1
+            for attack_level, attack_id in pairs(materia.magic) do
+                print("LV " .. attack_level .. " ID " .. attack_id)
+                if level >= attack_level then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_colour(1, 1, 1)
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_colour(0.6, 0.6, 0.6)
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_text(Game.Attacks[attack_id].name)
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_visible(true)
+                attack_index = attack_index + 1
+            end
+        elseif materia.type == Materia.TYPE.SUMMON then
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_colour(1, 1, 1)
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_text(Game.Attacks[materia.summon].name)
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_visible(true)
+        elseif materia.type == Materia.TYPE.COMMAND then
+            local attack_index = 1
+            for attack_level, attack_id in ipairs(materia.command) do
+                if level >= attack_level then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_colour(1, 1, 1)
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_colour(0.6, 0.6, 0.6)
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_text(Game.Commands[attack_id].name)
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability" .. tostring(attack_index)):set_visible(true)
+                attack_index = attack_index + 1
+            end
+        elseif materia.type == Materia.TYPE.INDEPENDENT then
+            local bonus = 0
+            for l = 1, max_level do
+                if l <= level and materia.ability.param[l] ~= nil then
+                    bonus = materia.ability.param[l]
+                end
+            end
+            if materia.ability ~= nil then
+                local text = "TEXT"
+                if materia.ability.id == Materia.ABILITY.MP_PLUS then
+                    text = "<include name=\"StatMP\"/> +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.HP_PLUS then
+                    text = "<include name=\"StatHP\"/> +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.STR_PLUS then
+                    text = "<include name=\"StatStr\"/> +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.VIT_PLUS then
+                    text = "<include name=\"StatVit\"/> +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.MAG_PLUS then
+                    text = "<include name=\"StatMag\"/> +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.DEX_PLUS then
+                    text = "<include name=\"StatDex\"/> +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.LCK_PLUS then
+                    text = "<include name=\"StatLck\"/> +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.EXP_PLUS then
+                    text = "<include name=\"StatEXP\"/> +"
+                elseif materia.ability.id == Materia.ABILITY.MONEY_PLUS then
+                    text = "<include name=\"Currency\"/> +"
+                elseif materia.ability.id == Materia.ABILITY.ENCOUNTER_DOWN then
+                    text = "<include name=\"MateriaEncounters\"/> -"
+                elseif materia.ability.id == Materia.ABILITY.ENCOUNTER_UP then
+                    text = "<include name=\"MateriaEncountersChocobo\"/> -"
+                elseif materia.ability.id == Materia.ABILITY.PREEMPTIVE then
+                    text = materia.name
+                elseif materia.ability.id == Materia.ABILITY.LONG_RANGE then
+                    text = materia.name
+                elseif materia.ability.id == Materia.ABILITY.MEGA_ALL then
+                    text = materia.name
+                elseif materia.ability.id == Materia.ABILITY.COUNTER_ATTACK then
+                    text = materia.name
+                elseif materia.ability.id == Materia.ABILITY.COVER then
+                    text = materia.name .. " +<colour value=\"1 1 0\">" .. tostring(bonus) .. "</colour>%"
+                elseif materia.ability.id == Materia.ABILITY.UNDERWATER then
+                    text = materia.name
+                elseif materia.ability.id == Materia.ABILITY.HP_MP then
+                    text = materia.name
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_colour(1, 1, 1)
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_text(text)
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_visible(true)
+            end
+        elseif materia.type == Materia.TYPE.SUPPORT then
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_colour(1, 1, 1)
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_text(materia.name)
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Abilities.Ability1"):set_visible(true)
+        end
+
+        -- Hide all effects
+        for a = 1, 7 do
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(a)):set_visible(false)
+            ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(a)):set_visible(false)
+        end
+
+        -- Show effects, if any.
+        --if #(materia.stats) > 0 then
+            local effect_index = 1
+            if materia.stats.str ~= nil and materia.stats.str ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatStr\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.str < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.str, 2) .. "</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.str, 2) .. "</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+            if materia.stats.vit ~= nil and materia.stats.vit ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatVit\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.vit < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.vit, 2) .. "</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.vit, 2) .. "</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+            if materia.stats.mag ~= nil and materia.stats.mag ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatMag\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.mag < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.mag, 2) .. "</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.mag, 2) .. "</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+            if materia.stats.spr ~= nil and materia.stats.spr ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatSpr\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.spr < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.spr, 2) .. "</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.spr, 2) .. "</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+            if materia.stats.dex ~= nil and materia.stats.dex ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatDex\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.dex < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.dex, 2) .. "</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.dex, 2) .. "</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+            if materia.stats.lck ~= nil and materia.stats.lck ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatLck\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.lck < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.lck, 2) .. "</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.lck, 2) .. "</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+            if materia.stats.hp ~= nil and materia.stats.hp ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatHP\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.hp < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.hp, 2) .. "%</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.hp, 2) .. "%</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+            if materia.stats.mp ~= nil and materia.stats.mp ~= 0 then
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_text("<include name=\"StatMP\" />")
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Stat" .. tostring(effect_index)):set_visible(true)
+                if materia.stats.mp < 0 then
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 0 0\">-" .. UiContainer.pad_value(-1 * materia.stats.mp, 2) .. "%</colour>")
+                else
+                    ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_text("<colour value=\"1 1 0\">+" .. UiContainer.pad_value(materia.stats.mp, 2) .. "%</colour>")
+                end
+                ui_manager:get_widget("MateriaMenu.Container.Details.Materia.Effects.Val" .. tostring(effect_index)):set_visible(true)
+                effect_index = effect_index + 1
+            end
+        --end
+    end,
+
+    --- Hides the item menu and goes back to the main menu.
+    hide = function(self)
+        ui_manager:get_widget("MateriaMenu"):set_visible(false)
+        UiContainer.current_menu = "main"
+        UiContainer.current_submenu = ""
+        return 0;
+    end,
+}

+ 32 - 0
data/data/texts/english/menu.xml

@@ -2,6 +2,23 @@
     <text name="Placeholder">#</text>
 
     <text name="Colon">:</text>
+    <text name="Yes">Yes</text>
+    <text name="No">No</text>
+
+    <text name="StatStr">Strength</text>
+    <text name="StatVit">Vitality</text>
+    <text name="StatMag">Magic</text>
+    <text name="StatSpr">Spirit</text>
+    <text name="StatDex">Speed</text>
+    <text name="StatLck">Luck</text>
+    <text name="StatHP">HP</text>
+    <text name="StatMP">MP</text>
+    <text name="StatEXP">EXP</text>
+
+    <text name="Currency">Gil</text>
+
+    <text name="MateriaEncounters">Encounters</text>
+    <text name="MateriaEncountersChocobo">Chocobo</text>
 
     <text name="BeginMenuNewGame">NEW GAME</text>
     <text name="BeginMenu1">DEBUG: Battle map</text>
@@ -35,6 +52,7 @@
     <text name="MainMenuConfig">Config</text>
     <text name="MainMenuPHS">PHS</text>
     <text name="MainMenuSave">Save</text>
+    <text name="MainMenuQuit">Quit</text>
 
     <text name="ItemMenuItem">Item</text>
     <text name="ItemMenuUse">Use</text>
@@ -70,6 +88,20 @@
     <text name="EquipMenuMEva"><colour value="0 0.8 1">Magic evasion:</colour></text>
     <text name="EquipMenuArrow"><colour value="0 0.6 0.8">-></colour></text>
 
+    <text name="MateriaMenuTitle">Materia</text>
+    <text name="MateriaMenuCheck"><colour value="0.7 0.7 0.7">Check</colour></text>
+    <text name="MateriaMenuOrder"><colour value="0.7 0.7 0.7">Order</colour></text>
+    <text name="MateriaMenuAP"><colour value="0 0.8 1">A.P.:</colour></text>
+    <text name="MateriaMenuAPNext"><colour value="0 0.8 1">Next:</colour></text>
+    <text name="MateriaMenuAbilities"><colour value="0 0.8 1">Abilities</colour></text>
+    <text name="MateriaMenuEffects"><colour value="0 0.8 1">Effects</colour></text>
+
+    <text name="NameMenuHelp">Enter a name</text>
+    <text name="NameMenuClear">Clear</text>
+    <text name="NameMenuDefault">Default</text>
+    <text name="NameMenuConfirm">Confirm</text>
+    <text name="NameMenuConfirmMsg">Confirm name</text>
+
     <text name="CharacterDataName">#Name#</text>
     <text name="CharacterDataLv">LV</text>
     <text name="CharacterDataLvNumber"> 0</text>