瀏覽代碼

Installer: Battle data installer started. Attacks, enemies, and enemy formations are correclty read from scene.bin and parsed to xml files.

Iñigo Valentin 3 年之前
父節點
當前提交
038403f75d

+ 1 - 1
Doxyfile

@@ -906,7 +906,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 # Note: If this tag is empty the current directory is searched.
 
 
-INPUT                  = V-Gears V-Gears-Installer
+INPUT                  = src
 
 
 # This tag can be used to specify the character encoding of the source files
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

+ 1 - 1
src/common/File.cpp

@@ -57,7 +57,7 @@ File::File(const File* file){
     file->GetFileBuffer(buffer_, 0, buffer_size_);
     file->GetFileBuffer(buffer_, 0, buffer_size_);
 }
 }
 
 
-File::~File(){free(buffer_);}
+File::~File(){/*free(buffer_);*/}
 
 
 void File::WriteFile(const Ogre::String& file) const{
 void File::WriteFile(const Ogre::String& file) const{
     FileSystem::WriteNewFile(file, buffer_, buffer_size_);
     FileSystem::WriteNewFile(file, buffer_, buffer_size_);

+ 0 - 16
src/core/Savemap.cpp

@@ -140,22 +140,6 @@ void Savemap::SetControlKey(const std::string control){
     empty_ = false;
     empty_ = false;
 }
 }
 
 
-/**
- * Sets the window colours in the current savemap.
- *
- * @param[in] t_l_r Top-left corner, red component.
- * @param[in] t_l_r Top-left corner, green component.
- * @param[in] t_l_r Top-left corner, blue component.
- * @param[in] t_r_r Top-right corner, red component.
- * @param[in] t_r_r Top-right corner, green component.
- * @param[in] t_r_r Top-right corner, blue component.
- * @param[in] b_r_r Bottom-right corner, red component.
- * @param[in] b_r_r Bottom-right corner, green component.
- * @param[in] b_r_r Bottom-right corner, blue component.
- * @param[in] b_l_r Bottom-left corner, red component.
- * @param[in] b_l_r Bottom-left corner, green component.
- * @param[in] b_l_r Bottom-left corner, blue component.
- */
 void Savemap::SetWindowColours(
 void Savemap::SetWindowColours(
   const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
   const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
   const unsigned int t_r_r, const unsigned int t_r_g, const unsigned int t_r_b,
   const unsigned int t_r_r, const unsigned int t_r_g, const unsigned int t_r_b,

+ 11 - 13
src/core/Savemap.h

@@ -40,7 +40,7 @@ class Savemap{
          *
          *
          * Copies a savemap into another, including control data.
          * Copies a savemap into another, including control data.
          *
          *
-         * @param[in] The savemap to copy from.
+         * @param[in] to_copy The savemap to copy from.
          */
          */
         void operator = (const Savemap &to_copy);
         void operator = (const Savemap &to_copy);
 
 
@@ -78,17 +78,17 @@ class Savemap{
          * Sets the window colours in the current savemap.
          * Sets the window colours in the current savemap.
          *
          *
          * @param[in] t_l_r Top-left corner, red component.
          * @param[in] t_l_r Top-left corner, red component.
-         * @param[in] t_l_r Top-left corner, green component.
-         * @param[in] t_l_r Top-left corner, blue component.
+         * @param[in] t_l_g Top-left corner, green component.
+         * @param[in] t_l_b Top-left corner, blue component.
          * @param[in] t_r_r Top-right corner, red component.
          * @param[in] t_r_r Top-right corner, red component.
-         * @param[in] t_r_r Top-right corner, green component.
-         * @param[in] t_r_r Top-right corner, blue component.
+         * @param[in] t_r_g Top-right corner, green component.
+         * @param[in] t_r_b Top-right corner, blue component.
          * @param[in] b_r_r Bottom-right corner, red component.
          * @param[in] b_r_r Bottom-right corner, red component.
-         * @param[in] b_r_r Bottom-right corner, green component.
-         * @param[in] b_r_r Bottom-right corner, blue component.
+         * @param[in] b_r_g Bottom-right corner, green component.
+         * @param[in] b_r_b Bottom-right corner, blue component.
          * @param[in] b_l_r Bottom-left corner, red component.
          * @param[in] b_l_r Bottom-left corner, red component.
-         * @param[in] b_l_r Bottom-left corner, green component.
-         * @param[in] b_l_r Bottom-left corner, blue component.
+         * @param[in] b_l_g Bottom-left corner, green component.
+         * @param[in] b_l_b Bottom-left corner, blue component.
          */
          */
         void SetWindowColours(
         void SetWindowColours(
           const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
           const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
@@ -201,7 +201,7 @@ class Savemap{
          * @param[in] triangle Walkmesh triangle ID.
          * @param[in] triangle Walkmesh triangle ID.
          * @param[in] angle Facing direction.
          * @param[in] angle Facing direction.
          * @param[in] field Field map ID, or empty for the world map.
          * @param[in] field Field map ID, or empty for the world map.
-         * @param[in] Name of the location to show in the save slot.
+         * @param[in] name Name of the location to show in the save slot.
          */
          */
         void SetLocation(
         void SetLocation(
           const float x, const float y, const float z,
           const float x, const float y, const float z,
@@ -345,7 +345,7 @@ class Savemap{
          * Retrieves a colour component from a window corner.
          * Retrieves a colour component from a window corner.
          *
          *
          * @param[in] corner The window corner. See {@see Corner}.
          * @param[in] corner The window corner. See {@see Corner}.
-         * @param[in] corner The color component to get. See {@see Colour}.
+         * @param[in] comp The color component to get. See {@see Colour}.
          * @return The color component of the specified corner of windows. If an invalid slot is
          * @return The color component of the specified corner of windows. If an invalid slot is
          * specified, or if the slot is empty, or if an invalid corner or color are requested, 0.
          * specified, or if the slot is empty, or if an invalid corner or color are requested, 0.
          */
          */
@@ -549,7 +549,6 @@ class Savemap{
         /**
         /**
          * Retrieves the char ID of a character.
          * Retrieves the char ID of a character.
          *
          *
-         * @param[in] slot The slot to read from.
          * @param[in] id The character ID.
          * @param[in] id The character ID.
          * @return The char ID of the character. If an invalid id is passed, or if the character
          * @return The char ID of the character. If an invalid id is passed, or if the character
          * doesn't have a char ID, -1.
          * doesn't have a char ID, -1.
@@ -559,7 +558,6 @@ class Savemap{
         /**
         /**
          * Retrieves the name of a character.
          * Retrieves the name of a character.
          *
          *
-         * @param[in] slot The slot to read from.
          * @param[in] id The character ID.
          * @param[in] id The character ID.
          * @return The name of the character. If an invalid id is passed, an empty string.
          * @return The name of the character. If an invalid id is passed, an empty string.
          */
          */

+ 15 - 14
src/core/SavemapManager.h

@@ -64,8 +64,8 @@ class SavemapManager : public Ogre::Singleton<SavemapManager>{
         /**
         /**
          * Copies the current savemap to a slot and writes the file.
          * Copies the current savemap to a slot and writes the file.
          *
          *
-         * @param slot[in] Slot at which to save.
-         * @param force[in] If false, it will check if the control data is the save (i.e. if its
+         * @param[in] slot Slot at which to save.
+         * @param[in] force If false, it will check if the control data is the save (i.e. if its
          * trying to overwrite a savemap of the same playthrought), and if they do not match, it
          * trying to overwrite a savemap of the same playthrought), and if they do not match, it
          * will do nothing and return false. If true, it will not be checked.
          * will do nothing and return false. If true, it will not be checked.
          * @return True if the data was saved, false ifforce was false and control data strings did
          * @return True if the data was saved, false ifforce was false and control data strings did
@@ -76,8 +76,9 @@ class SavemapManager : public Ogre::Singleton<SavemapManager>{
         /**
         /**
          * Saves a savemap to a slot and writes the file.
          * Saves a savemap to a slot and writes the file.
          *
          *
-         * @param slot[in] Slot at which to save.
-         * @param force[in] If false, it will check if the control data is the save (i.e. if its
+         * @param[in] savemap The savemap to save.
+         * @param[in] slot Slot at which to save.
+         * @param[in] force If false, it will check if the control data is the save (i.e. if its
          * trying to overwrite a savemap of the same playthrought), and if they do not match, it
          * trying to overwrite a savemap of the same playthrought), and if they do not match, it
          * will do nothing and return false. If true, it will not be checked.
          * will do nothing and return false. If true, it will not be checked.
          * @return True if the data was saved, false ifforce was false and control data strings did
          * @return True if the data was saved, false ifforce was false and control data strings did
@@ -112,17 +113,17 @@ class SavemapManager : public Ogre::Singleton<SavemapManager>{
          * Sets the window colours in the current savemap.
          * Sets the window colours in the current savemap.
          *
          *
          * @param[in] t_l_r Top-left corner, red component.
          * @param[in] t_l_r Top-left corner, red component.
-         * @param[in] t_l_r Top-left corner, green component.
-         * @param[in] t_l_r Top-left corner, blue component.
+         * @param[in] t_l_g Top-left corner, green component.
+         * @param[in] t_l_b Top-left corner, blue component.
          * @param[in] t_r_r Top-right corner, red component.
          * @param[in] t_r_r Top-right corner, red component.
-         * @param[in] t_r_r Top-right corner, green component.
-         * @param[in] t_r_r Top-right corner, blue component.
+         * @param[in] t_r_g Top-right corner, green component.
+         * @param[in] t_r_b Top-right corner, blue component.
          * @param[in] b_r_r Bottom-right corner, red component.
          * @param[in] b_r_r Bottom-right corner, red component.
-         * @param[in] b_r_r Bottom-right corner, green component.
-         * @param[in] b_r_r Bottom-right corner, blue component.
+         * @param[in] b_r_g Bottom-right corner, green component.
+         * @param[in] b_r_b Bottom-right corner, blue component.
          * @param[in] b_l_r Bottom-left corner, red component.
          * @param[in] b_l_r Bottom-left corner, red component.
-         * @param[in] b_l_r Bottom-left corner, green component.
-         * @param[in] b_l_r Bottom-left corner, blue component.
+         * @param[in] b_l_g Bottom-left corner, green component.
+         * @param[in] b_l_b Bottom-left corner, blue component.
          */
          */
         void SetWindowColours(
         void SetWindowColours(
           const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
           const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
@@ -235,7 +236,7 @@ class SavemapManager : public Ogre::Singleton<SavemapManager>{
          * @param[in] triangle Walkmesh triangle ID.
          * @param[in] triangle Walkmesh triangle ID.
          * @param[in] angle Facing direction.
          * @param[in] angle Facing direction.
          * @param[in] field Field map ID, or empty for the world map.
          * @param[in] field Field map ID, or empty for the world map.
-         * @param[in] Name of the location to show in the save slot.
+         * @param[in] name Name of the location to show in the save slot.
          */
          */
         void SetLocation(
         void SetLocation(
           const float x, const float y, const float z,
           const float x, const float y, const float z,
@@ -374,7 +375,7 @@ class SavemapManager : public Ogre::Singleton<SavemapManager>{
          *
          *
          * @param[in] slot The slot to read from.
          * @param[in] slot The slot to read from.
          * @param[in] corner The window corner. See {@see Savemap::Corner}.
          * @param[in] corner The window corner. See {@see Savemap::Corner}.
-         * @param[in] corner The color component to get. See {@see Savemap::Colour}.
+         * @param[in] comp The color component to get. See {@see Savemap::Colour}.
          * @return The color component of the specified corner of windows. If an invalid slot is
          * @return The color component of the specified corner of windows. If an invalid slot is
          * specified, or if the slot is empty, or if an invalid corner or color are requested, 0.
          * specified, or if the slot is empty, or if an invalid corner or color are requested, 0.
          */
          */

+ 438 - 0
src/data/GameData.h

@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+
+class GameData{
+
+    public:
+
+        class ELEMENT{
+
+            public:
+
+                /**
+                 * Fire element.
+                 */
+                static const unsigned int FIRE = 0;
+
+                /**
+                 * Ice element.
+                 */
+                static const unsigned int ICE = 1;
+
+                /**
+                 * Bolt element.
+                 */
+                static const unsigned int BOLT = 2;
+
+                /**
+                 * Earth element.
+                 */
+                static const unsigned int EARTH = 3;
+
+                /**
+                 * Poison element.
+                 */
+                static const unsigned int POISON = 4;
+
+                /**
+                 * Gravity element.
+                 */
+                static const unsigned int GRAVITY = 5;
+
+                /**
+                 * Water element.
+                 */
+                static const unsigned int WATER = 6;
+
+                /**
+                 * Wind element.
+                 */
+                static const unsigned int WIND = 7;
+
+                /**
+                 * Holy element.
+                 */
+                static const unsigned int HOLY = 8;
+
+                /**
+                 * Restorative element.
+                 */
+                static const unsigned int RESTORATIVE = 9;
+
+                /**
+                 * Cut element.
+                 */
+                static const unsigned int CUT = 10;
+
+                /**
+                 * Hit element.
+                 */
+                static const unsigned int HIT = 11;
+
+                /**
+                 * Punch element.
+                 */
+                static const unsigned int PUNCH = 12;
+
+                /**
+                 * Shoot element.
+                 */
+                static const unsigned int SHOOT = 13;
+
+                /**
+                 * Shout element.
+                 */
+                static const unsigned int SHOUT = 14;
+
+                /**
+                 * Hidden element.
+                 */
+                static const unsigned int HIDDEN = 15;
+        };
+
+        /**
+         * List of target selection options.
+         */
+        class TARGET_MODES{
+
+            public:
+
+                /**
+                 * The target can be selected.
+                 *
+                 * If the target of the attack is selectable, at least between some subset of
+                 * possible targets, this must be true.
+                 */
+                static const unsigned int SELECTION_ENABLED = 0;
+
+                /**
+                 * The default target is an enemy.
+                 *
+                 * When true, the cursor starts in an enemy row. When false, it starts in the ally
+                 * row. It may or may not be possible to change it.
+                 */
+                static const unsigned int DEFAULT_ENEMY = 1;
+
+                /**
+                 * The default target is a group.
+                 *
+                 * When true, the cursor starts will all the members of a row selected. When false,
+                 * it starts with only one target selected. It may or may not be possible to change
+                 * it.
+                 */
+                static const unsigned int DEFAULT_MULTIPLE = 2;
+
+                /**
+                 * 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.
+                 */
+                static const unsigned int TOGGLE_MULTIPLE = 3;
+
+                /**
+                 * The target group can be changed.
+                 *
+                 * When true, the target cannot be changed between enemies and allies. In this case
+                 * {@see default_enemy} will determine the group that can be targeted.
+                 */
+                static const unsigned int FIXED_ROW = 4;
+
+                /**
+                 * The command is short ranged.
+                 *
+                 * When true, if the target or the caster is not in the front of their row, the
+                 * target will take half damage. Also, when true, an enemy covered by another enemy
+                 * can't be targeted.
+                 */
+                static const unsigned int SHORT_RANGE = 5;
+
+                /**
+                 * Targets all, enemies and allies.
+                 *
+                 * When true, all allies and enemies are targets, and the selection can't be changed.
+                 */
+                static const unsigned int ALL_ROWS = 6;
+
+                /**
+                 * The target is random.
+                 *
+                 * When multiple targets are selected, one will be selected at random to be the
+                 * receiving target. Cursor will cycle among all viable targets.
+                 */
+                static const unsigned int RANDOM = 7;
+
+                /**
+                 * Retrieves a descriptive string from an option ID.
+                 *
+                 * @param[in] id Option ID.
+                 * @return Option lowercase description. "unknown_target_option" for unknown
+                 * options.
+                 */
+                static std::string to_string(const unsigned int id){
+                    switch(id){
+                        case SELECTION_ENABLED: return "selection_enabled";
+                        case DEFAULT_ENEMY: return "default_enemy";
+                        case DEFAULT_MULTIPLE: return "default_multiple";
+                        case TOGGLE_MULTIPLE: return "toggle_multiple";
+                        case FIXED_ROW: return "fixed_row";
+                        case SHORT_RANGE: return "short_range";
+                        case ALL_ROWS: return "all_rows";
+                        case RANDOM: return "random";
+                        default: return "unknown_target_option";
+                    }
+                }
+        };
+
+        /**
+         * Determines what an attack (or item) restores.
+         */
+        enum class RestoreType{
+
+            /**
+             * The attack affects a party member or group HP.
+             */
+            HP = 0,
+
+            /**
+             * The attack affects a party member or group MP.
+             */
+            MP = 1,
+
+            /**
+             * The attack affects a party member or group status.
+             */
+            STATUS = 2,
+
+            /**
+             * The attack is not restorative.
+             */
+            NONE = 3
+        };
+
+        /**
+         * List of altered status.
+         */
+        class STATUS{
+
+            public:
+
+                /**
+                 * The character is dead.
+                 */
+                static const unsigned int DEATH = 0;
+
+                /**
+                 * The character has critical HP.
+                 */
+                static const unsigned int NEAR_DEATH = 1;
+
+                /**
+                 * The character is asleep.
+                 */
+                static const unsigned int SLEEP = 2;
+
+                /**
+                 * The character is poisoned.
+                 */
+                static const unsigned int POISONED = 3;
+
+                /**
+                 * The character is sad.
+                 */
+                static const unsigned int SADNESS = 4;
+
+                /**
+                 * The character is furious.
+                 */
+                static const unsigned int FURY = 5;
+
+                /**
+                 * The character is confused.
+                 */
+                static const unsigned int CONFU = 5;
+
+                /**
+                 * The character is silenced.
+                 */
+                static const unsigned int SILENCE = 6;
+
+                /**
+                 * The character is slow.
+                 */
+                static const unsigned int HASTE = 7;
+
+                /**
+                 * The character is
+                 */
+                static const unsigned int SLOW = 8;
+
+                /**
+                 * The character is stopped.
+                 */
+                static const unsigned int STOP = 9;
+
+                /**
+                 * The character is a frog.
+                 */
+                static const unsigned int FROG = 10;
+
+                /**
+                 * The character is small.
+                 */
+                static const unsigned int SMALL = 11;
+
+                /**
+                 * The character is going to be petrified.
+                 */
+                static const unsigned int SLOW_NUMB = 12;
+
+                /**
+                 * The character is petrified.
+                 */
+                static const unsigned int PETRIFY = 13;
+
+                /**
+                 * The character is regenerating health.
+                 */
+                static const unsigned int REGEN = 14;
+
+                /**
+                 * The character has a physical barrier.
+                 */
+                static const unsigned int BARRIER = 15;
+
+                /**
+                 * The character has a magical barrier
+                 */
+                static const unsigned int M_BARRIER = 16;
+
+                /**
+                 * The character reflects magical attacks.
+                 */
+                static const unsigned int REFLECT = 17;
+
+                /**
+                 * The character has dual status.
+                 *
+                 * @todo What does it do.
+                 */
+                static const unsigned int DUAL = 18;
+
+                /**
+                 * The character has a shield.
+                 */
+                static const unsigned int SHIELD = 19;
+
+                /**
+                 * The character has a death sentence.
+                 */
+                static const unsigned int D_SENTENCE = 20;
+
+                /**
+                 * The character is being manipulated.
+                 */
+                static const unsigned int MANIPULATE = 21;
+
+                /**
+                 * The character is berserk.
+                 */
+                static const unsigned int BERSERK = 22;
+
+                /**
+                 * The character is invincible.
+                 */
+                static const unsigned int PEERLESS = 23;
+
+                /**
+                 * The character is paralyzed.
+                 */
+                static const unsigned int PARALYSIS = 24;
+
+                /**
+                 * The character is blinded.
+                 */
+                static const unsigned int DARKNESS = 25;
+
+                /**
+                 * The character has dual drain.
+                 */
+                static const unsigned int DUAL_DRAIN = 26;
+
+                /**
+                 * The character has deathforce.
+                 */
+                static const unsigned int DEATH_FORCE = 27;
+
+                /**
+                 * The character has resist.
+                 */
+                static const unsigned int RESIST = 28;
+
+                /**
+                 * Cait' Sith lucky girl mode.
+                 */
+                static const unsigned int LUCKY_GIRL = 29;
+
+                /**
+                 * The character is imprisioned.
+                 */
+                static const unsigned int IMPRISONED = 30;
+        };
+
+        /**
+         * How a status effect modifier attack or item can act.
+         */
+        class STATUS_EFFECT_MODE{
+
+            public:
+
+                /**
+                 * Inflicts status changes.
+                 */
+                static const unsigned int INFLICT = 0;
+
+                /**
+                 * Cures status changes.
+                 */
+                static const unsigned int CURE = 1;
+
+                /**
+                 * Toggles status changes.
+                 *
+                 * If the target doesn't have the status change, inflict it. If the target already
+                 * has the status change, cure it.
+                 */
+                static const unsigned int TOGGLE = 2;
+
+                /**
+                 * Retrieves a descriptive string from an mode ID.
+                 *
+                 * @param[in] id Mode ID.
+                 * @return Mode lowercase description. "unknown_effect_mode" for unknown modes.
+                 */
+                static std::string to_string(const unsigned int id){
+                    switch(id){
+                        case INFLICT: return "inflict";
+                        case CURE: return "cure";
+                        case TOGGLE: return "toggle";
+                        default: return "unknown_effect_mode";
+                    }
+                }
+        };
+};

+ 530 - 0
src/installer/BattleDataInstaller.cpp

@@ -0,0 +1,530 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <zlib.h>
+#include <tinyxml.h>
+#include "BattleDataInstaller.h"
+
+BattleDataInstaller::BattleDataInstaller(const std::string input_dir, const std::string output_dir):
+  input_dir_(input_dir), output_dir_(output_dir),
+  scene_bin_(input_dir + "data/battle/scene.bin"), total_scenes_(0), next_scene_(0)
+{}
+
+BattleDataInstaller::~BattleDataInstaller(){}
+
+unsigned int BattleDataInstaller::Initialize(){
+    next_scene_ = 0;
+    scenes_.clear();
+    enemies_.clear();
+    for (int i = 0; i < 32; i ++){
+        File scene_bin_block(&scene_bin_, i * 0x2000, 0x2000);
+        for (int o = 0; o < 10; o ++){
+            scene_bin_block.SetOffset(4 * o);
+            u32 start_offset = scene_bin_block.readU32LE() * 4;
+            u32 end_offset = scene_bin_block.readU32LE() * 4;
+            if (end_offset > start_offset + 0x2000) end_offset = start_offset + 0x2000;
+            if (start_offset >= 0 && start_offset < 0x2000){
+                File scene_bin_fragment(
+                  &scene_bin_block, start_offset, end_offset - start_offset - 1
+                );
+                File* extracted = ExtractGZipScene(scene_bin_fragment);
+                if (extracted != NULL){
+                    scenes_uncompressed_.push_back(extracted);
+                    total_scenes_ ++;
+                }
+            }
+            else break;
+        }
+    }
+    return total_scenes_;
+}
+
+unsigned int BattleDataInstaller::ProcessScene(){
+    if (next_scene_ >= scenes_uncompressed_.size()) return false;
+    BattleSceneFile scene(next_scene_, scenes_uncompressed_[next_scene_]);
+    scenes_.push_back(scene);
+    // Add new enemies to the list.
+    std::vector<Enemy> enemies = scene.GetEnemies();
+    for (int e = 0; e < enemies.size(); e ++){
+        bool already_added = false;
+        for (int l = 0; l < enemies_.size(); l ++){
+            if (enemies_[l].id == enemies[e].id){
+                already_added = true;
+                break;
+            }
+        }
+        if (already_added) continue;
+        enemies_.push_back(enemies[e]);
+    }
+    // Add new attacks to the list.
+    std::vector<Attack> attacks = scene.GetAttacks();
+    for (int a = 0; a < attacks.size(); a ++){
+        bool already_added = false;
+        for (int l = 0; l < attacks_.size(); l ++){
+            if (attacks_[l].id == attacks[a].id){
+                already_added = true;
+                break;
+            }
+        }
+        if (already_added) continue;
+        attacks_.push_back(attacks[a]);
+    }
+    // Add new formations to the list.
+    std::vector<Formation> formations = scene.GetFormations();
+    for (Formation f : formations){
+        f.id = formations_.size(); // Assign the next ID.
+        formations_.push_back(f);
+    }
+    next_scene_ ++;
+    return next_scene_;
+}
+
+void BattleDataInstaller::WriteEnemies(){
+    for (Enemy enemy : enemies_){
+        //std::cout << "Writting enemy " << enemy.id << ": " << (output_dir_ + "game/enemy/" + std::to_string(enemy.id) + ".xml") << "\n";
+        TiXmlDocument xml;
+        std::unique_ptr<TiXmlElement> container(new TiXmlElement("Enemy"));
+        container->SetAttribute("id", enemy.id);
+        container->SetAttribute("name", enemy.name);
+        container->SetAttribute("level", enemy.level);
+        container->SetAttribute("exp", enemy.exp);
+        container->SetAttribute("ap", enemy.ap);
+        container->SetAttribute("money", enemy.money);
+        container->SetAttribute("morph", enemy.morph);
+        container->SetDoubleAttribute("back_damage", enemy.back_damage);
+        std::unique_ptr<TiXmlElement> stats(new TiXmlElement("Stats"));
+        std::unique_ptr<TiXmlElement> stat_str(new TiXmlElement("Stat"));
+        stat_str->SetAttribute("id", "str");
+        stat_str->SetAttribute("value", enemy.str);
+        stats->LinkEndChild(stat_str.release());
+        std::unique_ptr<TiXmlElement> stat_mag(new TiXmlElement("Stat"));
+        stat_mag->SetAttribute("id", "mag");
+        stat_mag->SetAttribute("value", enemy.mag);
+        stats->LinkEndChild(stat_mag.release());
+        std::unique_ptr<TiXmlElement> stat_def(new TiXmlElement("Stat"));
+        stat_def->SetAttribute("id", "def");
+        stat_def->SetAttribute("value", enemy.def);
+        stats->LinkEndChild(stat_def.release());
+        std::unique_ptr<TiXmlElement> stat_mdef(new TiXmlElement("Stat"));
+        stat_mdef->SetAttribute("id", "mdef");
+        stat_mdef->SetAttribute("value", enemy.mdef);
+        stats->LinkEndChild(stat_mdef.release());
+        std::unique_ptr<TiXmlElement> stat_spd(new TiXmlElement("Stat"));
+        stat_spd->SetAttribute("id", "spd");
+        stat_spd->SetAttribute("value", enemy.spd);
+        stats->LinkEndChild(stat_spd.release());
+        std::unique_ptr<TiXmlElement> stat_lck(new TiXmlElement("Stat"));
+        stat_lck->SetAttribute("id", "lck");
+        stat_lck->SetAttribute("value", enemy.lck);
+        stats->LinkEndChild(stat_lck.release());
+        std::unique_ptr<TiXmlElement> stat_eva(new TiXmlElement("Stat"));
+        stat_eva->SetAttribute("id", "eva");
+        stat_eva->SetAttribute("value", enemy.eva);
+        stats->LinkEndChild(stat_eva.release());
+        container->LinkEndChild(stats.release());
+        std::unique_ptr<TiXmlElement> elements(new TiXmlElement("Elements"));
+        for (Enemy::Element element : enemy.elements){
+            std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("Element"));
+            xml_element->SetAttribute("id", element.id);
+            xml_element->SetDoubleAttribute("factor", element.factor);
+            elements->LinkEndChild(xml_element.release());
+        }
+        container->LinkEndChild(elements.release());
+        std::unique_ptr<TiXmlElement> immunities(new TiXmlElement("Immunities"));
+        for (Enemy::Immunity immunity : enemy.immunities){
+            std::unique_ptr<TiXmlElement> xml_immunity(new TiXmlElement("Immunity"));
+            xml_immunity->SetAttribute("status", immunity.status);
+            xml_immunity->SetDoubleAttribute("rate", immunity.rate);
+            immunities->LinkEndChild(xml_immunity.release());
+        }
+        container->LinkEndChild(immunities.release());
+        std::unique_ptr<TiXmlElement> attacks(new TiXmlElement("Attacks"));
+        for (Enemy::Attack attack : enemy.attacks){
+            std::unique_ptr<TiXmlElement> xml_attack(new TiXmlElement("Attack"));
+            xml_attack->SetAttribute("status", attack.id);
+            xml_attack->SetAttribute("camera", attack.camera);
+            attacks->LinkEndChild(xml_attack.release());
+        }
+        container->LinkEndChild(attacks.release());
+        std::unique_ptr<TiXmlElement> manip_attacks(new TiXmlElement("ManipulateAttacks"));
+        for (u16 attack : enemy.manipulate_attacks){
+            std::unique_ptr<TiXmlElement> xml_manip_attack(new TiXmlElement("Attack"));
+            xml_manip_attack->SetAttribute("id", attack);
+            manip_attacks->LinkEndChild(xml_manip_attack.release());
+        }
+        container->LinkEndChild(manip_attacks.release());
+        std::unique_ptr<TiXmlElement> steals(new TiXmlElement("Steal"));
+        for (Enemy::Item item : enemy.steal){
+            std::unique_ptr<TiXmlElement> xml_steal(new TiXmlElement("Item"));
+            xml_steal->SetAttribute("id", item.id);
+            xml_steal->SetDoubleAttribute("rate", item.rate);
+            steals->LinkEndChild(xml_steal.release());
+        }
+        container->LinkEndChild(steals.release());
+        std::unique_ptr<TiXmlElement> drops(new TiXmlElement("Drops"));
+        for (Enemy::Item item : enemy.drop){
+            std::unique_ptr<TiXmlElement> xml_drop(new TiXmlElement("Item"));
+            xml_drop->SetAttribute("id", item.id);
+            xml_drop->SetDoubleAttribute("rate", item.rate);
+            drops->LinkEndChild(xml_drop.release());
+        }
+        container->LinkEndChild(drops.release());
+        std::unique_ptr<TiXmlElement> animations(new TiXmlElement("Animations"));
+        for (unsigned int animation : enemy.animations){
+            std::unique_ptr<TiXmlElement> xml_animation(new TiXmlElement("Animation"));
+            xml_animation->SetAttribute("id", animation);
+            animations->LinkEndChild(xml_animation.release());
+        }
+        container->LinkEndChild(animations.release());
+        xml.LinkEndChild(container.release());
+        std::string file_name = BuildEnemyFileName(enemy);
+        xml.SaveFile(output_dir_ + file_name);
+        enemy_map_[enemy.id] = file_name;
+
+    }
+}
+
+void BattleDataInstaller::WriteAttacks(){
+    //std::cout << "WRITE ATTACKS TOTAL: " << attacks_.size() << "\n";
+    for (Attack attack : attacks_){
+        TiXmlDocument xml;
+        std::unique_ptr<TiXmlElement> container(new TiXmlElement("Attack"));
+        container->SetAttribute("id", attack.id);
+        container->SetAttribute("name", attack.name);
+        container->SetAttribute("mp", attack.mp);
+        container->SetAttribute("reflectable", attack.reflectable);
+        std::unique_ptr<TiXmlElement> damage(new TiXmlElement("Damage"));
+        std::unique_ptr<TiXmlElement> damage_power(new TiXmlElement("Parameter"));
+        damage_power->SetAttribute("id", "power");
+        damage_power->SetAttribute("value", attack.damage.power);
+        damage->LinkEndChild(damage_power.release());
+        std::unique_ptr<TiXmlElement> damage_formula(new TiXmlElement("Parameter"));
+        damage_formula->SetAttribute("id", "formula");
+        damage_formula->SetAttribute("value", attack.damage.formula);
+        damage->LinkEndChild(damage_formula.release());
+        std::unique_ptr<TiXmlElement> damage_modifier(new TiXmlElement("Parameter"));
+        damage_modifier->SetAttribute("id", "modifier");
+        damage_modifier->SetAttribute("value", attack.damage.modifier);
+        damage->LinkEndChild(damage_modifier.release());
+        std::unique_ptr<TiXmlElement> damage_acc(new TiXmlElement("Parameter"));
+        damage_acc->SetAttribute("id", "accuracy");
+        damage_acc->SetAttribute("value", attack.damage.accuracy);
+        damage->LinkEndChild(damage_acc.release());
+        std::unique_ptr<TiXmlElement> damage_darkness(new TiXmlElement("Parameter"));
+        damage_darkness->SetAttribute("id", "affected_by_darkness");
+        damage_darkness->SetAttribute("value", attack.damage.affected_by_darkness);
+        damage->LinkEndChild(damage_darkness.release());
+        std::unique_ptr<TiXmlElement> damage_restore(new TiXmlElement("Parameter"));
+        damage_restore->SetAttribute("id", "restore");
+        damage_restore->SetAttribute("value", static_cast<int>(attack.damage.restore));
+        damage->LinkEndChild(damage_restore.release());
+        std::unique_ptr<TiXmlElement> damage_mp(new TiXmlElement("Parameter"));
+        damage_mp->SetAttribute("id", "damage_mp");
+        damage_mp->SetAttribute("value", attack.damage.damage_mp);
+        damage->LinkEndChild(damage_mp.release());
+        std::unique_ptr<TiXmlElement> damage_drain_hp(new TiXmlElement("Parameter"));
+        damage_drain_hp->SetAttribute("id", "drain_hp");
+        damage_drain_hp->SetAttribute("value", attack.damage.drain_hp);
+        damage->LinkEndChild(damage_drain_hp.release());
+        std::unique_ptr<TiXmlElement> damage_drain_mp(new TiXmlElement("Parameter"));
+        damage_drain_mp->SetAttribute("id", "drain_mp");
+        damage_drain_mp->SetAttribute("value", attack.damage.drain_mp);
+        damage->LinkEndChild(damage_drain_mp.release());
+        std::unique_ptr<TiXmlElement> damage_ignore(new TiXmlElement("Parameter"));
+        damage_ignore->SetAttribute("id", "ignore_status_defense");
+        damage_ignore->SetAttribute("value", attack.damage.ignore_status_defense);
+        damage->LinkEndChild(damage_ignore.release());
+        std::unique_ptr<TiXmlElement> damage_critical(new TiXmlElement("Parameter"));
+        damage_critical->SetAttribute("id", "always_critical");
+        damage_critical->SetAttribute("value", attack.damage.critical);
+        damage->LinkEndChild(damage_critical.release());
+        container->LinkEndChild(damage.release());
+        std::unique_ptr<TiXmlElement> target_opts(new TiXmlElement("Target"));
+        std::unique_ptr<TiXmlElement> target_se(new TiXmlElement("Parameter"));
+        target_se->SetAttribute("id", "selection_enabled");
+        target_se->SetAttribute("value", attack.target.selection_enabled);
+        target_opts->LinkEndChild(target_se.release());
+        std::unique_ptr<TiXmlElement> target_de(new TiXmlElement("Parameter"));
+        target_de->SetAttribute("id", "default_enemy");
+        target_de->SetAttribute("value", attack.target.default_enemy);
+        target_opts->LinkEndChild(target_de.release());
+        std::unique_ptr<TiXmlElement> target_dm(new TiXmlElement("Parameter"));
+        target_dm->SetAttribute("id", "default_multiple");
+        target_dm->SetAttribute("value", attack.target.default_multiple);
+        target_opts->LinkEndChild(target_dm.release());
+        std::unique_ptr<TiXmlElement> target_tm(new TiXmlElement("Parameter"));
+        target_tm->SetAttribute("id", "toggle_multiple");
+        target_tm->SetAttribute("value", attack.target.toggle_multiple);
+        target_opts->LinkEndChild(target_tm.release());
+        std::unique_ptr<TiXmlElement> target_fr(new TiXmlElement("Parameter"));
+        target_fr->SetAttribute("id", "fixed_row");
+        target_fr->SetAttribute("value", attack.target.fixed_row);
+        target_opts->LinkEndChild(target_fr.release());
+        std::unique_ptr<TiXmlElement> target_sr(new TiXmlElement("Parameter"));
+        target_sr->SetAttribute("id", "short_range");
+        target_sr->SetAttribute("value", attack.target.short_range);
+        target_opts->LinkEndChild(target_sr.release());
+        std::unique_ptr<TiXmlElement> target_ar(new TiXmlElement("Parameter"));
+        target_ar->SetAttribute("id", "all_rows");
+        target_ar->SetAttribute("value", attack.target.all_rows);
+        target_opts->LinkEndChild(target_ar.release());
+        std::unique_ptr<TiXmlElement> target_rd(new TiXmlElement("Parameter"));
+        target_rd->SetAttribute("id", "random");
+        target_rd->SetAttribute("value", attack.target.random);
+        target_opts->LinkEndChild(target_rd.release());
+        std::unique_ptr<TiXmlElement> target_od(new TiXmlElement("Parameter"));
+        target_od->SetAttribute("id", "only_for_dead");
+        target_od->SetAttribute("value", attack.target.only_for_dead);
+        target_opts->LinkEndChild(target_od.release());
+        std::unique_ptr<TiXmlElement> target_nr(new TiXmlElement("Parameter"));
+        target_nr->SetAttribute("id", "no_retarget");
+        target_nr->SetAttribute("value", attack.target.no_re_target);
+        target_opts->LinkEndChild(target_nr.release());
+        container->LinkEndChild(target_opts.release());
+        std::unique_ptr<TiXmlElement> effects(new TiXmlElement("Effects"));
+        for (Attack::StatusEffect effect : attack.status_effects){
+            std::unique_ptr<TiXmlElement> xml_effect(new TiXmlElement("Effect"));
+            xml_effect->SetAttribute("id", effect.id);
+            xml_effect->SetDoubleAttribute("chance", effect.chance);
+            xml_effect->SetAttribute("mode", GameData::STATUS_EFFECT_MODE::to_string(effect.type));
+            effects->LinkEndChild(xml_effect.release());
+        }
+        container->LinkEndChild(effects.release());
+        std::unique_ptr<TiXmlElement> elements(new TiXmlElement("Elements"));
+        for (unsigned int element : attack.elements){
+            std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("Element"));
+            xml_element->SetAttribute("id", element);
+            elements->LinkEndChild(xml_element.release());
+        }
+        container->LinkEndChild(elements.release());
+        std::unique_ptr<TiXmlElement> anim(new TiXmlElement("Animation"));
+        std::unique_ptr<TiXmlElement> anim_e(new TiXmlElement("Parameter"));
+        anim_e->SetAttribute("id", "effect");
+        anim_e->SetAttribute("value", attack.animation.effect);
+        anim->LinkEndChild(anim_e.release());
+        std::unique_ptr<TiXmlElement> anim_ie(new TiXmlElement("Parameter"));
+        anim_ie->SetAttribute("id", "impact_effect");
+        anim_ie->SetAttribute("value", attack.animation.impact_effect);
+        anim->LinkEndChild(anim_ie.release());
+        std::unique_ptr<TiXmlElement> anim_s(new TiXmlElement("Parameter"));
+        anim_s->SetAttribute("id", "sound");
+        anim_s->SetAttribute("value", attack.animation.sound);
+        anim->LinkEndChild(anim_s.release());
+        std::unique_ptr<TiXmlElement> anim_ha(new TiXmlElement("Parameter"));
+        anim_ha->SetAttribute("id", "hurt_anim");
+        anim_ha->SetAttribute("value", attack.animation.hurt_anim);
+        anim->LinkEndChild(anim_ha.release());
+        std::unique_ptr<TiXmlElement> anim_c(new TiXmlElement("Parameter"));
+        anim_c->SetAttribute("id", "camera_single");
+        anim_c->SetAttribute("value", attack.animation.camera_single);
+        anim->LinkEndChild(anim_c.release());
+        std::unique_ptr<TiXmlElement> anim_cm(new TiXmlElement("Parameter"));
+        anim_cm->SetAttribute("id", "camera_multiple");
+        anim_cm->SetAttribute("value", attack.animation.camera_multiple);
+        anim->LinkEndChild(anim_cm.release());
+        container->LinkEndChild(anim.release());
+        xml.LinkEndChild(container.release());
+        std::string file_name = BuildAttackFileName(attack);
+        xml.SaveFile(output_dir_ + file_name);
+        attack_map_[attack.id] = file_name;
+    }
+}
+
+void BattleDataInstaller::WriteFormations(){
+    for (Formation formation : formations_){
+        //std::cout << "Writting enemy " << enemy.id << ": " << (output_dir_ + "game/enemy/" + std::to_string(enemy.id) + ".xml") << "\n";
+        TiXmlDocument xml;
+        std::unique_ptr<TiXmlElement> container(new TiXmlElement("Formation"));
+        container->SetAttribute("id", formation.id);
+        container->SetAttribute("next", formation.next_formation);
+        container->SetDoubleAttribute("escape_difficulty", formation.escape_counter);
+        container->SetAttribute("escapable", formation.escapable);
+        container->SetAttribute("skip_victory_pose", formation.skip_victory_pose);
+        container->SetAttribute("skip_spoils", formation.skip_spoils);
+        container->SetAttribute("skip_victory_pose", formation.skip_victory_pose);
+        container->SetAttribute("preemptive_disabled", formation.preemptive_disabled);
+        container->SetAttribute("layout", static_cast<int>(formation.layout));
+        std::unique_ptr<TiXmlElement> location(new TiXmlElement("Location"));
+        location->SetAttribute("id", formation.location);
+        location->SetAttribute("name", formation.location_name);
+        container->LinkEndChild(location.release());
+        std::unique_ptr<TiXmlElement> enemies(new TiXmlElement("Enemies"));
+        for (Formation::Enemy enemy : formation.enemies){
+            std::unique_ptr<TiXmlElement> enemy_xml(new TiXmlElement("Enemy"));
+            enemy_xml->SetAttribute("id", enemy.id);
+            enemy_xml->SetAttribute("x", enemy.x);
+            enemy_xml->SetAttribute("y", enemy.y);
+            enemy_xml->SetAttribute("z", enemy.z);
+            enemy_xml->SetAttribute("row", enemy.row);
+            std::string cover = "";
+            for (int c = 0; c < 5; c ++) cover += enemy.cover[c];
+            enemy_xml->SetAttribute("cover", cover);
+            enemy_xml->SetAttribute("visible", enemy.visible);
+            enemy_xml->SetAttribute("direction", enemy.direction);
+            enemy_xml->SetAttribute("targeteable", enemy.targeteable);
+            enemy_xml->SetAttribute("main_script_active", enemy.main_script_active);
+            enemies->LinkEndChild(enemy_xml.release());
+        }
+        container->LinkEndChild(enemies.release());
+        std::unique_ptr<TiXmlElement> cameras(new TiXmlElement("Camera"));
+        cameras->SetAttribute("initial", formation.initial_camera_position);
+        int c = 0;
+        for (Formation::Camera camera : formation.camera_positions){
+            std::unique_ptr<TiXmlElement> camera_xml(new TiXmlElement("Position"));
+            camera_xml->SetAttribute("id", c);
+            camera_xml->SetAttribute("x", camera.x);
+            camera_xml->SetAttribute("y", camera.y);
+            camera_xml->SetAttribute("z", camera.z);
+            camera_xml->SetAttribute("direction_x", camera.d_x);
+            camera_xml->SetAttribute("direction_y", camera.d_y);
+            camera_xml->SetAttribute("direction_z", camera.d_z);
+            cameras->LinkEndChild(camera_xml.release());
+            c ++;
+        }
+        container->LinkEndChild(cameras.release());
+        std::unique_ptr<TiXmlElement> arena(new TiXmlElement("Arena"));
+        arena->SetAttribute("is_arena", formation.is_arena_battle);
+        for (int next : formation.next_arena_formation_candidates){
+            std::unique_ptr<TiXmlElement> next_xml(new TiXmlElement("NextCandidate"));
+            next_xml->SetAttribute("id", next);
+            arena->LinkEndChild(next_xml.release());
+        }
+        container->LinkEndChild(arena.release());
+        xml.LinkEndChild(container.release());
+        std::string file_name = std::to_string(formation.id);
+        while (file_name.length() < 4) file_name = "0" + file_name;
+        file_name = "game/formation/" + file_name + ".xml";
+        xml.SaveFile(output_dir_ + file_name);
+        formation_map_[formation.id] = file_name;
+    }
+}
+
+std::string BattleDataInstaller::BuildEnemyFileName(Enemy enemy){
+    std::string file_name = "game/enemy/";
+    std::string id = std::to_string(enemy.id);
+    while (id.size() < 4) id = "0" + id;
+    file_name += (id + "_");
+    for (int n = 0; n < enemy.name.size(); n ++){
+        if (
+          (enemy.name[n] >= '0' && enemy.name[n] <= '9')
+          || (enemy.name[n] >= 'a' && enemy.name[n] <= 'z')
+          || (enemy.name[n] >= 'A' && enemy.name[n] <= 'Z')
+        ){
+            file_name += enemy.name[n];
+        }
+    }
+    file_name += ".xml";
+    return file_name;
+}
+
+std::string BattleDataInstaller::BuildAttackFileName(Attack attack){
+    std::string file_name = "game/attack/";
+    std::string id = std::to_string(attack.id);
+    while (id.size() < 4) id = "0" + id;
+    file_name += (id + "_");
+    for (int n = 0; n < attack.name.size(); n ++){
+        if (
+          (attack.name[n] >= '0' && attack.name[n] <= '9')
+          || (attack.name[n] >= 'a' && attack.name[n] <= 'z')
+          || (attack.name[n] >= 'A' && attack.name[n] <= 'Z')
+        ){
+            file_name += attack.name[n];
+        }
+    }
+    file_name += ".xml";
+    return file_name;
+}
+
+File* BattleDataInstaller::ExtractGZipScene(File file){
+    u32 offset = 0;
+    u8* file_buffer = static_cast<u8*>(malloc(file.GetFileSize()));
+    file.GetFileBuffer(file_buffer, 0, file.GetFileSize());
+    u32 extract_size = 256 * 1024;
+    u8* extract_buffer = static_cast<u8*>(malloc(extract_size));
+    int ret;
+    z_stream strm;
+
+    strm.zalloc = Z_NULL; // Used to allocate the internal state.
+    strm.zfree = Z_NULL; // Used to free the internal state.
+    strm.opaque = Z_NULL; // Private data object passed to zalloc and zfree.
+    strm.next_in = file_buffer + offset; //Next input byte.
+    strm.avail_in = file.GetFileSize() - offset; // Number of bytes available at next_in.
+    strm.next_out = extract_buffer; // Next output byte should be put there.
+    strm.avail_out = extract_size; // Remaining free space at next_out.
+    ret = inflateInit2(&strm, 15 + 32);
+    if (ret != Z_OK){
+        switch (ret){
+            case Z_MEM_ERROR:
+                inflateEnd(&strm);
+                std::cout << "Warning: inflateInit2 - Z_MEM_ERROR in file " << std::endl;
+                break;
+            case Z_STREAM_ERROR:
+                inflateEnd(&strm);
+                std::cout << "Warning: inflateInit2 - Z_STREAM_ERROR in file " << std::endl;
+                break;
+            default:
+                inflateEnd(&strm);
+                std::cout << "Warning: inflateInit2 - unknown error in file " << std::endl;
+        }
+        return NULL;
+    }
+    do{
+        if (strm.next_out == NULL){
+            inflateEnd(&strm);
+            std::cout << "Warning: strm.next_out == NULL in file " << std::endl;
+            return NULL;
+        }
+        ret = inflate(&strm, Z_NO_FLUSH);
+        assert(ret != Z_STREAM_ERROR);
+        switch (ret){
+            case Z_NEED_DICT:
+                inflateEnd(&strm);
+                std::cout << "Warning: inflate - Z_NEED_DICT in file " << std::endl;
+                return NULL;
+            case Z_DATA_ERROR:
+                inflateEnd(&strm);
+                std::cout << "Warning: inflate - Z_DATA_ERROR in file " << std::endl;
+                return NULL;
+            case Z_MEM_ERROR:
+                inflateEnd(&strm);
+                std::cout << "Warning: inflate - Z_MEM_ERROR in file " << std::endl;
+                return NULL;
+        }
+
+        if (ret != Z_STREAM_END){
+            extract_buffer = static_cast<u8*>(realloc(extract_buffer, extract_size * 2));
+            if (extract_buffer == NULL){
+                inflateEnd(&strm);
+                std::cout << "Warning: extract_buffer == NULL in file " << std::endl;
+                return NULL;
+            }
+            strm.next_out = static_cast<u8*>(extract_buffer + extract_size);
+            strm.avail_out = extract_size;
+            extract_size *= 2;
+        }
+    } while (ret != Z_STREAM_END);
+    extract_size = extract_size - strm.avail_out;
+    (void)inflateEnd(&strm);
+    if (ret != Z_STREAM_END){
+        std::cout << "Warning: ret != Z_STREAM_END in file " << std::endl;
+        return NULL;
+    }
+    File* out_file = new File(extract_buffer, 0, extract_size);
+    return out_file;
+}

+ 175 - 0
src/installer/BattleDataInstaller.h

@@ -0,0 +1,175 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/BinGZipFile.h"
+#include "common/TypeDefine.h"
+#include "data/BattleSceneFile.h"
+
+/**
+ * The battle data installer.
+ */
+class BattleDataInstaller{
+
+
+    public:
+
+        /**
+         * Constructor
+         *
+         * @param[in] input_dir Path to the directory containing the original data to parse.
+         * @param[in] output_dir Path to the directory of the installation data.
+         */
+        BattleDataInstaller(const std::string input_dir, const std::string output_dir);
+
+        /**
+         * Destructor.
+         */
+        ~BattleDataInstaller();
+
+        /**
+         * Prepares the installer.
+         *
+         * @return The total number of scenes.
+         */
+        unsigned int Initialize();
+
+        /**
+         * Processes the next battle scene.
+         *
+         * @return The total number of processed scenes.
+         */
+        unsigned int ProcessScene();
+
+        /**
+         * Writes enemy data to files.
+         */
+        void WriteEnemies();
+
+        /**
+         * Writes attack data to files.
+         */
+        void WriteAttacks();
+
+        /**
+         * Writes formation data to files.
+         */
+        void WriteFormations();
+
+    private:
+
+        /**
+         * Extract a gzipped file.
+         *
+         * @param[in] file Compressed file.
+         * @return Uncompressed file. NULL if errors were encountered (they will be logged to
+         * console).
+         */
+        File* ExtractGZipScene(File file);
+
+        /**
+         * Constructs a file name for the XML file to save an enemy to.
+         *
+         * The filename will be "dame/enemy/[ID]_[NAME].xml" where [ID] is the enemy ID, padded
+         * with '0' to a length of 4, and [NAME] is the enemy name using only alphanumeric
+         * characters.
+         *
+         * @param[in] enemy The enemy to be saved.
+         * @return The name for the enemy file.
+         */
+        std::string BuildEnemyFileName(Enemy enemy);
+
+        /**
+         * Constructs a file name for the XML file to save an attack to.
+         *
+         * The filename will be "dame/attack/[ID]_[NAME].xml" where [ID] is the attack ID, padded
+         * with '0' to a length of 4, and [NAME] is the attack name using only alphanumeric
+         * characters.
+         *
+         * @param[in] attack The attack to be saved.
+         * @return The name for the attack file.
+         */
+        std::string BuildAttackFileName(Attack attack);
+
+        /**
+         * The path to the directory from which to read the PC game data.
+         */
+        std::string input_dir_;
+
+        /**
+         * The path to the directory where to save the V-Gears data.
+         */
+        std::string output_dir_;
+
+        /**
+         * Next scene file to process;
+         */
+        unsigned int next_scene_;
+
+        /**
+         * Number of scenes in SCENE.BIN;
+         */
+        unsigned int total_scenes_;
+
+        /**
+         * The original scene.bin file.
+         */
+        File scene_bin_;
+
+        /**
+         * List of uncompressed scenes.
+         */
+        std::vector<File*> scenes_uncompressed_;
+
+        /**
+         * Processed scenes.
+         */
+        std::vector<BattleSceneFile> scenes_;
+
+        /**
+         * Enemy data read from the scenes;
+         */
+        std::vector<Enemy> enemies_;
+
+        /**
+         * Map of enemies, ID and xml file path.
+         */
+        std::map<int, std::string> enemy_map_;
+
+        /**
+         * Attack data read from the scenes;
+         */
+        std::vector<Attack> attacks_;
+
+        /**
+         * Map of attacks, ID and xml file path.
+         */
+        std::map<int, std::string> attack_map_;
+
+        /**
+         * Formations data read from the scenes;
+         */
+        std::vector<Formation> formations_;
+
+        /**
+         * Map of formations, ID and xml file path.
+         */
+        std::map<int, std::string> formation_map_;
+
+};

+ 2 - 0
src/installer/CMakeLists.txt

@@ -7,6 +7,7 @@ find_package(Qt5 COMPONENTS Core REQUIRED)
 # Source files for the installer.
 # Source files for the installer.
 set(INSTALLER_SOURCE_FILES
 set(INSTALLER_SOURCE_FILES
     main.cpp
     main.cpp
+    BattleDataInstaller.cpp
     DataInstaller.cpp
     DataInstaller.cpp
     FieldDataInstaller.cpp
     FieldDataInstaller.cpp
     FieldTextWriter.cpp
     FieldTextWriter.cpp
@@ -27,6 +28,7 @@ set(INSTALLER_SOURCE_FILES
     common/Surface.cpp
     common/Surface.cpp
     common/TimToVram.cpp
     common/TimToVram.cpp
     common/Vram.cpp
     common/Vram.cpp
+    data/BattleSceneFile.cpp
     decompiler/CodeGenerator.cpp
     decompiler/CodeGenerator.cpp
     decompiler/ControlFlow.cpp
     decompiler/ControlFlow.cpp
     decompiler/DecompilerException.cpp
     decompiler/DecompilerException.cpp

+ 37 - 0
src/installer/DataInstaller.cpp

@@ -39,6 +39,7 @@ DataInstaller::DataInstaller(
     // Assign weights.
     // Assign weights.
     for (int i = IDLE; i < STATE_COUNT; i ++) step_weight_[i] = 1;
     for (int i = IDLE; i < STATE_COUNT; i ++) step_weight_[i] = 1;
     step_weight_[IDLE] = 0;
     step_weight_[IDLE] = 0;
+    step_weight_[BATTLE_SCENES] = 1;
     step_weight_[MEDIA_IMAGES] = 3;
     step_weight_[MEDIA_IMAGES] = 3;
     step_weight_[MEDIA_SOUNDS] = 8;
     step_weight_[MEDIA_SOUNDS] = 8;
     step_weight_[MEDIA_MUSICS] = 9;
     step_weight_[MEDIA_MUSICS] = 9;
@@ -69,6 +70,39 @@ float DataInstaller::Progress(){
               options_.no_ffmpeg, options_.no_timidity
               options_.no_ffmpeg, options_.no_timidity
             );
             );
             field_installer_ = std::make_unique<FieldDataInstaller>(input_dir_, output_dir_);
             field_installer_ = std::make_unique<FieldDataInstaller>(input_dir_, output_dir_);
+            std::cout << "CREATE BATTLE INSTALLER: " << (input_dir_ + "data/battle/scene.bin") << "\n";
+            battle_installer_ = std::make_unique<BattleDataInstaller>(input_dir_, output_dir_);
+            installation_state_ = BATTLE_INIT;
+            return CalcProgress();
+        case BATTLE_INIT:
+            write_output_line_("Parsing battle scenes...", 2, true);
+            substeps_ = battle_installer_->Initialize();
+            cur_substep_ = 0;
+            installation_state_ = BATTLE_SCENES;
+            return CalcProgress();
+        case BATTLE_SCENES:
+            cur_substep_ = battle_installer_->ProcessScene();
+            if (cur_substep_ >= substeps_) installation_state_ = BATTLE_WRITE_ATTACKS;
+            return CalcProgress();
+        case BATTLE_WRITE_ATTACKS:
+            write_output_line_("Saving attacks...", 2, true);
+            cur_substep_ = 0;
+            substeps_ = 0;
+            battle_installer_->WriteAttacks();
+            installation_state_ = BATTLE_WRITE_ENEMIES;
+            return CalcProgress();
+        case BATTLE_WRITE_ENEMIES:
+            write_output_line_("Saving enemies...", 2, true);
+            cur_substep_ = 0;
+            substeps_ = 0;
+            battle_installer_->WriteEnemies();
+            installation_state_ = BATTLE_WRITE_FORMATIONS;
+            return CalcProgress();
+        case BATTLE_WRITE_FORMATIONS:
+            write_output_line_("Saving enemy formations...", 2, true);
+            cur_substep_ = 0;
+            substeps_ = 0;
+            battle_installer_->WriteFormations();
             installation_state_ = KERNEL_PRICES;
             installation_state_ = KERNEL_PRICES;
             return CalcProgress();
             return CalcProgress();
         case KERNEL_PRICES:
         case KERNEL_PRICES:
@@ -318,6 +352,9 @@ const float DataInstaller::CalcProgress(){
 void DataInstaller::CreateDirectories(){
 void DataInstaller::CreateDirectories(){
     CreateDir("temp");
     CreateDir("temp");
     CreateDir("game");
     CreateDir("game");
+    CreateDir("game/enemy");
+    CreateDir("game/attack");
+    CreateDir("game/formation");
     CreateDir("images/icons");
     CreateDir("images/icons");
     CreateDir("images/other");
     CreateDir("images/other");
     CreateDir("images/characters");
     CreateDir("images/characters");

+ 31 - 0
src/installer/DataInstaller.h

@@ -22,6 +22,7 @@
 #include "FieldDataInstaller.h"
 #include "FieldDataInstaller.h"
 #include "KernelDataInstaller.h"
 #include "KernelDataInstaller.h"
 #include "MediaDataInstaller.h"
 #include "MediaDataInstaller.h"
+#include "BattleDataInstaller.h"
 #include "ModelsAndAnimationsDb.h"
 #include "ModelsAndAnimationsDb.h"
 
 
 /**
 /**
@@ -164,6 +165,31 @@ class DataInstaller{
              */
              */
             INITIALIZE,
             INITIALIZE,
 
 
+            /**
+             * Initializes the battle installer.
+             */
+            BATTLE_INIT,
+
+            /**
+             * Extracts battle scenes.
+             */
+            BATTLE_SCENES,
+
+            /**
+             * Writes attack data.
+             */
+            BATTLE_WRITE_ATTACKS,
+
+            /**
+             * Writes enemy data.
+             */
+            BATTLE_WRITE_ENEMIES,
+
+            /**
+             * Writes enemy formation data.
+             */
+            BATTLE_WRITE_FORMATIONS,
+
             /**
             /**
              * Parses item and materia prices from ff7.exe.
              * Parses item and materia prices from ff7.exe.
              */
              */
@@ -395,6 +421,11 @@ class DataInstaller{
          */
          */
         std::unique_ptr<MediaDataInstaller> media_installer_;
         std::unique_ptr<MediaDataInstaller> media_installer_;
 
 
+        /**
+         * The installer for battle data.
+         */
+        std::unique_ptr<BattleDataInstaller> battle_installer_;
+
         /**
         /**
          * Function used to print text to the log output, line by line.
          * Function used to print text to the log output, line by line.
          *
          *

+ 1 - 0
src/installer/MainWindow.cpp

@@ -183,6 +183,7 @@ void MainWindow::on_btn_data_run_clicked(){
           "data/sound/audio.dat",
           "data/sound/audio.dat",
           "data/music/music.idx",
           "data/music/music.idx",
           "data/midi/midi.lgp",
           "data/midi/midi.lgp",
+          "data/battle/scene.bin",
           "ff7.exe"
           "ff7.exe"
         };
         };
         // Ensure required files are in the input dir
         // Ensure required files are in the input dir

+ 10 - 0
src/installer/VGearsUtility.cpp

@@ -13,7 +13,9 @@
  * GNU General Public License for more details.
  * GNU General Public License for more details.
  */
  */
 
 
+#include <boost/algorithm/string.hpp>
 #include "VGearsUtility.h"
 #include "VGearsUtility.h"
+#include "Characters.h"
 
 
 namespace VGears{
 namespace VGears{
 
 
@@ -59,4 +61,12 @@ namespace VGears{
         Application::destroyComponents();
         Application::destroyComponents();
     }
     }
 
 
+    std::string Utility::DecodeString(const u8* bytes, const unsigned int size){
+        std::string decoded("");
+        for (int i = 0; i < size; i ++)
+            if (bytes[i] > 0x00 && bytes[i] < 0XFF) decoded += ENGLISH_CHARS[bytes[i]];
+        boost::algorithm::trim(decoded);
+        return decoded;
+    }
+
 }
 }

+ 11 - 0
src/installer/VGearsUtility.h

@@ -47,6 +47,17 @@ namespace VGears{
              */
              */
             virtual Ogre::Camera* GetCamera(void);
             virtual Ogre::Camera* GetCamera(void);
 
 
+            /**
+             * Decodes a FF7 encoded string.
+             *
+             * It doesn't work for compressed strings.
+             *
+             * @param[in] bytes Raw bytes to read from.
+             * @param[in] size Number of bytes in the bytes parameter.
+             * @return The decoded string.
+             */
+            static std::string DecodeString(const u8* bytes, const unsigned int size);
+
         protected:
         protected:
 
 
             /**
             /**

+ 5 - 2
src/installer/common/BinGZipFile.cpp

@@ -14,6 +14,7 @@
  */
  */
 
 
 #include <cassert>
 #include <cassert>
+#include <iostream>
 #include <zlib.h>
 #include <zlib.h>
 #include "installer/common/Logger.h"
 #include "installer/common/Logger.h"
 #include "installer/common/BinGZipFile.h"
 #include "installer/common/BinGZipFile.h"
@@ -136,6 +137,7 @@ void BinGZipFile::InnerGetNumberOfFiles(){
     file_count_ = 0;
     file_count_ = 0;
     for (u32 pointer = 0; pointer < buffer_size_;){
     for (u32 pointer = 0; pointer < buffer_size_;){
         u16 temp = GetU16LE(pointer);
         u16 temp = GetU16LE(pointer);
+        if (temp == 0xFFFF) temp = 0; // Hack to deal with scene.bin
         pointer += 6;
         pointer += 6;
         // Condition for GZip header.
         // Condition for GZip header.
         if (
         if (
@@ -147,9 +149,10 @@ void BinGZipFile::InnerGetNumberOfFiles(){
     }
     }
 
 
     if (file_count_ == 0){
     if (file_count_ == 0){
-        LOGGER->Log(
+        std::cout << "Warning: " + file_name_ + " isn't archive. number_of_files == 0\n";
+        /*LOGGER->Log(
           "Warning: " + file_name_ + " isn't archive. number_of_files == 0"
           "Warning: " + file_name_ + " isn't archive. number_of_files == 0"
-        );
+        );*/
     }
     }
 }
 }
 
 

+ 339 - 0
src/installer/data/Attack.h

@@ -0,0 +1,339 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+#include "common/TypeDefine.h"
+#include "data/GameData.h"
+
+/**
+ * An attack structure.
+ */
+struct Attack{
+
+    /**
+     * Attack parameters related to the damage done.
+     */
+    struct Damage{
+
+        /**
+         * The attack power.
+         *
+         * Used for damage/healing calculation.
+         */
+        u16 power;
+
+        /**
+         * Indicates physical damage.
+         */
+        bool physical;
+
+        /**
+         * Indicates magical damage.
+         */
+        bool magical;
+
+        /**
+         * The damage formula.
+         */
+        int formula;
+
+        /**
+         * The damage modifier.
+         */
+        int modifier;
+
+        /**
+         * Probability of the attack to land.
+         */
+        float accuracy;
+
+        /**
+         * Indicates if the accuracy is affected by darkness.
+         */
+        bool affected_by_darkness;
+
+        /**
+         * The restore type.
+         */
+        GameData::RestoreType restore;
+
+        /**
+         * Indicates if the attack damages MP instead of HP.
+         */
+        bool damage_mp;
+
+        /**
+         * Indicates if the attack drains HP from the target.
+         */
+        bool drain_hp;
+
+        /**
+         * Indicates if the attack drains MP from the target.
+         */
+        bool drain_mp;
+
+        /**
+         * Indicates if the attack ignores status defense.
+         */
+        bool ignore_status_defense;
+
+        /**
+         * If true, the attack will always be critical.
+         */
+        bool critical;
+
+    };
+
+    /**
+     * Status effect information.
+     */
+    struct StatusEffect{
+
+        /**
+         * ID of the status effect.
+         */
+        u8 id;
+
+        /**
+         * Chance of inflicting
+         */
+        float chance;
+
+        /**
+         * Inflict/cure/toggle mode.
+         */
+        unsigned int type;
+    };
+
+    /**
+     * Targeting parameters.
+     */
+    struct Target{
+
+        /**
+         * The target can be selected.
+         *
+         * If the target of the attack is selectable, at least between some subset of
+         * possible targets, this must be true.
+         */
+        bool selection_enabled;
+
+        /**
+         * The default target is an enemy.
+         *
+         * When true, the cursor starts in an enemy row. When false, it starts in the ally
+         * row. It may or may not be possible to change it.
+         */
+        bool default_enemy;
+
+        /**
+         * The default target is a group.
+         *
+         * When true, the cursor starts will all the members of a row selected. When false,
+         * it starts with only one target selected. It may or may not be possible to change
+         * it.
+         */
+        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.
+         *
+         * When true, the target cannot be changed between enemies and allies. In this case
+         * {@see default_enemy} will determine the group that can be targeted.
+         */
+        bool fixed_row;
+
+        /**
+         * The command is short ranged.
+         *
+         * When true, if the target or the caster is not in the front of their row, the
+         * target will take half damage. Also, when true, an enemy covered by another enemy
+         * can't be targeted.
+         */
+        bool short_range;
+
+        /**
+         * Targets all, enemies and allies.
+         *
+         * When true, all allies and enemies are targets, and the selection can't be changed.
+         */
+        bool all_rows;
+
+        /**
+         * The target is random.
+         *
+         * When multiple targets are selected, one will be selected at random to be the
+         * receiving target. Cursor will cycle among all viable targets.
+         */
+        bool random;
+
+        /**
+         * If true, the attack will miss unless the target is dead.
+         */
+        bool only_for_dead;
+
+        /**
+         * The attack can be re-targeted if the target is dead.
+         */
+        bool no_re_target;
+    };
+
+    /**
+     * Animation data related to the attack;
+     */
+    struct Animation{
+
+        /**
+         * Atack effect ID.
+         *
+         * Used for animation in battle. -1 if the attack has no animation.
+         */
+        int effect;
+
+        /**
+         * Impact effect.
+         *
+         * The sprite shown when the attack hits. -1 to not show any.
+         */
+        int impact_effect;
+
+        /**
+         * Index of the animation played by the target when hit. -1 to not show any.
+         */
+        int hurt_anim;
+
+        /**
+         * Impact sound.
+         *
+         * Determines the camera movement when the command is executed over multiple targets. -1 to not
+         * play any.
+         */
+        int sound;
+
+        /**
+         * Camera movement ID for single target.
+         *
+         * Determines the camera movement when the command is executed over a single target.  -1 to not
+         * move the camera.
+         */
+        int camera_single;
+
+        /**
+         * Camera movement ID for multiple target.
+         *
+         * Determines the camera movement when the command is executed over multiple
+         * targets. -1 to not move the camera.
+         */
+        int camera_multiple;
+    };
+
+    /**
+     * Attack ID.
+     */
+    u32 id;
+
+    /**
+     * Attack name.
+     */
+    std::string name;
+
+    /**
+     * Damage parameters.
+     */
+    Damage damage;
+
+    /**
+     * MP cost.
+     *
+     * Determines the MP const of the attack.
+     */
+    u16 mp;
+
+    /**
+     * Targeting options.
+     */
+    Target target;
+
+    /**
+     * The attack can be reflected.
+     */
+    bool reflectable;
+
+    /**
+     * Information about status change mode and chance.
+     */
+    std::vector<StatusEffect> status_effects;
+
+    /**
+     * Information about the item elements. 2 bytes.
+     */
+    std::vector<unsigned int> elements;
+
+    /**
+     * Animation data.
+     */
+    Animation animation;
+
+    /**
+     * Constructor.
+     *
+     * Initializes data to default values.
+     */
+    Attack(): id(0), name(""), mp(0), reflectable(false){
+        damage.physical = false;
+        damage.magical = false;
+        damage.power = 0;
+        damage.formula = -1;
+        damage.modifier = -1;
+        damage.accuracy = 1.0f;
+        damage.affected_by_darkness = false;
+        damage.restore = GameData::RestoreType::NONE;
+        damage.damage_mp = false;
+        damage.drain_hp = false;
+        damage.drain_mp = false;
+        damage.ignore_status_defense = false;
+        damage.critical = false;
+        target.selection_enabled = false;
+        target.default_enemy = false;
+        target.default_multiple = false;
+        target.toggle_multiple = false;
+        target.fixed_row = false;
+        target.short_range = false;
+        target.all_rows = false;
+        target.random = false;
+        target.only_for_dead = false;
+        target.no_re_target = false;
+        animation.effect = -1;
+        animation.impact_effect = -1;
+        animation.hurt_anim = -1;
+        animation.sound = -1;
+        animation.camera_single = -1;
+        animation.camera_multiple = -1;
+        status_effects.clear();
+        elements.clear();
+    }
+};

+ 25 - 0
src/installer/data/BattleScene.h

@@ -0,0 +1,25 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+#include "common/TypeDefine.h"
+#include "data/GameData.h"
+
+struct BattleScene{
+
+};

+ 922 - 0
src/installer/data/BattleSceneFile.cpp

@@ -0,0 +1,922 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "BattleSceneFile.h"
+#include "VGearsUtility.h"
+
+BattleSceneFile::BattleSceneFile(const unsigned int id, File file): id_(id){Read(file);}
+
+BattleSceneFile::~BattleSceneFile(){}
+
+std::vector<Attack> BattleSceneFile::GetAttacks(){return attacks_;}
+
+std::vector<Enemy> BattleSceneFile::GetEnemies(){return enemies_;}
+
+std::vector<Formation> BattleSceneFile::GetFormations(){return formations_;}
+
+void BattleSceneFile::Read(File file){
+    file.SetOffset(0);
+    scene_.enemy[0] = file.readU16LE();
+    scene_.enemy[1] = file.readU16LE();
+    scene_.enemy[2] = file.readU16LE();
+    scene_.unused = file.readU16LE();
+    for (int s = 0; s < 4; s ++){
+        scene_.setup[s].location = file.readU16LE();
+        scene_.setup[s].next_battle = file.readU16LE();
+        scene_.setup[s].escape_counter = file.readU16LE();
+        scene_.setup[s].unused = file.readU16LE();
+        for (int a = 0; a < 4; a ++) scene_.setup[s].next_arena_candidates[a] = file.readU16LE();
+        scene_.setup[s].flags = file.readU16LE();
+        scene_.setup[s].layout = file.readU8();
+        scene_.setup[s].camera = file.readU8();
+    }
+    for (int c = 0; c < 4; c ++){
+        for (int p = 0; p < 3; p ++){
+            scene_.camera[c].camera[p].x = file.readU16LE();
+            scene_.camera[c].camera[p].y = file.readU16LE();
+            scene_.camera[c].camera[p].x = file.readU16LE();
+            scene_.camera[c].camera[p].d_x = file.readU16LE();
+            scene_.camera[c].camera[p].d_y = file.readU16LE();
+            scene_.camera[c].camera[p].d_x = file.readU16LE();
+        }
+        for (int p = 0; p < 12; p ++) scene_.camera[c].unused[p] = file.readU8();
+    }
+    for (int f = 0; f < 4; f ++){
+        for (int e = 0; e < 6; e ++){
+            scene_.formation[f][e].id = file.readU16LE();
+            scene_.formation[f][e].x = file.readU16LE();
+            scene_.formation[f][e].y = file.readU16LE();
+            scene_.formation[f][e].z = file.readU16LE();
+            scene_.formation[f][e].row = file.readU16LE();
+            scene_.formation[f][e].cover_flags = file.readU16LE();
+            scene_.formation[f][e].flags = file.readU32LE();
+        }
+    }
+    for (int e = 0; e < 3; e ++){
+        for (int n = 0; n < 32; n ++) scene_.enemy_data[e].name[n] = file.readU8();
+        scene_.enemy_data[e].level = file.readU8();
+        scene_.enemy_data[e].spd = file.readU8();
+        scene_.enemy_data[e].lck = file.readU8();
+        scene_.enemy_data[e].eva = file.readU8();
+        scene_.enemy_data[e].str = file.readU8();
+        scene_.enemy_data[e].def = file.readU8();
+        scene_.enemy_data[e].mag = file.readU8();
+        scene_.enemy_data[e].mdef = file.readU8();
+        for (int l = 0; l < 8; l ++) scene_.enemy_data[e].elements[l] = file.readU8();
+        for (int l = 0; l < 8; l ++) scene_.enemy_data[e].element_rates[l] = file.readU8();
+        for (int a = 0; a < 16; a ++) scene_.enemy_data[e].animation_index[a] = file.readU8();
+        for (int a = 0; a < 16; a ++) scene_.enemy_data[e].attacks[a] = file.readU16LE();
+        for (int a = 0; a < 16; a ++) scene_.enemy_data[e].attack_camera[a] = file.readU16LE();
+        for (int i = 0; i < 4; i ++) scene_.enemy_data[e].item_rate[i] = file.readU8();
+        for (int i = 0; i < 4; i ++) scene_.enemy_data[e].item[i] = file.readU16LE();
+        for (int m = 0; m < 3; m ++) scene_.enemy_data[e].maniputale_attacks[m] = file.readU16LE();
+        scene_.enemy_data[e].unknown_0 = file.readU16LE();
+        scene_.enemy_data[e].mp = file.readU16LE();
+        scene_.enemy_data[e].ap = file.readU16LE();
+        scene_.enemy_data[e].morph = file.readU16LE();
+        scene_.enemy_data[e].back_damage = file.readU8();
+        scene_.enemy_data[e].unused = file.readU8();
+        scene_.enemy_data[e].hp = file.readU32LE();
+        scene_.enemy_data[e].exp = file.readU32LE();
+        scene_.enemy_data[e].money = file.readU32LE();
+        scene_.enemy_data[e].immunities = file.readU32LE();
+        scene_.enemy_data[e].unknown_1 = file.readU32LE();
+    }
+    for (int a = 0; a < 32; a ++){
+        scene_.attack_data[a].accuracy = file.readU8();
+        scene_.attack_data[a].impact_effect = file.readU8();
+        scene_.attack_data[a].hurt_anim = file.readU8();
+        scene_.attack_data[a].unknown = file.readU8();
+        scene_.attack_data[a].mp = file.readU16LE();
+        scene_.attack_data[a].sound = file.readU16LE();
+        scene_.attack_data[a].camera_single = file.readU16LE();
+        scene_.attack_data[a].camera_multiple = file.readU16LE();
+        scene_.attack_data[a].target = file.readU8();
+        scene_.attack_data[a].effect = file.readU8();
+        scene_.attack_data[a].damage = file.readU8();
+        scene_.attack_data[a].power = file.readU8();
+        scene_.attack_data[a].condition = file.readU8();
+        scene_.attack_data[a].status_change = file.readU8();
+        scene_.attack_data[a].additional_effects = file.readU8();
+        scene_.attack_data[a].additional_effects_mod = file.readU8();
+        scene_.attack_data[a].status = file.readU32LE();
+        scene_.attack_data[a].element = file.readU16LE();
+        scene_.attack_data[a].special = file.readU16LE();
+    }
+    for (int a = 0; a < 32; a ++) scene_.attack_ids[a] = file.readU16LE();
+    for (int a = 0; a < 32; a ++)
+        for (int b = 0; b < 32; b ++) scene_.attack_names[a][b] = file.readU8();
+    // TODO: Keep reading for AI scripts.
+
+    // All the data is read, now format it.
+
+    // Populate enemy list.
+    for (int e = 0; e < 3; e ++){
+        if (scene_.enemy[e] == 0xFFFF) continue;
+        // If the enemy is already on the list, skip it.
+        bool already_added = false;
+        for (int t = 0; t < enemies_.size(); t ++){
+            if (enemies_[t].id == scene_.enemy[e]){
+                already_added = true;
+                break;
+            }
+        }
+        if (already_added) continue;
+        // Create a new enemy
+        Enemy enemy;
+        enemy.id = scene_.enemy[e];
+        enemy.name = VGears::Utility::DecodeString(scene_.enemy_data[e].name, 32);
+        enemy.level = scene_.enemy_data[e].level;
+        enemy.str = scene_.enemy_data[e].str;
+        enemy.mag = scene_.enemy_data[e].mag;
+        enemy.def = scene_.enemy_data[e].def;
+        enemy.mdef = scene_.enemy_data[e].mdef;
+        enemy.spd = scene_.enemy_data[e].spd;
+        enemy.lck = scene_.enemy_data[e].lck;
+        enemy.eva = scene_.enemy_data[e].eva;
+        enemy.hp = scene_.enemy_data[e].hp;
+        enemy.mp = scene_.enemy_data[e].mp;
+        enemy.exp = scene_.enemy_data[e].exp;
+        enemy.ap = scene_.enemy_data[e].ap;
+        enemy.money = scene_.enemy_data[e].money;
+        enemy.back_damage = scene_.enemy_data[e].back_damage / 8.0f;
+        for (int a = 0; a < 16; a ++){
+            if (scene_.enemy_data[e].animation_index[a] != 0xFF)
+                enemy.animations.push_back(scene_.enemy_data[e].animation_index[a]);
+        }
+        for (int l = 0; l < 8; l ++){
+            if (
+              scene_.enemy_data[e].elements[l] != 0xFF
+              && scene_.enemy_data[e].element_rates[l] != 0xFF
+            ){
+                Enemy::Element element;
+                element.id = scene_.enemy_data[e].elements[l];
+                switch (scene_.enemy_data[e].element_rates[l]){
+                    case 0x00: element.factor =  11.0f; break; // Death.
+                    case 0x02: element.factor =   2.0f; break; // Double damage.
+                    case 0x04: element.factor =   0.5f; break; // Half damage.
+                    case 0x05: element.factor =   0.0f; break; // Nullify;
+                    case 0x06: element.factor =  -1.0f; break; // Absorb 100%;
+                    case 0x07: element.factor = -11.0f; break; // Full cure;
+                    default: element.factor = 1.0f; // Normal damage.
+                }
+                enemy.elements.push_back(element);
+            }
+        }
+        if (scene_.enemy_data[e].immunities != 0xFFFFFFFF){
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000001)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::DEATH;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000002)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::NEAR_DEATH;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000004)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::SLEEP;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000008)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::POISONED;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000010)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::SADNESS;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000020)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::FURY;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000040)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::CONFU;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000080)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::SILENCE;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000100)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::HASTE;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000200)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::SLOW;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000400)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::STOP;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00000800)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::FROG;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00001000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::SMALL;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00002000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::SLOW_NUMB;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00004000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::PETRIFY;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00008000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::REGEN;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00010000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::BARRIER;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00020000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::M_BARRIER;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00040000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::REFLECT;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00080000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::DUAL;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00100000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::SHIELD;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00200000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::D_SENTENCE;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00400000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::MANIPULATE;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x00800000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::BERSERK;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x01000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::PEERLESS;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x02000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::PARALYSIS;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x04000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::DARKNESS;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x08000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::DUAL_DRAIN;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x10000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::DEATH_FORCE;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x20000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::RESIST;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x40000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::LUCKY_GIRL;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+            if (scene_.enemy_data[e].immunities == (scene_.enemy_data[e].immunities | 0x80000000)){
+                Enemy::Immunity immunity;
+                immunity.status = GameData::STATUS::IMPRISONED;
+                immunity.rate = 1.0f;
+                enemy.immunities.push_back(immunity);
+            }
+        }
+        for (int a = 0; a < 16; a ++){
+            if (scene_.enemy_data[e].attacks[a] == 0xFFFF) continue;
+            Enemy::Attack attack;
+            attack.id = scene_.enemy_data[e].attacks[a];
+            if (scene_.enemy_data[e].attack_camera[a] == 0xFFFF) attack.camera = -1;
+            else attack.camera = scene_.enemy_data[e].attack_camera[a];
+            enemy.attacks.push_back(attack);
+        }
+        for (int m = 0; m < 3; m ++){
+            if (scene_.enemy_data[e].maniputale_attacks[m] == 0xFFFF) continue;
+            enemy.manipulate_attacks.push_back(scene_.enemy_data[e].maniputale_attacks[m]);
+        }
+        for (int i = 0; i < 4; i ++){
+            if (scene_.enemy_data[e].item[i] == 0xFF) continue;
+            Enemy::Item item;
+            item.id = scene_.enemy_data[e].item[i];
+            if (scene_.enemy_data[e].item_rate[i] < 0x80){
+                item.rate = scene_.enemy_data[e].item_rate[i] / 63.0f;
+                enemy.drop.push_back(item);
+            }
+            else{
+                item.rate = (scene_.enemy_data[e].item_rate[i] - 0x80) / 63.0f;
+                enemy.steal.push_back(item);
+            }
+        }
+        if (scene_.enemy_data[e].morph != 0xFFFF) enemy.morph = scene_.enemy_data[e].morph;
+
+        //std::cout << "ENEMY " << enemy.id << ": " << enemy.name << "\n";
+
+        enemies_.push_back(enemy);
+    }
+
+    // Populate attack list.
+    for (int a = 0; a < 32; a ++){
+        if (scene_.attack_ids[a] == 0xFFFF) continue;
+        // If the attack is already on the list, skip it.
+        bool already_added = false;
+        for (int t = 0; t < attacks_.size(); t ++){
+            if (attacks_[t].id == scene_.attack_ids[a]){
+                already_added = true;
+                break;
+            }
+        }
+        if (already_added) continue;
+        // Create a new attack.
+        Attack attack;
+        attack.id = scene_.attack_ids[a];
+        attack.name = VGears::Utility::DecodeString(scene_.attack_names[a], 32);
+        attack.damage.accuracy = scene_.attack_data[a].accuracy / 255.0f;
+        if (scene_.attack_data[a].impact_effect != 0xFF)
+            attack.animation.impact_effect = scene_.attack_data[a].impact_effect;
+        if (scene_.attack_data[a].hurt_anim != 0xFF)
+            attack.animation.hurt_anim = scene_.attack_data[a].hurt_anim;
+        attack.mp = scene_.attack_data[a].mp;
+        if (scene_.attack_data[a].sound != 0xFFFF)
+            attack.animation.sound = scene_.attack_data[a].sound;
+        if (scene_.attack_data[a].camera_single != 0xFFFF)
+            attack.animation.camera_single = scene_.attack_data[a].camera_single;
+        if (scene_.attack_data[a].camera_multiple != 0xFFFF)
+            attack.animation.camera_multiple = scene_.attack_data[a].camera_multiple;
+        attack.target.selection_enabled
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x01));
+        attack.target.default_enemy
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x02));
+        attack.target.default_multiple
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x04));
+        attack.target.toggle_multiple
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x08));
+        attack.target.fixed_row
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x10));
+        attack.target.short_range
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x20));
+        attack.target.all_rows
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x40));
+        attack.target.random
+          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x80));
+        if (scene_.attack_data[a].effect != 0xFFFF)
+            attack.animation.effect = scene_.attack_data[a].effect;
+        attack.damage.formula = scene_.attack_data[a].damage >> 4;
+        attack.damage.modifier = scene_.attack_data[a].damage & 15;
+        attack.damage.power = scene_.attack_data[a].power;
+        switch(scene_.attack_data[a].condition){
+            case 0: attack.damage.restore = GameData::RestoreType::HP; break;
+            case 1: attack.damage.restore = GameData::RestoreType::MP; break;
+            case 2: attack.damage.restore = GameData::RestoreType::STATUS; break;
+            default: attack.damage.restore = GameData::RestoreType::NONE;
+        }
+        int status_chance_raw = 63 - (scene_.attack_data[a].status_change ^ 63); // Out of 63;
+        int status_mode;
+        if (
+          scene_.attack_data[a].status_change == (scene_.attack_data[a].status_change | 0x00000080)
+        ){
+            status_mode = GameData::STATUS_EFFECT_MODE::TOGGLE;
+        }
+        else if (
+          scene_.attack_data[a].status_change == (scene_.attack_data[a].status_change | 0x00000040)
+        ){
+            status_mode = GameData::STATUS_EFFECT_MODE::TOGGLE;
+        }
+        else status_mode = GameData::STATUS_EFFECT_MODE::TOGGLE;
+        if (scene_.attack_data[a].status != 0xFFFFFFFF){
+            float status_chance = status_chance_raw / 63.0f;
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000001)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::DEATH;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000002)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::NEAR_DEATH;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000004)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::SLEEP;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000008)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::POISONED;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000010)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::SADNESS;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000020)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::FURY;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000040)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::CONFU;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000080)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::SILENCE;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000100)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::HASTE;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000200)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::SLOW;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000400)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::STOP;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00000800)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::FROG;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00001000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::SMALL;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00002000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::SLOW_NUMB;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00004000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::PETRIFY;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00008000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::REGEN;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00010000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::BARRIER;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00020000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::M_BARRIER;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00040000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::REFLECT;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00080000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::DUAL;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00100000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::SHIELD;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00200000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::D_SENTENCE;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00400000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::MANIPULATE;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x00800000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::BERSERK;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x01000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::PEERLESS;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x02000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::PARALYSIS;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x04000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::DARKNESS;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x08000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::DUAL_DRAIN;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x10000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::DEATH_FORCE;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x20000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::RESIST;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x40000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::LUCKY_GIRL;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+            if (scene_.attack_data[a].status == (scene_.attack_data[a].status | 0x80000000)){
+                Attack::StatusEffect effect;
+                effect.id = GameData::STATUS::IMPRISONED;
+                effect.chance = status_chance;
+                effect.type = status_mode;
+                attack.status_effects.push_back(effect);
+            }
+        }
+        if (scene_.attack_data[a].element >= 0 && scene_.attack_data[a].element <= 0x0F)
+            attack.elements.push_back(scene_.attack_data[a].element);
+        attack.damage.damage_mp
+          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0001));
+        attack.damage.affected_by_darkness
+          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0004));
+        if ((scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0010))){
+            if (attack.damage.damage_mp) attack.damage.drain_mp = true;
+            else attack.damage.drain_hp = true;
+        }
+        if ((scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0020))){
+            attack.damage.drain_hp = true;
+            attack.damage.drain_mp = true;
+        }
+        attack.damage.ignore_status_defense
+          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0080));
+        attack.target.only_for_dead
+          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0100));
+        attack.reflectable
+          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0200));
+        attack.target.no_re_target
+          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0800));
+        attack.damage.critical
+          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x2000));
+
+        // TODO: Parse scene_.attack_data[a].additional_effects with
+        // scene_.attack_data[a].additional_effects_mod for special attack modifiers (23 total).
+        // https://wiki.ffrtt.ru/index.php/FF7/Battle/Attack_Special_Effects
+
+        //std::cout << "ATTACK " << ((int) attack.id) << ": " << attack.name << "\n";
+        attacks_.push_back(attack);
+    }
+
+    // Formations
+    for (int f = 0; f < 4; f ++){
+        Formation formation;
+        if (scene_.setup[f].next_battle != 0xFFFF)
+            formation.next_formation = scene_.setup[f].next_battle;
+        formation.escape_counter = (scene_.setup[f].escape_counter / 64.0f);
+        // TODO: What is flag 0x01? Multilimb enemies?
+        formation.escapable = (scene_.setup[f].flags == scene_.setup[f].flags | 0x02);
+        formation.skip_victory_pose = (scene_.setup[f].flags != scene_.setup[f].flags | 0x04);
+        formation.skip_spoils = (scene_.setup[f].flags != scene_.setup[f].flags | 0x08);
+        formation.preemptive_disabled = (scene_.setup[f].flags != scene_.setup[f].flags | 0x10);
+        // TODO: What is flag 0x20?
+        // TODO: What is flag 0x40?
+        // TODO: What is flag 0x80?
+        switch(scene_.setup[f].layout){
+            case 0x0: formation.layout = Formation::Layout::NORMAL; break;
+            case 0x1: formation.layout = Formation::Layout::PREEMPTIVE; break;
+            case 0x2: formation.layout = Formation::Layout::BACK_ATTACK; break;
+            case 0x3: formation.layout = Formation::Layout::SIDE_ATTACK; break;
+            case 0x4: formation.layout = Formation::Layout::PINCER_ATTACK; break;
+            case 0x5: formation.layout = Formation::Layout::PINCER_ATTACK; break;
+            case 0x6: formation.layout = Formation::Layout::SIDE_ATTACK; break;
+            case 0x7: formation.layout = Formation::Layout::SIDE_ATTACK; break;
+            case 0x8: formation.layout = Formation::Layout::LOCKED; break;
+            default: formation.layout = Formation::Layout::NORMAL;
+        }
+        for (int e = 0; e < 6; e ++){
+            if (scene_.formation[f][e].id != 0xFFFF){
+                Formation::Enemy enemy;
+                enemy.id = scene_.formation[f][e].id;
+                enemy.x = scene_.formation[f][e].x;
+                enemy.y = scene_.formation[f][e].y;
+                enemy.z = scene_.formation[f][e].z;
+                enemy.row = scene_.formation[f][e].row;
+                enemy.cover[0] = (
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x01)
+                  ? '1' : '0'
+                );
+                enemy.cover[1] = (
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x02)
+                  ? '1' : '0'
+                );
+                enemy.cover[2] = (
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x04)
+                  ? '1' : '0'
+                );
+                enemy.cover[3] = (
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x08)
+                  ? '1' : '0'
+                );
+                enemy.cover[4] = (
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x10)
+                  ? '1' : '0'
+                );
+                enemy.visible
+                  = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x01);
+                enemy.direction
+                  = ((scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x02) ? 1 : 0);
+                enemy.targeteable
+                  = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x08);
+                enemy.main_script_active
+                  = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x10);
+                formation.enemies.push_back(enemy);
+            }
+        }
+        formation.location = scene_.setup[f].location;
+        formation.location_name = LocationToString(scene_.setup[f].location);
+        for (int c = 0; c < 3; c ++){
+            Formation::Camera camera;
+            camera.x = scene_.camera[f].camera[c].x;
+            camera.y = scene_.camera[f].camera[c].y;
+            camera.y = scene_.camera[f].camera[c].z;
+            camera.d_x = scene_.camera[f].camera[c].d_x;
+            camera.d_y = scene_.camera[f].camera[c].d_y;
+            camera.d_y = scene_.camera[f].camera[c].d_z;
+            formation.camera_positions.push_back(camera);
+        }
+        if (scene_.setup[f].camera < formation.camera_positions.size())
+            formation.initial_camera_position = scene_.setup[f].camera;
+        for (int a = 0; a < 4; a ++){
+            if (scene_.setup[f].next_arena_candidates[a] != 999){
+                formation.next_arena_formation_candidates.push_back(
+                  scene_.setup[f].next_arena_candidates[a]
+                );
+                formation.is_arena_battle = true;
+            }
+        }
+        formations_.push_back(formation);
+    }
+
+}
+
+std::string BattleSceneFile::LocationToString(const int id){
+    switch(id){
+        case 0x0000: return "Blank";
+        case 0x0001: return "Bizarro Battle - Center";
+        case 0x0002: return "Grassland";
+        case 0x0003: return "Mt Nibel";
+        case 0x0004: return "Forest";
+        case 0x0005: return "Beach";
+        case 0x0006: return "Desert";
+        case 0x0007: return "Snow";
+        case 0x0008: return "Swamp";
+        case 0x0009: return "Sector 1 Train Station";
+        case 0x000A: return "Reactor 1";
+        case 0x000B: return "Reactor 1 Core";
+        case 0x000C: return "Reactor 1 Entrance";
+        case 0x000D: return "Sector 4 Subway";
+        case 0x000E: return "Nibel Caves or AForest Caves";
+        case 0x000F: return "Shinra HQ";
+        case 0x0010: return "Midgar Raid Subway";
+        case 0x0011: return "Hojo's Lab";
+        case 0x0012: return "Shinra Elevators";
+        case 0x0013: return "Shinra Roof";
+        case 0x0014: return "Midgar Highway";
+        case 0x0015: return "Wutai Pagoda";
+        case 0x0016: return "Church";
+        case 0x0017: return "Coral Valley";
+        case 0x0018: return "Midgar Slums";
+        case 0x0019: return "Sector 4 Corridors or Junon Path";
+        case 0x001A: return "Sector 4 Gantries or Midgar Underground";
+        case 0x001B: return "Sector 7 Support Pillar Stairway";
+        case 0x001C: return "Sector 7 Support Pillar Top";
+        case 0x001D: return "Sector 8";
+        case 0x001E: return "Sewers";
+        case 0x001F: return "Mythril Mines";
+        case 0x0020: return "Northern Crater - Floating Platforms";
+        case 0x0021: return "Corel Mountain Path";
+        case 0x0022: return "Junon Beach";
+        case 0x0023: return "Junon Cargo Ship";
+        case 0x0024: return "Corel Prison";
+        case 0x0025: return "Battle Square";
+        case 0x0026: return "Da Chao - Rapps Battle";
+        case 0x0027: return "Cid's Backyard";
+        case 0x0028: return "Final Descent to Sephiroth";
+        case 0x0029: return "Reactor 5 Entrance";
+        case 0x002A: return "Temple of the Ancients - Escher Room";
+        case 0x002B: return "Shinra Mansion";
+        case 0x002C: return "Junon Airship Dock";
+        case 0x002D: return "Whirlwind Maze";
+        case 0x002E: return "Junon Underwater Reactor";
+        case 0x002F: return "Gongaga Reactor";
+        case 0x0030: return "Gelnika";
+        case 0x0031: return "Train Graveyard";
+        case 0x0032: return "Great Glacier Ice Caves & Gaea Cliffs - Inside";
+        case 0x0033: return "Sister Ray";
+        case 0x0034: return "Sister Ray Base";
+        case 0x0035: return "Forgotten City Altar";
+        case 0x0036: return "Northern Crater - Initial Descent";
+        case 0x0037: return "Northern Crater - Hatchery";
+        case 0x0038: return "Northern Crater - Water Area";
+        case 0x0039: return "Safer Battle";
+        case 0x003A: return "Kalm Flashback - Dragon Battle";
+        case 0x003B: return "Junon Underwater Pipe";
+        case 0x003C: return "Blank";
+        case 0x003D: return "Corel Railway - Canyon";
+        case 0x003E: return "Whirlwind Maze - Crater";
+        case 0x003F: return "Corel Railway - Rollercoaster";
+        case 0x0040: return "Wooden Bridge";
+        case 0x0041: return "Da Chao";
+        case 0x0042: return "Fort Condor";
+        case 0x0043: return "Dirt Wasteland";
+        case 0x0044: return "Bizarro Battle - Right Side";
+        case 0x0045: return "Bizarro Battle - Left Side";
+        case 0x0046: return "Jenova*SYNTHESIS Battle";
+        case 0x0047: return "Corel Train Battle";
+        case 0x0048: return "Cosmo Canyon";
+        case 0x0049: return "Caverns of the Gi";
+        case 0x004A: return "Nibelheim Mansion Basement";
+        case 0x004B: return "Temple of the Ancients - Demons Gate";
+        case 0x004C: return "Temple of the Ancients - Mural Room";
+        case 0x004D: return "Temple of the Ancients - Clock Passage";
+        case 0x004E: return "Final Battle - Sephiroth";
+        case 0x004F: return "Jungle";
+        case 0x0050: return "Ultimate Weapon - Battle on Highwind";
+        case 0x0051: return "Corel Reactor";
+        case 0x0052: return "Unused";
+        case 0x0053: return "Don Corneo's Mansion";
+        case 0x0054: return "Emerald Weapon Battle";
+        case 0x0055: return "Reactor 5";
+        case 0x0056: return "Shinra HQ - Escape";
+        case 0x0057: return "Ultimate Weapon - Gongaga Reactor";
+        case 0x0058: return "Corel Prison - Dyne Battle";
+        case 0x0059: return "Ultimate Weapon - Forest";
+        default: return "UNKNONW location";
+    }
+}

+ 628 - 0
src/installer/data/BattleSceneFile.h

@@ -0,0 +1,628 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+#include <common/File.h>
+#include <data/Attack.h>
+#include <data/Enemy.h>
+#include <data/Formation.h>
+
+class BattleSceneFile{
+
+    public:
+
+        /**
+         * Constructor.
+         *
+         * Reads data from a file.
+         *
+         * @param[in] id ID of the file, the position at which it is found.
+         * @param[in] file File to read from.
+         */
+        BattleSceneFile(const unsigned int id, File file);
+
+        /**
+         * Destructor.
+         */
+        ~BattleSceneFile();
+
+        /**
+         * Retrieves the attacks found in the scene.
+         *
+         * @return The list of attacks.
+         */
+        std::vector<Attack> GetAttacks();
+
+        /**
+         * Retrieves the enemies found in the scene.
+         *
+         * @return The list of enemies.
+         */
+        std::vector<Enemy> GetEnemies();
+
+        /**
+         * Retrieves the formations found in the scene.
+         *
+         * @return The list of formations.
+         */
+        std::vector<Formation> GetFormations();
+
+    private:
+
+        /**
+         * A battle scene structure.
+         */
+        struct Scene{
+
+            /**
+             * Scene battle setup data (20 bytes).
+             */
+            struct Setup{
+
+                /**
+                 * Battle location.
+                 */
+                u16 location;
+
+                /**
+                 * ID of the battle that start upon defeating all enemies.
+                 *
+                 * If set, a new set of enemies will be loaded when all the current ones are
+                 * defeated, without ending the battle scene.
+                 */
+                u16 next_battle;
+
+                /**
+                 * @todo Understand and document.
+                 */
+                u16 escape_counter;
+
+                /**
+                 * Unused data.
+                 */
+                u16 unused;
+
+                /**
+                 * In arena battles, candidate enemy formation IDs for next round.
+                 */
+                u16 next_arena_candidates[4];
+
+                /**
+                 * Battle flags.
+                 *
+                 * Includes the escapable flag.
+                 */
+                u16 flags;
+
+                /**
+                 * Battle layout.
+                 *
+                 * 00 - Normal figh.t
+                 * 01 - Preemptive.
+                 * 02 - Back attack.
+                 * 03 - Side attack.
+                 * 04 - Attacked from both sides (pincer attack, reverse side attack).
+                 * 05 - Another attack from both sides battle (different maybe?).
+                 * 06 - Another side attack.
+                 * 07 - A third side attack.
+                 * 08 - Normal battle that locks player in the front row, change command disabled.
+                 */
+                u8 layout;
+
+                /**
+                 * Indexed Pre-Battle Camera position .
+                 */
+                u8 camera;
+            };
+
+            /**
+             * Scene camera placement data.
+             */
+            struct Camera{
+
+                /**
+                 * Position for a camera scene.
+                 */
+                struct Position{
+
+                    /**
+                     * X position.
+                     */
+                    u16 x;
+
+                    /**
+                     * Y position.
+                     */
+                    u16 y;
+
+                    /**
+                     * z position.
+                     */
+                    u16 z;
+
+                    /**
+                     * X direction.
+                     */
+                    u16 d_x;
+
+                    /**
+                     * Y direction.
+                     */
+                    u16 d_y;
+
+                    /**
+                     * z direction.
+                     */
+                    u16 d_z;
+                };
+
+                /**
+                 * Camera positions.
+                 *
+                 * Index 0 is primary, indexes 1 and 2 are secondary positions.
+                 */
+                Position camera[3];
+
+                /**
+                 * Unused data.
+                 */
+                u8 unused[12];
+            };
+
+            /**
+             * A scene formation structure.
+             */
+            struct Formation{
+
+                /**
+                 * Enemy ID,
+                 */
+                u16 id;
+
+                /**
+                 * Enemy X position.
+                 */
+                u16 x;
+
+                /**
+                 * Enemy Y position.
+                 */
+                u16 y;
+
+                /**
+                 * Enemy Z position.
+                 */
+                u16 z;
+
+                /**
+                 * Enemy row.
+                 */
+                u16 row;
+
+                /**
+                 * Binary "Cover Flags".
+                 *
+                 * These flags are used in conjunction with row to determine if a target can be
+                 * selected as the target of a short-range attack. The determination of this is
+                 * worked out in this way: An enemy exists in row 1 and another in row 2. If the
+                 * enemy in row 1 shares a cover flag with the enemy in row 2 then the enemy in row
+                 * 2 cannot be targeted until all enemies in row 1 that share a cover flag with the
+                 * row 2 enemy is defeated. It works like this. Two active enemies exist, A and B.
+                 *
+                 * If ((B's row > A's row) and (B's cover flags AND A's cover flags) > 0) then
+                 * enemy B cannot be targeted by short-range attacks.
+                 */
+                u16 cover_flags;
+
+                /**
+                 * Initial condition flags. Only last 5 bits are considered.
+                 * 0x0001  Visible.
+                 * 0x0002  Indicates initial direction facing if players get a side attack.
+                 * 0x0004  Unknown.
+                 * 0x0008  Targetable.
+                 * 0x0010  Main Script Active.
+                 */
+                u32 flags;
+            };
+
+            struct EnemyData{
+
+                /**
+                 * Enemy's name (completed by FFh bytes).
+                 */
+                u8 name[32];
+
+                /**
+                 * Enemy's level.
+                 */
+                u8 level;
+
+                /**
+                 * Enemy's speed.
+                 */
+                u8 spd;
+
+                /**
+                 * Enemy's luck.
+                 */
+                u8 lck;
+
+                /**
+                 * Enemy's evade stat.
+                 */
+                u8 eva;
+
+                /**
+                 * Enemy's strength.
+                 */
+                u8 str;
+
+                /**
+                 * Enemy's defense.
+                 */
+                u8 def;
+
+                /**
+                 * Enemy's magic.
+                 */
+                u8 mag;
+
+                /**
+                 * Enemy's magic defense.
+                 */
+                u8 mdef;
+
+                /**
+                 * Elements of the enemy.
+                 */
+                u8 elements[8];
+
+                /**
+                 * Element rates for @{see elements}, respectively.
+                 */
+                u8 element_rates[8];
+
+                /**
+                 * Animation index for each @{see attacks}.
+                 */
+                u8 animation_index[16];
+
+                /**
+                 * Enemy attack IDs.
+                 */
+                u16 attacks[16];
+
+                /**
+                 * Enemy Attacks Camera Movement Id for single and multiple targets (2 bytes each).
+                 *
+                 * If set this will overwrite camera movement set in attack itself.
+                 */
+                u16 attack_camera[16];
+
+                /**
+                 * Item drop/steal rates.
+                 *
+                 * These are chances to get items listed in @{see item}. 1 byte per item. If the
+                 * rate is lower than 80h, for e.g. 08h - then this is a drop item and has 8/63 [63
+                 * is max] chance for drop. But if rate is higher than 80h, let's say... A0h, then
+                 * this is an item for steal, and chances for successful steal is
+                 * A0h - 80h = 20h = 32/63.
+                 */
+                u8 item_rate[4];
+
+                /**
+                 * IDs of items dropped/stolen from the enemy.
+                 *
+                 * 0xFFFF means no item.
+                 */
+                u8 item[4];
+
+                /**
+                 * IDs of attacks that can be performed while manipulated.
+                 *
+                 * The first one is the attack to use in berserk mode.
+                 */
+                u16 maniputale_attacks[3];
+
+                /**
+                 * Unknown data.
+                 */
+                u16 unknown_0;
+
+                /**
+                 * Enemy's MP.
+                 */
+                u16 mp;
+
+                /**
+                 * AP given upon enemy defeat.
+                 */
+                u16 ap;
+
+                /**
+                 * ID of the item the enemy can be morphed into.
+                 *
+                 * If 0xFFFF, the enemy can't be morphed
+                 */
+                u16 morph;
+
+                /**
+                 * Back damage multiplier.
+                 *
+                 * damage = damage * back_damage / 8.
+                 */
+                u8 back_damage;
+
+                /**
+                 * Unused data (align).
+                 */
+                u8 unused;
+
+                /**
+                 * Enemy's HP.
+                 */
+                u32 hp;
+
+                /**
+                 * Experience points given upon defeat.
+                 */
+                u32 exp;
+
+                /**
+                 * Money given upon defeat.
+                 */
+                u32 money;
+
+                /**
+                 * Status immunities.
+                 *
+                 * @todo: a bit set is an immunity?
+                 */
+                u32 immunities;
+
+                /**
+                 * Unknown data.
+                 */
+                u32 unknown_1;
+            };
+
+            /**
+             * A battle scene attack data structure.
+             */
+            struct AttackData{
+
+                /**
+                 * Probability of the attack to land. 1 byte.
+                 */
+                u8 accuracy;
+
+                /**
+                 * Impact effect. 1 byte.
+                 *
+                 * The sprite shown when the attack hits.
+                 */
+                u8 impact_effect;
+
+                /**
+                 * Index of the animation played by the target when hit. 1 byte.
+                 */
+                u8 hurt_anim;
+
+                /**
+                 * Unknown data. 1 bytes.
+                 */
+                u8 unknown;
+
+                /**
+                 * MP cost. 2 bytes.
+                 *
+                 * Determines the camera movement when the command is executed over a single target.
+                 */
+                u16 mp;
+
+                /**
+                 * Impact sound. 2 bytes.
+                 *
+                 * Determines the camera movement when the command is executed over multiple targets.
+                 */
+                u16 sound;
+
+                /**
+                 * Camera movement ID for single target. 2 bytes.
+                 *
+                 * Determines the camera movement when the command is executed over a single target.
+                 */
+                u16 camera_single;
+
+                /**
+                 * Camera movement ID for multiple target. 2 bytes.
+                 *
+                 * Determines the camera movement when the command is executed over multiple
+                 * targets.
+                 */
+                u16 camera_multiple;
+
+                /**
+                 * Targeting mode. 1 byte.
+                 */
+                u8 target;
+
+                /**
+                 * Atack effect ID. 1 byte.
+                 *
+                 * Used for animation in battle.
+                 */
+                u8 effect;
+
+                /**
+                 * Damage formula. 1 byte.
+                 *
+                 * This byte is divided into two nybbles (four bits). Upper nybble determines what
+                 * considerations are made in calculating damage such as physical/magical, allowing
+                 * criticals, how to calculate accuracy, etc. There are also three sets of known
+                 * formulae that are paired with the upper nybble values. These are selected in the
+                 * Lower Nybble and determine how to calculate pre-defense damage
+                 */
+                u8 damage;
+
+                /**
+                 * The attack power. 1 byte.
+                 *
+                 * Used for damage/healing calculation.
+                 */
+                u8 power;
+
+                /**
+                 * The restore type.
+                 */
+                u8 condition;
+
+                /**
+                 * Information about status change mode and chance. 1 bytes.
+                 */
+                u8 status_change;
+
+                /**
+                 * Special flags. 1 byte.
+                 *
+                 * @todo Document and retrieve with info from
+                 * https://wiki.ffrtt.ru/index.php?title=FF7/Battle/Attack_Special_Effects
+                 */
+                u8 additional_effects;
+
+                /**
+                 * Special flags. 1 byte.
+                 *
+                 * @todo Document and retrieve with info from
+                 * https://wiki.ffrtt.ru/index.php?title=FF7/Item_data
+                 */
+                u8 additional_effects_mod;
+
+                /**
+                 * 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;
+
+                /**
+                 * Information about the item elements. 2 bytes.
+                 */
+                u16 element;
+
+                /**
+                 * Special flags. 2 bytes.
+                 *
+                 * @todo document and parse with info from
+                 * https://wiki.ffrtt.ru/index.php?title=FF7/Battle/Special_Attack_Flags
+                 */
+                u16 special;
+
+            };
+
+            /**
+             * Enemy IDs.
+             */
+            u16 enemy[3];
+
+            /**
+             * Unused data (padding).
+             */
+            u16 unused;
+
+            /**
+             * Battle scene set-ups.
+             */
+            Setup setup[4];
+
+            /**
+             * Camera placement data.
+             */
+            Camera camera[4];
+
+            /**
+             * Battle formation data.
+             */
+            Formation formation[4][6];
+
+            /**
+             * Enemy data.
+             */
+            EnemyData enemy_data[3];
+
+            /**
+             * Attack data.
+             */
+            AttackData attack_data[32];
+
+            /**
+             * Attack IDs.
+             */
+            u16 attack_ids[32];
+
+            /**
+             * Attack names.
+             */
+            u8 attack_names[32][32];
+
+            // TOOD: AI
+        };
+
+        /**
+         * Retrieves a location name from it's ID.
+         *
+         * @param[in] id Location ID.
+         * @return The location name, or "UNKNOWN Location" if the ID is invalid.
+         */
+        static std::string LocationToString(const int id);
+
+        /**
+         * Reads data from a file.
+         *
+         * @param[in] file File to read from.
+         */
+        void Read(File file);
+
+        /**
+         * The file ID.
+         */
+        unsigned int id_;
+
+        /**
+         * The scene data.
+         */
+        Scene scene_;
+
+        /**
+         * Attacks found in the scene.
+         */
+        std::vector<Attack> attacks_;
+
+        /**
+         * Enemies found in the scene.
+         */
+        std::vector<Enemy> enemies_;
+
+        /**
+         * Formations found in the scene.
+         */
+        std::vector<Formation> formations_;
+
+};

+ 241 - 0
src/installer/data/Enemy.h

@@ -0,0 +1,241 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+#include "common/TypeDefine.h"
+#include "data/GameData.h"
+
+struct Enemy{
+
+    struct Element{
+
+        /**
+         * The element ID.
+         */
+        u8 id;
+
+        /**
+         * Damage modification factor when attacked by the element.
+         *
+         * 1 means normal damage taken.
+         * 0 means no damage taken.
+         * [0-1] means reduced damage.
+         * [1-10) means extended damage.
+         * (-10-0) means recovery
+         * >10 means death.
+         * <-10 means full recovery.
+         */
+        float factor;
+    };
+
+    /**
+     * Enemy attack data.
+     */
+    struct Attack{
+
+        /**
+         * Attack ID.
+         */
+        u16 id;
+
+        /**
+         * Camera ID to use during the attack.
+         *
+         * -1 to not move the camera.
+         */
+        int camera;
+    };
+
+    /**
+     * Data for drop and steal items.
+     */
+    struct Item{
+
+        /**
+         * Item ID.
+         */
+        unsigned int id;
+
+        /**
+         * Steal or drop rate.
+         */
+        float rate;
+    };
+
+    /**
+     * Status immunity.
+     */
+    struct Immunity{
+
+        /**
+         * Status ID.
+         */
+        unsigned int status;
+
+        /**
+         * Immunity rate.
+         *
+         * 0 or lower means no immunity, 1 or higher completely immune. Values between 0 and 1
+         * indicate resistance.
+         */
+        float rate;
+    };
+
+    /**
+     * The enemy ID.
+     */
+    u32 id;
+
+    /**
+     * The enemy name.
+     */
+    std::string name;
+
+    /**
+     * The enemy level.
+     */
+    u8 level;
+
+    /**
+     * The enemy strength stat.
+     */
+    u8 str;
+
+    /**
+     * The enemy magic stat.
+     */
+    u8 mag;
+
+    /**
+     * The enemy defense stat.
+     */
+    u8 def;
+
+    /**
+     * The enemy magic defense stat.
+     */
+    u8 mdef;
+
+    /**
+     * The enemy speed stat.
+     */
+    u8 spd;
+
+    /**
+     * The enemy luck stat.
+     */
+    u8 lck;
+
+    /**
+     * The enemy evasion stat.
+     */
+    u8 eva;
+
+    /**
+     * The enemy HP.
+     */
+    u32 hp;
+
+    /**
+     * The enemy MP.
+     */
+    u32 mp;
+
+    /**
+     * Experience given upon defeat.
+     */
+    u32 exp;
+
+    /**
+     * AP given upon defeat.
+     */
+    u32 ap;
+
+    /**
+     * Money given upon defeat.
+     */
+    u32 money;
+
+    /**
+     * Back damage multiplier.
+     */
+    float back_damage;
+
+    /**
+     * List of animations.
+     */
+    std::vector<unsigned int> animations;
+
+    /**
+     * Elemental affinities.
+     */
+    std::vector<Element> elements;
+
+    /**
+     * Status immunities and resistances.
+     */
+    std::vector<Immunity> immunities;
+
+    /**
+     * Enemy attacks.
+     */
+    std::vector<Attack> attacks;
+
+    /**
+     * List of attacks that can be used while manipulated.
+     *
+     * The first one is also the one used in berserkr. If empty, it means the enemy can't be
+     * manipulated or berserkd.
+     */
+    std::vector<u16> manipulate_attacks;
+
+    /**
+     * List of the items that can be stolen from the enemy.
+     */
+    std::vector<Item> steal;
+
+    /**
+     * List of the items that can be dropped from the enemy.
+     */
+    std::vector<Item> drop;
+
+    /**
+     * ID of the item the enemy can be morphed into.
+     *
+     * If -1, it can't be morphed.
+     */
+    unsigned int morph;
+
+    /**
+     * Constructor.
+     *
+     * Initializes to default values.
+     */
+    Enemy():
+      id(0), name(""), level(1), str(1), mag(1), def(1), mdef(1), spd(1), lck(1), eva(1), hp(1),
+      mp(1), exp(0), ap(0), money(0), back_damage(1.0f), morph(-1)
+    {
+        animations.clear();
+        elements.clear();
+        immunities.clear();
+        attacks.clear();
+        manipulate_attacks.clear();
+        steal.clear();
+        drop.clear();
+    }
+};

+ 321 - 0
src/installer/data/Formation.h

@@ -0,0 +1,321 @@
+/*
+ * V-Gears
+ * Copyright (C) 2022 V-Gears Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+#include "common/TypeDefine.h"
+#include "data/GameData.h"
+
+/**
+ * An enemy formation
+ */
+struct Formation{
+
+    /**
+     * Battle layouts.
+     */
+    enum class Layout{
+
+        /**
+         * Normal fight.
+         */
+        NORMAL = 0,
+
+        /**
+         * Preemptive attack.
+         *
+         * Enemies are on their back, they bars are empty. When attacked, or after a certain time,
+         * they turn and their bars start to fill.
+         */
+        PREEMPTIVE,
+
+        /**
+         * Back attack.
+         *
+         * Players are on their back, they bars are empty. When attacked, or after a certain time,
+         * they turn and their bars start to fill. Player rows are reversed.
+         */
+        BACK_ATTACK,
+
+        /**
+         * Side attack.
+         *
+         * Players in both sides, enemies in center, facing one or other direction. Change command
+         * disabled.
+         */
+        SIDE_ATTACK,
+
+        /**
+         * Enemies in both sides, player in center, front row, facing one or the other direction.
+         * Change command disabled. Unescapable until one of the sides is cleared.
+         */
+        PINCER_ATTACK,
+
+        /**
+         * Locked position fight.
+         *
+         * Like a normal one, but all players in the front row. Change command disabled.
+         */
+        LOCKED
+    };
+
+    /**
+     * Each enemy in the formation.
+     */
+    struct Enemy{
+
+        /**
+         * Enemy ID.
+         */
+        unsigned int id;
+
+        /**
+         * Enemy X position.
+         */
+        int x;
+
+        /**
+         * Enemy Y position.
+         */
+        int y;
+
+        /**
+         * Enemy Z position.
+         */
+        int z;
+
+        /**
+         * Enemy row.
+         */
+        unsigned int row;
+
+        /**
+         * Cover flags.
+         *
+         * These flags are used in conjunction with row to determine if a target can be selected
+         * as the target of a short-range attack. The determination of this is worked out in this
+         * way: An enemy exists in row 1 and another in row 2. If the enemy in row 1 shares a cover
+         * flag with the enemy in row 2 then the enemy in row 2 cannot be targeted until all
+         * enemies in row 1 that share a cover flag with the row 2 enemy is defeated. It works like
+         * this. Two active enemies exist, A and B.
+         *
+         * If ((B's row > A's row) and (B's cover flags AND A's cover flags) > 0)
+         * then enemy B cannot be targeted by short-range attacks.
+         *
+         * For any enemies A and B.
+         *
+         * Example:
+         * Consider the Battery Cap x6 battle in the forest between Nibelheim and Rocket Town.
+         * Their cover flags (in binary) are:
+         *
+         * Row 1:       00100
+         * Row 2:    00110 01100
+         * Row 3: 00011 00100 11000
+         *
+         * The battery caps in row 2 cannot be targeted by a short-range attack until the one in
+         * row 1 has been defeated because they share the 0x4 cover flag. Once row 1 has been
+         * cleared:
+         *
+         * Row 2:    00110 01100
+         * Row 3: 00011 00100 11000
+         *
+         * The battery cap on left in row 2 covers the left two in row 3 because it shares flag 0x4
+         * with the one in the middle and flag 0x2 with the one on the far left. As long as it is
+         * active these in row 3 cannot be targeted. Similarly, the battery cap on the right in row
+         * 2 shares the 0x4 flag with the middle of row 3 and the 0x8 flag with the far right of
+         * row 3 so these cannot be targeted until the right side of row 2 is defeated. It is also
+         * necessary to note that because row 1 does not share any flags with the extreme right and
+         * left of row 3, they can be targeted if the corresponding enemy in row 2 is defeated even
+         * if the row 1 enemy is still active. Also of note is that enemies in the same row that
+         * share cover flags are not considered.
+         */
+        char cover[5];
+
+        /**
+         * Indicates if the enemy is initially visible.
+         */
+        bool visible;
+
+        /**
+         * Indicates initial direction facing if players get a side attack.
+         */
+        int direction;
+
+        /**
+         * Indicates if the enemy is targeteable.
+         */
+        bool targeteable;
+
+        /**
+         * @todo Understand and document.
+         */
+        bool main_script_active;
+
+        /**
+         * Constructor.
+         *
+         * Initializes to default values.
+         */
+        Enemy():
+          id(0), x(0), y(0), z(0), row(0), visible(true),
+          direction(0), targeteable(true), main_script_active(true)
+        {
+            for (int c = 0; c < 5; c ++) cover[c] = '0';
+        }
+    };
+
+    /**
+     * Position for a camera.
+     */
+    struct Camera{
+
+        /**
+         * X position.
+         */
+        int x;
+
+        /**
+         * Y position.
+         */
+        int y;
+
+        /**
+         * z position.
+         */
+        int z;
+
+        /**
+         * X direction.
+         */
+        int d_x;
+
+        /**
+         * Y direction.
+         */
+        int d_y;
+
+        /**
+         * z direction.
+         */
+        int d_z;
+
+        /**
+         * Constructor.
+         *
+         * Initializes to default values.
+         */
+        Camera(): x(0), y(0), z(0), d_x(0), d_y(0), d_z(0){}
+    };
+
+    /**
+     * Formation ID.
+     */
+    unsigned int id;
+
+    /**
+     * When all enemies are defeated, begin battle with this ID without ending battle scene.
+     */
+    int next_formation;
+
+    /**
+     * Difficulty to escape.
+     */
+    float escape_counter;
+
+    /**
+     * Indicates if the battle can be escaped.
+     */
+    bool escapable;
+
+    /**
+     * If true, the victory pose will be played after the battle.
+     */
+    bool skip_victory_pose;
+
+    /**
+     * If true, spoils (item, ap, money...) will not be obtained after the battle.
+     *
+     * The spoil screens will not be displayed.
+     */
+    bool skip_spoils;
+
+    /**
+     * If false, this battle can never be preemtied.
+     */
+    bool preemptive_disabled;
+
+    /**
+     * Battle layout.
+     */
+    Layout layout;
+
+    /**
+     * The enemies in the formation.
+     */
+    std::vector<Enemy> enemies;
+
+    /**
+     * Location ID.
+     *
+     * Must be set, except for formations referenced by other formation's {@see next_formation} or
+     * {@see next_arena_formation_candidates}. In those cases, it can be -1.
+     */
+    int location;
+
+    /**
+     * Location name.
+     *
+     * Never used, it's to have descriptive XML files.
+     */
+    std::string location_name;
+
+    /**
+     * Camera positions.
+     */
+    std::vector<Camera> camera_positions;
+
+    /**
+     * Index of the initial camera position.
+     */
+    int initial_camera_position;
+
+    /**
+     * Indicates if the formation is an arena battle.
+     */
+    bool is_arena_battle;
+
+    /**
+     * FOr arena battles, candidates fro the next formation.
+     */
+    std::vector<int> next_arena_formation_candidates;
+
+    /**
+     * Constructor.
+     *
+     * Initializes to default values.
+     */
+    Formation():
+      id(0), next_formation(-1), escape_counter(0.0f), escapable(true), skip_victory_pose(false),
+      skip_spoils(false), preemptive_disabled(false), layout(Layout::NORMAL), location(-1),
+      location_name(""), initial_camera_position(0),is_arena_battle(false)
+    {
+        enemies.clear();
+        camera_positions.clear();
+        next_arena_formation_candidates.clear();
+    }
+
+};