Эх сурвалжийг харах

Installer reworked:
- Field data installer separated from the main installer.
- Process divided in more steps, provides better feedback.
- Code cleanup and reorganizaion, improved readability.

Iñigo Valentin 3 жил өмнө
parent
commit
a815e4b7cb

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

@@ -96,6 +96,7 @@ set(HEADER_FILES
     include/SpawnPointDb.h
     include/KernelDataInstaller.h
     include/MediaDataInstaller.h
+    include/FieldDataInstaller.h
 )
 
 
@@ -171,6 +172,7 @@ set(SOURCE_FILES
     src/TexFile.cpp
     src/KernelDataInstaller.cpp
     src/MediaDataInstaller.cpp
+    src/FieldDataInstaller.cpp
 )
 
 # For SUDM. TODO: Add elses.

+ 113 - 201
V-Gears-Installer/include/DataInstaller.h

@@ -19,25 +19,10 @@
 #include <vector>
 #include <iostream>
 #include "common/VGearsApplication.h"
-#include "common/FinalFantasy7/FF7NameLookup.h"
-#include "data/VGearsTriggersFile.h"
-#include "data/VGearsFLevelFile.h"
-#include "FieldTextWriter.h"
+#include "FieldDataInstaller.h"
 #include "KernelDataInstaller.h"
 #include "MediaDataInstaller.h"
 #include "ModelsAndAnimationsDb.h"
-#include "ScopedLgp.h"
-#include "SpawnPointDb.h"
-
-// TODO: Separate classes in files.
-// TODO: Move implementations to cpp file.
-
-typedef std::set<std::string> MapCollection;
-
-typedef std::map<u16, SpawnPointDb> FieldSpawnPointsMap;
-
-typedef std::map<u16, float> FieldScaleFactorMap;
-
 
 /**
  * The data installer.
@@ -59,15 +44,13 @@ class DataInstaller{
          *
          * @param[in] input_dir Path to the directory containing the original
          * data to parse.
-         * @param[in] exe_path Path to the ff7.exe file. It's optional and it may be empty.
          * @param[in] output_dir Path to the directory to write generated data
          * to.
          * @param[in] write_output_line Pointer to function to write output.
          */
         DataInstaller(
-          const std::string input_dir, const std::string exe_path, const std::string output_dir,
-          std::function<void(std::string)> write_output_line,
-          std::function<void(std::string)> set_progress_label
+          const std::string input_dir, const std::string output_dir,
+          std::function<void(std::string, int, bool)> write_output_line
         );
 
         /**
@@ -101,134 +84,151 @@ class DataInstaller{
         void CreateDir(const std::string& dir);
 
         /**
-         * Converts a tex file to png.
+         * Creates all directories required for the installation.
          *
-         * The image is saved in the model data directory.
-         *
-         * @param[in] name Path to the tex file to convert.
+         * It also adds certain location to the resource group manager.
          */
-        void TexToPng(const std::string name);
+        void CreateDirectories();
 
         /**
-         * Exports a mesh to a file.
-         *
-         * The file will have the mesh name.
-         *
-         * @param[in] outdir Path to the directory where the file will be saved.
-         * @param[in] mesh The mesh to export.
+         * Cleans up after the installation is complete.
          */
-        void ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh);
+        void CleanInstall();
 
         /**
-         * Converts a FFVII PC field to a V-Gears field.
-         *
-         * @param[in] field The field map to convert.
+         * Installation steps.
          */
-        void PcFieldToVGearsField(VGears::FLevelFilePtr& field);
+        enum InstallationSteps{
 
-        /**
-         * Initializer for {@see CollectionFieldSpawnAndScaleFactors}.
-         *
-         * Installation step 1.
-         */
-        void InitCollectSpawnAndScaleFactors();
+            /**
+             * Installation not started.
+             */
+            IDLE = 0,
 
-        /**
-         * Reads spawn points and scale factors from flevel files.
-         *
-         * Installation step 2.
-         */
-        void CollectionFieldSpawnAndScaleFactors();
+            /**
+             * Create all directories required for the installation.
+             */
+            CREATE_DIRECTORIES,
 
-        /**
-         * Converts FFVII PC fields to V-Gears format.
-         */
-        void ConvertFieldsIteration();
+            /**
+             * Initialize data for installation.
+             */
+            INITIALIZE,
 
-        /**
-         * Initializer for {@see WriteMapsXmlIteration}.
-         *
-         * Installation step 3.
-         */
-        void WriteMapsXmlBegin();
+            /**
+             * Parses item and materia prices from ff7.exe.
+             */
+            KERNEL_PRICES,
 
-        /**
-         * Saves the game maps to XML files.
-         *
-         * Installation step 4.
-         */
-        void WriteMapsXmlIteration();
+            /**
+             * Parses command data from KERNEL.BIN.
+             */
+            KERNEL_COMMANDS,
 
-        /**
-         * Cleans up after {@see WriteMapsXmlIteration}.
-         *
-         * Installation step 5.
-         */
-        void EndWriteMapsXml();
+            /**
+             * Parses attack data from KERNEL.BIN.
+             */
+            KERNEL_ATTACKS,
 
-        /**
-         * Initializer for {@see ConvertFieldModelsIteration}.
-         *
-         * Installation step 6.
-         */
-        void ConvertFieldModelsBegin();
+            /**
+             * Parses characters data from KERNEL.BIN.
+             */
+            KERNEL_CHARACTERS,
 
-        /**
-         * Converts the field models to V-Gears format.
-         *
-         * Installation step 7 and final.
-         */
-        void ConvertFieldModelsIteration();
+            /**
+             * Parses item data from KERNEL.BIN.
+             */
+            KERNEL_ITEMS,
 
-        /**
-         * Installation steps.
-         */
-        enum InstallationSteps{
+            /**
+             * Parses growth data from KERNEL.BIN.
+             */
+            KERNEL_GROWTH,
 
             /**
-             * Installation not started.
+             * Parses weapon data from KERNEL.BIN.
              */
-            IDLE = 0,
+            KERNEL_WEAPONS,
+
+            /**
+             * Parses armor data from KERNEL.BIN.
+             */
+            KERNEL_ARMORS,
+
+            /**
+             * Parses accessory data from KERNEL.BIN.
+             */
+            KERNEL_ACCESSORIES,
+
+            /**
+             * Parses materia data from KERNEL.BIN.
+             */
+            KERNEL_MATERIA,
+
+            /**
+             * Parses key item data data from KERNEL.BIN.
+             */
+            KERNEL_KEY_ITEMS,
+
+            /**
+             * Parses summon names data from KERNEL.BIN.
+             */
+            KERNEL_SUMMON_NAMES,
+
+            /**
+             * Parses the initial savemap from KERNEL.BIN.
+             */
+            KERNEL_SAVEMAP,
+
+            /**
+             * Extract game images and icons from menu.lgp.
+             */
+            MEDIA_IMAGES,
 
             /**
              * Initializer for {@see SPAWN_POINTS_AND_SCALE_FACTORS}.
              */
-            SPAWN_POINTS_AND_SCALE_FACTORS_INIT = 1,
+            FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT,
 
             /**
              * Step that collects spawn points and sclae factors.
              */
-            SPAWN_POINTS_AND_SCALE_FACTORS = 2,
+            FIELD_SPAWN_POINTS_AND_SCALE_FACTORS,
 
             /**
              * Step that convets fields to V-Gears format.
              */
-            CONVERT_FIELDS = 3,
+            FIELD_CONVERT,
 
             /**
              * Initializer for {@see WRITE_MAPS}.
              */
-            WRITE_MAPS_INIT = 4,
+            FIELD_WRITE_INIT,
 
             /**
              * Step that writes XML maps.
              */
-            WRITE_MAPS = 5,
+            FIELD_WRITE,
 
             /**
              * Clean up after {@see WRITE_MAPS}.
              */
-            WRITE_MAPS_CLEAN = 6,
+            FIELD_WRITE_END,
 
             /**
              * Initializer for {@see CONVERT_FIELD_MODELS}.
              */
-            CONVERT_FIELD_MODELS_INIT = 7,
+            FIELD_CONVERT_MODELS_INIT,
 
             /**
              * Step that converts field models.
              */
-            CONVERT_FIELD_MODELS = 8,
+            FIELD_CONVERT_MODELS,
+
+            /**
+             * Cleans up after the installation.
+             */
+            CLEAN,
 
             /**
              * Number of installation steps.
@@ -246,6 +246,12 @@ class DataInstaller{
          */
         InstallationSteps installation_state_ = IDLE;
 
+        int substeps_;
+
+        int cur_substep_;
+
+        std::vector<std::string> field_model_names_;
+
         /**
          * The path to the directory from which to read the PC game data.
          */
@@ -261,21 +267,6 @@ class DataInstaller{
          */
         std::string output_dir_;
 
-        /**
-         * Iterator counter.
-         */
-        size_t iterator_counter_;
-
-        /**
-         * Helper variable to indicate internal progress of installation steps.
-         */
-        size_t conversion_step_;
-
-        /**
-         * Helper variable to indicate internal progress of installation steps.
-         */
-        size_t progress_step_num_elements_;
-
         /**
          * The installer application.
          *
@@ -289,6 +280,11 @@ class DataInstaller{
          */
         std::unique_ptr<ScopedLgp> fields_lgp_;
 
+        /**
+         * The installer for fiel maps data.
+         */
+        std::unique_ptr<FieldDataInstaller> field_installer_;
+
         /**
          * The installer for kernel data.
          */
@@ -299,98 +295,14 @@ class DataInstaller{
          */
         std::unique_ptr<MediaDataInstaller> media_installer_;
 
-        /**
-         * LGP archive with texture data.
-         */
-        std::unique_ptr<ScopedLgp> textures_lgp_;
-
-        /**
-         * LGP archive with field model data.
-         */
-        std::unique_ptr<ScopedLgp> field_models_lgp_;
-
-
-        /**
-         * List of flevel files.
-         */
-        Ogre::StringVectorPtr flevel_file_list_;
-
-        /**
-         * The list of maps.
-         */
-        std::vector<std::string> map_list_;
-
-        /**
-         * Map of the collected spawn points.
-         */
-        FieldSpawnPointsMap spawn_points_;
-
-        /**
-         * Map of th collected scale factors.
-         */
-        FieldScaleFactorMap scale_factors_;
-
-        /**
-         * ModelsAndAnimationsUsedByConvertedFields
-         */
-        ModelsAndAnimationsDb used_models_and_anims_;
-
-        /**
-         * List of converted maps.
-         */
-        MapCollection converted_map_list_;
-
-        /**
-         * Iterator for {@see converted_map_list}.
-         */
-        MapCollection::iterator converted_map_list_iterator_;
-
-        /**
-         * Field currently being processed.
-         */
-        VGears::FLevelFilePtr field_;
-
-        /**
-         * List of model files.
-         */
-        Ogre::StringVectorPtr field_model_file_list_;
-
-        /**
-         * An XML document.
-         *
-         * Internally used during some installation steps.
-         */
-        std::unique_ptr<TiXmlDocument> doc_;
-
-        /**
-         * An XML element.
-         *
-         * Internally used during some installation steps.
-         */
-        std::unique_ptr<TiXmlElement> element_;
-
-        /**
-         * Iterator for {@see used_models_and_anims_}.
-         */
-        ModelAnimationMap::iterator model_animation_map_iterator_;
-
         /**
          * Function used to print text to the log output, line by line.
+         *
+         * @param[in] msg the message to write.
+         * @param[in] level Log level for the message.
+         * @param[in] as_progress If true, the text will also be set as the current text behind the
+         * progress bar.
          */
-        std::function<void(std::string)> write_output_line_;
-
-        /**
-         * Function used to print set the current installation progress text.
-         */
-        std::function<void(std::string)> set_progress_label_;
-
-        /**
-         * Field text writer.
-         */
-        FieldTextWriter field_text_writer_;
+        std::function<void(std::string msg, int level, bool progress)> write_output_line_;
 
-        /**
-         * Written materials.
-         */
-        std::vector<std::string> materials_;
 };

+ 687 - 0
V-Gears-Installer/include/FieldDataInstaller.h

@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+
+#include <vector>
+#include <string>
+#include <OgreMesh.h>
+#include <tinyxml.h>
+#include "data/VGearsFLevelFile.h"
+#include "ScopedLgp.h"
+#include "SpawnPointDb.h"
+#include "ModelsAndAnimationsDb.h"
+#include "FieldTextWriter.h"
+#include "decompiler/field/FieldScriptFormatter.h"
+#include "decompiler/field/FieldDecompiler.h"
+#include "common/FinalFantasy7/FF7NameLookup.h"
+
+typedef std::vector<std::string> MapList;
+
+typedef std::map<u16, SpawnPointDb> FieldSpawnPointsMap;
+
+typedef std::map<u16, float> FieldScaleFactorMap;
+
+class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
+
+    public:
+
+        /**
+         * Constructor.
+         *
+         * @param[in] field_name The field name.
+         * @param[in] field_id_to_name_lookup Field name lookup table to use.
+         * @param[in] spawn_points The list of spawn points.
+         */
+        BaseFF7FieldScriptFormatter(
+          const std::string& field_name, const std::vector<std::string>& field_id_to_name_lookup,
+          FieldSpawnPointsMap& spawn_points
+        ) :
+          field_name_(field_name), field_id_to_name_lookup_(field_id_to_name_lookup),
+          spawn_points_(spawn_points)
+        {}
+
+        /**
+         * Composes a spawn point name
+         *
+         * The name will have the format
+         * {FIELD_NAME}_{ENTITY}_{FUNCTION_NAME}_addr_{ADDRESS}
+         *
+         * @param[in] map_id Unused.
+         * @param[in] entity Entity name.
+         * @param[in] function_name Name of the function.
+         * @param[in] address @todo Understand and document.
+         * @return The composed spawn point name.
+         */
+        virtual std::string GetSpawnPointName(
+          unsigned int map_id, const std::string& entity,
+          const std::string& function_name, unsigned int address
+        ) override{
+            return
+              field_name_ + "_" + entity + "_" + function_name + "_addr_" + std::to_string(address);
+        }
+
+        /**
+         * Retrieves a map name from it's ID.
+         *
+         * @param[in] map_id The map ID.
+         * @return The map name.
+         */
+        virtual std::string GetMapName(unsigned int map_id) override{
+            return field_id_to_name_lookup_[map_id];
+        }
+
+        /**
+         * Retrieves a user friendly variable name.
+         *
+         * @param[in] bank Variable bank.
+         * @param[in] addr Variable address.
+         * @return Friendly name for the variable, or an empty string if it
+         * doesn't have one assigned.
+         */
+        virtual std::string GetFriendlyVarName(
+          unsigned int bank, unsigned int addr
+        ) override{
+            return VGears::NameLookup::FieldScriptVarName(bank, addr);
+        }
+
+        /**
+         * Retrieves a user friendly entity name.
+         *
+         * @param[in] entity Entity name.
+         * @return Friendly name for the entity, or the current name if it
+         * doesn't have one assigned.
+         */
+        virtual std::string GetFriendlyEntityName(const std::string& entity) override{
+            return VGears::NameLookup::FieldScriptEntityName(entity);
+        }
+
+        /**
+         * Retrieves a user friendly character name.
+         *
+         * @param[in] char_id Character ID.
+         * @return Friendly name for the entity, or an empty string if it
+         * doesn't have one assigned.
+         */
+        virtual std::string GetFriendlyCharName(int char_id) override{
+            return VGears::NameLookup::CharName(char_id);
+        }
+
+        /**
+         * Retrieves a user friendly function name.
+         *
+         * @param[in] entity The name of the entity the function belongs to.
+         * @param[in] function_name Function name.
+         * @return Friendly name for the function, or the current name if it
+         * doesn't have one assigned.
+         */
+        virtual std::string GetFriendlyFunctionName(
+          const std::string& entity, const std::string& function_name
+        ) override{
+            return VGears::NameLookup::FieldScriptFunctionName(
+              field_name_, entity, function_name
+            );
+        }
+
+        /**
+         * Retrieves the header comment for a function in an entity.
+         *
+         * @param[in] entity The name of the entity the function belongs to.
+         * @param[in] function_name Function name.
+         * @return Friendly comment for the function, or an empty string if it
+         * doesn't have one assigned.
+         */
+        virtual std::string GetFunctionComment(
+          const std::string& entity, const std::string& function_name
+        ) override{
+            return VGears::NameLookup::FieldScriptFunctionComment(
+              field_name_, entity, function_name
+            );
+        }
+
+    protected:
+
+        /**
+         * The field name.
+         */
+        std::string field_name_;
+
+        /**
+         * The list of spawn points.
+         */
+        FieldSpawnPointsMap& spawn_points_;
+
+        /**
+         * Look up table relatingfield IDs and names.
+         */
+        const std::vector<std::string>& field_id_to_name_lookup_;
+};
+
+/**
+ * Handles the script gateway colection.
+ */
+class FF7FieldScriptGatewayCollector : public BaseFF7FieldScriptFormatter{
+
+    // TODO: Move declaration to a .h file.
+
+    public:
+
+        /**
+         * Cosntructor.
+         */
+        FF7FieldScriptGatewayCollector(
+          const std::string& field_name, const std::vector<std::string>& field_id_to_name_lookup,
+          FieldSpawnPointsMap& spawn_points, size_t this_field_id
+        ) :
+          BaseFF7FieldScriptFormatter(field_name,field_id_to_name_lookup, spawn_points),
+          field_id_(this_field_id)
+        {}
+
+        /**
+         * Adds an spawn point.
+         *
+         * If the spawn is new, a new record will be added to the spawn point
+         * database. If it was already there, update the record to add the
+         * origin.
+         *
+         * @param[in] map_id The target map ID.
+         * @param[in] entity The entity that acts as the spawn point.
+         * @param[in] function_name The spawn function name.
+         * @param[in] address @todo Understand and document.
+         * @param[in] x X coordinate of the field target at which to spawn.
+         * @param[in] y Y coordinate of the field target at which to spawn.
+         * @param[in] z Z coordinate of the field target at which to spawn.
+         * @param[in] angle Orientation at which to spawn.
+         */
+        virtual void AddSpawnPoint(
+          unsigned int map_id, const std::string& entity, const std::string& function_name,
+          unsigned int address, int x, int y, int z, int angle
+        ) override {
+            auto it = spawn_points_.find(map_id);
+            VGears::TriggersFile::Gateway gw = {};
+            gw.destination_field_id = map_id;
+            gw.destination.x = x;
+            gw.destination.y = y;
+            gw.destination.z = z;
+            gw.dir = angle;
+            if (it != std::end(spawn_points_)){
+                // Add to the list of gateways that link to destination_field_id.
+                SpawnPointDb::Record rec;
+                rec.from_script = true;
+                rec.field_id = field_id_;
+                rec.gateway = gw;
+                rec.entity_name = entity;
+                rec.script_function_name = function_name;
+                rec.gateway_index_or_map_jump_address = address;
+                it->second.gateways_to_this_field.push_back(rec);
+            }
+            else{
+                // Create a new record for destination_field_id
+                SpawnPointDb db;
+                db.target_field_id = map_id;
+                SpawnPointDb::Record rec;
+                rec.from_script = true;
+                rec.field_id = field_id_;
+                rec.gateway = gw;
+                rec.entity_name = entity;
+                rec.script_function_name = function_name;
+                rec.gateway_index_or_map_jump_address = address;
+                db.gateways_to_this_field.push_back(rec);
+                spawn_points_.insert(std::make_pair(db.target_field_id, db));
+            }
+        }
+
+    private:
+
+        /**
+         * The field map ID.
+         */
+        size_t field_id_;
+
+};
+
+/**
+ * Handles the formatting of field scripts.
+ */
+class FF7FieldScriptFormatter : public BaseFF7FieldScriptFormatter{
+
+    public:
+
+        /**
+         * Constructor.
+         *
+         * @param[in] field_name The field name.
+         * @param[in] models The list of field models.
+         * @param[in] field_id_to_name_lookup Field name lookup table to use.
+         * @param[in] spawn_points The list of spawn points.
+         */
+        FF7FieldScriptFormatter(
+          const std::string& field_name, const VGears::ModelListFilePtr& models,
+          const std::vector<std::string>& field_id_to_name_lookup,
+          FieldSpawnPointsMap& spawn_points
+        ) :
+          BaseFF7FieldScriptFormatter(field_name, field_id_to_name_lookup, spawn_points),
+          model_loader_(models)
+        {}
+
+        // Names an animation, can't return empty
+        /**
+         * Retreieves an animation name from iths ID.
+         *
+         * @param[in] char_id ID of the character the animation belogs to.
+         * @param[in] id Animation ID.
+         * @return The animation name. If there is no name assigned, or it
+         * can't be found, a string with the animation ID.
+         */
+        virtual std::string GetFriendlyAnimationName(int char_id, int id) override{
+            // Get the animation file name, then look up the friendly name of
+            // the "raw" animation.
+            if (static_cast<unsigned int>(char_id) >= model_loader_->GetModels().size()){
+                std::cerr << "FF7FieldScriptFormatter::AnimationName ERROR:"
+                  << "Char ID " << char_id << " out of bounds" << std::endl;
+                return std::to_string(id);
+            }
+            const auto& model_info = model_loader_->GetModels().at(char_id);
+            if (static_cast<unsigned int>(id) >= model_info.animations.size()){
+                std::cerr << "FF7FieldScriptFormatter::AnimationName ERROR:"
+                  << "In field " << field_name_ << " the model " << model_info.name
+                  << " animation with ID " << id << " is out of bounds ("
+                  << model_info.animations.size() << ")" << std::endl;
+                return std::to_string(id);
+            }
+            const auto raw_name = model_info.animations.at(id).name;
+            // Trim off ".yos" or whatever extension the model loader adds in.
+            Ogre::String base_name;
+            VGears::StringUtil::splitBase(raw_name, base_name);
+            VGears::StringUtil::toLowerCase(base_name);
+            return VGears::NameLookup::Animation(base_name);
+        }
+
+    private:
+
+        /**
+         * The field model loader.
+         */
+        const VGears::ModelListFilePtr& model_loader_;
+};
+
+class FieldDataInstaller{
+
+    public:
+
+        /**
+         * Installer constructor
+         *
+         * @param[in] input_dir Path to the directory containing the original
+         * data to parse.
+         * @param[in] output_dir Path to the directory to write generated data
+         * to.
+         */
+        FieldDataInstaller(const std::string input_dir, const std::string output_dir);
+
+        /**
+         * Installer destructor.
+         */
+        ~FieldDataInstaller();
+
+        /**
+         * Initializer for {@see CollectionFieldSpawnAndScaleFactors}.
+         *
+         * Installation step 1.
+         *
+         * @param[in] res_mgr The application resource manager.
+         * @return The total number of level files to process.
+         */
+        int CollectSpawnAndScaleFactorsInit(Ogre::ResourceGroupManager* res_mgr);
+
+        /**
+         * Reads spawn points and scale factors from flevel files.
+         *
+         * Installation step 2.
+         *
+         * @param[in] field_index Index of the field to collect from. Must be less than the value
+         * returned by {@see CollectSpawnAndScaleFactorsInit}.
+         */
+        void CollectSpawnAndScaleFactors(int field_index);
+
+        /**
+         * Converts FFVII PC fields to V-Gears format.
+         *
+         * @param[in] field_index Index of the field to collect from. Must be less than the value
+         * returned by {@see CollectSpawnAndScaleFactorsInit}.
+         */
+        void Convert(int field_index);
+
+        /**
+         * Initializer for {@see WriteMapsXmlIteration}.
+         *
+         * Installation step 3.
+         *
+         * @return The number of converted maps that need to be written.
+         */
+        int WriteInit();
+
+        /**
+         * Saves the game maps to XML files.
+         *
+         * Installation step 4.
+         *
+         * @param[in] field_index Index of the field to collect from. Must be less than the value
+         * returned by {@see WriteInit}.
+         */
+        void Write(int field_index);
+
+        /**
+         * Cleans up after {@see WriteMapsXmlIteration}.
+         *
+         * Installation step 5.
+         */
+        void WriteEnd();
+
+        /**
+         * Initializer for {@see ConvertFieldModelsIteration}.
+         *
+         * Installation step 6.
+         *
+         * @return List of names of models to convert.
+         */
+        std::vector<std::string> ConvertModelsInit();
+
+        /**
+         * Converts the field models to V-Gears format.
+         *
+         * Installation step 7 and final.
+         *
+         * @param[in] model_name Name of the model to convert.
+         */
+        void ConvertModels(std::string model_name);
+
+    private:
+
+        /**
+         * Creates a gateway script.
+         *
+         * @param[in] gateway_entity_name Name of the gateway entity.
+         * @param[in] target_map_name Name of the target map.
+         * @param[in] source_spawn_point_name The spawn point name.
+         * @return Gateway LUA script.
+         */
+        static std::string CreateGateWayScript(
+          const std::string& gateway_entity_name, const std::string& target_map_name,
+          const std::string& source_spawn_point_name
+        );
+
+        /**
+         * Retrieves a field ID from a name.
+         *
+         * @param[in] name The name of the field.
+         * @param[in] field_id_to_name_lookup Lookup table to use.
+         * @return The field ID for the specified name.
+         * @throws std::runtime_error If there is no ID for the field name.
+         */
+        static size_t GetFieldId(
+          const std::string& name, const std::vector<std::string>& field_id_to_name_lookup
+        );
+
+        /**
+         * Checks if a file is a field file.
+         *
+         * To verify it, it just checks that the extension is not '.tex', '.tut' or
+         * '.siz' and that the name is not 'maplist'.
+         *
+         * @param[in] resource_name The name of the file to test.
+         * @return False if the file is surely not a field file, true otherwise.
+         */
+        static bool IsAFieldFile(const Ogre::String& resource_name);
+
+        /**
+         * Checks if a map cause fatal crash bugs.
+         *
+         * It uses a hardcoded list of maps that are known to cause errors. This can be
+         * removed when whatever is causing the crash(s) is fixed.
+         *
+         * NOTE: Even so, conversion of all models will fail with a bone index out of
+         * bounds.
+         *
+         * @param[in] resource_name The name of the map file to test.
+         * @return True if the map will crash, false otherwise.
+         */
+        static bool WillCrash(const Ogre::String& resource_name);
+
+        /**
+         * Checks if a map is a test field.
+         *
+         * It uses a hardcoded list of maps that are known to be test maps. Unused maps
+         * are considered to be test maps.
+         *
+         * @param[in] resource_name The name of the map file to test.
+         * @return True if the map is a test map, false otherwise.
+         */
+        static bool IsTestField(const Ogre::String& resource_name);
+
+        /**
+         * Collects the scale factor from a map.
+         *
+         * @param[in] field The field to collect from.
+         * @param[out] scale_factors The scale factor for the map will be set here, in
+         * the appropiate map entry.
+         * @param[in] field_id_to_name_lookup Lookup table to relate field IDs and
+         * names.
+         */
+        static void CollectFieldScaleFactors(
+          VGears::FLevelFilePtr& field, FieldScaleFactorMap& scale_factors,
+          const std::vector<std::string>& field_id_to_name_lookup
+        );
+
+        /**
+         * Collects the spawn point from a field.
+         *
+         * If a spawn is new, a new record will be added to the spawn point database.
+         * If it was already there, update the record to add the origin.
+         *
+         * @param[in] field The field to collect from.
+         * @param[in] field_id_to_name_lookup Lookup table to relate field IDs and
+         * names.
+         * @param[out] spawn_points Spawn points will be added sequentially to the end
+         * of this map.
+         */
+        static void CollectSpawnPoints(
+          VGears::FLevelFilePtr& field, const std::vector<std::string>& field_id_to_name_lookup,
+          FieldSpawnPointsMap& spawn_points
+        );
+
+        /**
+         * The scale factor for line point coordinates.
+         *
+         * When a LINE opcode is found in the game scripts, the line points X and Y coordinates
+         * must be scaled down by this factor.
+         */
+        static float LINE_SCALE_FACTOR;
+
+        /**
+         * Path to the field models directory.
+         */
+        static std::string FIELD_MODELS_DIR;
+
+        /**
+         * Path to the field maps directory.
+         */
+        static std::string FIELD_MAPS_DIR;
+
+        /**
+         * Gateways to this map ID are considered to be inactive.
+         */
+        static int INACTIVE_GATEWAY_ID;
+
+        /**
+         * Exports a mesh to a file.
+         *
+         * The file will have the mesh name.
+         *
+         * @param[in] outdir Path to the directory where the file will be saved.
+         * @param[in] mesh The mesh to export.
+         */
+        void ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh);
+
+        /**
+         * Converts a FFVII PC field to a V-Gears field.
+         *
+         * @param[in] field The field map to convert.
+         */
+        void PcFieldToVGearsField(VGears::FLevelFilePtr& field);
+
+        /**
+         * Retrieves a field scale factor.
+         *
+         * @param[in] field_id The id (name) of the field.
+         * @return The scale factor for the map.
+         * @throws std::runtime_error If there is no ID for the field name.
+         */
+        float GetFieldScaleFactor(size_t field_id);
+
+        /**
+         * Creates a directory in the outputh path.
+         *
+         * @param[in] dir Path of the directory to create, relative to the
+         * output path..
+         * @throws std::runtime_error If the directory can't be created.
+         */
+        void CreateDir(const std::string& dir);
+
+        /**
+         * 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_;
+
+        /**
+         * Helper variable to indicate internal progress of installation steps.
+         */
+        size_t conversion_step_;
+
+        /**
+         * Helper variable to indicate internal progress of installation steps.
+         */
+        size_t progress_step_num_elements_;
+
+        /**
+         * Iterator counter.
+         */
+        size_t iterator_counter_;
+
+        /**
+         * LGP archive with field data.
+         */
+        std::unique_ptr<ScopedLgp> fields_lgp_;
+
+        /**
+         * LGP archive with texture data.
+         */
+        std::unique_ptr<ScopedLgp> textures_lgp_;
+
+        /**
+         * LGP archive with field model data.
+         */
+        std::unique_ptr<ScopedLgp> field_models_lgp_;
+
+
+        /**
+         * List of flevel files.
+         */
+        Ogre::StringVectorPtr flevel_file_list_;
+
+        /**
+         * The list of maps.
+         */
+        std::vector<std::string> map_list_;
+
+        /**
+         * Map of the collected spawn points.
+         */
+        FieldSpawnPointsMap spawn_points_;
+
+        /**
+         * Map of th collected scale factors.
+         */
+        FieldScaleFactorMap scale_factors_;
+
+        /**
+         * ModelsAndAnimationsUsedByConvertedFields
+         */
+        ModelsAndAnimationsDb used_models_and_anims_;
+
+        /**
+         * List of converted maps.
+         */
+        MapList converted_map_list_;
+
+        /**
+         * Iterator for {@see converted_map_list}.
+         */
+        MapList::iterator converted_map_list_iterator_;
+
+        /**
+         * Field currently being processed.
+         */
+        VGears::FLevelFilePtr field_;
+
+        /**
+         * List of model files.
+         */
+        Ogre::StringVectorPtr field_model_file_list_;
+
+        /**
+         * An XML document.
+         *
+         * Internally used during some installation steps.
+         */
+        std::unique_ptr<TiXmlDocument> doc_;
+
+        /**
+         * An XML element.
+         *
+         * Internally used during some installation steps.
+         */
+        std::unique_ptr<TiXmlElement> element_;
+
+        /**
+         * Iterator for {@see used_models_and_anims_}.
+         */
+        ModelAnimationMap::iterator model_animation_map_iterator_;
+
+        /**
+         * Function used to print text to the log output, line by line.
+         */
+        std::function<void(std::string)> write_output_line_;
+
+        /**
+         * Function used to print set the current installation progress text.
+         */
+        std::function<void(std::string)> set_progress_label_;
+
+        /**
+         * Field text writer.
+         */
+        FieldTextWriter field_text_writer_;
+
+        /**
+         * Written materials.
+         */
+        std::vector<std::string> materials_;
+};

+ 10 - 4
V-Gears-Installer/include/KernelDataInstaller.h

@@ -27,7 +27,7 @@ class KernelDataInstaller{
         /**
          * Constructor.
          *
-         * @param[in] path Absolute path to the KERNEL.BIN file
+         * @param[in] path Path selected in the installation as source directory.
          */
         KernelDataInstaller(std::string path);
 
@@ -38,10 +38,8 @@ class KernelDataInstaller{
 
         /**
          * Reads items and materia prices from the executable.
-         *
-         * @param[in] menu_path PAth to the "ff7.exe" file.
          */
-        void ReadPrices(std::string menu_path);
+        void ReadPrices();
 
         /**
          * Reads the command data from the kernel.
@@ -2441,6 +2439,9 @@ class KernelDataInstaller{
          */
         std::vector<SummonNameData> summon_names_;
 
+        /**
+         * The initial savemap structure.
+         */
         SaveMap savemap_;
 
         /**
@@ -2448,6 +2449,11 @@ class KernelDataInstaller{
          */
         BinGZipFile kernel_;
 
+        /**
+         * Path to ff7.exe file.
+         */
+        std::string exe_path_;
+
         /**
          * Item and materia shop prices.
          *

+ 0 - 7
V-Gears-Installer/include/MainWindow.h

@@ -86,13 +86,6 @@ class MainWindow : public QMainWindow{
          */
         void on_btn_data_src_clicked();
 
-        /**
-         * Triggered when the ff7.exe file button is clicked.
-         *
-         * Opens a file manager for file selection.
-         */
-        void on_btn_exe_src_clicked();
-
         /**
          * Triggered when the output data directory has done being edited.
          *

+ 155 - 1409
V-Gears-Installer/src/DataInstaller.cpp

@@ -13,61 +13,22 @@
  * GNU General Public License for more details.
  */
 
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <boost/program_options.hpp>
-#include <OgreConfigFile.h>
-#include <OgreArchiveManager.h>
-#include <OgreMeshSerializer.h>
-#include <OgreMaterialSerializer.h>
-#include <OgreTechnique.h>
-#include <OgreTextureManager.h>
-#include <OgreMaterialManager.h>
-#include <OgreSkeletonSerializer.h>
-#include <OgreMeshManager.h>
-#include <OgreRenderWindow.h>
-#include <OgreHardwarePixelBuffer.h>
-#include <OgreResourceGroupManager.h>
-#include <OgreLog.h>
-#include "VGearsGameState.h"
-#include "data/VGearsAFileManager.h"
-#include "data/VGearsBackgroundFileManager.h"
-#include "data/VGearsCameraMatrixFileManager.h"
-#include "data/VGearsHRCFileManager.h"
-#include "data/VGearsLZSFLevelFileManager.h"
-#include "data/VGearsPaletteFileManager.h"
-#include "data/VGearsPFileManager.h"
-#include "data/VGearsRSDFileManager.h"
-#include "data/VGearsTexFile.h"
-#include "data/VGearsTexCodec.h"
-#include "map/VGearsBackground2DFileManager.h"
-#include "map/VGearsWalkmeshFileManager.h"
-#include "data/FinalFantasy7/FF7ModelListFileManager.h"
-#include "data/VGearsLGPArchiveFactory.h"
-#include "common/FinalFantasy7/FF7NameLookup.h"
-#include "data/VGearsTexCodec.h"
-#include "data/VGearsMapListFile.h"
-#include <memory>
 #include <QtCore/QDir>
 #include "DataInstaller.h"
-#include "decompiler/field/FieldScriptFormatter.h"
-#include "decompiler/field/FieldDecompiler.h"
 
 float DataInstaller::LINE_SCALE_FACTOR = 0.0078124970964f;
 
 DataInstaller::DataInstaller(
-  const std::string input_dir, const std::string exe_path, const std::string output_dir,
-  std::function<void(std::string)> write_output_line,
-  std::function<void(std::string)> set_progress_label
+  const std::string input_dir, const std::string output_dir,
+  std::function<void(std::string, int, bool)> write_output_line
 )
 #ifdef _DEBUG
     : application_("plugins_d.cfg", "resources_d.cfg", "install_d.log"),
 #else
   : application_("plugins.cfg", "resources.cfg", "install.log"),
 #endif
-  input_dir_(input_dir), exe_path_(exe_path), output_dir_(output_dir),
-  write_output_line_(write_output_line), set_progress_label_(set_progress_label),
-  iterator_counter_(0), conversion_step_(0), progress_step_num_elements_(0)
+  input_dir_(input_dir), output_dir_(output_dir),
+  write_output_line_(write_output_line)
 {
     if (!application_.initOgre(true)) throw std::runtime_error("Ogre init failure");
     Ogre::Log* default_log( Ogre::LogManager::getSingleton().getDefaultLog());
@@ -78,1421 +39,206 @@ DataInstaller::DataInstaller(
 
 DataInstaller::~DataInstaller(){}
 
-const int DataInstaller::CalcProgress(){
-    // TODO: Make more accurate with iterator_counter_ and
-    // progress_step_num_elements_.
-    float curr_step = installation_state_ / static_cast<float>(STATE_COUNT);
-    curr_step = curr_step * 100.0f;
-    return static_cast<int>(curr_step);
-}
-
 int DataInstaller::Progress(){
     switch (installation_state_){
         case IDLE:
-            write_output_line_("Loading flevel.lgp");
-            set_progress_label_("Loading flevel.lgp");
-            progress_step_num_elements_ = 1;
-            iterator_counter_ = 0;
-            fields_lgp_ = std::make_unique<ScopedLgp>(
-              application_.getRoot(), input_dir_ + "data/field/flevel.lgp", "LGP", "FFVIIFields"
-            );
-            set_progress_label_("Installing kernel data...");
-            kernel_installer_ = std::make_unique<KernelDataInstaller>(
-              input_dir_ + "data/kernel/KERNEL.BIN"
-            );
-            kernel_installer_->ReadPrices(exe_path_);
-            CreateDir("game");
+            installation_state_ = CREATE_DIRECTORIES;
+            return CalcProgress();
+        case CREATE_DIRECTORIES:
+            write_output_line_("Creating directories...", 2, true);
+            CreateDirectories();
+            installation_state_ = INITIALIZE;
+            return CalcProgress();
+        case INITIALIZE:
+            write_output_line_("Initializing installers...", 2, true);
+            kernel_installer_ = std::make_unique<KernelDataInstaller>(input_dir_);
+            media_installer_ = std::make_unique<MediaDataInstaller>(input_dir_, output_dir_);
+            field_installer_ = std::make_unique<FieldDataInstaller>(input_dir_, output_dir_);
+            installation_state_ = KERNEL_PRICES;
+            return CalcProgress();
+        case KERNEL_PRICES:
+            write_output_line_("Parsing item and materia prices...", 2, true);
+            kernel_installer_->ReadPrices();
+            installation_state_ = KERNEL_COMMANDS;
+            return CalcProgress();
+        case KERNEL_COMMANDS:
+            write_output_line_("Extracting command data...", 2, true);
             kernel_installer_->ReadCommands();
             kernel_installer_->WriteCommands(output_dir_ + "game/commands.lua");
+            installation_state_ = KERNEL_ATTACKS;
+            return CalcProgress();
+        case KERNEL_ATTACKS:
+            write_output_line_("Extracting attack data...", 2, true);
             kernel_installer_->ReadAttacks();
             kernel_installer_->WriteAttacks(output_dir_ + "game/attacks.lua");
+            installation_state_ = KERNEL_CHARACTERS;
+            return CalcProgress();
+        case KERNEL_CHARACTERS:
+            write_output_line_("Extracting character data...", 2, true);
             kernel_installer_->ReadCharacters();
             kernel_installer_->WriteCharacters(output_dir_ + "game/characters.lua");
+            installation_state_ = KERNEL_ITEMS;
+            return CalcProgress();
+        case KERNEL_ITEMS:
+            write_output_line_("Extracting item data...", 2, true);
             kernel_installer_->ReadItems();
             kernel_installer_->WriteItems(output_dir_ + "game/items.lua");
+            installation_state_ = KERNEL_GROWTH;
+            return CalcProgress();
+        case KERNEL_GROWTH:
+            write_output_line_("Extracting growth data...", 2, true);
             kernel_installer_->ReadGrowth();
             kernel_installer_->WriteGrowth(output_dir_ + "game/growth.lua");
+            installation_state_ = KERNEL_WEAPONS;
+            return CalcProgress();
+        case KERNEL_WEAPONS:
+            write_output_line_("Extracting weapon data...", 2, true);
             kernel_installer_->ReadWeapons();
             kernel_installer_->WriteWeapons(output_dir_ + "game/weapons.lua");
-            kernel_installer_->ReadArmors() > 0;
+            installation_state_ = KERNEL_ARMORS;
+            return CalcProgress();
+        case KERNEL_ARMORS:
+            write_output_line_("Extracting armor data...", 2, true);
+            kernel_installer_->ReadArmors();
             kernel_installer_->WriteArmors(output_dir_ + "game/armors.lua");
+            installation_state_ = KERNEL_ACCESSORIES;
+            return CalcProgress();
+        case KERNEL_ACCESSORIES:
+            write_output_line_("Extracting accessory data...", 2, true);
             kernel_installer_->ReadAccessories();
             kernel_installer_->WriteAccessories(output_dir_ + "game/accessories.lua");
+            installation_state_ = KERNEL_MATERIA;
+            return CalcProgress();
+        case KERNEL_MATERIA:
+            write_output_line_("Extracting materia data...", 2, true);
             kernel_installer_->ReadMateria();
             kernel_installer_->WriteMateria(output_dir_ + "game/materia.lua");
+            installation_state_ = KERNEL_KEY_ITEMS;
+            return CalcProgress();
+        case KERNEL_KEY_ITEMS:
+            write_output_line_("Extracting key item data...", 2, true);
             kernel_installer_->ReadKeyItems();
             kernel_installer_->WriteKeyItems(output_dir_ + "game/key_items.lua");
+            installation_state_ = KERNEL_SUMMON_NAMES;
+            return CalcProgress();
+        case KERNEL_SUMMON_NAMES:
+            write_output_line_("Extracting summon attack names data...", 2, true);
             kernel_installer_->ReadSummonNames();
             kernel_installer_->WriteSummonNames(output_dir_ + "game/summons.lua");
+            installation_state_ = KERNEL_SAVEMAP;
+            return CalcProgress();
+        case KERNEL_SAVEMAP:
+            write_output_line_("Extracting the initial savemap...", 2, true);
             kernel_installer_->ReadInitialSaveMap();
             kernel_installer_->WriteInitialSaveMap(output_dir_ + "game/initial_savemap.lua");
-
-
-
-            media_installer_ = std::make_unique<MediaDataInstaller>(input_dir_, output_dir_);
-            CreateDir("images/icons");
-            CreateDir("images/other");
-            CreateDir("images/characters");
-            CreateDir("images/fonts");
-            CreateDir("images/reels");
-            CreateDir("images/window");
+            installation_state_ = MEDIA_IMAGES;
+            return CalcProgress();
+        case MEDIA_IMAGES:
+            write_output_line_("Extracting game images...", 2, true);
             media_installer_->InstallSprites();
-
-            //exit(0);
-
-            installation_state_ = SPAWN_POINTS_AND_SCALE_FACTORS_INIT;
-            // TODO: DEBUG:
-            //installation_state_ = CONVERT_FIELDS;
+            installation_state_ = FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT;
             return CalcProgress();
-        case SPAWN_POINTS_AND_SCALE_FACTORS_INIT:
-            write_output_line_("Collecting spawn points and scale factors...");
-            set_progress_label_("Collecting spawn points and scale factors...");
-            InitCollectSpawnAndScaleFactors();
+        case FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT:
+            write_output_line_("Collecting spawn points and scale factors...", 2, true);
+            substeps_ = field_installer_->CollectSpawnAndScaleFactorsInit(application_.ResMgr());
+            cur_substep_ = 0;
+            installation_state_ = FIELD_SPAWN_POINTS_AND_SCALE_FACTORS;
             return CalcProgress();
-        case SPAWN_POINTS_AND_SCALE_FACTORS:
-            CollectionFieldSpawnAndScaleFactors();
+        case FIELD_SPAWN_POINTS_AND_SCALE_FACTORS:
+            field_installer_->CollectSpawnAndScaleFactors(cur_substep_);
+            cur_substep_ ++;
+            if (cur_substep_ == substeps_){
+                installation_state_ = FIELD_CONVERT;
+                cur_substep_ = 0;
+            }
             return CalcProgress();
-        case CONVERT_FIELDS:
-            ConvertFieldsIteration();
+        case FIELD_CONVERT:
+            field_installer_->Convert(cur_substep_);
+            cur_substep_ ++;
+            if (cur_substep_ == substeps_){
+                installation_state_ = FIELD_WRITE_INIT;
+                cur_substep_ = 0;
+            }
             return CalcProgress();
-        case WRITE_MAPS_INIT:
-            write_output_line_("Writing fields...");
-            set_progress_label_("Writing fields...");
-            WriteMapsXmlBegin();
+        case FIELD_WRITE_INIT:
+            write_output_line_("Writing fields...", 2, true);
+            substeps_ = field_installer_->WriteInit();
+            cur_substep_ = 0;
+            installation_state_ = FIELD_WRITE;
             return CalcProgress();
-        case WRITE_MAPS:
-            WriteMapsXmlIteration();
+        case FIELD_WRITE:
+            field_installer_->Write(cur_substep_);
+            cur_substep_ ++;
+            if (cur_substep_ == substeps_){
+                installation_state_ = FIELD_WRITE_END;
+                cur_substep_ = 0;
+            }
             return CalcProgress();
-        case WRITE_MAPS_CLEAN:
-            EndWriteMapsXml();
+        case FIELD_WRITE_END:
+            field_installer_->WriteEnd();
+            installation_state_ = FIELD_CONVERT_MODELS_INIT;
             return CalcProgress();
-        case CONVERT_FIELD_MODELS_INIT:
-            write_output_line_("Converting field models...");
-            set_progress_label_("Converting field models...");
-            ConvertFieldModelsBegin();
+        case FIELD_CONVERT_MODELS_INIT:
+            write_output_line_("Converting field models...", 2, true);
+            field_model_names_ = field_installer_->ConvertModelsInit();
+            substeps_ = field_model_names_.size();
+            cur_substep_ = 0;
+            installation_state_ = FIELD_CONVERT_MODELS;
             return CalcProgress();
-        case CONVERT_FIELD_MODELS:
-            ConvertFieldModelsIteration();
+        case FIELD_CONVERT_MODELS:
+            field_installer_->ConvertModels(field_model_names_[cur_substep_]);
+            cur_substep_ ++;
+            if (cur_substep_ == substeps_){
+                installation_state_ = CLEAN;
+                cur_substep_ = 0;
+            }
+            return CalcProgress();
+        case CLEAN:
+            CleanInstall();
+            installation_state_ = DONE;
             return CalcProgress();
         case DONE:
+            write_output_line_("Installation complete", 2, true);
+            return 100;
         default:
             return 100;
     }
 }
 
-static std::string FieldModelDir(){return "models/fields/entities";}
-
-std::vector<std::string> materials_;
-
-void DataInstaller::TexToPng(const std::string name){
-    Ogre::DataStreamPtr stream(
-      Ogre::ResourceGroupManager::getSingleton().openResource(name, "FFVIITextures", true, NULL)
-    );
-    VGears::TexFile* tex = new VGears::TexFile();
-    tex->Read(stream);
-    Ogre::ImageCodec::ImageData* image_data = tex->GetImageData();
-    Ogre::Image* image = new Ogre::Image(
-      Ogre::PF_R8G8B8A8_UINT, image_data->width, image_data->height
+const int DataInstaller::CalcProgress(){
+    // TODO: Make more accurate with iterator_counter_ and
+    // progress_step_num_elements_.
+    float curr_step = installation_state_ / static_cast<float>(STATE_COUNT);
+    curr_step = curr_step * 100.0f;
+    int progress = static_cast<int>(curr_step);
+    if (progress >= 100) progress = 99;
+    return progress;
+}
+
+void DataInstaller::CreateDirectories(){
+    CreateDir("temp");
+    CreateDir("game");
+    CreateDir("images/icons");
+    CreateDir("images/other");
+    CreateDir("images/characters");
+    CreateDir("images/fonts");
+    CreateDir("images/reels");
+    CreateDir("images/window");
+    CreateDir("models/fields/entities");
+    application_.ResMgr()->addResourceLocation("data/temp/char/", "FileSystem", "FFVII", true, true);
+    application_.ResMgr()->addResourceLocation("data/models/", "FileSystem", "FFVII", true, true);
+    fields_lgp_ = std::make_unique<ScopedLgp>(
+      application_.getRoot(), input_dir_ + "data/field/flevel.lgp", "LGP", "FFVIIFields"
     );
-    for (int y = 0; y < image_data->height; y ++){
-        for (int x = 0; x < image_data->width; x ++){
-            // This is the position from which to read from the tex file colour data
-            // TODO: It doesn't work for non-square images. For example, the lower part of bxje is
-            // all wrong.
-            int i = (image_data->height * y) + x;
-            image->setColourAt(
-              Ogre::ColourValue(
-                tex->image_data_[i].comp.red / 255.0f,
-                tex->image_data_[i].comp.green / 255.0f,
-                tex->image_data_[i].comp.blue / 255.0f,
-                tex->image_data_[i].comp.alpha),
-              x, y, 0
-            );
-        }
-    }
-    Ogre::String base_name;
-    VGears::StringUtil::splitBase(name, base_name);
-    image->save(output_dir_ + "/" + FieldModelDir() + "/" + base_name + ".png");
-
 }
 
-void DataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh){
-
-    // TODO: Share function with pc model exporter
-    VGears::String base_mesh_name;
-    VGears::StringUtil::splitFull(mesh->getName(), base_mesh_name);
-    Ogre::MeshSerializer mesh_serializer;
-    Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
-    Ogre::SkeletonSerializer skeleton_serializer;
-    skeleton_serializer.exportSkeleton(
-      skeleton.getPointer(), outdir + base_mesh_name + ".skeleton"
-    );
-    mesh->setSkeletonName(FieldModelDir() + "/" + base_mesh_name + ".skeleton");
-    mesh_serializer.exportMesh(mesh.getPointer(), outdir + mesh->getName());
-    Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
-    Ogre::MaterialSerializer    mat_ser;
-    size_t i(0);
-    std::set<std::string> textures;
-    while (it.hasMoreElements()){
-        Ogre::SubMesh *sub_mesh(it.getNext());
-        Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(
-          sub_mesh->getMaterialName())
-        );
-        if (mat != nullptr){
-            for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){
-                Ogre::Technique* tech = mat->getTechnique(techs);
-                if (tech){
-                    for (size_t pass_num = 0; pass_num < tech->getNumPasses(); pass_num ++){
-                        Ogre::Pass* pass = tech->getPass(pass_num);
-                        if (pass){
-                            for (
-                              size_t texture_unit_num = 0;
-                              texture_unit_num < pass->getNumTextureUnitStates();
-                              texture_unit_num ++
-                            ){
-                                Ogre::TextureUnitState* unit
-                                  = pass->getTextureUnitState(texture_unit_num);
-                                if (unit && unit->getTextureName().empty() == false){
-                                    // Convert the texture from .tex to .png.
-                                    std::cout << "[TEX2PNG] Calling with " << unit->getTextureName() << "\n";
-                                    //TexToPng(unit->getTextureName());
-                                    TexFile tex(output_dir_ + "temp/char/" + unit->getTextureName());
-
-                                    // Ensure the output material script references png files
-                                    // rather than tex
-                                    // files.
-                                    Ogre::String base_name;
-                                    VGears::StringUtil::splitBase(
-                                      unit->getTextureName(), base_name
-                                    );
-                                    unit->setTextureName(
-                                      FieldModelDir() + "/" + base_mesh_name
-                                      + "_" + base_name + ".png"
-                                    );
-
-                                    std::cout << "SAVING PNG: " << output_dir_ << FieldModelDir() << "/" << base_name << ".png\n";
-                                    tex.SavePng(output_dir_ + FieldModelDir() + "/" + base_name + ".png", 0);
-                                    // Copy subtexture (xxxx.png) to
-                                    // model_xxxx.png
-                                    // TODO: obtain the "data" folder
-                                    // programatically.
-                                    boost::filesystem::copy_file(
-                                      "data/" + FieldModelDir() + "/" + base_name + ".png",
-                                      "data/" + FieldModelDir() + "/"
-                                        + base_mesh_name + "_" + base_name + ".png",
-                                      boost::filesystem::copy_option::overwrite_if_exists
-                                    );
-                                    textures.insert(unit->getTextureName());
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
-                //std::cout << "[MATERIAL] Writting material "
-                // << sub_mesh->getMaterialName() << std::endl;
-                mat_ser.queueForExport(mat);
-                materials_.push_back(sub_mesh->getMaterialName());
-            }
-
-        }
-        ++ i;
-    }
-    mat_ser.exportQueued(outdir + base_mesh_name + VGears::EXT_MATERIAL);
-    for (auto& texture_name : textures){
-        std::string tex_name = texture_name.c_str();
-        try{
-            Ogre::TexturePtr texture_ptr
-              = Ogre::TextureManager::getSingleton().load(tex_name, "FFVIITextures" /*"FFVII"*/);
-            Ogre::Image image;
-            texture_ptr->convertToImage(image);
-            Ogre::String base_name;
-            VGears::StringUtil::splitBase(texture_name, base_name);
-            image.save(outdir + base_mesh_name + "_" + base_name + ".png");
-        }
-        catch (std::exception const& ex){
-            std::cerr << "[ERROR] Exception: " << ex.what() << std::endl;
-        }
-    }
+void DataInstaller::CleanInstall(){
+    // TODO: Remove 'data/temp' directory.
+    // Either implement an option to keep it, o do it when the installer is mature enough.
 }
 
-/**
- * Normalizes a field name.
- *
- * It does nothing
- *
- * @param[in] name The field name.
- * @return The normalized field name.
- */
-static std::string FieldName(const std::string& name){return name;}
-
 void DataInstaller::CreateDir(const std::string& path){
     QString target = QString::fromStdString(output_dir_ + path);
     QDir dir(target);
     if (!dir.mkpath(".")) throw std::runtime_error("Failed to mkpath");
 }
-
-/**
- * Retrieves the path to the field map directory.
- *
- * @return The relative path to the field map directory.
- */
-static std::string FieldMapDir(){return "fields";}
-
-/**
- * Utilities for formatting field scripts.
- */
-class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
-
-    public:
-
-        /**
-         * Constructor.
-         *
-         * @param[in] field_name The field name.
-         * @param[in] field_id_to_name_lookup Field name lookup table to use.
-         * @param[in] spawn_points The list of spawn points.
-         */
-        BaseFF7FieldScriptFormatter(
-          const std::string& field_name, const std::vector<std::string>& field_id_to_name_lookup,
-          FieldSpawnPointsMap& spawn_points
-        ) :
-          field_name_(field_name), field_id_to_name_lookup_(field_id_to_name_lookup),
-          spawn_points_(spawn_points)
-        {}
-
-        /**
-         * Composes a spawn point name
-         *
-         * The name will have the format
-         * {FIELD_NAME}_{ENTITY}_{FUNCTION_NAME}_addr_{ADDRESS}
-         *
-         * @param[in] map_id Unused.
-         * @param[in] entity Entity name.
-         * @param[in] function_name Name of the function.
-         * @param[in] address @todo Understand and document.
-         * @return The composed spawn point name.
-         */
-        virtual std::string GetSpawnPointName(
-          unsigned int map_id, const std::string& entity,
-          const std::string& function_name, unsigned int address
-        ) override{
-            return
-              field_name_ + "_" + entity + "_" + function_name + "_addr_" + std::to_string(address);
-        }
-
-        /**
-         * Retrieves a map name from it's ID.
-         *
-         * @param[in] map_id The map ID.
-         * @return The map name.
-         */
-        virtual std::string GetMapName(unsigned int map_id) override{
-            return FieldName(field_id_to_name_lookup_[map_id]);
-        }
-
-        /**
-         * Retrieves a user friendly variable name.
-         *
-         * @param[in] bank Variable bank.
-         * @param[in] addr Variable address.
-         * @return Friendly name for the variable, or an empty string if it
-         * doesn't have one assigned.
-         */
-        virtual std::string GetFriendlyVarName(
-          unsigned int bank, unsigned int addr
-        ) override{
-            return VGears::NameLookup::FieldScriptVarName(bank, addr);
-        }
-
-        /**
-         * Retrieves a user friendly entity name.
-         *
-         * @param[in] entity Entity name.
-         * @return Friendly name for the entity, or the current name if it
-         * doesn't have one assigned.
-         */
-        virtual std::string GetFriendlyEntityName(const std::string& entity) override{
-            return VGears::NameLookup::FieldScriptEntityName(entity);
-        }
-
-        /**
-         * Retrieves a user friendly character name.
-         *
-         * @param[in] char_id Character ID.
-         * @return Friendly name for the entity, or an empty string if it
-         * doesn't have one assigned.
-         */
-        virtual std::string GetFriendlyCharName(int char_id) override{
-            return VGears::NameLookup::CharName(char_id);
-        }
-
-        /**
-         * Retrieves a user friendly function name.
-         *
-         * @param[in] entity The name of the entity the function belongs to.
-         * @param[in] function_name Function name.
-         * @return Friendly name for the function, or the current name if it
-         * doesn't have one assigned.
-         */
-        virtual std::string GetFriendlyFunctionName(
-          const std::string& entity, const std::string& function_name
-        ) override{
-            return VGears::NameLookup::FieldScriptFunctionName(
-              field_name_, entity, function_name
-            );
-        }
-
-        /**
-         * Retrieves the header comment for a function in an entity.
-         *
-         * @param[in] entity The name of the entity the function belongs to.
-         * @param[in] function_name Function name.
-         * @return Friendly comment for the function, or an empty string if it
-         * doesn't have one assigned.
-         */
-        virtual std::string GetFunctionComment(
-          const std::string& entity, const std::string& function_name
-        ) override{
-            return VGears::NameLookup::FieldScriptFunctionComment(
-              field_name_, entity, function_name
-            );
-        }
-
-    protected:
-
-        /**
-         * The field name.
-         */
-        std::string field_name_;
-
-        /**
-         * The list of spawn points.
-         */
-        FieldSpawnPointsMap& spawn_points_;
-
-        /**
-         * Look up table relatingfield IDs and names.
-         */
-        const std::vector<std::string>& field_id_to_name_lookup_;
-};
-
-/**
- * Handles the formatting of field scripts.
- */
-class FF7FieldScriptFormatter : public BaseFF7FieldScriptFormatter{
-
-    public:
-
-        /**
-         * Constructor.
-         *
-         * @param[in] field_name The field name.
-         * @param[in] models The list of field models.
-         * @param[in] field_id_to_name_lookup Field name lookup table to use.
-         * @param[in] spawn_points The list of spawn points.
-         */
-        FF7FieldScriptFormatter(
-          const std::string& field_name, const VGears::ModelListFilePtr& models,
-          const std::vector<std::string>& field_id_to_name_lookup,
-          FieldSpawnPointsMap& spawn_points
-        ) :
-          BaseFF7FieldScriptFormatter(field_name, field_id_to_name_lookup, spawn_points),
-          model_loader_(models)
-        {}
-
-        // Names an animation, can't return empty
-        /**
-         * Retreieves an animation name from iths ID.
-         *
-         * @param[in] char_id ID of the character the animation belogs to.
-         * @param[in] id Animation ID.
-         * @return The animation name. If there is no name assigned, or it
-         * can't be found, a string with the animation ID.
-         */
-        virtual std::string GetFriendlyAnimationName(int char_id, int id) override{
-            // Get the animation file name, then look up the friendly name of
-            // the "raw" animation.
-            if (static_cast<unsigned int>(char_id) >= model_loader_->GetModels().size()){
-                std::cerr << "FF7FieldScriptFormatter::AnimationName ERROR:"
-                  << "Char ID " << char_id << " out of bounds" << std::endl;
-                return std::to_string(id);
-            }
-            const auto& model_info = model_loader_->GetModels().at(char_id);
-            if (static_cast<unsigned int>(id) >= model_info.animations.size()){
-                std::cerr << "FF7FieldScriptFormatter::AnimationName ERROR:"
-                  << "In field " << field_name_ << " the model " << model_info.name
-                  << " animation with ID " << id << " is out of bounds ("
-                  << model_info.animations.size() << ")" << std::endl;
-                return std::to_string(id);
-            }
-            const auto raw_name = model_info.animations.at(id).name;
-            // Trim off ".yos" or whatever extension the model loader adds in.
-            Ogre::String base_name;
-            VGears::StringUtil::splitBase(raw_name, base_name);
-            VGears::StringUtil::toLowerCase(base_name);
-            return VGears::NameLookup::Animation(base_name);
-        }
-
-    private:
-
-        /**
-         * The field model loader.
-         */
-        const VGears::ModelListFilePtr& model_loader_;
-};
-
-/**
- * Creates a gateway script.
- *
- * @param[in] gateway_entity_name Name of the gateway entity.
- * @param[in] target_map_name Name of the target map.
- * @param[in] source_spawn_point_name The spawn point name.
- * @return Gateway LUA script.
- */
-static std::string CreateGateWayScript(
-  const std::string& gateway_entity_name, const std::string& target_map_name,
-  const std::string& source_spawn_point_name
-){
-    return
-      "\nEntityContainer[ \"" + gateway_entity_name + "\" ] = {\n"
-      "on_start = function(self)\n"
-      "    return 0\n"
-      "end,\n"
-      "on_enter_line = function(self, entity)\n"
-      "    return 0\n"
-      "end,\n\n"
-      "on_move_to_line = function(self, entity)\n"
-      "   return 0\n"
-      "end,\n\n"
-      "on_cross_line = function(self, entity)\n"
-      "    if entity == \"Cloud\" then\n"
-      "        if not FFVII.Data.DisableGateways then\n"
-      "            load_field_map_request(\""
-      + target_map_name + "\", \"" + source_spawn_point_name + "\")\n"
-      "        end\n"
-      "    end\n\n"
-      "    return 0\n"
-      "end,\n\n"
-      "on_leave_line = function(self, entity)\n"
-      "   return 0\n"
-      "end,\n"
-      "}\n\n";
-
-}
-
-/**
- * Gateways whith map ID are considered to be inactive.
- */
-const int INACTIVE_GATEWAY_ID = 32767;
-
-/**
- * Retrieves a field ID from a name.
- *
- * @param[in] name The name of the field.
- * @param[in] field_id_to_name_lookup Lookup table to use.
- * @return The field ID for the specified name.
- * @throws std::runtime_error If there is no ID for the field name.
- */
-static size_t FieldId(
-  const std::string& name,
-  const std::vector<std::string>& field_id_to_name_lookup
-){
-    for (size_t i = 0; i < field_id_to_name_lookup.size(); i ++)
-        if (field_id_to_name_lookup[i] == name) return i;
-    throw std::runtime_error("No Id found for field name");
-}
-
-/**
- * Retrieves a field scale factor.
- *
- * @param[in] scale_factor_map Lookup table of map scale factors.
- * @param[in] field_id The id (name) of the field.
- * @return The scale factor for the map.
- * @throws std::runtime_error If there is no ID for the field name.
- */
-static float FieldScaleFactor(
-  const FieldScaleFactorMap& scale_factor_map, size_t field_id
-){
-    auto it = scale_factor_map.find(field_id);
-    if (it == std::end(scale_factor_map))
-        throw std::runtime_error("Scale factor not found for field id");
-    return it->second;
-}
-
-void DataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
-    // Generate triggers script to insert into main
-    // decompiled FF7 field -> LUA script.
-    std::string gateway_script_data;
-    const VGears::TriggersFilePtr& triggers = field->GetTriggers();
-    const auto& gateways = triggers->GetGateways();
-    for (size_t i = 0; i < gateways.size(); i ++){
-        const VGears::TriggersFile::Gateway& gateway = gateways[i];
-        if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
-            const std::string gateway_entity_name = "Gateway" + std::to_string(i);
-            gateway_script_data += CreateGateWayScript(
-              gateway_entity_name,
-              FieldName(map_list_.at(gateway.destination_field_id)),
-              "Spawn_" + field->getName() + "_" + std::to_string(i)
-            );
-        }
-    }
-    const VGears::ModelListFilePtr& models = field->GetModelList();
-    FieldDecompiler::DecompiledScript decompiled;
-    FF7FieldScriptFormatter formatter(field->getName(), models, map_list_, spawn_points_);
-    try{
-        // Get the raw script bytes.
-        const std::vector<u8> raw_field_data = field->GetRawScript();
-        // Decompile to LUA.
-        decompiled = FieldDecompiler::Decompile(
-          field->getName(), raw_field_data, formatter, gateway_script_data,
-          "EntityContainer = {}\n\n"
-        );
-        std::ofstream script_file(
-          output_dir_ + "/" + FieldMapDir() + "/" + field->getName() + "/script.lua"
-        );
-        if (script_file.is_open()){
-            script_file << decompiled.luaScript;
-            field_text_writer_.Begin(output_dir_ + "/" + FieldMapDir() + "/" + field->getName() + "/text.xml");
-            try{field_text_writer_.Write(raw_field_data, field->getName());}
-            catch (const std::out_of_range& ex){
-                write_output_line_(
-                  "[ERROR] Failed to read texts from field " + field->getName() + ": " + ex.what()
-                );
-                std::cerr << "[ERROR] Failed to read texts from field "
-                  << field->getName() << ": " << ex.what() << std::endl;
-            }
-            field_text_writer_.End();
-        }
-        else{
-            write_output_line_(
-              "[ERROR] Failed to open script file from field " + field->getName() + " for writing."
-            );
-            std::cerr << "[ERROR] Failed to open script file from field "
-              << field->getName() << "for writing." << std::endl;
-        }
-    }
-    catch (const ::DecompilerException& ex){
-        write_output_line_(
-          "[ERROR] Internal decompiler error in field " + field->getName() + ": " + ex.what()
-        );
-        std::cerr << "[ERROR] Internal decompiler error in field "
-          << field->getName() << ": " << ex.what() << std::endl;
-    }
-    // Write out the map file which links all the other files together for a given field.
-    {
-        TiXmlDocument doc;
-        std::unique_ptr<TiXmlElement> element(new TiXmlElement("map"));
-        // Script.
-        std::unique_ptr<TiXmlElement> xml_script_element(new TiXmlElement("script"));
-        xml_script_element->SetAttribute(
-          "file_name", FieldMapDir() + "/" + field->getName() + "/script.lua"
-        );
-        element->LinkEndChild(xml_script_element.release());
-        // Background.
-        std::unique_ptr<TiXmlElement> xml_background_2d(new TiXmlElement("background2d"));
-        xml_background_2d->SetAttribute(
-          "file_name", FieldMapDir() + "/" + field->getName() + "/bg.xml"
-        );
-        element->LinkEndChild(xml_background_2d.release());
-        // Texts.
-        std::unique_ptr<TiXmlElement> xml_texts(new TiXmlElement("texts"));
-        xml_texts->SetAttribute(
-          "file_name", FieldMapDir() + "/" + field->getName() + "/text.xml"
-        );
-        element->LinkEndChild(xml_texts.release());
-        // Walkmesh.
-        std::unique_ptr<TiXmlElement> xml_walkmesh(new TiXmlElement("walkmesh"));
-        xml_walkmesh->SetAttribute(
-          "file_name", FieldMapDir() + "/" + field->getName() + "/wm.xml"
-        );
-        element->LinkEndChild(xml_walkmesh.release());
-        // Forward direction (angle player moves when "up" is pressed)
-        std::unique_ptr<TiXmlElement> xml_forward_direction(new TiXmlElement("movement_rotation"));
-        xml_forward_direction->SetAttribute(
-          "degree", std::to_string(triggers->MovementRotation())
-        );
-        element->LinkEndChild(xml_forward_direction.release());
-        // Get this fields Id.
-        const size_t this_field_id = FieldId(field->getName(), map_list_);
-        // Use the ID to find the pre-computed list of gateways in all other fields that link to
-        // this field.
-        auto spawn_iterator = spawn_points_.find(this_field_id);
-        Ogre::Vector3 first_entity_point = Ogre::Vector3::ZERO;
-
-        // If none found it probably just means no other fields have doors to this one.
-        if (spawn_iterator != std::end(spawn_points_)){
-            const std::vector<SpawnPointDb::Record>& spawn_point_records
-              = spawn_iterator->second.gateways_to_this_field;
-            for (size_t i = 0; i < spawn_point_records.size(); i ++){
-                const VGears::TriggersFile::Gateway& gateway = spawn_point_records[i].gateway;
-                // Entity_point:
-                std::unique_ptr<TiXmlElement> xml_entity_point(new TiXmlElement("entity_point"));
-                if (spawn_point_records[i].from_script){
-                    // Spawn points from map jumps have a another algorithm.
-                    xml_entity_point->SetAttribute(
-                      "name",
-                      map_list_.at(
-                        spawn_point_records[i].field_id) + "_" + spawn_point_records[i].entity_name
-                        + "_" + spawn_point_records[i].script_function_name + "_addr_"
-                        + std::to_string(spawn_point_records[i].gateway_index_or_map_jump_address)
-                      );
-                }
-                else{
-                    // Must also include the gateway index for the case where 2
-                    // fields have more than one door linking to each other.
-                    xml_entity_point->SetAttribute(
-                      "name",
-                      "Spawn_" + map_list_.at(spawn_point_records[i].field_id) + "_"
-                        + std::to_string(spawn_point_records[i].gateway_index_or_map_jump_address)
-                    );
-                }
-                const float downscaler_next = 128.0f * FieldScaleFactor(
-                  scale_factors_, gateway.destination_field_id
-                );
-
-                // Position Z is actually the target walkmesh triangle ID, so this is tiny bit more
-                // complex. Now "get the Z value of the triangle with that ID". Note that ID
-                // actually just means index.
-                unsigned int triangle_index = static_cast<unsigned int>(gateway.destination.z);
-                if (
-                  triangle_index >= field->GetWalkmesh()->GetTriangles().size()
-                ){
-                    write_output_line_(
-                      "[WARNING] In field " + field->getName() + ": Map jump triangle ("
-                      + std::to_string(triangle_index) + ") out of bounds ("
-                      + std::to_string(field->GetWalkmesh()->GetTriangles().size()) + ")"
-                    );
-                    std::cerr << "[WARNING] In field " << field->getName()
-                      << ": Map jump triangle (" << triangle_index << ") out of bounds ("
-                      << field->GetWalkmesh()->GetTriangles().size() << ")" << std::endl;
-                    triangle_index = 0;
-                }
-                const float z_of_triangle_with_id
-                  = field->GetWalkmesh()->GetTriangles().at(triangle_index).a.z;
-                const Ogre::Vector3 position(
-                  gateway.destination.x / downscaler_next, gateway.destination.y / downscaler_next,
-                  z_of_triangle_with_id
-                );
-                if (position != Ogre::Vector3::ZERO && first_entity_point == Ogre::Vector3::ZERO)
-                    first_entity_point = position;
-                xml_entity_point->SetAttribute(
-                  "position", Ogre::StringConverter::toString(position)
-                );
-                const float rotation = (360.0f * static_cast<float>(gateway.dir)) / 255.0f;
-                xml_entity_point->SetAttribute("rotation", std::to_string(rotation));
-                element->LinkEndChild(xml_entity_point.release());
-            }
-        }
-
-        // Get lines. Add them to a list, so they aren't processed later as regular entities.
-        std::vector<std::string> line_entities;
-        for (FieldDecompiler::Line line : decompiled.lines){
-
-            std::unique_ptr<TiXmlElement> xml_entity_trigger(new TiXmlElement("entity_trigger"));
-            line_entities.push_back(line.name);
-            xml_entity_trigger->SetAttribute("name", line.name);
-            xml_entity_trigger->SetAttribute(
-              "point1",
-              std::to_string(line.point_a[0] * DataInstaller::LINE_SCALE_FACTOR)
-                + " " + std::to_string(line.point_a[1] * DataInstaller::LINE_SCALE_FACTOR)
-                + " " + std::to_string(line.point_a[2] * DataInstaller::LINE_SCALE_FACTOR)
-            );
-            xml_entity_trigger->SetAttribute(
-              "point2",
-              std::to_string(line.point_b[0] * DataInstaller::LINE_SCALE_FACTOR)
-                + " " + std::to_string(line.point_b[1] * DataInstaller::LINE_SCALE_FACTOR)
-                + " " + std::to_string(line.point_b[2] * DataInstaller::LINE_SCALE_FACTOR)
-            );
-            xml_entity_trigger->SetAttribute("enabled", "true");
-            element->LinkEndChild(xml_entity_trigger.release());
-        }
-
-        // Get entities.
-        for (FieldDecompiler::FieldEntity entity : decompiled.entities){
-            // If the entity has been added as a line, skip.
-            if (line_entities.size() > 0){
-                if (
-                  *std::find(line_entities.begin(), line_entities.end(), entity.name) == entity.name
-                ){
-                    continue;
-                }
-            }
-            const int char_id = entity.char_id;
-            if (char_id != -1){
-                const VGears::ModelListFile::ModelDescription& desc = models->GetModels().at(char_id);
-                auto& animations = used_models_and_anims_.ModelAnimations(desc.hrc_name);
-                for (const auto& anim : desc.animations)
-                    animations.insert(used_models_and_anims_.NormalizeAnimationName(anim.name));
-                std::unique_ptr<TiXmlElement> xml_entity_script(new TiXmlElement("entity_model"));
-                xml_entity_script->SetAttribute("name", entity.name);
-                // TODO: Add to list of HRC's to convert, obtain name of converted .mesh file.
-                auto lower_case_hrc_name = desc.hrc_name;
-                VGears::StringUtil::toLowerCase(lower_case_hrc_name);
-                xml_entity_script->SetAttribute(
-                  "file_name",
-                  FieldModelDir() + "/" + used_models_and_anims_.ModelMetaDataName(lower_case_hrc_name)
-                );
-                xml_entity_script->SetAttribute("index", entity.index);
-                if (desc.type == VGears::ModelListFile::PLAYER){
-                    // For player models the position is set manually in the xml because if a map
-                    // is loaded manually this is where the player will end up. Hence we set the
-                    // position to the first entity_point that we have.
-                    xml_entity_script->SetAttribute(
-                      "position", Ogre::StringConverter::toString(first_entity_point)
-                    );
-                }
-                else{
-                    xml_entity_script->SetAttribute(
-                      "position", Ogre::StringConverter::toString(Ogre::Vector3::ZERO)
-                    );
-                }
-                xml_entity_script->SetAttribute("direction", "0");
-                // TODO: This isn't quite right, the models animation
-                // translation seems to be inverted?
-                xml_entity_script->SetAttribute("scale", "0.03125 0.03125 0.03125");
-                xml_entity_script->SetAttribute(
-                  "root_orientation", "0.7071067811865476 0.7071067811865476 0 0"
-                );
-                element->LinkEndChild(xml_entity_script.release());
-            }
-            else{
-                std::unique_ptr<TiXmlElement> xml_entity_script(new TiXmlElement("entity_script"));
-                xml_entity_script->SetAttribute("name", entity.name);//it.first);
-                element->LinkEndChild(xml_entity_script.release());
-            }
-        }
-        const float downscaler_this = 128.0f * FieldScaleFactor(scale_factors_, this_field_id);
-        for (size_t i = 0; i < gateways.size(); i ++){
-            const VGears::TriggersFile::Gateway& gateway = gateways[i];
-            // If non-inactive gateway:
-            if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
-                std::unique_ptr<TiXmlElement> xml_entity_trigger(
-                  new TiXmlElement("entity_trigger")
-                );
-                xml_entity_trigger->SetAttribute("name", "Gateway" + std::to_string(i));
-                xml_entity_trigger->SetAttribute(
-                  "point1",
-                  Ogre::StringConverter::toString(
-                    Ogre::Vector3(
-                      gateway.exit_line[0].x,
-                      gateway.exit_line[0].y,
-                      gateway.exit_line[0].z) / downscaler_this
-                  )
-                );
-                xml_entity_trigger->SetAttribute(
-                  "point2",
-                  Ogre::StringConverter::toString(
-                    Ogre::Vector3(
-                      gateway.exit_line[1].x,
-                      gateway.exit_line[1].y,
-                      gateway.exit_line[1].z) / downscaler_this
-                    )
-                  );
-                // Enabled is hard coded to true.
-                xml_entity_trigger->SetAttribute("enabled", "true");
-                element->LinkEndChild(xml_entity_trigger.release());
-            }
-        }
-        doc.LinkEndChild(element.release());
-        doc.SaveFile(output_dir_ + "/" + FieldMapDir() + "/" + field->getName() + "/map.xml");
-        const VGears::PaletteFilePtr& pal = field->GetPalette();
-        const VGears::BackgroundFilePtr& bg = field->GetBackground();
-        std::unique_ptr<Ogre::Image> bg_image(bg->CreateImage(pal));
-        bg_image->encode("png");
-        bg_image->save(output_dir_ + "/" + FieldMapDir() + "/" + field->getName() + "/tiles.png");
-        {
-            TiXmlDocument bg_doc;
-            std::unique_ptr<TiXmlElement> bg_element(new TiXmlElement("background2d"));
-            // Magic constants.
-            const int BG_SCALE_UP_FACTOR = 3;
-            const int BG_PSX_SCREEN_WIDTH = 320;
-            const int BG_PSX_SCREEN_HEIGHT = 240;
-            // Get texture atlas size.
-            const int width = bg_image->getWidth();
-            const int height = bg_image->getHeight();
-            const VGears::CameraMatrixFilePtr& camera_matrix = field->GetCameraMatrix();
-            const Ogre::Vector3 position
-              = camera_matrix->GetPosition() / FieldScaleFactor(scale_factors_, this_field_id);
-            const Ogre::Quaternion orientation = camera_matrix->GetOrientation();
-            const Ogre::Degree fov
-              = camera_matrix->GetFov(static_cast<float>(BG_PSX_SCREEN_HEIGHT));
-            const int min_x = triggers->GetCameraRange().left * BG_SCALE_UP_FACTOR;
-            const int min_y = triggers->GetCameraRange().top * BG_SCALE_UP_FACTOR;
-            const int max_x = triggers->GetCameraRange().right * BG_SCALE_UP_FACTOR;
-            const int max_y = triggers->GetCameraRange().bottom * BG_SCALE_UP_FACTOR;
-            bg_element->SetAttribute("image", FieldMapDir() + "/" + field->getName() + "/tiles.png");
-            bg_element->SetAttribute("position", Ogre::StringConverter::toString(position));
-            bg_element->SetAttribute("orientation", Ogre::StringConverter::toString(orientation));
-            bg_element->SetAttribute("fov", Ogre::StringConverter::toString(fov));
-            bg_element->SetAttribute(
-              "range",
-              std::to_string(min_x) + " " + std::to_string(min_y) + " "
-              + std::to_string(max_x) + " " + std::to_string(max_y)
-            );
-            bg_element->SetAttribute(
-              "clip",
-              std::to_string(BG_PSX_SCREEN_WIDTH * BG_SCALE_UP_FACTOR) + " "
-              + std::to_string(BG_PSX_SCREEN_HEIGHT * BG_SCALE_UP_FACTOR)
-            );
-            // Write out the *_BG.XML data.
-            auto& layers = bg->GetLayers();
-            for (const auto& layer : layers){
-                // TODO: Should only the tiles to construct enabled layers be outputted?
-                //  if (layer.enabled){
-
-                for (const VGears::BackgroundFile::SpriteData& sprite : layer.sprites){
-                    std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("tile"));
-                    xml_element->SetAttribute(
-                      "destination",
-                      Ogre::StringConverter::toString(sprite.dst.x * BG_SCALE_UP_FACTOR) + " "
-                      + Ogre::StringConverter::toString(sprite.dst.y * BG_SCALE_UP_FACTOR)
-                    );
-                    xml_element->SetAttribute(
-                      "width", Ogre::StringConverter::toString(sprite.width * BG_SCALE_UP_FACTOR)
-                    );
-                    xml_element->SetAttribute(
-                      "height", Ogre::StringConverter::toString(sprite.height * BG_SCALE_UP_FACTOR)
-                    );
-                    // Each tile is added to a big texture atlas with hard coded size of 1024x1024,
-                    // convert UV's to the 0.0f to 1.0f range.
-                    const float u0 = static_cast<float>(sprite.src.x) / width;
-                    const float v0 = static_cast<float>(sprite.src.y) / height;
-                    const float u1 = static_cast<float>(sprite.src.x + sprite.width) / width;
-                    const float v1 = static_cast<float>(sprite.src.y + sprite.height) / height;
-                    xml_element->SetAttribute(
-                      "uv",
-                      Ogre::StringConverter::toString(u0) + " "
-                      + Ogre::StringConverter::toString(v0) + " "
-                      + Ogre::StringConverter::toString(u1) + " "
-                      + Ogre::StringConverter::toString(v1)
-                    );
-                    // TODO: It works (on FFVII PC), but why this number?
-                    xml_element->SetAttribute(
-                      "depth",
-                      Ogre::StringConverter::toString(static_cast<float>(sprite.depth) * (0.03125f))
-                    );
-                    // TODO: Copied from DatFile::AddTile.
-                    // Add to common method.
-                    Ogre::String blending_str = "";
-                    if (sprite.blending == 0) blending_str = "alpha";
-                    // 3 is source + 0.25 * destination:
-                    else if (sprite.blending == 1 || sprite.blending == 3) blending_str = "add";
-                    else if (sprite.blending == 2) blending_str = "subtract";
-                    // TODO: Should probably throw to fail conversion:
-                    else blending_str = "unknown";
-                    xml_element->SetAttribute("blending", blending_str);
-                    bg_element->LinkEndChild(xml_element.release());
-                }
-                //}
-            }
-            bg_doc.LinkEndChild(bg_element.release());
-            bg_doc.SaveFile(output_dir_ + "/" + FieldMapDir() + "/" + field->getName() + "/bg.xml");
-        }
-    }
-    {
-        // Save out the walk mesh as XML.
-        const VGears::WalkmeshFilePtr& walkmesh = field->GetWalkmesh();
-        TiXmlDocument doc;
-        std::unique_ptr<TiXmlElement> wmesh_element(new TiXmlElement("walkmesh"));
-        for (VGears::WalkmeshFile::Triangle& tri : walkmesh->GetTriangles()){
-            std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("triangle"));
-            xml_element->SetAttribute("a", Ogre::StringConverter::toString(tri.a));
-            xml_element->SetAttribute("b", Ogre::StringConverter::toString(tri.b));
-            xml_element->SetAttribute("c", Ogre::StringConverter::toString(tri.c));
-            xml_element->SetAttribute("a_b", std::to_string(tri.access_side[0]));
-            xml_element->SetAttribute("b_c", std::to_string(tri.access_side[1]));
-            xml_element->SetAttribute("c_a", std::to_string(tri.access_side[2]));
-            wmesh_element->LinkEndChild(xml_element.release());
-        }
-        doc.LinkEndChild(wmesh_element.release());
-        doc.SaveFile(output_dir_ + "/" + FieldMapDir() + "/" + field->getName() + "/wm.xml");
-    }
-    converted_map_list_.insert(field->getName());
-}
-
-/**
- * Handles the script gateway colection.
- */
-class FF7FieldScriptGatewayCollector : public BaseFF7FieldScriptFormatter{
-
-    // TODO: Move declaration to a .h file.
-
-    public:
-
-        /**
-         * Cosntructor.
-         */
-        FF7FieldScriptGatewayCollector(
-          const std::string& field_name, const std::vector<std::string>& field_id_to_name_lookup,
-          FieldSpawnPointsMap& spawn_points, size_t this_field_id
-        ) :
-          BaseFF7FieldScriptFormatter(field_name,field_id_to_name_lookup, spawn_points),
-          field_id_(this_field_id)
-        {}
-
-        /**
-         * Adds an spawn point.
-         *
-         * If the spawn is new, a new record will be added to the spawn point
-         * database. If it was already there, update the record to add the
-         * origin.
-         *
-         * @param[in] map_id The target map ID.
-         * @param[in] entity The entity that acts as the spawn point.
-         * @param[in] function_name The spawn function name.
-         * @param[in] address @todo Understand and document.
-         * @param[in] x X coordinate of the field target at which to spawn.
-         * @param[in] y Y coordinate of the field target at which to spawn.
-         * @param[in] z Z coordinate of the field target at which to spawn.
-         * @param[in] angle Orientation at which to spawn.
-         */
-        virtual void AddSpawnPoint(
-          unsigned int map_id, const std::string& entity, const std::string& function_name,
-          unsigned int address, int x, int y, int z, int angle
-        ) override {
-            auto it = spawn_points_.find(map_id);
-            VGears::TriggersFile::Gateway gw = {};
-            gw.destination_field_id = map_id;
-            gw.destination.x = x;
-            gw.destination.y = y;
-            gw.destination.z = z;
-            gw.dir = angle;
-            if (it != std::end(spawn_points_)){
-                // Add to the list of gateways that link to destination_field_id.
-                SpawnPointDb::Record rec;
-                rec.from_script = true;
-                rec.field_id = field_id_;
-                rec.gateway = gw;
-                rec.entity_name = entity;
-                rec.script_function_name = function_name;
-                rec.gateway_index_or_map_jump_address = address;
-                it->second.gateways_to_this_field.push_back(rec);
-            }
-            else{
-                // Create a new record for destination_field_id
-                SpawnPointDb db;
-                db.target_field_id = map_id;
-                SpawnPointDb::Record rec;
-                rec.from_script = true;
-                rec.field_id = field_id_;
-                rec.gateway = gw;
-                rec.entity_name = entity;
-                rec.script_function_name = function_name;
-                rec.gateway_index_or_map_jump_address = address;
-                db.gateways_to_this_field.push_back(rec);
-                spawn_points_.insert(std::make_pair(db.target_field_id, db));
-            }
-        }
-
-    private:
-
-        /**
-         * The field map ID.
-         */
-        size_t field_id_;
-
-};
-
-/**
- * Collects the spawn point from a field.
- *
- * If a spawn is new, a new record will be added to the spawn point database.
- * If it was already there, update the record to add the origin.
- *
- * @param[in] field The field to collect from.
- * @param[in] field_id_to_name_lookup Lookup table to relate field IDs and
- * names.
- * @param[out] spawn_points Spawn points will be added sequentially to the end
- * of this map.
- */
-static void CollectSpawnPoints(
-  VGears::FLevelFilePtr& field, const std::vector<std::string>& field_id_to_name_lookup,
-  FieldSpawnPointsMap& spawn_points
-){
-    //std::cout << "Decompile FIELD " << field->getName() << "\n";
-    const size_t this_field_id = FieldId(field->getName(), field_id_to_name_lookup);
-    // Decompile the script just so the MAPJUMPs can be collected. This is needed to construct the
-    // full list of entries to each field.
-    try{
-        // Get the raw script bytes.
-        FieldDecompiler::DecompiledScript decompiled;
-        const std::vector<u8> raw_field_data = field->GetRawScript();
-        // Decompile to LUA.
-        FF7FieldScriptGatewayCollector formatter(
-          field->getName(), field_id_to_name_lookup, spawn_points, this_field_id
-        );
-        decompiled = FieldDecompiler::Decompile(
-          field->getName(), raw_field_data, formatter, "", "EntityContainer = {}\n\n"
-        );
-    }
-    catch (const ::DecompilerException& ex){
-        std::cerr << "CollectSpawnPoints: DecompilerException: " << ex.what() << std::endl;
-    }
-    const VGears::TriggersFilePtr& triggers = field->GetTriggers();
-    const auto& gateways = triggers->GetGateways();
-    for (size_t i = 0; i < gateways.size(); i ++){
-        const VGears::TriggersFile::Gateway& gateway = gateways[i];
-        if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
-            auto it = spawn_points.find(gateway.destination_field_id);
-            if (it != std::end(spawn_points)){
-                // Add to the list of gateways that link to
-                // destination_field_id.
-                SpawnPointDb::Record rec;
-                rec.field_id = this_field_id;
-                rec.gateway = gateway;
-                rec.gateway_index_or_map_jump_address = i;
-                it->second.gateways_to_this_field.push_back(rec);
-            }
-            else{
-                // Create a new record for destination_field_id.
-                SpawnPointDb db;
-                db.target_field_id = gateway.destination_field_id;
-                SpawnPointDb::Record rec;
-                rec.field_id = this_field_id;
-                rec.gateway = gateway;
-                rec.gateway_index_or_map_jump_address = i;
-                db.gateways_to_this_field.push_back(rec);
-                spawn_points.insert(std::make_pair(db.target_field_id, db));
-            }
-        }
-    }
-}
-
-/**
- * Checks if a file is a field file.
- *
- * To verify it, it just checks that the extension is not '.tex', '.tut' or
- * '.siz' and that the name is not 'maplist'.
- *
- * @param[in] resource_name The name of the file to test.
- * @return False if the file is surely not a field file, true otherwise.
- */
-static bool IsAFieldFile(const Ogre::String& resource_name){
-    return (
-      !VGears::StringUtil::endsWith(resource_name, ".tex")
-      && !VGears::StringUtil::endsWith(resource_name, ".tut")
-      && !VGears::StringUtil::endsWith(resource_name, ".siz")
-      && resource_name != "maplist"
-    );
-}
-
-
-/**
- * Checks if a map cause fatal crash bugs.
- *
- * It uses a hardcoded list of maps that are known to cause errors. This can be
- * removed when whatever is causing the crash(s) is fixed.
- *
- * NOTE: Even so, conversion of all models will fail with a bone index out of
- * bounds.
- *
- * @param[in] resource_name The name of the map file to test.
- * @return True if the map will crash, false otherwise.
- */
-static bool WillCrash(const Ogre::String& resource_name){
-    if (
-      resource_name == "bugin1a" // crashes in back ground image generation
-      || resource_name == "frcyo" // Hangs in decompliation
-      || resource_name == "fr_e" // Background image crash
-      || resource_name == "las4_2" // Background image crash
-      || resource_name == "las4_3" // Background image crash
-      || resource_name == "lastmap" // Background image crash
-      || resource_name == "md_e1" // Background image crash
-      || resource_name == "trnad_3" // Background image crash
-      || resource_name == "bonevil2" // stof
-      || resource_name == "coloin1" // boost::bad_format_string: format-string is ill-formed
-      || resource_name == "del3" // Segmentation fault.
-      || resource_name == "elmin4_2" // boost::bad_format_string: format-string is ill-formed
-    ){return true;}
-    return false;
-}
-
-/**
- * Checks if a map is a test field.
- *
- * It uses a hardcoded list of maps that are known to be test maps. Unused maps
- * are considered to be test maps.
- *
- * @param[in] resource_name The name of the map file to test.
- * @return True if the map is a test map, false otherwise.
- */
-static bool IsTestField(const Ogre::String& resource_name){
-    if (
-      resource_name == "startmap"
-      || resource_name == "md1stin"
-      || resource_name == "md1_1"
-      || resource_name == "md1_2"
-      || resource_name == "md8_1"
-      || resource_name == "nmkin_1"
-      || resource_name == "nmkin_2"
-      || resource_name == "nmkin_3"
-      || resource_name == "nmkin_4"
-      || resource_name == "nmkin_5"
-      || resource_name == "nrthmk"
-      || resource_name == "elevtr1"
-      || resource_name == "tin_1"
-      || resource_name == "tin_2"
-      || resource_name == "rootmap"
-      || resource_name == "md8_4"
-    ){return true;}
-    return false;
-}
-
-/**
- * Collects the scale factor from a map.
- *
- * @param[in] field The field to collect from.
- * @param[out] scale_factors The scale factor for the map will be set here, in
- * the appropiate map entry.
- * @param[in] field_id_to_name_lookup Lookup table to relate field IDs and
- * names.
- */
-static void CollectFieldScaleFactors(
-  VGears::FLevelFilePtr& field, FieldScaleFactorMap& scale_factors,
-  const std::vector<std::string>& field_id_to_name_lookup
-){
-    scale_factors[FieldId(field->getName(), field_id_to_name_lookup)]
-      = FieldDecompiler::ScaleFactor(field->GetRawScript());
-}
-
-void DataInstaller::InitCollectSpawnAndScaleFactors(){
-    progress_step_num_elements_ = 1;
-    CreateDir(FieldMapDir());
-    CreateDir(FieldModelDir());
-    // List what's in the LGP archive.
-    flevel_file_list_ = application_.ResMgr()->listResourceNames("FFVIIFields", "*");
-    // Load the map list field.
-    VGears::MapListFilePtr map_list
-      = VGears::MapListFileManager::GetSingleton().load(
-        "maplist", "FFVIIFields"
-      ).staticCast<VGears::MapListFile>();
-    map_list_ = map_list->GetMapList();
-    iterator_counter_ = 0;
-    conversion_step_ = 0;
-    installation_state_ = SPAWN_POINTS_AND_SCALE_FACTORS;
-}
-
-void DataInstaller::CollectionFieldSpawnAndScaleFactors(){
-    // On the first pass collate required field information.
-    progress_step_num_elements_ = flevel_file_list_->size();
-    if (iterator_counter_ < flevel_file_list_->size()){
-        auto resource_name = (*flevel_file_list_)[iterator_counter_];
-        // Exclude things that are not fields.
-        //if (IsAFieldFile(resource_name) && !WillCrash(resource_name)){
-        // TODO: DEBUG: Only testing fields.
-        if (IsAFieldFile(resource_name) && IsTestField(resource_name) && !WillCrash(resource_name)){
-            conversion_step_ ++;
-            if (conversion_step_ == 1){
-                field_ = VGears::LZSFLevelFileManager::GetSingleton().load(
-                  resource_name, "FFVIIFields"
-                ).staticCast<VGears::FLevelFile>();
-                //write_output_line_("Load field " + resource_name);
-                return;
-            }
-            if (conversion_step_ == 2){
-                //write_output_line_("Read spawn points from scripts");
-                CollectSpawnPoints(field_, map_list_, spawn_points_);
-                return;
-            }
-            if (conversion_step_ == 3){
-                //write_output_line_("Read scale factor");
-                CollectFieldScaleFactors(field_, scale_factors_, map_list_);
-                conversion_step_ = 0;
-                iterator_counter_ ++;
-                return;
-            }
-        }
-        else iterator_counter_ ++;
-    }
-    else{
-        field_.reset();
-        iterator_counter_ = 0;
-        installation_state_ = CONVERT_FIELDS;
-        //field_text_writer_.Begin(output_dir_ + "/texts/english/dialogs_mission1.xml");
-    }
-}
-
-void DataInstaller::ConvertFieldsIteration(){
-    // Do the full conversion with the collated data.
-    progress_step_num_elements_ = flevel_file_list_->size();
-    if (iterator_counter_ < flevel_file_list_->size()){
-        auto resource_name = (*flevel_file_list_)[iterator_counter_];
-        // Exclude things that are not fields.
-        if (IsAFieldFile(resource_name)){
-            //if (/*IsTestField(resource_name) &&*/ !WillCrash(resource_name)){
-            // TODO: DEBUG: Only test fields
-            if (IsTestField(resource_name) && !WillCrash(resource_name)){
-                //write_output_line_("Converting field " + resource_name);
-                std::cout << " - Converting field: " << resource_name << std::endl;
-                CreateDir(FieldMapDir() + "/" + resource_name);
-                VGears::FLevelFilePtr field = VGears::LZSFLevelFileManager::GetSingleton().load(
-                    resource_name, "FFVIIFields"
-                  ).staticCast<VGears::FLevelFile>();
-                PcFieldToVGearsField(field);
-            }
-            else{
-                /*write_output_line_(
-                  "[ERROR] Skip field " + resource_name + " due to crash or hang issue."
-                );*/
-                std::cerr << "[ERROR] Skip field: " << resource_name
-                  << " due to crash or hang issue." << std::endl;
-            }
-        }
-        iterator_counter_ ++;
-    }
-    else{
-        iterator_counter_ = 0;
-        installation_state_ = WRITE_MAPS_INIT;
-        field_text_writer_.End();
-    }
-}
-
-void DataInstaller::WriteMapsXmlBegin(){
-    // Write out maps.xml.
-    progress_step_num_elements_ = 1;
-    doc_ = std::make_unique<TiXmlDocument>();
-    element_ = std::make_unique<TiXmlElement>("maps");
-    iterator_counter_ = 0;
-    installation_state_ = WRITE_MAPS;
-    iterator_counter_ = 0;
-    converted_map_list_iterator_ = converted_map_list_.begin();
-}
-
-void DataInstaller::WriteMapsXmlIteration(){
-    // TODO: Probably need to inject "empty" and "test" fields.
-    progress_step_num_elements_ = converted_map_list_.size();
-    if (converted_map_list_iterator_ != converted_map_list_.end()){
-        auto map = *converted_map_list_iterator_;
-        write_output_line_("Writing field " + FieldName(map));
-        std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("map"));
-        xml_element->SetAttribute("name", FieldName(map));
-        xml_element->SetAttribute("file_name", FieldMapDir() + "/" + map + "/map.xml");
-        element_->LinkEndChild(xml_element.release());
-        iterator_counter_ ++;
-        ++ converted_map_list_iterator_;
-    }
-    else installation_state_ = WRITE_MAPS_CLEAN;
-}
-
-void DataInstaller::EndWriteMapsXml(){
-    progress_step_num_elements_ = 1;
-    doc_->LinkEndChild(element_.release());
-    doc_->SaveFile(output_dir_ + "/maps.xml");
-    installation_state_ = CONVERT_FIELD_MODELS_INIT;
-}
-
-void DataInstaller::ConvertFieldModelsBegin(){
-    // TODO: Convert models and animations in model_animation_db.
-    /*progress_step_num_elements_ = 1;
-    field_models_lgp_ = std::make_unique<ScopedLgp>(
-      application_.getRoot(), input_dir_ + "data/field/char.lgp", "LGP", "FFVII"
-    );
-    field_model_file_list_ = application_.ResMgr()->listResourceNames("FFVII", "*");
-    // Save file
-    //Ogre::StringVectorPtr file_list = field_models_lgp_->list(true, true);
-    VGears::LGPArchive::FileList files = field_models_lgp_.GetFiles();
-    CreateDir("temp/char");
-    for (int i = 0; i < file_list->size(); i ++){
-        VGears::LGPArchive::FileEntry f = files.at(i);
-        // Save the TEX File
-        std::fstream out;
-        out.open(output_dir_ + "temp/char/" + f.file_name, std::ios::out);
-        menu.SetOffset(f.data_offset);
-        for (int j = 0; j < f.data_size; j ++) out << menu.readU8();
-        out.close();
-    }*/
-
-    field_model_file_list_;// = application_.ResMgr()->listResourceNames("FFVII", "*");
-
-    // Open char_lgp as a lgp archive
-    VGears::LGPArchive char_lgp(input_dir_ + "data/field/char.lgp", "LGP");
-    char_lgp.open(input_dir_ + "data/field/char.lgp", true);
-    char_lgp.load();
-    // Also, open it as a file for reading
-    File char_file(input_dir_ + "data/field/char.lgp");
-
-    //Ogre::StringVectorPtr file_list = char_lgp.list(true, true);
-    field_model_file_list_ = char_lgp.list(true, true);
-    VGears::LGPArchive::FileList files = char_lgp.GetFiles();
-    CreateDir("temp/char");
-    std::cout << "[CHAR_LGP] START \n" ;
-    for (int i = 0; i < field_model_file_list_->size(); i ++){
-        VGears::LGPArchive::FileEntry f = files.at(i);
-        //std::cout << "[CHAR_LGP] File : " << f.file_name << "\n";
-        // Save the TEX File
-        std::fstream out;
-        out.open(output_dir_ + "temp/char/" + f.file_name, std::ios::out);
-        char_file.SetOffset(f.data_offset);
-        for (int j = 0; j < f.data_size; j ++) out << char_file.readU8();
-        out.close();
-
-        //field_model_file_list_->push_back(f.file_name);
-    }
-
-    application_.ResMgr()->addResourceLocation("data/temp/char/", "FileSystem", "FFVII", true, true);
-
-    iterator_counter_ = 0;
-    installation_state_ = CONVERT_FIELD_MODELS;
-    iterator_counter_ = 0;
-    conversion_step_ = 0;
-    model_animation_map_iterator_ = used_models_and_anims_.map.begin();
-}
-
-void DataInstaller::ConvertFieldModelsIteration(){
-    progress_step_num_elements_ = used_models_and_anims_.map.size();
-    if (model_animation_map_iterator_ != used_models_and_anims_.map.end()){
-        if (conversion_step_ == 0){
-            write_output_line_("Converting model " + model_animation_map_iterator_->first);
-            conversion_step_ ++;
-        }
-        else{
-            try{
-                std::cout << "OPEN HRC: " << model_animation_map_iterator_->first << std::endl;
-                Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(
-                    model_animation_map_iterator_->first, "FFVII"
-                );
-                //Ogre::ResourcePtr hrc = VGears::HRCFile();
-                Ogre::String base_name;
-                VGears::StringUtil::splitBase(model_animation_map_iterator_->first, base_name);
-                auto mesh_name = VGears::NameLookup::model(base_name) + ".mesh";
-                Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(mesh_name, "FFVII"));
-                Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
-                for (auto& anim : model_animation_map_iterator_->second){
-                    VGears::AFileManager &afl_mgr(VGears::AFileManager::GetSingleton());
-                    VGears::AFilePtr a = afl_mgr.load(anim, "FFVII").staticCast<VGears::AFile>();
-                    // Convert the FF7 name to a more readable name set in the meta data.
-                    VGears::StringUtil::splitBase(anim, base_name);
-                    a->AddTo(skeleton, VGears::NameLookup::Animation(base_name));
-                }
-                ExportMesh(output_dir_ + "/" + FieldModelDir() + "/", mesh);
-            }
-            catch (const Ogre::Exception& ex){
-                write_output_line_(
-                  "[ERROR] Ogre exception converting model "
-                  + model_animation_map_iterator_->first + ": " + ex.what()
-                );
-                std::cerr << "[ERROR] Ogre exception converting model "
-                  << model_animation_map_iterator_->first <<": " << ex.what() << std::endl;
-            }
-            catch (const std::exception& ex){
-                write_output_line_(
-                  "[ERROR] Exception converting model "
-                  + model_animation_map_iterator_->first + ": " + ex.what()
-                );
-                std::cerr << "[ERROR] Exception converting model "
-                  << model_animation_map_iterator_->first << ": " << ex.what() << std::endl;
-            }
-            iterator_counter_ ++;
-            ++ model_animation_map_iterator_;
-            conversion_step_ = 0;
-        }
-    }
-    else{
-        std::cout << "Installation finished." << std::endl;
-        write_output_line_("Installation finished.");
-        set_progress_label_("Installation finished.");
-        installation_state_ = DONE;
-    }
-}

+ 845 - 0
V-Gears-Installer/src/FieldDataInstaller.cpp

@@ -0,0 +1,845 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <string>
+#include <boost/filesystem.hpp>
+#include <QtCore/QDir>
+#include "FieldDataInstaller.h"
+#include "TexFile.h"
+#include "common/File.h"
+#include "data/VGearsMapListFile.h"
+#include "data/VGearsLZSFLevelFileManager.h"
+#include "data/VGearsHRCFileManager.h"
+#include "data/VGearsLGPArchive.h"
+#include "data/VGearsAFileManager.h"
+
+float FieldDataInstaller::LINE_SCALE_FACTOR(0.0078124970964f);
+
+std::string FieldDataInstaller::FIELD_MODELS_DIR("models/fields/entities");
+
+std::string FieldDataInstaller::FIELD_MAPS_DIR("fields");
+
+int FieldDataInstaller::INACTIVE_GATEWAY_ID(32767);
+
+std::string FieldDataInstaller::CreateGateWayScript(
+  const std::string& gateway_entity_name, const std::string& target_map_name,
+  const std::string& source_spawn_point_name
+){
+    return
+      "\nEntityContainer[ \"" + gateway_entity_name + "\" ] = {\n\n"
+      "    on_start = function(self)\n"
+      "        return 0\n"
+      "    end,\n\n"
+      "    on_enter_line = function(self, entity)\n"
+      "        return 0\n"
+      "    end,\n\n"
+      "    on_move_to_line = function(self, entity)\n"
+      "        return 0\n"
+      "    end,\n\n"
+      "    on_cross_line = function(self, entity)\n"
+      "        if entity == \"Cloud\" then\n"
+      "            if not FFVII.Data.DisableGateways then\n"
+      "                load_field_map_request(\""
+      + target_map_name + "\", \"" + source_spawn_point_name + "\")\n"
+      "            end\n"
+      "        end\n"
+      "        return 0\n"
+      "    end,\n\n"
+      "    on_leave_line = function(self, entity)\n"
+      "        return 0\n"
+      "    end,\n"
+      "}\n\n";
+}
+
+size_t FieldDataInstaller::GetFieldId(
+  const std::string& name, const std::vector<std::string>& field_id_to_name_lookup
+){
+    for (size_t i = 0; i < field_id_to_name_lookup.size(); i ++)
+        if (field_id_to_name_lookup[i] == name) return i;
+    throw std::runtime_error("No Id found for field name");
+}
+
+bool FieldDataInstaller::IsAFieldFile(const Ogre::String& resource_name){
+    return (
+      !VGears::StringUtil::endsWith(resource_name, ".tex")
+      && !VGears::StringUtil::endsWith(resource_name, ".tut")
+      && !VGears::StringUtil::endsWith(resource_name, ".siz")
+      && resource_name != "maplist"
+    );
+}
+
+bool FieldDataInstaller::WillCrash(const Ogre::String& resource_name){
+    if (
+      resource_name == "bugin1a" // crashes in back ground image generation
+      || resource_name == "frcyo" // Hangs in decompliation
+      || resource_name == "fr_e" // Background image crash
+      || resource_name == "las4_2" // Background image crash
+      || resource_name == "las4_3" // Background image crash
+      || resource_name == "lastmap" // Background image crash
+      || resource_name == "md_e1" // Background image crash
+      || resource_name == "trnad_3" // Background image crash
+      || resource_name == "bonevil2" // stof
+      || resource_name == "coloin1" // boost::bad_format_string: format-string is ill-formed
+      || resource_name == "del3" // Segmentation fault.
+      || resource_name == "elmin4_2" // boost::bad_format_string: format-string is ill-formed
+    ){return true;}
+    return false;
+}
+
+bool FieldDataInstaller::IsTestField(const Ogre::String& resource_name){
+    if (
+      resource_name == "startmap"
+      || resource_name == "md1stin"
+      || resource_name == "md1_1"
+      || resource_name == "md1_2"
+      || resource_name == "md8_1"
+      || resource_name == "nmkin_1"
+      || resource_name == "nmkin_2"
+      || resource_name == "nmkin_3"
+      || resource_name == "nmkin_4"
+      || resource_name == "nmkin_5"
+      || resource_name == "nrthmk"
+      || resource_name == "elevtr1"
+      || resource_name == "tin_1"
+      || resource_name == "tin_2"
+      || resource_name == "rootmap"
+      || resource_name == "md8_4"
+    ){return true;}
+    return false;
+}
+
+void FieldDataInstaller::CreateDir(const std::string& path){
+    QString target = QString::fromStdString(output_dir_ + path);
+    QDir dir(target);
+    if (!dir.mkpath(".")) throw std::runtime_error("Failed to mkpath");
+}
+
+void FieldDataInstaller::CollectFieldScaleFactors(
+  VGears::FLevelFilePtr& field, FieldScaleFactorMap& scale_factors,
+  const std::vector<std::string>& field_id_to_name_lookup
+){
+    scale_factors[GetFieldId(field->getName(), field_id_to_name_lookup)]
+      = FieldDecompiler::ScaleFactor(field->GetRawScript());
+}
+
+void FieldDataInstaller::CollectSpawnPoints(
+  VGears::FLevelFilePtr& field, const std::vector<std::string>& field_id_to_name_lookup,
+  FieldSpawnPointsMap& spawn_points
+){
+    const size_t this_field_id = GetFieldId(field->getName(), field_id_to_name_lookup);
+    // Decompile the script just so the MAPJUMPs can be collected. This is needed to construct the
+    // full list of entries to each field.
+    try{
+        // Get the raw script bytes.
+        FieldDecompiler::DecompiledScript decompiled;
+        const std::vector<u8> raw_field_data = field->GetRawScript();
+        // Decompile to LUA.
+        FF7FieldScriptGatewayCollector formatter(
+          field->getName(), field_id_to_name_lookup, spawn_points, this_field_id
+        );
+        decompiled = FieldDecompiler::Decompile(
+          field->getName(), raw_field_data, formatter, "", "EntityContainer = {}\n\n"
+        );
+    }
+    catch (const ::DecompilerException& ex){
+        std::cerr << "CollectSpawnPoints: DecompilerException: " << ex.what() << std::endl;
+    }
+    const VGears::TriggersFilePtr& triggers = field->GetTriggers();
+    const auto& gateways = triggers->GetGateways();
+    for (size_t i = 0; i < gateways.size(); i ++){
+        const VGears::TriggersFile::Gateway& gateway = gateways[i];
+        if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
+            auto it = spawn_points.find(gateway.destination_field_id);
+            if (it != std::end(spawn_points)){
+                // Add to the list of gateways that link to
+                // destination_field_id.
+                SpawnPointDb::Record rec;
+                rec.field_id = this_field_id;
+                rec.gateway = gateway;
+                rec.gateway_index_or_map_jump_address = i;
+                it->second.gateways_to_this_field.push_back(rec);
+            }
+            else{
+                // Create a new record for destination_field_id.
+                SpawnPointDb db;
+                db.target_field_id = gateway.destination_field_id;
+                SpawnPointDb::Record rec;
+                rec.field_id = this_field_id;
+                rec.gateway = gateway;
+                rec.gateway_index_or_map_jump_address = i;
+                db.gateways_to_this_field.push_back(rec);
+                spawn_points.insert(std::make_pair(db.target_field_id, db));
+            }
+        }
+    }
+}
+
+FieldDataInstaller::FieldDataInstaller(const std::string input_dir, const std::string output_dir):
+  input_dir_(input_dir), output_dir_(output_dir)
+{}
+
+FieldDataInstaller::~FieldDataInstaller(){}
+
+int FieldDataInstaller::CollectSpawnAndScaleFactorsInit(Ogre::ResourceGroupManager* res_mgr){
+    // List what's in the LGP archive.
+    //flevel_file_list_ = application_.ResMgr()->listResourceNames("FFVIIFields", "*");
+    flevel_file_list_ = res_mgr->listResourceNames("FFVIIFields", "*");
+    // Load the map list field.
+    VGears::MapListFilePtr map_list = VGears::MapListFileManager::GetSingleton().load(
+        "maplist", "FFVIIFields"
+      ).staticCast<VGears::MapListFile>();
+    map_list_ = map_list->GetMapList();
+    return flevel_file_list_->size();
+}
+
+void FieldDataInstaller::CollectSpawnAndScaleFactors(int field_index){
+    // On the first pass collate required field information.
+    progress_step_num_elements_ = flevel_file_list_->size();
+    if (field_index >= flevel_file_list_->size()){
+        // TODO: Show invalid index.
+        return;
+    }
+    auto resource_name = (*flevel_file_list_)[field_index];
+    // Exclude things that are not fields.
+    //if (IsAFieldFile(resource_name) && !WillCrash(resource_name)){
+    // TODO: DEBUG: Only testing fields.
+    if (IsAFieldFile(resource_name) && IsTestField(resource_name) && !WillCrash(resource_name)){
+        VGears::FLevelFilePtr field = VGears::LZSFLevelFileManager::GetSingleton().load(
+          resource_name, "FFVIIFields"
+        ).staticCast<VGears::FLevelFile>();
+        CollectSpawnPoints(field, map_list_, spawn_points_);
+        CollectFieldScaleFactors(field, scale_factors_, map_list_);
+    }
+}
+
+void FieldDataInstaller::Convert(int field_index){
+    // Do the full conversion with the collated data.
+    if (field_index >= flevel_file_list_->size()){
+        // TODO: Show invalid index.
+        return;
+    }
+    auto resource_name = (*flevel_file_list_)[field_index];
+    // Exclude things that are not fields.
+    if (IsAFieldFile(resource_name)){
+        //if (/*IsTestField(resource_name) &&*/ !WillCrash(resource_name)){
+        // TODO: DEBUG: Only test fields
+        if (IsTestField(resource_name) && !WillCrash(resource_name)){
+            //write_output_line_("Converting field " + resource_name);
+            std::cout << " - Converting field: " << resource_name << std::endl;
+            CreateDir(FIELD_MAPS_DIR + "/" + resource_name);
+            VGears::FLevelFilePtr field = VGears::LZSFLevelFileManager::GetSingleton().load(
+                resource_name, "FFVIIFields"
+              ).staticCast<VGears::FLevelFile>();
+            PcFieldToVGearsField(field);
+        }
+        else{
+            /*write_output_line_(
+              "[ERROR] Skip field " + resource_name + " due to crash or hang issue."
+            );*/
+            std::cerr << "[ERROR] Skip field: " << resource_name
+              << " due to crash or hang issue." << std::endl;
+        }
+    }
+}
+
+int FieldDataInstaller::WriteInit(){
+    doc_ = std::make_unique<TiXmlDocument>();
+    element_ = std::make_unique<TiXmlElement>("maps");
+    return converted_map_list_.size();
+}
+
+void FieldDataInstaller::Write(int field_index){
+    // TODO: Probably need to inject "empty" and "test" fields.
+    if (field_index >= flevel_file_list_->size()){
+        // TODO: Show invalid index.
+        return;
+    }
+    auto map = converted_map_list_[field_index];
+    //write_output_line_("Writing field " + FieldName(map));
+    std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("map"));
+    xml_element->SetAttribute("name", map);
+    xml_element->SetAttribute("file_name", FIELD_MAPS_DIR + "/" + map + "/map.xml");
+    element_->LinkEndChild(xml_element.release());
+}
+
+void FieldDataInstaller::WriteEnd(){
+    doc_->LinkEndChild(element_.release());
+    doc_->SaveFile(output_dir_ + "/maps.xml");
+}
+
+std::vector<std::string> FieldDataInstaller::ConvertModelsInit(){
+
+    std::vector<std::string> models;
+
+    // Open char_lgp as a lgp archive
+    VGears::LGPArchive char_lgp(input_dir_ + "data/field/char.lgp", "LGP");
+    char_lgp.open(input_dir_ + "data/field/char.lgp", true);
+    char_lgp.load();
+    // Also, open it as a file for reading
+    File char_file(input_dir_ + "data/field/char.lgp");
+
+    //Ogre::StringVectorPtr file_list = char_lgp.list(true, true);
+    field_model_file_list_ = char_lgp.list(true, true);
+    VGears::LGPArchive::FileList files = char_lgp.GetFiles();
+    for (int i = 0; i < field_model_file_list_->size(); i ++){
+        VGears::LGPArchive::FileEntry f = files.at(i);
+        // Save the TEX File
+        std::fstream out;
+        out.open(output_dir_ + "temp/char/" + f.file_name, std::ios::out);
+        char_file.SetOffset(f.data_offset);
+        for (int j = 0; j < f.data_size; j ++) out << char_file.readU8();
+        out.close();
+        //field_model_file_list_->push_back(f.file_name);
+    }
+    for (auto it = used_models_and_anims_.map.begin(); it != used_models_and_anims_.map.end(); it ++){
+        models.push_back(it->first);
+    }
+    return models;
+}
+
+void FieldDataInstaller::ConvertModels(std::string model_name){
+    auto model = used_models_and_anims_.map.find(model_name);
+    try{
+        Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(model->first, "FFVII");
+        //Ogre::ResourcePtr hrc = VGears::HRCFile();
+        Ogre::String base_name;
+        VGears::StringUtil::splitBase(model->first, base_name);
+        auto mesh_name = VGears::NameLookup::model(base_name) + ".mesh";
+        Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(mesh_name, "FFVII"));
+        Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
+        for (auto& anim : model->second){
+            VGears::AFileManager &afl_mgr(VGears::AFileManager::GetSingleton());
+            VGears::AFilePtr a = afl_mgr.load(anim, "FFVII").staticCast<VGears::AFile>();
+            // Convert the FF7 name to a more readable name set in the meta data.
+            VGears::StringUtil::splitBase(anim, base_name);
+            a->AddTo(skeleton, VGears::NameLookup::Animation(base_name));
+        }
+        ExportMesh(output_dir_ + FIELD_MODELS_DIR + "/", mesh);
+    }
+    catch (const Ogre::Exception& ex){
+        write_output_line_(
+          "[ERROR] Ogre exception converting model "
+          + model->first + ": " + ex.what()
+        );
+        std::cerr << "[ERROR] Ogre exception converting model "
+          << model->first <<": " << ex.what() << std::endl;
+    }
+    catch (const std::exception& ex){
+        write_output_line_(
+          "[ERROR] Exception converting model " + model->first + ": " + ex.what()
+        );
+        std::cerr << "[ERROR] Exception converting model "
+          << model->first << ": " << ex.what() << std::endl;
+    }
+}
+
+
+void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh){
+
+    // TODO: Share function with pc model exporter
+    VGears::String base_mesh_name;
+    VGears::StringUtil::splitFull(mesh->getName(), base_mesh_name);
+    Ogre::MeshSerializer mesh_serializer;
+    Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
+    Ogre::SkeletonSerializer skeleton_serializer;
+    skeleton_serializer.exportSkeleton(
+      skeleton.getPointer(), outdir + base_mesh_name + ".skeleton"
+    );
+    mesh->setSkeletonName(FIELD_MODELS_DIR + "/" + base_mesh_name + ".skeleton");
+    mesh_serializer.exportMesh(mesh.getPointer(), outdir + mesh->getName());
+    Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
+    Ogre::MaterialSerializer    mat_ser;
+    size_t i(0);
+    std::set<std::string> textures;
+    while (it.hasMoreElements()){
+        Ogre::SubMesh *sub_mesh(it.getNext());
+        Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(
+          sub_mesh->getMaterialName())
+        );
+        if (mat != nullptr){
+            for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){
+                Ogre::Technique* tech = mat->getTechnique(techs);
+                if (tech){
+                    for (size_t pass_num = 0; pass_num < tech->getNumPasses(); pass_num ++){
+                        Ogre::Pass* pass = tech->getPass(pass_num);
+                        if (pass){
+                            for (
+                              size_t texture_unit_num = 0;
+                              texture_unit_num < pass->getNumTextureUnitStates();
+                              texture_unit_num ++
+                            ){
+                                Ogre::TextureUnitState* unit
+                                  = pass->getTextureUnitState(texture_unit_num);
+                                if (unit && unit->getTextureName().empty() == false){
+                                    // Convert the texture from .tex to .png.
+                                    TexFile tex(
+                                      output_dir_ + "temp/char/" + unit->getTextureName()
+                                    );
+
+                                    // Ensure the output material script references png files
+                                    // rather than tex files.
+                                    Ogre::String base_name;
+                                    VGears::StringUtil::splitBase(
+                                      unit->getTextureName(), base_name
+                                    );
+                                    unit->setTextureName(
+                                      FIELD_MODELS_DIR + "/" + base_mesh_name
+                                      + "_" + base_name + ".png"
+                                    );
+
+                                    tex.SavePng(
+                                      output_dir_ + FIELD_MODELS_DIR + "/" + base_name + ".png", 0
+                                    );
+                                    // Copy subtexture (xxxx.png) to model_xxxx.png
+                                    // TODO: obtain the "data" folder
+                                    // programatically.
+                                    boost::filesystem::copy_file(
+                                        output_dir_ + FIELD_MODELS_DIR + "/" + base_name + ".png",
+                                        output_dir_ + FIELD_MODELS_DIR + "/"
+                                        + base_mesh_name + "_" + base_name + ".png",
+                                      boost::filesystem::copy_option::overwrite_if_exists
+                                    );
+                                    textures.insert(unit->getTextureName());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
+                //std::cout << "[MATERIAL] Writting material "
+                // << sub_mesh->getMaterialName() << std::endl;
+                mat_ser.queueForExport(mat);
+                materials_.push_back(sub_mesh->getMaterialName());
+            }
+
+        }
+        ++ i;
+    }
+    mat_ser.exportQueued(outdir + base_mesh_name + VGears::EXT_MATERIAL);
+    for (auto& texture_name : textures){
+        std::string tex_name = texture_name.c_str();
+        try{
+            Ogre::TexturePtr texture_ptr
+              = Ogre::TextureManager::getSingleton().load(tex_name, "FFVIITextures" /*"FFVII"*/);
+            Ogre::Image image;
+            texture_ptr->convertToImage(image);
+            Ogre::String base_name;
+            VGears::StringUtil::splitBase(texture_name, base_name);
+            image.save(outdir + base_mesh_name + "_" + base_name + ".png");
+        }
+        catch (std::exception const& ex){
+            std::cerr << "[ERROR] Exception: " << ex.what() << std::endl;
+        }
+    }
+}
+
+float FieldDataInstaller::GetFieldScaleFactor(size_t field_id){
+    auto it = scale_factors_.find(field_id);
+    if (it == std::end(scale_factors_))
+        throw std::runtime_error("Scale factor not found for field id");
+    return it->second;
+}
+
+void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
+    // Generate triggers script to insert into main
+    // decompiled FF7 field -> LUA script.
+    std::string gateway_script_data;
+    const VGears::TriggersFilePtr& triggers = field->GetTriggers();
+    const auto& gateways = triggers->GetGateways();
+    for (size_t i = 0; i < gateways.size(); i ++){
+        const VGears::TriggersFile::Gateway& gateway = gateways[i];
+        if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
+            const std::string gateway_entity_name = "Gateway" + std::to_string(i);
+            gateway_script_data += CreateGateWayScript(
+              gateway_entity_name,
+              map_list_.at(gateway.destination_field_id),
+              "Spawn_" + field->getName() + "_" + std::to_string(i)
+            );
+        }
+    }
+    const VGears::ModelListFilePtr& models = field->GetModelList();
+    FieldDecompiler::DecompiledScript decompiled;
+    FF7FieldScriptFormatter formatter(field->getName(), models, map_list_, spawn_points_);
+    try{
+        // Get the raw script bytes.
+        const std::vector<u8> raw_field_data = field->GetRawScript();
+        // Decompile to LUA.
+        decompiled = FieldDecompiler::Decompile(
+          field->getName(), raw_field_data, formatter, gateway_script_data,
+          "EntityContainer = {}\n\n"
+        );
+        std::ofstream script_file(
+          output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/script.lua"
+        );
+        if (script_file.is_open()){
+            script_file << decompiled.luaScript;
+            field_text_writer_.Begin(output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/text.xml");
+            try{field_text_writer_.Write(raw_field_data, field->getName());}
+            catch (const std::out_of_range& ex){
+                write_output_line_(
+                  "[ERROR] Failed to read texts from field " + field->getName() + ": " + ex.what()
+                );
+                std::cerr << "[ERROR] Failed to read texts from field "
+                  << field->getName() << ": " << ex.what() << std::endl;
+            }
+            field_text_writer_.End();
+        }
+        else{
+            write_output_line_(
+              "[ERROR] Failed to open script file from field " + field->getName() + " for writing."
+            );
+            std::cerr << "[ERROR] Failed to open script file from field "
+              << field->getName() << "for writing." << std::endl;
+        }
+    }
+    catch (const ::DecompilerException& ex){
+        write_output_line_(
+          "[ERROR] Internal decompiler error in field " + field->getName() + ": " + ex.what()
+        );
+        std::cerr << "[ERROR] Internal decompiler error in field "
+          << field->getName() << ": " << ex.what() << std::endl;
+    }
+    // Write out the map file which links all the other files together for a given field.
+    {
+        TiXmlDocument doc;
+        std::unique_ptr<TiXmlElement> element(new TiXmlElement("map"));
+        // Script.
+        std::unique_ptr<TiXmlElement> xml_script_element(new TiXmlElement("script"));
+        xml_script_element->SetAttribute(
+          "file_name", FIELD_MAPS_DIR + "/" + field->getName() + "/script.lua"
+        );
+        element->LinkEndChild(xml_script_element.release());
+        // Background.
+        std::unique_ptr<TiXmlElement> xml_background_2d(new TiXmlElement("background2d"));
+        xml_background_2d->SetAttribute(
+          "file_name", FIELD_MAPS_DIR + "/" + field->getName() + "/bg.xml"
+        );
+        element->LinkEndChild(xml_background_2d.release());
+        // Texts.
+        std::unique_ptr<TiXmlElement> xml_texts(new TiXmlElement("texts"));
+        xml_texts->SetAttribute(
+          "file_name", FIELD_MAPS_DIR + "/" + field->getName() + "/text.xml"
+        );
+        element->LinkEndChild(xml_texts.release());
+        // Walkmesh.
+        std::unique_ptr<TiXmlElement> xml_walkmesh(new TiXmlElement("walkmesh"));
+        xml_walkmesh->SetAttribute(
+          "file_name", FIELD_MAPS_DIR + "/" + field->getName() + "/wm.xml"
+        );
+        element->LinkEndChild(xml_walkmesh.release());
+        // Forward direction (angle player moves when "up" is pressed)
+        std::unique_ptr<TiXmlElement> xml_forward_direction(new TiXmlElement("movement_rotation"));
+        xml_forward_direction->SetAttribute(
+          "degree", std::to_string(triggers->MovementRotation())
+        );
+        element->LinkEndChild(xml_forward_direction.release());
+        // Get this fields Id.
+        const size_t this_field_id = GetFieldId(field->getName(), map_list_);
+        // Use the ID to find the pre-computed list of gateways in all other fields that link to
+        // this field.
+        auto spawn_iterator = spawn_points_.find(this_field_id);
+        Ogre::Vector3 first_entity_point = Ogre::Vector3::ZERO;
+
+        // If none found it probably just means no other fields have doors to this one.
+        if (spawn_iterator != std::end(spawn_points_)){
+            const std::vector<SpawnPointDb::Record>& spawn_point_records
+              = spawn_iterator->second.gateways_to_this_field;
+            for (size_t i = 0; i < spawn_point_records.size(); i ++){
+                const VGears::TriggersFile::Gateway& gateway = spawn_point_records[i].gateway;
+                // Entity_point:
+                std::unique_ptr<TiXmlElement> xml_entity_point(new TiXmlElement("entity_point"));
+                if (spawn_point_records[i].from_script){
+                    // Spawn points from map jumps have a another algorithm.
+                    xml_entity_point->SetAttribute(
+                      "name",
+                      map_list_.at(
+                        spawn_point_records[i].field_id) + "_" + spawn_point_records[i].entity_name
+                        + "_" + spawn_point_records[i].script_function_name + "_addr_"
+                        + std::to_string(spawn_point_records[i].gateway_index_or_map_jump_address)
+                      );
+                }
+                else{
+                    // Must also include the gateway index for the case where 2
+                    // fields have more than one door linking to each other.
+                    xml_entity_point->SetAttribute(
+                      "name",
+                      "Spawn_" + map_list_.at(spawn_point_records[i].field_id) + "_"
+                        + std::to_string(spawn_point_records[i].gateway_index_or_map_jump_address)
+                    );
+                }
+                const float downscaler_next = 128.0f * GetFieldScaleFactor(
+                  gateway.destination_field_id
+                );
+
+                // Position Z is actually the target walkmesh triangle ID, so this is tiny bit more
+                // complex. Now "get the Z value of the triangle with that ID". Note that ID
+                // actually just means index.
+                unsigned int triangle_index = static_cast<unsigned int>(gateway.destination.z);
+                if (
+                  triangle_index >= field->GetWalkmesh()->GetTriangles().size()
+                ){
+                    write_output_line_(
+                      "[WARNING] In field " + field->getName() + ": Map jump triangle ("
+                      + std::to_string(triangle_index) + ") out of bounds ("
+                      + std::to_string(field->GetWalkmesh()->GetTriangles().size()) + ")"
+                    );
+                    std::cerr << "[WARNING] In field " << field->getName()
+                      << ": Map jump triangle (" << triangle_index << ") out of bounds ("
+                      << field->GetWalkmesh()->GetTriangles().size() << ")" << std::endl;
+                    triangle_index = 0;
+                }
+                const float z_of_triangle_with_id
+                  = field->GetWalkmesh()->GetTriangles().at(triangle_index).a.z;
+                const Ogre::Vector3 position(
+                  gateway.destination.x / downscaler_next, gateway.destination.y / downscaler_next,
+                  z_of_triangle_with_id
+                );
+                if (position != Ogre::Vector3::ZERO && first_entity_point == Ogre::Vector3::ZERO)
+                    first_entity_point = position;
+                xml_entity_point->SetAttribute(
+                  "position", Ogre::StringConverter::toString(position)
+                );
+                const float rotation = (360.0f * static_cast<float>(gateway.dir)) / 255.0f;
+                xml_entity_point->SetAttribute("rotation", std::to_string(rotation));
+                element->LinkEndChild(xml_entity_point.release());
+            }
+        }
+
+        // Get lines. Add them to a list, so they aren't processed later as regular entities.
+        std::vector<std::string> line_entities;
+        for (FieldDecompiler::Line line : decompiled.lines){
+
+            std::unique_ptr<TiXmlElement> xml_entity_trigger(new TiXmlElement("entity_trigger"));
+            line_entities.push_back(line.name);
+            xml_entity_trigger->SetAttribute("name", line.name);
+            xml_entity_trigger->SetAttribute(
+              "point1",
+              std::to_string(line.point_a[0] * LINE_SCALE_FACTOR)
+                + " " + std::to_string(line.point_a[1] * LINE_SCALE_FACTOR)
+                + " " + std::to_string(line.point_a[2] * LINE_SCALE_FACTOR)
+            );
+            xml_entity_trigger->SetAttribute(
+              "point2",
+              std::to_string(line.point_b[0] * LINE_SCALE_FACTOR)
+                + " " + std::to_string(line.point_b[1] * LINE_SCALE_FACTOR)
+                + " " + std::to_string(line.point_b[2] * LINE_SCALE_FACTOR)
+            );
+            xml_entity_trigger->SetAttribute("enabled", "true");
+            element->LinkEndChild(xml_entity_trigger.release());
+        }
+
+        // Get entities.
+        for (FieldDecompiler::FieldEntity entity : decompiled.entities){
+            // If the entity has been added as a line, skip.
+            if (line_entities.size() > 0){
+                if (
+                  *std::find(line_entities.begin(), line_entities.end(), entity.name) == entity.name
+                ){
+                    continue;
+                }
+            }
+            const int char_id = entity.char_id;
+            if (char_id != -1){
+                const VGears::ModelListFile::ModelDescription& desc = models->GetModels().at(char_id);
+                auto& animations = used_models_and_anims_.ModelAnimations(desc.hrc_name);
+                for (const auto& anim : desc.animations)
+                    animations.insert(used_models_and_anims_.NormalizeAnimationName(anim.name));
+                std::unique_ptr<TiXmlElement> xml_entity_script(new TiXmlElement("entity_model"));
+                xml_entity_script->SetAttribute("name", entity.name);
+                // TODO: Add to list of HRC's to convert, obtain name of converted .mesh file.
+                auto lower_case_hrc_name = desc.hrc_name;
+                VGears::StringUtil::toLowerCase(lower_case_hrc_name);
+                xml_entity_script->SetAttribute(
+                  "file_name",
+                  FIELD_MODELS_DIR + "/" + used_models_and_anims_.ModelMetaDataName(lower_case_hrc_name)
+                );
+                xml_entity_script->SetAttribute("index", entity.index);
+                if (desc.type == VGears::ModelListFile::PLAYER){
+                    // For player models the position is set manually in the xml because if a map
+                    // is loaded manually this is where the player will end up. Hence we set the
+                    // position to the first entity_point that we have.
+                    xml_entity_script->SetAttribute(
+                      "position", Ogre::StringConverter::toString(first_entity_point)
+                    );
+                }
+                else{
+                    xml_entity_script->SetAttribute(
+                      "position", Ogre::StringConverter::toString(Ogre::Vector3::ZERO)
+                    );
+                }
+                xml_entity_script->SetAttribute("direction", "0");
+                // TODO: This isn't quite right, the models animation
+                // translation seems to be inverted?
+                xml_entity_script->SetAttribute("scale", "0.03125 0.03125 0.03125");
+                xml_entity_script->SetAttribute(
+                  "root_orientation", "0.7071067811865476 0.7071067811865476 0 0"
+                );
+                element->LinkEndChild(xml_entity_script.release());
+            }
+            else{
+                std::unique_ptr<TiXmlElement> xml_entity_script(new TiXmlElement("entity_script"));
+                xml_entity_script->SetAttribute("name", entity.name);//it.first);
+                element->LinkEndChild(xml_entity_script.release());
+            }
+        }
+        const float downscaler_this = 128.0f * GetFieldScaleFactor(this_field_id);
+        for (size_t i = 0; i < gateways.size(); i ++){
+            const VGears::TriggersFile::Gateway& gateway = gateways[i];
+            // If non-inactive gateway:
+            if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
+                std::unique_ptr<TiXmlElement> xml_entity_trigger(
+                  new TiXmlElement("entity_trigger")
+                );
+                xml_entity_trigger->SetAttribute("name", "Gateway" + std::to_string(i));
+                xml_entity_trigger->SetAttribute(
+                  "point1",
+                  Ogre::StringConverter::toString(
+                    Ogre::Vector3(
+                      gateway.exit_line[0].x,
+                      gateway.exit_line[0].y,
+                      gateway.exit_line[0].z) / downscaler_this
+                  )
+                );
+                xml_entity_trigger->SetAttribute(
+                  "point2",
+                  Ogre::StringConverter::toString(
+                    Ogre::Vector3(
+                      gateway.exit_line[1].x,
+                      gateway.exit_line[1].y,
+                      gateway.exit_line[1].z) / downscaler_this
+                    )
+                  );
+                // Enabled is hard coded to true.
+                xml_entity_trigger->SetAttribute("enabled", "true");
+                element->LinkEndChild(xml_entity_trigger.release());
+            }
+        }
+        doc.LinkEndChild(element.release());
+        doc.SaveFile(output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/map.xml");
+        const VGears::PaletteFilePtr& pal = field->GetPalette();
+        const VGears::BackgroundFilePtr& bg = field->GetBackground();
+        std::unique_ptr<Ogre::Image> bg_image(bg->CreateImage(pal));
+        bg_image->encode("png");
+        bg_image->save(output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/tiles.png");
+        {
+            TiXmlDocument bg_doc;
+            std::unique_ptr<TiXmlElement> bg_element(new TiXmlElement("background2d"));
+            // Magic constants.
+            const int BG_SCALE_UP_FACTOR = 3;
+            const int BG_PSX_SCREEN_WIDTH = 320;
+            const int BG_PSX_SCREEN_HEIGHT = 240;
+            // Get texture atlas size.
+            const int width = bg_image->getWidth();
+            const int height = bg_image->getHeight();
+            const VGears::CameraMatrixFilePtr& camera_matrix = field->GetCameraMatrix();
+            const Ogre::Vector3 position
+              = camera_matrix->GetPosition() / GetFieldScaleFactor(this_field_id);
+            const Ogre::Quaternion orientation = camera_matrix->GetOrientation();
+            const Ogre::Degree fov
+              = camera_matrix->GetFov(static_cast<float>(BG_PSX_SCREEN_HEIGHT));
+            const int min_x = triggers->GetCameraRange().left * BG_SCALE_UP_FACTOR;
+            const int min_y = triggers->GetCameraRange().top * BG_SCALE_UP_FACTOR;
+            const int max_x = triggers->GetCameraRange().right * BG_SCALE_UP_FACTOR;
+            const int max_y = triggers->GetCameraRange().bottom * BG_SCALE_UP_FACTOR;
+            bg_element->SetAttribute("image", FIELD_MAPS_DIR + "/" + field->getName() + "/tiles.png");
+            bg_element->SetAttribute("position", Ogre::StringConverter::toString(position));
+            bg_element->SetAttribute("orientation", Ogre::StringConverter::toString(orientation));
+            bg_element->SetAttribute("fov", Ogre::StringConverter::toString(fov));
+            bg_element->SetAttribute(
+              "range",
+              std::to_string(min_x) + " " + std::to_string(min_y) + " "
+              + std::to_string(max_x) + " " + std::to_string(max_y)
+            );
+            bg_element->SetAttribute(
+              "clip",
+              std::to_string(BG_PSX_SCREEN_WIDTH * BG_SCALE_UP_FACTOR) + " "
+              + std::to_string(BG_PSX_SCREEN_HEIGHT * BG_SCALE_UP_FACTOR)
+            );
+            // Write out the *_BG.XML data.
+            auto& layers = bg->GetLayers();
+            for (const auto& layer : layers){
+                // TODO: Should only the tiles to construct enabled layers be outputted?
+                //  if (layer.enabled){
+
+                for (const VGears::BackgroundFile::SpriteData& sprite : layer.sprites){
+                    std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("tile"));
+                    xml_element->SetAttribute(
+                      "destination",
+                      Ogre::StringConverter::toString(sprite.dst.x * BG_SCALE_UP_FACTOR) + " "
+                      + Ogre::StringConverter::toString(sprite.dst.y * BG_SCALE_UP_FACTOR)
+                    );
+                    xml_element->SetAttribute(
+                      "width", Ogre::StringConverter::toString(sprite.width * BG_SCALE_UP_FACTOR)
+                    );
+                    xml_element->SetAttribute(
+                      "height", Ogre::StringConverter::toString(sprite.height * BG_SCALE_UP_FACTOR)
+                    );
+                    // Each tile is added to a big texture atlas with hard coded size of 1024x1024,
+                    // convert UV's to the 0.0f to 1.0f range.
+                    const float u0 = static_cast<float>(sprite.src.x) / width;
+                    const float v0 = static_cast<float>(sprite.src.y) / height;
+                    const float u1 = static_cast<float>(sprite.src.x + sprite.width) / width;
+                    const float v1 = static_cast<float>(sprite.src.y + sprite.height) / height;
+                    xml_element->SetAttribute(
+                      "uv",
+                      Ogre::StringConverter::toString(u0) + " "
+                      + Ogre::StringConverter::toString(v0) + " "
+                      + Ogre::StringConverter::toString(u1) + " "
+                      + Ogre::StringConverter::toString(v1)
+                    );
+                    // TODO: It works (on FFVII PC), but why this number?
+                    xml_element->SetAttribute(
+                      "depth",
+                      Ogre::StringConverter::toString(static_cast<float>(sprite.depth) * (0.03125f))
+                    );
+                    // TODO: Copied from DatFile::AddTile.
+                    // Add to common method.
+                    Ogre::String blending_str = "";
+                    if (sprite.blending == 0) blending_str = "alpha";
+                    // 3 is source + 0.25 * destination:
+                    else if (sprite.blending == 1 || sprite.blending == 3) blending_str = "add";
+                    else if (sprite.blending == 2) blending_str = "subtract";
+                    // TODO: Should probably throw to fail conversion:
+                    else blending_str = "unknown";
+                    xml_element->SetAttribute("blending", blending_str);
+                    bg_element->LinkEndChild(xml_element.release());
+                }
+                //}
+            }
+            bg_doc.LinkEndChild(bg_element.release());
+            bg_doc.SaveFile(output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/bg.xml");
+        }
+    }
+    {
+        // Save out the walk mesh as XML.
+        const VGears::WalkmeshFilePtr& walkmesh = field->GetWalkmesh();
+        TiXmlDocument doc;
+        std::unique_ptr<TiXmlElement> wmesh_element(new TiXmlElement("walkmesh"));
+        for (VGears::WalkmeshFile::Triangle& tri : walkmesh->GetTriangles()){
+            std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("triangle"));
+            xml_element->SetAttribute("a", Ogre::StringConverter::toString(tri.a));
+            xml_element->SetAttribute("b", Ogre::StringConverter::toString(tri.b));
+            xml_element->SetAttribute("c", Ogre::StringConverter::toString(tri.c));
+            xml_element->SetAttribute("a_b", std::to_string(tri.access_side[0]));
+            xml_element->SetAttribute("b_c", std::to_string(tri.access_side[1]));
+            xml_element->SetAttribute("c_a", std::to_string(tri.access_side[2]));
+            wmesh_element->LinkEndChild(xml_element.release());
+        }
+        doc.LinkEndChild(wmesh_element.release());
+        doc.SaveFile(output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/wm.xml");
+    }
+    converted_map_list_.push_back(field->getName());
+}

+ 4 - 3
V-Gears-Installer/src/KernelDataInstaller.cpp

@@ -21,14 +21,15 @@
 #include "common/FinalFantasy7/FF7NameLookup.h"
 #include <boost/algorithm/string/replace.hpp>
 
-KernelDataInstaller::KernelDataInstaller(std::string path): kernel_(path){
+KernelDataInstaller::KernelDataInstaller(std::string path):
+  kernel_(path + "data/kernel/KERNEL.BIN"), exe_path_(path + "ff7.exe"){
     for (int i = 0; i < 416; i ++) prices_[i] = 50;
 }
 
 KernelDataInstaller::~KernelDataInstaller(){}
 
-void KernelDataInstaller::ReadPrices(std::string exe_path){
-    File exe_file(exe_path);
+void KernelDataInstaller::ReadPrices(){
+    File exe_file(exe_path_);
     exe_file.SetOffset(0x00514518); // Items, weapons, armors, accessories start here
     for (int i = 0; i < 320; i ++) prices_[i] = exe_file.readU32LE();
     exe_file.SetOffset(0x00514B18); // Materias start here

+ 5 - 14
V-Gears-Installer/src/MainWindow.cpp

@@ -41,7 +41,6 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), main_window_(new U
     main_window_->line_vgears_exe->setText(settings_->value("VGearsEXE").toString());
     // TODO" Hard coded paths for debugging to save time. Remove them.
     main_window_->line_data_src->setText("/home/ivalentin/FF7InstallDisk/");
-    main_window_->line_exe_src->setText("/home/ivalentin/FF7InstallDisk/ff7.exe");
     //main_window_->line_data_dst->setText("/home/ivalentin/.v-gears/data/");
     timer_ = new QTimer(this);
     connect(timer_, SIGNAL(timeout()), this, SLOT(DoProgress()));
@@ -137,14 +136,6 @@ void MainWindow::on_btn_data_src_clicked(){
     main_window_->line_data_src->setText(temp);
 }
 
-void MainWindow::on_btn_exe_src_clicked(){
-    QString temp = QFileDialog::getOpenFileName(
-      this, tr("Location of original executable (ff7.exe)"),
-      QDir::homePath(), "PC executable (ff7.exe)", 0
-    );
-    main_window_->line_exe_src->setText(temp);
-}
-
 void MainWindow::on_line_data_dst_editingFinished(){
     settings_->setValue("DataDir", main_window_->line_data_dst->text());
 }
@@ -176,7 +167,6 @@ void MainWindow::on_btn_data_run_clicked(){
         // Normalize the paths so its in / format separators.
         QString input = QDir::fromNativeSeparators(main_window_->line_data_src->text());
         if (!input.endsWith("/")) input += "/";
-        QString input_exe = QDir::fromNativeSeparators(main_window_->line_exe_src->text());
         QString output = QDir::fromNativeSeparators(main_window_->line_data_dst->text());
         if (!output.endsWith("/")) output += "/";
         // TODO: Enumerate files or find some better way to do this.
@@ -215,11 +205,12 @@ void MainWindow::on_btn_data_run_clicked(){
             installer_created = true;
             installer_ = std::make_unique<DataInstaller>(
               QDir::toNativeSeparators(input).toStdString(),
-              QDir::toNativeSeparators(input_exe).toStdString(),
               QDir::toNativeSeparators(output).toStdString(),
-              [this](const std::string log_line){main_window_->data_log->append(log_line.c_str());},
-              [this](const std::string progress){
-                main_window_->label_progress->setText(progress.c_str());
+              [this](const std::string log_line, int level, bool as_progress = false){
+                 main_window_->data_log->append(log_line.c_str());
+                 std::cout << log_line << std::endl;
+                 if (as_progress)
+                     main_window_->label_progress->setText(log_line.c_str());
               }
             );
             OnInstallStarted();

+ 0 - 64
V-Gears-Installer/src/MainWindow.ui

@@ -92,70 +92,6 @@
           </item>
          </layout>
         </item>
-        <item>
-         <layout class="QHBoxLayout" name="horizontalLayout_5">
-          <item>
-           <widget class="QLabel" name="label_14">
-            <property name="minimumSize">
-             <size>
-              <width>200</width>
-              <height>0</height>
-             </size>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>200</width>
-              <height>50</height>
-             </size>
-            </property>
-            <property name="text">
-             <string>Original FFVII executable:</string>
-            </property>
-            <property name="alignment">
-             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QLineEdit" name="line_exe_src"/>
-          </item>
-          <item>
-           <widget class="QPushButton" name="btn_exe_src">
-            <property name="maximumSize">
-             <size>
-              <width>24</width>
-              <height>24</height>
-             </size>
-            </property>
-            <property name="text">
-             <string>...</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QPushButton" name="help_exe_src">
-            <property name="toolTip">
-             <string>
-              &lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Path to the FFVII (PC version) ff7.exe.&lt;/b&gt;&lt;br&gt;&lt;br&gt;It's not mandatory, but if not set, prices for all items and materiain shops will be set to 50 gil.&lt;/body&gt;&lt;/html&gt;
-             </string>
-            </property>
-            <property name="text">
-             <string/>
-            </property>
-            <property name="icon">
-             <iconset theme="dialog-information">
-              <normaloff>.</normaloff>.</iconset>
-            </property>
-            <property name="checkable">
-             <bool>false</bool>
-            </property>
-            <property name="flat">
-             <bool>true</bool>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </item>
         <item>
          <layout class="QHBoxLayout" name="horizontalLayout_3">
           <item>

+ 0 - 1
V-Gears-Installer/src/MediaDataInstaller.cpp

@@ -273,5 +273,4 @@ void MediaDataInstaller::InstallSprites(){
         // Delete Tex file.
         std::remove((output_dir_ + "images/" + f.file_name).c_str());
     }
-
 }