Prechádzať zdrojové kódy

Merge branch 'feature/installer_uses_disk_images' into develop

Iñigo Valentin 2 týždňov pred
rodič
commit
f3727d08ba
82 zmenil súbory, kde vykonal 3223 pridanie a 2870 odobranie
  1. 17 0
      CMakeLists.txt
  2. 2 0
      debian/install
  3. 64 8
      src/data/VGearsHRCMeshLoader.cpp
  4. 56 115
      src/installer/BattleDataInstaller.cpp
  5. 46 46
      src/installer/BattleDataInstaller.h
  6. 4 2
      src/installer/CMakeLists.txt
  7. 43 44
      src/installer/DataInstaller.cpp
  8. 55 37
      src/installer/DataInstaller.h
  9. 411 84
      src/installer/DiskImage.cpp
  10. 273 0
      src/installer/DiskImage.h
  11. 60 123
      src/installer/FieldDataInstaller.cpp
  12. 48 70
      src/installer/FieldDataInstaller.h
  13. 3 9
      src/installer/FieldTextWriter.cpp
  14. 1 3
      src/installer/FieldTextWriter.h
  15. 97 201
      src/installer/KernelDataInstaller.cpp
  16. 166 132
      src/installer/KernelDataInstaller.h
  17. 42 77
      src/installer/MainWindow.cpp
  18. 10 4
      src/installer/MainWindow.h
  19. 683 186
      src/installer/MainWindow.ui
  20. 64 47
      src/installer/MediaDataInstaller.cpp
  21. 31 8
      src/installer/MediaDataInstaller.h
  22. 0 246
      src/installer/Release.h
  23. 17 11
      src/installer/SaveMap.h
  24. 1 2
      src/installer/ScopedLgp.cpp
  25. 2 6
      src/installer/TexFile.cpp
  26. 31 23
      src/installer/TexFile.h
  27. 3 7
      src/installer/VGearsUtility.cpp
  28. 95 121
      src/installer/WorldInstaller.cpp
  29. 15 12
      src/installer/WorldInstaller.h
  30. 9 29
      src/installer/common/BinGZipFile.cpp
  31. 6 10
      src/installer/common/DrawSkeleton.cpp
  32. 12 24
      src/installer/common/FontFile.cpp
  33. 5 15
      src/installer/common/Logger.cpp
  34. 2 6
      src/installer/common/OgreBase.cpp
  35. 11 30
      src/installer/common/OgreBase.h
  36. 23 38
      src/installer/common/OgreGenUtilites.cpp
  37. 6 11
      src/installer/common/OgreGenUtilites.h
  38. 1 2
      src/installer/common/TimToVram.cpp
  39. 1 2
      src/installer/common/Vram.cpp
  40. 9 16
      src/installer/data/AbFile.cpp
  41. 12 11
      src/installer/data/AbFile.h
  42. 30 25
      src/installer/data/Attack.h
  43. 1 3
      src/installer/data/BattleScene.h
  44. 32 73
      src/installer/data/BattleSceneFile.cpp
  45. 53 36
      src/installer/data/BattleSceneFile.h
  46. 3 9
      src/installer/data/DaFile.cpp
  47. 43 32
      src/installer/data/DaFile.h
  48. 4 4
      src/installer/data/Enemy.h
  49. 52 51
      src/installer/data/FF7Data.h
  50. 45 35
      src/installer/data/Formation.h
  51. 8 14
      src/installer/data/WorldMapWalkmesh.cpp
  52. 46 34
      src/installer/data/WorldMapWalkmesh.h
  53. 14 39
      src/installer/decompiler/CodeGenerator.cpp
  54. 2 6
      src/installer/decompiler/CodeGenerator.h
  55. 38 88
      src/installer/decompiler/ControlFlow.cpp
  56. 4 9
      src/installer/decompiler/DecompilerException.cpp
  57. 1 3
      src/installer/decompiler/Disassembler.cpp
  58. 1 3
      src/installer/decompiler/Engine.h
  59. 2 7
      src/installer/decompiler/Graph.cpp
  60. 1 3
      src/installer/decompiler/Graph.h
  61. 1 3
      src/installer/decompiler/RefCounted.h
  62. 8 12
      src/installer/decompiler/ScriptFormatter.cpp
  63. 2 6
      src/installer/decompiler/ScriptFormatter.h
  64. 4 9
      src/installer/decompiler/Value.cpp
  65. 24 68
      src/installer/decompiler/field/FieldCodeGenerator.cpp
  66. 31 96
      src/installer/decompiler/field/FieldDisassembler.cpp
  67. 1 1
      src/installer/decompiler/field/FieldDisassembler.h
  68. 6 16
      src/installer/decompiler/field/FieldEngine.cpp
  69. 1 4
      src/installer/decompiler/field/FieldScriptFormatter.cpp
  70. 2 4
      src/installer/decompiler/instruction/BinaryOpStackInstruction.cpp
  71. 1 2
      src/installer/decompiler/instruction/KernelCallStackInstruction.cpp
  72. 2 1
      src/installer/decompiler/instruction/KernelCallStackInstruction.h
  73. 3 3
      src/installer/decompiler/instruction/ReturnInstruction.cpp
  74. 2 3
      src/installer/decompiler/instruction/UncondJumpInstruction.cpp
  75. 54 170
      src/installer/decompiler/world/WorldDisassembler.cpp
  76. 1 3
      src/installer/decompiler/world/WorldEngine.cpp
  77. 5 0
      src/installer/installer_resources.qrc
  78. 7 0
      src/installer/main.cpp
  79. 239 177
      src/installer/ui_MainWindow.h
  80. 12 0
      v-gears-installer.desktop
  81. BIN
      v-gears-installer.png
  82. BIN
      v-gears.png

+ 17 - 0
CMakeLists.txt

@@ -58,4 +58,21 @@ if(BUILD_TESTS)
     add_subdirectory(test)
 endif()
 
+# Install Linux desktop integration assets for the installer so desktop shells
+# can map the running window to its icon.
+if(UNIX AND NOT APPLE AND BUILD_INSTALLER)
+    install(
+        FILES ${CMAKE_SOURCE_DIR}/v-gears-installer.desktop
+        DESTINATION share/applications
+    )
+    install(
+        FILES ${CMAKE_SOURCE_DIR}/v-gears-installer.png
+        DESTINATION share/icons/hicolor/256x256/apps
+    )
+    install(
+        FILES ${CMAKE_SOURCE_DIR}/v-gears-installer.png
+        DESTINATION share/pixmaps
+    )
+endif()
+
 # TODO: Generate Installer (.msi, .deb, .appImage...)

+ 2 - 0
debian/install

@@ -1,4 +1,6 @@
 v-gears.desktop /usr/share/applications
 v-gears.png /usr/share/pixmaps
+v-gears-installer.desktop /usr/share/applications
+v-gears-installer.png /usr/share/pixmaps
 v-gears-launcher /usr/bin
 output/resources.cfg /usr/share/v-gears

+ 64 - 8
src/data/VGearsHRCMeshLoader.cpp

@@ -14,6 +14,7 @@
  */
 
 #include <OgreMesh.h>
+#include <vector>
 #include "data/VGearsHRCMeshLoader.h"
 #include "common/VGearsStringUtil.h"
 #include "data/VGearsPFile.h"
@@ -23,6 +24,49 @@
 
 namespace VGears{
 
+  namespace {
+
+    template<typename ResourcePtrType, typename LoaderFunc>
+    ResourcePtrType loadWithFallbacks(
+      const Ogre::String& group,
+      const std::vector<Ogre::String>& candidates,
+      LoaderFunc loader
+    ){
+      Ogre::Exception last_exception(
+        Ogre::Exception::ERR_ITEM_NOT_FOUND,
+        "No candidate resource could be loaded.",
+        "loadWithFallbacks"
+      );
+      for (const auto& candidate : candidates){
+        if (candidate.empty()) continue;
+        try{
+          return loader(candidate);
+        }
+        catch (const Ogre::Exception& ex){
+          last_exception = ex;
+        }
+      }
+      throw last_exception;
+    }
+
+    std::vector<Ogre::String> rsdCandidates(const Ogre::String& rsd_base){
+      std::vector<Ogre::String> names;
+      names.push_back(rsd_base + EXT_RSD);
+      names.push_back(rsd_base);
+      names.push_back(rsd_base + "rsd");
+      return names;
+    }
+
+    std::vector<Ogre::String> pCandidates(const Ogre::String& pfile_name){
+      std::vector<Ogre::String> names;
+      names.push_back(pfile_name);
+      names.push_back(StringUtil::replaceAll(pfile_name, EXT_P, ""));
+      names.push_back(pfile_name + "p");
+      return names;
+    }
+
+  }
+
     typedef HRCFile::Bone Bone;
 
     typedef HRCFile::BoneList BoneList;
@@ -52,21 +96,33 @@ namespace VGears{
         const String &bone_name(bone.name);
         RSDNameList::const_iterator it(bone.rsd_names.begin());
         RSDNameList::const_iterator end(bone.rsd_names.end());
-        String rsd_base, rsdfile_name_, pfile_name_;
+        String rsd_base, rsdfile_name_;
         while (it != end){
             rsd_base = path + *it;
             StringUtil::toLowerCase(rsd_base);
             rsdfile_name_ = rsd_base + EXT_RSD;
-            RSDFilePtr rsd_file = RSDFileManager::getSingleton().load(
-              rsdfile_name_, hrc_file_.getGroup()
-            ).staticCast<RSDFile>();
+            RSDFilePtr rsd_file = loadWithFallbacks<RSDFilePtr>(
+              hrc_file_.getGroup(),
+              rsdCandidates(rsd_base),
+              [this](const Ogre::String& candidate){
+                  return RSDFileManager::getSingleton().load(
+                    candidate, hrc_file_.getGroup()
+                  ).staticCast<RSDFile>();
+              }
+            );
             assert(rsd_file != nullptr);
-            pfile_name_ = path + rsd_file->GetPolygonName();
+            String pfile_name_ = path + rsd_file->GetPolygonName();
             StringUtil::toLowerCase(pfile_name_);
             pfile_name_ = StringUtil::replaceAll(pfile_name_, EXT_PLY, EXT_P);
-            PFilePtr p_file =  PFileManager::getSingleton().load(
-              pfile_name_, hrc_file_.getGroup()
-            ).staticCast<PFile>();
+            PFilePtr p_file = loadWithFallbacks<PFilePtr>(
+              hrc_file_.getGroup(),
+              pCandidates(pfile_name_),
+              [this](const Ogre::String& candidate){
+                  return PFileManager::getSingleton().load(
+                    candidate, hrc_file_.getGroup()
+                  ).staticCast<PFile>();
+              }
+            );
             assert(p_file != nullptr);
             p_file->AddGroups(mesh, bone_name, rsd_file);
             ++ it;

+ 56 - 115
src/installer/BattleDataInstaller.cpp

@@ -18,6 +18,7 @@
 
 #include <iostream>
 #include <fstream>
+#include <algorithm>
 #include <zlib.h>
 #include <tinyxml.h>
 #include <boost/filesystem.hpp>
@@ -33,10 +34,10 @@
 #include "common/FinalFantasy7/FF7NameLookup.h"
 
 BattleDataInstaller::BattleDataInstaller(
-  const std::string input_dir, const std::string output_dir, Ogre::ResourceGroupManager* res_mgr
+  const DiskImage& disk_image, const std::string output_dir, Ogre::ResourceGroupManager* res_mgr
 ):
-  input_dir_(input_dir), output_dir_(output_dir),
-  scene_bin_(input_dir + "data/battle/scene.bin"), total_scenes_(0), next_scene_(0),
+  disk_image_(disk_image), output_dir_(output_dir),
+  scene_bin_(*const_cast<DiskImage&>(disk_image).filePath("data/battle/scene.bin")), total_scenes_(0), next_scene_(0),
   res_mgr_(res_mgr)
 {}
 
@@ -56,9 +57,7 @@ unsigned int BattleDataInstaller::InitializeScenes(){
             u32 end_offset = scene_bin_block.readU32LE() * 4;
             if (end_offset > start_offset + 0x2000) end_offset = start_offset + 0x2000;
             if (start_offset >= 0 && start_offset < 0x2000){
-                File scene_bin_fragment(
-                  &scene_bin_block, start_offset, end_offset - start_offset - 1
-                );
+                File scene_bin_fragment(&scene_bin_block, start_offset, end_offset - start_offset - 1);
                 File* extracted = ExtractGZipScene(scene_bin_fragment);
                 if (extracted != NULL){
                     scenes_uncompressed_.push_back(extracted);
@@ -78,10 +77,10 @@ unsigned int BattleDataInstaller::InitializeBattleModels(){
     battle_lgp_file_names_.clear();
     battle_models_.clear();
     // Open battle.lgp
-    File battle_lgp_file(input_dir_ + "data/battle/battle.lgp");
+    File battle_lgp_file(*disk_image_.filePath("data/battle/battle.lgp"));
     // Also, open it as a LGP archive.
-    VGears::LGPArchive battle_lgp(input_dir_ + "data/battle/battle.lgp", "LGP");
-    battle_lgp.open(input_dir_ + "data/battle/battle.lgp", true);
+    VGears::LGPArchive battle_lgp(*disk_image_.filePath("data/battle/battle.lgp"), "LGP");
+    battle_lgp.open(*disk_image_.filePath("data/battle/battle.lgp"), true);
     battle_lgp.load();
     VGears::LGPArchive::FileList file_list = battle_lgp.GetFiles();
     for (int i = 0; i < file_list.size(); i ++){
@@ -102,10 +101,10 @@ unsigned int BattleDataInstaller::InitializeSpellModels(){
     battle_lgp_file_names_.clear();
     battle_models_.clear();
     // Open battle.lgp
-    File magic_lgp_file(input_dir_ + "data/battle/magic.lgp");
+    File magic_lgp_file(*disk_image_.filePath("data/battle/magic.lgp"));
     // Also, open it as a LGP archive.
-    VGears::LGPArchive magic_lgp(input_dir_ + "data/battle/magic.lgp", "LGP");
-    magic_lgp.open(input_dir_ + "data/battle/magic.lgp", true);
+    VGears::LGPArchive magic_lgp(*disk_image_.filePath("data/battle/magic.lgp"), "LGP");
+    magic_lgp.open(*disk_image_.filePath("data/battle/magic.lgp"), true);
     magic_lgp.load();
     VGears::LGPArchive::FileList file_list = magic_lgp.GetFiles();
     for (int i = 0; i < file_list.size(); i ++){
@@ -126,27 +125,17 @@ unsigned int BattleDataInstaller::ProcessScene(){
     // Add new enemies to the list.
     std::vector<Enemy> enemies = scene.GetEnemies();
     for (int e = 0; e < enemies.size(); e ++){
-        bool already_added = false;
-        for (int l = 0; l < enemies_.size(); l ++){
-            if (enemies_[l].id == enemies[e].id){
-                already_added = true;
-                break;
-            }
-        }
-        if (already_added) continue;
+        if (std::any_of(
+          enemies_.begin(), enemies_.end(), [&](const Enemy& existing){ return existing.id == enemies[e].id; }
+        )) continue;
         enemies_.push_back(enemies[e]);
     }
     // Add new attacks to the list.
     std::vector<Attack> attacks = scene.GetAttacks();
     for (int a = 0; a < attacks.size(); a ++){
-        bool already_added = false;
-        for (int l = 0; l < attacks_.size(); l ++){
-            if (attacks_[l].id == attacks[a].id){
-                already_added = true;
-                break;
-            }
-        }
-        if (already_added) continue;
+        if (std::any_of(
+          attacks_.begin(), attacks_.end(), [&](const Attack& existing){ return existing.id == attacks[a].id; }
+        )) continue;
         attacks_.push_back(attacks[a]);
     }
     // Add new formations to the list.
@@ -203,16 +192,14 @@ unsigned int BattleDataInstaller::ProcessBattleModel(){
           output_dir_ + "temp/battle_models/"
           + battle_lgp_file_names_[next_model_to_process_] + ".script"
         );
-        bool found = false;
-        for (int m = 0; m < battle_models_.size(); m ++){
-            if (battle_models_[m].id == id){
-                battle_models_[m].script
-                  = battle_lgp_file_names_[next_model_to_process_] + ".script";
-                found = true;
-                break;
-            }
+        auto model_it = std::find_if(
+            battle_models_.begin(), battle_models_.end(),
+            [&](const BattleModel& model){ return model.id == id; }
+        );
+        if (model_it != battle_models_.end()){
+                model_it->script = battle_lgp_file_names_[next_model_to_process_] + ".script";
         }
-        if (found == false){
+        else{
             BattleModel model;
             model.id = id;
             model.script = battle_lgp_file_names_[next_model_to_process_] + ".script";
@@ -222,8 +209,7 @@ unsigned int BattleDataInstaller::ProcessBattleModel(){
     else if (type == "da"){
         // .da Animation file.
         battle_lgp_files_[next_model_to_process_].WriteFile(
-          output_dir_ + "temp/battle_models/"
-          + battle_lgp_file_names_[next_model_to_process_] + ".anim"
+          output_dir_ + "temp/battle_models/" + battle_lgp_file_names_[next_model_to_process_] + ".anim"
         );
         bool found = false;
         for (int m = 0; m < battle_models_.size(); m ++){
@@ -246,8 +232,7 @@ unsigned int BattleDataInstaller::ProcessBattleModel(){
     ){
         // .tex texture files. Save directly to their directory.
         battle_lgp_files_[next_model_to_process_].WriteFile(
-          output_dir_ + "temp/battle_models/"
-          + battle_lgp_file_names_[next_model_to_process_] + ".tex"
+          output_dir_ + "temp/battle_models/" + battle_lgp_file_names_[next_model_to_process_] + ".tex"
         );
         TexFile tex(battle_lgp_files_[next_model_to_process_]);
         std::string path = output_dir_ + "models/battle/";
@@ -317,8 +302,7 @@ unsigned int BattleDataInstaller::ProcessBattleModel(){
     else{
         // .p polygon file.
         battle_lgp_files_[next_model_to_process_].WriteFile(
-          output_dir_ + "temp/battle_models/"
-          + battle_lgp_file_names_[next_model_to_process_] + ".p"
+          output_dir_ + "temp/battle_models/" + battle_lgp_file_names_[next_model_to_process_] + ".p"
         );
         bool found = false;
         for (int m = 0; m < battle_models_.size(); m ++){
@@ -361,9 +345,7 @@ unsigned int BattleDataInstaller::ProcessSpellModel(){
 
     if (ext == "d"){
         // Skeleton file.
-        magic_lgp_files_[next_model_to_process_].WriteFile(
-          output_dir_ + "temp/spell_models/" + fname
-        );
+        magic_lgp_files_[next_model_to_process_].WriteFile(output_dir_ + "temp/spell_models/" + fname);
         bool found = false;
         for (int m = 0; m < spell_models_.size(); m ++){
             if (spell_models_[m].id == id){
@@ -409,9 +391,7 @@ unsigned int BattleDataInstaller::ProcessSpellModel(){
     if (ext == "tex"){
         // Texture files. They can be shared among various spells, so just convert them and don't
         // add them to a particular model.
-        magic_lgp_files_[next_model_to_process_].WriteFile(
-          output_dir_ + "temp/spell_models/" + fname
-        );
+        magic_lgp_files_[next_model_to_process_].WriteFile(output_dir_ + "temp/spell_models/" + fname);
         TexFile tex(output_dir_ + "temp/spell_models/" + fname);
         std::string path = output_dir_ + "models/battle/attacks/" + id + ".png";
         // Many of these tex files are wrong and dummied out in the OG, so try/catch.
@@ -425,9 +405,7 @@ unsigned int BattleDataInstaller::ProcessSpellModel(){
     }
     if (ext == "a00"){
         // Animation file.
-        magic_lgp_files_[next_model_to_process_].WriteFile(
-          output_dir_ + "temp/spell_models/" + fname
-        );
+        magic_lgp_files_[next_model_to_process_].WriteFile(output_dir_ + "temp/spell_models/" + fname);
         bool found = false;
         for (int m = 0; m < spell_models_.size(); m ++){
             if (spell_models_[m].id == id){
@@ -485,25 +463,19 @@ unsigned int BattleDataInstaller::ConvertBattleModel(){
 
         if ("" != model.anim){ // Skip for non-animated, ie battle backgrounds
             DaFile da(File(output_dir_ + "temp/battle_models/" + model.anim));
-            std::vector<std::string> a_files = da.GenerateAFiles(
-              model.id, output_dir_ + "temp/battle_models/"
-            );
+            std::vector<std::string> a_files = da.GenerateAFiles(model.id, output_dir_ + "temp/battle_models/");
             for (std::string file_name : a_files) model.a.push_back(file_name);
         }
 
         if ("" != model.script){ // Skip non scripted models
             AbFile ab(File(output_dir_ + "temp/battle_models/" + model.script), info.is_enemy);
-            ab.GenerateScripts(
-                model.id + "_" + info.name_normal, output_dir_ + "scripts/battle_models/"
-            );
+            ab.GenerateScripts(model.id + "_" + info.name_normal, output_dir_ + "scripts/battle_models/");
         }
 
         // Reload the resources for the newly created hrc and rsd.
         // TODO: Could this be done with res_mgr_->declareResource() ? Probably faster
         res_mgr_->removeResourceLocation(output_dir_ + "temp/battle_models/", "FFVII");
-        res_mgr_->addResourceLocation(
-          output_dir_ + "temp/battle_models/", "FileSystem", "FFVII", true, true
-        );
+        res_mgr_->addResourceLocation(output_dir_ + "temp/battle_models/", "FileSystem", "FFVII", true, true);
 
         Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(
           model.id + "_" + info.name_normal + ".hrc", "FFVII"
@@ -517,8 +489,7 @@ unsigned int BattleDataInstaller::ConvertBattleModel(){
             VGears::AFileManager &afl_mgr(VGears::AFileManager::GetSingleton());
             Ogre::String a_base_name;
             VGears::StringUtil::splitBase(anim, a_base_name);
-            VGears::AFilePtr a
-              = afl_mgr.load(a_base_name + ".a", "FFVII").staticCast<VGears::AFile>();
+            VGears::AFilePtr a = afl_mgr.load(a_base_name + ".a", "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));
@@ -526,12 +497,10 @@ unsigned int BattleDataInstaller::ConvertBattleModel(){
         ExportMesh(path, mesh);
     }
     catch (const Ogre::Exception& ex){
-        std::cerr << "[ERROR] Ogre exception converting battle model "
-          << model.hrc <<": " << ex.what() << std::endl;
+        std::cerr << "[ERROR] Ogre exception converting battle model " << model.hrc <<": " << ex.what() << std::endl;
     }
     catch (const std::exception& ex){
-        std::cerr << "[ERROR] Exception converting battle model "
-          << model.hrc << ": " << ex.what() << std::endl;
+        std::cerr << "[ERROR] Exception converting battle model " << model.hrc << ": " << ex.what() << std::endl;
     }
     next_model_to_convert_ ++;
     return next_model_to_convert_;
@@ -559,25 +528,19 @@ unsigned int BattleDataInstaller::ConvertSpellModel(){
         // Decompile the .a00 file (if any) into a .a file.
         if ("" != model.anim){ // Skip for non-animated.
             DaFile da(File(output_dir_ + "temp/spell_models/" + model.anim));
-            std::vector<std::string> a_files = da.GenerateAFiles(
-              model.id, output_dir_ + "temp/spell_models/"
-            );
+            std::vector<std::string> a_files = da.GenerateAFiles(model.id, output_dir_ + "temp/spell_models/");
             for (std::string file_name : a_files) model.a.push_back(file_name);
         }
 
         // Reload the resources for the newly created hrc and rsd.
         // TODO: Could this be done with res_mgr_->declareResource() ? Probably faster
         res_mgr_->removeResourceLocation(output_dir_ + "temp/spell_models/", "FFVII");
-        res_mgr_->addResourceLocation(
-          output_dir_ + "temp/spell_models/", "FileSystem", "FFVII", true, true
-        );
+        res_mgr_->addResourceLocation(output_dir_ + "temp/spell_models/", "FileSystem", "FFVII", true, true);
 
         // Export meshes thet have an .hrc skeleton.
         if (model.hrc != ""){
             std::cout << "[EXPORT MESH] " << model.hrc << std::endl;
-            Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(
-              model.id + ".hrc", "FFVII"
-            );
+            Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(model.id + ".hrc", "FFVII");
             Ogre::String base_name;
             VGears::StringUtil::splitBase(model.hrc, base_name);
             auto mesh_name = model.id + ".mesh";
@@ -587,8 +550,7 @@ unsigned int BattleDataInstaller::ConvertSpellModel(){
                 VGears::AFileManager &afl_mgr(VGears::AFileManager::GetSingleton());
                 Ogre::String a_base_name;
                 VGears::StringUtil::splitBase(anim, a_base_name);
-                VGears::AFilePtr a
-                  = afl_mgr.load(a_base_name + ".a", "FFVII").staticCast<VGears::AFile>();
+                VGears::AFilePtr a = afl_mgr.load(a_base_name + ".a", "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));
@@ -597,12 +559,10 @@ unsigned int BattleDataInstaller::ConvertSpellModel(){
         }
     }
     catch (const Ogre::Exception& ex){
-        std::cerr << "[ERROR] Ogre exception converting spell model "
-          << model.hrc <<": " << ex.what() << std::endl;
+        std::cerr << "[ERROR] Ogre exception converting spell model " << model.hrc <<": " << ex.what() << std::endl;
     }
     catch (const std::exception& ex){
-        std::cerr << "[ERROR] Exception converting spell model "
-          << model.hrc << ": " << ex.what() << std::endl;
+        std::cerr << "[ERROR] Exception converting spell model " << model.hrc << ": " << ex.what() << std::endl;
     }
     next_model_to_convert_ ++;
     return next_model_to_convert_;
@@ -977,9 +937,7 @@ std::string BattleDataInstaller::BuildAttackFileName(Attack attack){
           (attack.name[n] >= '0' && attack.name[n] <= '9')
           || (attack.name[n] >= 'a' && attack.name[n] <= 'z')
           || (attack.name[n] >= 'A' && attack.name[n] <= 'Z')
-        ){
-            file_name += attack.name[n];
-        }
+        ) file_name += attack.name[n];
     }
     file_name += ".xml";
     return file_name;
@@ -1005,26 +963,22 @@ File* BattleDataInstaller::ExtractGZipScene(File file){
         switch (ret){
             case Z_MEM_ERROR:
                 inflateEnd(&strm);
-                std::cout << "Warning: inflateInit2 - Z_MEM_ERROR in file "
-                  << file.GetFileName() << std::endl;
+                std::cout << "Warning: inflateInit2 - Z_MEM_ERROR in file " << file.GetFileName() << std::endl;
                 break;
             case Z_STREAM_ERROR:
                 inflateEnd(&strm);
-                std::cout << "Warning: inflateInit2 - Z_STREAM_ERROR in file "
-                  << file.GetFileName() << std::endl;
+                std::cout << "Warning: inflateInit2 - Z_STREAM_ERROR in file " << file.GetFileName() << std::endl;
                 break;
             default:
                 inflateEnd(&strm);
-                std::cout << "Warning: inflateInit2 - unknown error in file "
-                  << file.GetFileName() << std::endl;
+                std::cout << "Warning: inflateInit2 - unknown error in file " << file.GetFileName() << std::endl;
         }
         return NULL;
     }
     do{
         if (strm.next_out == NULL){
             inflateEnd(&strm);
-            std::cout << "Warning: strm.next_out == NULL in file "
-              << file.GetFileName() << std::endl;
+            std::cout << "Warning: strm.next_out == NULL in file " << file.GetFileName() << std::endl;
             return NULL;
         }
         ret = inflate(&strm, Z_NO_FLUSH);
@@ -1032,18 +986,15 @@ File* BattleDataInstaller::ExtractGZipScene(File file){
         switch (ret){
             case Z_NEED_DICT:
                 inflateEnd(&strm);
-                std::cout << "Warning: inflate - Z_NEED_DICT in file "
-                  << file.GetFileName() << std::endl;
+                std::cout << "Warning: inflate - Z_NEED_DICT in file " << file.GetFileName() << std::endl;
                 return NULL;
             case Z_DATA_ERROR:
                 inflateEnd(&strm);
-                std::cout << "Warning: inflate - Z_DATA_ERROR in file "
-                  << file.GetFileName() << std::endl;
+                std::cout << "Warning: inflate - Z_DATA_ERROR in file " << file.GetFileName() << std::endl;
                 return NULL;
             case Z_MEM_ERROR:
                 inflateEnd(&strm);
-                std::cout << "Warning: inflate - Z_MEM_ERROR in file "
-                  << file.GetFileName() << std::endl;
+                std::cout << "Warning: inflate - Z_MEM_ERROR in file " << file.GetFileName() << std::endl;
                 return NULL;
         }
         if (ret != Z_STREAM_END){
@@ -1079,18 +1030,14 @@ void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshP
       skeleton.get(), output_dir_ + "models/battle/" + outdir + base_mesh_name + ".skeleton"
     );
     mesh->setSkeletonName(outdir + base_mesh_name + ".skeleton");
-    mesh_serializer.exportMesh(
-      mesh.get(), output_dir_ + "models/battle/" + outdir + mesh->getName()
-    );
+    mesh_serializer.exportMesh(mesh.get(), output_dir_ + "models/battle/" + outdir + mesh->getName());
     Ogre::MaterialSerializer mat_ser;
     std::set<std::string> textures;
     Ogre::Mesh::SubMeshList sub_meshes = mesh->getSubMeshes();
     for (Ogre::SubMesh* sub_mesh : sub_meshes){
         // Change material name to avoid conflicts with field model materials.
         sub_mesh->setMaterialName(sub_mesh->getMaterialName());
-        Ogre::MaterialPtr mat(
-            Ogre::MaterialManager::getSingleton().getByName(sub_mesh->getMaterialName())
-        );
+        Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(sub_mesh->getMaterialName()));
         if (mat == nullptr) continue;
         for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){
             Ogre::Technique* tech = mat->getTechnique(techs);
@@ -1099,9 +1046,7 @@ void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshP
                 Ogre::Pass* pass = tech->getPass(pass_num);
                 if (pass == nullptr) continue;
                 for (
-                  size_t texture_unit_num = 0;
-                  texture_unit_num < pass->getNumTextureUnitStates();
-                  texture_unit_num ++
+                  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){
@@ -1119,9 +1064,7 @@ void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshP
             battle_materials_.push_back(sub_mesh->getMaterialName());
         }
     }
-    mat_ser.exportQueued(
-      output_dir_ + "models/battle/" + outdir + base_mesh_name + VGears::EXT_MATERIAL
-    );
+    mat_ser.exportQueued(output_dir_ + "models/battle/" + outdir + base_mesh_name + VGears::EXT_MATERIAL);
 }
 
 void BattleDataInstaller::GenerateRsdFiles(BattleModel model, std::string path){
@@ -1134,8 +1077,7 @@ void BattleDataInstaller::GenerateRsdFiles(BattleModel model, std::string path){
         content += "MAT=" + base_name + ".MAT\n";
         content += "GRP=" + base_name + ".GRP\n";
         content += "NTEX=" + std::to_string(model.tex.size()) + "\n";
-        for (int t = 0; t < model.tex.size(); t ++)
-            content += "TEX[" + std::to_string(t) + "]=" + model.tex[t] + "\n";
+        for (int t = 0; t < model.tex.size(); t ++) content += "TEX[" + std::to_string(t) + "]=" + model.tex[t] + "\n";
         content += "\n";
         std::ofstream out(path + base_name + ".rsd");
         out << content;
@@ -1236,8 +1178,7 @@ void BattleDataInstaller::DecompileSpellModelHrc(
         if (offset == 0) decompiled += "0\n\n";
         else {
             decompiled += std::to_string(offset) + " ";
-            if (p_index < model.p.size())
-                decompiled += model.p[p_index].substr(0, model.p[p_index].length() - 2);
+            if (p_index < model.p.size()) decompiled += model.p[p_index].substr(0, model.p[p_index].length() - 2);
             p_index ++;
             decompiled += "\n\n";
         }

+ 46 - 46
src/installer/BattleDataInstaller.h

@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include "DiskImage.h"
 #include "common/BinGZipFile.h"
 #include "common/TypeDefine.h"
 #include "data/BattleSceneFile.h"
@@ -34,13 +35,12 @@ class BattleDataInstaller{
         /**
          * Constructor
          *
-         * @param[in] input_dir Path to the directory containing the original data to parse.
+         * @param[in] disk_image Disk image to extract the data from.
          * @param[in] output_dir Path to the directory of the installation data.
          * @param[in] res_mgr The application resource manager..
          */
         BattleDataInstaller(
-          const std::string input_dir, const std::string output_dir,
-          Ogre::ResourceGroupManager* res_mgr
+          const DiskImage& disk_image, const std::string output_dir, Ogre::ResourceGroupManager* res_mgr
         );
 
         /**
@@ -83,8 +83,8 @@ class BattleDataInstaller{
         /**
          * Processes the next battle 3D model.
          *
-         * Saves .hrc, .a and .p files for later conversion. .tex files are converted directly to
-         * png.
+         * Saves .hrc, .a and .p files for later conversion. .tex files are
+         * converted directly to png.
          *
          * @return The total number of processed models.
          */
@@ -93,8 +93,8 @@ class BattleDataInstaller{
         /**
          * Processes the next spell 3D model.
          *
-         * Saves .hrc, .a and .p files for later conversion. .tex files are converted directly to
-         * png.
+         * Saves .hrc, .a and .p files for later conversion. .tex files are
+         * converted directly to png.
          *
          * @return The total number of processed models.
          */
@@ -165,21 +165,24 @@ class BattleDataInstaller{
             /**
              * Model identifier.
              *
-             * For battle models, a two letters string. For spell models, anything.
+             * For battle models, a two letters string. For spell models,
+             * anything.
              */
             std::string id;
 
             /**
              * Name of the model .hrc file.
              *
-             * For battle models, it will be the *aa file. For spell models, the *.d file.
+             * For battle models, it will be the *aa file. For spell models,
+             * the *.d file.
              */
             std::string hrc;
 
             /**
              * Name of the animation file.
              *
-             * For battle models, it will be the *da file. For spell models, the *.b file.
+             * For battle models, it will be the *da file. For spell models,
+             * the *.b file.
              */
             std::string anim;
 
@@ -305,17 +308,17 @@ class BattleDataInstaller{
          * Extract a gzipped file.
          *
          * @param[in] file Compressed file.
-         * @return Uncompressed file. NULL if errors were encountered (they will be logged to
-         * console).
+         * @return Uncompressed file. NULL if errors were encountered (they will
+         * be logged to console).
          */
         File* ExtractGZipScene(File file);
 
         /**
          * Constructs a file name for the XML file to save an enemy to.
          *
-         * The filename will be "dame/enemy/[ID]_[NAME].xml" where [ID] is the enemy ID, padded
-         * with '0' to a length of 4, and [NAME] is the enemy name using only alphanumeric
-         * characters.
+         * The filename will be "dame/enemy/[ID]_[NAME].xml" where [ID] is the
+         * enemy ID, padded with '0' to a length of 4, and [NAME] is the enemy
+         * name using only alphanumeric characters.
          *
          * @param[in] enemy The enemy to be saved.
          * @return The name for the enemy file.
@@ -325,9 +328,9 @@ class BattleDataInstaller{
         /**
          * Constructs a file name for the XML file to save an attack to.
          *
-         * The filename will be "dame/attack/[ID]_[NAME].xml" where [ID] is the attack ID, padded
-         * with '0' to a length of 4, and [NAME] is the attack name using only alphanumeric
-         * characters.
+         * The filename will be "dame/attack/[ID]_[NAME].xml" where [ID] is the
+         * attack ID, padded with '0' to a length of 4, and [NAME] is the attack
+         * name using only alphanumeric characters.
          *
          * @param[in] attack The attack to be saved.
          * @return The name for the attack file.
@@ -347,7 +350,8 @@ class BattleDataInstaller{
         /**
          * Generates all required .rsd models for a model.
          *
-         * To be used for enemy, playable character and scene models (anything in battle.lgp).
+         * To be used for enemy, playable character and scene models (anything
+         * in battle.lgp).
          *
          * @param[in] model Model information.
          * @param[in] path Output path.
@@ -367,59 +371,55 @@ class BattleDataInstaller{
         /**
          * Decompiles a compiled HRC file.
          *
-         * To be used for battle enemy and playable character models, since they have a
-         * well-defined bone hierarchy. For battle background models, use
-         * {@see DecompileSceneSceneHrc}, and for spell models, use {@see DecompileSpellModelHrc}.
-         * Note that this is not a general purpose HRC decompiler, it only works for files in
-         * battle.lgp.
+         * To be used for battle enemy and playable character models, since they
+         * have a well-defined bone hierarchy. For battle background models, use
+         * {@see DecompileSceneSceneHrc}, and for spell models, use
+         * {@see DecompileSpellModelHrc}. Note that this is not a general
+         * purpose HRC decompiler, it only works for files in battle.lgp.
          *
          * @param[in] compiled The compiles HRC file.
-         * @param[in] model The model, it can be a battle model or an spell model.
+         * @param[in] model The model, can be a battle model or an spell model.
          * @param[in] path Path to save the decompiled file to.
          * @param[in] skeleton_name Name for the skeleton.
          */
-        void DecompileBattleModelHrc(
-          File compiled, BattleModel model, std::string path, std::string skeleton_name
-        );
+        void DecompileBattleModelHrc(File compiled, BattleModel model, std::string path, std::string skeleton_name);
 
         /**
          * Decompiles a compiled HRC file.
          *
-         * To be used for spell models, since they have a well-defined bone hierarchy. For battle
-         * background models, use {@see DecompileSceneSceneHrc}, and for enemy and playable
-         * character models, use {@see DecompileBattleModelHrc}. Note that this is not a general
-         * purpose HRC decompiler, it only works for files in magic.lgp.
+         * To be used for spell models, since they have a well-defined bone
+         * hierarchy. For battle background models, use
+         * {@see DecompileSceneSceneHrc}, and for enemy and playable character
+         * models, use {@see DecompileBattleModelHrc}. Note that this is not a
+         * general purpose HRC decompiler, it only works for files in magic.lgp.
          *
          * @param[in] compiled The compiles HRC file.
-         * @param[in] model The model, it can be a battle model or an spell model.
+         * @param[in] model The model, can be a battle model or an spell model.
          * @param[in] path Path to save the decompiled file to.
          * @param[in] skeleton_name Name for the skeleton.
          */
-        void DecompileSpellModelHrc(
-          File compiled, SpellModel model, std::string path, std::string skeleton_name
-        );
+        void DecompileSpellModelHrc(File compiled, SpellModel model, std::string path, std::string skeleton_name);
 
         /**
          * Decompiles a compiled HRC file.
          *
-         * To be used only for battle background HRC files, since they don't have a defined bone
-         * hierarchy, and every bone is assumed to be a child of the previous one. For enemy and
-         * playable character models, use {@see DecompileBattleModelHrc}. Note that this is not a
-         * general purpose HRC decompiler, it only works for files in battle.lgp.
+         * To be used only for battle background HRC files, since they don't
+         * have a defined bone hierarchy, and every bone is assumed to be a
+         * child of the previous one. For enemy and playable character models,
+         * use {@see DecompileBattleModelHrc}. Note that this is not a general
+         * purpose HRC decompiler, it only works for files in battle.lgp.
          *
          * @param[in] compiled The compiles HRC file.
-         * @param[in] model The model, it can be a battle model or an spell model.
+         * @param[in] model The model, can be a battle model or an spell model.
          * @param[in] path Path to save the decompiled file to.
          * @param[in] skeleton_name Name for the skeleton.
          */
-        void DecompileSceneModelHrc(
-          File compiled, BattleModel model, std::string path, std::string skeleton_name
-        );
+        void DecompileSceneModelHrc(File compiled, BattleModel model, std::string path, std::string skeleton_name);
 
         /**
-         * The path to the directory from which to read the PC game data.
+         * The disk image to extract the data from.
          */
-        std::string input_dir_;
+        DiskImage disk_image_;
 
         /**
          * The path to the directory where to save the V-Gears data.

+ 4 - 2
src/installer/CMakeLists.txt

@@ -9,11 +9,11 @@ set(INSTALLER_SOURCE_FILES
     main.cpp
     BattleDataInstaller.cpp
     DataInstaller.cpp
+    DiskImage.cpp
     FieldDataInstaller.cpp
     FieldTextWriter.cpp
     KernelDataInstaller.cpp
     MainWindow.cpp
-    Release.cpp
     MediaDataInstaller.cpp
     ModelsAndAnimationsDb.cpp
     ScopedLgp.cpp
@@ -110,6 +110,8 @@ set(v-gears-installer_UIS MainWindow.ui)
 QT5_WRAP_UI(UIS ${v-gears-installer_UIS})
 set(v-gears-installer_MOCS MainWindow.h)
 QT5_WRAP_CPP(MOCS ${v-gears-installer_MOCS})
+set(v-gears-installer_RESOURCES installer_resources.qrc)
+qt5_add_resources(RCC_SOURCES ${v-gears-installer_RESOURCES})
 
 
 # Compiler options.
@@ -119,7 +121,7 @@ endif()
 
 
 # Generate v-gears-installer executable.
-add_executable (v-gears-installer ${INSTALLER_SOURCE_FILES} ${UIS} ${MOCS})
+add_executable (v-gears-installer ${INSTALLER_SOURCE_FILES} ${UIS} ${MOCS} ${RCC_SOURCES})
 SET_PROPERTY(TARGET v-gears-installer PROPERTY FOLDER "build/v-gears-installer")
 if (APPLE)
     target_link_libraries(v-gears-installer "-framework CoreFoundation -framework Cocoa -framework IOKit")

+ 43 - 44
src/installer/DataInstaller.cpp

@@ -16,11 +16,12 @@
 #include <QtCore/QDir>
 #include <boost/filesystem.hpp>
 #include "DataInstaller.h"
+#include "DiskImage.h"
 
 float DataInstaller::LINE_SCALE_FACTOR = 0.0078124970964f;
 
 DataInstaller::DataInstaller(
-  const std::string input_dir, const std::string output_dir, AdvancedOptions options,
+  const DiskImage& disk_image, const std::string output_dir, AdvancedOptions options,
   std::function<void(std::string, int, bool)> write_output_line
 )
 #ifdef _DEBUG
@@ -28,7 +29,7 @@ DataInstaller::DataInstaller(
 #else
   : application_("plugins.cfg", "resources.cfg", "install.log"),
 #endif
-  input_dir_(input_dir), output_dir_(output_dir),
+  disk_image_(disk_image), output_dir_(output_dir),
   options_(options), write_output_line_(write_output_line)
 {
     if (!application_.initOgre(true)) throw std::runtime_error("Ogre init failure");
@@ -63,25 +64,25 @@ float DataInstaller::Progress(){
             CreateDirectories();
             installation_state_ = INITIALIZE;
             return CalcProgress();
-        case INITIALIZE:
+        case INITIALIZE:{
             write_output_line_("Initializing installers...", 2, true);
-            kernel_installer_ = std::make_unique<KernelDataInstaller>(input_dir_);
+            const auto input_dir = disk_image_.getContentPath();
+            if (input_dir.empty()) throw std::runtime_error("Input data directory is empty");
+            kernel_installer_ = std::make_unique<KernelDataInstaller>(disk_image_);
             media_installer_ = std::make_unique<MediaDataInstaller>(
-              input_dir_, output_dir_, options_.keep_originals,
-              options_.no_ffmpeg, options_.no_timidity
-            );
-            field_installer_ = std::make_unique<FieldDataInstaller>(input_dir_, output_dir_);
-            battle_installer_ = std::make_unique<BattleDataInstaller>(
-              input_dir_, output_dir_, application_.ResMgr()
+              disk_image_, output_dir_, options_.keep_originals, options_.ffmpeg, options_.timidity
             );
+            field_installer_ = std::make_unique<FieldDataInstaller>(disk_image_, output_dir_);
+            battle_installer_ = std::make_unique<BattleDataInstaller>(disk_image_, output_dir_, application_.ResMgr());
             world_installer_ = std::make_unique<WorldInstaller>(
-              input_dir_, output_dir_, options_.keep_originals, application_.ResMgr()
+              disk_image_, output_dir_, options_.keep_originals, application_.ResMgr()
             );
             installation_state_ = BATTLE_SCENES_INIT;
             return CalcProgress();
+        }
         case BATTLE_SCENES_INIT:
             // Skip kernel data if option is set.
-            if (options_.skip_battle_data){
+            if (!options_.battle_data){
                 write_output_line_("Skipping battle data installation...", 2, true);
                 installation_state_ = BATTLE_MODELS_INIT;
                 return CalcProgress();
@@ -120,7 +121,7 @@ float DataInstaller::Progress(){
             return CalcProgress();
         case BATTLE_MODELS_INIT:
             // Skip kernel data if option is set.
-            if (options_.skip_battle_models){
+            if (!options_.battle_models){
                 write_output_line_("Skipping battle models installation...", 2, true);
                 installation_state_ = KERNEL_PRICES;
                 return CalcProgress();
@@ -174,7 +175,7 @@ float DataInstaller::Progress(){
             return CalcProgress();
         case KERNEL_PRICES:
             // Skip kernel data if option is set.
-            if (options_.skip_kernel){
+            if (!options_.kernel){
                 write_output_line_("Skipping kernel data installation...", 2, true);
                 installation_state_ = MEDIA_SOUNDS_INIT;
                 return CalcProgress();
@@ -257,7 +258,7 @@ float DataInstaller::Progress(){
             return CalcProgress();
         case MEDIA_IMAGES:
             // Skip images if option is set.
-            if (options_.skip_images){
+            if (!options_.images){
                 write_output_line_("Skipping images installation...", 2, true);
                 installation_state_ = MEDIA_SOUNDS_INIT;
                 return CalcProgress();
@@ -268,7 +269,7 @@ float DataInstaller::Progress(){
             return CalcProgress();
         case MEDIA_SOUNDS_INIT:
             // Skip sounds if option is set.
-            if (options_.skip_sounds){
+            if (!options_.sounds){
                 write_output_line_("Skipping sound effects installation...", 2, true);
                 installation_state_ = MEDIA_MUSICS_INIT;
                 return CalcProgress();
@@ -279,8 +280,7 @@ float DataInstaller::Progress(){
             cur_substep_ = 0;
             return CalcProgress();
         case MEDIA_SOUNDS:
-            if (media_installer_->InstallSounds() == true)
-                installation_state_ = MEDIA_SOUNDS_INDEX;
+            if (media_installer_->InstallSounds() == true) installation_state_ = MEDIA_SOUNDS_INDEX;
             else cur_substep_ ++;
             return CalcProgress();
         case MEDIA_SOUNDS_INDEX:
@@ -290,7 +290,7 @@ float DataInstaller::Progress(){
             return CalcProgress();
         case MEDIA_MUSICS_INIT:
             // Skip music if option is set.
-            if (options_.skip_music){
+            if (!options_.music){
                 write_output_line_("Skipping music tracks installation...", 2, true);
                 installation_state_ = FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT;
                 return CalcProgress();
@@ -301,8 +301,7 @@ float DataInstaller::Progress(){
             cur_substep_ = 0;
             return CalcProgress();
         case MEDIA_MUSICS:
-            if (media_installer_->InstallMusics() == true)
-                installation_state_ = MEDIA_MUSICS_HQ;
+            if (media_installer_->InstallMusics() == true) installation_state_ = MEDIA_MUSICS_HQ;
             else cur_substep_ ++;
             return CalcProgress();
         case MEDIA_MUSICS_HQ:
@@ -316,9 +315,20 @@ float DataInstaller::Progress(){
             media_installer_->WriteMusicsIndex();
             installation_state_ = FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT;
             return CalcProgress();
+        case MEDIA_VIDEOS:
+            // Skip videos if option is set.
+            if (!options_.videos){
+                write_output_line_("Skipping video installation...", 2, true);
+                installation_state_ = FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT;
+                return CalcProgress();
+            }
+            write_output_line_("Extracting videos...", 2, true);
+            media_installer_->InstallVideos();
+            installation_state_ = FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT;
+            return CalcProgress();
         case FIELD_SPAWN_POINTS_AND_SCALE_FACTORS_INIT:
             // Skip fields if option is set.
-            if (options_.skip_fields){
+            if (!options_.fields){
                 write_output_line_("Skipping field maps installation...", 2, true);
                 installation_state_ = WM_INIT;
                 return CalcProgress();
@@ -364,7 +374,7 @@ float DataInstaller::Progress(){
             return CalcProgress();
         case FIELD_CONVERT_MODELS_INIT:
             // Skip fields if option is set.
-            if (options_.skip_field_models){
+            if (!options_.field_models){
                 write_output_line_("Skipping field maps installation...", 2, true);
                 installation_state_ = WM_INIT;
                 return CalcProgress();
@@ -385,7 +395,7 @@ float DataInstaller::Progress(){
             return CalcProgress();
         case WM_INIT:
             // Skip world map if option set
-            if (options_.skip_wm){
+            if (!options_.wm){
                 write_output_line_("Skipping world map data installation...", 2, true);
                 installation_state_ = CLEAN;
                 return CalcProgress();
@@ -408,8 +418,7 @@ float DataInstaller::Progress(){
             }
             return CalcProgress();
         case WM_MODELS:
-            if (options_.skip_wm_models)
-                write_output_line_("Skipping world map model installation...", 2, true);
+            if (!options_.wm_models) write_output_line_("Skipping world map model installation...", 2, true);
             else world_installer_->ProcessModels();
             installation_state_ = CLEAN;
             return CalcProgress();
@@ -437,9 +446,7 @@ const float DataInstaller::CalcProgress(){
         if (installation_state_ > i) weight_done += step_weight_[i];
     }
 
-    if (substeps_ > 0 && cur_substep_ < substeps_){
-        weight_substeps = (cur_substep_ * weight) / substeps_;
-    }
+    if (substeps_ > 0 && cur_substep_ < substeps_) weight_substeps = (cur_substep_ * weight) / substeps_;
 
     float progress = 100 * (weight_done + weight_substeps) / weight_total;
     if (progress >= 100.0f) progress = 99.9f;
@@ -485,18 +492,10 @@ void DataInstaller::CreateDirectories(){
     CreateDir("world/1");
     CreateDir("world/2");
 
-    application_.ResMgr()->addResourceLocation(
-      output_dir_ + "temp/char/", "FileSystem", "FFVII", true, true
-    );
-    application_.ResMgr()->addResourceLocation(
-      output_dir_ + "temp/battle_models/", "FileSystem", "FFVII", true, true
-    );
-    application_.ResMgr()->addResourceLocation(
-      output_dir_ + "temp/world_models/", "FileSystem", "FFVII", true, true
-    );
-    application_.ResMgr()->addResourceLocation(
-      output_dir_ + "models/", "FileSystem", "FFVII", true, true
-    );
+    application_.ResMgr()->addResourceLocation(output_dir_ + "temp/char/", "FileSystem", "FFVII", true, true);
+    application_.ResMgr()->addResourceLocation(output_dir_ + "temp/battle_models/", "FileSystem", "FFVII", true, true);
+    application_.ResMgr()->addResourceLocation(output_dir_ + "temp/world_models/", "FileSystem", "FFVII", true, true);
+    application_.ResMgr()->addResourceLocation(output_dir_ + "models/", "FileSystem", "FFVII", true, true);
     application_.ResMgr()->addResourceLocation(
       output_dir_ + "models/fields/", "FileSystem", "FFVIITextures", true, true
     );
@@ -515,9 +514,9 @@ void DataInstaller::CreateDirectories(){
     application_.ResMgr()->addResourceLocation(
       output_dir_ + "models/world/", "FileSystem", "FFVIITextures", true, true
     );
-    fields_lgp_ = std::make_unique<ScopedLgp>(
-      application_.getRoot(), input_dir_ + "data/field/flevel.lgp", "LGP", "FFVIIFields"
-    );
+    const auto fields_lgp_path = disk_image_.filePath("data/field/flevel.lgp");
+    if (!fields_lgp_path) throw std::runtime_error("Missing required file: data/field/flevel.lgp");
+    fields_lgp_ = std::make_unique<ScopedLgp>(application_.getRoot(), *fields_lgp_path, "LGP", "FFVIIFields");
 }
 
 void DataInstaller::CleanInstall(){boost::filesystem::remove_all(output_dir_ + "data/temp/");}

+ 55 - 37
src/installer/DataInstaller.h

@@ -19,6 +19,7 @@
 #include <vector>
 #include <iostream>
 #include "common/VGearsApplication.h"
+#include "DiskImage.h"
 #include "FieldDataInstaller.h"
 #include "KernelDataInstaller.h"
 #include "MediaDataInstaller.h"
@@ -39,89 +40,100 @@ class DataInstaller{
         struct AdvancedOptions{
 
             /**
-             * Option to skip battle scenes installation.
+             * Option to install battle scenes.
              */
-            bool skip_battle_data;
+            bool battle_data;
 
             /**
-             * Option to skip battle models installation.
+             * Option to install battle models.
              */
-            bool skip_battle_models;
+            bool battle_models;
 
             /**
-             * Option to skip kernel data installation.
+             * Option to install kernel data.
              */
-            bool skip_kernel;
+            bool kernel;
 
             /**
-             * Option to skip image installation.
+             * Option to install images.
              */
-            bool skip_images;
+            bool images;
 
             /**
-             * Option to skip sound effects installation.
+             * Option to install sound effects.
              */
-            bool skip_sounds;
+            bool sounds;
 
             /**
-             * Option to skip music tracks installation.
+             * Option to install music tracks.
              */
-            bool skip_music;
+            bool music;
 
             /**
-             * Option to skip field maps installation.
+             * Option to install FMV videos.
              */
-            bool skip_fields;
+            bool videos;
 
             /**
-             * Option to skip field models installation.
+             * Option to enable calls to the ffmpeg executable.
              */
-            bool skip_field_models;
+            bool ffmpeg;
 
             /**
-             * Option to skip world map data installation.
+             * Option to enable calls to the timidity executable
              */
-            bool skip_wm;
+            bool timidity;
 
             /**
-             * Option to skip world map models installation.
+             * Option to install field maps.
              */
-            bool skip_wm_models;
+            bool fields;
 
             /**
-             * Option to keep original data after installation.
+             * Option to install field models.
              */
-            bool keep_originals;
+            bool field_models;
+
+            /**
+             * Option to install world map data.
+             */
+            bool wm;
+
+            /**
+             * Option to install world map models.
+             */
+            bool wm_models;
 
             /**
-             * Option to avoid calls to the ffmpeg executable.
+             * Option to keep original data after installation.
              */
-            bool no_ffmpeg;
+            bool keep_originals;
 
             /**
-             * Option to avoid calls to the timidity executable
+             * Option to perform a clean installation.
              */
-            bool no_timidity;
+            bool clean_install;
+
         };
 
         /**
          * 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.
+         * 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;
 
         /**
          * 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.
-         * @param[in] options Adavanced options for the installer.
+         * @param[in] disk_image Disk image with the original data to parse.
+         * @param[in] output_dir Path to the directory to write generated data.
+         * @param[in] options Advanced options for the installer.
          * @param[in] write_output_line Pointer to function to write output.
          */
         DataInstaller(
-          const std::string input_dir, const std::string output_dir, AdvancedOptions options,
+          const DiskImage& disk_image, const std::string output_dir, AdvancedOptions options,
           std::function<void(std::string, int, bool)> write_output_line
         );
 
@@ -149,7 +161,8 @@ class DataInstaller{
         /**
          * Creates a directory in the outputh path.
          *
-         * @param[in] dir Path of the directory to create, relative to the output 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);
@@ -366,6 +379,11 @@ class DataInstaller{
              */
             MEDIA_MUSICS_INDEX,
 
+            /**
+             * Copies the FMV videos.
+             */
+            MEDIA_VIDEOS,
+
             /**
              * Initializer for {@see SPAWN_POINTS_AND_SCALE_FACTORS}.
              */
@@ -470,9 +488,9 @@ class DataInstaller{
         std::vector<std::string> field_model_names_;
 
         /**
-         * The path to the directory from which to read the PC game data.
+         * The disk image from which to read the PC game data.
          */
-        std::string input_dir_;
+        DiskImage disk_image_;
 
         /**
          * The path to the directory where to save the V-Gears data.
@@ -527,8 +545,8 @@ class DataInstaller{
          *
          * @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.
+         * @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 msg, int level, bool progress)> write_output_line_;
 

+ 411 - 84
src/installer/Release.cpp → src/installer/DiskImage.cpp

@@ -19,45 +19,48 @@
 #include <regex>
 #include <archive.h>
 #include <archive_entry.h>
-#include "Release.h"
+#include "DiskImage.h"
 #include <QtCore/qstring.h>
 #include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
 #include <QtCore/qfileinfo.h>
 #include <QtCore/qglobal.h>
 #include <algorithm>
 #include <cctype>
+#include <cstdint>
 #include <cstdio>
 #include <fstream>
 #include <regex>
+#include <functional>
 #include <stdexcept>
 #include <sys/stat.h>
 #include <vector>
 
-Release::Release(){
-    id = "Unknown ISO file";
+DiskImage::DiskImage(){
+    id = "Unknown image file";
     platform = PLATFORM_UNKNOWN;
     region = REGION_UNKNOWN;
     language = LANGUAGE_UNKNOWN;
     disk = DISK_UNKNOWN;
     valid = false;
     supported = false;
-    error_message = "The selected file is not a valid Final Fantasy VII ISO file.";
+    error_message = "The selected file is not a valid Final Fantasy VII image file.";
     warning_message = "";
+    is_extracted = false;
 };
 
-Release::Release(std::string iso_path) : Release() {
+DiskImage::DiskImage(std::string path) : DiskImage() {
     struct archive* a = archive_read_new();
     struct archive_entry* entry;
     archive_read_support_format_iso9660(a);
     archive_read_support_format_all(a);
     archive_read_support_filter_all(a);
 
-    if (archive_read_open_filename(a, iso_path.c_str(), 2048) != ARCHIVE_OK) {
+    if (archive_read_open_filename(a, path.c_str(), 2048) != ARCHIVE_OK) {
         const char* archive_error = archive_error_string(a);
         const std::string open_error = archive_error ? archive_error : "Unknown archive error";
-        // Error reading iso
-        std::cerr << "Failed to read ISO file: " << open_error << std::endl;
-        error_message = "Failed to read ISO file: " + open_error;
+        // Error reading image
+        error_message = "Failed to read image file: " + open_error;
         archive_read_free(a);
         return;
     }
@@ -69,7 +72,7 @@ Release::Release(std::string iso_path) : Release() {
     };
 
     // Normalize a path from the ISO9660 filesystem, removing version suffixes and trailing dots.
-    auto normalize_iso_path = [&](std::string value) {
+    auto normalize_path = [&](std::string value) {
         std::replace(value.begin(), value.end(), '\\', '/');
         while (
           !value.empty()
@@ -112,7 +115,7 @@ Release::Release(std::string iso_path) : Release() {
           normalized == "FF7CONFIG.EXE" || normalized == "FF7CONFI.EXE"
           || normalized == "FF7CON~1.EXE" || normalized == "FF7INST.EXE"
         ) {
-            this->iso_path = iso_path;
+            this->path = path;
             id = "Final Fantasy VII (PC, 1998, USA) Install Disk";
             platform = PLATFORM_PC;
             region = REGION_NORTH_AMERICA;
@@ -125,7 +128,7 @@ Release::Release(std::string iso_path) : Release() {
             return true;
         }
         if (normalized == "FF7/MOVIES/BIKE.AVI") {
-            this->iso_path = iso_path;
+            this->path = path;
             id = "Final Fantasy VII (PC, 1998, USA) Disk 1";
             platform = PLATFORM_PC;
             region = REGION_NORTH_AMERICA;
@@ -138,7 +141,7 @@ Release::Release(std::string iso_path) : Release() {
             return true;
         }
         if (normalized == "FF7/MOVIES/BIGLIGHT.AVI") {
-            this->iso_path = iso_path;
+            this->path = path;
             id = "Final Fantasy VII (PC, 1998, USA) Disk 2";
             platform = PLATFORM_PC;
             region = REGION_NORTH_AMERICA;
@@ -151,7 +154,7 @@ Release::Release(std::string iso_path) : Release() {
             return true;
         }
         if (normalized == "FF7/MOVIES/ENDING1.AVI") {
-            this->iso_path = iso_path;
+            this->path = path;
             id = "Final Fantasy VII (PC, 1998, USA) Disk 3";
             platform = PLATFORM_PC;
             region = REGION_NORTH_AMERICA;
@@ -170,10 +173,8 @@ Release::Release(std::string iso_path) : Release() {
     int r = ARCHIVE_OK;
     bool saw_header = false;
     while (true) {
-        std::cout << "Reading next header from ISO..." << std::endl;
         r = archive_read_next_header(a, &entry);
         if (r == ARCHIVE_EOF) {
-            std::cout << "Reached end of ISO archive." << std::endl;
             break;
         }
         if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
@@ -187,36 +188,31 @@ Release::Release(std::string iso_path) : Release() {
         saw_header = true;
 
         std::string path = archive_entry_pathname(entry);
-        std::cout << "Found file in ISO: " << path << std::endl;
 
         // Look for know files in the PC release disks.
-        const std::string normalized = normalize_iso_path(path);
+        const std::string normalized = normalize_path(path);
         if (apply_pc_release_from_path(normalized)) {
             pc_marker_found = true;
             break;
         }
-        std::cout << "Finished checking PC releases." << std::endl;
 
-        // If we haven't found the PC install marker, look for SYSTEM.CNF
+        // If the PC install marker hasn't been found, look for SYSTEM.CNF
         // to determine if it's a PSX release and extract the game ID.
         std::string upper_path = to_upper(path);
         if (
           upper_path == "SYSTEM.CNF" || upper_path == "/SYSTEM.CNF"
           || upper_path.find("SYSTEM.CNF;") != std::string::npos
         ) {
-            std::cout << "SYSTEM.CNF FOUND." << std::endl;
             size_t size = archive_entry_size(entry);
             if (size > 0) {
                 std::vector<char> buffer(size);
                 la_ssize_t bytes_read = archive_read_data(a, buffer.data(), size);
                 if (bytes_read > 0) {
                     std::string cnf_content(buffer.begin(), buffer.begin() + bytes_read);
-                    std::cout << "SYSTEM.CNF content:\n" << cnf_content << std::endl;
                     std::regex code_regex(R"(([A-Z]{4})_(\d{3})\.(\d{2}))");
                     std::smatch match;
                     if (std::regex_search(cnf_content, match, code_regex)) {
                         game_id = match[1].str() + "-" + match[2].str() + match[3].str();
-                        std::cout << "Detected game ID from SYSTEM.CNF: " << game_id << std::endl;
                         if (game_id == "SCUS-94163") {
                             id = "Final Fantasy VII (USA) Disc 1";
                             platform = PLATFORM_PS1;
@@ -446,7 +442,7 @@ Release::Release(std::string iso_path) : Release() {
                             "Use the PC release instead.";
                         }
                         else {
-                            id = "Unknown Release";
+                            id = "Unknown DiskImage";
                             platform = PLATFORM_UNKNOWN;
                             region = REGION_UNKNOWN;
                             language = LANGUAGE_UNKNOWN;
@@ -463,65 +459,105 @@ Release::Release(std::string iso_path) : Release() {
     }
 
     if (!saw_header && r == ARCHIVE_EOF) {
-        std::cout << "No entries found by libarchive, trying isoinfo listing..." << std::endl;
-
-        auto escape_shell = [](const std::string& value) {
-            std::string out;
-            for (char c : value) {
-                if (c == '"' || c == '\\') {
-                    out.push_back('\\');
-                }
-                out.push_back(c);
-            }
-            return out;
-        };
-
-        const std::string escaped_iso_path = escape_shell(iso_path);
-        const std::vector<std::string> list_cmds = {
-            "isoinfo -J -f -i \"" + escaped_iso_path + "\" 2>/dev/null",
-            "isoinfo -R -J -f -i \"" + escaped_iso_path + "\" 2>/dev/null",
-            "isoinfo -R -f -i \"" + escaped_iso_path + "\" 2>/dev/null",
-            "isoinfo -f -i \"" + escaped_iso_path + "\" 2>/dev/null"
+        //std::cout << "libarchive returned no entries, falling back to direct ISO9660 parsing..." << std::endl;
+        auto read_le32 = [](const uint8_t* bytes) -> uint32_t {
+            return static_cast<uint32_t>(bytes[0])
+                | (static_cast<uint32_t>(bytes[1]) << 8)
+                | (static_cast<uint32_t>(bytes[2]) << 16)
+                | (static_cast<uint32_t>(bytes[3]) << 24);
         };
 
-        char buffer[4096];
-        for (const auto& list_cmd : list_cmds) {
-            FILE* list_pipe = popen(list_cmd.c_str(), "r");
-            if (!list_pipe) {
-                continue;
-            }
+        std::ifstream file(path, std::ios::binary);
+        if (file) {
+            const std::size_t sector_size = 2048;
+
+            auto read_bytes = [&](std::streamoff offset, std::size_t size) {
+                std::vector<uint8_t> bytes(size);
+                file.seekg(offset, std::ios::beg);
+                file.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(size));
+                const std::size_t bytes_read = static_cast<std::size_t>(file.gcount());
+                bytes.resize(bytes_read);
+                return bytes;
+            };
+
+            const std::vector<uint8_t> pvd = read_bytes(static_cast<std::streamoff>(16) * sector_size, sector_size);
+            const bool pvd_valid =
+                pvd.size() == sector_size
+                && pvd[0] == 1
+                && pvd[1] == 'C' && pvd[2] == 'D'
+                && pvd[3] == '0' && pvd[4] == '0' && pvd[5] == '1';
+
+            if (pvd_valid) {
+                const uint32_t root_lba = read_le32(&pvd[156 + 2]);
+                const uint32_t root_size = read_le32(&pvd[156 + 10]);
+
+                std::function<void(uint32_t, uint32_t, const std::string&)> walk_directory;
+                walk_directory = [&](uint32_t extent_lba, uint32_t extent_size, const std::string& prefix) {
+                    if (pc_marker_found || extent_size == 0) {
+                        return;
+                    }
 
-            while (fgets(buffer, sizeof(buffer), list_pipe)) {
-                std::string line(buffer);
-                while (!line.empty() && (line.back() == '\n' || line.back() == '\r')) {
-                    line.pop_back();
-                }
-                if (line.empty()) {
-                    continue;
-                }
-                if (apply_pc_release_from_path(normalize_iso_path(line))) {
-                    pc_marker_found = true;
-                    break;
-                }
-            }
+                    const std::vector<uint8_t> dir_data = read_bytes(
+                        static_cast<std::streamoff>(extent_lba) * sector_size,
+                        extent_size
+                    );
+
+                    std::size_t offset = 0;
+                    while (offset < dir_data.size()) {
+                        const uint8_t record_size = dir_data[offset];
+                        if (record_size == 0) {
+                            offset = ((offset / sector_size) + 1) * sector_size;
+                            continue;
+                        }
+                        if (offset + record_size > dir_data.size() || offset + 33 > dir_data.size()) {
+                            break;
+                        }
 
-            pclose(list_pipe);
-            if (pc_marker_found) {
-                break;
+                        const uint32_t entry_lba = read_le32(&dir_data[offset + 2]);
+                        const uint32_t entry_size = read_le32(&dir_data[offset + 10]);
+                        const uint8_t entry_flags = dir_data[offset + 25];
+                        const uint8_t entry_name_len = dir_data[offset + 32];
+
+                        if (entry_name_len > 0 && offset + 33 + entry_name_len <= dir_data.size()) {
+                            const bool is_dot_entry =
+                                entry_name_len == 1
+                                && (dir_data[offset + 33] == 0x00 || dir_data[offset + 33] == 0x01);
+
+                            if (!is_dot_entry) {
+                                const std::string raw_name(
+                                    reinterpret_cast<const char*>(&dir_data[offset + 33]),
+                                    reinterpret_cast<const char*>(&dir_data[offset + 33 + entry_name_len])
+                                );
+                                const std::string normalized_name = normalize_path(raw_name);
+                                const std::string full_path
+                                  = prefix.empty() ? normalized_name : prefix + "/" + normalized_name;
+                                if (apply_pc_release_from_path(full_path)) {
+                                    pc_marker_found = true;
+                                    return;
+                                }
+                                if ((entry_flags & 0x02) != 0) {
+                                    walk_directory(entry_lba, entry_size, full_path);
+                                    if (pc_marker_found) {
+                                        return;
+                                    }
+                                }
+                            }
+                        }
+                        offset += record_size;
+                    }
+                };
+                walk_directory(root_lba, root_size, "");
             }
         }
     }
-
-    std::cout << "Finished checking releases." << std::endl;
-
     archive_read_close(a);
     archive_read_free(a);
     return;
 };
 
-Release::~Release(){};
+DiskImage::~DiskImage(){};
 
-bool Release::extractIso(std::string installation_path){
+bool DiskImage::extractImage(std::string installation_path){
     struct archive* a;
     struct archive* ext;
     struct archive_entry* entry;
@@ -562,14 +598,16 @@ bool Release::extractIso(std::string installation_path){
         }
     }
 
-    // Open the ISO file
-    if ((r = archive_read_open_filename(a, iso_path.c_str(), 10240))) {
-        std::cerr << "Error: Could not open ISO file: " << archive_error_string(a) << std::endl;
+    // Open the image file
+    if ((r = archive_read_open_filename(a, path.c_str(), 10240))) {
+        std::cerr << "Error: Could not open image file: " << archive_error_string(a) << std::endl;
         return false;
     }
 
     // Read through the archive entries
+    bool saw_header = false;
     while ((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK || r == ARCHIVE_WARN) {
+        saw_header = true;
         if (r == ARCHIVE_WARN) {
             std::cerr << "archive_read_next_header warning during extraction: " << archive_error_string(a) << std::endl;
         }
@@ -594,7 +632,7 @@ bool Release::extractIso(std::string installation_path){
             std::cerr << "Warning (Header): " << archive_error_string(ext) << std::endl;
         }
         else if (archive_entry_size(entry) > 0) {
-            // Copy data from the ISO to the disk
+            // Copy data from the image to the disk
             const void* buff;
             size_t size;
             la_int64_t offset;
@@ -615,6 +653,177 @@ bool Release::extractIso(std::string installation_path){
         }
         archive_write_finish_entry(ext);
     }
+
+    if (!saw_header && r == ARCHIVE_EOF) {
+        archive_read_close(a);
+        archive_read_free(a);
+        archive_write_close(ext);
+        archive_write_free(ext);
+
+        auto read_le32 = [](const uint8_t* bytes) -> uint32_t {
+            return static_cast<uint32_t>(bytes[0])
+                | (static_cast<uint32_t>(bytes[1]) << 8)
+                | (static_cast<uint32_t>(bytes[2]) << 16)
+                | (static_cast<uint32_t>(bytes[3]) << 24);
+        };
+
+        auto normalize_image_name = [](std::string value) {
+            std::replace(value.begin(), value.end(), '\\', '/');
+            std::size_t semi = value.find(';');
+            if (semi != std::string::npos) {
+                bool version_suffix = true;
+                for (std::size_t i = semi + 1; i < value.size(); ++i) {
+                    if (!std::isdigit(static_cast<unsigned char>(value[i]))) {
+                        version_suffix = false;
+                        break;
+                    }
+                }
+                if (version_suffix) {
+                    value = value.substr(0, semi);
+                }
+            }
+            while (!value.empty() && value.back() == '.') {
+                value.pop_back();
+            }
+            return value;
+        };
+
+        std::ifstream image_file(path, std::ios::binary);
+        if (!image_file) {
+            error_message = "Failed to open image file for direct ISO9660 extraction.";
+            return false;
+        }
+
+        const std::size_t sector_size = 2048;
+        bool extraction_ok = true;
+
+        auto read_bytes = [&](std::streamoff offset, std::size_t size) {
+            std::vector<uint8_t> bytes(size);
+            image_file.seekg(offset, std::ios::beg);
+            image_file.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(size));
+            const std::size_t bytes_read = static_cast<std::size_t>(image_file.gcount());
+            bytes.resize(bytes_read);
+            return bytes;
+        };
+
+        const std::vector<uint8_t> pvd = read_bytes(static_cast<std::streamoff>(16) * sector_size, sector_size);
+        const bool pvd_valid =
+            pvd.size() == sector_size
+            && pvd[0] == 1
+            && pvd[1] == 'C' && pvd[2] == 'D'
+            && pvd[3] == '0' && pvd[4] == '0' && pvd[5] == '1';
+
+        if (!pvd_valid) {
+            error_message = "Direct ISO9660 fallback failed: invalid primary volume descriptor.";
+            return false;
+        }
+
+        const uint32_t root_lba = read_le32(&pvd[156 + 2]);
+        const uint32_t root_size = read_le32(&pvd[156 + 10]);
+
+        std::function<void(uint32_t, uint32_t, const std::string&)> extract_directory;
+        extract_directory = [&](uint32_t extent_lba, uint32_t extent_size, const std::string& prefix) {
+            if (!extraction_ok || extent_size == 0) {
+                return;
+            }
+
+            const std::vector<uint8_t> dir_data = read_bytes(
+                static_cast<std::streamoff>(extent_lba) * sector_size,
+                extent_size
+            );
+            if (dir_data.empty() && extent_size > 0) {
+                extraction_ok = false;
+                return;
+            }
+
+            std::size_t offset = 0;
+            while (offset < dir_data.size()) {
+                const uint8_t record_size = dir_data[offset];
+                if (record_size == 0) {
+                    offset = ((offset / sector_size) + 1) * sector_size;
+                    continue;
+                }
+                if (offset + record_size > dir_data.size() || offset + 33 > dir_data.size()) {
+                    break;
+                }
+
+                const uint32_t entry_lba = read_le32(&dir_data[offset + 2]);
+                const uint32_t entry_size = read_le32(&dir_data[offset + 10]);
+                const uint8_t entry_flags = dir_data[offset + 25];
+                const uint8_t entry_name_len = dir_data[offset + 32];
+
+                if (entry_name_len > 0 && offset + 33 + entry_name_len <= dir_data.size()) {
+                    const bool is_dot_entry =
+                        entry_name_len == 1
+                        && (dir_data[offset + 33] == 0x00 || dir_data[offset + 33] == 0x01);
+                    if (!is_dot_entry) {
+                        std::string raw_name(
+                            reinterpret_cast<const char*>(&dir_data[offset + 33]),
+                            reinterpret_cast<const char*>(&dir_data[offset + 33 + entry_name_len])
+                        );
+                        raw_name = normalize_image_name(raw_name);
+                        const std::string relative_path = prefix.empty() ? raw_name : prefix + "/" + raw_name;
+                        const std::string absolute_path = data_dir + "/" + relative_path;
+
+                        if ((entry_flags & 0x02) != 0) {
+                            QDir mkdir_dir;
+                            if (!mkdir_dir.mkpath(QString::fromStdString(absolute_path))) {
+                                extraction_ok = false;
+                                return;
+                            }
+                            extract_directory(entry_lba, entry_size, relative_path);
+                            if (!extraction_ok) {
+                                return;
+                            }
+                        }
+                        else {
+                            QString abs_qpath = QString::fromStdString(absolute_path);
+                            QString abs_parent = QFileInfo(abs_qpath).absolutePath();
+                            QDir parent_dir(abs_parent);
+                            if (!parent_dir.exists() && !parent_dir.mkpath(".")) {
+                                extraction_ok = false;
+                                return;
+                            }
+
+                            std::ofstream out_file(absolute_path, std::ios::binary);
+                            if (!out_file) {
+                                extraction_ok = false;
+                                return;
+                            }
+
+                            const std::streamoff file_offset = static_cast<std::streamoff>(entry_lba) * sector_size;
+                            image_file.seekg(file_offset, std::ios::beg);
+                            std::vector<char> chunk(64 * 1024);
+                            uint32_t remaining = entry_size;
+                            while (remaining > 0) {
+                                const std::size_t to_read = std::min<std::size_t>(chunk.size(), remaining);
+                                image_file.read(chunk.data(), static_cast<std::streamsize>(to_read));
+                                const std::streamsize got = image_file.gcount();
+                                if (got <= 0) {
+                                    extraction_ok = false;
+                                    return;
+                                }
+                                out_file.write(chunk.data(), got);
+                                remaining -= static_cast<uint32_t>(got);
+                            }
+                        }
+                    }
+                }
+                offset += record_size;
+            }
+        };
+
+        extract_directory(root_lba, root_size, "");
+        if (!extraction_ok) {
+            error_message = "Direct ISO9660 fallback failed while extracting files.";
+            return false;
+        }
+
+        content_path = data_dir;
+        is_extracted = true;
+        return true;
+    }
+
     if (r != ARCHIVE_EOF && r != ARCHIVE_OK && r != ARCHIVE_WARN) {
         std::cerr << "archive_read_next_header error during extraction: " << archive_error_string(a) << " (code=" << r << ")" << std::endl;
         // continue cleanup and return failure
@@ -632,45 +841,163 @@ bool Release::extractIso(std::string installation_path){
     archive_write_free(ext);
 
     content_path = data_dir;
+    is_extracted = true;
     return true;
 }
 
-const std::string Release::getId(){
+std::unique_ptr<std::string> DiskImage::filePath(std::string file_path) {
+    if (!is_extracted) {
+        std::cerr << "Error: Attempted to check for file existence before extracting the disk image." << std::endl;
+        return nullptr;
+    }
+    auto normalize_segment = [](QString segment) {
+        segment = segment.trimmed();
+        while (!segment.isEmpty() && (segment.endsWith('.') || segment.endsWith(' '))) {
+            segment.chop(1);
+        }
+        int semi = segment.indexOf(';');
+        if (semi >= 0) {
+            bool numeric_suffix = true;
+            for (int i = semi + 1; i < segment.size(); ++i) {
+                if (!segment.at(i).isDigit()) {
+                    numeric_suffix = false;
+                    break;
+                }
+            }
+            if (numeric_suffix) {
+                segment = segment.left(semi);
+            }
+        }
+        return segment.toUpper();
+    };
+    auto split_segments = [&](const QString& path) {
+        QString normalized = path;
+        normalized.replace('\\', '/');
+        while (normalized.startsWith('/')) {
+            normalized.remove(0, 1);
+        }
+        normalized = QDir::cleanPath(normalized);
+        QStringList parts = normalized.split('/', Qt::SkipEmptyParts);
+        QStringList out;
+        for (const QString& part : parts) {
+            if (part == ".") {
+                continue;
+            }
+            out.push_back(normalize_segment(part));
+        }
+        return out;
+    };
+    auto split_base_ext = [](const QString& name) {
+        const int dot = name.lastIndexOf('.');
+        if (dot <= 0 || dot == name.size() - 1) {
+            return qMakePair(name, QString());
+        }
+        return qMakePair(name.left(dot), name.mid(dot + 1));
+    };
+    auto segment_matches = [&](const QString& requested, const QString& actual) {
+        if (requested == actual) {
+            return true;
+        }
+
+        const auto req = split_base_ext(requested);
+        const auto act = split_base_ext(actual);
+        if (req.second != act.second) {
+            return false;
+        }
+
+        QString req_base = req.first;
+        QString act_base = act.first;
+
+        const int req_tilde = req_base.indexOf('~');
+        const int act_tilde = act_base.indexOf('~');
+        if (req_tilde >= 0) {
+            req_base = req_base.left(req_tilde);
+        }
+        if (act_tilde >= 0) {
+            act_base = act_base.left(act_tilde);
+        }
+
+        if (req_base.isEmpty() || act_base.isEmpty()) {
+            return false;
+        }
+
+        return req_base.startsWith(act_base) || act_base.startsWith(req_base);
+    };
+    const QString base_dir = QDir::cleanPath(QString::fromStdString(content_path).replace('\\', '/'));
+    const QString requested_path = QString::fromStdString(file_path);
+    const QString direct_candidate = QDir(base_dir).filePath(requested_path);
+    QFileInfo direct_file(direct_candidate);
+    if (direct_file.exists() && direct_file.isFile()) {
+        return std::make_unique<std::string>(direct_file.absoluteFilePath().toStdString());
+    }
+    const QStringList requested_segments = split_segments(requested_path);
+    if (requested_segments.isEmpty()) {
+        return nullptr;
+    }
+    QDirIterator it(base_dir, QDir::Files, QDirIterator::Subdirectories);
+    while (it.hasNext()) {
+        const QString file_full_path = it.next();
+        const QString relative_path = QDir(base_dir).relativeFilePath(file_full_path);
+        const QStringList actual_segments = split_segments(relative_path);
+        if (actual_segments.size() < requested_segments.size()) {
+            continue;
+        }
+
+        bool all_segments_match = true;
+        const int offset = actual_segments.size() - requested_segments.size();
+        for (int i = 0; i < requested_segments.size(); ++i) {
+            if (!segment_matches(requested_segments[i], actual_segments[offset + i])) {
+                all_segments_match = false;
+                break;
+            }
+        }
+        if (all_segments_match) {
+            return std::make_unique<std::string>(QFileInfo(file_full_path).absoluteFilePath().toStdString());
+        }
+    }
+    return nullptr;
+}
+
+const std::string DiskImage::getPath(){
+    return path;
+}
+
+const std::string DiskImage::getId(){
     return id;
 };
 
-const Release::Platform Release::getPlatform(){
+const DiskImage::Platform DiskImage::getPlatform(){
     return platform;
 };
 
-const Release::Region Release::getRegion(){
+const DiskImage::Region DiskImage::getRegion(){
     return region;
 };
 
-const Release::Language Release::getLanguage(){
+const DiskImage::Language DiskImage::getLanguage(){
     return language;
 };
 
-const Release::Disk Release::getDisk(){
+const DiskImage::Disk DiskImage::getDisk(){
     return disk;
 };
 
-const bool Release::isValid(){
+const bool DiskImage::isValid(){
     return valid;
 };
 
-const bool Release::isSupported(){
+const bool DiskImage::isSupported(){
     return supported;
 };
 
-const std::string Release::getErrorMessage(){
+const std::string DiskImage::getErrorMessage(){
     return error_message;
 };
 
-const std::string Release::getWarningMessage(){
+const std::string DiskImage::getWarningMessage(){
     return warning_message;
 };
 
-std::string const Release::getContentPath() {
+std::string const DiskImage::getContentPath() {
     return content_path;
 }

+ 273 - 0
src/installer/DiskImage.h

@@ -0,0 +1,273 @@
+/*
+ * 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 <memory>
+#include <string>
+
+/**
+ * Information about a Final Fantasy VII disk image.
+ */
+struct DiskImage{
+
+    public:
+
+        /**
+         * The platform for which the disk image is for.
+         */
+        enum Platform{
+
+            /** The disk image is for PC. */
+            PLATFORM_PC,
+            /** The disk image is for PS1. */
+            PLATFORM_PS1,
+            /** The disk image is for an unknown platform. */
+            PLATFORM_UNKNOWN
+        };
+
+        /**
+         * The region for which the disk image is for.
+         */
+        enum Region{
+            /** The disk image is for Japan. */
+            REGION_JAPAN,
+            /** The disk image is for North America. */
+            REGION_NORTH_AMERICA,
+            /** The disk image is for Europe. */
+            REGION_EUROPE,
+            /** The disk image is for an unknown region. */
+            REGION_UNKNOWN
+        };
+
+        /**
+         * The language for which the disk image is in.
+         */
+        enum Language{
+            /** The disk image is in Japanese. */
+            LANGUAGE_JAPANESE,
+            /** The disk image is in English. */
+            LANGUAGE_ENGLISH,
+            /** The disk image is in French. */
+            LANGUAGE_FRENCH,
+            /** The disk image is in Spanish. */
+            LANGUAGE_SPANISH,
+            /** The disk image is in German. */
+            LANGUAGE_GERMAN,
+            /** The disk image is in Italian. */
+            LANGUAGE_ITALIAN,
+            /** The disk image is for an unknown language. */
+            LANGUAGE_UNKNOWN
+        };
+
+        /**
+         * The disk for which the disk image corresponds to.
+         */
+        enum Disk{
+            /** The disk image is for the install disk (PC only). */
+            DISK_INSTALL,
+            /** The disk image is for disk 1. */
+            DISK_1,
+            /** The disk image is for disk 2. */
+            DISK_2,
+            /** The disk image is for disk 3. */
+            DISK_3,
+            /** The disk image is for disk 4. */
+            DISK_4,
+            /** The disk image is for an unknown disk. */
+            DISK_UNKNOWN
+        };
+
+        /**
+         * Constructs a new disk image.
+         */
+        DiskImage();
+
+        /**
+         * Constructs a new disk image from an ISO path.
+         *
+         * @param[in] path The path to the ISO file.
+         */
+        DiskImage(std::string path);
+
+        /**
+         * Destroys the disk image.
+         */
+        ~DiskImage();
+
+        /**
+         * Gets the path to the disk image file.
+         *
+         * @return The path to the disk image file.
+         */
+        std::string const getPath();
+
+        /**
+         * Gets the human readable ID of the disk image.
+         *
+         * @return The ID of the disk image.
+         */
+        std::string const getId();
+
+        /**
+         * Gets the platform for which the disk image is intended.
+         *
+         * @return The platform of the disk image.
+         */
+        Platform const getPlatform();
+
+        /**
+         * Gets the region for which the disk image is intended.
+         *
+         * @return The region of the disk image.
+         */
+        Region const getRegion();
+
+        /**
+         * Gets the language for which the disk image is intended.
+         *
+         * @return The language of the disk image.
+         */
+        Language const getLanguage();
+
+        /**
+         * Gets the disk for which the disk image is intended.
+         *
+         * @return The disk of the disk image.
+         */
+        Disk const getDisk();
+
+        /**
+         * Checks if the disk image is a valid Final Fantasy VII release.
+         *
+         * @return True if the disk image is valid, false otherwise.
+         */
+        bool const isValid();
+
+        /**
+         * Gets the error message for the disk image.
+         *
+         * @return The error message.
+         */
+        std::string const getErrorMessage();
+
+        /**
+         * Checks if the disk image is supported and tested in V-Gears.
+         *
+         * @return True if the disk image is supported, false otherwise.
+         */
+        bool const isSupported();
+
+        /**
+         * Gets a warning in case the disk image is not fully supported.
+         *
+         * @return The warning message.
+         */
+        std::string const getWarningMessage();
+
+        /**
+         * Gets the path where the content of the disk image has been extracted.
+         * 
+         * @return The path to the extracted content, null if the content has
+         * not been extracted yet.
+         */
+        std::string const getContentPath();
+
+        /**
+         * Extracts the disk image file to the specified output path. The files
+         * will be extracted to the "original_data" subdirectory of the output
+         * path.
+         *
+         * @param[in] installation_path The v-gears installation directory.
+         * @return True if the extraction was successful, false otherwise.
+         */
+        bool extractImage(std::string installation_path);
+
+        /**
+         * Finds a file in the extracted content of the disk image.
+         * 
+         * It handles differnt name cases and differnt path separators. It also
+         * handles the case where the file is in a subdirectory of the extracted
+         * content.
+         *
+         * @param[in] file_path The relative path to the file within the
+         * extracted content.
+         * @return A pointer to the full path of the matched file, or nullptr
+         * if no matching file exists. If the image has not been extracted yet,
+         * this function will always return nullptr.
+         */
+        std::unique_ptr<std::string> filePath(std::string file_path);
+
+    private:
+
+        /**
+         * The path to the image file.
+         */
+        std::string path;
+
+        /**
+         * A human readable ID of the disk image.
+         */
+        std::string id;
+
+        /**
+         * The platform for which the disk image is intended.
+         */
+        Platform platform;
+
+        /**
+         * The region for which the disk image is intended.
+         */
+        Region region;
+
+        /**
+         * The language for which the disk image is intended.
+         */
+        Language language;
+
+        /**
+         * The disk for which the disk image is intended.
+         */
+        Disk disk;
+
+        /**
+         * Whether the disk image is valid.
+         */
+        bool valid;
+
+        /**
+         * Whether the disk image is supported.
+         */
+        bool supported;
+
+        /**
+         * The error message for the disk image. Empty if no error.
+         */
+        std::string error_message;
+
+        /**
+         * The warning message for the disk image. Empty if no warning.
+         */
+        std::string warning_message;
+
+        /**
+         * The path where the content has been extracted.
+         */
+        std::string content_path;
+
+        /**
+         * Whether the content has been extracted.
+         */
+        bool is_extracted;
+};

+ 60 - 123
src/installer/FieldDataInstaller.cpp

@@ -16,6 +16,7 @@
 #include <string>
 #include <boost/filesystem.hpp>
 #include <QtCore/QDir>
+#include "DiskImage.h"
 #include "FieldDataInstaller.h"
 #include "TexFile.h"
 #include "common/File.h"
@@ -51,8 +52,7 @@ std::string FieldDataInstaller::CreateGateWayScript(
       "    on_near = function(self, entity)\n"
       "        if entity == \"Cloud\" then\n"
       "            if not Data.DisableGateways then\n"
-      "                load_field_map_request(\""
-      + target_map_name + "\", \"" + source_spawn_point_name + "\")\n"
+      "                load_field_map_request(\"" + target_map_name + "\", \"" + source_spawn_point_name + "\")\n"
       "            end\n"
       "        end\n"
       "        return 0\n"
@@ -66,8 +66,9 @@ std::string FieldDataInstaller::CreateGateWayScript(
 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 ++)
+    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");
 }
 
@@ -94,7 +95,7 @@ bool FieldDataInstaller::WillCrash(const Ogre::String& resource_name){
       || 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 true;
     return false;
 }
 
@@ -137,7 +138,7 @@ bool FieldDataInstaller::IsTestField(const Ogre::String& resource_name){
       || resource_name == "mds7_w2"
       || resource_name == "mds7_w3"
       || resource_name == "mds7"
-    ){return true;}
+    )return true;
     return false;
 }
 
@@ -207,8 +208,8 @@ void FieldDataInstaller::CollectSpawnPoints(
     }
 }
 
-FieldDataInstaller::FieldDataInstaller(const std::string input_dir, const std::string output_dir):
-  input_dir_(input_dir), output_dir_(output_dir)
+FieldDataInstaller::FieldDataInstaller(const DiskImage& disk_image, const std::string output_dir):
+  disk_image_(disk_image), output_dir_(output_dir)
 {}
 
 FieldDataInstaller::~FieldDataInstaller(){}
@@ -237,9 +238,8 @@ void FieldDataInstaller::CollectSpawnAndScaleFactors(int field_index){
     //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>();
+        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_);
     }
@@ -259,17 +259,13 @@ void FieldDataInstaller::Convert(int field_index){
         if (IsTestField(resource_name) && !WillCrash(resource_name)){
             //write_output_line_("Converting field " + resource_name);
             CreateDir(FIELD_MAPS_DIR + "/" + resource_name);
-            VGears::FLevelFilePtr field = VGears::LZSFLevelFileManager::GetSingleton().load(
-                resource_name, "FFVIIFields"
-              ).staticCast<VGears::FLevelFile>();
+            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;
+            //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;
         }
     }
 }
@@ -303,11 +299,11 @@ 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);
+    VGears::LGPArchive char_lgp(*disk_image_.filePath("data/field/char.lgp"), "LGP");
+    char_lgp.open(*disk_image_.filePath("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");
+    File char_file(*disk_image_.filePath("data/field/char.lgp"));
 
     //Ogre::StringVectorPtr file_list = char_lgp.list(true, true);
     field_model_file_list_ = char_lgp.list(true, true);
@@ -322,9 +318,7 @@ std::vector<std::string> FieldDataInstaller::ConvertModelsInit(){
         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 ++
-    ){
+    for (auto it = used_models_and_anims_.map.begin(); it != used_models_and_anims_.map.end(); it ++){
         models.push_back(it->first);
     }
     return models;
@@ -350,16 +344,12 @@ void FieldDataInstaller::ConvertModels(std::string model_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;
+        //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;
+        //write_output_line_("[ERROR] Exception converting model " + model->first + ": " + ex.what());
+        std::cerr << "[ERROR] Exception converting model " << model->first << ": " << ex.what() << std::endl;
     }
 }
 
@@ -372,9 +362,7 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
     Ogre::MeshSerializer mesh_serializer;
     Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
     Ogre::SkeletonSerializer skeleton_serializer;
-    skeleton_serializer.exportSkeleton(
-      skeleton.getPointer(), outdir + base_mesh_name + ".skeleton"
-    );
+    skeleton_serializer.exportSkeleton(skeleton.getPointer(), outdir + base_mesh_name + ".skeleton");
     mesh->setSkeletonName(base_mesh_name + ".skeleton");
     mesh_serializer.exportMesh(mesh.getPointer(), outdir + mesh->getName());
     Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
@@ -383,9 +371,7 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
     std::set<std::string> textures;
     while (it.hasMoreElements()){
         Ogre::SubMesh *sub_mesh(it.getNext());
-        Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(
-          sub_mesh->getMaterialName())
-        );
+        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);
@@ -398,25 +384,18 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
                               texture_unit_num < pass->getNumTextureUnitStates();
                               texture_unit_num ++
                             ){
-                                Ogre::TextureUnitState* unit
-                                  = pass->getTextureUnitState(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()
-                                    );
+                                    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
-                                    );
+                                    VGears::StringUtil::splitBase(unit->getTextureName(), base_name);
                                     unit->setTextureName(base_mesh_name + "_" + base_name + ".png");
 
-                                    tex.SavePng(
-                                      output_dir_ + FIELD_MODELS_DIR + "/" + base_name + ".png", 0
-                                    );
+                                    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.
@@ -445,8 +424,8 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
     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::TexturePtr texture_ptr = Ogre::TextureManager::getSingleton().load(tex_name, "FFVIITextures");
+            //Ogre::TexturePtr texture_ptr = Ogre::TextureManager::getSingleton().load(tex_name, "FFVII");
             Ogre::Image image;
             texture_ptr->convertToImage(image);
             Ogre::String base_name;
@@ -461,8 +440,7 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
 
 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");
+    if (it == std::end(scale_factors_)) throw std::runtime_error("Scale factor not found for field id");
     return it->second;
 }
 
@@ -496,8 +474,7 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
         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),
+              gateway_entity_name, map_list_.at(gateway.destination_field_id),
               "Spawn_" + field->getName() + "_" + std::to_string(i)
             );
         }
@@ -511,39 +488,28 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
 
         // 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"
+          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"
-            );
+            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()
-                );
+                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."
-            );
+            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()
-        );
+        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;
     }
@@ -605,17 +571,13 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                         + std::to_string(spawn_point_records[i].gateway_index_or_map_jump_address)
                     );
                 }
-                const float downscaler_next = 128.0f * GetFieldScaleFactor(
-                  gateway.destination_field_id
-                );
+                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()
-                ){
+                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 ("
@@ -626,17 +588,15 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                       << 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 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)
+                if (position != Ogre::Vector3::ZERO && first_entity_point == Ogre::Vector3::ZERO){
                     first_entity_point = position;
-                xml_entity_point->SetAttribute(
-                  "position", Ogre::StringConverter::toString(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());
@@ -670,19 +630,15 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
         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;
-                }
+                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);
+                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)
+                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.
@@ -696,22 +652,16 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                     // 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)
-                    );
+                    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("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"
-                );
+                xml_entity_script->SetAttribute("root_orientation", "0.7071067811865476 0.7071067811865476 0 0");
                 element->LinkEndChild(xml_entity_script.release());
             }
             else{
@@ -725,26 +675,20 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
             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")
-                );
+                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
+                      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
+                      gateway.exit_line[1].x, gateway.exit_line[1].y, gateway.exit_line[1].z) / downscaler_this
                     )
                   );
                 // Enabled is hard coded to true.
@@ -783,11 +727,9 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
             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::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 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;
@@ -833,15 +775,12 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                     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)
+                      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))
+                      "depth", Ogre::StringConverter::toString(static_cast<float>(sprite.depth) * (0.03125f))
                     );
                     // TODO: Copied from DatFile::AddTile.
                     // Add to common method.
@@ -858,9 +797,7 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                 //}
             }
             bg_doc.LinkEndChild(bg_element.release());
-            bg_doc.SaveFile(
-              output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/bg.xml"
-            );
+            bg_doc.SaveFile(output_dir_ + "/" + FIELD_MAPS_DIR + "/" + field->getName() + "/bg.xml");
         }
     }
     {

+ 48 - 70
src/installer/FieldDataInstaller.h

@@ -20,6 +20,7 @@
 #include <OgreMesh.h>
 #include <tinyxml.h>
 #include "data/VGearsFLevelFile.h"
+#include "DiskImage.h"
 #include "ScopedLgp.h"
 #include "SpawnPointDb.h"
 #include "ModelsAndAnimationsDb.h"
@@ -48,9 +49,7 @@ class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
         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)
+        ) : field_name_(field_name), field_id_to_name_lookup_(field_id_to_name_lookup), spawn_points_(spawn_points)
         {}
 
         /**
@@ -66,11 +65,9 @@ class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
          * @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
+          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);
+            return field_name_ + "_" + entity + "_" + function_name + "_addr_" + std::to_string(address);
         }
 
         /**
@@ -79,9 +76,7 @@ class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
          * @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];
-        }
+        virtual std::string GetMapName(unsigned int map_id) override{return field_id_to_name_lookup_[map_id];}
 
         /**
          * Retrieves a user friendly variable name.
@@ -91,9 +86,7 @@ class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
          * @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{
+        virtual std::string GetFriendlyVarName(unsigned int bank, unsigned int addr) override{
             return VGears::NameLookup::FieldScriptVarName(bank, addr);
         }
 
@@ -115,9 +108,7 @@ class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
          * @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);
-        }
+        virtual std::string GetFriendlyCharName(int char_id) override{return VGears::NameLookup::CharName(char_id);}
 
         /**
          * Retrieves a user friendly function name.
@@ -130,9 +121,7 @@ class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
         virtual std::string GetFriendlyFunctionName(
           const std::string& entity, const std::string& function_name
         ) override{
-            return VGears::NameLookup::FieldScriptFunctionName(
-              field_name_, entity, function_name
-            );
+            return VGears::NameLookup::FieldScriptFunctionName(field_name_, entity, function_name);
         }
 
         /**
@@ -143,12 +132,8 @@ class BaseFF7FieldScriptFormatter : public FieldScriptFormatter{
          * @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
-            );
+        virtual std::string GetFunctionComment(const std::string& entity, const std::string& function_name) override{
+            return VGears::NameLookup::FieldScriptFunctionComment(field_name_, entity, function_name);
         }
 
     protected:
@@ -184,9 +169,7 @@ class FF7FieldScriptGatewayCollector : public BaseFF7FieldScriptFormatter{
         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)
+        ) : BaseFF7FieldScriptFormatter(field_name,field_id_to_name_lookup, spawn_points), field_id_(this_field_id)
         {}
 
         /**
@@ -269,11 +252,8 @@ class FF7FieldScriptFormatter : public BaseFF7FieldScriptFormatter{
          */
         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)
+          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
@@ -295,9 +275,8 @@ class FF7FieldScriptFormatter : public BaseFF7FieldScriptFormatter{
             }
             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 ("
+                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);
             }
@@ -324,12 +303,11 @@ class FieldDataInstaller{
         /**
          * Installer constructor
          *
-         * @param[in] input_dir Path to the directory containing the original
-         * data to parse.
+         * @param[in] disk_image Disk image to extract the data from.
          * @param[in] output_dir Path to the directory to write generated data
          * to.
          */
-        FieldDataInstaller(const std::string input_dir, const std::string output_dir);
+        FieldDataInstaller(const DiskImage& disk_image, const std::string output_dir);
 
         /**
          * Installer destructor.
@@ -351,16 +329,18 @@ class FieldDataInstaller{
          *
          * Installation step 2.
          *
-         * @param[in] field_index Index of the field to collect from. Must be less than the value
-         * returned by {@see CollectSpawnAndScaleFactorsInit}.
+         * @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}.
+         * @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);
 
@@ -378,8 +358,8 @@ class FieldDataInstaller{
          *
          * Installation step 4.
          *
-         * @param[in] field_index Index of the field to collect from. Must be less than the value
-         * returned by {@see WriteInit}.
+         * @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);
 
@@ -431,15 +411,13 @@ class FieldDataInstaller{
          * @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
-        );
+        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'.
+         * 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.
@@ -449,12 +427,11 @@ class FieldDataInstaller{
         /**
          * 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.
+         * 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.
          */
@@ -463,8 +440,8 @@ class FieldDataInstaller{
         /**
          * 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.
+         * 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.
@@ -475,10 +452,10 @@ class FieldDataInstaller{
          * 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.
+         * @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,
@@ -488,14 +465,15 @@ class FieldDataInstaller{
         /**
          * 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.
+         * 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.
+         * @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,
@@ -505,8 +483,8 @@ class FieldDataInstaller{
         /**
          * 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.
+         * 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;
 
@@ -571,7 +549,7 @@ class FieldDataInstaller{
         /**
          * The path to the directory from which to read the PC game data.
          */
-        std::string input_dir_;
+        DiskImage disk_image_;
 
         /**
          * The path to the directory where to save the V-Gears data.

+ 3 - 9
src/installer/FieldTextWriter.cpp

@@ -46,9 +46,7 @@ u8 FieldTextWriter::GetU8(u32 offset){
     return data_[offset];
 }
 
-void FieldTextWriter::Write(
-  const std::vector<u8>& script_section_buffer, std::string field_name, bool english
-){
+void FieldTextWriter::Write(const std::vector<u8>& script_section_buffer, std::string field_name, bool english){
     data_ = script_section_buffer;
     // Get sector 1 offset (scripts and dialog).
     u32 offset_to_sector = 0; // Our buffer IS the start of scripts and dialog.
@@ -76,9 +74,7 @@ void FieldTextWriter::Write(
             i * 0x02 // *2 because each char is 2 bytes
           );
         AddText(dialog);
-        logger_->Log(
-          "<dialog name=\"" + field_name + "_" + std::to_string(i) + "\">"
-        );
+        logger_->Log("<dialog name=\"" + field_name + "_" + std::to_string(i) + "\">");
         tag_open_ = true;
         // Some maps have unclosed color tags.
         // Use this counter to close them at the end.
@@ -176,9 +172,7 @@ void FieldTextWriter::Write(
                 AddText(dialog);
                 logger_->Log("<image sprite=\"ButtonCross\" />");
             }
-            else if (
-              (temp == 0xFA || temp == 0xFB || temp == 0xFC || temp == 0xFD) && english == false
-            ){
+            else if ((temp == 0xFA || temp == 0xFB || temp == 0xFC || temp == 0xFD) && english == false){
                 ++ offset;
                 unsigned char temp2 = GetU8(offset);
                 if (temp == 0xFA){

+ 1 - 3
src/installer/FieldTextWriter.h

@@ -43,9 +43,7 @@ class FieldTextWriter{
          * @param[in] field_name The field of the text.
          * @param[in] english True if the language to write is english.
          */
-        void Write(
-          const std::vector<u8>& script_section_buffer, std::string field_name, bool english = true
-        );
+        void Write(const std::vector<u8>& script_section_buffer, std::string field_name, bool english = true);
 
         /**
          * Finalizes the text file.

+ 97 - 201
src/installer/KernelDataInstaller.cpp

@@ -17,12 +17,14 @@
 #include <tinyxml.h>
 #include <fstream>
 #include <iostream>
+#include "DiskImage.h"
 #include "KernelDataInstaller.h"
 #include "common/FinalFantasy7/FF7NameLookup.h"
 #include <boost/algorithm/string/replace.hpp>
 
-KernelDataInstaller::KernelDataInstaller(std::string path):
-  kernel_(path + "data/kernel/KERNEL.BIN"), exe_path_(path + "ff7.exe"){
+KernelDataInstaller::KernelDataInstaller(DiskImage& disk_image):
+  kernel_(*disk_image.filePath("data/kernel/KERNEL.BIN")), exe_path_(*disk_image.filePath("ff7.exe")
+){
     for (int i = 0; i < 416; i ++) prices_[i] = 50;
 }
 
@@ -86,9 +88,7 @@ int KernelDataInstaller::ReadCommands(){
                     u8 comp_offset = command_desc_file.GetU8(desc_offset + 1) & 63;
 
                     for (int c = 0; c < comp_size; c ++){
-                        data.description += ENGLISH_CHARS[
-                          command_desc_file.GetU8(desc_offset - 1 - comp_offset + c)
-                        ];
+                        data.description += ENGLISH_CHARS[command_desc_file.GetU8(desc_offset - 1 - comp_offset + c)];
                     }
                     desc_offset ++;
                 }
@@ -268,10 +268,8 @@ int KernelDataInstaller::ReadAttacks(){
         data.damage_formula = data.damage_raw >> 4; // Upper nybble.
         data.damage_modifier = data.damage_raw & 15; // Lower nybble.
         data.status.chance = 63 - (data.status_change_raw ^ 63); // Out of 63;
-        if (data.status_change_raw == (data.status_change_raw | 0x00000080))
-            data.status.mode = TOGGLE;
-        else if (data.status_change_raw == (data.status_change_raw | 0x00000040))
-            data.status.mode = CURE;
+        if (data.status_change_raw == (data.status_change_raw | 0x00000080)) data.status.mode = TOGGLE;
+        else if (data.status_change_raw == (data.status_change_raw | 0x00000040)) data.status.mode = CURE;
         else data.status.mode = INFLICT;
         switch (data.condition_raw){
             case 0: data.restore_type = RESTORE_HP; break;
@@ -284,70 +282,38 @@ int KernelDataInstaller::ReadAttacks(){
         if (data.status.chance <= 64){
             // TODO: Im fairly sure that this can be done with a single-line loop...
             // ... but I don't know how
-            if (data.status_raw == (data.status_raw | 0x00000001))
-                data.status.status.push_back(DEATH);
-            if (data.status_raw == (data.status_raw | 0x00000002))
-                data.status.status.push_back(NEAR_DEATH);
-            if (data.status_raw == (data.status_raw | 0x00000004))
-                data.status.status.push_back(SLEEP);
-            if (data.status_raw == (data.status_raw | 0x00000008))
-                data.status.status.push_back(POISONED);
-            if (data.status_raw == (data.status_raw | 0x00000010))
-                data.status.status.push_back(SADNESS);
-            if (data.status_raw == (data.status_raw | 0x00000020))
-                data.status.status.push_back(FURY);
-            if (data.status_raw == (data.status_raw | 0x00000040))
-                data.status.status.push_back(CONFU);
-            if (data.status_raw == (data.status_raw | 0x00000080))
-                data.status.status.push_back(SILENCE);
-            if (data.status_raw == (data.status_raw | 0x00000100))
-                data.status.status.push_back(HASTE);
-            if (data.status_raw == (data.status_raw | 0x00000200))
-                data.status.status.push_back(SLOW);
-            if (data.status_raw == (data.status_raw | 0x00000400))
-                data.status.status.push_back(STOP);
-            if (data.status_raw == (data.status_raw | 0x00000800))
-                data.status.status.push_back(FROG);
-            if (data.status_raw == (data.status_raw | 0x00001000))
-                data.status.status.push_back(SMALL);
-            if (data.status_raw == (data.status_raw | 0x00002000))
-                data.status.status.push_back(SLOW_NUMB);
-            if (data.status_raw == (data.status_raw | 0x00004000))
-                data.status.status.push_back(PETRIFY);
-            if (data.status_raw == (data.status_raw | 0x00008000))
-                data.status.status.push_back(REGEN);
-            if (data.status_raw == (data.status_raw | 0x00010000))
-                data.status.status.push_back(BARRIER);
-            if (data.status_raw == (data.status_raw | 0x00020000))
-                data.status.status.push_back(M_BARRIER);
-            if (data.status_raw == (data.status_raw | 0x00040000))
-                data.status.status.push_back(REFLECT);
-            if (data.status_raw == (data.status_raw | 0x00080000))
-                data.status.status.push_back(DUAL);
-            if (data.status_raw == (data.status_raw | 0x00100000))
-                data.status.status.push_back(SHIELD);
-            if (data.status_raw == (data.status_raw | 0x00200000))
-                data.status.status.push_back(D_SENTENCE);
-            if (data.status_raw == (data.status_raw | 0x00400000))
-                data.status.status.push_back(MANIPULATE);
-            if (data.status_raw == (data.status_raw | 0x00800000))
-                data.status.status.push_back(BERSERK);
-            if (data.status_raw == (data.status_raw | 0x01000000))
-                data.status.status.push_back(PEERLESS);
-            if (data.status_raw == (data.status_raw | 0x02000000))
-                data.status.status.push_back(PARALYSIS);
-            if (data.status_raw == (data.status_raw | 0x04000000))
-                data.status.status.push_back(DARKNESS);
-            if (data.status_raw == (data.status_raw | 0x08000000))
-                data.status.status.push_back(DUAL_DRAIN);
-            if (data.status_raw == (data.status_raw | 0x10000000))
-                data.status.status.push_back(DEATH_FORCE);
-            if (data.status_raw == (data.status_raw | 0x20000000))
-                data.status.status.push_back(RESIST);
-            if (data.status_raw == (data.status_raw | 0x40000000))
-                data.status.status.push_back(LUCKY_GIRL);
-            if (data.status_raw == (data.status_raw | 0x80000000))
-                data.status.status.push_back(IMPRISONED);
+            if (data.status_raw == (data.status_raw | 0x00000001)) data.status.status.push_back(DEATH);
+            if (data.status_raw == (data.status_raw | 0x00000002)) data.status.status.push_back(NEAR_DEATH);
+            if (data.status_raw == (data.status_raw | 0x00000004)) data.status.status.push_back(SLEEP);
+            if (data.status_raw == (data.status_raw | 0x00000008)) data.status.status.push_back(POISONED);
+            if (data.status_raw == (data.status_raw | 0x00000010)) data.status.status.push_back(SADNESS);
+            if (data.status_raw == (data.status_raw | 0x00000020)) data.status.status.push_back(FURY);
+            if (data.status_raw == (data.status_raw | 0x00000040)) data.status.status.push_back(CONFU);
+            if (data.status_raw == (data.status_raw | 0x00000080)) data.status.status.push_back(SILENCE);
+            if (data.status_raw == (data.status_raw | 0x00000100)) data.status.status.push_back(HASTE);
+            if (data.status_raw == (data.status_raw | 0x00000200)) data.status.status.push_back(SLOW);
+            if (data.status_raw == (data.status_raw | 0x00000400)) data.status.status.push_back(STOP);
+            if (data.status_raw == (data.status_raw | 0x00000800)) data.status.status.push_back(FROG);
+            if (data.status_raw == (data.status_raw | 0x00001000)) data.status.status.push_back(SMALL);
+            if (data.status_raw == (data.status_raw | 0x00002000)) data.status.status.push_back(SLOW_NUMB);
+            if (data.status_raw == (data.status_raw | 0x00004000)) data.status.status.push_back(PETRIFY);
+            if (data.status_raw == (data.status_raw | 0x00008000)) data.status.status.push_back(REGEN);
+            if (data.status_raw == (data.status_raw | 0x00010000)) data.status.status.push_back(BARRIER);
+            if (data.status_raw == (data.status_raw | 0x00020000)) data.status.status.push_back(M_BARRIER);
+            if (data.status_raw == (data.status_raw | 0x00040000)) data.status.status.push_back(REFLECT);
+            if (data.status_raw == (data.status_raw | 0x00080000)) data.status.status.push_back(DUAL);
+            if (data.status_raw == (data.status_raw | 0x00100000)) data.status.status.push_back(SHIELD);
+            if (data.status_raw == (data.status_raw | 0x00200000)) data.status.status.push_back(D_SENTENCE);
+            if (data.status_raw == (data.status_raw | 0x00400000)) data.status.status.push_back(MANIPULATE);
+            if (data.status_raw == (data.status_raw | 0x00800000)) data.status.status.push_back(BERSERK);
+            if (data.status_raw == (data.status_raw | 0x01000000)) data.status.status.push_back(PEERLESS);
+            if (data.status_raw == (data.status_raw | 0x02000000)) data.status.status.push_back(PARALYSIS);
+            if (data.status_raw == (data.status_raw | 0x04000000)) data.status.status.push_back(DARKNESS);
+            if (data.status_raw == (data.status_raw | 0x08000000)) data.status.status.push_back(DUAL_DRAIN);
+            if (data.status_raw == (data.status_raw | 0x10000000)) data.status.status.push_back(DEATH_FORCE);
+            if (data.status_raw == (data.status_raw | 0x20000000)) data.status.status.push_back(RESIST);
+            if (data.status_raw == (data.status_raw | 0x40000000)) data.status.status.push_back(LUCKY_GIRL);
+            if (data.status_raw == (data.status_raw | 0x80000000)) data.status.status.push_back(IMPRISONED);
         }
         // Get elements
         if (data.element_raw == (data.element_raw | 0x0001)) data.elements.push_back(FIRE);
@@ -370,7 +336,6 @@ int KernelDataInstaller::ReadAttacks(){
         // If that happens, clear them all.
         if (data.elements.size() == 16) data.elements.clear();
 
-
         // Add to the list of items.
         attacks_.push_back(data);
         attack_names_[i] = data.name;
@@ -604,20 +569,16 @@ void KernelDataInstaller::WriteGrowth(std::string file_name){
     file.open(file_name, std::ios::out);
     if (!file) return;
     file << "Game.Growth.bonus.primary = {";
-    for (int i = 0; i < 12; i ++)
-        file << "[" << i << "] = " << static_cast<int>(growth_.bonus_stat[i]) << ", ";
+    for (int i = 0; i < 12; i ++) file << "[" << i << "] = " << static_cast<int>(growth_.bonus_stat[i]) << ", ";
     file << "}\nGame.Growth.bonus.hp = {";
-    for (int i = 0; i < 12; i ++)
-        file << "[" << i << "] = " << static_cast<int>(growth_.bonus_hp[i]) << ", ";
+    for (int i = 0; i < 12; i ++) file << "[" << i << "] = " << static_cast<int>(growth_.bonus_hp[i]) << ", ";
     file << "}\nGame.Growth.bonus.mp = {";
-    for (int i = 0; i < 12; i ++)
-        file << "[" << i << "] = " << static_cast<int>(growth_.bonus_mp[i]) << ", ";
+    for (int i = 0; i < 12; i ++) file << "[" << i << "] = " << static_cast<int>(growth_.bonus_mp[i]) << ", ";
     file << "}\n";
     for (int i = 0; i < 64; i ++){
         file << "Game.Growth.curves[" << i << "] = {\n";
         for (int j = 0; j < 8; j ++){
-            file << "    [" << j << "] = {gradient = "
-              << static_cast<int>(growth_.curves[i].gradient[j])
+            file << "    [" << j << "] = {gradient = " << static_cast<int>(growth_.curves[i].gradient[j])
               << ", base = " << static_cast<int>(growth_.curves[i].gradient[j]) << "},\n";
         }
         file << "}\n";
@@ -674,9 +635,7 @@ int KernelDataInstaller::ReadItems(){
                     u8 comp_offset = items_desc_file.GetU8(desc_offset + 1) & 63;
 
                     for (int c = 0; c < comp_size; c ++){
-                        data.description += ENGLISH_CHARS[
-                          items_desc_file.GetU8(desc_offset - 1 - comp_offset + c)
-                        ];
+                        data.description += ENGLISH_CHARS[items_desc_file.GetU8(desc_offset - 1 - comp_offset + c)];
                     }
                     desc_offset ++;
                 }
@@ -731,10 +690,8 @@ int KernelDataInstaller::ReadItems(){
         data.damage_formula = data.damage_raw >> 4; // Upper nybble.
         data.damage_modifier = data.damage_raw & 15; // Lower nybble.
         data.status.chance = 63 - (data.status_change_raw ^ 63); // Out of 63;
-        if (data.status_change_raw == (data.status_change_raw | 0x00000080))
-            data.status.mode = TOGGLE;
-        else if (data.status_change_raw == (data.status_change_raw | 0x00000040))
-            data.status.mode = CURE;
+        if (data.status_change_raw == (data.status_change_raw | 0x00000080)) data.status.mode = TOGGLE;
+        else if (data.status_change_raw == (data.status_change_raw | 0x00000040)) data.status.mode = CURE;
         else data.status.mode = INFLICT;
         switch (data.condition_raw){
             case 0: data.restore_type = RESTORE_HP; break;
@@ -749,70 +706,38 @@ int KernelDataInstaller::ReadItems(){
         if (data.status.chance <= 64){
             // TODO: Im fairly sure that this can be done with a single-line loop...
             // ... but I don't know how
-            if (data.status_raw == (data.status_raw | 0x00000001))
-                data.status.status.push_back(DEATH);
-            if (data.status_raw == (data.status_raw | 0x00000002))
-                data.status.status.push_back(NEAR_DEATH);
-            if (data.status_raw == (data.status_raw | 0x00000004))
-                data.status.status.push_back(SLEEP);
-            if (data.status_raw == (data.status_raw | 0x00000008))
-                data.status.status.push_back(POISONED);
-            if (data.status_raw == (data.status_raw | 0x00000010))
-                data.status.status.push_back(SADNESS);
-            if (data.status_raw == (data.status_raw | 0x00000020))
-                data.status.status.push_back(FURY);
-            if (data.status_raw == (data.status_raw | 0x00000040))
-                data.status.status.push_back(CONFU);
-            if (data.status_raw == (data.status_raw | 0x00000080))
-                data.status.status.push_back(SILENCE);
-            if (data.status_raw == (data.status_raw | 0x00000100))
-                data.status.status.push_back(HASTE);
-            if (data.status_raw == (data.status_raw | 0x00000200))
-                data.status.status.push_back(SLOW);
-            if (data.status_raw == (data.status_raw | 0x00000400))
-                data.status.status.push_back(STOP);
-            if (data.status_raw == (data.status_raw | 0x00000800))
-                data.status.status.push_back(FROG);
-            if (data.status_raw == (data.status_raw | 0x00001000))
-                data.status.status.push_back(SMALL);
-            if (data.status_raw == (data.status_raw | 0x00002000))
-                data.status.status.push_back(SLOW_NUMB);
-            if (data.status_raw == (data.status_raw | 0x00004000))
-                data.status.status.push_back(PETRIFY);
-            if (data.status_raw == (data.status_raw | 0x00008000))
-                data.status.status.push_back(REGEN);
-            if (data.status_raw == (data.status_raw | 0x00010000))
-                data.status.status.push_back(BARRIER);
-            if (data.status_raw == (data.status_raw | 0x00020000))
-                data.status.status.push_back(M_BARRIER);
-            if (data.status_raw == (data.status_raw | 0x00040000))
-                data.status.status.push_back(REFLECT);
-            if (data.status_raw == (data.status_raw | 0x00080000))
-                data.status.status.push_back(DUAL);
-            if (data.status_raw == (data.status_raw | 0x00100000))
-                data.status.status.push_back(SHIELD);
-            if (data.status_raw == (data.status_raw | 0x00200000))
-                data.status.status.push_back(D_SENTENCE);
-            if (data.status_raw == (data.status_raw | 0x00400000))
-                data.status.status.push_back(MANIPULATE);
-            if (data.status_raw == (data.status_raw | 0x00800000))
-                data.status.status.push_back(BERSERK);
-            if (data.status_raw == (data.status_raw | 0x01000000))
-                data.status.status.push_back(PEERLESS);
-            if (data.status_raw == (data.status_raw | 0x02000000))
-                data.status.status.push_back(PARALYSIS);
-            if (data.status_raw == (data.status_raw | 0x04000000))
-                data.status.status.push_back(DARKNESS);
-            if (data.status_raw == (data.status_raw | 0x08000000))
-                data.status.status.push_back(DUAL_DRAIN);
-            if (data.status_raw == (data.status_raw | 0x10000000))
-                data.status.status.push_back(DEATH_FORCE);
-            if (data.status_raw == (data.status_raw | 0x20000000))
-                data.status.status.push_back(RESIST);
-            if (data.status_raw == (data.status_raw | 0x40000000))
-                data.status.status.push_back(LUCKY_GIRL);
-            if (data.status_raw == (data.status_raw | 0x80000000))
-                data.status.status.push_back(IMPRISONED);
+            if (data.status_raw == (data.status_raw | 0x00000001)) data.status.status.push_back(DEATH);
+            if (data.status_raw == (data.status_raw | 0x00000002)) data.status.status.push_back(NEAR_DEATH);
+            if (data.status_raw == (data.status_raw | 0x00000004)) data.status.status.push_back(SLEEP);
+            if (data.status_raw == (data.status_raw | 0x00000008)) data.status.status.push_back(POISONED);
+            if (data.status_raw == (data.status_raw | 0x00000010)) data.status.status.push_back(SADNESS);
+            if (data.status_raw == (data.status_raw | 0x00000020)) data.status.status.push_back(FURY);
+            if (data.status_raw == (data.status_raw | 0x00000040)) data.status.status.push_back(CONFU);
+            if (data.status_raw == (data.status_raw | 0x00000080)) data.status.status.push_back(SILENCE);
+            if (data.status_raw == (data.status_raw | 0x00000100)) data.status.status.push_back(HASTE);
+            if (data.status_raw == (data.status_raw | 0x00000200)) data.status.status.push_back(SLOW);
+            if (data.status_raw == (data.status_raw | 0x00000400)) data.status.status.push_back(STOP);
+            if (data.status_raw == (data.status_raw | 0x00000800)) data.status.status.push_back(FROG);
+            if (data.status_raw == (data.status_raw | 0x00001000)) data.status.status.push_back(SMALL);
+            if (data.status_raw == (data.status_raw | 0x00002000)) data.status.status.push_back(SLOW_NUMB);
+            if (data.status_raw == (data.status_raw | 0x00004000)) data.status.status.push_back(PETRIFY);
+            if (data.status_raw == (data.status_raw | 0x00008000)) data.status.status.push_back(REGEN);
+            if (data.status_raw == (data.status_raw | 0x00010000)) data.status.status.push_back(BARRIER);
+            if (data.status_raw == (data.status_raw | 0x00020000)) data.status.status.push_back(M_BARRIER);
+            if (data.status_raw == (data.status_raw | 0x00040000)) data.status.status.push_back(REFLECT);
+            if (data.status_raw == (data.status_raw | 0x00080000)) data.status.status.push_back(DUAL);
+            if (data.status_raw == (data.status_raw | 0x00100000)) data.status.status.push_back(SHIELD);
+            if (data.status_raw == (data.status_raw | 0x00200000)) data.status.status.push_back(D_SENTENCE);
+            if (data.status_raw == (data.status_raw | 0x00400000)) data.status.status.push_back(MANIPULATE);
+            if (data.status_raw == (data.status_raw | 0x00800000)) data.status.status.push_back(BERSERK);
+            if (data.status_raw == (data.status_raw | 0x01000000)) data.status.status.push_back(PEERLESS);
+            if (data.status_raw == (data.status_raw | 0x02000000)) data.status.status.push_back(PARALYSIS);
+            if (data.status_raw == (data.status_raw | 0x04000000)) data.status.status.push_back(DARKNESS);
+            if (data.status_raw == (data.status_raw | 0x08000000)) data.status.status.push_back(DUAL_DRAIN);
+            if (data.status_raw == (data.status_raw | 0x10000000)) data.status.status.push_back(DEATH_FORCE);
+            if (data.status_raw == (data.status_raw | 0x20000000)) data.status.status.push_back(RESIST);
+            if (data.status_raw == (data.status_raw | 0x40000000)) data.status.status.push_back(LUCKY_GIRL);
+            if (data.status_raw == (data.status_raw | 0x80000000)) data.status.status.push_back(IMPRISONED);
         }
 
         // Get elements
@@ -937,9 +862,7 @@ int KernelDataInstaller::ReadWeapons(){
                     u8 comp_offset = weapons_desc_file.GetU8(desc_offset + 1) & 63;
 
                     for (int c = 0; c < comp_size; c ++){
-                        data.description += ENGLISH_CHARS[
-                          weapons_desc_file.GetU8(desc_offset - 1 - comp_offset + c)
-                        ];
+                        data.description += ENGLISH_CHARS[weapons_desc_file.GetU8(desc_offset - 1 - comp_offset + c)];
                     }
                     desc_offset ++;
                 }
@@ -1028,15 +951,10 @@ int KernelDataInstaller::ReadWeapons(){
             }
         }
         for (int s = 0; s < 8; s ++){
-            if (data.slots_raw[s] == 1 || data.slots_raw[s] == 5){
-                data.materia_slots.push_back(SLOT_UNLINKED);
-            }
+            if (data.slots_raw[s] == 1 || data.slots_raw[s] == 5) data.materia_slots.push_back(SLOT_UNLINKED);
             else if (
-              data.slots_raw[s] == 2 || data.slots_raw[s] == 3
-              || data.slots_raw[s] == 6 || data.slots_raw[s] == 7
-            ){
-                data.materia_slots.push_back(SLOT_LINKED);
-            }
+              data.slots_raw[s] == 2 || data.slots_raw[s] == 3 || data.slots_raw[s] == 6 || data.slots_raw[s] == 7
+            ) data.materia_slots.push_back(SLOT_LINKED);
         }
         if (data.element_raw == (data.element_raw | 0x0001)) data.elements.push_back(FIRE);
         if (data.element_raw == (data.element_raw | 0x0002)) data.elements.push_back(ICE);
@@ -1166,9 +1084,7 @@ int KernelDataInstaller::ReadArmors(){
                     u8 comp_offset = armor_desc_file.GetU8(desc_offset + 1) & 63;
 
                     for (int c = 0; c < comp_size; c ++){
-                        data.description += ENGLISH_CHARS[
-                          armor_desc_file.GetU8(desc_offset - 1 - comp_offset + c)
-                        ];
+                        data.description += ENGLISH_CHARS[armor_desc_file.GetU8(desc_offset - 1 - comp_offset + c)];
                     }
                     desc_offset ++;
                 }
@@ -1231,11 +1147,8 @@ int KernelDataInstaller::ReadArmors(){
                 data.materia_slots.push_back(SLOT_UNLINKED);
             }
             else if (
-              data.slots_raw[i] == 2 || data.slots_raw[i] == 3
-              || data.slots_raw[i] == 6 || data.slots_raw[i] == 7
-            ){
-                data.materia_slots.push_back(SLOT_LINKED);
-            }
+              data.slots_raw[i] == 2 || data.slots_raw[i] == 3 || data.slots_raw[i] == 6 || data.slots_raw[i] == 7
+            ) data.materia_slots.push_back(SLOT_LINKED);
         }
         // In KERNEL.BIN, items with no elemental defense actually do have all elements marked,
         // with mode 255. If, that's the case, consider that there are no elements.
@@ -1249,8 +1162,7 @@ int KernelDataInstaller::ReadArmors(){
             if (data.element_raw == (data.element_raw | 0x0040)) data.elements.push_back(WATER);
             if (data.element_raw == (data.element_raw | 0x0080)) data.elements.push_back(WIND);
             if (data.element_raw == (data.element_raw | 0x0100)) data.elements.push_back(HOLY);
-            if (data.element_raw == (data.element_raw | 0x0200))
-                data.elements.push_back(RESTORATIVE);
+            if (data.element_raw == (data.element_raw | 0x0200)) data.elements.push_back(RESTORATIVE);
             if (data.element_raw == (data.element_raw | 0x0400)) data.elements.push_back(CUT);
             if (data.element_raw == (data.element_raw | 0x0800)) data.elements.push_back(HIT);
             if (data.element_raw == (data.element_raw | 0x1000)) data.elements.push_back(PUNCH);
@@ -1365,9 +1277,7 @@ int KernelDataInstaller::ReadAccessories(){
                     u8 comp_offset = accessory_desc_file.GetU8(desc_offset + 1) & 63;
 
                     for (int c = 0; c < comp_size; c ++){
-                        data.description += ENGLISH_CHARS[
-                          accessory_desc_file.GetU8(desc_offset - 1 - comp_offset + c)
-                        ];
+                        data.description += ENGLISH_CHARS[accessory_desc_file.GetU8(desc_offset - 1 - comp_offset + c)];
                     }
                     desc_offset ++;
                 }
@@ -1459,8 +1369,7 @@ int KernelDataInstaller::ReadAccessories(){
             if (data.element_raw == (data.element_raw | 0x0040)) data.elements.push_back(WATER);
             if (data.element_raw == (data.element_raw | 0x0080)) data.elements.push_back(WIND);
             if (data.element_raw == (data.element_raw | 0x0100)) data.elements.push_back(HOLY);
-            if (data.element_raw == (data.element_raw | 0x0200))
-                data.elements.push_back(RESTORATIVE);
+            if (data.element_raw == (data.element_raw | 0x0200)) data.elements.push_back(RESTORATIVE);
             if (data.element_raw == (data.element_raw | 0x0400)) data.elements.push_back(CUT);
             if (data.element_raw == (data.element_raw | 0x0800)) data.elements.push_back(HIT);
             if (data.element_raw == (data.element_raw | 0x1000)) data.elements.push_back(PUNCH);
@@ -1567,9 +1476,7 @@ int KernelDataInstaller::ReadMateria(){
                     u8 comp_offset = materia_desc_file.GetU8(desc_offset + 1) & 63;
 
                     for (int c = 0; c < comp_size; c ++){
-                        data.description += ENGLISH_CHARS[
-                          materia_desc_file.GetU8(desc_offset - 1 - comp_offset + c)
-                        ];
+                        data.description += ENGLISH_CHARS[materia_desc_file.GetU8(desc_offset - 1 - comp_offset + c)];
                     }
                     desc_offset ++;
                 }
@@ -1993,12 +1900,9 @@ void KernelDataInstaller::WriteMateria(std::string file_name){
                     switch (materia.attribute[0]){
                         case 0x00: file << "Materia.ABILITY.MONEY_PLUS,\n"; break;
                         case 0x01: // Enemy Away / Enemy lure / Chocobo lure
-                            if (materia.id == 7)
-                                file << "Materia.ABILITY.ENCOUNTER_DOWN,\n";
-                            else if (materia.id == 8)
-                                file << "Materia.ABILITY.ENCOUNTER_UP,\n";
-                            else if (materia.id == 9)
-                                file << "Materia.ABILITY.ENCOUNTER_CHOCOBO,\n";
+                            if (materia.id == 7) file << "Materia.ABILITY.ENCOUNTER_DOWN,\n";
+                            else if (materia.id == 8) file << "Materia.ABILITY.ENCOUNTER_UP,\n";
+                            else if (materia.id == 9) file << "Materia.ABILITY.ENCOUNTER_CHOCOBO,\n";
                             else file << "nil,\n";
                             break;
                         case 0x03: file << "Materia.ABILITY.PREEMPTIVE,\n"; break;
@@ -2071,9 +1975,7 @@ int KernelDataInstaller::ReadKeyItems(){
                     u8 comp_offset = key_items_desc_file.GetU8(desc_offset + 1) & 63;
 
                     for (int i = 0; i < comp_size; i ++){
-                        data.description += ENGLISH_CHARS[
-                          key_items_desc_file.GetU8(desc_offset - 1 - comp_offset + i)
-                        ];
+                        data.description += ENGLISH_CHARS[key_items_desc_file.GetU8(desc_offset - 1 - comp_offset + i)];
                     }
                     desc_offset ++;
                 }
@@ -2102,8 +2004,7 @@ void KernelDataInstaller::WriteKeyItems(std::string file_name){
     if (!file) return;
     for (KeyItemData key_item : key_items_){
         file << "Game.Key_Items[" << key_item.id << "] = {\n"
-          << "    name = \"" << key_item.name << "\",\n"
-          << "    description = \"" << key_item.description << "\"\n}\n";
+          << "    name = \"" << key_item.name << "\",\n description = \"" << key_item.description << "\"\n}\n";
     }
     file.close();
 }
@@ -2188,8 +2089,7 @@ void KernelDataInstaller::ReadInitialSaveMap(){
             savemap_.characters[i].name_raw[i] = savemap_file.readU8();
             if (name_end == false){
                 if (savemap_.characters[i].name_raw[i] != 0XFF){
-                    savemap_.characters[i].name
-                      += ENGLISH_CHARS[savemap_.characters[i].name_raw[i]];
+                    savemap_.characters[i].name += ENGLISH_CHARS[savemap_.characters[i].name_raw[i]];
                 }
                 else name_end = true;
             }
@@ -2249,17 +2149,13 @@ void KernelDataInstaller::ReadInitialSaveMap(){
             savemap_.characters[i].weapon_materia[m].id = savemap_file.readU8();
             // TODO: Identify Enemy Skill, do special treatment
             savemap_.characters[i].weapon_materia[m].ap // WARNING: AP has only three bytes.
-              = savemap_file.readU8()
-              + (savemap_file.readU8() << 8)
-              + (savemap_file.readU8() << 16);
+              = savemap_file.readU8() + (savemap_file.readU8() << 8) + (savemap_file.readU8() << 16);
         }
         for (int m = 0; m < 8; m ++){
             savemap_.characters[i].armor_materia[m].id = savemap_file.readU8();
             // TODO: Identify Enemy Skill, do special treatment
             savemap_.characters[i].armor_materia[m].ap // WARNING: AP has only three bytes.
-              = savemap_file.readU8()
-              + (savemap_file.readU8() << 8)
-              + (savemap_file.readU8() << 16);
+              = savemap_file.readU8() + (savemap_file.readU8() << 8) + (savemap_file.readU8() << 16);
         }
         savemap_.characters[i].exp_to_next = savemap_file.readU32LE();
     }

+ 166 - 132
src/installer/KernelDataInstaller.h

@@ -17,6 +17,7 @@
 
 #include "common/BinGZipFile.h"
 #include "common/TypeDefine.h"
+#include "DiskImage.h"
 #include "Characters.h"
 #include "SaveMap.h"
 
@@ -27,9 +28,9 @@ class KernelDataInstaller{
         /**
          * Constructor.
          *
-         * @param[in] path Path selected in the installation as source directory.
+         * @param[in] disk_image Disk image to extract the data from.
          */
-        KernelDataInstaller(std::string path);
+        KernelDataInstaller(DiskImage& disk_image);
 
         /**
          * Destructor.
@@ -453,64 +454,70 @@ class KernelDataInstaller{
             /**
              * The target can be selected.
              *
-             * If the target of the attack is selectable, at least between some subset of possible
-             * targets, this must be true.
+             * If the target of the attack is selectable, at least between some
+             * subset of possible targets, this must be true.
              */
             bool selection_enabled;
 
             /**
              * The default target is an enemy.
              *
-             * When true, the cursor starts in an enemy row. When false, it starts in the ally row.
-             * It may or may not be possible to change it.
+             * When true, the cursor starts in an enemy row. When false, it
+             * starts in the ally row. It may or may not be possible to change
+             * it.
              */
             bool default_enemy;
 
             /**
              * The default target is a group.
              *
-             * When true, the cursor starts will all the members of a row selected. When false, it
-             * starts with only one target selected. It may or may not be possible to change it.
+             * When true, the cursor starts will all the members of a row
+             * selected. When false, it starts with only one target selected. It
+             * may or may not be possible to change it.
              */
             bool default_multiple;
 
             /**
              * The selection can change between groups and individuals.
              *
-             * When true, the target can be changed between groups and individuals When false,
-             * only {@see default_multiple} determines if the target is an individual or a group.
+             * When true, the target can be changed between groups and
+             * individuals. When false, only {@see default_multiple} determines
+             * if the target is an individual or a group.
              */
             bool toggle_multiple;
 
             /**
              * The target group can be changed.
              *
-             * When true, the target cannot be changed between enemies and allies. In this case
-             * {@see default_enemy} will determine the group that can be targeted.
+             * When true, the target cannot be changed between enemies and
+             * allies. In this case {@see default_enemy} will determine the
+             * group that can be targeted.
              */
             bool fixed_row;
 
             /**
              * The command is short ranged.
              *
-             * When true, if the target or the caster is not in the front of their row, the target
-             * will take half damage. Also, when true, an enemy covered by another enemy can't be
-             * targeted.
+             * When true, if the target or the caster is not in the front of
+             * their row, the target will take half damage. Also, when true, an
+             * enemy covered by another enemy can't be targeted.
              */
             bool short_range;
 
             /**
              * Targets all, enemies and allies.
              *
-             * When true, all allies and enemies are targets, and the selection can't be changed.
+             * When true, all allies and enemies are targets, and the selection
+             * can't be changed.
              */
             bool all_rows;
 
             /**
              * The target is random.
              *
-             * When multiple targets are selected, one will be selected at random to be the
-             * receiving target. Cursor will cycle among all viable targets.
+             * When multiple targets are selected, one will be selected at
+             * random to be the receiving target. Cursor will cycle among all
+             * viable targets.
              */
             bool random;
 
@@ -728,8 +735,8 @@ class KernelDataInstaller{
             /**
              * Toggles status changes.
              *
-             * If the target doesn't have the status change, inflict it. If the target already has
-             * the status change, cure it.
+             * If the target doesn't have the status change, inflict it. If the
+             * target already has the status change, cure it.
              */
             TOGGLE = 2
         };
@@ -824,9 +831,10 @@ class KernelDataInstaller{
         /**
          * Data for each command.
          *
-         * As found in KERNEL.BIN, file 0, in order, up to camera_multiple. Members after
-         * camera_multiple are not found literally in KERNEL.BIN, but are derived from other
-         * members or found in other files of the kernel (text files).
+         * As found in KERNEL.BIN, file 0, in order, up to camera_multiple.
+         * Members after camera_multiple are not found literally in KERNEL.BIN,
+         * but are derived from other members or found in other files of the
+         * kernel (text files).
          */
         struct CommandData{
 
@@ -850,7 +858,8 @@ class KernelDataInstaller{
             /**
              * Camera movement ID for single target. 2 bytes.
              *
-             * Determines the camera movement when the command is executed over a single target.
+             * Determines the camera movement when the command is executed over
+             * a single target.
              */
             u16 camera_single;
 
@@ -864,21 +873,22 @@ class KernelDataInstaller{
             /**
              * Command ID.
              *
-             * Not actually in the item file data, it's the order at which it appears.
+             * Not actually in the item file data, it's the order at which it
+             * appears.
              */
             int id;
 
             /**
              * Command name.
              *
-             * Not found in the same file as the rest of the data, but on file 17.
+             * Not in the same file as the rest of the data, but on file 17.
              */
             std::string name;
 
             /**
              * Command description.
              *
-             * Not found in the same file as the rest of the data, but on file 9.
+             * Not in the same file as the rest of the data, but on file 9.
              */
             std::string description;
 
@@ -901,9 +911,9 @@ class KernelDataInstaller{
         /**
          * Data for each attack.
          *
-         * As found in KERNEL.BIN, file 1, in order, up to camera_multiple. Members after
-         * special_raw are not found literally in KERNEL.BIN, but are derived from other
-         * members.
+         * As found in KERNEL.BIN, file 1, in order, up to camera_multiple.
+         * Members after special_raw are not found literally in KERNEL.BIN, but
+         * are derived from other members.
          */
         struct AttackData{
 
@@ -931,29 +941,27 @@ class KernelDataInstaller{
 
             /**
              * MP cost. 2 bytes.
-             *
-             * Determines the camera movement when the command is executed over a single target.
              */
             u16 mp;
 
             /**
              * Impact sound. 2 bytes.
-             *
-             * Determines the camera movement when the command is executed over multiple targets.
              */
             u16 sound;
 
             /**
              * Camera movement ID for single target. 2 bytes.
              *
-             * Determines the camera movement when the command is executed over a single target.
+             * Determines the camera movement when the command is executed over
+             * a single target.
              */
             u16 camera_single;
 
             /**
              * Camera movement ID for multiple target. 2 bytes.
              *
-             * Determines the camera movement when the command is executed over multiple targets.
+             * Determines the camera movement when the command is executed over
+             * multiple targets.
              */
             u16 camera_multiple;
 
@@ -974,11 +982,13 @@ class KernelDataInstaller{
             /**
              * Damage formula. 1 byte.
              *
-             * This byte is divided into two nybbles (four bits). Upper nybble determines what
-             * considerations are made in calculating damage such as physical/magical, allowing
-             * criticals, how to calculate accuracy, etc. There are also three sets of known
-             * formulae that are paired with the upper nybble values. These are selected in the
-             * Lower Nybble and determine how to calculate pre-defense damage
+             * This byte is divided into two nybbles (four bits). Upper nybble
+             * determines what considerations are made in calculating damage
+             * such as physical/magical, allowing criticals, how to calculate
+             * accuracy, etc. There are also three sets of known formulae that
+             * are paired with the upper nybble values. These are selected in
+             * the Lower Nybble and determine how to calculate pre-defense
+             * damage.
              */
             u8 damage_raw;
 
@@ -992,7 +1002,7 @@ class KernelDataInstaller{
             /**
              * The restore type.
              *
-             * See {@see restore_type} and {@see RESTORE_TYPE} for more information.
+             * See {@see restore_type} and {@see RESTORE_TYPE}.
              */
             u8 condition_raw;
 
@@ -1020,9 +1030,10 @@ class KernelDataInstaller{
             /**
              * Information about what status can be inflicted or cured. 4 bytes.
              *
-             * The first six bits are the chance of inflicting/curing (out of 63). If the seventh
-             * bit is set, the status is cured instead of inflicted. If the eighth bit is set, then
-             * the status is toggled (cured if present, inflicted if not present).
+             * The first six bits are the chance of inflicting/curing (out of
+             * 63). If the seventh bit is set, the status is cured instead of
+             * inflicted. If the eighth bit is set, then the status is toggled
+             * (cured if present, inflicted if not present).
              */
             u32 status_raw;
 
@@ -1042,21 +1053,21 @@ class KernelDataInstaller{
             /**
              * Attack ID.
              *
-             * Not actually in the item file data, it's the order at which it appears.
+             * Not in the item file data, it's the order at which it appears.
              */
             int id;
 
             /**
              * Attack name.
              *
-             * Not found in the same file as the rest of the data, but on file 18.
+             * Not in the same file as the rest of the data, but on file 18.
              */
             std::string name;
 
             /**
              * Attack description.
              *
-             * Not found in the same file as the rest of the data, but on file 10.
+             * Not in the same file as the rest of the data, but on file 10.
              */
             std::string description;
 
@@ -1107,9 +1118,9 @@ class KernelDataInstaller{
         /**
          * Data for each items
          *
-         * As found in KERNEL.BIN, file 4, in order, up to special. Members after special are not
-         * found literally in KERNEL.BIN, but are derived from other members or found in other
-         * files of the kernel (text files).
+         * As found in KERNEL.BIN, file 4, in order, up to special. Members
+         * after special are not found literally in KERNEL.BIN, but are derived
+         * from other members or found in other files (text files).
          */
         struct ItemData{
 
@@ -1133,8 +1144,8 @@ class KernelDataInstaller{
             /**
              * Item restrictions. 2 bytes.
              *
-             * Contains bits indicating if the item is {@see selleable}, {@see useable_battle} and
-             * {@see useable_menu}.
+             * Contains bits indicating if the item is {@see selleable},
+             * {@see useable_battle} and {@see useable_menu}.
              */
             u16 restrict_raw;
 
@@ -1155,11 +1166,13 @@ class KernelDataInstaller{
             /**
              * Damage formula. 1 byte.
              *
-             * This byte is divided into two nybbles (four bits). Upper nybble determines what
-             * considerations are made in calculating damage such as physical/magical, allowing
-             * criticals, how to calculate accuracy, etc. There are also three sets of known
-             * formulae that are paired with the upper nybble values. These are selected in the
-             * Lower Nybble and determine how to calculate pre-defense damage
+             * This byte is divided into two nybbles (four bits). Upper nybble
+             * determines what considerations are made in calculating damage
+             * such as physical/magical, allowing criticals, how to calculate
+             * accuracy, etc. There are also three sets of known formulae that
+             * are paired with the upper nybble values. These are selected in
+             * the Lower Nybble and determine how to calculate pre-defense
+             * damage.
              */
             u8 damage_raw;
 
@@ -1173,7 +1186,7 @@ class KernelDataInstaller{
             /**
              * The restore type.
              *
-             * See {@see restore_type} and {@see RESTORE_TYPE} for more information.
+             * See {@see restore_type} and {@see RESTORE_TYPE}.
              */
             u8 condition_raw;
 
@@ -1201,9 +1214,10 @@ class KernelDataInstaller{
             /**
              * Information about what status can be inflicted or cured. 4 bytes.
              *
-             * The first six bits are the chance of inflicting/curing (out of 63). If the seventh
-             * bit is set, the status is cured instead of inflicted. If the eighth bit is set, then
-             * the status is toggled (cured if present, inflicted if not present).
+             * The first six bits are the chance of inflicting/curing (out of
+             * 63). If the seventh bit is set, the status is cured instead of
+             * inflicted. If the eighth bit is set, then the status is toggled
+             * (cured if present, inflicted if not present).
              */
             u32 status_raw;
 
@@ -1223,21 +1237,21 @@ class KernelDataInstaller{
             /**
              * Item ID.
              *
-             * Not actually in the item file data, it's the order at which it appears.
+             * Not in the item file data, it's the order at which it appears.
              */
             int id;
 
             /**
              * Item name.
              *
-             * Not found in the same file as the rest of the data, but on file 19.
+             * Not in the same file as the rest of the data, but on file 19.
              */
             std::string name;
 
             /**
              * Item description.
              *
-             * Not found in the same file as the rest of the data, but on file 11.
+             * Not in the same file as the rest of the data, but on file 11.
              */
             std::string description;
 
@@ -1319,9 +1333,9 @@ class KernelDataInstaller{
             /**
              * A linked slot.
              *
-             * If the slot is in an even position, it's linked to the one in the right. If it's in
-             * an odd position, it;s linked to the one in the Left. Remember that slot positions
-             * are 0-index.
+             * If the slot is in an even position, it's linked to the one in the
+             * right. If it's in an odd position, it's linked to the one in the
+             * left. Remember that slot positions are 0-index.
              */
             SLOT_LINKED = 1
         };
@@ -1345,9 +1359,9 @@ class KernelDataInstaller{
         /**
          * Data for each weapon
          *
-         * As found in KERNEL.BIN, file 5, in order, up to special. Members after special are not
-         * found literally in KERNEL.BIN, but are derived from other members or found in other
-         * files of the kernel (text files).
+         * As found in KERNEL.BIN, file 5, in order, up to special. Members
+         * after special are not found literally in KERNEL.BIN, but are derived
+         * from other members or found in other kernel files (text files).
          */
         struct WeaponData{
 
@@ -1366,11 +1380,13 @@ class KernelDataInstaller{
             /**
              * Damage formula. 1 byte.
              *
-             * This byte is divided into two nybbles (four bits). Upper nybble determines what
-             * considerations are made in calculating damage such as physical/magical, allowing
-             * criticals, how to calculate accuracy, etc. There are also three sets of known
-             * formulae that are paired with the upper nybble values. These are selected in the
-             * Lower Nybble and determine how to calculate pre-defense damage
+             * This byte is divided into two nybbles (four bits). Upper nybble
+             * determines what considerations are made in calculating damage
+             * such as physical/magical, allowing criticals, how to calculate
+             * accuracy, etc. There are also three sets of known formulae that
+             * are paired with the upper nybble values. These are selected in
+             * the Lower Nybble and determine how to calculate pre-defense
+             * damage.
              */
             u8 damage_raw;
 
@@ -1409,9 +1425,10 @@ class KernelDataInstaller{
             /**
              * Weapon battle model. 1 byte.
              *
-             * This byte is divided into two nybbles (four bits). Upper nybble determines the
-             * attack animation modifier, and it's only used for Barret and Vincent. The lower
-             * nybble is actually the index of the weapon model.
+             * This byte is divided into two nybbles (four bits). Upper nybble
+             * determines the attack animation modifier, and it's only used for
+             * Barret and Vincent. The lower nybble is actually the index of the
+             * weapon model.
              */
             u8 model_raw;
 
@@ -1430,15 +1447,16 @@ class KernelDataInstaller{
             /**
              * Camera movement ID. 2 bytes.
              *
-             * Used for both single and multiple targeted attacks with this weapon. Always 0xFFFF.
+             * Used for both single and multiple targeted attacks with this
+             * weapon. Always 0xFFFF.
              */
             u16 camera;
 
             /**
              * Characters that can equip the weapon. 2 bytes.
              *
-             * Contains bits indicating the characters that can equip the weapon {@see character}
-             * for more info.
+             * Contains bits indicating the characters that can equip the weapon
+             * {@see character} for more info.
              */
             u16 equip_raw;
 
@@ -1506,29 +1524,31 @@ class KernelDataInstaller{
             /**
              * Weapon restrictions. 2 bytes.
              *
-             * Contains bits indicating if the item is {@see selleable}, {@see useable_battle} and
-             * {@see useable_menu}and {@see throwable}.
+             * Contains bits indicating if the item is {@see selleable},
+             * {@see useable_battle} and {@see useable_menu} and
+             * {@see throwable}.
              */
             u16 restrict_raw;
 
             /**
              * Weapon ID.
              *
-             * Not actually in the item file data, it's the order at which it appears.
+             * Not actually in the item file data, it's the order at which it
+             * appears.
              */
             int id;
 
             /**
              * Weapon name.
              *
-             * Not found in the same file as the rest of the data, but on file 20.
+             * Not in the same file as the rest of the data, but on file 20.
              */
             std::string name;
 
             /**
              * Weapon description.
              *
-             * Not found in the same file as the rest of the data, but on file 12.
+             * Not in the same file as the rest of the data, but on file 12.
              */
             std::string description;
 
@@ -1584,7 +1604,8 @@ class KernelDataInstaller{
             /**
              * Attack animation ID.
              *
-             * It's the upper nybble in {@see model_raw}. It's only used for Barret and Vincent.
+             * It's the upper nybble in {@see model_raw}. It's only used for
+             * Barret and Vincent.
              */
             u8 animation_index;
 
@@ -1628,9 +1649,10 @@ class KernelDataInstaller{
         /**
          * Data for each armor
          *
-         * As found in KERNEL.BIN, file 6, in order, up to unknown_3. Members after unknown_3 are
-         * not found literally in KERNEL.BIN, but are derived from other members or found in other
-         * files of the kernel (text files).
+         * As found in KERNEL.BIN, file 6, in order, up to unknown_3. Members
+         * after unknown_3 are not found literally in KERNEL.BIN, but are
+         * derived from other members or found in other kernel files (text
+         * files).
          */
         struct ArmorData{
 
@@ -1695,8 +1717,8 @@ class KernelDataInstaller{
             /**
              * Characters that can equip the armor. 2 bytes.
              *
-             * Contains bits indicating the characters that can equip the armor {@see character}
-             * for more info.
+             * Contains bits indicating the characters that can equip the armor
+             * {@see character} for more info.
              */
             u16 equip_raw;
 
@@ -1727,8 +1749,9 @@ class KernelDataInstaller{
             /**
              * Weapon restrictions. 2 bytes.
              *
-             * Contains bits indicating if the item is {@see selleable}, {@see useable_battle} and
-             * {@see useable_menu}and {@see throwable}.
+             * Contains bits indicating if the item is {@see selleable},
+             * {@see useable_battle} and {@see useable_menu} and
+             * {@see throwable}.
              */
             u16 restrict_raw;
 
@@ -1740,21 +1763,21 @@ class KernelDataInstaller{
             /**
              * Armor ID.
              *
-             * Not actually in the item file data, it's the order at which it appears.
+             * Not in the item file data, it's the order at which it appears.
              */
             int id;
 
             /**
              * Armor name.
              *
-             * Not found in the same file as the rest of the data, but on file 20.
+             * Not in the same file as the rest of the data, but on file 20.
              */
             std::string name;
 
             /**
              * Armor description.
              *
-             * Not found in the same file as the rest of the data, but on file 12.
+             * Not in the same file as the rest of the data, but on file 12.
              */
             std::string description;
 
@@ -1812,9 +1835,10 @@ class KernelDataInstaller{
         /**
          * Data for each accessory
          *
-         * As found in KERNEL.BIN, file 7, in order, up to unknown_3. Members after unknown_3 are
-         * not found literally in KERNEL.BIN, but are derived from other members or found in other
-         * files of the kernel (text files).
+         * As found in KERNEL.BIN, file 7, in order, up to unknown_3. Members
+         * after unknown_3 are not found literally in KERNEL.BIN, but are not
+         * found literally in KERNEL.BIN, but are derived from other members or
+         * found in other files of the kernel (text files).
          */
         struct AccessoryData{
 
@@ -1872,7 +1896,8 @@ class KernelDataInstaller{
             /**
              * Characters that can equip the armor. 2 bytes.
              *
-             * Contains bits indicating the characters that can equip the armor {@see character}
+             * Contains bits indicating the characters that can equip the armor
+             * {@see character} for more info. 
              * for more info.
              */
             u16 equip_raw;
@@ -1880,29 +1905,30 @@ class KernelDataInstaller{
             /**
              * Weapon restrictions. 2 bytes.
              *
-             * Contains bits indicating if the item is {@see selleable}, {@see useable_battle} and
-             * {@see useable_menu}and {@see throwable}.
+             * Contains bits indicating if the item is {@see selleable},
+             * {@see useable_battle} and {@see useable_menu} and
+             * {@see throwable}.
              */
             u16 restrict_raw;
 
             /**
              * Accessory ID.
              *
-             * Not actually in the item file data, it's the order at which it appears.
+             * Not in the item file data, it's the order at which itappears.
              */
             int id;
 
             /**
              * Accessory name.
              *
-             * Not found in the same file as the rest of the data, but on file 20.
+             * Not in the same file as the rest of the data, but on file 20.
              */
             std::string name;
 
             /**
              * Accessory description.
              *
-             * Not found in the same file as the rest of the data, but on file 12.
+             * Not in the same file as the rest of the data, but on file 12.
              */
             std::string description;
 
@@ -2044,9 +2070,10 @@ class KernelDataInstaller{
         /**
          * Data for each materia.
          *
-         * As found in KERNEL.BIN, file 8, in order, up to attributes. Members after attributes are
-         * not found literally in KERNEL.BIN, but are derived from other members or found in other
-         * files of the kernel (text files).
+         * As found in KERNEL.BIN, file 8, in order, up to attributes. Members
+         * after attributes are not found literally in KERNEL.BIN, but are
+         * derived from other members or found in other files of the kernel
+         * (text files).
          */
         struct MateriaData{
 
@@ -2075,8 +2102,8 @@ class KernelDataInstaller{
             /**
              * Materia type. 1 byte.
              *
-             * 2 nybbles. The upper one is the subtype, and it's ignored in V-Gears. The lower one
-             * the actual type.
+             * 2 nybbles. The upper one is the subtype, and it's ignored in
+             * V-Gears. The lower one the is actual type.
              */
             u8 type_raw;
 
@@ -2090,21 +2117,21 @@ class KernelDataInstaller{
             /**
              * Accessory ID.
              *
-             * Not actually in the item file data, it's the order at which it appears.
+             * Not in the item file data, it's the order at which it appears.
              */
             int id;
 
             /**
              * Accessory name.
              *
-             * Not found in the same file as the rest of the data, but on file 20.
+             * Not in the same file as the rest of the data, but on file 20.
              */
             std::string name;
 
             /**
              * Accessory description.
              *
-             * Not found in the same file as the rest of the data, but on file 12.
+             * Not in the same file as the rest of the data, but on file 12.
              */
             std::string description;
 
@@ -2153,7 +2180,8 @@ class KernelDataInstaller{
             /**
              * Number of times per battle an attack can be used, per level.
              *
-             * -1 means infinite times. Derived from {@see type_raw} and {@see attribute}.
+             * -1 means infinite times. Derived from {@see type_raw} and
+             * {@see attribute}.
              */
             int attack_times[6];
 
@@ -2167,8 +2195,8 @@ class KernelDataInstaller{
             /**
              * Parameter for the ability, varies per level.
              *
-             * It can be the number of times the ability can be executed, or the chance to
-             * activate...
+             * It can be the number of times the ability can be executed, or the
+             * chance to activate...
              */
             int ability_parameters[5];
         };
@@ -2176,15 +2204,15 @@ class KernelDataInstaller{
         /**
          * Data for each key item.
          *
-         * There is no data file about key items in KERNEL.BIN, there are only names (file 24) and
-         * descriptions (file 16)
+         * There is no data file about key items in KERNEL.BIN, there are only
+         * names (file 24) and descriptions (file 16)
          */
         struct KeyItemData{
 
             /**
              * Key item ID.
              *
-             * Not actually in KERNEL.BIN, it;s the order at which they appear.
+             * Not in KERNEL.BIN, it's the order at which they appear.
              */
             int id;
 
@@ -2202,7 +2230,8 @@ class KernelDataInstaller{
         /**
          * List of summon names.
          *
-         * There is no data file about key items in KERNEL.BIN, there are only names (file 26).
+         * There is no data file about key items in KERNEL.BIN, there are only
+         * names (file 26).
          */
         struct SummonNameData{
 
@@ -2222,10 +2251,11 @@ class KernelDataInstaller{
         /**
          * Character data.
          *
-         * Contains information about a character. Contained in the file 3 of the kernel, one
-         * entry for each character. Each section is 56 bytes long. The character ID is not
-         * actually in the data, but it's the order at which they appear. The character name is
-         * also not in the dataand  it must be filled manually.
+         * Contains information about a character. Contained in the file 3 of
+         * the kernel, one entry for each character. Each section is 56 bytes
+         * long. The character ID is not actually in the data, but it's the
+         * order at which they appear. The character name is also not in the
+         * data and it must be filled manually.
          */
         struct CharacterData{
 
@@ -2424,7 +2454,8 @@ class KernelDataInstaller{
         /**
          * Types of stat curves.
          *
-         * Althought any of them can be used for any stat, this is their intended use.
+         * Althought any of them can be used for any stat, this is their
+         * intended use.
          */
         enum STAT_CURVE_TYPE{
 
@@ -2444,7 +2475,8 @@ class KernelDataInstaller{
             CURVE_MP,
 
             /**
-             * Experience curve. Base must be ignored when calculating. Gradient is quadratic.
+             * Experience curve. Base must be ignored when calculating. Gradient
+             * is quadratic.
              */
             CURVE_EXP
 
@@ -2482,9 +2514,10 @@ class KernelDataInstaller{
         /**
          * Battle data.
          *
-         * Contains several pieces of data related to character growth. Contained in the file 3 of
-         * the kernel, after the character data (starts at offset 0x01F8). The kernel contains more
-         * data, but it's not needed for V-Gears (random table, scene lookup...)
+         * Contains several pieces of data related to character growth.
+         * Contained in the file 3 of the kernel, after the character data
+         * (starts at offset 0x01F8). The kernel contains more data, but it's
+         * not needed for V-Gears (random table, scene lookup...)
          */
         struct GrowthData{
 
@@ -2515,7 +2548,8 @@ class KernelDataInstaller{
          * Retrieves the 3-letter name of a basic stat from it's id.
          *
          * @param[in] id The stat ID.
-         * @return Stat name abbreviation. If the stat ID is unknown, "stat" will be returned.
+         * @return Stat name abbreviation. If the stat ID is unknown, "stat"
+         * will be returned.
          */
         std::string GetBasicStatName(int id);
 

+ 42 - 77
src/installer/MainWindow.cpp

@@ -26,17 +26,15 @@
 #include "DataInstaller.h"
 #include "MainWindow.h"
 #include "ui_MainWindow.h"
-#include "Release.h"
 
 /**
  * Indicates if an installer has already been created.
  */
 static bool installer_created = false;
 
-Release release;
-
 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), main_window_(new Ui::MainWindow){
     main_window_->setupUi(this);
+    main_window_->advancedOptions->setVisible(main_window_->chk_advanced_options->isChecked());
     // Goto the data tab by default.
     main_window_->tabWidget->setCurrentIndex(0);
     // Turn off the launcher tab for now since it dosen't actually work
@@ -45,8 +43,6 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), main_window_(new U
     main_window_->btn_vgears_config->setText(settings_->value("ConfigDir").toString());
     main_window_->line_data_dst->setText(settings_->value("DataDir").toString());
     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_data_dst->setText("/home/ivalentin/.v-gears/data/");
     timer_ = new QTimer(this);
     connect(timer_, SIGNAL(timeout()), this, SLOT(DoProgress()));
@@ -63,9 +59,7 @@ void MainWindow::InitSettings(void){
     );
     win = true;
 #else
-    settings_ = new QSettings(
-      QSettings::NativeFormat,QSettings::UserScope, "v-gears", "launcher", 0
-    );
+    settings_ = new QSettings(QSettings::NativeFormat,QSettings::UserScope, "v-gears", "launcher", 0);
 #endif
     //Check settings.
     if (settings_->value("ConfigDir").isNull()){
@@ -79,11 +73,7 @@ void MainWindow::InitSettings(void){
     }
 
     if (settings_->value("VGearsEXE").isNull()){
-        if (win){
-            settings_->setValue(
-              "VGearsEXE",QString("%1/v-gears.exe").arg(QCoreApplication::applicationDirPath())
-            );
-        }
+        if (win) settings_->setValue("VGearsEXE",QString("%1/v-gears.exe").arg(QCoreApplication::applicationDirPath()));
         else settings_->setValue("VGearsEXE", QString("/usr/games/v-gears"));
     }
 }
@@ -94,8 +84,7 @@ void MainWindow::on_line_vgears_config_editingFinished(){
 
 void MainWindow::on_btn_vgears_config_clicked(){
     QString temp = QFileDialog::getExistingDirectory(
-      this, tr("Select Location of VGears Configuration Data,"),
-      settings_->value("ConfigDir").toString()
+      this, tr("Select Location of VGears Configuration Data,"), settings_->value("ConfigDir").toString()
     );
     if (!temp.isNull()){
         settings_->setValue("ConfigDir", temp);
@@ -108,9 +97,7 @@ void MainWindow::on_line_vgears_exe_editingFinished(){
 }
 
 void MainWindow::on_btn_vgears_exe_clicked(){
-    QString temp = QFileDialog::getOpenFileName(
-      this, tr("Location of VGears Executable,"), QDir::rootPath()
-    );
+    QString temp = QFileDialog::getOpenFileName(this, tr("Location of VGears Executable,"), QDir::rootPath());
     if (!temp.isNull()){
         settings_->setValue("VGearsEXE", temp);
         main_window_->line_vgears_exe->setText(temp);
@@ -121,15 +108,9 @@ void MainWindow::on_btn_vgears_run_clicked(){
     QString configDir(main_window_->btn_vgears_config->text());
     QString exe(main_window_->line_vgears_exe->text());
     QStringList args;
-    args.append(
-      QString("--resources-file=%1/resources.cfg").arg(main_window_->btn_vgears_config->text())
-    );
-    args.append(
-      QString("--config-file=%1/v-gears.cfg").arg(main_window_->btn_vgears_config->text())
-    );
-    args.append(
-      QString("--plugins-file=/%1/plugins.cfg").arg(main_window_->btn_vgears_config->text())
-    );
+    args.append(QString("--resources-file=%1/resources.cfg").arg(main_window_->btn_vgears_config->text()));
+    args.append(QString("--config-file=%1/v-gears.cfg").arg(main_window_->btn_vgears_config->text()));
+    args.append(QString("--plugins-file=/%1/plugins.cfg").arg(main_window_->btn_vgears_config->text()));
     // Check that the config dir is set up correctly.
     QProcess vGears;
     vGears.startDetached(exe,args,configDir);
@@ -138,23 +119,20 @@ void MainWindow::on_btn_vgears_run_clicked(){
 void MainWindow::on_btn_data_src_clicked(){
     const QString filter = tr("ISO images (*.iso)");
     QString temp = QFileDialog::getOpenFileName(
-      this,
-      tr("Select ISO image"),
-      settings_->value("DataDir").toString(),
-      filter
+      this, tr("Select ISO image"), settings_->value("DataDir").toString(), filter
     );
     std::cout << "Selected ISO: " << temp.toStdString() << std::endl;
     if (!temp.isNull()){
         main_window_->line_data_src->setText(temp);
-        release = Release(temp.toStdString());
-        main_window_->isoData->setText(QString::fromStdString(release.getId()));
-        if (!release.isValid()){
+        disk_image_ = DiskImage(temp.toStdString());
+        main_window_->isoData->setText(QString::fromStdString(disk_image_.getId()));
+        if (!disk_image_.isValid()){
             main_window_->isoError->setStyleSheet("QLabel { color : red; }");
-            main_window_->isoError->setText(QString::fromStdString(release.getErrorMessage()));
+            main_window_->isoError->setText(QString::fromStdString(disk_image_.getErrorMessage()));
         }
-        else if (!release.isSupported()){
+        else if (!disk_image_.isSupported()){
             main_window_->isoError->setStyleSheet("QLabel { color : orange; }");
-            main_window_->isoError->setText(QString::fromStdString(release.getWarningMessage()));
+            main_window_->isoError->setText(QString::fromStdString(disk_image_.getWarningMessage()));
         }
         else{
             main_window_->isoError->setStyleSheet("QLabel { color : green; }");
@@ -181,34 +159,29 @@ void MainWindow::on_btn_data_run_clicked(){
     if (main_window_->line_data_src->text().isEmpty()){
         QMessageBox::critical(
           this, tr("Input error"),
-          tr(
-            "The installation needs a Final Fantasy VII ISO image.\n\n"
-            "Select the ISO from the PC install disk."
-          )
+          tr("The installation needs a Final Fantasy VII ISO image.\n\nSelect the ISO from the PC install disk.")
         );
     }
-    if (release.isValid() == false){
+    if (disk_image_.isValid() == false){
         QMessageBox::critical(
           this, tr("Input error"),
-          tr(
-            "The selected ISO is not a valid Final Fantasy VII game."
-            "\n\nSelect a valid ISO image of Final Fantasy VII.")
+          tr("The selected ISO is not a valid Final Fantasy VII game.\n\nSelect a valid image of Final Fantasy VII.")
         );
     }
     else if (main_window_->line_data_dst->text().isEmpty())
         QMessageBox::critical(this, tr("Output error"), tr("No output path provided"));
     else{
-      // Normalize the output path and extract to original_data.
+        // Normalize the output path and extract to original_data.
         QString output = QDir::fromNativeSeparators(main_window_->line_data_dst->text());
         if (!output.endsWith("/")) output += "/";
 
-        bool extraction_success = release.extractIso(output.toStdString());
+        bool extraction_success = disk_image_.extractImage(output.toStdString());
         if (!extraction_success) {
             QMessageBox::critical(this, tr("Extraction error"), tr("Failed to extract ISO file."));
             return;
         }
 
-      QString input = QDir::fromNativeSeparators(QString::fromStdString(release.getContentPath()));
+      QString input = QDir::fromNativeSeparators(QString::fromStdString(disk_image_.getContentPath()));
       if (input.isEmpty()) {
         QMessageBox::critical(this, tr("Extraction error"), tr("Extracted data path is invalid."));
         return;
@@ -237,14 +210,13 @@ void MainWindow::on_btn_data_run_clicked(){
 
         // Ensure required files are in the input dir
         for (auto& file : required_files){
-            QString full_path = input + QString::fromStdString(file);
-            if (!QFile::exists(full_path)){
+            std::cout << "Checking for required file: " << file << std::endl;
+            const auto matched_file = disk_image_.filePath(file);
+            if (!matched_file){
                 QMessageBox::critical(
                   this, tr("Missing input file"),
-                  tr(
-                    "Some of the required files were not found among the extracted data.\n\n"
-                    " Make sure that the file '"
-                  ) + file.c_str() + tr("' is in the extracted data.")
+                  tr("Some of the required files were not found among the extracted data.\n\nMake sure that the file '")
+                    + file.c_str() + tr("' is in the extracted data.")
                 );
                 return;
             }
@@ -253,36 +225,31 @@ void MainWindow::on_btn_data_run_clicked(){
             // Due to the use of singletons, the installer can't be re-run.
             // TODO: Verify that this is true, and if so, try to fix it.
             QMessageBox::critical(
-              this, tr("Error"),
-              tr("Please, fix the errors, and then close the installer before trying again.")
+              this, tr("Error"), tr("Please, fix the errors, and then close the installer before trying again.")
             );
             return;
         }
         // Start data conversion
         try{
-
             // Parse advanced options.
             DataInstaller::AdvancedOptions options;
-            options.skip_battle_data
-              = (Qt::Checked == main_window_->chk_no_battle_data->checkState());
-            options.skip_battle_models
-              = (Qt::Checked == main_window_->chk_no_battle_models->checkState());
-            options.skip_kernel = (Qt::Checked == main_window_->chk_no_kernel->checkState());
-            options.skip_images = (Qt::Checked == main_window_->chk_no_images->checkState());
-            options.skip_sounds = (Qt::Checked == main_window_->chk_no_sounds->checkState());
-            options.skip_music = (Qt::Checked == main_window_->chk_no_music->checkState());
-            options.skip_fields = (Qt::Checked == main_window_->chk_no_fields->checkState());
-            options.skip_field_models
-              = (Qt::Checked == main_window_->chk_no_field_models->checkState());
-            options.skip_wm = (Qt::Checked == main_window_->chk_no_wm->checkState());
-            options.skip_wm_models = (Qt::Checked == main_window_->chk_no_wm_models->checkState());
-            options.no_ffmpeg = (Qt::Checked == main_window_->chk_no_ffmpeg->checkState());
-            options.no_timidity = (Qt::Checked == main_window_->chk_no_timidity->checkState());
+            options.battle_data = (Qt::Checked == main_window_->chk_battle_data->checkState());
+            options.battle_models = (Qt::Checked == main_window_->chk_battle_models->checkState());
+            options.kernel = (Qt::Checked == main_window_->chk_kernel->checkState());
+            options.images = (Qt::Checked == main_window_->chk_images->checkState());
+            options.sounds = (Qt::Checked == main_window_->chk_sounds->checkState());
+            options.music = (Qt::Checked == main_window_->chk_music->checkState());
+            options.videos = (Qt::Checked == main_window_->chk_videos->checkState());
+            options.fields = (Qt::Checked == main_window_->chk_fields->checkState());
+            options.field_models = (Qt::Checked == main_window_->chk_field_models->checkState());
+            options.wm = (Qt::Checked == main_window_->chk_wm->checkState());
+            options.wm_models = (Qt::Checked == main_window_->chk_wm_models->checkState());
+            options.ffmpeg = (Qt::Checked == main_window_->chk_ffmpeg->checkState());
+            options.timidity = (Qt::Checked == main_window_->chk_timidity->checkState());
             options.keep_originals = (Qt::Checked == main_window_->chk_keep_original->checkState());
-
             installer_created = true;
             installer_ = std::make_unique<DataInstaller>(
-              QDir::toNativeSeparators(input).toStdString(),
+              disk_image_,
               QDir::toNativeSeparators(output).toStdString(),
               options,
               [this](const std::string log_line, int level, bool as_progress = false){
@@ -301,9 +268,7 @@ void MainWindow::on_btn_data_run_clicked(){
 }
 
 void MainWindow::on_chk_advanced_options_stateChanged(){
-    if (main_window_->chk_advanced_options->checkState() == Qt::Checked)
-        main_window_->advancedOptions->setMaximumHeight(500);
-    else main_window_->advancedOptions->setMaximumHeight(0);
+    main_window_->advancedOptions->setVisible(main_window_->chk_advanced_options->checkState() == Qt::Checked);
 }
 
 void MainWindow::EnableUi(bool enable){

+ 10 - 4
src/installer/MainWindow.h

@@ -15,9 +15,10 @@
 
 #pragma once
 
-#include <QtWidgets/QMainWindow> // IVV fix path #include <QMainWindow>
+#include <QtWidgets/QMainWindow>
 #include <QtCore/QSettings>
 #include <memory>
+#include "DiskImage.h"
 
 namespace Ui {
     class MainWindow;
@@ -147,9 +148,9 @@ class MainWindow : public QMainWindow{
          */
         void InitSettings(void);
 
-        // The Q_OBJECT macro must appear in the private section of a class definition
-        //that declares its own signals and slots or that uses other services provided
-        // by Qt's meta-object system.
+        // The Q_OBJECT macro must appear in the private section of a class
+        // definition that declares its own signals and slots or that uses other
+        // services provided by Qt's meta-object system.
         Q_OBJECT
 
         /**
@@ -171,4 +172,9 @@ class MainWindow : public QMainWindow{
          * The installer.
          */
         std::unique_ptr<class DataInstaller> installer_;
+
+        /**
+         * The selected disk image.
+         */
+        DiskImage disk_image_;
 };

+ 683 - 186
src/installer/MainWindow.ui

@@ -7,9 +7,15 @@
     <x>0</x>
     <y>0</y>
     <width>720</width>
-    <height>703</height>
+    <height>705</height>
    </rect>
   </property>
+  <property name="minimumSize">
+   <size>
+    <width>0</width>
+    <height>0</height>
+   </size>
+  </property>
   <property name="windowTitle">
    <string>VGears Installer</string>
   </property>
@@ -24,6 +30,12 @@
        <number>0</number>
       </property>
       <widget class="QWidget" name="tab_installer">
+       <property name="minimumSize">
+        <size>
+         <width>698</width>
+         <height>630</height>
+        </size>
+       </property>
        <attribute name="title">
         <string>Data Installer</string>
        </attribute>
@@ -71,9 +83,7 @@
           <item>
            <widget class="QPushButton" name="help_data_src">
             <property name="toolTip">
-             <string>
-              &lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Path to the extracted FFVII (PC version) data.&lt;/b&gt;&lt;br&gt;&lt;br&gt;It must contain (at least) the folders 'kernel' and 'field'.&lt;/body&gt;&lt;/html&gt;
-             </string>
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Path to a FFVII (PC version) ISO image.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
             </property>
             <property name="text">
              <string/>
@@ -189,204 +199,646 @@
         </item>
         <item>
          <widget class="QGroupBox" name="advancedOptions">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
           <property name="sizePolicy">
-           <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
+          <property name="minimumSize">
+           <size>
+            <width>680</width>
+            <height>180</height>
+           </size>
+          </property>
           <property name="maximumSize">
            <size>
             <width>16777215</width>
-            <height>0</height>
+            <height>180</height>
            </size>
           </property>
           <property name="title">
            <string/>
           </property>
-          <layout class="QVBoxLayout" name="verticalLayout_5">
-           <property name="spacing">
-            <number>5</number>
-           </property>
-           <property name="leftMargin">
-            <number>11</number>
-           </property>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_6">
-             <item>
-              <widget class="QCheckBox" name="chk_no_battle_data">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract battle data.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, battle data such as enemies, attacks or enemy formations will not be extracted. Data from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract battle data</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QCheckBox" name="chk_no_battle_models">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract battle models.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, battle 3D models will not be extracted. This includes models for players, enemies, attacks, battle scenarios... Models from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the models.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract battle models</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_6">
-             <item>
-              <widget class="QCheckBox" name="chk_no_kernel">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract kernel data.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, game data such as items, materia, character information or initial savemap will not be extracted. Data from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract kernel data</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QCheckBox" name="chk_no_images">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract images.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, images will not be extracted. This includes window decorations, menus icons, character portraits... It doesn' include backgrounds or textures. Images from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the images.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract images</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_7">
-             <item>
-              <widget class="QCheckBox" name="chk_no_sounds">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract sounds.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, sound effects will not be extracted. This doesn' include music tracks. Sounds from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;Installing sounds can take a few minutes, and it's OK to skip this step if they are already installed. You can also check this if you are getting errors installing sounds or if FFmpeg is not installed in your system.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract sounds</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QCheckBox" name="chk_no_music">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract audio tracks.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, audio tracks will not be extracted. This doesn' include sound effects. Tracks from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;Installing music can take a few minutes, and it's OK to skip this step if they are already installed. You can also check this if you are getting errors installing sounds or if FFmpeg or TiMidity are not installed in your system.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract music tracks</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_8">
-             <item>
-              <widget class="QCheckBox" name="chk_no_fields">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract field maps.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, map fields will not be extracted. Field maps from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the fields. Skipping field map installation will also skip the installation of field 3D models.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract field maps</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QCheckBox" name="chk_no_field_models">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract field 3D models.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, field 3D models and textures will not be extracted. Models from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping field map installation will also skip the installation of field 3D models.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Dont't extract field 3D models</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_9">
-             <item>
-              <widget class="QCheckBox" name="chk_no_wm">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract world map data.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, world map models and data will not be extracted. Data from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the world map data. Skipping world map data installation will also skip the installation of the world map 3D models.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Don't extract world map data</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QCheckBox" name="chk_no_wm_models">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract world map 3D models.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, world map 3D models and textures will not be extracted. Models from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping world map data installation will also skip the installation of world map 3D models.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Dont't extract world 3D models</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_5">
-             <item>
-              <widget class="QCheckBox" name="chk_no_ffmpeg">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't use the ffmpeg command.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, sound effects and background music will not be available to V-Gears.&lt;br&gt;&lt;br&gt;Sound effects and music indexes will still be built, but you will need to provide your own ogg files.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Prevent FFmpeg calls</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QCheckBox" name="chk_no_timidity">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't use the timidity command.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, background music will not be available to V-Gears.&lt;br&gt;&lt;br&gt;Music indexes will still be built, but you will need to provide your own ogg tracks.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Prevent TiMidity calls</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-           <item>
-            <layout class="QHBoxLayout" name="horizontalLayout_9">
-             <item>
-              <widget class="QCheckBox" name="chk_keep_original">
-               <property name="toolTip">
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't delete original data after installation.&lt;/b&gt;&lt;br&gt;&lt;br&gt;The installer extracts some original data from the installation disk, such as MIDI sounds, TEX images, and some LGP archives. If checked, this data will not be deleted when the installation is complete.&lt;br&gt;&lt;br&gt;This data is never used by V-Gears, and there is usually no need to check this.&lt;/body&gt;&lt;/html&gt;</string>
-               </property>
-               <property name="text">
-                <string>Preserve original data</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-          </layout>
-         </widget>
-        </item>
-        <item>
-         <spacer name="verticalSpacer">
-          <property name="orientation">
-           <enum>Qt::Orientation::Vertical</enum>
+          <property name="alignment">
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
           </property>
-          <property name="sizeType">
-           <enum>QSizePolicy::Policy::Fixed</enum>
+          <property name="flat">
+           <bool>false</bool>
           </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>20</width>
-            <height>40</height>
-           </size>
+          <property name="checkable">
+           <bool>false</bool>
           </property>
-         </spacer>
+          <widget class="QLabel" name="advancedLabelBattle">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>10</y>
+             <width>130</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="font">
+            <font>
+             <bold>true</bold>
+             <underline>true</underline>
+            </font>
+           </property>
+           <property name="text">
+            <string>Battle data</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+           </property>
+          </widget>
+          <widget class="QLabel" name="advancedLabelGame">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>30</y>
+             <width>130</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="font">
+            <font>
+             <bold>true</bold>
+             <underline>true</underline>
+            </font>
+           </property>
+           <property name="text">
+            <string>Game data</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+           </property>
+          </widget>
+          <widget class="QLabel" name="advancedLabelMedia">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>50</y>
+             <width>130</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="font">
+            <font>
+             <bold>true</bold>
+             <underline>true</underline>
+            </font>
+           </property>
+           <property name="text">
+            <string>Multimedia</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_sounds">
+           <property name="geometry">
+            <rect>
+             <x>360</x>
+             <y>50</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Don't extract sound.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;If checked, game sound efects will not be extracted.&lt;br/&gt;&lt;br/&gt;Sound extraction can take a few minutes, and it's ok to skip this step if it has already been installed.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract sounds</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_music">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>70</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Don't extract music.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;If checked, game music tracks will not be extracted.&lt;br/&gt;&lt;br/&gt;Music extraction can take a few minutes, and it's ok to skip this step if it has already been installed.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract music</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QLabel" name="advancedLabelFields">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>110</y>
+             <width>130</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="font">
+            <font>
+             <bold>true</bold>
+             <underline>true</underline>
+            </font>
+           </property>
+           <property name="text">
+            <string>Field Maps</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+           </property>
+          </widget>
+          <widget class="QLabel" name="advancedLabelWorld">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>130</y>
+             <width>130</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="font">
+            <font>
+             <bold>true</bold>
+             <underline>true</underline>
+            </font>
+           </property>
+           <property name="text">
+            <string>World Map</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+           </property>
+          </widget>
+          <widget class="QLabel" name="advancedLabelOther">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>150</y>
+             <width>130</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="font">
+            <font>
+             <bold>true</bold>
+             <underline>true</underline>
+            </font>
+           </property>
+           <property name="text">
+            <string>Other</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_clean_install">
+           <property name="geometry">
+            <rect>
+             <x>360</x>
+             <y>150</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Delete previously installed files.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Performs a clean installation, unconditionally removing all previously installed data.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Clean install</string>
+           </property>
+           <property name="checkable">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_wm">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>130</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract world map data.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, world map models and data will not be extracted. Data from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the world map data. Skipping world map data installation will also skip the installation of the world map 3D models.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract world map data</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+           <property name="tristate">
+            <bool>false</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_ffmpeg">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>90</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't use the ffmpeg command.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, sound effects and background music will not be available to V-Gears.&lt;br&gt;&lt;br&gt;Sound effects and music indexes will still be built, but you will need to provide your own ogg files.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Prevent FFmpeg calls</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_images">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>50</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract images.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, images will not be extracted. This includes window decorations, menus icons, character portraits... It doesn' include backgrounds or textures. Images from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the images.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract images</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_battle_data">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>10</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract battle data.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, battle data such as enemies, attacks or enemy formations will not be extracted. Data from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract battle data</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_wm_models">
+           <property name="geometry">
+            <rect>
+             <x>360</x>
+             <y>130</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract world map 3D models.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, world map 3D models and textures will not be extracted. Models from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping world map data installation will also skip the installation of world map 3D models.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Dont't extract world 3D models</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_timidity">
+           <property name="geometry">
+            <rect>
+             <x>360</x>
+             <y>90</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't use the timidity command.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, background music will not be available to V-Gears.&lt;br&gt;&lt;br&gt;Music indexes will still be built, but you will need to provide your own ogg tracks.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Prevent TiMidity calls</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_keep_original">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>150</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't delete original data after installation.&lt;/b&gt;&lt;br&gt;&lt;br&gt;The installer extracts some original data from the installation disk, such as MIDI sounds, TEX images, and some LGP archives. If checked, this data will not be deleted when the installation is complete.&lt;br&gt;&lt;br&gt;This data is never used by V-Gears, and there is usually no need to check this.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Preserve original data</string>
+           </property>
+           <property name="checked">
+            <bool>false</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_fields">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>110</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract field maps.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, map fields will not be extracted. Field maps from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the fields. Skipping field map installation will also skip the installation of field 3D models.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract field maps</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_kernel">
+           <property name="geometry">
+            <rect>
+             <x>150</x>
+             <y>30</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract kernel data.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, game data such as items, materia, character information or initial savemap will not be extracted. Data from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract kernel data</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_field_models">
+           <property name="geometry">
+            <rect>
+             <x>360</x>
+             <y>110</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract field 3D models.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, field 3D models and textures will not be extracted. Models from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping field map installation will also skip the installation of field 3D models.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Dont't extract field 3D models</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_videos">
+           <property name="geometry">
+            <rect>
+             <x>360</x>
+             <y>70</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract videos.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, FMV videos will not be extracted. Videos from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;Installing videos can take a few minutes, and it's OK to skip this step if they are already installed.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract videos</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+          <widget class="QCheckBox" name="chk_battle_models">
+           <property name="geometry">
+            <rect>
+             <x>360</x>
+             <y>10</y>
+             <width>200</width>
+             <height>20</height>
+            </rect>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>200</width>
+             <height>20</height>
+            </size>
+           </property>
+           <property name="baseSize">
+            <size>
+             <width>0</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;b&gt;Don't extract battle models.&lt;/b&gt;&lt;br&gt;&lt;br&gt;If checked, battle 3D models will not be extracted. This includes models for players, enemies, attacks, battle scenarios... Models from previous installations will not be deleted.&lt;br&gt;&lt;br&gt;This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the models.&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>Don't extract battle models</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </widget>
         </item>
         <item>
          <widget class="QPushButton" name="btn_data_run">
@@ -616,6 +1068,9 @@
     <property name="title">
      <string>File</string>
     </property>
+    <addaction name="actionABout"/>
+    <addaction name="actionHelp"/>
+    <addaction name="separator"/>
     <addaction name="actionExit"/>
    </widget>
    <addaction name="menuFile"/>
@@ -625,8 +1080,50 @@
     <string>Exit</string>
    </property>
   </action>
+  <action name="actionABout">
+   <property name="text">
+    <string>About</string>
+   </property>
+  </action>
+  <action name="actionHelp">
+   <property name="text">
+    <string>Help</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
+ <tabstops>
+  <tabstop>tabWidget</tabstop>
+  <tabstop>line_data_src</tabstop>
+  <tabstop>btn_data_src</tabstop>
+  <tabstop>help_data_src</tabstop>
+  <tabstop>line_data_dst</tabstop>
+  <tabstop>btn_data_dst</tabstop>
+  <tabstop>help_data_dest</tabstop>
+  <tabstop>chk_advanced_options</tabstop>
+  <tabstop>chk_battle_data</tabstop>
+  <tabstop>chk_battle_models</tabstop>
+  <tabstop>chk_kernel</tabstop>
+  <tabstop>chk_images</tabstop>
+  <tabstop>chk_sounds</tabstop>
+  <tabstop>chk_music</tabstop>
+  <tabstop>chk_videos</tabstop>
+  <tabstop>chk_ffmpeg</tabstop>
+  <tabstop>chk_timidity</tabstop>
+  <tabstop>chk_fields</tabstop>
+  <tabstop>chk_field_models</tabstop>
+  <tabstop>chk_wm</tabstop>
+  <tabstop>chk_wm_models</tabstop>
+  <tabstop>chk_keep_original</tabstop>
+  <tabstop>chk_clean_install</tabstop>
+  <tabstop>btn_data_run</tabstop>
+  <tabstop>data_log</tabstop>
+  <tabstop>line_vgears_exe</tabstop>
+  <tabstop>btn_vgears_exe</tabstop>
+  <tabstop>line_vgears_config</tabstop>
+  <tabstop>btn_vgears_config</tabstop>
+  <tabstop>btn_vgears_launch</tabstop>
+ </tabstops>
  <resources/>
  <connections>
   <connection>

+ 64 - 47
src/installer/MediaDataInstaller.cpp

@@ -25,6 +25,7 @@
 #include <boost/predef/os.h>
 #include <boost/filesystem.hpp>
 #include <tinyxml.h>
+#include "DiskImage.h"
 #include "MediaDataInstaller.h"
 #include "data/VGearsLGPArchive.h"
 #include "data/VGearsTexFile.h"
@@ -47,14 +48,20 @@
 int MediaDataInstaller::TOTAL_SOUNDS = 750;
 
 MediaDataInstaller::MediaDataInstaller(
-  const std::string input_dir, const std::string output_dir, const bool keep_originals,
-  const bool no_ffmpeg, const bool no_timidity
+  const DiskImage& disk_image, const std::string output_dir, const bool keep_originals,
+  const bool ffmpeg, const bool timidity
 ):
-  input_dir_(input_dir), output_dir_(output_dir), keep_originals_(keep_originals),
-  no_ffmpeg_(no_ffmpeg), no_timidity_(no_timidity),
-  menu_(input_dir + "data/menu/menu_us.lgp", "LGP"), window_(input_dir + "data/kernel/WINDOW.BIN"),
-  midi_(input_dir + "data/midi/midi.lgp", "LGP")
-{PopulateMaps();}
+  disk_image_(disk_image), output_dir_(output_dir), keep_originals_(keep_originals),
+  ffmpeg_(ffmpeg), timidity_(timidity),
+    window_(*disk_image_.filePath("data/kernel/WINDOW.BIN")),
+    processed_sounds_(0),
+    menu_(VGears::LGPArchive(*disk_image_.filePath("data/menu/menu_us.lgp"), "LGP")),
+    midi_(VGears::LGPArchive(*disk_image_.filePath("data/midi/midi.lgp"), "LGP")),
+    processed_musics_(0),
+    processed_videos_(0)
+{
+    PopulateMaps();
+}
 
 
 std::string MediaDataInstaller::GetExecutablePath(){
@@ -72,9 +79,7 @@ std::string MediaDataInstaller::GetExecutablePath(){
     #elif (BOOST_OS_MACOS)
     char exe_path[PATH_MAX];
     uint32_t len = sizeof(exe_path);
-    if (_NSGetExecutablePath(exe_path, &len) != 0){
-        exe_path[0] = '\0'; // buffer too small (!)
-    }
+    if (_NSGetExecutablePath(exe_path, &len) != 0) exe_path[0] = '\0'; // buffer too small (!)
     else {
         // resolve symlinks, ., .. if possible
         char *canonical_path = realpath(exe_path, NULL);
@@ -95,8 +100,7 @@ std::string MediaDataInstaller::GetExecutablePath(){
     #endif
     return
       strlen(exe_path) > 0
-      ? boost::filesystem::path(exe_path).remove_filename().make_preferred().string()
-      : std::string();
+      ? boost::filesystem::path(exe_path).remove_filename().make_preferred().string() : std::string();
 }
 
 
@@ -143,8 +147,7 @@ void MediaDataInstaller::PopulateMaps(){
     sound_map_[19] = "Grenade";
     sound_description_[19] = "Grenade item in battle.";
     sound_map_[20] = "Ladder|BattleItem";
-    sound_description_[20]
-      = "Character grabbing a ladder on the fields, character reaching for an item during battle";
+    sound_description_[20] = "Character grabbing a ladder on the fields, character reaching for an item during battle";
     sound_map_[21] = "EnemyDeath";
     sound_description_[21] = "When most enemies die.";
     sound_map_[22] = "AeriCriticalHit";
@@ -402,8 +405,7 @@ void MediaDataInstaller::PopulateMaps(){
     sound_map_[148] = "Ailment";
     sound_description_[148] = "Status ailment inflicted.";
     sound_map_[149] = "Sensor";
-    sound_description_[149]
-      = "Sensors or lasers on some fields, like the red ones blocking the party on the plate.";
+    sound_description_[149] = "Sensors or lasers on some fields, like the red ones blocking the party on the plate.";
     sound_map_[150] = "TV";
     sound_description_[150] = "Television transmitting.";
     sound_map_[151] = "AribusterExplode";
@@ -837,8 +839,7 @@ void MediaDataInstaller::PopulateMaps(){
     sound_map_[365] = "BigGuard";
     sound_description_[365] = "Big Guard enemy skill.";
     sound_map_[366] = "Heartbeat";
-    sound_description_[366]
-      = "ShinRa mansion heartbeat background effects. Heard in other places too.";
+    sound_description_[366] = "ShinRa mansion heartbeat background effects. Heard in other places too.";
     sound_map_[367] = "";
     sound_description_[367] = "";
     sound_map_[368] = "BladeBeam";
@@ -1134,10 +1135,10 @@ MediaDataInstaller::~MediaDataInstaller(){}
 
 void MediaDataInstaller::InstallSprites(){
     // Actually open the lgp as a file for reading
-    File menu(input_dir_ + "data/menu/menu_us.lgp");
+    File menu(*disk_image_.filePath("data/menu/menu_us.lgp"));
 
     // Also, open it as a LGP archive.
-    menu_.open(input_dir_ + "data/menu/menu_us.lgp", true);
+    menu_.open(*disk_image_.filePath("data/menu/menu_us.lgp"), true);
     menu_.load();
 
 
@@ -1165,9 +1166,7 @@ void MediaDataInstaller::InstallSprites(){
           && f.file_name != "btl_win_a_h.tex"
           && f.file_name != "btl_win_b_h.tex"
           && f.file_name != "btl_win_b_l.tex"
-        ){
-            continue;
-        }
+        ) continue;
 
         // Save the TEX File
         std::fstream out;
@@ -1338,16 +1337,12 @@ void MediaDataInstaller::InstallSprites(){
             // Fonts, save all palettes, make one default.
             for (int p = 0; p < 16; p ++){
                 // Digits font. Default palette 7.
-                tex.SavePng(
-                  img_dir + "fonts/digits_" + std::to_string(p) + ".png", 16, 0, 156, 16, p
-                );
+                tex.SavePng(img_dir + "fonts/digits_" + std::to_string(p) + ".png", 16, 0, 156, 16, p);
                 if (p == 7) tex.SavePng(img_dir + "fonts/digits.png", 16, 0, 156, 16, p);
             }
             for (int p = 0; p < 16; p ++){
                 // Time font. Default palette 12.
-                tex.SavePng(
-                  img_dir + "fonts/timer_" + std::to_string(p) + ".png", 32, 160, 224, 90, p
-                );
+                tex.SavePng(img_dir + "fonts/timer_" + std::to_string(p) + ".png", 32, 160, 224, 90, p);
                 if (p == 12) tex.SavePng(img_dir + "fonts/timer.png", 32, 160, 224, 90, p);
             }
             // Reels, palete 11.
@@ -1377,31 +1372,28 @@ int MediaDataInstaller::InstallSoundsInit(){
     // SFXDump handles the wav conversion
     std::string command = (boost::format(
       "%1%/sfxdump %2%data/sound/audio.fmt %2%data/sound/audio.dat %3%audio/sounds/"
-    ) % GetExecutablePath() % input_dir_ % output_dir_).str();    
+    ) % GetExecutablePath() % disk_image_.getContentPath() % output_dir_).str();    
     std::system(command.c_str());
     processed_sounds_ = 0;
     return TOTAL_SOUNDS;
 }
 
 bool MediaDataInstaller::InstallSounds(){
-    if (!no_ffmpeg_){
-        std::string f_path
-          = output_dir_ + "audio/sounds/" + std::to_string(processed_sounds_) + ".wav";
+    if (ffmpeg_){
+        // TODO: check for ffmpeg executable and warn if not found.
+        std::string f_path = output_dir_ + "audio/sounds/" + std::to_string(processed_sounds_) + ".wav";
         std::ifstream file(f_path);
         if (file.is_open()){
             file.close();
              std::string command = (boost::format(
-              "ffmpeg -hide_banner -loglevel panic -y "
-              "-i %1%audio/sounds/%2%.wav %1%audio/sounds/%2%.ogg"
+              "ffmpeg -hide_banner -loglevel panic -y  -i %1%audio/sounds/%2%.wav %1%audio/sounds/%2%.ogg"
             ) % output_dir_ % processed_sounds_).str();
             std::system(command.c_str());
             //std::cout << "    ADD SOUND " << processed_sounds_ << ".ogg" << std::endl;
             sounds_.push_back("audio/sounds/" + std::to_string(processed_sounds_) + ".ogg");
             // Remove the wav file.
             if (!keep_originals_)
-                std::remove((
-                  output_dir_ + "audio/sounds/" + std::to_string(processed_sounds_) + ".wav"
-                ).c_str());
+                std::remove((output_dir_ + "audio/sounds/" + std::to_string(processed_sounds_) + ".wav").c_str());
         }
         else{
             sounds_.push_back("audio/sounds/INVALID.ogg");
@@ -1439,7 +1431,7 @@ void MediaDataInstaller::WriteSoundIndex(){
 
 int MediaDataInstaller::InstallMusicsInit(){
     // Read the music index file.
-    std::ifstream music_idx(input_dir_ + "data/music/music.idx");
+    std::ifstream music_idx(*disk_image_.filePath("data/music/music.idx"));
     int i = 0;
     std::string name;
     if (music_idx.is_open()){
@@ -1473,7 +1465,7 @@ bool MediaDataInstaller::InstallMusics(){
         }
     }
 
-    File midi(input_dir_ + "data/midi/midi.lgp");
+    File midi(*disk_image_.filePath("data/midi/midi.lgp"));
 
     std::fstream out;
     out.open(output_dir_ + "audio/musics/" + std::to_string(index) + ".mid", std::ios::out);
@@ -1482,15 +1474,15 @@ bool MediaDataInstaller::InstallMusics(){
     out.close();
 
     // Convert to ogg (TiMidity + FFMpeg)
+    // TODO: Chek for ffmpeg and timidity executables and warn if not found.
     std::string command = (boost::format(
       "timidity --quiet=3 %1%audio/musics/%2%.mid -Ow -o - "
       "| ffmpeg -hide_banner -loglevel panic -y -i - %1%audio/musics/%2%.ogg"
     ) % output_dir_ % index).str();
-    if (!no_ffmpeg_ && !no_timidity_) std::system(command.c_str());
+    if (ffmpeg_ && timidity_) std::system(command.c_str());
 
     // Remove the midi file.
-    if (!keep_originals_)
-        std::remove((output_dir_ + "audio/musics/" + std::to_string(index) + ".mid").c_str());
+    if (!keep_originals_) std::remove((output_dir_ + "audio/musics/" + std::to_string(index) + ".mid").c_str());
 
     musics_.push_back("audio/musics/" + std::to_string(processed_musics_) + ".ogg");
     processed_musics_ ++;
@@ -1500,7 +1492,7 @@ bool MediaDataInstaller::InstallMusics(){
 
 void MediaDataInstaller::InstallHQMusics(){
     std::vector<std::string> hq_musics = {"hearth", "sato", "sensui", "wind"};
-    for (std::string hq_music : hq_musics){
+    for (const std::string hq_music : hq_musics){
 
         int index = 2000;
         for (auto const& m : musics_map_){
@@ -1512,15 +1504,14 @@ void MediaDataInstaller::InstallHQMusics(){
 
         std::string command = (boost::format(
           "ffmpeg -hide_banner -loglevel panic -y -i %1%musics/%2%.wav %3%audio/sounds/%4%.ogg"
-        ) % input_dir_ % hq_music % output_dir_ % index).str();
-        if (!no_ffmpeg_)  std::system(command.c_str());
+        ) % disk_image_.getContentPath() % hq_music % output_dir_ % index).str();
+        if (ffmpeg_)  std::system(command.c_str());
     }
 }
 
 void MediaDataInstaller::WriteMusicsIndex(){
     TiXmlDocument xml;
     std::unique_ptr<TiXmlElement> container(new TiXmlElement("musics"));
-    //for (string item : items_){
     for (int id = 0; id < musics_.size(); id ++){
         std::string path = musics_[id];
         std::unique_ptr<TiXmlElement> xml_music(new TiXmlElement("music"));
@@ -1577,3 +1568,29 @@ void MediaDataInstaller::WriteMusicsIndex(){
     xml.SaveFile(output_dir_ + "audio/musics/_musics.xml");
 }
 
+int MediaDataInstaller::InstallVideosInit(){
+    // TODO: Look ofor anothe rvideo folders
+    processed_videos_ = 0;
+    const boost::filesystem::path movies_dir(output_dir_ + "/ff7/movies/");
+    if (!boost::filesystem::exists(movies_dir) || !boost::filesystem::is_directory(movies_dir)) return 0;
+
+    int total_videos = 0;
+    for (const auto& entry : boost::filesystem::directory_iterator(movies_dir)){
+        if (boost::filesystem::is_regular_file(entry.path())){
+            std::cout << entry.path().filename().string() << std::endl;
+            videos_.push_back(entry.path().string());
+            total_videos ++;
+        }
+    }
+    return total_videos;
+}
+
+void MediaDataInstaller::InstallVideos(){
+    if (processed_videos_ < 0 || processed_videos_ >= videos_.size()) return;
+    const std::string output_dir = output_dir_ + "video/";
+    boost::filesystem::create_directories(output_dir);
+    const boost::filesystem::path source(videos_[processed_videos_]);
+    const boost::filesystem::path destination = boost::filesystem::path(output_dir) / source.filename();
+    boost::filesystem::copy_file(source, destination, boost::filesystem::copy_options::overwrite_existing);
+    processed_videos_ ++;
+}

+ 31 - 8
src/installer/MediaDataInstaller.h

@@ -16,6 +16,7 @@
 #pragma once
 
 #include <unordered_map>
+#include "DiskImage.h"
 #include "data/VGearsLGPArchive.h"
 #include "common/BinGZipFile.h"
 #include "TexFile.h"
@@ -31,14 +32,14 @@ class MediaDataInstaller{
         /**
          * Constructor
          *
-         * @param[in] input_dir Path to the directory containing the original data to parse.
+         * @param[in] disk_image Disk image to extract the data from.
          * @param[in] output_dir Path to the directory of the installation data.
          * @param[in] keep_originals True to keep original data after conversion, false to remove.
          * @param[in] no_ffmpeg True to prevent system calls to ffmpeg command, false to allow.
          * @param[in] no_timidity True to prevent system calls to timidity command, false to allow.
          */
         MediaDataInstaller(
-          const std::string input_dir, const std::string output_dir, const bool keep_originals,
+          const DiskImage& disk_image, const std::string output_dir, const bool keep_originals,
           const bool no_ffmpeg, const bool no_timidity
         );
 
@@ -105,6 +106,18 @@ class MediaDataInstaller{
          */
         void WriteMusicsIndex();
 
+        /**
+         * Prepares the installer for the video extraction.
+         *
+         * @return The total number of videos to process.
+         */
+        int InstallVideosInit();
+
+        /**
+         * Copies the FMV videos.
+         */
+        void InstallVideos();
+
     private:
 
         /**
@@ -121,9 +134,9 @@ class MediaDataInstaller{
 		std::string GetExecutablePath();
 
         /**
-         * The path to the directory from which to read the PC game data.
+         * The disk image to read the original data from.
          */
-        std::string input_dir_;
+        DiskImage disk_image_;
 
         /**
          * The path to the directory where to save the V-Gears data.
@@ -160,6 +173,11 @@ class MediaDataInstaller{
          */
         std::vector<std::string> sounds_;
 
+        /**
+         * Video files to copy
+         */
+        std::vector<std::string> videos_;
+
         /**
          * The midi.lgp file.
          */
@@ -170,6 +188,11 @@ class MediaDataInstaller{
          */
         int processed_musics_;
 
+        /**
+         * Number of video files already processed.
+         */
+        int processed_videos_;
+
         /**
          * Map for music files with descriptive names.
          */
@@ -186,13 +209,13 @@ class MediaDataInstaller{
         bool keep_originals_;
 
         /**
-         * Flag to prevent system calls to ffmpeg.
+         * Flag to enable system calls to ffmpeg.
          */
-        bool no_ffmpeg_;
+        bool ffmpeg_;
 
         /**
-         * Flag to prevent system calls to timidity.
+         * Flag to enable system calls to timidity.
          */
-        bool no_timidity_;
+        bool timidity_;
 
 };

+ 0 - 246
src/installer/Release.h

@@ -1,246 +0,0 @@
-/*
- * 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 <string>
-
-/**
- * Information about Final Fantasy VII releases.
- */
-struct Release{
-
-    public:
-
-        /**
-         * The platform for which the release is intended.
-         */
-        enum Platform{
-
-            /** The release is for PC. */
-            PLATFORM_PC,
-            /** The release is for PS1. */
-            PLATFORM_PS1,
-            /** The release is for an unknown platform. */
-            PLATFORM_UNKNOWN
-        };
-
-        /**
-         * The region for which the release is intended.
-         */
-        enum Region{
-            /** The release is for Japan. */
-            REGION_JAPAN,
-            /** The release is for North America. */
-            REGION_NORTH_AMERICA,
-            /** The release is for Europe. */
-            REGION_EUROPE,
-            /** The release is for an unknown region. */
-            REGION_UNKNOWN
-        };
-
-        /**
-         * The language for which the release is intended.
-         */
-        enum Language{
-            /** The release is in Japanese. */
-            LANGUAGE_JAPANESE,
-            /** The release is in English. */
-            LANGUAGE_ENGLISH,
-            /** The release is in French. */
-            LANGUAGE_FRENCH,
-            /** The release is in Spanish. */
-            LANGUAGE_SPANISH,
-            /** The release is in German. */
-            LANGUAGE_GERMAN,
-            /** The release is in Italian. */
-            LANGUAGE_ITALIAN,
-            /** The release is for an unknown language. */
-            LANGUAGE_UNKNOWN
-        };
-
-        /**
-         * The disk for which the release is intended.
-         */
-        enum Disk{
-            /** The release is for the install disk (PC only). */
-            DISK_INSTALL,
-            /** The release is for disk 1. */
-            DISK_1,
-            /** The release is for disk 2. */
-            DISK_2,
-            /** The release is for disk 3. */
-            DISK_3,
-            /** The release is for disk 4. */
-            DISK_4,
-            /** The release is for an unknown disk. */
-            DISK_UNKNOWN
-        };
-
-        /**
-         * Constructs a new release.
-         */
-        Release();
-
-        /**
-         * Constructs a new release from an ISO path.
-         *
-         * @param iso_path The path to the ISO file.
-         */
-        Release(std::string iso_path);
-
-        /**
-         * Destroys the release.
-         */
-        ~Release();
-
-        std::string getIsoPath() const { return iso_path; }
-
-        /**
-         * Gets the human readable ID of the release.
-         *
-         * @return The ID of the release.
-         */
-        std::string const getId();
-
-        /**
-         * Gets the platform for which the release is intended.
-         *
-         * @return The platform of the release.
-         */
-        Platform const getPlatform();
-
-        /**
-         * Gets the region for which the release is intended.
-         *
-         * @return The region of the release.
-         */
-        Region const getRegion();
-
-        /**
-         * Gets the language for which the release is intended.
-         *
-         * @return The language of the release.
-         */
-        Language const getLanguage();
-
-        /**
-         * Gets the disk for which the release is intended.
-         *
-         * @return The disk of the release.
-         */
-        Disk const getDisk();
-
-        /**
-         * Checks if the release is a valid Final Fantasy VII release.
-         *
-         * @return True if the release is valid, false otherwise.
-         */
-        bool const isValid();
-
-        /**
-         * Gets the error message for the release.
-         *
-         * @return The error message.
-         */
-        std::string const getErrorMessage();
-
-        /**
-         * Checks if the release is supported and tested in V-Gears.
-         *
-         * @return True if the release is supported, false otherwise.
-         */
-        bool const isSupported();
-
-        /**
-         * Gets a warning in case the release is not fully supported.
-         *
-         * @return The warning message.
-         */
-        std::string const getWarningMessage();
-
-        /**
-         * Gets the path where the content has been extracted.
-         * 
-         * @return The path to the extracted content, null if the content has
-         * not been extracted yet.
-         */
-        std::string const getContentPath();
-
-        /**
-         * Extracts the ISO file to the specified output path. The files will
-         * be extracted to the "original_data" subdirectory of the output path.
-         *
-         * @param installation_path The v-gears installation directory.
-         * @return True if the extraction was successful, false otherwise.
-         */
-        bool extractIso(std::string installation_path);
-
-    private:
-
-        /**
-         * The path to the ISO file.
-         */
-        std::string iso_path;
-
-        /**
-         * A human readable ID of the release.
-         */
-        std::string id;
-
-        /**
-         * The platform for which the release is intended.
-         */
-        Platform platform;
-
-        /**
-         * The region for which the release is intended.
-         */
-        Region region;
-
-        /**
-         * The language for which the release is intended.
-         */
-        Language language;
-
-        /**
-         * The disk for which the release is intended.
-         */
-        Disk disk;
-
-        /**
-         * Whether the release is valid.
-         */
-        bool valid;
-
-        /**
-         * Whether the release is supported.
-         */
-        bool supported;
-
-        /**
-         * The error message for the release. Empty if no error.
-         */
-        std::string error_message;
-
-        /**
-         * The warning message for the release. Empty if no warning.
-         */
-        std::string warning_message;
-
-        /**
-         * The path where the content has been extracted.
-         */
-        std::string content_path;
-};

+ 17 - 11
src/installer/SaveMap.h

@@ -84,8 +84,8 @@ struct SaveMap{
         /**
          * Materia AP. 3 bytes, not four.
          *
-         * For Enemy Skill materia, ap is not the actual AP, but each byte set indicates a learned
-         * skill.
+         * For Enemy Skill materia, ap is not the actual AP, but each byte set
+         * indicates a learned skill.
          */
         u32 ap;
 
@@ -127,9 +127,10 @@ struct SaveMap{
         /**
          * The item bytes, as read from KERNEL.BIN in the save map.
          *
-         * The first 7 bits are the item quantity. The 9 last bits are the index. All the other
-         * members can be derived from this. Indexex 0-127 are items, 128-255 are weapons, 256-287
-         * are weapons and 288-319 are accessories.
+         * The first 7 bits are the item quantity. The 9 last bits are the
+         * index. All the other members can be derived from this. Indexex 0-127
+         * are items, 128-255 are weapons, 256-287 are armors and 288-319 are
+         * accessories.
          */
         u16 item_raw;
 
@@ -178,8 +179,9 @@ struct SaveMap{
         /**
          * Character identifier 1 byte.
          *
-         * It's not the standard character ID. Used only for Cait Sith and Vincent, to distinguish
-         * them from Young Cloud and Sephiroth. V-Gears mostly ignores this.
+         * It's not the standard character ID. Used only for Cait Sith and
+         * Vincent, to distinguish them from Young Cloud and Sephiroth. V-Gears
+         * mostly ignores this.
          */
         u8 identifier;
 
@@ -450,7 +452,8 @@ struct SaveMap{
         u16 mask;
 
         /**
-         * Character seelction with the mask applied. Selected character IDs are true.
+         * Character seelction with the mask applied. Selected character IDs are
+         * true.
          */
         bool character[9];
     };
@@ -576,7 +579,8 @@ struct SaveMap{
          * Magic order: Attack restore indirect (0x0C)
          * Magic order: Indirect restore attack (0x10)
          * Magic order: Indirect attack restore (0x14)
-         * Extra battle window displaying information: Inactive (0x00); Active (0x40)
+         * Extra battle window displaying information:
+         *   Inactive (0x00); Active (0x40)
          */
         u8 mask_2;
 
@@ -641,7 +645,8 @@ struct SaveMap{
     /**
      * Inventory list. 320 x 2 bytes,
      *
-     * It includes items, unequipped weapons, unequipped armor and unequipped accessories.
+     * It includes items, unequipped weapons, unequipped armor and unequipped
+     * accessories.
      */
     Item inventory[320];
 
@@ -712,7 +717,8 @@ struct SaveMap{
     /**
      * Saving location. 2 bytes.
      *
-     * @todo Does this indicte the current field? The coordinates in a field? If so, 2 nybbles?
+     * @todo Does this indicte the current field? The coordinates in a field? If
+     * so, 2 nybbles?
      */
     u16 location;
 

+ 1 - 2
src/installer/ScopedLgp.cpp

@@ -25,6 +25,5 @@ ScopedLgp::ScopedLgp(Ogre::Root* root, std::string full_path, std::string type,
 }
 
 ScopedLgp::~ScopedLgp(){
-     if (root_)
-         Ogre::ResourceGroupManager::getSingleton().removeResourceLocation(full_path_, group_);
+     if (root_) Ogre::ResourceGroupManager::getSingleton().removeResourceLocation(full_path_, group_);
 }

+ 2 - 6
src/installer/TexFile.cpp

@@ -136,9 +136,7 @@ void TexFile::SavePng(
   unsigned int palette
 ){
     if (version_ != 0x00000001){
-        std::cout
-          << "[WARNING] Tried to convert an invalid TEX file to png. Version ID: "
-          << version_ << std::endl;
+        std::cout << "[WARNING] Tried to convert an invalid TEX file to png. Version ID: " << version_ << std::endl;
         return;
     }
     // TODO: Warn if wrong palette index.
@@ -194,9 +192,7 @@ void TexFile::SavePng(
   unsigned int w1, unsigned int w2, unsigned int h, unsigned int palette
 ){
     if (version_ != 0x00000001){
-        std::cout
-          << "[WARNING] Tried to convert an invalid TEX file to png. Version ID: "
-          << version_ << std::endl;
+        std::cout << "[WARNING] Tried to convert an invalid TEX file to png. Version ID: " << version_ << std::endl;
         return;
     }
     // TODO: Warn if wrong palette index.

+ 31 - 23
src/installer/TexFile.h

@@ -25,8 +25,8 @@ class TexFile{
         /**
          * Constructor.
          *
-         * @param[in,out] file File with the tex data. The file data will not be modified, but it's
-         * offset will.
+         * @param[in,out] file File with the tex data. The file data will not be
+         * modified, but it's offset will.
          */
         TexFile(File file);
 
@@ -45,15 +45,16 @@ class TexFile{
         /**
          * Saves a fragment of the TEX file as a PNG file.
          *
-         * If any of the read data invalidates the file, it will silently fail and do nothing.
+         * If any of the read data invalidates the file, it will silently fail
+         * and do nothing.
          *
          * @param[in] file_name Full path of the destination file.
          * @param[in] x X coordinate of the image to extract from the TEX file.
          * @param[in] y Y coordinate of the image to extract from the TEX file.
          * @param[in] w Width of the image to extract from the TEX file.
          * @param[in] h Height of the image to extract from the TEX file.
-         * @param[in] palette Index of the color palette to use. For non paletted files it's
-         * ignored.
+         * @param[in] palette Index of the color palette to use. For non
+         * paletted files it's ignored.
          */
         void SavePng(
           std::string file_name, unsigned int x, unsigned int y, unsigned int w, unsigned int h,
@@ -63,31 +64,36 @@ class TexFile{
         /**
          * Saves the TEX file as a PNG file.
          *
-         * If any of the read data invalidates the file, it will silently fail and do nothing.
+         * If any of the read data invalidates the file, it will silently fail
+         * and do nothing.
          *
          * @param[in] file_name Full path of the destination file.
-         * @param[in] palette Index of the color palette to use. For non paletted files it's
-         * ignored.
+         * @param[in] palette Index of the color palette to use. For non
+         * paletted files it's ignored.
          */
         void SavePng(std::string file_name, unsigned int palette = 0);
 
         /**
          * Saves two fragments of the TEX file as a single PNG image.
          *
-         * The second fragment will be at the right of the first one. They must have the same
-         * height. If any of the read data invalidates the file, it will silently fail and do
-         * nothing.
+         * The second fragment will be at the right of the first one. They must
+         * have the same height. If any of the read data invalidates the file,
+         * it will silently fail and do nothing.
          *
          * @param[in] file_name Full path of the destination file.
-         * @param[in] x1 X coordinate of the first image to extract from the TEX file.
-         * @param[in] x2 X coordinate of the second image to extract from the TEX file.
-         * @param[in] y1 Y coordinate of the first image to extract from the TEX file.
-         * @param[in] y2 Y coordinate of the first image to extract from the TEX file.
+         * @param[in] x1 X coordinate of the first image to extract from the TEX
+         * file.
+         * @param[in] x2 X coordinate of the second image to extract from the
+         * TEX file.
+         * @param[in] y1 Y coordinate of the first image to extract from the TEX
+         * file.
+         * @param[in] y2 Y coordinate of the first image to extract from the TEX
+         * file.
          * @param[in] w1 Width of the first image to extract from the TEX file.
          * @param[in] w2 Width of the second image to extract from the TEX file.
          * @param[in] h Height of the images to extract from the TEX file.
-         * @param[in] palette Index of the color palette to use. For non paletted files it's
-         * ignored.
+         * @param[in] palette Index of the color palette to use. For non
+         * paletted files it's ignored. 
          */
         void SavePng(
           std::string file_name, unsigned int x1, unsigned int x2, unsigned int y1, unsigned int y2,
@@ -395,8 +401,9 @@ class TexFile{
         /**
          * Alpha reference value. 4 bytes.
          *
-         * Only applies to paltted images, if the alpha value sampled from the palee is 0xFE, this
-         * value should be replaced with the reference alpha.
+         * Only applies to paltted images, if the alpha value sampled from the
+         * palette is 0xFE, this value should be replaced with the reference
+         * alpha.
          */
         u32 ref_alpha_;
 
@@ -470,10 +477,11 @@ class TexFile{
         std::vector<Ogre::ColourValue> pixel_colour_;
 
         /**
-         * Pixel data for paletted images. {@see width} * {@see height} * {@see bytes_per pixels}.
-         *
-         * In paletted images, every byte is a color reference in the palette. For non-paletted
-         * images, a pixel format section.
+         * Pixel data for paletted images.
+         * 
+         * {@see width} * {@see height} * {@see bytes_per pixels}.
+         * In paletted images, every byte is a color reference in the palette.
+         * For non-paletted images, a pixel format section.
          */
         std::vector<u8> pixel_ref_;
 

+ 3 - 7
src/installer/VGearsUtility.cpp

@@ -20,8 +20,7 @@
 namespace VGears{
 
     Utility::Utility(int argc, char *argv[]) :
-      Application(argc, argv), frame_listener_(NULL), scene_manager_(NULL),
-      camera_(NULL), viewport_(NULL)
+      Application(argc, argv), frame_listener_(NULL), scene_manager_(NULL), amera_(NULL), viewport_(NULL)
     {}
 
     Utility::~Utility(){}
@@ -47,9 +46,7 @@ namespace VGears{
         Ogre::RenderWindow *window(getRenderWindow());
         viewport_ = window->addViewport(camera_);
         viewport_->setBackgroundColour(Ogre::ColourValue(0.0f, 0.4f, 0.0f));
-        camera_->setAspectRatio(
-          Ogre::Real(viewport_->getActualWidth()) / Ogre::Real(viewport_->getActualHeight())
-        );
+        camera_->setAspectRatio(Ogre::Real(viewport_->getActualWidth()) / Ogre::Real(viewport_->getActualHeight()));
         frame_listener_ = new DisplayFrameListener(window);
         frame_listener_->setCamera(camera_);
         root->addFrameListener(frame_listener_);
@@ -63,8 +60,7 @@ namespace VGears{
 
     std::string Utility::DecodeString(const u8* bytes, const unsigned int size){
         std::string decoded("");
-        for (int i = 0; i < size; i ++)
-            if (bytes[i] > 0x00 && bytes[i] < 0XFF) decoded += ENGLISH_CHARS[bytes[i]];
+        for (int i = 0; i < size; i ++) if (bytes[i] > 0x00 && bytes[i] < 0XFF) decoded += ENGLISH_CHARS[bytes[i]];
         boost::algorithm::trim(decoded);
         return decoded;
     }

+ 95 - 121
src/installer/WorldInstaller.cpp

@@ -23,6 +23,7 @@
 #include "WorldInstaller.h"
 #include "TexFile.h"
 #include "common/Lzs.h"
+#include "common/FinalFantasy7/FF7NameLookup.h"
 #include "common/VGearsStringUtil.h"
 #include "data/VGearsLGPArchive.h"
 #include "data/WorldMapWalkmesh.h"
@@ -34,20 +35,18 @@ std::string WorldInstaller::ELEMENT_MODELS_DIR("models/world/element");
 std::string WorldInstaller::TERRAIN_MODELS_DIR("models/world/terrain");
 
 WorldInstaller::WorldInstaller(
-  const std::string input_dir, const std::string output_dir,
+  const DiskImage& disk_image, const std::string output_dir,
   const bool keep_originals, Ogre::ResourceGroupManager* res_mgr
-):
-  input_dir_(input_dir), output_dir_(output_dir), keep_originals_(keep_originals), res_mgr_(res_mgr)
-{}
+): disk_image_(disk_image), output_dir_(output_dir), keep_originals_(keep_originals), res_mgr_(res_mgr) {}
 
 WorldInstaller::~WorldInstaller(){}
 
 unsigned int WorldInstaller::Initialize(){
     if (wm_map_.size() > 0) wm_map_.clear();
     processed_maps_ = 0;
-    wm_map_.push_back(File(input_dir_ + "/data/wm/WM0.MAP"));
-    wm_map_.push_back(File(input_dir_ + "/data/wm/WM2.MAP"));
-    wm_map_.push_back(File(input_dir_ + "/data/wm/WM3.MAP"));
+    wm_map_.push_back(File(*disk_image_.filePath("data/wm/WM0.MAP")));
+    wm_map_.push_back(File(*disk_image_.filePath("data/wm/WM2.MAP")));
+    wm_map_.push_back(File(*disk_image_.filePath("data/wm/WM3.MAP")));
     return wm_map_.size();
 }
 
@@ -341,82 +340,67 @@ void WorldInstaller::GenerateMaterials(){
         Ogre::Technique* tech = mat->getTechnique(0);
         Ogre::Pass* pass = tech->getPass(0);
         pass->setDiffuse(0.7, 0.7, 0.7, 0.7);
-        std::cout << "SAVING MATERIAL: " << (output_dir_ + "models/world/" + t.name + ".material") << std::endl;
         mSer.exportMaterial(mat, output_dir_ + "models/world/" + t.name + ".material");
     }
 }
 
 void WorldInstaller::ProcessModels(){
     // Open world_us.lgp
-    File world_file(input_dir_ + "data/wm/world_us.lgp");
-    VGears::LGPArchive world_lgp(input_dir_ + "data/wm/world_us.lgp", "LGP");
-    world_lgp.open(input_dir_ + "data/wm/world_us.lgp", true);
+    File world_file(*disk_image_.filePath("data/wm/world_us.lgp"));
+    boost::filesystem::create_directories(output_dir_ + "temp/world_models/");
+    boost::filesystem::create_directories(output_dir_ + "data/models/world/");
+    VGears::LGPArchive world_lgp(*disk_image_.filePath("data/wm/world_us.lgp"), "LGP");
+    world_lgp.open(*disk_image_.filePath("data/wm/world_us.lgp"), true);
     world_lgp.load();
     VGears::LGPArchive::FileList file_list = world_lgp.GetFiles();
+    std::vector<std::string> hrc_file_names;
+
+    // First pass: extract all files so HRC sidecar dependencies (.rsd, .p, .tex) exist.
     for (int i = 0; i < file_list.size(); i ++){
         VGears::LGPArchive::FileEntry f = file_list.at(i);
         if (f.data_offset + f.data_size <= world_file.GetFileSize()){
             File w_lgp_file(&world_file, f.data_offset, f.data_size);
             std::string file_name = f.file_name;
+            VGears::StringUtil::toLowerCase(file_name);
             w_lgp_file.WriteFile(output_dir_ + "temp/world_models/" + file_name);
-            if (file_name.substr(4, 3) == "hrc"){
-
-                std::string id = file_name.substr(0, 3);
-                std::string model_name = FF7Data::GetWorldMapModelName(id);
-                std::cout << "CONVERTING WORLD MODEL: " << file_name << " " << i
-                  << "/" << file_list.size() << std::endl;
-
-
-
-                // TODO: Can this be done with declare resource?
-                // TODO: If not, do aloop to save all the hrc files, then call this once, then
-                // another loop to process them.
-                /*res_mgr_->declareResource(
-                  output_dir_ + "temp/world_models/" + file_name, "Skeleton", "FFVII"
-                );*/
-                std::cout << "1\n";
-                res_mgr_->removeResourceLocation(output_dir_ + "temp/world_models/", "FFVII");
-                std::cout << "2\n";
-                res_mgr_->addResourceLocation(
-                  output_dir_ + "temp/world_models/", "FileSystem", "FFVII", true, true
-                );
-
-
+            if (file_name.size() >= 7 && file_name.substr(4, 3) == "hrc") hrc_file_names.push_back(file_name);
+        }
+    }
 
-                // TODO: This needs work to assemble all the pieces. Similar to field models.
+    // Second pass: convert HRC models once all dependencies are available on disk.
+    for (int i = 0; i < hrc_file_names.size(); i ++){
+        const std::string& file_name = hrc_file_names.at(i);
 
+        // TODO: Can this be done with declare resource?
+        // TODO: If not, do aloop to save all the hrc files, then call this once, then
+        // another loop to process them.
+        res_mgr_->declareResource(output_dir_ + "temp/world_models/" + file_name, "Skeleton", "FFVII");
+        res_mgr_->removeResourceLocation(output_dir_ + "temp/world_models/", "FFVII");
+        res_mgr_->addResourceLocation(output_dir_ + "temp/world_models/", "FileSystem", "FFVII", true, true);
 
-                std::cout << "3\n";
-                Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(
-                  file_name, "FFVII"
-                );
-                std::cout << "4\n";
-                auto mesh_name = model_name + ".mesh";
-                std::cout << "5\n";
-                Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(mesh_name, "FFVII"));
-                std::cout << "6\n";
-                Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
-                // TODO: a files??
-                /*for (std::string anim : model.a){
-                    VGears::AFileManager &afl_mgr(VGears::AFileManager::GetSingleton());
-                    Ogre::String a_base_name;
-                    VGears::StringUtil::splitBase(anim, a_base_name);
-                    VGears::AFilePtr a
-                      = afl_mgr.load(a_base_name + ".a", "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));
-                }*/
-                std::cout << "EXPORT MESH: " << (output_dir_ + "data/models/world/" + mesh_name) << std::endl;
-                ExportMesh(output_dir_ + "data/models/world/" + mesh_name, mesh);
-                std::cout << "    EXPORTED" << std::endl;
-            }
-        }
+        // TODO: This needs work to assemble all the pieces. Similar to field models.
+        Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(file_name, "FFVII");
+        Ogre::String hrc_base_name;
+        VGears::StringUtil::splitBase(file_name, hrc_base_name);
+        auto mesh_name = VGears::NameLookup::model(hrc_base_name) + ".mesh";
+        Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(mesh_name, "FFVII"));
+        Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
+        // TODO: a files??
+        /*for (std::string anim : model.a){
+            VGears::AFileManager &afl_mgr(VGears::AFileManager::GetSingleton());
+            Ogre::String a_base_name;
+            VGears::StringUtil::splitBase(anim, a_base_name);
+            VGears::AFilePtr a
+              = afl_mgr.load(a_base_name + ".a", "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_ + "data/models/world/", mesh);
     }
 }
 
 bool WorldInstaller::ProcessMap(){
-    //std::cout << "[WI] Processing map " << processed_maps_ << "/" << wm_map_.size() << std::endl;
     if (processed_maps_ >= wm_map_.size()) return true;
     // Initialize the xml file.
     TiXmlDocument doc;
@@ -439,7 +423,7 @@ bool WorldInstaller::ProcessMap(){
     xml->LinkEndChild(xml_walkmesh.release());
     // Terrain (don't close yet).
     std::unique_ptr<TiXmlElement> xml_terrain(new TiXmlElement("terrain"));
-
+    
     // Initialize the walkmesh data.
     WorldMapWalkmesh walkmesh(processed_maps_, processed_maps_ == 0 ? true : false);
 
@@ -463,8 +447,7 @@ bool WorldInstaller::ProcessMap(){
             u32 mesh_size = wm_map_[processed_maps_].readU32LE();
             // Add size to lzss
             wm_map_[processed_maps_].SetOffset(mesh_offset);
-            for (int d = 0; d < 4; d ++)
-                lzss.push_back(wm_map_[processed_maps_].readU8());
+            for (int d = 0; d < 4; d ++) lzss.push_back(wm_map_[processed_maps_].readU8());
             for (int d = 0; d < mesh_size ; d ++) lzss.push_back(wm_map_[processed_maps_].readU8());
             // Extract the lzss file.
             std::vector<unsigned char> data = Lzs::Decompress(lzss);
@@ -478,8 +461,7 @@ bool WorldInstaller::ProcessMap(){
                 if (processed_maps_ >= wm_map_.size()) return true;
                 else return false;
             }
-            for (int d = 0; d <= data.size(); d ++)
-                dat.write((char *) &data[d], sizeof(unsigned char));
+            for (int d = 0; d < data.size(); d ++) dat.write((char *) &data[d], sizeof(unsigned char));
             dat.close();
 
             // Open and read the decompressed file.
@@ -587,9 +569,7 @@ bool WorldInstaller::ProcessMap(){
                 //  << (int) map.blocks[b].mesh[m].triangles[t].vertex_index[1] << ", "
                 //  << (int) map.blocks[b].mesh[m].triangles[t].vertex_index[2] << std::endl;
                 // TODO: El index deberia incrementar por triangulo por cada material
-                man.setMaterialName(
-                  0, texture_.at(map.blocks[b].mesh[m].triangles[t].texture).name
-                );
+                man.setMaterialName(0, texture_.at(map.blocks[b].mesh[m].triangles[t].texture).name);
                 man.triangle(
                   map.blocks[b].mesh[m].triangles[t].vertex_index[0],
                   map.blocks[b].mesh[m].triangles[t].vertex_index[1],
@@ -604,8 +584,7 @@ bool WorldInstaller::ProcessMap(){
         //  << std::endl;
         mesh_serializer.exportMesh(
           mesh.getPointer(),
-          output_dir_ + TERRAIN_MODELS_DIR + "/" + std::to_string(processed_maps_)
-            + "/" + mesh->getName() + ".mesh"
+          output_dir_ + TERRAIN_MODELS_DIR + "/" + std::to_string(processed_maps_) + "/" + mesh->getName() + ".mesh"
         );
         // WM0 is te main worldmap, its shape changes according to the history progress, it has
         // 68 blocks but only 63 are used at a time
@@ -613,12 +592,9 @@ bool WorldInstaller::ProcessMap(){
             std::unique_ptr<TiXmlElement> xml_block(new TiXmlElement("block"));
             xml_block->SetAttribute("index", std::to_string(b));
             xml_block->SetAttribute(
-              "file_name",
-              "terrain/" + std::to_string(processed_maps_) + "/" + mesh->getName() + ".mesh"
-            );
-            xml_block->SetAttribute(
-              "position", std::to_string(wm_offset_x) + " " + std::to_string(wm_offset_y) + " 0"
+              "file_name", "terrain/" + std::to_string(processed_maps_) + "/" + mesh->getName() + ".mesh"
             );
+            xml_block->SetAttribute("position", std::to_string(wm_offset_x) + " " + std::to_string(wm_offset_y) + " 0");
             if (processed_maps_ == 0){
                 // TODO: Correct history points for each block.
                 switch (b){
@@ -657,8 +633,7 @@ bool WorldInstaller::ProcessMap(){
     doc.LinkEndChild(xml.release());
     // TODO: Add models, entities...
     doc.SaveFile(
-      output_dir_ + "/world/" + std::to_string(processed_maps_)
-      + "/world" + std::to_string(processed_maps_) + ".xml"
+      output_dir_ + "/world/" + std::to_string(processed_maps_) + "/world" + std::to_string(processed_maps_) + ".xml"
     );
     // Generate the walkmesh xml file
     walkmesh.generate(output_dir_ + "/world/" + std::to_string(processed_maps_) + "/wm.xml");
@@ -670,27 +645,32 @@ bool WorldInstaller::ProcessMap(){
 }
 
 void WorldInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh){
+    if (mesh.isNull()){
+        std::cerr << "[ERROR] ExportMesh called with null mesh." << std::endl;
+        return;
+    }
+    boost::filesystem::create_directories(outdir);
 
     // 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(base_mesh_name + ".skeleton");
+    if (!skeleton.isNull()){
+        Ogre::SkeletonSerializer skeleton_serializer;
+        skeleton_serializer.exportSkeleton(skeleton.getPointer(), outdir + base_mesh_name + ".skeleton");
+        mesh->setSkeletonName(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;
+    std::set<std::string> queued_materials;
+    bool has_queued_materials = false;
     while (it.hasMoreElements()){
         Ogre::SubMesh *sub_mesh(it.getNext());
-        Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(
-          sub_mesh->getMaterialName())
-        );
+        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);
@@ -703,57 +683,51 @@ void WorldInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &m
                               texture_unit_num < pass->getNumTextureUnitStates();
                               texture_unit_num ++
                             ){
-                                Ogre::TextureUnitState* unit
-                                  = pass->getTextureUnitState(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/wm/" + unit->getTextureName()
-                                    );
+                                    try{
+                                        // Convert the texture from .tex to .png.
+                                        TexFile tex(output_dir_ + "temp/world_models/" + 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(base_mesh_name + "_" + base_name + ".png");
+                                        // 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(base_mesh_name + "_" + base_name + ".png");
 
-                                    tex.SavePng(
-                                      output_dir_ + ELEMENT_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_ + ELEMENT_MODELS_DIR + "/" + base_name + ".png",
-                                        output_dir_ + ELEMENT_MODELS_DIR + "/"
-                                        + base_mesh_name + "_" + base_name + ".png",
-                                      boost::filesystem::copy_options::overwrite_existing
-                                    );
-                                    textures.insert(unit->getTextureName());
+                                        const auto plain_png = outdir + base_name + ".png";
+                                        const auto model_png = outdir + base_mesh_name + "_" + base_name + ".png";
+                                        tex.SavePng(plain_png, 0);
+                                        boost::filesystem::copy_file(
+                                          plain_png, model_png, boost::filesystem::copy_options::overwrite_existing
+                                        );
+                                        textures.insert(unit->getTextureName());
+                                    }
+                                    catch (const std::exception& ex){
+                                        std::cerr << "[ERROR] Texture export failed for "
+                                          << unit->getTextureName() << ": " << ex.what() << std::endl;
+                                    }
                                 }
                             }
                         }
                     }
                 }
             }
-            // TODO: Check what to do with materials
-            /**
-            if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
+            // Queue each material once. Exporting with an empty queue throws in Ogre.
+            if (queued_materials.insert(sub_mesh->getMaterialName()).second){
                 mat_ser.queueForExport(mat);
-                materials_.push_back(sub_mesh->getMaterialName());
-            }*/
+                has_queued_materials = true;
+            }
 
         }
         ++ i;
     }
-    mat_ser.exportQueued(outdir + base_mesh_name + VGears::EXT_MATERIAL);
+    if (has_queued_materials) 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::TexturePtr texture_ptr = Ogre::TextureManager::getSingleton().load(tex_name, "FFVIITextures");
+            //Ogre::TexturePtr texture_ptr = Ogre::TextureManager::getSingleton().load(tex_name, "FFVII");
             Ogre::Image image;
             texture_ptr->convertToImage(image);
             Ogre::String base_name;

+ 15 - 12
src/installer/WorldInstaller.h

@@ -18,6 +18,7 @@
 #include <string>
 #include <vector>
 #include <common/TypeDefine.h>
+#include "DiskImage.h"
 #include "common/File.h"
 
 /**
@@ -30,13 +31,14 @@ class WorldInstaller{
         /**
          * Constructor.
          *
-         * @param[in] input_dir Path to the directory containing the original data to parse.
+         * @param[in] disk_image Disk image to extract the data from.
          * @param[in] output_dir Path to the directory of the installation data.
-         * @param[in] keep_originals True to keep original data after conversion, false to remove.
+         * @param[in] keep_originals True to keep original data after
+         * conversion, false to remove.
          * @param[in] res_mgr The application resource manager.
          */
         WorldInstaller(
-          const std::string input_dir, const std::string output_dir,
+          const DiskImage& disk_image, const std::string output_dir,
           const bool keep_originals, Ogre::ResourceGroupManager* res_mgr
         );
 
@@ -60,16 +62,16 @@ class WorldInstaller{
         /**
          * Generates the materials needed for the world map.
          *
-         * TODO: Note that this function stil doesn't use the game data, and it has a lot of
-         * hardcoded things.
+         * @todo Note that this function stil doesn't use the game data, and it
+         * has a lot of hardcoded things.
          */
         void GenerateMaterials();
 
         /**
          * Processes the next map to process.
          *
-         * @return True if, once the map has been processed, all maps are processed. False if there
-         * are more maps to process.
+         * @return True if, once the map has been processed, all maps are
+         * processed. False if there are more maps to process.
          */
         bool ProcessMap();
 
@@ -220,7 +222,7 @@ class WorldInstaller{
         /**
          * A map.
          *
-         * A map is defined in a WM*.MAP file. They are divided in 0xB800 byte blocks.
+         * A map is defined in a WM*.MAP file. Divided in 0xB800 byte blocks.
          */
         struct Map{
 
@@ -269,8 +271,9 @@ class WorldInstaller{
         /**
          * Path to the world map elements models directory.
          *
-         * Elements can be locations, playable characters, enemies, transportations... anything
-         * that it's not in itself part of the terrain.
+         * Elements can be locations, playable characters, enemies,
+         * transportations... anything that it's not in itself part of the
+         * terrain.
          */
         static std::string ELEMENT_MODELS_DIR;
 
@@ -313,9 +316,9 @@ class WorldInstaller{
         std::vector<Texture> texture_;
 
         /**
-         * The path to the directory from which to read the PC game data.
+         * The disk image to extract the PC game data from.
          */
-        std::string input_dir_;
+        DiskImage disk_image_;
 
         /**
          * The path to the directory where to save the V-Gears data.

+ 9 - 29
src/installer/common/BinGZipFile.cpp

@@ -59,21 +59,15 @@ File* BinGZipFile::ExtractGZip(u32 file_index){
         switch (ret){
             case Z_MEM_ERROR:
                 inflateEnd(&strm);
-                LOGGER->Log(
-                  "Warning: inflateInit2 - Z_MEM_ERROR in file " + file_name_
-                );
+                LOGGER->Log("Warning: inflateInit2 - Z_MEM_ERROR in file " + file_name_);
                 break;
             case Z_STREAM_ERROR:
                 inflateEnd(&strm);
-                LOGGER->Log(
-                  "Warning: inflateInit2 - Z_STREAM_ERROR in file " + file_name_
-                );
+                LOGGER->Log("Warning: inflateInit2 - Z_STREAM_ERROR in file " + file_name_);
                 break;
             default:
                 inflateEnd(&strm);
-                LOGGER->Log(
-                  "Warning: inflateInit2 - unknown error in file " + file_name_
-                );
+                LOGGER->Log("Warning: inflateInit2 - unknown error in file " + file_name_);
         }
         return NULL;
     }
@@ -88,21 +82,15 @@ File* BinGZipFile::ExtractGZip(u32 file_index){
         switch (ret){
             case Z_NEED_DICT:
                 inflateEnd(&strm);
-                LOGGER->Log(
-                  "Warning: inflate - Z_NEED_DICT in file " + file_name_
-                );
+                LOGGER->Log("Warning: inflate - Z_NEED_DICT in file " + file_name_);
                 return NULL;
             case Z_DATA_ERROR:
                 inflateEnd(&strm);
-                LOGGER->Log(
-                  "Warning: inflate - Z_DATA_ERROR in file " + file_name_
-                );
+                LOGGER->Log("Warning: inflate - Z_DATA_ERROR in file " + file_name_);
                 return NULL;
             case Z_MEM_ERROR:
                 inflateEnd(&strm);
-                LOGGER->Log(
-                  "Warning: inflate - Z_MEM_ERROR in file " + file_name_
-                );
+                LOGGER->Log("Warning: inflate - Z_MEM_ERROR in file " + file_name_);
                 return NULL;
         }
 
@@ -110,9 +98,7 @@ File* BinGZipFile::ExtractGZip(u32 file_index){
             extract_buffer = static_cast<u8*>(realloc(extract_buffer, extract_size * 2));
             if (extract_buffer == NULL){
                 inflateEnd(&strm);
-                LOGGER->Log(
-                  "Warning: extract_buffer == NULL in file " + file_name_
-                );
+                LOGGER->Log("Warning: extract_buffer == NULL in file " + file_name_);
                 return NULL;
             }
             strm.next_out = static_cast<u8*>(extract_buffer + extract_size);
@@ -140,11 +126,7 @@ void BinGZipFile::InnerGetNumberOfFiles(){
         if (temp == 0xFFFF) temp = 0; // Hack to deal with scene.bin
         pointer += 6;
         // Condition for GZip header.
-        if (
-          GetU32LE(pointer) == 0x00088B1F && GetU32LE(pointer + 4) == 0x00000000
-        ){
-            ++ file_count_;
-        }
+        if (GetU32LE(pointer) == 0x00088B1F && GetU32LE(pointer + 4) == 0x00000000) ++ file_count_;
         pointer += temp;
     }
 
@@ -163,9 +145,7 @@ u32 BinGZipFile::InnerGetFileOffset(u32 file_index){
         u16 temp = GetU16LE(pointer);
         pointer += 6;
         // Condition for GZip header
-        if (
-          GetU32LE(pointer) == 0x00088B1F && GetU32LE(pointer + 4) == 0x00000000
-        ){
+        if (GetU32LE(pointer) == 0x00088B1F && GetU32LE(pointer + 4) == 0x00000000){
             if (file_index == current_file) return pointer;
         }
         ++ current_file;

+ 6 - 10
src/installer/common/DrawSkeleton.cpp

@@ -61,8 +61,7 @@ void DrawSkeleton(const Skeleton& skeleton, const Ogre::MeshPtr& mesh){
     sub_mesh->indexData = new Ogre::IndexData();
     sub_mesh->indexData->indexStart = 0;
     sub_mesh->indexData->indexCount = sub_mesh->vertexData->vertexCount;
-    sub_mesh->indexData->indexBuffer
-      = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
+    sub_mesh->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
         Ogre::HardwareIndexBuffer::IT_16BIT,
         sub_mesh->indexData->indexCount,
         Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY
@@ -85,16 +84,14 @@ void DrawSkeleton(const Skeleton& skeleton, const Ogre::MeshPtr& mesh){
 
     // 2nd buffer.
     decl->addElement(COLOUR_BINDING, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
-    Ogre::HardwareVertexBufferSharedPtr vbuf1
-      = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(
+    Ogre::HardwareVertexBufferSharedPtr vbuf1 = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(
         decl->getVertexSize(COLOUR_BINDING),
         sub_mesh->vertexData->vertexCount,
-        Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
+        Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY
+    );
     // Set vertex buffer binding so buffer 1 is bound to our colour buffer.
     bind->setBinding(COLOUR_BINDING, vbuf1);
-    position = static_cast<float*> (
-      vbuf0->lock(Ogre::HardwareBuffer::HBL_DISCARD)
-    );
+    position = static_cast<float*> (vbuf0->lock(Ogre::HardwareBuffer::HBL_DISCARD));
     std::vector<Ogre::RGBA> colours(sub_mesh->vertexData->vertexCount);
     DrawBone(skeleton, -1, colours.data());
     vbuf0->unlock();
@@ -116,8 +113,7 @@ void DrawBone(const Skeleton& skeleton, int parent_index, Ogre::RGBA* colours){
             *position ++ = 0;
             Ogre::ColourValue colour1 = Ogre::ColourValue(1, 0, 0);
             Ogre::ColourValue colour2 = Ogre::ColourValue(1, 1, 0);
-            Ogre::RenderSystem* rs
-              = Ogre::Root::getSingleton().getRenderSystem();
+            Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem();
             colours[cur_index] = colour1.getAsBYTE();
             colours[cur_index + 1] = colour2.getAsBYTE();
             index_data[cur_index + 0] = cur_index + 0;

+ 12 - 24
src/installer/common/FontFile.cpp

@@ -23,11 +23,9 @@ using namespace VGears;
 
 FontFile::FontFile(File* file): File(file){}
 
-FontFile::FontFile(File* file, const u32& offset, const u32& length):
-  File(file, offset, length){}
+FontFile::FontFile(File* file, const u32& offset, const u32& length): File(file, offset, length){}
 
-FontFile::FontFile(u8* buffer, const u32& offset, const u32& length):
-  File(buffer, offset, length){}
+FontFile::FontFile(u8* buffer, const u32& offset, const u32& length): File(buffer, offset, length){}
 
 FontFile::~FontFile(void){}
 
@@ -80,9 +78,7 @@ Surface* FontFile::GetSurface(void){
                 color.g = (((data >> i) & 0x01) == 1) ? 0 : 255;
                 color.b = (((data >> i) & 0x01) == 1) ? 0 : 255;
                 color.a = 255;
-                memcpy(
-                  glyth->pixels.data() + 64 * y + j, &color, sizeof(ClutColor)
-                );
+                memcpy(glyth->pixels.data() + 64 * y + j, &color, sizeof(ClutColor));
                 j += 4;
             }
             data = (y < 11) ? GetU8(0xE + chars * 22 + y * 2 + 1) : 0;
@@ -92,9 +88,7 @@ Surface* FontFile::GetSurface(void){
                 color.g = (((data >> i) & 0x01) == 1) ? 0 : 255;
                 color.b = (((data >> i) & 0x01) == 1) ? 0 : 255;
                 color.a = 255;
-                memcpy(
-                  glyth->pixels.data() + 64 * y + j, &color, sizeof(ClutColor)
-                );
+                memcpy(glyth->pixels.data() + 64 * y + j, &color, sizeof(ClutColor));
                 j += 4;
             }
         }
@@ -110,30 +104,24 @@ Surface* FontFile::GetSurface(void){
     Ogre::TexturePtr ptex;
     Ogre::HardwarePixelBufferSharedPtr buffer;
     ptex = Ogre::TextureManager::getSingleton().createManual(
-      "DynaTex", "General", Ogre::TEX_TYPE_2D,
-      ret->width, ret->height, 0, Ogre::PF_R8G8B8A8, Ogre::TU_STATIC
+      "DynaTex", "General", Ogre::TEX_TYPE_2D, ret->width, ret->height, 0, Ogre::PF_R8G8B8A8, Ogre::TU_STATIC
     );
     buffer = ptex->getBuffer(0, 0);
     buffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
     const Ogre::PixelBox& pb = buffer->getCurrentLock();
     for (auto y = 0; y < ret->height; ++ y){
-        // IVV type cast
-        auto* data = static_cast<unsigned int*> (
-          reinterpret_cast<unsigned int *>(pb.data) + y * pb.rowPitch
-        );
+        // Type cast
+        auto* data = static_cast<unsigned int*> (reinterpret_cast<unsigned int *>(pb.data) + y * pb.rowPitch);
         for (auto x = 0; x < ret->width; ++ x){
-            auto clut
-              = ret->pixels[y * ret->width * 4 + x * 4 + 3]
-                | (ret->pixels[y * ret->width * 4 + x * 4 + 2] << 8)
-                | (ret->pixels[y * ret->width * 4 + x * 4 + 1] << 16)
-                | (ret->pixels[y * ret->width * 4 + x * 4 + 0] << 24);
+            auto clut = ret->pixels[y * ret->width * 4 + x * 4 + 3]
+              | (ret->pixels[y * ret->width * 4 + x * 4 + 2] << 8)
+              | (ret->pixels[y * ret->width * 4 + x * 4 + 1] << 16)
+              | (ret->pixels[y * ret->width * 4 + x * 4 + 0] << 24);
             data[x] = clut;
         }
     }
     Ogre::Image image;
-    image.loadDynamicImage(
-      (Ogre::uchar*) pb.data, ret->width, ret->height, Ogre::PF_R8G8B8A8
-    );
+    image.loadDynamicImage((Ogre::uchar*) pb.data, ret->width, ret->height, Ogre::PF_R8G8B8A8);
     image.save("font.png");
     buffer->unlock();
     Ogre::TextureManager::getSingleton().remove("DynaTex");

+ 5 - 15
src/installer/common/Logger.cpp

@@ -28,17 +28,11 @@ Ogre::String HexToString(int value, unsigned short width, char fill){
     return stream.str();
 }
 
-Ogre::String BoolToString(bool value){
-    return Ogre::StringConverter::toString(value);
-}
+Ogre::String BoolToString(bool value){return Ogre::StringConverter::toString(value);}
 
-Ogre::String IntToString(int value){
-    return Ogre::StringConverter::toString(value);
-}
+Ogre::String IntToString(int value){return Ogre::StringConverter::toString(value);}
 
-Ogre::String FloatToString(float value){
-    return Ogre::StringConverter::toString(value);
-}
+Ogre::String FloatToString(float value){return Ogre::StringConverter::toString(value);}
 
 Logger::Logger(const Ogre::String& log_file_name):
     log_file_(log_file_name){
@@ -47,13 +41,9 @@ Logger::Logger(const Ogre::String& log_file_name):
 
 Logger::~Logger(){}
 
-void Logger::Log(const Ogre::String& text){
-    FileSystem::WriteFile(log_file_, text.c_str(), text.size());
-}
+void Logger::Log(const Ogre::String& text){FileSystem::WriteFile(log_file_, text.c_str(), text.size());}
 
-void Logger::LogW(const Ogre::UTFString& text){
-    FileSystem::WriteFile(log_file_, text.c_str(), text.size() * 2);
-}
+void Logger::LogW(const Ogre::UTFString& text){FileSystem::WriteFile(log_file_, text.c_str(), text.size() * 2);}
 
 void Logger::Log(std::vector< unsigned char >& text){
     unsigned char* temp = new unsigned char[ text.size() ];

+ 2 - 6
src/installer/common/OgreBase.cpp

@@ -92,9 +92,7 @@ void InitializeOgreBase(const Ogre::String& name){
     window = root->createRenderWindow("VGearsWindow", 800, 600, false, &misc);
     // Initialize resources in the current launch directory.
     Ogre::ResourceGroupManager::getSingleton().addResourceLocation("./", "FileSystem", "General");
-    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
-      "./exported", "FileSystem", "General"
-    );
+    Ogre::ResourceGroupManager::getSingleton().addResourceLocation("./exported", "FileSystem", "General");
     Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
     Ogre::SceneManager* scene_manager;
     Ogre::Viewport* viewport;
@@ -114,9 +112,7 @@ void InitializeOgreBase(const Ogre::String& name){
     camera->getParentSceneNode()->lookAt(Ogre::Vector3(0, 0, 0), Ogre::Node::TS_LOCAL);
     viewport = window->addViewport(camera);
     viewport->setBackgroundColour(Ogre::ColourValue(0.0f, 0.4f, 0.0f));
-    camera->setAspectRatio(
-      Ogre::Real(viewport->getActualWidth()) / Ogre::Real(viewport->getActualHeight())
-    );
+    camera->setAspectRatio(Ogre::Real(viewport->getActualWidth()) / Ogre::Real(viewport->getActualHeight()));
     LOGGER = new Logger("game.log");
 }
 

+ 11 - 30
src/installer/common/OgreBase.h

@@ -71,8 +71,7 @@ class DisplayFrameListener :
          * @param[in] win The render window. Used to determine input context.
          */
         DisplayFrameListener(Ogre::RenderWindow* win):
-          window_(win), input_manager_(0), keyboard_(0),
-          mouse_(0), mouse_rotate_(false), exit_(false)
+          window_(win), input_manager_(0), keyboard_(0),mouse_(0), mouse_rotate_(false), exit_(false)
         {
 
             using namespace OIS;
@@ -84,13 +83,9 @@ class DisplayFrameListener :
             window_hnd_str << window_hnd;
             pl.insert(std::make_pair(std::string("WINDOW"), window_hnd_str.str()));
             input_manager_ = InputManager::createInputSystem(pl);
-            keyboard_ = static_cast<Keyboard*>(
-              input_manager_->createInputObject(OIS::OISKeyboard, true)
-            );
+            keyboard_ = static_cast<Keyboard*>(input_manager_->createInputObject(OIS::OISKeyboard, true));
             keyboard_->setEventCallback(this);
-            mouse_ = static_cast<OIS::Mouse*>(
-              input_manager_->createInputObject(OIS::OISMouse, true)
-            );
+            mouse_ = static_cast<OIS::Mouse*>(input_manager_->createInputObject(OIS::OISMouse, true));
             mouse_->setEventCallback(this);
             //Register as a Window listener.
             Ogre::WindowEventUtilities::addWindowEventListener(window_, this);
@@ -99,7 +94,7 @@ class DisplayFrameListener :
             info_text->setCaption("");
             info_text->setMetricsMode(Ogre::GMM_PIXELS);
             info_text->setPosition(5, 5);
-           // info_text->setFontName("BlueHighway");
+            // info_text->setFontName("BlueHighway");
             info_text->setCharHeight(18);
             info_overlay = Ogre::OverlayManager::getSingleton().create("DebugOverlay");
             info_overlay->setZOrder(1);
@@ -148,9 +143,7 @@ class DisplayFrameListener :
                         Ogre::AnimationState* state = animations.getNext();
                         if (state->getEnabled() == true){
                             state->addTime(evt.timeSinceLastFrame);
-                            info_text->setCaption(
-                              entities[i]->getName() + ": " + state->getAnimationName()
-                            );
+                            info_text->setCaption(entities[i]->getName() + ": " + state->getAnimationName());
                         }
                     }
                     break;
@@ -158,27 +151,19 @@ class DisplayFrameListener :
             }
             if (keyboard_->isKeyDown(OIS::KC_A)){
                 assert(camera_);
-                camera_->getParentSceneNode()->translate(
-                  Ogre::Vector3(-0.005f, 0.0f, 0.0f), Ogre::Node::TS_LOCAL
-                );
+                camera_->getParentSceneNode()->translate(Ogre::Vector3(-0.005f, 0.0f, 0.0f), Ogre::Node::TS_LOCAL);
             }
             if (keyboard_->isKeyDown(OIS::KC_D)){
                 assert(camera_);
-                camera_->getParentSceneNode()->translate(
-                  Ogre::Vector3(0.005f, 0.0f, 0.0f), Ogre::Node::TS_LOCAL
-                );
+                camera_->getParentSceneNode()->translate(Ogre::Vector3(0.005f, 0.0f, 0.0f), Ogre::Node::TS_LOCAL);
             }
             if (keyboard_->isKeyDown(OIS::KC_W)){
                 assert(camera_);
-                camera_->getParentSceneNode()->translate(
-                  Ogre::Vector3(0.0f, 0.0f, -0.005f), Ogre::Node::TS_LOCAL
-                );
+                camera_->getParentSceneNode()->translate(Ogre::Vector3(0.0f, 0.0f, -0.005f), Ogre::Node::TS_LOCAL);
             }
             if (keyboard_->isKeyDown(OIS::KC_S)){
                 assert(camera_);
-                camera_->getParentSceneNode()->translate(
-                  Ogre::Vector3(0.0f, 0.0f,  0.005f), Ogre::Node::TS_LOCAL
-                );
+                camera_->getParentSceneNode()->translate(Ogre::Vector3(0.0f, 0.0f,  0.005f), Ogre::Node::TS_LOCAL);
             }
             if (mouse_rotate_ == true){
                 assert(camera_);
@@ -236,8 +221,7 @@ class DisplayFrameListener :
                         for (unsigned int i = 0; i < entities.size(); ++ i){
                             if (entities[i]->isVisible() == true){
                                 Ogre::AnimationStateIterator animations
-                                  = entities[i]->getAllAnimationStates()
-                                    ->getAnimationStateIterator();
+                                  = entities[i]->getAllAnimationStates()->getAnimationStateIterator();
                                 Ogre::AnimationState* old_state;
                                 Ogre::AnimationState* new_state = animations.getNext();
                                 while (animations.hasMoreElements() == true){
@@ -268,10 +252,7 @@ class DisplayFrameListener :
                                     state->setLoop(true);
                                     break;
                                 }
-                                if (
-                                  state->getEnabled() == true
-                                  && animations.hasMoreElements() != false
-                                ){
+                                if (state->getEnabled() == true && animations.hasMoreElements() != false){
                                     state->setEnabled(false);
                                     state->setLoop(false);
                                     change = true;

+ 23 - 38
src/installer/common/OgreGenUtilites.cpp

@@ -17,33 +17,28 @@
 #include "installer/common/Logger.h"
 
 void CreateTextureFromVram(
-  const Ogre::PixelBox& pixel_box, Vram* vram,
-  const int start_x, const int start_y, const int clut_x, const int clut_y,
-  const int texture_x, const int texture_y, const BPP bpp,
-  const bool transparency
+  const Ogre::PixelBox& pixel_box, Vram* vram, const int start_x,
+  const int start_y, const int clut_x, const int clut_y, const int texture_x,
+  const int texture_y, const BPP bpp, const bool transparency
 ){
     for (u32 y = 0; y < 256; ++ y){
         // IVV type cast
-        u32* data = static_cast<u32*>(
-          (unsigned int *) ((pixel_box.data) + (y + start_y) * pixel_box.rowPitch)
-        );
+        u32* data = static_cast<u32*>((unsigned int *) ((pixel_box.data) + (y + start_y) * pixel_box.rowPitch));
         for (u32 x = 0; x < 256; ++ x){
             if (bpp == BPP_4){
                 u8 pixel = vram->GetU8(texture_x * 2 + x / 2, texture_y + y);
                 u16 clut1 = vram->GetU16(clut_x * 2 + (pixel & 0xf) * 2, clut_y);
                 u16 clut2 = vram->GetU16(clut_x * 2 + (pixel >> 4) * 2, clut_y);
-                u32 ogre_pixel1
-                  = (((clut1 & 0x1f) * 255 / 31) << 0x18)
-                    | ((((clut1 >>  5) & 0x1f) * 255 / 31) << 0x10)
-                    | ((((clut1 >> 10) & 0x1f) * 255 / 31) << 0x8)
-                    | 0;
+                u32 ogre_pixel1= (((clut1 & 0x1f) * 255 / 31) << 0x18)
+                  | ((((clut1 >>  5) & 0x1f) * 255 / 31) << 0x10)
+                  | ((((clut1 >> 10) & 0x1f) * 255 / 31) << 0x8)
+                  | 0;
                 u8 stp = (clut1 & 0x8000) >> 15;
                 AddTransparency(ogre_pixel1, transparency, stp > 0);
-                u32 ogre_pixel2
-                  = (((clut2 & 0x1f) * 255 / 31) << 0x18)
-                    | ((((clut2 >>  5) & 0x1f) * 255 / 31) << 0x10)
-                    | ((((clut2 >> 10) & 0x1f) * 255 / 31) << 0x8)
-                    | 0;
+                u32 ogre_pixel2 = (((clut2 & 0x1f) * 255 / 31) << 0x18)
+                  | ((((clut2 >>  5) & 0x1f) * 255 / 31) << 0x10)
+                  | ((((clut2 >> 10) & 0x1f) * 255 / 31) << 0x8)
+                  | 0;
                 stp = (clut2 & 0x8000) >> 15;
                 AddTransparency(ogre_pixel2, transparency, stp > 0);
                 data[start_x + x] = ogre_pixel1;
@@ -53,11 +48,10 @@ void CreateTextureFromVram(
             else if (bpp == BPP_8){
                 u8 pixel = vram->GetU8(texture_x * 2 + x, texture_y + y);
                 u16 clut = vram->GetU16(clut_x * 2 + pixel * 2, clut_y);
-                u32 ogre_pixel
-                  = (((clut & 0x1f) * 255 / 31) << 0x18)
-                    | ((((clut >>  5) & 0x1f) * 255 / 31) << 0x10)
-                    | ((((clut >> 10) & 0x1f) * 255 / 31) << 0x8)
-                    | 0;
+                u32 ogre_pixel = (((clut & 0x1f) * 255 / 31) << 0x18)
+                  | ((((clut >>  5) & 0x1f) * 255 / 31) << 0x10)
+                  | ((((clut >> 10) & 0x1f) * 255 / 31) << 0x8)
+                  | 0;
                 u8 stp = (clut & 0x8000) >> 15;
                 AddTransparency(ogre_pixel, transparency, stp > 0);
                 data[start_x + x ] = ogre_pixel;
@@ -68,8 +62,7 @@ void CreateTextureFromVram(
 }
 
 void CreateTexture(
-  Vram* vram, const MeshData& mesh_data, const Ogre::String& texture_file_name,
-  const VectorTexForGen& textures
+  Vram* vram, const MeshData& mesh_data, const Ogre::String& texture_file_name, const VectorTexForGen& textures
 ){
     Ogre::TexturePtr ptex;
     Ogre::HardwarePixelBufferSharedPtr buffer;
@@ -94,19 +87,15 @@ void CreateTexture(
         );
     }
     Ogre::Image image;
-    image.loadDynamicImage(
-      (Ogre::uchar*) pixel_box.data, mesh_data.tex_width, mesh_data.tex_height, Ogre::PF_R8G8B8A8
-    );
+    image.loadDynamicImage((Ogre::uchar*) pixel_box.data, mesh_data.tex_width, mesh_data.tex_height, Ogre::PF_R8G8B8A8);
     image.save(texture_file_name);
     buffer->unlock();
     return;
 }
 
-void
-CreateMaterial(
-  const Ogre::String& material_name, const Ogre::String& material_file_name,
-  const Ogre::String& texture_name, const Ogre::String& vertex_program,
-  const Ogre::String& fragment_program
+void CreateMaterial(
+  const Ogre::String& material_name, const Ogre::String& material_file_name, const Ogre::String& texture_name,
+  const Ogre::String& vertex_program, const Ogre::String& fragment_program
 ){
     Ogre::MaterialPtr material
       = Ogre::MaterialManager::getSingleton().create(material_name, "General");
@@ -128,10 +117,7 @@ CreateMaterial(
     mat.exportMaterial(material, material_file_name);
 }
 
-void AddTexture(
-  TexForGen& texture, const MeshData& data, VectorTexForGen& textures,
-  Logger* logger
-){
+void AddTexture(TexForGen& texture, const MeshData& data, VectorTexForGen& textures, Logger* logger){
     for (unsigned int i = 0; i < textures.size(); ++ i){
         if (texture == textures[ i ]){
             texture.start_x = textures[ i ].start_x;
@@ -147,8 +133,7 @@ void AddTexture(
         start_y = float(textures[ textures.size() - 1 ].start_y);
         if (start_x + 256 >= data.tex_width){
             if (start_y + 256 >= data.tex_height){
-                if (logger != NULL)
-                    logger->Log("[ERROR] Can't add anymore textures. Increase tex size.\n");
+                if (logger != NULL) logger->Log("[ERROR] Can't add anymore textures. Increase tex size.\n");
                 return;
             }
             start_x = 0;

+ 6 - 11
src/installer/common/OgreGenUtilites.h

@@ -132,8 +132,7 @@ typedef std::vector< TexForGen > VectorTexForGen;
  * @param[in] textures @todo It's used to get some coordinates, but for what?
  */
 void CreateTexture(
-  Vram* vram, const MeshData& mesh_data,
-  const Ogre::String& texture_file_name, const VectorTexForGen& textures
+  Vram* vram, const MeshData& mesh_data, const Ogre::String& texture_file_name, const VectorTexForGen& textures
 );
 
 /**
@@ -146,9 +145,8 @@ void CreateTexture(
  * @param[in] fragment_program @todo Understand and document. Can be empty.
  */
 void CreateMaterial(
-  const Ogre::String& material_name, const Ogre::String& material_file_name,
-  const Ogre::String& texture_name, const Ogre::String& vertex_program,
-  const Ogre::String& fragment_program
+  const Ogre::String& material_name, const Ogre::String& material_file_name, const Ogre::String& texture_name,
+  const Ogre::String& vertex_program, const Ogre::String& fragment_program
 );
 
 /**
@@ -167,9 +165,8 @@ void CreateMaterial(
  * @todo What's the difference between start_x/start_y and texture_x/texture_y?
  */
 void CreateTextureFromVram(
-  const Ogre::PixelBox& pixel_box, Vram* vram, const int start_x, const int start_y,
-  const int clut_x, const int clut_y, const int texture_x, const int texture_y,
-  const BPP bpp, const bool transparency
+  const Ogre::PixelBox& pixel_box, Vram* vram, const int start_x, const int start_y, const int clut_x,
+  const int clut_y, const int texture_x, const int texture_y, const BPP bpp, const bool transparency
 );
 
 /**
@@ -181,9 +178,7 @@ void CreateTextureFromVram(
  * @param[in] logger Custom logger to print info about the process. It can be
  * NULL, but then nothing will be printed.
  */
-void AddTexture(
-  TexForGen& texture, const MeshData& data, VectorTexForGen& textures, Logger* logger
-);
+void AddTexture(TexForGen& texture, const MeshData& data, VectorTexForGen& textures, Logger* logger);
 
 /**
  * Adds or removes a transparency to a colour.

+ 1 - 2
src/installer/common/TimToVram.cpp

@@ -48,8 +48,7 @@ void LoadTimFileToVram(File* file, int tim_offset, Vram* vram){
     LOGGER->Log(
       "ImageVramPositionX (" + HexToString(image_vram_position_x, 4, '0') + ") ImageVramPositionY ("
       + HexToString(image_vram_position_y, 4, '0') + ") ImageWidth ("
-      + HexToString(image_width, 4, '0') + ") ImageHeight (" +  HexToString(image_height, 4, '0')
-      + ")\n"
+      + HexToString(image_width, 4, '0') + ") ImageHeight (" +  HexToString(image_height, 4, '0') + ")\n"
     );
     for (int y = 0; y < image_height; ++ y){
         for (int x = 0; x < image_width; ++ x){

+ 1 - 2
src/installer/common/Vram.cpp

@@ -39,8 +39,7 @@ void Vram::Save(const Ogre::String& file){
     u32* data = static_cast<u32*>((unsigned int *) pb.data);
     for (int i = 0, size = width_ * height_ / 2; i < size; ++ i){
         u16 col = (vram_[i * 2 + 1] << 8) | vram_[i * 2 + 0];
-        data[i]
-          = (((col & 0x1f) * 255 / 31) << 0x18)
+        data[i] = (((col & 0x1f) * 255 / 31) << 0x18)
           | ((((col >>  5) & 0x1f) * 255 / 31) << 0x10)
           | ((((col >> 10) & 0x1f) * 255 / 31) << 0x8)
           | 0xff;

+ 9 - 16
src/installer/data/AbFile.cpp

@@ -27,8 +27,7 @@ unsigned int AbFile::GenerateScripts(std::string model_id, std::string path) con
     std::string file_name = path + model_id + ".lua";
     std::fstream file(file_name, std::ios::out);
     if (!file.is_open()){
-        std::cerr << "Unable to open file " << file_name
-          << " for animation script generation." << std::endl;
+        std::cerr << "Unable to open file " << file_name  << " for animation script generation." << std::endl;
         return 0;
     }
     file << model_id << " = {}" << std::endl << std::endl;
@@ -69,9 +68,7 @@ std::string AbFile::DecompileScript(u32 offset){
         }
         total_opcodes ++;
         u8 opcode = ab_file_.readU8();
-        if (opcode >= 0x00 && opcode <= 0x8D){
-            line << "    play_animation(" << static_cast<int>(opcode) << ")";
-        }
+        if (opcode >= 0x00 && opcode <= 0x8D) line << "    play_animation(" << static_cast<int>(opcode) << ")";
         else{
             switch (opcode){
                 case 0x91: // Play effect, one 1 byte parameter.
@@ -90,13 +87,12 @@ std::string AbFile::DecompileScript(u32 offset){
                      //   distance (2 bytes), distance from the joint.
                      //   start (1 byte), effect start.
                      //   end (1 byte), effect end.
-                    line << "    set_effect(" << static_cast<int>(ab_file_.readU8()) << ", "
-                      << ab_file_.readU16LE() << ", " << static_cast<int>(ab_file_.readU8()) << ", "
+                    line << "    set_effect(" << static_cast<int>(ab_file_.readU8()) << ", " << ab_file_.readU16LE()
+                      << ", " << static_cast<int>(ab_file_.readU8()) << ", "
                       << static_cast<int>(ab_file_.readU8()) << ")";
                     break;
                 case 0xB6: // Pause camera and finish animation. One byte parameter, animation ID.
-                    line << "    pause_camera_finish_animation("
-                      << static_cast<int>(ab_file_.readU8()) << ")";
+                    line << "    pause_camera_finish_animation(" << static_cast<int>(ab_file_.readU8()) << ")";
                     break;
                 case 0xB9: // Set camera. One byte parameter, camera ID.
                     line << "    set_camera(" << static_cast<int>(ab_file_.readU8()) << ")";
@@ -129,9 +125,7 @@ std::string AbFile::DecompileScript(u32 offset){
                         line << "    ::jump::";
                         label_set = true;
                     }
-                    else{
-                        line << "    -- Invalid jump label";
-                    }
+                    else line << "    -- Invalid jump label";
                     break;
                 case 0xCA:
                      // Jump to label set by 0xC9 (::jump::) if nothing is being loaded in the
@@ -151,16 +145,15 @@ std::string AbFile::DecompileScript(u32 offset){
                      //   distance (2 byte), unknown.
                      //   unknown (2 bytes), unknown.
                      //   steps (1 byte), number of steps.
-                    line << "    move_to_target(" << ab_file_.readU16LE() << ", "
-                      << ab_file_.readU16LE() << ", " << static_cast<int>(ab_file_.readU8()) << ")";
+                    line << "    move_to_target(" << ab_file_.readU16LE() << ", " << ab_file_.readU16LE() << ", "
+                      << static_cast<int>(ab_file_.readU8()) << ")";
                     break;
                 case 0xD8:
                     // Play sound using attacker settings after waiting given number of frames.
                     // Two parameters:
                     //   wait (1 byte), frames to wait before the sound.
                     //   sound (2 bytes), sound ID.
-                    line << "    play_sound_for_attacker("
-                      << static_cast<int>(ab_file_.readU8()) << ", "
+                    line << "    play_sound_for_attacker(" << static_cast<int>(ab_file_.readU8()) << ", "
                       << ab_file_.readU16LE() <<  ")";
                     break;
                 case 0xE5:

+ 12 - 11
src/installer/data/AbFile.h

@@ -34,24 +34,25 @@ class AbFile{
          *
          * Reads the file.
          *
-         * @param[in, out] file The file to read from. The file contents will not be altered, but
-         * it's offset will be changed while reading it.
-         * @param[in] enemy Indicates if the animation is for an enemy. Enemies have a different
-         * number of animations than characters or backgrounds.
+         * @param[in, out] file The file to read from. The file contents will
+         * not be altered, but it's offset will be changed while reading it.
+         * @param[in] enemy Indicates if the animation is for an enemy. Enemies
+         * have a different number of animations than characters or backgrounds.
          */
         AbFile(File file, bool enemy);
 
         /**
          * Generates script files from the ba file.
          *
-         * A file will be generated with every script in the *ab file. The file will be created in
-         * the specified path, and will be named like the model id, followed by the ".lua"
-         * extension. For example, if path is "/home/user/.v-gears", the model id is "rt", the file
-         * name will be: "/home/user/.v-gears/rt.lua". Each script will be named "script" + index
-         * (script1(), script2() ... script44() ...)
+         * A file will be generated with every script in the *ab file. The file
+         * will be created in the specified path, and will be named like the
+         * model id, followed by the ".lua" extension. For example, if path is
+         * "/home/user/.v-gears", the model id is "rt", the file name will be:
+         * "/home/user/.v-gears/rt.lua". Each script will be named
+         * "script" + index (script1(), script2() ... script44() ...)
          *
-         * @param[in] model_id ID of the model the animation belongs to, usually a two letter
-         * code. Used to generate the file name.
+         * @param[in] model_id ID of the model the animation belongs to, usually
+         * a two letter code. Used to generate the file name.
          * @param[in] path Path to the directory where the files will be saved.
          * @return The number of scripts written to the file.
          */

+ 30 - 25
src/installer/data/Attack.h

@@ -128,66 +128,70 @@ struct Attack{
         /**
          * The target can be selected.
          *
-         * If the target of the attack is selectable, at least between some subset of
-         * possible targets, this must be true.
+         * If the target of the attack is selectable, at least between some
+         * subset of possible targets, this must be true.
          */
         bool selection_enabled;
 
         /**
          * The default target is an enemy.
          *
-         * When true, the cursor starts in an enemy row. When false, it starts in the ally
-         * row. It may or may not be possible to change it.
+         * When true, the cursor starts in an enemy row. When false, it starts
+         * in the ally row. It may or may not be possible to change it.
          */
         bool default_enemy;
 
         /**
          * The default target is a group.
          *
-         * When true, the cursor starts will all the members of a row selected. When false,
-         * it starts with only one target selected. It may or may not be possible to change
-         * it.
+         * When true, the cursor starts will all the members of a row selected.
+         * When false, it starts with only one target selected. It may or may
+         * not be possible to change it.
          */
         bool default_multiple;
 
         /**
          * The selection can change between groups and individuals.
          *
-         * When true, the target can be changed between groups and individuals When false,
-         * only {@see default_multiple} determines if the target is an individual or a
-         * group.
+         * When true, the target can be changed between groups and individuals. 
+         * When false, only {@see default_multiple} determines if the target is
+         * an individual or a group.
          */
         bool toggle_multiple;
 
         /**
          * The target group can be changed.
          *
-         * When true, the target cannot be changed between enemies and allies. In this case
-         * {@see default_enemy} will determine the group that can be targeted.
+         * When true, the target cannot be changed between enemies and allies.
+         * In this case, {@see default_enemy} will determine the group that can
+         * be targeted.  {@see default_enemy} will determine the group that can
+         * be targeted.
          */
         bool fixed_row;
 
         /**
          * The command is short ranged.
          *
-         * When true, if the target or the caster is not in the front of their row, the
-         * target will take half damage. Also, when true, an enemy covered by another enemy
-         * can't be targeted.
+         * When true, if the target or the caster is not in the front of their
+         * row, the target will take half damage. Also, when true, an enemy
+         * covered by another enemy  target will take half damage. Also, when
+         * true, an enemy covered by another enemy can't be targeted.
          */
         bool short_range;
 
         /**
          * Targets all, enemies and allies.
          *
-         * When true, all allies and enemies are targets, and the selection can't be changed.
+         * When true, all allies and enemies are targets, and the selection
+         * can't be changed.
          */
         bool all_rows;
 
         /**
          * The target is random.
          *
-         * When multiple targets are selected, one will be selected at random to be the
-         * receiving target. Cursor will cycle among all viable targets.
+         * When multiple targets are selected, one will be selected at random to
+         * be the receiving target. Cursor will cycle among all viable targets.
          */
         bool random;
 
@@ -222,31 +226,32 @@ struct Attack{
         int impact_effect;
 
         /**
-         * Index of the animation played by the target when hit. -1 to not show any.
+         * Index of the animation played by the target when hit. -1 to not show
+         * any.
          */
         int hurt_anim;
 
         /**
          * Impact sound.
          *
-         * Determines the camera movement when the command is executed over multiple targets. -1 to not
-         * play any.
+         * Determines the camera movement when the command is executed over
+         * multiple targets. -1 to not play any.
          */
         int sound;
 
         /**
          * Camera movement ID for single target.
          *
-         * Determines the camera movement when the command is executed over a single target.  -1 to not
-         * move the camera.
+         * Determines the camera movement when the command is executed over a
+         * single target.  -1 to not move the camera.
          */
         int camera_single;
 
         /**
          * Camera movement ID for multiple target.
          *
-         * Determines the camera movement when the command is executed over multiple
-         * targets. -1 to not move the camera.
+         * Determines the camera movement when the command is executed over
+         * multiple targets. -1 to not move the camera. 
          */
         int camera_multiple;
     };

+ 1 - 3
src/installer/data/BattleScene.h

@@ -20,6 +20,4 @@
 #include "common/TypeDefine.h"
 #include "data/GameData.h"
 
-struct BattleScene{
-
-};
+struct BattleScene{};

+ 32 - 73
src/installer/data/BattleSceneFile.cpp

@@ -147,8 +147,7 @@ void BattleSceneFile::Read(File file){
         scene_.attack_data[a].special = file.readU16LE();
     }
     for (int a = 0; a < 32; a ++) scene_.attack_ids[a] = file.readU16LE();
-    for (int a = 0; a < 32; a ++)
-        for (int b = 0; b < 32; b ++) scene_.attack_names[a][b] = file.readU8();
+    for (int a = 0; a < 32; a ++) for (int b = 0; b < 32; b ++) scene_.attack_names[a][b] = file.readU8();
     // TODO: Keep reading for AI scripts.
 
     // All the data is read, now format it.
@@ -189,10 +188,7 @@ void BattleSceneFile::Read(File file){
                 enemy.animations.push_back(scene_.enemy_data[e].animation_index[a]);
         }
         for (int l = 0; l < 8; l ++){
-            if (
-              scene_.enemy_data[e].elements[l] != 0xFF
-              && scene_.enemy_data[e].element_rates[l] != 0xFF
-            ){
+            if (scene_.enemy_data[e].elements[l] != 0xFF && scene_.enemy_data[e].element_rates[l] != 0xFF ){
                 Enemy::Element element;
                 element.id = scene_.enemy_data[e].elements[l];
                 switch (scene_.enemy_data[e].element_rates[l]){
@@ -428,8 +424,6 @@ void BattleSceneFile::Read(File file){
         }
         if (scene_.enemy_data[e].morph != 0xFFFF) enemy.morph = scene_.enemy_data[e].morph;
 
-        //std::cout << "ENEMY " << enemy.id << ": " << enemy.name << "\n";
-
         enemies_.push_back(enemy);
     }
 
@@ -452,33 +446,22 @@ void BattleSceneFile::Read(File file){
         attack.damage.accuracy = scene_.attack_data[a].accuracy / 255.0f;
         if (scene_.attack_data[a].impact_effect != 0xFF)
             attack.animation.impact_effect = scene_.attack_data[a].impact_effect;
-        if (scene_.attack_data[a].hurt_anim != 0xFF)
-            attack.animation.hurt_anim = scene_.attack_data[a].hurt_anim;
+        if (scene_.attack_data[a].hurt_anim != 0xFF) attack.animation.hurt_anim = scene_.attack_data[a].hurt_anim;
         attack.mp = scene_.attack_data[a].mp;
-        if (scene_.attack_data[a].sound != 0xFFFF)
-            attack.animation.sound = scene_.attack_data[a].sound;
+        if (scene_.attack_data[a].sound != 0xFFFF) attack.animation.sound = scene_.attack_data[a].sound;
         if (scene_.attack_data[a].camera_single != 0xFFFF)
             attack.animation.camera_single = scene_.attack_data[a].camera_single;
         if (scene_.attack_data[a].camera_multiple != 0xFFFF)
             attack.animation.camera_multiple = scene_.attack_data[a].camera_multiple;
-        attack.target.selection_enabled
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x01));
-        attack.target.default_enemy
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x02));
-        attack.target.default_multiple
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x04));
-        attack.target.toggle_multiple
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x08));
-        attack.target.fixed_row
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x10));
-        attack.target.short_range
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x20));
-        attack.target.all_rows
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x40));
-        attack.target.random
-          = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x80));
-        if (scene_.attack_data[a].effect != 0xFFFF)
-            attack.animation.effect = scene_.attack_data[a].effect;
+        attack.target.selection_enabled = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x01));
+        attack.target.default_enemy = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x02));
+        attack.target.default_multiple = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x04));
+        attack.target.toggle_multiple = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x08));
+        attack.target.fixed_row = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x10));
+        attack.target.short_range = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x20));
+        attack.target.all_rows = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x40));
+        attack.target.random = (scene_.attack_data[a].target == (scene_.attack_data[a].target | 0x80));
+        if (scene_.attack_data[a].effect != 0xFFFF) attack.animation.effect = scene_.attack_data[a].effect;
         attack.damage.formula = scene_.attack_data[a].damage >> 4;
         attack.damage.modifier = scene_.attack_data[a].damage & 15;
         attack.damage.power = scene_.attack_data[a].power;
@@ -490,16 +473,10 @@ void BattleSceneFile::Read(File file){
         }
         int status_chance_raw = 63 - (scene_.attack_data[a].status_change ^ 63); // Out of 63;
         int status_mode;
-        if (
-          scene_.attack_data[a].status_change == (scene_.attack_data[a].status_change | 0x00000080)
-        ){
+        if (scene_.attack_data[a].status_change == (scene_.attack_data[a].status_change | 0x00000080))
             status_mode = GameData::STATUS_EFFECT_MODE::TOGGLE;
-        }
-        else if (
-          scene_.attack_data[a].status_change == (scene_.attack_data[a].status_change | 0x00000040)
-        ){
+        else if (scene_.attack_data[a].status_change == (scene_.attack_data[a].status_change | 0x00000040))
             status_mode = GameData::STATUS_EFFECT_MODE::TOGGLE;
-        }
         else status_mode = GameData::STATUS_EFFECT_MODE::TOGGLE;
         if (scene_.attack_data[a].status != 0xFFFFFFFF){
             float status_chance = status_chance_raw / 63.0f;
@@ -730,10 +707,8 @@ void BattleSceneFile::Read(File file){
         }
         if (scene_.attack_data[a].element >= 0 && scene_.attack_data[a].element <= 0x0F)
             attack.elements.push_back(scene_.attack_data[a].element);
-        attack.damage.damage_mp
-          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0001));
-        attack.damage.affected_by_darkness
-          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0004));
+        attack.damage.damage_mp = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0001));
+        attack.damage.affected_by_darkness = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0004));
         if ((scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0010))){
             if (attack.damage.damage_mp) attack.damage.drain_mp = true;
             else attack.damage.drain_hp = true;
@@ -744,14 +719,10 @@ void BattleSceneFile::Read(File file){
         }
         attack.damage.ignore_status_defense
           = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0080));
-        attack.target.only_for_dead
-          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0100));
-        attack.reflectable
-          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0200));
-        attack.target.no_re_target
-          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0800));
-        attack.damage.critical
-          = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x2000));
+        attack.target.only_for_dead = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0100));
+        attack.reflectable = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0200));
+        attack.target.no_re_target = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x0800));
+        attack.damage.critical = (scene_.attack_data[a].special != (scene_.attack_data[a].special | 0x2000));
 
         // TODO: Parse scene_.attack_data[a].additional_effects with
         // scene_.attack_data[a].additional_effects_mod for special attack modifiers (23 total).
@@ -764,8 +735,7 @@ void BattleSceneFile::Read(File file){
     // Formations
     for (int f = 0; f < 4; f ++){
         Formation formation;
-        if (scene_.setup[f].next_battle != 0xFFFF)
-            formation.next_formation = scene_.setup[f].next_battle;
+        if (scene_.setup[f].next_battle != 0xFFFF) formation.next_formation = scene_.setup[f].next_battle;
         formation.escape_counter = (scene_.setup[f].escape_counter / 64.0f);
         // TODO: What is flag 0x01? Multilimb enemies?
         formation.escapable = (scene_.setup[f].flags == scene_.setup[f].flags | 0x02);
@@ -796,33 +766,24 @@ void BattleSceneFile::Read(File file){
                 enemy.z = scene_.formation[f][e].z;
                 enemy.row = scene_.formation[f][e].row;
                 enemy.cover[0] = (
-                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x01)
-                  ? '1' : '0'
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x01) ? '1' : '0'
                 );
                 enemy.cover[1] = (
-                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x02)
-                  ? '1' : '0'
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x02) ? '1' : '0'
                 );
                 enemy.cover[2] = (
-                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x04)
-                  ? '1' : '0'
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x04) ? '1' : '0'
                 );
                 enemy.cover[3] = (
-                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x08)
-                  ? '1' : '0'
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x08) ? '1' : '0'
                 );
                 enemy.cover[4] = (
-                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x10)
-                  ? '1' : '0'
+                  scene_.formation[f][e].cover_flags == (scene_.formation[f][e].cover_flags | 0x10) ? '1' : '0'
                 );
-                enemy.visible
-                  = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x01);
-                enemy.direction
-                  = ((scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x02) ? 1 : 0);
-                enemy.targeteable
-                  = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x08);
-                enemy.main_script_active
-                  = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x10);
+                enemy.visible = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x01);
+                enemy.direction = ((scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x02) ? 1 : 0);
+                enemy.targeteable = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x08);
+                enemy.main_script_active = (scene_.formation[f][e].flags == scene_.formation[f][e].flags | 0x10);
                 formation.enemies.push_back(enemy);
             }
         }
@@ -842,9 +803,7 @@ void BattleSceneFile::Read(File file){
             formation.initial_camera_position = scene_.setup[f].camera;
         for (int a = 0; a < 4; a ++){
             if (scene_.setup[f].next_arena_candidates[a] != 999){
-                formation.next_arena_formation_candidates.push_back(
-                  scene_.setup[f].next_arena_candidates[a]
-                );
+                formation.next_arena_formation_candidates.push_back(scene_.setup[f].next_arena_candidates[a]);
                 formation.is_arena_battle = true;
             }
         }

+ 53 - 36
src/installer/data/BattleSceneFile.h

@@ -82,8 +82,8 @@ class BattleSceneFile{
                 /**
                  * ID of the battle that start upon defeating all enemies.
                  *
-                 * If set, a new set of enemies will be loaded when all the current ones are
-                 * defeated, without ending the battle scene.
+                 * If set, a new set of enemies will be loaded when all the
+                 * current ones are defeated, without ending the battle scene.
                  */
                 u16 next_battle;
 
@@ -98,7 +98,8 @@ class BattleSceneFile{
                 u16 unused;
 
                 /**
-                 * In arena battles, candidate enemy formation IDs for next round.
+                 * In arena battles, candidate enemy formation IDs for next
+                 * round.
                  */
                 u16 next_arena_candidates[4];
 
@@ -116,16 +117,17 @@ class BattleSceneFile{
                  * 01 - Preemptive.
                  * 02 - Back attack.
                  * 03 - Side attack.
-                 * 04 - Attacked from both sides (pincer attack, reverse side attack).
+                 * 04 - Attacked from both sides (pincer attack, reverse side).
                  * 05 - Another attack from both sides battle (different maybe?).
                  * 06 - Another side attack.
                  * 07 - A third side attack.
-                 * 08 - Normal battle that locks player in the front row, change command disabled.
+                 * 08 - Normal battle that locks player in the front row, change
+                 * command disabled.
                  */
                 u8 layout;
 
                 /**
-                 * Indexed Pre-Battle Camera position .
+                 * Indexed Pre-Battle Camera position.
                  */
                 u8 camera;
             };
@@ -217,22 +219,26 @@ class BattleSceneFile{
                 /**
                  * Binary "Cover Flags".
                  *
-                 * These flags are used in conjunction with row to determine if a target can be
-                 * selected as the target of a short-range attack. The determination of this is
-                 * worked out in this way: An enemy exists in row 1 and another in row 2. If the
-                 * enemy in row 1 shares a cover flag with the enemy in row 2 then the enemy in row
-                 * 2 cannot be targeted until all enemies in row 1 that share a cover flag with the
-                 * row 2 enemy is defeated. It works like this. Two active enemies exist, A and B.
+                 * These flags are used in conjunction with row to determine if
+                 * a target can be selected as the target of a short-range
+                 * attack. The determination of this is worked out in this way:
+                 * An enemy exists in row 1 and another in row 2. If the enemy
+                 * in row 1 shares a cover flag with the enemy in row 2 then the
+                 * enemy in row 2 cannot be targeted until all enemies in row 1
+                 * that share a cover flag with the row 2 enemy is defeated. It
+                 * works like this. Two active enemies exist, A and B.
                  *
-                 * If ((B's row > A's row) and (B's cover flags AND A's cover flags) > 0) then
-                 * enemy B cannot be targeted by short-range attacks.
+                 * If ((B's row > A's row) and
+                 * (B's cover flags AND A's cover flags) > 0) then enemy B
+                 * cannot be targeted by short-range attacks.
                  */
                 u16 cover_flags;
 
                 /**
                  * Initial condition flags. Only last 5 bits are considered.
                  * 0x0001  Visible.
-                 * 0x0002  Indicates initial direction facing if players get a side attack.
+                 * 0x0002  Indicates initial direction facing if players get a
+                 * side attack.
                  * 0x0004  Unknown.
                  * 0x0008  Targetable.
                  * 0x0010  Main Script Active.
@@ -308,19 +314,22 @@ class BattleSceneFile{
                 u16 attacks[16];
 
                 /**
-                 * Enemy Attacks Camera Movement Id for single and multiple targets (2 bytes each).
+                 * Enemy Attacks Camera Movement Id for single and multiple
+                 * targets (2 bytes each).
                  *
-                 * If set this will overwrite camera movement set in attack itself.
+                 * If set this will overwrite camera movement set in attack
+                 * itself.
                  */
                 u16 attack_camera[16];
 
                 /**
                  * Item drop/steal rates.
                  *
-                 * These are chances to get items listed in @{see item}. 1 byte per item. If the
-                 * rate is lower than 80h, for e.g. 08h - then this is a drop item and has 8/63 [63
-                 * is max] chance for drop. But if rate is higher than 80h, let's say... A0h, then
-                 * this is an item for steal, and chances for successful steal is
+                 * These are chances to get items listed in @{see item}. 1 byte
+                 * per item. If the rate is lower than 80h, for e.g. 08h - then
+                 * this is a drop item and has 8/63 [63 is max] chance for drop.
+                 * But if rate is higher than 80h, let's say... A0h, then this
+                 * is an item for steal, and chances for successful steal is
                  * A0h - 80h = 20h = 32/63.
                  */
                 u8 item_rate[4];
@@ -431,29 +440,32 @@ class BattleSceneFile{
                 /**
                  * MP cost. 2 bytes.
                  *
-                 * Determines the camera movement when the command is executed over a single target.
+                 * Determines the camera movement when the command is executed
+                 * over a single target.
                  */
                 u16 mp;
 
                 /**
                  * Impact sound. 2 bytes.
                  *
-                 * Determines the camera movement when the command is executed over multiple targets.
+                 * Determines the camera movement when the command is executed
+                 * over multiple targets.
                  */
                 u16 sound;
 
                 /**
                  * Camera movement ID for single target. 2 bytes.
                  *
-                 * Determines the camera movement when the command is executed over a single target.
+                 * Determines the camera movement when the command is executed
+                 * over a single target.
                  */
                 u16 camera_single;
 
                 /**
                  * Camera movement ID for multiple target. 2 bytes.
                  *
-                 * Determines the camera movement when the command is executed over multiple
-                 * targets.
+                 * Determines the camera movement when the command is executed
+                 * over multiple targets.
                  */
                 u16 camera_multiple;
 
@@ -472,11 +484,13 @@ class BattleSceneFile{
                 /**
                  * Damage formula. 1 byte.
                  *
-                 * This byte is divided into two nybbles (four bits). Upper nybble determines what
-                 * considerations are made in calculating damage such as physical/magical, allowing
-                 * criticals, how to calculate accuracy, etc. There are also three sets of known
-                 * formulae that are paired with the upper nybble values. These are selected in the
-                 * Lower Nybble and determine how to calculate pre-defense damage
+                 * This byte is divided into two nybbles (four bits). Upper
+                 * nybble determines what considerations are made in calculating
+                 * damage such as physical/magical, allowing criticals, how to
+                 * calculate accuracy, etc. There are also three sets of known
+                 * formulae that are paired with the upper nybble values. These
+                 * are selected in the Lower Nybble and determine how to
+                 * calculate pre-defense damage
                  */
                 u8 damage;
 
@@ -514,11 +528,14 @@ class BattleSceneFile{
                 u8 additional_effects_mod;
 
                 /**
-                 * Information about what status can be inflicted or cured. 4 bytes.
+                 * Information about what status can be inflicted or cured. 4
+                 * bytes.
                  *
-                 * The first six bits are the chance of inflicting/curing (out of 63). If the
-                 * seventh bit is set, the status is cured instead of inflicted. If the eighth bit
-                 * is set, then the status is toggled (cured if present, inflicted if not present).
+                 * The first six bits are the chance of inflicting/curing (out
+                 * of 63). If the seventh bit is set, the status is cured
+                 * instead of inflicted. If the eighth bit is set, then the
+                 * status is toggled (cured if present, inflicted if not
+                 * present).
                  */
                 u32 status;
 
@@ -589,7 +606,7 @@ class BattleSceneFile{
          * Retrieves a location name from it's ID.
          *
          * @param[in] id Location ID.
-         * @return The location name, or "UNKNOWN Location" if the ID is invalid.
+         * @return The location name, or "UNKNOWN Location" for an invalid ID.
          */
         static std::string LocationToString(const int id);
 

+ 3 - 9
src/installer/data/DaFile.cpp

@@ -67,15 +67,9 @@ std::vector<std::string> DaFile::GenerateAFiles(std::string model_id, std::strin
             a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].offset.f_z), 4);
             // Bones.
             for (int b = 1; b < animations_[anim].frames[f].bones.size(); b ++){
-                a.write(
-                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_x), 4
-                );
-                a.write(
-                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_y), 4
-                );
-                a.write(
-                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_z), 4
-                );
+                a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_x), 4);
+                a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_y), 4);
+                a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_z), 4);
             }
         }
         a.close();

+ 43 - 32
src/installer/data/DaFile.h

@@ -22,8 +22,9 @@
 /**
  * Represents a compiled animations file.
  *
- * Da files are bundled into battle.lgp. They contain information about a skeleton animation. They
- * are heavily compressed, and involve a lot of bit reading to decompile.
+ * Da files are bundled into battle.lgp. They contain information about a
+ * skeleton animation. They are heavily compressed, and involve a lot of bit
+ * reading to decompile.
  */
 class DaFile{
 
@@ -34,26 +35,29 @@ class DaFile{
          *
          * Reads the file.
          *
-         * @param[in, out] file The file to read from. The file contents will not be altered, but
-         * it's offset will be changed while reading it.
+         * @param[in, out] file The file to read from. The file contents will
+         * not be altered, but it's offset will be changed while reading it.
          */
         DaFile(File file);
 
         /**
          * Generates .a files from the da file.
          *
-         * A file will be generated for each animation in the da file. See {@see VGearsAFile.h} for
-         * more info about .a files. Files will be created in the specified path, and will be named
-         * like the model id, followed by an underscore, followed a two-digit animation index
-         * (starting from 0) followed by the ".a extension". For example, if path is
-         * "/home/user/.v-gears", the model id is "ac", and the da file contains three animations,
-         * the following files will be generated: "/home/user/.v-gears/ac_00.a",
-         * "/home/user/.v-gears/ac_01.a" and "/home/user/.v-gears/ac_02.a".
+         * A file will be generated for each animation in the da file. See
+         * {@see VGearsAFile.h} for  more info about .a files. Files will be
+         * created in the specified path, and will be named like the model id,
+         * followed by an underscore, followed a two-digit animation index
+         * (starting from 0) followed by the ".a extension". For example, if
+         * path is "/home/user/.v-gears", the model id is "ac", and the da file
+         * contains three animations, the following files will be generated:
+         * "/home/user/.v-gears/ac_00.a", "/home/user/.v-gears/ac_01.a" and
+         * "/home/user/.v-gears/ac_02.a".
          *
-         * @param[in] model_id ID of the model the animation belongs to, usually a two letter
-         * code. Used to generate the file name.
+         * @param[in] model_id ID of the model the animation belongs to, usually
+         * a two letter code. Used to generate the file name.
          * @param[in] path Path to the directory where the files will be saved.
-         * @return A list of the generated files. They include the path and the extension.
+         * @return A list of the generated files. They include the path and the
+         * extension.
          */
         std::vector<std::string> GenerateAFiles(std::string model_id, std::string path);
 
@@ -67,8 +71,8 @@ class DaFile{
         /**
          * A three-coordinate value.
          *
-         * Can be used to store bone rotations, offsets, translations... Each coordinate has short,
-         * integer and float formats.
+         * Can be used to store bone rotations, offsets, translations... Each
+         * coordinate has short, integer and float formats.
          */
         struct TriValue{
 
@@ -188,11 +192,13 @@ class DaFile{
         /**
          * Reads an animation from the *da file.
          *
-         * Requires {@see bit_offset_} to indicate the bit at which the animation begins.
+         * Requires {@see bit_offset_} to indicate the bit at which the
+         * animation begins.
          *
-         * If the read animation is invalid (i.e. with a size less than 11 bytes), the
-         * {@see Animation.frame_count} will be 0 and the rest of the data in the animation is not
-         * to be trusted, and the animation should not be used.
+         * If the read animation is invalid (i.e. with a size less than 11
+         * bytes), the {@see Animation.frame_count} will be 0 and the rest of
+         * the data in the animation is not to be trusted, and the animation
+         * should not be used.
          *
          * @return The animation read.
          */
@@ -201,11 +207,12 @@ class DaFile{
         /**
          * Reads the first, uncompressed frame.
          *
-         * Requires {@see bit_offset_} to indicate the bit at which the frame begins.
+         * Requires {@see bit_offset_} to indicate the bit at which the frame
+         * begins.
          *
          * @param[in] bones The number of bones involved in the animation.
-         * @param[in] key Key for decoding the animation (fifth byte in the animation second
-         * header).
+         * @param[in] key Key for decoding the animation (fifth byte in the
+         * animation second header).
          * @return The first frame.
          */
         Frame ReadFirstFrame(u32 bones, u8 key);
@@ -213,11 +220,12 @@ class DaFile{
         /**
          * Reads a compressed frame.
          *
-         * Requires {@see bit_offset_} to indicate the bit at which the frame begins.
+         * Requires {@see bit_offset_} to indicate the bit at which the frame
+         * begins.
          *
          * @param[in] bones The number of bones involved in the animation.
-         * @param[in] key Key for decoding the animation (fifth byte in the animation second
-         * header).
+         * @param[in] key Key for decoding the animation (fifth byte in the
+         * animation second header).
          * @param[in] prev Previous frame.
          * @return The first frame.
          */
@@ -226,10 +234,11 @@ class DaFile{
         /**
          * Retrieves a positional offset value of dynamic size from the file.
          *
-         * Requires {@see bit_offset_} to indicate the bit at which the frame begins. The first
-         * read bit will determine the length of the read data. If it's 1, 7 more bits of data will
-         * be read and returned. It it isn't, 16 more bits of data will be read and returned. In
-         * total, either 17 or 8 bits of data will be read.
+         * Requires {@see bit_offset_} to indicate the bit at which the frame
+         * begins. The first read bit will determine the length of the read
+         * data. If it's 1, 7 more bits of data will be read and returned. It it
+         * isn't, 16 more bits of data will be read and returned. In total,
+         * either 17 or 8 bits of data will be read.
          *
          * @return The read value.
          */
@@ -258,9 +267,11 @@ class DaFile{
         /**
          * Decodes a delta rotation read from the stream.
          *
-         * Requires {@see bit_offset_} to indicate the bit at which the delta begins
+         * Requires {@see bit_offset_} to indicate the bit at which the delta
+         * begins
          *
-         * @param lowered_precision_bits How many bits to sift the read value. The animation key.
+         * @param lowered_precision_bits How many bits to sift the read value.
+         * The animation key.
          * @return The decoded value.
          */
         int GetCompressedDeltaFromStream(int lowered_precision_bits);

+ 4 - 4
src/installer/data/Enemy.h

@@ -90,8 +90,8 @@ struct Enemy{
         /**
          * Immunity rate.
          *
-         * 0 or lower means no immunity, 1 or higher completely immune. Values between 0 and 1
-         * indicate resistance.
+         * 0 or lower means no immunity, 1 or higher completely immune. Values
+         * between 0 and 1 indicate resistance.
          */
         float rate;
     };
@@ -199,8 +199,8 @@ struct Enemy{
     /**
      * List of attacks that can be used while manipulated.
      *
-     * The first one is also the one used in berserkr. If empty, it means the enemy can't be
-     * manipulated or berserkd.
+     * The first one is also the one used in berserkr. If empty, it means the
+     * enemy can't be manipulated or berserkd.
      */
     std::vector<u16> manipulate_attacks;
 

+ 52 - 51
src/installer/data/FF7Data.h

@@ -180,8 +180,8 @@ class FF7Data{
          * Retrieves an enemy model ID from an enemy ID.
          *
          * @param[in] enemy_id Numeric enemy ID.
-         * @return Enemy alphanumeric ID. Two-lowercase letters. If an invalid ID is provided, an
-         * empty string will be returned.
+         * @return Enemy alphanumeric ID. Two-lowercase letters. If an invalid
+         * ID is provided, an empty string will be returned.
          */
         static std::string GetEnemyModelId(const unsigned int enemy_id){
             if (enemy_id > 25 * 26) return "";
@@ -196,24 +196,28 @@ class FF7Data{
         /**
          * Retrieves information about a battle model from it's name.
          *
-         * Can retrieve information for party character, enemy and scenario models, but not for
-         * spells models.
+         * Can retrieve information for party character, enemy and scenario
+         * models, but not for spells models.
          *
-         * The return structure has the following properties ({@see BattleModelInfo}):
+         * The return structure has the following properties
+         * ({@see BattleModelInfo}):
          *
-         * - numeric_id: A numeric ID assigned to the model. For enemies, it's a unique enemy ID.
-         * For scenes, it's a unique numeric id. For playable characters, it is the id of the
-         * character the model references, but since  a character may have many models, it's not
-         * guaranteed to be unique. There is a special case where it is -1: for the playable frog,
-         * which is related to all characters.
+         * - numeric_id: A numeric ID assigned to the model. For enemies, it's a
+         * unique enemy ID. For scenes, it's a unique numeric id. For playable
+         * characters, it is the id of the character the model references, but
+         * since a character may have many models, it's not guaranteed to be
+         * unique. There is a special case where it is -1: for the playable
+         * frog, which is related to all characters.
          *
-         * - alphanumeric_id: A unique id assigned to the model. Two lowercase letters.
+         * - alphanumeric_id: A unique id assigned to the model. Two lowercase
+         * letters.
          *
-         * - name: A descriptive name for the model. For debugging purposes only, it's not intended
-         * to ever be displayed, and it's not suitable to use in filenames.
+         * - name: A descriptive name for the model. For debugging purposes
+         * only, it's not intended to ever be displayed, and it's not suitable
+         * to use in filenames.
          *
-         * - name_normal: Simplified name, suitable to be used on filenames. Not garanteed to be
-         * unique.
+         * - name_normal: Simplified name, suitable to be used on filenames.
+         * Not garanteed to be unique.
          *
          * - is_enemy: Indicates if the models is a battle enemy.
          *
@@ -221,18 +225,18 @@ class FF7Data{
          *
          * - is_scene: Indicates if the models is a battle scenario.
          *
-         * @param[in] model_id ID of the model. It can be two letters, or two letters preceded by
-         * "btl_". Case insensitive. If the id contains more than four letters (not counting the
-         * optional "btl_" prefix), only the first two sill be considered.
-         * @return A battle model info structure. If an invvalid model ID is provided, or if the
-         * model doesn't exist, an empty structure will be returned.
+         * @param[in] model_id ID of the model. It can be two letters, or two
+         * letters preceded by "btl_". Case insensitive. If the id contains more
+         * than four letters (not counting the optional "btl_" prefix), only the
+         * first two sill be considered.
+         * @return A battle model info structure. If an invvalid model ID is
+         * provided, or if the model doesn't exist, an empty structure will be
+         * returned.
          */
         static BattleModelInfo GetBattleModelInfo(std::string model_id){
             BattleModelInfo info;
             std::string alphanumeric_id = model_id;
-            std::transform(
-              alphanumeric_id.begin(), alphanumeric_id.end(), alphanumeric_id.begin(), ::tolower
-            );
+            std::transform(alphanumeric_id.begin(), alphanumeric_id.end(), alphanumeric_id.begin(), ::tolower);
             if (alphanumeric_id.size() < 2) return info; // Empty
             if (alphanumeric_id.size() > 4 && alphanumeric_id.substr(0, 4) == "btl_")
                 alphanumeric_id = alphanumeric_id.substr(4, alphanumeric_id.size() - 4);
@@ -3019,10 +3023,7 @@ class FF7Data{
                 info.is_player = true;
             }
             else if (
-              "sb" == alphanumeric_id
-              || "sc" == alphanumeric_id
-              || "sd" == alphanumeric_id
-              || "se" == alphanumeric_id
+              "sb" == alphanumeric_id || "sc" == alphanumeric_id || "sd" == alphanumeric_id || "se" == alphanumeric_id
             ){
                 info.numeric_id = CHAR_ID::BARRET;
                 info.name = "Barret";
@@ -3065,11 +3066,7 @@ class FF7Data{
                 info.name_normal = "cid";
                 info.is_player = true;
             }
-            else if (
-              "sf" == alphanumeric_id
-              || "sg" == alphanumeric_id
-              || "sh" == alphanumeric_id
-            ){
+            else if ("sf" == alphanumeric_id || "sg" == alphanumeric_id || "sh" == alphanumeric_id){
                 info.numeric_id = CHAR_ID::VINCENT;
                 info.name = "Vincent";
                 info.name_normal = "vincent";
@@ -3494,34 +3491,38 @@ class FF7Data{
          * Retrieves information about a spell model from it's name.
          *
          *
-         * The return structure has the following properties ({@see BattleModelInfo}):
+         * The return structure has the following properties
+         * ({@see BattleModelInfo}):
          *
-         * - numeric_id: A numeric ID assigned to the model. For enemies, it's a unique enemy ID.
-         * For scenes, it's a unique numeric id. For playable characters, it is the id of the
-         * character the model references, but since  a character may have many models, it's not
-         * guaranteed to be unique. There is a special case where it is -1: for the playable frog,
-         * which is related to all characters.
+         * - numeric_id: A numeric ID assigned to the model. For enemies,
+         * it's a unique enemy ID. For scenes, it's a unique numeric id. For
+         * playable characters, it is the id of the character the model
+         * references, but since  a character may have many models, it's not
+         * guaranteed to be unique. There is a special case where it is -1: for
+         * the playable frog, which is related to all characters.
          *
-         * - alphanumeric_id: A unique id assigned to the model. Two lowercase letters.
+         * - alphanumeric_id: A unique id assigned to the model. Two lowercase
+         * letters.
          *
-         * - name: A descriptive name for the model. For debugging purposes only, it's not intended
-         * to ever be displayed, and it's not suitable to use in filenames.
+         * - name: A descriptive name for the model. For debugging purposes
+         * only, it's not intended to ever be displayed, and it's not suitable
+         * to use in filenames.
          *
-         * - name_normal: Simplified name, suitable to be used on filenames. Not garanteed to be
-         * unique.
+         * - name_normal: Simplified name, suitable to be used on filenames. Not
+         * guaranteed to be unique.
          *
-         * @param[in] model_id ID of the model. It can be two letters, or two letters preceded by
-         * "btl_". Case insensitive. If the id contains more than four letters (not counting the
-         * optional "btl_" prefix), only the first two sill be considered.
-         * @return A spell model info structure. If an invalid model ID is provided, or if the
-         * model doesn't exist, an empty structure will be returned.
+         * @param[in] model_id ID of the model. It can be two letters, or two
+         * letters preceded by "btl_". Case insensitive. If the id contains more
+         * than four letters (not counting the optional "btl_" prefix), only the
+         * first two sill be considered.
+         * @return A spell model info structure. If an invalid model ID is
+         * provided, or if the model doesn't exist, an empty structure will be
+         * returned.
          */
         static SpellModelInfo GetSpellModelInfo(std::string model_id){
             SpellModelInfo info;
             std::string alphanumeric_id = model_id;
-            std::transform(
-              alphanumeric_id.begin(), alphanumeric_id.end(), alphanumeric_id.begin(), ::tolower
-            );
+            std::transform(alphanumeric_id.begin(), alphanumeric_id.end(), alphanumeric_id.begin(), ::tolower);
             info.alphanumeric_id = alphanumeric_id;
             info.name = alphanumeric_id;
             info.name_normal = alphanumeric_id;

+ 45 - 35
src/installer/data/Formation.h

@@ -38,37 +38,40 @@ struct Formation{
         /**
          * Preemptive attack.
          *
-         * Enemies are on their back, they bars are empty. When attacked, or after a certain time,
-         * they turn and their bars start to fill.
+         * Enemies are on their back, they bars are empty. When attacked, or
+         * after a certain time, they turn and their bars start to fill.
          */
         PREEMPTIVE,
 
         /**
          * Back attack.
          *
-         * Players are on their back, they bars are empty. When attacked, or after a certain time,
-         * they turn and their bars start to fill. Player rows are reversed.
+         * Players are on their back, they bars are empty. When attacked, or
+         * after a certain time, they turn and their bars start to fill.
+         * Player rows are reversed.
          */
         BACK_ATTACK,
 
         /**
          * Side attack.
          *
-         * Players in both sides, enemies in center, facing one or other direction. Change command
-         * disabled.
+         * Players in both sides, enemies in center, facing one or other
+         * direction. Change command disabled.
          */
         SIDE_ATTACK,
 
         /**
-         * Enemies in both sides, player in center, front row, facing one or the other direction.
-         * Change command disabled. Unescapable until one of the sides is cleared.
+         * Enemies in both sides, player in center, front row, facing one or the
+         * other direction. Change command disabled. Unescapable until one of
+         * the sides is cleared.
          */
         PINCER_ATTACK,
 
         /**
          * Locked position fight.
          *
-         * Like a normal one, but all players in the front row. Change command disabled.
+         * Like a normal one, but all players in the front row. Change command
+         * disabled.
          */
         LOCKED
     };
@@ -106,42 +109,46 @@ struct Formation{
         /**
          * Cover flags.
          *
-         * These flags are used in conjunction with row to determine if a target can be selected
-         * as the target of a short-range attack. The determination of this is worked out in this
-         * way: An enemy exists in row 1 and another in row 2. If the enemy in row 1 shares a cover
-         * flag with the enemy in row 2 then the enemy in row 2 cannot be targeted until all
-         * enemies in row 1 that share a cover flag with the row 2 enemy is defeated. It works like
-         * this. Two active enemies exist, A and B.
+         * These flags are used in conjunction with row to determine if a target
+         * can be selected as the target of a short-range attack. The
+         * determination of this is worked out in this way: An enemy exists in
+         * row 1 and another in row 2. If the enemy in row 1 shares a cover flag
+         * with the enemy in row 2 then the enemy in row 2 cannot be targeted
+         * until all enemies in row 1 that share a cover flag with the row 2
+         * enemy are defeated. It works like this. Two active enemies exist, A
+         * and B.
          *
-         * If ((B's row > A's row) and (B's cover flags AND A's cover flags) > 0)
+         * If ((B's row > A's row) and (B's flags AND A's flags) > 0)
          * then enemy B cannot be targeted by short-range attacks.
          *
          * For any enemies A and B.
          *
          * Example:
-         * Consider the Battery Cap x6 battle in the forest between Nibelheim and Rocket Town.
-         * Their cover flags (in binary) are:
+         * Consider the Battery Cap x6 battle in the forest between Nibelheim
+         * and Rocket Town. Their cover flags (in binary) are:
          *
          * Row 1:       00100
          * Row 2:    00110 01100
          * Row 3: 00011 00100 11000
          *
-         * The battery caps in row 2 cannot be targeted by a short-range attack until the one in
-         * row 1 has been defeated because they share the 0x4 cover flag. Once row 1 has been
-         * cleared:
+         * The battery caps in row 2 cannot be targeted by a short-range attack
+         * until the one in row 1 has been defeated because they share the 0x4
+         * cover flag. Once row 1 has been cleared:
          *
          * Row 2:    00110 01100
          * Row 3: 00011 00100 11000
          *
-         * The battery cap on left in row 2 covers the left two in row 3 because it shares flag 0x4
-         * with the one in the middle and flag 0x2 with the one on the far left. As long as it is
-         * active these in row 3 cannot be targeted. Similarly, the battery cap on the right in row
-         * 2 shares the 0x4 flag with the middle of row 3 and the 0x8 flag with the far right of
-         * row 3 so these cannot be targeted until the right side of row 2 is defeated. It is also
-         * necessary to note that because row 1 does not share any flags with the extreme right and
-         * left of row 3, they can be targeted if the corresponding enemy in row 2 is defeated even
-         * if the row 1 enemy is still active. Also of note is that enemies in the same row that
-         * share cover flags are not considered.
+         * The battery cap on left in row 2 covers the left two in row 3 because
+         * it shares flag 0x4 with the one in the middle and flag 0x2 with the
+         * one on the far left. As long as it is active these in row 3 cannot be
+         * targeted. Similarly, the battery cap on the right in row 2 shares the
+         * 0x4 flag with the middle of row 3 and the 0x8 flag with the far right
+         * of row 3 so these cannot be targeted until the right side of row 2 is
+         * defeated. It is also necessary to note that because row 1 does not
+         * share any flags with the extreme right and left of row 3, they can be
+         * targeted if the corresponding enemy in row 2 is defeated even if the
+         * row 1 enemy is still active. Also of note is that enemies in the same
+         * row that share cover flags are not considered.
          */
         char cover[5];
 
@@ -227,7 +234,8 @@ struct Formation{
     unsigned int id;
 
     /**
-     * When all enemies are defeated, begin battle with this ID without ending battle scene.
+     * When all enemies are defeated, begin battle with this ID without ending
+     * battle scene.
      */
     int next_formation;
 
@@ -247,7 +255,8 @@ struct Formation{
     bool skip_victory_pose;
 
     /**
-     * If true, spoils (item, ap, money...) will not be obtained after the battle.
+     * If true, spoils (item, ap, money...) will not be obtained after the
+     * battle.
      *
      * The spoil screens will not be displayed.
      */
@@ -271,8 +280,9 @@ struct Formation{
     /**
      * Location ID.
      *
-     * Must be set, except for formations referenced by other formation's {@see next_formation} or
-     * {@see next_arena_formation_candidates}. In those cases, it can be -1.
+     * Must be set, except for formations referenced by other formation's
+     * {@see next_formation} or {@see next_arena_formation_candidates}. In those
+     * cases, it can be -1.
      */
     int location;
 
@@ -299,7 +309,7 @@ struct Formation{
     bool is_arena_battle;
 
     /**
-     * FOr arena battles, candidates fro the next formation.
+     * For arena battles, candidates for the next formation.
      */
     std::vector<int> next_arena_formation_candidates;
 

+ 8 - 14
src/installer/data/WorldMapWalkmesh.cpp

@@ -62,8 +62,7 @@ void WorldMapWalkmesh::Triangle::setCA(const int id, const bool warp){
     ca_.warp = warp;
 }
 
-WorldMapWalkmesh::WorldMapWalkmesh(const unsigned int id, const bool warp):
-  id_(id), warp_(warp), calculated_(false){
+WorldMapWalkmesh::WorldMapWalkmesh(const unsigned int id, const bool warp): id_(id), warp_(warp), calculated_(false){
     triangles_.clear();
 }
 
@@ -73,9 +72,10 @@ void WorldMapWalkmesh::addTriangle(
 ){
     calculated_ = false;
     Triangle tri(triangles_.size(), a, b, c, walkability, event);
-    if (id_ == 0 && triangles_.size() < 50){
-        std::cout << "Adding triangle " << tri.getId() << " W: "<< walkability << " - " << tri.getWalkability() << "     E: " << event << " - " << tri.getEvent() << std::endl;
-    }
+    //if (id_ == 0 && triangles_.size() < 50){
+    //    std::cout << "Adding triangle " << tri.getId() << " W: "<< walkability << " - " << tri.getWalkability()
+    //      << "     E: " << event << " - " << tri.getEvent() << std::endl;
+    //}
     triangles_.push_back(tri);
 }
 
@@ -106,17 +106,11 @@ void WorldMapWalkmesh::generate(std::string path){
         );
         // WARN: This is never actually calculated. See the comments on calculate()
         if (tri.getAB().id > -1)
-            xml_tri->SetAttribute(
-              "a_b", std::to_string(tri.getAB().id) + (tri.getAB().warp ? " warp" : "")
-            );
+            xml_tri->SetAttribute("a_b", std::to_string(tri.getAB().id) + (tri.getAB().warp ? " warp" : ""));
         if (tri.getBC().id > -1)
-            xml_tri->SetAttribute(
-              "b_c", std::to_string(tri.getBC().id) + (tri.getBC().warp ? " warp" : "")
-            );
+            xml_tri->SetAttribute("b_c", std::to_string(tri.getBC().id) + (tri.getBC().warp ? " warp" : ""));
         if (tri.getCA().id > -1)
-            xml_tri->SetAttribute(
-              "c_a", std::to_string(tri.getCA().id) + (tri.getCA().warp ? " warp" : "")
-            );
+            xml_tri->SetAttribute("c_a", std::to_string(tri.getCA().id) + (tri.getCA().warp ? " warp" : ""));
         xml_tri->SetAttribute("walkability", std::to_string(tri.getWalkability()));
         if (tri.getEvent() > -1) xml_tri->SetAttribute("event", std::to_string(tri.getEvent()));
         xml->LinkEndChild(xml_tri.release());

+ 46 - 34
src/installer/data/WorldMapWalkmesh.h

@@ -33,7 +33,8 @@ class WorldMapWalkmesh{
             public:
 
                 /**
-                 * Triangle reached when exiting this triangle from one of its borders.
+                 * Triangle reached when exiting this triangle from one of its
+                 * borders.
                  */
                 struct Target{
 
@@ -45,8 +46,8 @@ class WorldMapWalkmesh{
                     int id;
 
                     /**
-                     * False if the target triangle is truly adjacent, true if it warps to other
-                     * side of the world.
+                     * False if the target triangle is truly adjacent, true if
+                     * it warps to other side of the world.
                      */
                     bool warp;
                 };
@@ -59,7 +60,8 @@ class WorldMapWalkmesh{
                  * @param[in] b X, Y, Z coordinates of the second vertex.
                  * @param[in] c X, Y, Z coordinates of the third vertex.
                  * @param[in] walkability Walkability mode ID.
-                 * @param[in] event Event triggered upon entering the triangle. -1 for no event.
+                 * @param[in] event Event triggered upon entering the triangle,
+                 * or -1 for no event.
                  */
                 Triangle(
                   const unsigned int id, Ogre::Vector3 a, const Ogre::Vector3 b,
@@ -95,24 +97,24 @@ class WorldMapWalkmesh{
                 Ogre::Vector3 getC() const;
 
                 /**
-                 * Retrieves the triangle reached when exiting the triangle by the side between the
-                 * vertices A and B.
+                 * Retrieves the triangle reached when exiting the triangle by
+                 * the side between the vertices A and B.
                  *
                  * @return Target triangle.
                  */
                 Target getAB() const;
 
                 /**
-                 * Retrieves the triangle reached when exiting the triangle by the side between the
-                 * vertices B and C.
+                 * Retrieves the triangle reached when exiting the triangle by
+                 * the side between the vertices B and C.
                  *
                  * @return Target triangle.
                  */
                 Target getBC() const;
 
                 /**
-                 * Retrieves the triangle reached when exiting the triangle by the side between the
-                 * vertices C and A.
+                 * Retrieves the triangle reached when exiting the triangle by
+                 * the side between the vertices C and A.
                  *
                  * @return Target triangle.
                  */
@@ -128,36 +130,41 @@ class WorldMapWalkmesh{
                 /**
                  * Retrieves the event triggered upon entering the triangle.
                  *
-                 * @return The ID of the event triggered on entering, or -1 if none.
+                 * @return The ID of the event triggered on entering, or -1 if
+                 * none.
                  */
                 int getEvent() const;
 
                 /**
-                 * Sets the triangle reached when exiting the triangle by the side between the
-                 * vertices A and B.
+                 * Sets the triangle reached when exiting the triangle by the
+                 * side between the vertices A and B.
                  *
-                 * @param[in] id Target triangle ID. -1 to indicate no way out by the edge.
-                 * @param[in] warp True to indicate world warping (ie, if walking into an edge of
-                 * the world gets you at the other end).
+                 * @param[in] id Target triangle ID. -1 to indicate no way out
+                 * by the edge.
+                 * @param[in] warp True to indicate world warping (ie, if
+                 * walking into an edge of the world gets you at the other end).
                  */
                 void setAB(const int id, const bool warp);
 
                 /**
-                 * Sets the triangle reached when exiting the triangle by the side between the
-                 * vertices B and C.
+                 * Sets the triangle reached when exiting the triangle by the
+                 * side between the vertices B and C.
                  *
-                 * @param[in] id Target triangle ID. -1 to indicate no way out by the edge.
-                 * @param[in] warp True to indicate world warping (ie, if walking into an edge of
-                 * the world gets you at the other end).
+                 * @param[in] id Target triangle ID. -1 to indicate no way out
+                 * by the edge.
+                 * @param[in] warp True to indicate world warping (ie, if
+                 * walking into an edge of the world gets you at the other end).
                  */
                 void setBC(const int id, const bool warp);
 
                 /**
-                 * Sets the triangle reached when exiting the triangle by the side between the
-                 * vertices C and A.
+                 * Sets the triangle reached when exiting the triangle by the
+                 * side between the vertices C and A.
                  *
-                 * @param[in] id Target triangle ID. -1 to indicate no way out by the edge.
-                 * @param[in] warp True to indicate world warping (ie, if walking into an edge of
+                 * @param[in] id Target triangle ID. -1 to indicate no way out
+                 * by the edge.
+                 * @param[in] warp True to indicate world warping (ie, if
+                 * walking into an edge of the world gets you at the other end).
                  * the world gets you at the other end).
                  */
                 void setCA(const int id, const bool warp);
@@ -185,17 +192,20 @@ class WorldMapWalkmesh{
                 Ogre::Vector3 c_;
 
                 /**
-                 * Target reached when exiting by the side formed by {@see a_} and {@see b_}.
+                 * Target reached when exiting by the side formed by {@see a_}
+                 * and {@see b_}.
                  */
                 Target ab_;
 
                 /**
-                 * Target reached when exiting by the side formed by {@see b_} and {@see c_}.
+                 * Target reached when exiting by the side formed by {@see b_}
+                 * and {@see c_}.
                  */
                 Target bc_;
 
                 /**
-                 * Target reached when exiting by the side formed by {@see c_} and {@see a_}.
+                 * Target reached when exiting by the side formed by {@see c_}
+                 * and {@see a_}.
                  */
                 Target ca_;
 
@@ -214,8 +224,8 @@ class WorldMapWalkmesh{
          * Constructor.
          *
          * @param[in] id World map ID.
-         * @param[in] warp Indicates if the world map is warpable (ie, if walking into an edge of
-         * the world gets you at the other end).
+         * @param[in] warp Indicates if the world map is warpable (ie, if
+         * walking into an edge of the world gets you at the other end).
          */
         WorldMapWalkmesh(const unsigned int id, const bool warp);
 
@@ -226,7 +236,8 @@ class WorldMapWalkmesh{
          * @param[in] b X, Y, Z coordinates of the second vertex.
          * @param[in] c X, Y, Z coordinates of the third vertex.
          * @param[in] walkability Walkability mode ID.
-         * @param[in] event Event triggered upon entering the triangle. -1 for no event.
+         * @param[in] event Event triggered upon entering the triangle. -1 for
+         * no event.
          */
         void addTriangle(
           const Ogre::Vector3 a, const Ogre::Vector3 b, const Ogre::Vector3 c,
@@ -236,7 +247,8 @@ class WorldMapWalkmesh{
         /**
          * Generates a world map walkmesh file.
          *
-         * @param[in] path Path where the file will be saved (directory must exist).
+         * @param[in] path Path where the file will be saved (directory must
+         * exist).
          */
         void generate(const std::string path);
 
@@ -258,8 +270,8 @@ class WorldMapWalkmesh{
         std::vector<Triangle> triangles_;
 
         /**
-         * Indicates if the relations between triangles has been calculated since the last triangle
-         * was added.
+         * Indicates if the relations between triangles has been calculated
+         * since the last triangle was added.
          */
         bool calculated_;
 

+ 14 - 39
src/installer/decompiler/CodeGenerator.cpp

@@ -25,9 +25,8 @@
 #define GET(vertex)    (boost::get(boost::vertex_name, graph_, vertex))
 #define GET_EDGE(edge) (boost::get(boost::edge_attribute, graph_, edge))
 
-CodeGenerator::CodeGenerator(
-  Engine *engine, std::ostream &output, ARGUMENT_ORDER bin_order, ARGUMENT_ORDER call_order
-): output_(output), bin_order_(bin_order), call_order_(call_order)
+CodeGenerator::CodeGenerator(Engine *engine, std::ostream &output, ARGUMENT_ORDER bin_order, ARGUMENT_ORDER call_order):
+  output_(output), bin_order_(bin_order), call_order_(call_order)
 {
     engine_ = engine;
     indent_level_ = 0;
@@ -53,9 +52,7 @@ void CodeGenerator::WriteFunctionCall(
             case 'b': func_call += params[param_index]->GetUnsigned() ? "true" : "false"; break;
             case 'n': func_call += std::to_string(params[param_index]->GetUnsigned()); break;
             case 'f':
-                func_call += std::to_string(
-                  static_cast<float>(params[param_index]->GetUnsigned()) / 30.0f
-                );
+                func_call += std::to_string(static_cast<float>(params[param_index]->GetUnsigned()) / 30.0f);
                 break;
             case '_': skip_argument = true; break;// Ignore param
             default: throw std::runtime_error("Unknown param type");
@@ -131,9 +128,7 @@ void CodeGenerator::ProcessUncondJumpInst(Function& function, InstVec& insts, co
                 bool print_jump = true;
                 OutEdgeRange jump_targets = boost::out_edges(cur_vertex_, graph_);
                 for (
-                  OutEdgeIterator target = jump_targets.first;
-                  target != jump_targets.second && print_jump;
-                  ++ target
+                  OutEdgeIterator target = jump_targets.first; target != jump_targets.second && print_jump; ++ target
                 ){
                     Group* next = cur_group_->next;
                     if (next){
@@ -151,20 +146,16 @@ void CodeGenerator::ProcessUncondJumpInst(Function& function, InstVec& insts, co
                           boost::target(*target, graph_), graph_
                         );
                         for (
-                          OutEdgeIterator target_it = target_range.first;
-                          target_it != target_range.second;
-                          ++ target_it
+                          OutEdgeIterator target_it = target_range.first; target_it != target_range.second; ++ target_it
                         ){
                             // Don't output jump to while loop that has jump to next vertex.
-                            if (boost::target(*target_it, graph_) == next->vertex)
-                                print_jump = false;
+                            if (boost::target(*target_it, graph_) == next->vertex) print_jump = false;
                         }
                         if (print_jump){
                             // Check if this instruction is the last instruction in the function
                             // and its an uncond jump.
                             if (
-                              cur_group_->type == GROUP_TYPE_DO_WHILE
-                              && inst->GetAddress() == function.end_addr
+                              cur_group_->type == GROUP_TYPE_DO_WHILE && inst->GetAddress() == function.end_addr
                               && inst->IsUncondJump()
                             ){
                                 print_jump = false;
@@ -203,12 +194,8 @@ void CodeGenerator::ProcessCondJumpInst(const InstPtr inst){
                 bool coalesce_else = false;
                 for (OutEdgeIterator oe = oer.first; oe != oer.second; ++ oe){
                     GroupPtr oGr = GET(boost::target(*oe, graph_))->prev;
-                    if (
-                      std::find(oGr->end_else.begin(), oGr->end_else.end(), cur_group_.get())
-                      != oGr->end_else.end()
-                    ){
+                    if (std::find(oGr->end_else.begin(), oGr->end_else.end(), cur_group_.get()) != oGr->end_else.end())
                         coalesce_else = true;
-                    }
                 }
                 if (coalesce_else){
                     cur_group_->code.clear();
@@ -221,15 +208,13 @@ void CodeGenerator::ProcessCondJumpInst(const InstPtr inst){
             AddOutputLine(s.str(), cur_group_->coalesced_else, true);
             break;
         case GROUP_TYPE_WHILE:
-            s << target_lang_->WhileHeader(true) << stack_.Pop()->Negate()
-              << target_lang_->WhileHeader(false) << " "
+            s << target_lang_->WhileHeader(true) << stack_.Pop()->Negate() << target_lang_->WhileHeader(false) << " "
               << target_lang_->StartBlock(LuaLanguage::BEGIN_WHILE);
             AddOutputLine(s.str(), false, true);
             break;
         case GROUP_TYPE_DO_WHILE:
             s << target_lang_->EndBlock(LuaLanguage::END_WHILE) <<  " "
-              << target_lang_->WhileHeader(true) << stack_.Pop()
-              << target_lang_->WhileHeader(false);
+              << target_lang_->WhileHeader(true) << stack_.Pop() << target_lang_->WhileHeader(false);
             AddOutputLine(s.str(), true, false);
             break;
         default:
@@ -257,11 +242,7 @@ typedef std::pair<GraphVertex, ValueStack> DFSEntry;
 
 void CodeGenerator::GeneratePass(InstVec& insts, const Graph& graph){
     graph_ = graph;
-    for (
-      FuncMap::iterator fn = engine_->functions.begin();
-      fn != engine_->functions.end();
-      ++ fn
-    ){
+    for (FuncMap::iterator fn = engine_->functions.begin(); fn != engine_->functions.end(); ++ fn){
         while (!stack_.IsEmpty()) stack_.Pop();
         GraphVertex entry_point = fn->second.vertex;
         std::string func_signature = ConstructFuncSignature(fn->second);
@@ -338,15 +319,13 @@ void CodeGenerator::Process(Function& function, InstVec& insts, GraphVertex vert
     for (InEdgeIterator ie = ier.first; ie != ier.second; ++ ie){
         GraphVertex in = boost::source(*ie, graph_);
         GroupPtr in_group = GET(in);
-        if (!boost::get(boost::edge_attribute, graph_, *ie).is_jump || in_group->stack_level == -1)
-            continue;
+        if (!boost::get(boost::edge_attribute, graph_, *ie).is_jump || in_group->stack_level == -1) continue;
         switch (in_group->type){
             case GROUP_TYPE_DO_WHILE:
                 AddOutputLine(target_lang_->DoLoopHeader(), false, true);
                 break;
             case GROUP_TYPE_IF:
-                if (!cur_group_->start_else)
-                    AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_IF), true, false);
+                if (!cur_group_->start_else) AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_IF), true, false);
                 break;
             case GROUP_TYPE_WHILE:
                 AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_WHILE), true, false);
@@ -365,11 +344,7 @@ void CodeGenerator::Process(Function& function, InstVec& insts, GraphVertex vert
         ProcessInst(function, insts, *it);
     } while (it++ != cur_group_->end);
     // Add else end if necessary
-    for (
-      ElseEndIterator else_it = cur_group_->end_else.begin();
-      else_it != cur_group_->end_else.end();
-      ++ else_it
-    ){
+    for (ElseEndIterator else_it = cur_group_->end_else.begin(); else_it != cur_group_->end_else.end(); ++ else_it){
         if (!(*else_it)->coalesced_else)
             AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_IF_ELSE_CHAIN), true, false);
     }

+ 2 - 6
src/installer/decompiler/CodeGenerator.h

@@ -67,9 +67,7 @@ class CodeGenerator {
          * @param[in] bin_order Order of arguments for binary operators.
          * @param[in] call_order Order of arguments for function calls.
          */
-        CodeGenerator(
-          Engine *engine, std::ostream &output, ARGUMENT_ORDER bin_order, ARGUMENT_ORDER call_order
-        );
+        CodeGenerator(Engine *engine, std::ostream &output, ARGUMENT_ORDER bin_order, ARGUMENT_ORDER call_order);
 
         /**
          * Destructor.
@@ -113,9 +111,7 @@ class CodeGenerator {
          * @param[in] indent_after Whether or not to add an indentation level
          * after the line. Defaults to false.
          */
-        virtual void AddOutputLine(
-          std::string line, bool unindent_before = false, bool indent_after = false
-        );
+        virtual void AddOutputLine(std::string line, bool unindent_before = false, bool indent_after = false);
 
         /**
          * Writes a comment line indicating an unimplemented opcode.

+ 38 - 88
src/installer/decompiler/ControlFlow.cpp

@@ -16,6 +16,7 @@
 #include <algorithm>
 #include <iostream>
 #include <set>
+#include <stdexcept>
 #include <boost/format.hpp>
 #include "decompiler/ControlFlow.h"
 
@@ -62,6 +63,7 @@
 #define GET_EDGE(edge) (boost::get(boost::edge_attribute, graph_, edge))
 
 ControlFlow::ControlFlow(InstVec& insts, Engine& engine): insts_(insts),engine_(engine){
+    if (insts_.empty()) return;
 
     // Automatically add a function if we're not supposed to look for more functions
     // and no functions are defined.
@@ -87,13 +89,11 @@ ControlFlow::ControlFlow(InstVec& insts, Engine& engine): insts_(insts),engine_(
         prev = GET(cur);
     }
     // Add regular edges.
-    FuncMap::iterator fn;
     GraphVertex last ={};
     bool add_edge = false;
     prev = NULL;
     for (InstIterator it = insts.begin(); it != insts.end(); ++it){
-        if (engine_.functions.find((*it)->GetAddress()) != engine_.functions.end())
-            add_edge = false;
+        if (engine_.functions.find((*it)->GetAddress()) != engine_.functions.end()) add_edge = false;
         GraphVertex cur = Find(it);
         if (add_edge){
             GraphEdge e = boost::add_edge(last, cur, graph_).first;
@@ -107,7 +107,17 @@ ControlFlow::ControlFlow(InstVec& insts, Engine& engine): insts_(insts),engine_(
     // Add jump edges.
     for (InstIterator it = insts.begin(); it != insts.end(); ++ it){
         if ((*it)->IsJump()){
-            GraphEdge e = boost::add_edge(Find(it), Find((*it)->GetDestAddress()), graph_).first;
+            const uint32 src_addr = (*it)->GetAddress();
+            const uint32 dest_addr = (*it)->GetDestAddress();
+            auto src_it = addr_map_.find(src_addr);
+            auto dest_it = addr_map_.find(dest_addr);
+            if (src_it == addr_map_.end() || dest_it == addr_map_.end()){
+                std::cerr << "Invalid jump edge in control-flow graph: source " << boost::format("0x%08x") % src_addr
+                  << " -> destination " << boost::format("0x%08x") % dest_addr << " (opcode "
+                  << boost::format("0x%02x") % (*it)->GetOpcode() << ")" << std::endl;
+                continue;
+            }
+            GraphEdge e = boost::add_edge(src_it->second, dest_it->second, graph_).first;
             PUT_EDGE(e, true);
         }
     }
@@ -121,10 +131,8 @@ GraphVertex ControlFlow::Find(ConstInstIterator it){return addr_map_[(*it)->GetA
 
 GraphVertex ControlFlow::Find(uint32 address){
     std::map<uint32, GraphVertex>::iterator it = addr_map_.find(address);
-    if (it == addr_map_.end()){
-        std::cerr << "Request for instruction at unknown address "
-          << boost::format("0x%08x") % address << std::endl;
-    }
+    if (it == addr_map_.end())
+        throw std::runtime_error((boost::format("Request for instruction at unknown address 0x%08x") % address).str());
     return it->second;
 }
 
@@ -191,15 +199,9 @@ void ControlFlow::CreateGroups(){
       //&& GET(engine_.functions.begin()->second.GetVertex())->_stackLevel != -1
       //&& GET(engine_.functions.begin()->second.vertex_)->_stackLevel != -1
       && GET(engine_.functions.begin()->second.vertex)->stack_level != -1
-    ){
-        return;
-    }
+    ) return;
 
-    for (
-      FuncMap::iterator fn = engine_.functions.begin();
-      fn != engine_.functions.end();
-      ++ fn
-    ){
+    for (FuncMap::iterator fn = engine_.functions.begin(); fn != engine_.functions.end(); ++ fn){
         SetStackLevel(fn->second.vertex, 0);
     }
     ConstInstIterator cur_inst, next_inst;
@@ -296,9 +298,7 @@ void ControlFlow::DetectShortCircuit(){
             OutEdgeRange range_prev = boost::out_edges(prev, graph_);
             for (OutEdgeIterator it = range_prev.first; it != range_prev.second; ++ it){
                 GraphVertex target = boost::target(*it, graph_);
-                do_merge &= (
-                  std::find(succs.begin(), succs.end(), target) != succs.end() || target == cur
-                );
+                do_merge &= (std::find(succs.begin(), succs.end(), target) != succs.end() || target == cur);
             }
             if (do_merge){
                 gr = gr->prev;
@@ -335,9 +335,7 @@ void ControlFlow::DetectWhile(){
                 if (
                   (*source_gr->start)->GetAddress() > (*gr->start)->GetAddress()
                   && source_gr->type != GROUP_TYPE_DO_WHILE
-                ){
-                    is_while = true;
-                }
+                ) is_while = true;
             }
             if (is_while) gr->type = GROUP_TYPE_WHILE;
         }
@@ -354,8 +352,7 @@ void ControlFlow::DetectDoWhile(){
             for (OutEdgeIterator e = oer.first; e != oer.second; ++e){
                 GroupPtr target_gr = GET(boost::target(*e, graph_));
                 // ...to earlier in code.
-                if ((*target_gr->start)->GetAddress() < (*gr->start)->GetAddress())
-                    gr->type = GROUP_TYPE_DO_WHILE;
+                if ((*target_gr->start)->GetAddress() < (*gr->start)->GetAddress()) gr->type = GROUP_TYPE_DO_WHILE;
             }
         }
     }
@@ -366,11 +363,7 @@ void ControlFlow::DetectBreak(){
     for (VertexIterator v = vertex_range.first; v != vertex_range.second; ++ v){
         GroupPtr gr = GET(*v);
         // Undetermined block with unconditional jump...
-        if (
-          gr->type == GROUP_TYPE_NORMAL
-          && ((*gr->end)->IsUncondJump())
-          && out_degree(*v, graph_) == 1
-        ){
+        if (gr->type == GROUP_TYPE_NORMAL && ((*gr->end)->IsUncondJump()) && out_degree(*v, graph_) == 1){
             OutEdgeIterator oe = boost::out_edges(*v, graph_).first;
             GraphVertex target = boost::target(*oe, graph_);
             GroupPtr target_gr = GET(target);
@@ -397,42 +390,26 @@ void ControlFlow::DetectContinue(){
     for (VertexIterator v = vertex_range.first; v != vertex_range.second; ++ v){
         GroupPtr gr = GET(*v);
         // Undetermined block with unconditional jump...
-        if (
-          gr->type == GROUP_TYPE_NORMAL
-          && ((*gr->end)->IsUncondJump())
-          && out_degree(*v, graph_) == 1
-        ){
+        if ( gr->type == GROUP_TYPE_NORMAL && ((*gr->end)->IsUncondJump()) && out_degree(*v, graph_) == 1){
             OutEdgeIterator oe = boost::out_edges(*v, graph_).first;
             GraphVertex target = boost::target(*oe, graph_);
             GroupPtr target_gr = GET(target);
             // ...to a while or do-while condition...
-            if (
-              target_gr->type == GROUP_TYPE_WHILE || target_gr->type == GROUP_TYPE_DO_WHILE
-            ){
+            if (target_gr->type == GROUP_TYPE_WHILE || target_gr->type == GROUP_TYPE_DO_WHILE){
                 bool is_continue = true;
                 // ...unless...
                 OutEdgeRange toer = boost::out_edges(target, graph_);
                 bool after_jump_jargets = true;
                 for (OutEdgeIterator toe = toer.first; toe != toer.second; ++ toe){
                     // ...it is targeting a while condition which jumps to the next sequential group
-                    if (
-                      target_gr->type == GROUP_TYPE_WHILE
-                      && GET(boost::target(*toe, graph_)) == gr->next
-                    ){
+                    if (target_gr->type == GROUP_TYPE_WHILE && GET(boost::target(*toe, graph_)) == gr->next)
                         is_continue = false;
-                    }
                     // ...or the instruction is placed after all jump targets from condition.
-                    if (
-                      (*GET(boost::target(*toe, graph_))->start)->GetAddress()
-                        > (*gr->start)->GetAddress()
-                    ){
+                    if ((*GET(boost::target(*toe, graph_))->start)->GetAddress() > (*gr->start)->GetAddress())
                         after_jump_jargets = false;
-                    }
                 }
                 if (after_jump_jargets) is_continue = false;
-
-                if (is_continue && ValidateBreakOrContinue(gr, target_gr))
-                    gr->type = GROUP_TYPE_CONTINUE;
+                if (is_continue && ValidateBreakOrContinue(gr, target_gr)) gr->type = GROUP_TYPE_CONTINUE;
             }
         }
     }
@@ -450,18 +427,12 @@ bool ControlFlow::ValidateBreakOrContinue(GroupPtr group, GroupPtr condition_gro
         to = group;
         from = condition_group->next;
     }
-    GROUP_TYPE ogt = (
-      condition_group->type == GROUP_TYPE_DO_WHILE ? GROUP_TYPE_WHILE : GROUP_TYPE_DO_WHILE
-    );
+    GROUP_TYPE ogt = (condition_group->type == GROUP_TYPE_DO_WHILE ? GROUP_TYPE_WHILE : GROUP_TYPE_DO_WHILE);
     // Verify that destination deals with innermost while/do-while.
     for (cursor = from; cursor->next != NULL && cursor != to; cursor = cursor->next){
         if (cursor->type == condition_group->type){
             OutEdgeRange oer_validate = boost::out_edges(Find(cursor->start), graph_);
-            for (
-              OutEdgeIterator oe_validate = oer_validate.first;
-              oe_validate != oer_validate.second;
-              ++ oe_validate
-            ){
+            for (OutEdgeIterator oe_validate = oer_validate.first; oe_validate != oer_validate.second; ++ oe_validate){
                 GraphVertex v_validate = boost::target(*oe_validate, graph_);
                 GroupPtr g_validate = GET(v_validate);
                 // For all other loops of same type found in range,
@@ -469,14 +440,10 @@ bool ControlFlow::ValidateBreakOrContinue(GroupPtr group, GroupPtr condition_gro
                 if (
                   (*g_validate->start)->GetAddress() < (*from->start)->GetAddress()
                   || (*g_validate->start)->GetAddress() > (*to->start)->GetAddress()
-                ){
-                    return false;
-                }
+                ) return false;
                 InEdgeRange ier_validate = boost::in_edges(v_validate, graph_);
                 for (
-                  InEdgeIterator ie_validate = ier_validate.first;
-                  ie_validate != ier_validate.second;
-                  ++ ie_validate
+                  InEdgeIterator ie_validate = ier_validate.first; ie_validate != ier_validate.second; ++ ie_validate
                 ){
                     GroupPtr ig_validate = GET(boost::source(*ie_validate, graph_));
                     // All loops of other type going into range must be placed within range.
@@ -501,8 +468,7 @@ void ControlFlow::DetectIf(){
     for (VertexIterator v = vr.first; v != vr.second; ++v){
         GroupPtr gr = GET(*v);
         // If: Undetermined block with conditional jump.
-        if (gr->type == GROUP_TYPE_NORMAL && ((*gr->end)->IsCondJump()))
-            gr->type = GROUP_TYPE_IF;
+        if (gr->type == GROUP_TYPE_NORMAL && ((*gr->end)->IsCondJump())) gr->type = GROUP_TYPE_IF;
     }
 }
 
@@ -527,16 +493,11 @@ void ControlFlow::DetectElse(){
             // Else: Jump target of if immediately preceded by an unconditional jump...
             if (!(*target_gr->prev->end)->IsUncondJump()) continue;
             // ...which is not a break or a continue...
-            if (
-              target_gr->prev->type == GROUP_TYPE_CONTINUE
-              || target_gr->prev->type == GROUP_TYPE_BREAK
-            ){
+            if (target_gr->prev->type == GROUP_TYPE_CONTINUE || target_gr->prev->type == GROUP_TYPE_BREAK){
                 continue;
             }
             // ...to later in the code.
-            OutEdgeIterator toe = boost::out_edges(
-              Find((*target_gr->prev->start)->GetAddress()), graph_
-            ).first;
+            OutEdgeIterator toe = boost::out_edges(Find((*target_gr->prev->start)->GetAddress()), graph_).first;
             GroupPtr target_target_gr = GET(boost::target(*toe, graph_));
             if ((*target_target_gr->start)->GetAddress() > (*target_gr->end)->GetAddress()){
                 if (ValidateElseBlock(gr, target_gr, target_target_gr)){
@@ -550,11 +511,7 @@ void ControlFlow::DetectElse(){
 
 bool ControlFlow::ValidateElseBlock(GroupPtr if_group, GroupPtr start, GroupPtr end){
     for (GroupPtr cursor = start; cursor != end; cursor = cursor->next){
-        if (
-          cursor->type == GROUP_TYPE_IF
-          || cursor->type == GROUP_TYPE_WHILE
-          || cursor->type == GROUP_TYPE_DO_WHILE
-        ){
+        if (cursor->type == GROUP_TYPE_IF || cursor->type == GROUP_TYPE_WHILE || cursor->type == GROUP_TYPE_DO_WHILE){
             // Validate outgoing edges of conditions.
             OutEdgeRange oer = boost::out_edges(Find(cursor->start), graph_);
             for (OutEdgeIterator oe = oer.first; oe != oer.second; ++ oe){
@@ -564,17 +521,11 @@ bool ControlFlow::ValidateElseBlock(GroupPtr if_group, GroupPtr start, GroupPtr
                 if (
                   (*start->start)->GetAddress() > (*target_gr->start)->GetAddress()
                   || (*target_gr->start)->GetAddress() > (*end->start)->GetAddress()
-                ){
-                    return false;
-                }
+                ) return false;
             }
         }
         // If previous group ends an else, that else must start inside the range.
-        for (
-          ElseEndIterator it = cursor->prev->end_else.begin();
-          it != cursor->prev->end_else.end();
-          ++ it
-        ){
+        for (ElseEndIterator it = cursor->prev->end_else.begin(); it != cursor->prev->end_else.end(); ++ it){
             if ((*(*it)->start)->GetAddress() < (*start->start)->GetAddress()) return false;
         }
         // Unless group is a simple unconditional jump...
@@ -586,8 +537,7 @@ bool ControlFlow::ValidateElseBlock(GroupPtr if_group, GroupPtr start, GroupPtr
             GroupPtr source_gr = GET(source);
             // Edges going to conditions...
             if (
-              source_gr->type == GROUP_TYPE_IF
-              || source_gr->type == GROUP_TYPE_WHILE
+              source_gr->type == GROUP_TYPE_IF || source_gr->type == GROUP_TYPE_WHILE
               || source_gr->type == GROUP_TYPE_DO_WHILE
             ){
                 // ...must not come from outside the range [start, end]...

+ 4 - 9
src/installer/decompiler/DecompilerException.cpp

@@ -22,17 +22,13 @@ UnknownOpcodeParameterException::UnknownOpcodeParameterException(std::string par
 
 const char* UnknownOpcodeParameterException::what() const noexcept{return what_.c_str();}
 
-UnknownConditionalOperatorException::UnknownConditionalOperatorException(
-  unsigned int address, unsigned int op
-){
-    what_ = "Unknown conditional operator: " + std::to_string(op)
-      + " at address " + std::to_string(address);
+UnknownConditionalOperatorException::UnknownConditionalOperatorException(unsigned int address, unsigned int op){
+    what_ = "Unknown conditional operator: " + std::to_string(op) + " at address " + std::to_string(address);
 }
 
 const char* UnknownConditionalOperatorException::what() const noexcept{return what_.c_str();}
 
-UnknownOpcodeException::UnknownOpcodeException(uint32 address, uint32 opcode):
-  address_(address), opcode_(opcode){}
+UnknownOpcodeException::UnknownOpcodeException(uint32 address, uint32 opcode): address_(address), opcode_(opcode){}
 
 const char* UnknownOpcodeException::what() const noexcept{
     sprintf(buffer_, "Unknown %s (address: 0x%08x, opcode: 0x%04x)", Type(), address_, opcode_);
@@ -42,8 +38,7 @@ const char* UnknownOpcodeException::what() const noexcept{
 const char*  UnknownOpcodeException::Type() const{return "Opcode";}
 
 UnknownJumpTypeException::UnknownJumpTypeException(unsigned int address, unsigned int opcode){
-    what_ = "Unknown jump type: " + std::to_string(opcode)
-      + " at address " + std::to_string(address);
+    what_ = "Unknown jump type: " + std::to_string(opcode) + " at address " + std::to_string(address);
 }
 
 const char* UnknownJumpTypeException::what() const noexcept{return what_.c_str();}

+ 1 - 3
src/installer/decompiler/Disassembler.cpp

@@ -77,9 +77,7 @@ void Disassembler::ReadParams(InstPtr inst, const char *types){
     }
 }
 
-void Disassembler::ReadParams(
-  InstPtr inst, const char *types, const std::vector<std::string>& params
-){
+void Disassembler::ReadParams(InstPtr inst, const char *types, const std::vector<std::string>& params){
     while (*types){
         std::string type(types, 1);
         if (type == "N") address_ ++;

+ 1 - 3
src/installer/decompiler/Engine.h

@@ -56,9 +56,7 @@ class Engine {
          * @param[out] output Stream to output the code to.
          * @return Pointer to a CodeGenerator for the engine.
          */
-        virtual std::unique_ptr<CodeGenerator> GetCodeGenerator(
-          const InstVec& insts, std::ostream &output
-        ) = 0;
+        virtual std::unique_ptr<CodeGenerator> GetCodeGenerator(const InstVec& insts, std::ostream &output) = 0;
 
         /**
          * Post-processing step after CFG analysis.

+ 2 - 7
src/installer/decompiler/Graph.cpp

@@ -39,8 +39,7 @@ Group::Group(GraphVertex vertex, InstIterator start, InstIterator end, GroupPtr
     coalesced_else = false;
 }
 
-GraphProperties::GraphProperties(Engine *engine, const Graph &graph): engine(engine), graph(&graph)
-{}
+GraphProperties::GraphProperties(Engine *engine, const Graph &graph): engine(engine), graph(&graph){}
 
 GraphProperties::GraphProperties(const GraphProperties& rhs){*this = rhs;}
 
@@ -54,11 +53,7 @@ GraphProperties& GraphProperties::operator = (const GraphProperties& rhs){
 
 void GraphProperties::operator()(std::ostream& out) const{
     out << "node [shape=record]" << std::endl;
-    for (
-      FuncMap::iterator fn = engine->functions.begin();
-      fn != engine->functions.end();
-      ++ fn
-    ){
+    for (FuncMap::iterator fn = engine->functions.begin(); fn != engine->functions.end(); ++ fn){
         int index = (boost::get(boost::vertex_index, *graph, fn->second.vertex));
         out << "XXX" << index << " [shape=none, label=\"\", height=0]" << std::endl;
         out << "XXX" << index << " -> " << index << std::endl;

+ 1 - 3
src/installer/decompiler/Graph.h

@@ -139,9 +139,7 @@ namespace boost {
              * @param[out] out The stream to output to.
              * @param[in] v The vertex or edge to output the attribute for.
              */
-            template <class VertexOrEdge> void operator()(
-              std::ostream& out, const VertexOrEdge& v
-            ) const {
+            template <class VertexOrEdge> void operator()(std::ostream& out, const VertexOrEdge& v) const {
                 out << "[arrowhead=\"" << get(name, v) << "\"]";
             }
 

+ 1 - 3
src/installer/decompiler/RefCounted.h

@@ -57,6 +57,4 @@ inline void intrusive_ptr_add_ref(RefCounted *ptr){++ (ptr->ref_count_);}
  *
  * @param[in] ptr Pointer to de-reference.
  */
-inline void intrusive_ptr_release(RefCounted *ptr){
-    if (-- (ptr->ref_count_) == 0) delete ptr;
-}
+inline void intrusive_ptr_release(RefCounted *ptr){if (-- (ptr->ref_count_) == 0) delete ptr;}

+ 8 - 12
src/installer/decompiler/ScriptFormatter.cpp

@@ -17,20 +17,16 @@
 
 std::string ScriptFormatter::GetFriendlyVarName(unsigned int bank, unsigned int addr){return "";}
 
-std::string ScriptFormatter::GetFriendlyEntityName(const std::string& entity_name){
-    return entity_name;
-}
+std::string ScriptFormatter::GetFriendlyEntityName(const std::string& entity_name){return entity_name;}
 
-std::string ScriptFormatter::GetFriendlyAnimationName(int animation_id, int id){
-    return std::to_string(animation_id);
-}
+std::string ScriptFormatter::GetFriendlyAnimationName(int animation_id, int id){return std::to_string(animation_id);}
 
 std::string ScriptFormatter::GetFriendlyCharName(int char_id){return std::to_string(char_id);}
 
-std::string ScriptFormatter::GetFriendlyFunctionName(
-  const std::string& entity_name, const std::string& function_name
-){return function_name;}
+std::string ScriptFormatter::GetFriendlyFunctionName(const std::string& entity_name, const std::string& function_name){
+    return function_name;
+}
 
-std::string ScriptFormatter::GetFunctionComment(
-  const std::string& entity_name, const std::string& function_name
-){return "";}
+std::string ScriptFormatter::GetFunctionComment(const std::string& entity_name, const std::string& function_name){
+    return "";
+}

+ 2 - 6
src/installer/decompiler/ScriptFormatter.h

@@ -73,9 +73,7 @@ class ScriptFormatter{
          * @return Friendly name assigned to the entity, or
          * function_name if there is none.
          */
-        virtual std::string GetFriendlyFunctionName(
-          const std::string& entity_name, const std::string& function_name
-        );
+        virtual std::string GetFriendlyFunctionName(const std::string& entity_name, const std::string& function_name);
 
         /**
          * Retrieves the header comment for a function in an entity.
@@ -85,7 +83,5 @@ class ScriptFormatter{
          * @return The function comment. An empty string if the entity or
          * the function don't exist.
          */
-        virtual std::string GetFunctionComment(
-          const std::string& entity_name, const std::string& function_name
-        );
+        virtual std::string GetFunctionComment(const std::string& entity_name, const std::string& function_name);
 };

+ 4 - 9
src/installer/decompiler/Value.cpp

@@ -113,8 +113,7 @@ std::ostream &AddressValue::Print(std::ostream &output) const{
     return output << boost::format("0x%X") % val_;
 }
 
-RelAddressValue::RelAddressValue(uint32 base_addr, int32 offset):
-  IntValue(offset, true), base_addr_(base_addr){};
+RelAddressValue::RelAddressValue(uint32 base_addr, int32 offset): IntValue(offset, true), base_addr_(base_addr){};
 
 bool RelAddressValue::IsAddress(){return true;}
 
@@ -135,9 +134,7 @@ std::ostream &DupValue::Print(std::ostream &output) const{return output << "temp
 
 StringValue::StringValue(std::string str): str_(str){}
 
-std::ostream &StringValue::Print(std::ostream& output) const{
-    return output << "\"" << str_ << "\"";
-}
+std::ostream &StringValue::Print(std::ostream& output) const{return output << "\"" << str_ << "\"";}
 
 UnquotedStringValue::UnquotedStringValue(std::string str): StringValue(str){}
 
@@ -147,13 +144,11 @@ VarValue::VarValue(std::string name): name_(name){}
 
 std::ostream &VarValue::Print(std::ostream& output) const{return output << name_;}
 
-ArrayValue::ArrayValue(const std::string& name, const ValueList& indexes):
-  VarValue(name), indexes_(indexes){}
+ArrayValue::ArrayValue(const std::string& name, const ValueList& indexes): VarValue(name), indexes_(indexes){}
 
 std::ostream &ArrayValue::Print(std::ostream &output) const {
     output << name_;
-    for (ValueList::const_iterator i = indexes_.begin(); i != indexes_.end(); ++ i)
-        output << "[" << *i << "]";
+    for (ValueList::const_iterator i = indexes_.begin(); i != indexes_.end(); ++ i) output << "[" << *i << "]";
     return output;
 }
 

+ 24 - 68
src/installer/decompiler/field/FieldCodeGenerator.cpp

@@ -86,20 +86,14 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
     auto instruction = insts.begin();
     
     std::vector<std::pair<Function&, InstVec>> functions_with_bodies;
-    for (
-      auto function = engine_->functions.begin(); function != engine_->functions.end(); ++ function
-    ){
+    for (auto function = engine_->functions.begin(); function != engine_->functions.end(); ++ function){
         InstVec body;
         for (size_t i = 0; i < function->second.num_instructions; ++ i, ++ instruction)
             body.push_back(*instruction);
         functions_with_bodies.push_back(std::pair<Function&, InstVec> {function->second, body});
     }
 
-    for (
-      auto function = functions_with_bodies.begin();
-      function != functions_with_bodies.end();
-      ++ function
-    ){
+    for (auto function = functions_with_bodies.begin(); function != functions_with_bodies.end(); ++ function){
 
         OnBeforeStartFunction(function->first);
         auto signature = ConstructFuncSignature(function->first);
@@ -108,11 +102,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
         
         // Generate labels for jump instruction targets.
         std::unordered_map<uint32, InstVec> labels;
-        for (
-          auto instruction = function->second.begin();
-          instruction != function->second.end();
-          ++ instruction
-        ){
+        for (auto instruction = function->second.begin(); instruction != function->second.end(); ++ instruction){
             if ((*instruction)->IsCondJump() || (*instruction)->IsUncondJump()){
                 auto targetAddr = (*instruction)->GetDestAddress();
                 auto label = labels.find(targetAddr);
@@ -127,11 +117,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
         // address of the next function) and the 'end' are not written to the Lua script. Keep
         // track of how many are needed and add them at the end of the function.
         int ends_needed = 0;
-        for (
-          auto instruction = function->second.begin();
-          instruction != function->second.end();
-          ++ instruction
-        ){
+        for (auto instruction = function->second.begin(); instruction != function->second.end(); ++ instruction){
             auto label = labels.find((*instruction)->GetAddress());
             if (label != labels.end()){
                 bool needs_label = false;
@@ -145,8 +131,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
                     else needs_label = true;
                 }
                 if (needs_new_line) AddOutputLine("");
-                if (needs_label)
-                    AddOutputLine((boost::format("::label_0x%1$X::") % label->first).str());
+                if (needs_label) AddOutputLine((boost::format("::label_0x%1$X::") % label->first).str());
             }
             ValueStack stack;
             (*instruction)->ProcessInst(function->first, stack, engine_, this);
@@ -156,9 +141,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
             //}
             if ((*instruction)->IsCondJump()){
                 ends_needed ++;
-                AddOutputLine(
-                  (boost::format("if (%s) then") % stack.Pop()->GetString()).str(), false, true
-                );
+                AddOutputLine((boost::format("if (%s) then") % stack.Pop()->GetString()).str(), false, true);
                 // If the next instruction is the last in the function, mark the next pass to
                 // add an 'end' after the instruction to close the if.
                 //if ((*(instruction + 1))->GetAddress() == (*(function->second.back())).GetAddress())
@@ -168,64 +151,47 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
                 // If destination address is outside the functions, turn goto into a return.
                 if ((*instruction)->GetDestAddress() > function->first.end_addr){
                     AddOutputLine(
-                      "-- Overflowed jump to "
-                      + (boost::format("0x%1$X") % (*instruction)->GetDestAddress()).str()
-                      + " (last address in function is "
-                      + (boost::format("0x%1$X)") % function->first.end_addr).str()
+                      "-- Overflowed jump to " + (boost::format("0x%1$X") % (*instruction)->GetDestAddress()).str()
+                      + " (last address in function is " + (boost::format("0x%1$X)") % function->first.end_addr).str()
                     );
                     AddOutputLine("do return 0 end");
                 }
                 // Prevent backward jumps in the on_start script.
                 else if (
-                  "on_start" == function->first.name
-                  && (*instruction)->GetDestAddress() <= (*instruction)->GetAddress()
+                  "on_start" == function->first.name && (*instruction)->GetDestAddress() <= (*instruction)->GetAddress()
                 ){
                     AddOutputLine("-- No infinite loops in the on_start script.");
-                    AddOutputLine((
-                      boost::format("-- goto label_0x%1$X") % (*instruction)->GetDestAddress()
-                    ).str());
-                }
-                else{
-                    AddOutputLine(
-                      (boost::format("goto label_0x%1$X") % (*instruction)->GetDestAddress()).str()
-                    );
+                    AddOutputLine((boost::format("-- goto label_0x%1$X") % (*instruction)->GetDestAddress()).str());
                 }
+                else AddOutputLine((boost::format("goto label_0x%1$X") % (*instruction)->GetDestAddress()).str());
             }
             // Else, already output'd.
         }
         for(; ends_needed > 0; ends_needed --) AddOutputLine("end -- clean-up", true, false);
         // Add missing return:
-        if (
-          "return 0" != lines_.at(lines_.size() - 1).line
-          && "do return 0 end" != lines_.at(lines_.size() - 1).line
-        ){
+        if ("return 0" != lines_.at(lines_.size() - 1).line && "do return 0 end" != lines_.at(lines_.size() - 1).line)
             AddOutputLine("do return 0 end", false, false);
-        }
         OnEndFunction(function->first);
     }
 
     for (auto i = lines_.begin(); i != lines_.end(); ++i){
-        if (i->unindent_before && indent_level_ > 0){
-            indent_level_ --;
-        }
+        if (i->unindent_before && indent_level_ > 0) indent_level_ --;
         output_ << IndentString(i->line) << std::endl;
         if (i->indent_after) indent_level_++;
     }
 }
 
-void FieldCodeGenerator::AddOutputLine(
-  std::string line, bool unindent_before, bool indent_after
-){lines_.push_back(CodeLine(line, unindent_before, indent_after));}
+void FieldCodeGenerator::AddOutputLine(std::string line, bool unindent_before, bool indent_after){
+    lines_.push_back(CodeLine(line, unindent_before, indent_after));
+}
 
-float FieldCodeGenerator::GetScaleFactor() const
-{return static_cast<FieldEngine*>(engine_)->GetScaleFactor();}
+float FieldCodeGenerator::GetScaleFactor() const{return static_cast<FieldEngine*>(engine_)->GetScaleFactor();}
 
 void FieldCodeGenerator::OnBeforeStartFunction(const Function& function){
     FunctionMetaData meta_data(function.metadata);
     if (meta_data.IsStart()){
         AddOutputLine("EntityContainer[\"" + meta_data.GetEntityName() + "\"] = {", false, true);
-        if (meta_data.GetCharacterId() != -1)
-            AddOutputLine(meta_data.GetEntityName() + " = nil,\n");
+        if (meta_data.GetCharacterId() != -1) AddOutputLine(meta_data.GetEntityName() + " = nil,\n");
     }
     const auto comment = formatter_.GetFunctionComment(meta_data.GetEntityName(), function.name);
     if (!comment.empty()) AddOutputLine("-- " + comment);
@@ -241,10 +207,7 @@ void FieldCodeGenerator::OnStartFunction(const Function& func){
         AddOutputLine("background2d:autoscroll_to_entity(entity_manager:get_entity(\"Cloud\"))\n");
     }
     // For lines, exit if they are off.
-    if (
-      func.name == "on_approach" || func.name == "on_cross"
-      || func.name == "on_near" || func.name == "on_leave"
-    ){
+    if (func.name == "on_approach" || func.name == "on_cross" || func.name == "on_near" || func.name == "on_leave"){
         AddOutputLine("if self.on ~= nill and self.on == false then", false, true);
         AddOutputLine("return 0");
         AddOutputLine("end", true, false);
@@ -272,8 +235,7 @@ void FieldCodeGenerator::OnEndFunction(const Function& function){
 std::string FieldCodeGenerator::ConstructFuncSignature(const Function &function){
     // Generate name
     FunctionMetaData meta_data(function.metadata);
-    return formatter_.GetFriendlyFunctionName(meta_data.GetEntityName(), function.name)
-      + " = function(self)";
+    return formatter_.GetFriendlyFunctionName(meta_data.GetEntityName(), function.name) + " = function(self)";
 }
 
 bool FieldCodeGenerator::OutputOnlyRequiredLabels() const{return true;}
@@ -285,8 +247,7 @@ const std::string FieldCodeGenerator::FormatInstructionNotImplemented(
 ){
     return (
       boost::format(
-        "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: "
-        "instruction 0x%3$04x not implemented\")"
+        "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: instruction 0x%3$04x not implemented\")"
       ) % entity % address % opcode
     ).str();
 }
@@ -303,16 +264,11 @@ const std::string FieldCodeGenerator::FormatInstructionNotImplemented(
     }
     return(
       boost::format(
-        "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: "
-        "instruction %3%(%4%) not implemented\")"
+        "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: instruction %3%(%4%) not implemented\")"
         ) % entity % address % instruction.GetName() % parameterList.str()
       ).str();
 }
 
-const std::string FieldCodeGenerator::FormatBool(uint32 value){
-    return value == 0 ? "false" : "true";
-}
+const std::string FieldCodeGenerator::FormatBool(uint32 value){return value == 0 ? "false" : "true";}
 
-const std::string FieldCodeGenerator::FormatInvertedBool(uint32 value){
-    return value == 0 ? "true" : "false";
-}
+const std::string FieldCodeGenerator::FormatInvertedBool(uint32 value){return value == 0 ? "true" : "false";}

+ 31 - 96
src/installer/decompiler/field/FieldDisassembler.cpp

@@ -54,11 +54,9 @@ FieldDisassembler::FieldDisassembler(
     ReadHeader();
 }
 
-FieldDisassembler::FieldDisassembler(
-  FieldScriptFormatter& formatter, FieldEngine *engine, InstVec &insts
-): Disassembler(insts), engine_(engine), formatter_(formatter){
-    section_pointers_size_ = (sizeof(uint32) * NUM_SECTIONS);
-}
+FieldDisassembler::FieldDisassembler(FieldScriptFormatter& formatter, FieldEngine *engine, InstVec &insts):
+  Disassembler(insts), engine_(engine), formatter_(formatter)
+{section_pointers_size_ = (sizeof(uint32) * NUM_SECTIONS);}
 
 float FieldDisassembler::GetScaleFactor() const{return scale_factor_;}
 
@@ -70,8 +68,7 @@ void FieldDisassembler::ReadHeader(){
         for (int i = 0; i < NUM_SECTIONS; i ++) sections_[i] = stream_->ReadU32();
         // Now fix up from PSX RAM pointers to simple file offsets.
         const uint32 basePtr = sections_[0];
-        for (int i = 0; i < NUM_SECTIONS; i++)
-            sections_[i] = (sections_[i] - basePtr) + section_pointers_size_;
+        for (int i = 0; i < NUM_SECTIONS; i++) sections_[i] = (sections_[i] - basePtr) + section_pointers_size_;
         // Now seek to the script section.
         stream_->Seek(sections_[SCRIPT]);
     }
@@ -86,9 +83,7 @@ void FieldDisassembler::Open(const char *filename){
     ReadHeader();
 }
 
-uint32 FieldDisassembler::GetEndOfScriptOffset(
-  uint16 cur_entry_point, size_t entity_index, size_t script_index
-){
+uint32 FieldDisassembler::GetEndOfScriptOffset(uint16 cur_entry_point, size_t entity_index, size_t script_index){
     uint16 next_entry_point = cur_entry_point;
     do{
         if (script_index + 1 >= 32){
@@ -129,9 +124,7 @@ void FieldDisassembler::DoDisassemble(){
         std::vector<ScriptInfo> script_info;
         // Collect the scripts to parse
         for (
-          size_t script_index = 0;
-          script_index < header_.entity_scripts[entity_number].size();
-          script_index ++
+          size_t script_index = 0; script_index < header_.entity_scripts[entity_number].size(); script_index ++
         ){
             uint16 script_entry_point = header_.entity_scripts[entity_number][script_index];
             // If this scripts entry point is already parsed, don't do it again as it means
@@ -151,8 +144,7 @@ void FieldDisassembler::DoDisassemble(){
             const bool is_start = it == script_info.begin();
             const bool is_end = it == (-- script_info.end());
             DisassembleIndivdualScript(
-              entity_name, entity_number, it->index, it->entry_point,
-              it->next_entry_point, is_start, is_end
+              entity_name, entity_number, it->index, it->entry_point, it->next_entry_point, is_start, is_end
             );
         }
     }
@@ -161,8 +153,7 @@ void FieldDisassembler::DoDisassemble(){
 int FieldDisassembler::FindId(uint32 start_addr, uint32 end_addr, const InstVec& insts){
     for (const InstPtr& instruction : insts){
         if (instruction->GetAddress() >= start_addr && instruction->GetAddress() <= end_addr){
-            if (instruction->GetOpcode() == OPCODES::opCodeCHAR)
-                return instruction->GetParam(0)->GetSigned();
+            if (instruction->GetOpcode() == OPCODES::opCodeCHAR) return instruction->GetParam(0)->GetSigned();
         }
     }
     return -1;
@@ -183,9 +174,7 @@ void FieldDisassembler::AddFunc(
     // Read.
     if (to_return_only){
         // Read opcodes to the end or bail at the first return.
-        is_line = ReadOpCodesToPositionOrReturn(
-          next_script_entry_point + section_pointers_size_, point_a, point_b
-        );
+        is_line = ReadOpCodesToPositionOrReturn(next_script_entry_point + section_pointers_size_, point_a, point_b);
         auto stream_pos = stream_->GetPosition();
         const size_t endPos = next_script_entry_point + section_pointers_size_;
         // Can't be the end if there is more data:
@@ -194,11 +183,8 @@ void FieldDisassembler::AddFunc(
     else{
         // Keep going until all of the scriptis fectched, i.e if bailed at a
         // return, then call again until everything is parsed.
-        while (stream_->GetPosition() != next_script_entry_point + section_pointers_size_){
-            is_line = ReadOpCodesToPositionOrReturn(
-              next_script_entry_point + section_pointers_size_, point_a, point_b
-            );
-        }
+        while (stream_->GetPosition() != next_script_entry_point + section_pointers_size_)
+            is_line = ReadOpCodesToPositionOrReturn(next_script_entry_point + section_pointers_size_, point_a, point_b);
     }
     // Read metadata, mark start and/or end.
     std::string meta_data;
@@ -260,31 +246,21 @@ void FieldDisassembler::DisassembleIndivdualScript(
     if (script_index > 0){
         if (script_index == 1){
             AddFunc(
-              entity_name, entity_index, script_index, next_script_entry_point,
-              is_start, is_end, false, "on_interact"
-            );
-        }
-        else{
-            AddFunc(
-              entity_name, entity_index, script_index, next_script_entry_point,
-              is_start, is_end, false, ""
+              entity_name, entity_index, script_index, next_script_entry_point, is_start, is_end, false, "on_interact"
             );
         }
+        else AddFunc(entity_name, entity_index, script_index, next_script_entry_point, is_start, is_end, false, "");
     }
     else{
         const size_t end_pos = next_script_entry_point + section_pointers_size_;
         // Read the init script, which means stop at the first return.
-        AddFunc(
-          entity_name, entity_index, script_index, next_script_entry_point,
-          is_start, is_end, true, "on_start"
-        );
+        AddFunc(entity_name, entity_index, script_index, next_script_entry_point, is_start, is_end, true, "on_start");
         // Not at the end of this script? Then the remaining data is the "main" script
         auto stream_pos = stream_->GetPosition();
         if (stream_pos != end_pos){
             // The "main" script can have more than one return statement...
             AddFunc(
-              entity_name, entity_index, script_index, next_script_entry_point,
-              false, is_end, false, "on_update"
+              entity_name, entity_index, script_index, next_script_entry_point, false, is_end, false, "on_update"
             );
             stream_pos = stream_->GetPosition();
             // ... but should end exactly on the end pos
@@ -779,90 +755,51 @@ bool FieldDisassembler::ReadOpCodesToPositionOrReturn(
                     switch (opcode){
                         case OPCODES_KAWAI::EYETX:
                             // Parameters were "BBBB"
-                            ParseOpcode(
-                              opcode, "EYETX", new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "EYETX", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::TRNSP:
                             // Parameters were "B"
-                            ParseOpcode(
-                              opcode, "TRNSP", new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "TRNSP", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::AMBNT:
                             // Parameters were "AMBNT"
-                            ParseOpcode(
-                              opcode, "AMBNT",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "AMBNT", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown03:
-                            ParseOpcode(
-                              opcode, "Unknown03",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown03", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown04:
-                            ParseOpcode(
-                              opcode, "Unknown04",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown04", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown05:
-                            ParseOpcode(
-                              opcode, "Unknown03",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown05", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::LIGHT:
-                            ParseOpcode(
-                              opcode, "LIGHT",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "LIGHT", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown07:
-                            ParseOpcode(
-                              opcode, "Unknown07",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown07", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown08:
-                            ParseOpcode(
-                              opcode, "Unknown08",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown08", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown09:
-                            ParseOpcode(
-                              opcode, "Unknown09",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown09", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::SBOBJ:
-                            ParseOpcode(
-                              opcode, "SBOBJ", new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "SBOBJ", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown0B:
-                            ParseOpcode(
-                              opcode, "Unknown0B",
-                              new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown0B", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::Unknown0C:
-                            ParseOpcode(
-                              opcode, "Unknown0C", new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "Unknown0C", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::SHINE:
-                            ParseOpcode(
-                              opcode, "SHINE", new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "SHINE", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         case OPCODES_KAWAI::RESET:
-                            ParseOpcode(
-                              opcode, "RESET", new FieldModelInstruction(), 0, parameters.c_str()
-                            );
+                            ParseOpcode(opcode, "RESET", new FieldModelInstruction(), 0, parameters.c_str());
                             break;
                         default:
                             throw UnknownSubOpcodeException(this->address_, opcode);
@@ -1097,9 +1034,7 @@ bool FieldDisassembler::ReadOpCodesToPositionOrReturn(
                 ParseOpcode(full_opcode, "ADPAL", new FieldBackgroundInstruction(), 0, "NNNBBBBBB");
                 break;
             case OPCODES::MPPAL2:
-                ParseOpcode(
-                  full_opcode, "MPPAL2", new FieldBackgroundInstruction(), 0, "NNNBBBBBB"
-                );
+                ParseOpcode(full_opcode, "MPPAL2", new FieldBackgroundInstruction(), 0, "NNNBBBBBB");
                 break;
             case OPCODES::STPLS:
                 ParseOpcode(full_opcode, "STPLS", new FieldBackgroundInstruction(), 0, "BBBB");

+ 1 - 1
src/installer/decompiler/field/FieldDisassembler.h

@@ -724,7 +724,7 @@ class FieldDisassembler : public Disassembler{
         template<typename T> void ParseOpcode(
           int opcode, std::string name, T instruction, int stack_change, const char* argument_format
         ){
-            uint32 full_opcode = (full_opcode << 8) + opcode;
+            const uint32 full_opcode = static_cast<uint32>(opcode);
             this->insts_.push_back(instruction);
             this->insts_.back()->SetOpcode(full_opcode);
             this->insts_.back()->SetAddress(this->address_);

+ 6 - 16
src/installer/decompiler/field/FieldEngine.cpp

@@ -52,8 +52,7 @@
  * TURNW
  */
 
-FieldEngine::Entity::Entity(const std::string& name, size_t index):
-  name_(name), index_(index), is_line_(false){}
+FieldEngine::Entity::Entity(const std::string& name, size_t index): name_(name), index_(index), is_line_(false){}
 
 std::string FieldEngine::Entity::GetName() const{return name_;}
 
@@ -65,13 +64,9 @@ std::string FieldEngine::Entity::FunctionByIndex(size_t index) const{
     return it->second;
 }
 
-void FieldEngine::Entity::AddFunction(const std::string& name, size_t index){
-    functions_[index] = name;
-}
+void FieldEngine::Entity::AddFunction(const std::string& name, size_t index){functions_[index] = name;}
 
-void FieldEngine::Entity::MarkAsLine(
-  bool line, std::vector<float> point_a, std::vector<float> point_b
-){
+void FieldEngine::Entity::MarkAsLine(bool line, std::vector<float> point_a, std::vector<float> point_b){
     is_line_ = line;
     point_a_.clear();
     point_b_.clear();
@@ -118,9 +113,7 @@ std::unique_ptr<Disassembler> FieldEngine::GetDisassembler(InstVec &insts){
     return std::move(ret);
 }
 
-std::unique_ptr<CodeGenerator> FieldEngine::GetCodeGenerator(
-  const InstVec& insts, std::ostream &output
-){
+std::unique_ptr<CodeGenerator> FieldEngine::GetCodeGenerator(const InstVec& insts, std::ostream &output){
     // The broken version:
     //return std::make_unique<FieldCodeGenerator>(this, insts, output);
     // The not-as-nice-but-at-least-it-works version:
@@ -200,8 +193,7 @@ std::vector<FieldDecompiler::Line> FieldEngine::GetLineList() const{
 }
 
 void FieldEngine::AddEntityFunction(
-  const std::string& entity_name, size_t entity_index,
-  const std::string& func_name, size_t func_index
+  const std::string& entity_name, size_t entity_index, const std::string& func_name, size_t func_index
 ){
     auto it = entity_index_map_.find(entity_index);
     if (it != std::end(entity_index_map_)) (*it).second.AddFunction(func_name, func_index);
@@ -216,9 +208,7 @@ void FieldEngine::MarkEntityAsLine(
   size_t entity_index, bool line, std::vector<float> point_a, std::vector<float> point_b
 ){
     auto it = entity_index_map_.find(entity_index);
-    if (it != std::end(entity_index_map_)){
-        (*it).second.MarkAsLine(line, point_a, point_b);
-    }
+    if (it != std::end(entity_index_map_)) (*it).second.MarkAsLine(line, point_a, point_b);
 }
 
 bool FieldEngine::EntityIsLine(size_t entity_index){

+ 1 - 4
src/installer/decompiler/field/FieldScriptFormatter.cpp

@@ -23,9 +23,6 @@ void FieldScriptFormatter::AddSpawnPoint(
 std::string FieldScriptFormatter::GetSpawnPointName(
   unsigned int map_id, const std::string& entity,
   const std::string& function_name, unsigned int address
-){
-    return GetMapName(map_id)
-      + "_" + entity + "_" + function_name + "_" + std::to_string(address);
-}
+){return GetMapName(map_id) + "_" + entity + "_" + function_name + "_" + std::to_string(address);}
 
 std::string FieldScriptFormatter::GetMapName(unsigned int map_id) { return std::to_string(map_id);}

+ 2 - 4
src/installer/decompiler/instruction/BinaryOpStackInstruction.cpp

@@ -21,8 +21,6 @@ void BinaryOpStackInstruction::ProcessInst(
 ){
     ValuePtr op1 = stack.Pop();
     ValuePtr op2 = stack.Pop();
-    if (code_gen->GetBinaryOrder() == FIFO_ARGUMENT_ORDER)
-        stack.Push(new BinaryOpValue(op2, op1, code_gen_data_));
-    else if (code_gen->GetBinaryOrder() == LIFO_ARGUMENT_ORDER)
-        stack.Push(new BinaryOpValue(op1, op2, code_gen_data_));
+    if (code_gen->GetBinaryOrder() == FIFO_ARGUMENT_ORDER) stack.Push(new BinaryOpValue(op2, op1, code_gen_data_));
+    else if (code_gen->GetBinaryOrder() == LIFO_ARGUMENT_ORDER) stack.Push(new BinaryOpValue(op1, op2, code_gen_data_));
 }

+ 1 - 2
src/installer/decompiler/instruction/KernelCallStackInstruction.cpp

@@ -22,8 +22,7 @@ void KernelCallStackInstruction::ProcessInst(
     code_gen->GetArgList().clear();
     bool returns_value = (code_gen_data_.find("r") == 0);
     std::string metadata = (!returns_value ? code_gen_data_ : code_gen_data_.substr(1));
-    for (size_t i = 0; i < metadata.length(); i ++)
-        code_gen->ProcessSpecialMetadata(this, metadata[i], i);
+    for (size_t i = 0; i < metadata.length(); i ++) code_gen->ProcessSpecialMetadata(this, metadata[i], i);
     stack.Push(new CallValue(name_, code_gen->GetArgList()));
     if (!returns_value){
         std::stringstream stream;

+ 2 - 1
src/installer/decompiler/instruction/KernelCallStackInstruction.h

@@ -18,7 +18,8 @@
 #include "Instruction.h"
 
 /**
- * Default implementation for stack-based instruction performing a kernel function call.
+ * Default implementation for stack-based instruction performing a kernel
+ * function call.
  */
 class KernelCallStackInstruction : public Instruction{
 

+ 3 - 3
src/installer/decompiler/instruction/ReturnInstruction.cpp

@@ -18,6 +18,6 @@
 
 bool ReturnInstruction::IsReturn() const{return true;}
 
-void ReturnInstruction::ProcessInst(
-  Function& function, ValueStack& stack, Engine* engine, CodeGenerator *code_gen
-){code_gen->AddOutputLine("return;");}
+void ReturnInstruction::ProcessInst(Function& function, ValueStack& stack, Engine* engine, CodeGenerator *code_gen){
+    code_gen->AddOutputLine("return;");
+}

+ 2 - 3
src/installer/decompiler/instruction/UncondJumpInstruction.cpp

@@ -17,6 +17,5 @@
 
 bool UncondJumpInstruction::IsUncondJump() const{return true;}
 
-void UncondJumpInstruction::ProcessInst(
-  Function& function, ValueStack& stack, Engine* engine, CodeGenerator* code_gen
-){}
+void UncondJumpInstruction::ProcessInst(Function& function, ValueStack& stack, Engine* engine, CodeGenerator* code_gen){
+}

+ 54 - 170
src/installer/decompiler/world/WorldDisassembler.cpp

@@ -96,9 +96,7 @@ void WorldDisassembler::DoDisassemble(){
                         ParseOpcode(opcode, "reset stack", new WorldNoOutputInstruction(), 0, "");
                         break;
                     case 0x201:
-                        ParseOpcode(
-                          opcode, "jump if false", new WorldCondJumpInstruction(), 0, "w"
-                        );
+                        ParseOpcode(opcode, "jump if false", new WorldCondJumpInstruction(), 0, "w");
                         break;
                     case 0x203:
                         ParseOpcode(opcode, "return", new WorldKernelCallInstruction(), 0, "");
@@ -106,34 +104,23 @@ void WorldDisassembler::DoDisassemble(){
                     case 0x200:
                         ParseOpcode(opcode, "jump", new WorldUncondJumpInstruction(), 0, "w");
                         break;
-
                     case 0x118:
-                        ParseOpcode(
-                          opcode, "push byte from bank 0", new WorldLoadBankInstruction(), 2, "w"
-                        );
+                        ParseOpcode(opcode, "push byte from bank 0", new WorldLoadBankInstruction(), 2, "w");
                         break;
                     case 0x110:
                         ParseOpcode(opcode, "push constant", new WorldLoadInstruction(), 2, "w");
                         break;
                     case 0x119:
-                        ParseOpcode(
-                          opcode, "push byte from bank 1", new WorldLoadBankInstruction(), 2, "w"
-                        );
+                        ParseOpcode(opcode, "push byte from bank 1", new WorldLoadBankInstruction(), 2, "w");
                         break;
                     case 0x11c:
-                        ParseOpcode(
-                          opcode, "push word from bank 0", new WorldLoadBankInstruction(), 2, "w"
-                        );
+                        ParseOpcode(opcode, "push word from bank 0", new WorldLoadBankInstruction(), 2, "w");
                         break;
                     case 0x114:
-                        ParseOpcode(
-                          opcode, "push bit from bank 0", new WorldLoadBankInstruction(), 2, "w"
-                        );
+                        ParseOpcode(opcode, "push bit from bank 0", new WorldLoadBankInstruction(), 2, "w");
                         break;
                     case 0x11b:
-                        ParseOpcode(
-                          opcode, "push special", new WorldLoadBankInstruction(), 2, "w"
-                        );
+                        ParseOpcode(opcode, "push special", new WorldLoadBankInstruction(), 2, "w");
                         break;
                     case 0x11f:
                         ParseOpcode(opcode, "push special", new WorldLoadInstruction(), 2, "w");
@@ -141,7 +128,6 @@ void WorldDisassembler::DoDisassemble(){
                     case 0x117:
                         ParseOpcode(opcode, "push special", new WorldLoadBankInstruction(), 2, "w");
                         break;
-
                     case 0x0e0:
                         ParseOpcode(opcode, "write bank", new WorldStoreInstruction(), 0, "");
                         break;
@@ -165,9 +151,7 @@ void WorldDisassembler::DoDisassemble(){
                         ParseOpcode(opcode, "push logicand", new WorldSubStackInstruction(), 2, "");
                         break;
                     case 0x062:
-                        ParseOpcode(
-                          opcode, "push lessequal", new WorldSubStackInstruction(), 2, ""
-                        );
+                        ParseOpcode(opcode, "push lessequal", new WorldSubStackInstruction(), 2, "");
                         break;
                     case 0x030:
                         ParseOpcode(opcode, "push mul", new WorldSubStackInstruction(), 2, "");
@@ -182,17 +166,13 @@ void WorldDisassembler::DoDisassemble(){
                         ParseOpcode(opcode, "push or", new WorldSubStackInstruction(), 2, "");
                         break;
                     case 0x070:
-                        ParseOpcode(
-                          opcode, "push equal", new WorldBinaryEqualStackInstruction(), 2, ""
-                        );
+                        ParseOpcode(opcode, "push equal", new WorldBinaryEqualStackInstruction(), 2, "");
                         break;
                     case 0x041:
                         ParseOpcode(opcode, "push sub", new WorldSubStackInstruction(), 2, "");
                         break;
                     case 0x063:
-                        ParseOpcode(
-                          opcode, "push greaterequal", new WorldSubStackInstruction(), 2, ""
-                        );
+                        ParseOpcode(opcode, "push greaterequal", new WorldSubStackInstruction(), 2, "");
                         break;
                     case 0x017:
                         ParseOpcode(opcode, "push logicnot", new WorldSubStackInstruction(), 2, "");
@@ -209,7 +189,6 @@ void WorldDisassembler::DoDisassemble(){
                     case 0x015:
                         ParseOpcode(opcode, "push neg", new WorldSubStackInstruction(), 2, "");
                         break;
-
                     case 0x320:
                         ParseOpcode(opcode, "unknown 320", new WorldKernelCallInstruction(), 0, "");
                         break;
@@ -265,26 +244,20 @@ void WorldDisassembler::DoDisassemble(){
                         ParseOpcode(opcode, "unknown 331", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x319:
-                        ParseOpcode(
-                          opcode, "unknown 319", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "unknown 319", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x353:
                         ParseOpcode(opcode, "unknown 353", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x33e:
-                        ParseOpcode(
-                          opcode, "unknown 33e", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "unknown 33e", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x321:
                         ParseOpcode(opcode, "unknown 321", new WorldKernelCallInstruction(), 0, "");
                         break;
 
                     case 0x31d:
-                        ParseOpcode(
-                          opcode, "play sound effect", new WorldKernelCallInstruction(), 2, "w"
-                        );
+                        ParseOpcode(opcode, "play sound effect", new WorldKernelCallInstruction(), 2, "w");
                         break;
                     case 0x336:
                         ParseOpcode(
@@ -293,235 +266,146 @@ void WorldDisassembler::DoDisassemble(){
                         );
                         break;
                     case 0x328:
-                        ParseOpcode(
-                          opcode, "set active entity direction",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set active entity direction", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x33d:
-                        ParseOpcode(
-                          opcode, "set field entry point2?", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set field entry point2?", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x351:
-                        ParseOpcode(
-                          opcode, "set music volume", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set music volume", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x352:
-                        ParseOpcode(
-                          opcode, "shake camera on / off", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "shake camera on / off", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x34f:
-                        ParseOpcode(
-                          opcode, "set active entity y position",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set active entity y position", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x30b:
-                        ParseOpcode(
-                          opcode, "set active entity y offset",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set active entity y offset", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x347:
                         ParseOpcode(
-                          opcode, "move active entity to entity by model id ?",
-                          new WorldKernelCallInstruction(), 0, ""
+                          opcode, "move active entity to entity by model id ?", new WorldKernelCallInstruction(), 0, ""
                         );
                         break;
                     case 0x30e:
-                        ParseOpcode(
-                          opcode, "active entity play animation",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "active entity play animation", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x305:
-                        ParseOpcode(
-                          opcode, "set wait frames", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set wait frames", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x306:
                         ParseOpcode(opcode, "wait?", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x307:
-                        ParseOpcode(
-                          opcode, "set control lock", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set control lock", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x303:
-                        ParseOpcode(
-                          opcode, "set active entity movespeed",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set active entity movespeed", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x32c:
-                        ParseOpcode(
-                          opcode, "set window parameters", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set window parameters", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x32d:
-                        ParseOpcode(
-                          opcode, "wait for window ready", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "wait for window ready", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x324:
-                        ParseOpcode(
-                          opcode, "set window dimensions", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set window dimensions", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x325:
-                        ParseOpcode(
-                          opcode, "set window message", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set window message", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x32e:
-                        ParseOpcode(
-                          opcode, "wait for message acknowledge",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "wait for message acknowledge", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x32b:
-                        ParseOpcode(
-                          opcode, "set battle lock", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set battle lock", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x326:
-                        ParseOpcode(
-                          opcode, "set window prompt", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set window prompt", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x327:
-                        ParseOpcode(
-                          opcode, "wait for prompt acknowledge ?",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "wait for prompt acknowledge ?", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x318:
-                        ParseOpcode(
-                          opcode, "enter field scene", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "enter field scene", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x33b:
-                        ParseOpcode(
-                          opcode, "fade out?", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "fade out?", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x348:
-                        ParseOpcode(
-                          opcode, "fade in ?", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "fade in ?", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x330:
-                        ParseOpcode(
-                          opcode, "set active entity", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set active entity", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x350:
-                        ParseOpcode(
-                          opcode, "set meteor texture on/off",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set meteor texture on/off", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x34b:
-                        ParseOpcode(
-                          opcode, "set chocobo type", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set chocobo type", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x34c:
-                        ParseOpcode(
-                          opcode, "set submarine color", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set submarine color", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x349:
-                        ParseOpcode(
-                          opcode, "set world progress", new WorldKernelCallInstruction(), 0, ""
+                        ParseOpcode(opcode, "set world progress", new WorldKernelCallInstruction(), 0, ""
                         );
                         break;
                     case 0x300:
-                        ParseOpcode(
-                          opcode, "load model", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "load model", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x302:
-                        ParseOpcode(
-                          opcode, "set player entity", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set player entity", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x30c:
-                        ParseOpcode(
-                          opcode, "enter vehicle?", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "enter vehicle?", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x308:
                         ParseOpcode(
-                          opcode, "set active entity mesh coordinates",
-                          new WorldKernelCallInstruction(), 0, ""
+                          opcode, "set active entity mesh coordinates", new WorldKernelCallInstruction(), 0, ""
                         );
                         break;
                     case 0x309:
                         ParseOpcode(
-                          opcode, "set active entity coordinates in mesh",
-                          new WorldKernelCallInstruction(), 0, ""
+                          opcode, "set active entity coordinates in mesh", new WorldKernelCallInstruction(), 0, ""
                         );
                         break;
                     case 0x304:
                         ParseOpcode(
-                          opcode, "set active entity direction & facing",
-                          new WorldKernelCallInstruction(), 0, ""
+                          opcode, "set active entity direction & facing", new WorldKernelCallInstruction(), 0, ""
                         );
                         break;
                     case 0x310:
-                        ParseOpcode(
-                          opcode, "set active point", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set active point", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x311:
-                        ParseOpcode(
-                          opcode, "set point mesh coordinates",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set point mesh coordinates", new WorldKernelCallInstruction(), 0, "" );
                         break;
                     case 0x312:
-                        ParseOpcode(
-                          opcode, "set point coordinates in mesh",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set point coordinates in mesh", new WorldKernelCallInstruction(), 0, "" );
                         break;
                     case 0x313:
-                        ParseOpcode(
-                          opcode, "set point terrain BGR", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set point terrain BGR", new WorldKernelCallInstruction(), 0, "" );
                         break;
                     case 0x314:
-                        ParseOpcode(
-                          opcode, "set point dropoff parameters",
-                          new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set point dropoff parameters", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x315:
-                        ParseOpcode(
-                          opcode, "set point sky BGR", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set point sky BGR", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x316:
-                        ParseOpcode(
-                          opcode, "set point BGR3?", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "set point BGR3?", new WorldKernelCallInstruction(), 0, "");
                         break;
                     case 0x317:
-                        ParseOpcode(
-                          opcode, "trigger battle", new WorldKernelCallInstruction(), 0, ""
-                        );
+                        ParseOpcode(opcode, "trigger battle", new WorldKernelCallInstruction(), 0, "");
                         break;
                     default: throw UnknownOpcodeException(this->address_, opcode);
                 }
             }
             this->address_ += 2;
         }
-        for (auto& instruction : this->insts_)
-            instruction->SetAddress(instruction->GetAddress() / 2);
+        for (auto& instruction : this->insts_) instruction->SetAddress(instruction->GetAddress() / 2);
     }
 }
 

+ 1 - 3
src/installer/decompiler/world/WorldEngine.cpp

@@ -30,9 +30,7 @@
 
 WorldEngine::BankValue::BankValue(std::string name) : VarValue(name){}
 
-WorldEngine::WorldEngine(int script_number) : script_number_(script_number){
-    SetOutputStackEffect(true);
-}
+WorldEngine::WorldEngine(int script_number) : script_number_(script_number){SetOutputStackEffect(true);}
 
 std::unique_ptr<Disassembler> WorldEngine::GetDisassembler(InstVec &insts){
     return std::make_unique<WorldDisassembler>(this, insts, script_number_);

+ 5 - 0
src/installer/installer_resources.qrc

@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/icons">
+        <file alias="v-gears-installer.png">../../v-gears-installer.png</file>
+    </qresource>
+</RCC>

+ 7 - 0
src/installer/main.cpp

@@ -14,6 +14,7 @@
  */
 
 #include <QtWidgets/QApplication>
+#include <QtGui/QIcon>
 
 #include "MainWindow.h"
 
@@ -25,8 +26,14 @@
  * @return The application return code. 0 is OK.
  */
 int main(int argc, char *argv[]){
+    QApplication::setApplicationName(QStringLiteral("v-gears-installer"));
+    QApplication::setApplicationDisplayName(QStringLiteral("V-Gears Installer"));
     QApplication application(argc, argv);
+    QApplication::setDesktopFileName(QStringLiteral("v-gears-installer"));
+    const QIcon installerIcon(QStringLiteral(":/icons/v-gears-installer.png"));
+    if (!installerIcon.isNull()) application.setWindowIcon(installerIcon);
     MainWindow window;
+    if (!installerIcon.isNull()) window.setWindowIcon(installerIcon);
     window.show();
     return application.exec();
 }

+ 239 - 177
src/installer/ui_MainWindow.h

@@ -1,7 +1,7 @@
 /********************************************************************************
 ** Form generated from reading UI file 'MainWindow.ui'
 **
-** Created by: Qt User Interface Compiler version 5.15.17
+** Created by: Qt User Interface Compiler version 5.15.19
 **
 ** WARNING! All changes made in this file will be lost when recompiling UI file!
 ********************************************************************************/
@@ -23,7 +23,6 @@
 #include <QtWidgets/QMenuBar>
 #include <QtWidgets/QProgressBar>
 #include <QtWidgets/QPushButton>
-#include <QtWidgets/QSpacerItem>
 #include <QtWidgets/QTabWidget>
 #include <QtWidgets/QTextEdit>
 #include <QtWidgets/QVBoxLayout>
@@ -35,6 +34,8 @@ class Ui_MainWindow
 {
 public:
     QAction *actionExit;
+    QAction *actionABout;
+    QAction *actionHelp;
     QWidget *centralWidget;
     QVBoxLayout *verticalLayout;
     QTabWidget *tabWidget;
@@ -55,28 +56,27 @@ public:
     QPushButton *help_data_dest;
     QCheckBox *chk_advanced_options;
     QGroupBox *advancedOptions;
-    QVBoxLayout *verticalLayout_5;
-    QHBoxLayout *horizontalLayout_6;
-    QCheckBox *chk_no_battle_data;
-    QCheckBox *chk_no_battle_models;
-    QHBoxLayout *horizontalLayout_61;
-    QCheckBox *chk_no_kernel;
-    QCheckBox *chk_no_images;
-    QHBoxLayout *horizontalLayout_7;
-    QCheckBox *chk_no_sounds;
-    QCheckBox *chk_no_music;
-    QHBoxLayout *horizontalLayout_8;
-    QCheckBox *chk_no_fields;
-    QCheckBox *chk_no_field_models;
-    QHBoxLayout *horizontalLayout_9;
-    QCheckBox *chk_no_wm;
-    QCheckBox *chk_no_wm_models;
-    QHBoxLayout *horizontalLayout_5;
-    QCheckBox *chk_no_ffmpeg;
-    QCheckBox *chk_no_timidity;
-    QHBoxLayout *horizontalLayout_91;
+    QLabel *advancedLabelBattle;
+    QLabel *advancedLabelGame;
+    QLabel *advancedLabelMedia;
+    QCheckBox *chk_sounds;
+    QCheckBox *chk_music;
+    QLabel *advancedLabelFields;
+    QLabel *advancedLabelWorld;
+    QLabel *advancedLabelOther;
+    QCheckBox *chk_clean_install;
+    QCheckBox *chk_wm;
+    QCheckBox *chk_ffmpeg;
+    QCheckBox *chk_images;
+    QCheckBox *chk_battle_data;
+    QCheckBox *chk_wm_models;
+    QCheckBox *chk_timidity;
     QCheckBox *chk_keep_original;
-    QSpacerItem *verticalSpacer;
+    QCheckBox *chk_fields;
+    QCheckBox *chk_kernel;
+    QCheckBox *chk_field_models;
+    QCheckBox *chk_videos;
+    QCheckBox *chk_battle_models;
     QPushButton *btn_data_run;
     QProgressBar *data_progress_bar;
     QLabel *label_percent;
@@ -101,9 +101,14 @@ public:
     {
         if (MainWindow->objectName().isEmpty())
             MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
-        MainWindow->resize(720, 703);
+        MainWindow->resize(720, 705);
+        MainWindow->setMinimumSize(QSize(0, 0));
         actionExit = new QAction(MainWindow);
         actionExit->setObjectName(QString::fromUtf8("actionExit"));
+        actionABout = new QAction(MainWindow);
+        actionABout->setObjectName(QString::fromUtf8("actionABout"));
+        actionHelp = new QAction(MainWindow);
+        actionHelp->setObjectName(QString::fromUtf8("actionHelp"));
         centralWidget = new QWidget(MainWindow);
         centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
         verticalLayout = new QVBoxLayout(centralWidget);
@@ -115,6 +120,7 @@ public:
         tabWidget->setTabPosition(QTabWidget::TabPosition::North);
         tab_installer = new QWidget();
         tab_installer->setObjectName(QString::fromUtf8("tab_installer"));
+        tab_installer->setMinimumSize(QSize(698, 630));
         verticalLayout_3 = new QVBoxLayout(tab_installer);
         verticalLayout_3->setSpacing(6);
         verticalLayout_3->setContentsMargins(11, 11, 11, 11);
@@ -217,131 +223,139 @@ public:
 
         advancedOptions = new QGroupBox(tab_installer);
         advancedOptions->setObjectName(QString::fromUtf8("advancedOptions"));
-        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+        advancedOptions->setEnabled(true);
+        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
         sizePolicy.setHorizontalStretch(0);
         sizePolicy.setVerticalStretch(0);
         sizePolicy.setHeightForWidth(advancedOptions->sizePolicy().hasHeightForWidth());
         advancedOptions->setSizePolicy(sizePolicy);
-        advancedOptions->setMaximumSize(QSize(16777215, 0));
-        verticalLayout_5 = new QVBoxLayout(advancedOptions);
-        verticalLayout_5->setSpacing(5);
-        verticalLayout_5->setContentsMargins(11, 11, 11, 11);
-        verticalLayout_5->setObjectName(QString::fromUtf8("verticalLayout_5"));
-        verticalLayout_5->setContentsMargins(11, -1, -1, -1);
-        horizontalLayout_6 = new QHBoxLayout();
-        horizontalLayout_6->setSpacing(6);
-        horizontalLayout_6->setObjectName(QString::fromUtf8("horizontalLayout_6"));
-        chk_no_battle_data = new QCheckBox(advancedOptions);
-        chk_no_battle_data->setObjectName(QString::fromUtf8("chk_no_battle_data"));
-
-        horizontalLayout_6->addWidget(chk_no_battle_data);
-
-        chk_no_battle_models = new QCheckBox(advancedOptions);
-        chk_no_battle_models->setObjectName(QString::fromUtf8("chk_no_battle_models"));
-
-        horizontalLayout_6->addWidget(chk_no_battle_models);
-
-
-        verticalLayout_5->addLayout(horizontalLayout_6);
-
-        horizontalLayout_61 = new QHBoxLayout();
-        horizontalLayout_61->setSpacing(6);
-        horizontalLayout_61->setObjectName(QString::fromUtf8("horizontalLayout_61"));
-        chk_no_kernel = new QCheckBox(advancedOptions);
-        chk_no_kernel->setObjectName(QString::fromUtf8("chk_no_kernel"));
-
-        horizontalLayout_61->addWidget(chk_no_kernel);
-
-        chk_no_images = new QCheckBox(advancedOptions);
-        chk_no_images->setObjectName(QString::fromUtf8("chk_no_images"));
-
-        horizontalLayout_61->addWidget(chk_no_images);
-
-
-        verticalLayout_5->addLayout(horizontalLayout_61);
-
-        horizontalLayout_7 = new QHBoxLayout();
-        horizontalLayout_7->setSpacing(6);
-        horizontalLayout_7->setObjectName(QString::fromUtf8("horizontalLayout_7"));
-        chk_no_sounds = new QCheckBox(advancedOptions);
-        chk_no_sounds->setObjectName(QString::fromUtf8("chk_no_sounds"));
-
-        horizontalLayout_7->addWidget(chk_no_sounds);
-
-        chk_no_music = new QCheckBox(advancedOptions);
-        chk_no_music->setObjectName(QString::fromUtf8("chk_no_music"));
-
-        horizontalLayout_7->addWidget(chk_no_music);
-
-
-        verticalLayout_5->addLayout(horizontalLayout_7);
-
-        horizontalLayout_8 = new QHBoxLayout();
-        horizontalLayout_8->setSpacing(6);
-        horizontalLayout_8->setObjectName(QString::fromUtf8("horizontalLayout_8"));
-        chk_no_fields = new QCheckBox(advancedOptions);
-        chk_no_fields->setObjectName(QString::fromUtf8("chk_no_fields"));
-
-        horizontalLayout_8->addWidget(chk_no_fields);
-
-        chk_no_field_models = new QCheckBox(advancedOptions);
-        chk_no_field_models->setObjectName(QString::fromUtf8("chk_no_field_models"));
-
-        horizontalLayout_8->addWidget(chk_no_field_models);
-
-
-        verticalLayout_5->addLayout(horizontalLayout_8);
-
-        horizontalLayout_9 = new QHBoxLayout();
-        horizontalLayout_9->setSpacing(6);
-        horizontalLayout_9->setObjectName(QString::fromUtf8("horizontalLayout_9"));
-        chk_no_wm = new QCheckBox(advancedOptions);
-        chk_no_wm->setObjectName(QString::fromUtf8("chk_no_wm"));
-
-        horizontalLayout_9->addWidget(chk_no_wm);
-
-        chk_no_wm_models = new QCheckBox(advancedOptions);
-        chk_no_wm_models->setObjectName(QString::fromUtf8("chk_no_wm_models"));
-
-        horizontalLayout_9->addWidget(chk_no_wm_models);
-
-
-        verticalLayout_5->addLayout(horizontalLayout_9);
-
-        horizontalLayout_5 = new QHBoxLayout();
-        horizontalLayout_5->setSpacing(6);
-        horizontalLayout_5->setObjectName(QString::fromUtf8("horizontalLayout_5"));
-        chk_no_ffmpeg = new QCheckBox(advancedOptions);
-        chk_no_ffmpeg->setObjectName(QString::fromUtf8("chk_no_ffmpeg"));
-
-        horizontalLayout_5->addWidget(chk_no_ffmpeg);
-
-        chk_no_timidity = new QCheckBox(advancedOptions);
-        chk_no_timidity->setObjectName(QString::fromUtf8("chk_no_timidity"));
-
-        horizontalLayout_5->addWidget(chk_no_timidity);
-
-
-        verticalLayout_5->addLayout(horizontalLayout_5);
-
-        horizontalLayout_91 = new QHBoxLayout();
-        horizontalLayout_91->setSpacing(6);
-        horizontalLayout_91->setObjectName(QString::fromUtf8("horizontalLayout_91"));
+        advancedOptions->setMinimumSize(QSize(680, 180));
+        advancedOptions->setMaximumSize(QSize(16777215, 180));
+        advancedOptions->setAlignment(Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop);
+        advancedOptions->setFlat(false);
+        advancedOptions->setCheckable(false);
+        advancedLabelBattle = new QLabel(advancedOptions);
+        advancedLabelBattle->setObjectName(QString::fromUtf8("advancedLabelBattle"));
+        advancedLabelBattle->setGeometry(QRect(10, 10, 120, 20));
+        QFont font;
+        font.setBold(true);
+        font.setUnderline(true);
+        advancedLabelBattle->setFont(font);
+        advancedLabelGame = new QLabel(advancedOptions);
+        advancedLabelGame->setObjectName(QString::fromUtf8("advancedLabelGame"));
+        advancedLabelGame->setGeometry(QRect(10, 30, 120, 20));
+        advancedLabelGame->setFont(font);
+        advancedLabelMedia = new QLabel(advancedOptions);
+        advancedLabelMedia->setObjectName(QString::fromUtf8("advancedLabelMedia"));
+        advancedLabelMedia->setGeometry(QRect(10, 50, 120, 20));
+        advancedLabelMedia->setFont(font);
+        chk_sounds = new QCheckBox(advancedOptions);
+        chk_sounds->setObjectName(QString::fromUtf8("chk_sounds"));
+        chk_sounds->setGeometry(QRect(360, 50, 200, 20));
+        chk_sounds->setMinimumSize(QSize(200, 20));
+        chk_sounds->setMaximumSize(QSize(200, 20));
+        chk_sounds->setChecked(true);
+        chk_music = new QCheckBox(advancedOptions);
+        chk_music->setObjectName(QString::fromUtf8("chk_music"));
+        chk_music->setGeometry(QRect(150, 70, 200, 20));
+        chk_music->setMinimumSize(QSize(200, 20));
+        chk_music->setMaximumSize(QSize(200, 20));
+        chk_music->setChecked(true);
+        advancedLabelFields = new QLabel(advancedOptions);
+        advancedLabelFields->setObjectName(QString::fromUtf8("advancedLabelFields"));
+        advancedLabelFields->setGeometry(QRect(10, 110, 120, 20));
+        advancedLabelFields->setFont(font);
+        advancedLabelWorld = new QLabel(advancedOptions);
+        advancedLabelWorld->setObjectName(QString::fromUtf8("advancedLabelWorld"));
+        advancedLabelWorld->setGeometry(QRect(10, 130, 120, 20));
+        advancedLabelWorld->setFont(font);
+        advancedLabelOther = new QLabel(advancedOptions);
+        advancedLabelOther->setObjectName(QString::fromUtf8("advancedLabelOther"));
+        advancedLabelOther->setGeometry(QRect(10, 150, 120, 20));
+        advancedLabelOther->setFont(font);
+        chk_clean_install = new QCheckBox(advancedOptions);
+        chk_clean_install->setObjectName(QString::fromUtf8("chk_clean_install"));
+        chk_clean_install->setGeometry(QRect(360, 150, 200, 20));
+        chk_clean_install->setMinimumSize(QSize(200, 20));
+        chk_clean_install->setMaximumSize(QSize(200, 20));
+        chk_clean_install->setCheckable(true);
+        chk_wm = new QCheckBox(advancedOptions);
+        chk_wm->setObjectName(QString::fromUtf8("chk_wm"));
+        chk_wm->setGeometry(QRect(150, 130, 200, 20));
+        chk_wm->setMinimumSize(QSize(200, 20));
+        chk_wm->setMaximumSize(QSize(200, 20));
+        chk_wm->setChecked(true);
+        chk_wm->setTristate(false);
+        chk_ffmpeg = new QCheckBox(advancedOptions);
+        chk_ffmpeg->setObjectName(QString::fromUtf8("chk_ffmpeg"));
+        chk_ffmpeg->setGeometry(QRect(150, 90, 200, 20));
+        chk_ffmpeg->setMinimumSize(QSize(200, 20));
+        chk_ffmpeg->setMaximumSize(QSize(200, 20));
+        chk_ffmpeg->setChecked(true);
+        chk_images = new QCheckBox(advancedOptions);
+        chk_images->setObjectName(QString::fromUtf8("chk_images"));
+        chk_images->setGeometry(QRect(150, 50, 200, 20));
+        chk_images->setMinimumSize(QSize(200, 20));
+        chk_images->setMaximumSize(QSize(200, 20));
+        chk_images->setChecked(true);
+        chk_battle_data = new QCheckBox(advancedOptions);
+        chk_battle_data->setObjectName(QString::fromUtf8("chk_battle_data"));
+        chk_battle_data->setGeometry(QRect(150, 10, 200, 20));
+        chk_battle_data->setMinimumSize(QSize(200, 20));
+        chk_battle_data->setMaximumSize(QSize(200, 20));
+        chk_battle_data->setChecked(true);
+        chk_wm_models = new QCheckBox(advancedOptions);
+        chk_wm_models->setObjectName(QString::fromUtf8("chk_wm_models"));
+        chk_wm_models->setGeometry(QRect(360, 130, 200, 20));
+        chk_wm_models->setMinimumSize(QSize(200, 20));
+        chk_wm_models->setMaximumSize(QSize(200, 20));
+        chk_wm_models->setChecked(true);
+        chk_timidity = new QCheckBox(advancedOptions);
+        chk_timidity->setObjectName(QString::fromUtf8("chk_timidity"));
+        chk_timidity->setGeometry(QRect(360, 90, 200, 20));
+        chk_timidity->setMinimumSize(QSize(200, 20));
+        chk_timidity->setMaximumSize(QSize(200, 20));
+        chk_timidity->setChecked(true);
         chk_keep_original = new QCheckBox(advancedOptions);
         chk_keep_original->setObjectName(QString::fromUtf8("chk_keep_original"));
-
-        horizontalLayout_91->addWidget(chk_keep_original);
-
-
-        verticalLayout_5->addLayout(horizontalLayout_91);
-
+        chk_keep_original->setGeometry(QRect(150, 150, 200, 20));
+        chk_keep_original->setMinimumSize(QSize(200, 20));
+        chk_keep_original->setMaximumSize(QSize(200, 20));
+        chk_keep_original->setChecked(false);
+        chk_fields = new QCheckBox(advancedOptions);
+        chk_fields->setObjectName(QString::fromUtf8("chk_fields"));
+        chk_fields->setGeometry(QRect(150, 110, 200, 20));
+        chk_fields->setMinimumSize(QSize(200, 20));
+        chk_fields->setMaximumSize(QSize(200, 20));
+        chk_fields->setChecked(true);
+        chk_kernel = new QCheckBox(advancedOptions);
+        chk_kernel->setObjectName(QString::fromUtf8("chk_kernel"));
+        chk_kernel->setGeometry(QRect(150, 30, 200, 20));
+        chk_kernel->setMinimumSize(QSize(200, 20));
+        chk_kernel->setMaximumSize(QSize(200, 20));
+        chk_kernel->setChecked(true);
+        chk_field_models = new QCheckBox(advancedOptions);
+        chk_field_models->setObjectName(QString::fromUtf8("chk_field_models"));
+        chk_field_models->setGeometry(QRect(360, 110, 200, 20));
+        chk_field_models->setMinimumSize(QSize(200, 20));
+        chk_field_models->setMaximumSize(QSize(200, 20));
+        chk_field_models->setChecked(true);
+        chk_videos = new QCheckBox(advancedOptions);
+        chk_videos->setObjectName(QString::fromUtf8("chk_videos"));
+        chk_videos->setGeometry(QRect(360, 70, 200, 20));
+        chk_videos->setMinimumSize(QSize(200, 20));
+        chk_videos->setMaximumSize(QSize(200, 20));
+        chk_videos->setChecked(true);
+        chk_battle_models = new QCheckBox(advancedOptions);
+        chk_battle_models->setObjectName(QString::fromUtf8("chk_battle_models"));
+        chk_battle_models->setGeometry(QRect(360, 10, 200, 20));
+        chk_battle_models->setMinimumSize(QSize(200, 20));
+        chk_battle_models->setMaximumSize(QSize(200, 20));
+        chk_battle_models->setBaseSize(QSize(0, 0));
+        chk_battle_models->setChecked(true);
 
         verticalLayout_3->addWidget(advancedOptions);
 
-        verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Policy::Fixed, QSizePolicy::Minimum);
-
-        verticalLayout_3->addItem(verticalSpacer);
-
         btn_data_run = new QPushButton(tab_installer);
         btn_data_run->setObjectName(QString::fromUtf8("btn_data_run"));
 
@@ -364,10 +378,10 @@ public:
         label_percent->setSizePolicy(sizePolicy1);
         label_percent->setMinimumSize(QSize(0, 0));
         label_percent->setMaximumSize(QSize(16777215, 10));
-        QFont font;
-        font.setPointSize(8);
-        font.setItalic(true);
-        label_percent->setFont(font);
+        QFont font1;
+        font1.setPointSize(8);
+        font1.setItalic(true);
+        label_percent->setFont(font1);
         label_percent->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
 
         verticalLayout_3->addWidget(label_percent);
@@ -378,7 +392,7 @@ public:
         label_progress->setSizePolicy(sizePolicy1);
         label_progress->setMinimumSize(QSize(0, 0));
         label_progress->setMaximumSize(QSize(16777215, 10));
-        label_progress->setFont(font);
+        label_progress->setFont(font1);
         label_progress->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
 
         verticalLayout_3->addWidget(label_progress);
@@ -386,17 +400,17 @@ public:
         label_5 = new QLabel(tab_installer);
         label_5->setObjectName(QString::fromUtf8("label_5"));
         label_5->setMaximumSize(QSize(16777215, 15));
-        QFont font1;
-        font1.setFamily(QString::fromUtf8("Courier"));
-        font1.setPointSize(8);
-        label_5->setFont(font1);
+        QFont font2;
+        font2.setFamily(QString::fromUtf8("Courier"));
+        font2.setPointSize(8);
+        label_5->setFont(font2);
         label_5->setMargin(-1);
 
         verticalLayout_3->addWidget(label_5);
 
         data_log = new QTextEdit(tab_installer);
         data_log->setObjectName(QString::fromUtf8("data_log"));
-        data_log->setFont(font1);
+        data_log->setFont(font2);
         data_log->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
         data_log->setUndoRedoEnabled(false);
         data_log->setReadOnly(true);
@@ -477,8 +491,40 @@ public:
         menuFile = new QMenu(menuBar);
         menuFile->setObjectName(QString::fromUtf8("menuFile"));
         MainWindow->setMenuBar(menuBar);
+        QWidget::setTabOrder(tabWidget, line_data_src);
+        QWidget::setTabOrder(line_data_src, btn_data_src);
+        QWidget::setTabOrder(btn_data_src, help_data_src);
+        QWidget::setTabOrder(help_data_src, line_data_dst);
+        QWidget::setTabOrder(line_data_dst, btn_data_dst);
+        QWidget::setTabOrder(btn_data_dst, help_data_dest);
+        QWidget::setTabOrder(help_data_dest, chk_advanced_options);
+        QWidget::setTabOrder(chk_advanced_options, chk_battle_data);
+        QWidget::setTabOrder(chk_battle_data, chk_battle_models);
+        QWidget::setTabOrder(chk_battle_models, chk_kernel);
+        QWidget::setTabOrder(chk_kernel, chk_images);
+        QWidget::setTabOrder(chk_images, chk_sounds);
+        QWidget::setTabOrder(chk_sounds, chk_music);
+        QWidget::setTabOrder(chk_music, chk_videos);
+        QWidget::setTabOrder(chk_videos, chk_ffmpeg);
+        QWidget::setTabOrder(chk_ffmpeg, chk_timidity);
+        QWidget::setTabOrder(chk_timidity, chk_fields);
+        QWidget::setTabOrder(chk_fields, chk_field_models);
+        QWidget::setTabOrder(chk_field_models, chk_wm);
+        QWidget::setTabOrder(chk_wm, chk_wm_models);
+        QWidget::setTabOrder(chk_wm_models, chk_keep_original);
+        QWidget::setTabOrder(chk_keep_original, chk_clean_install);
+        QWidget::setTabOrder(chk_clean_install, btn_data_run);
+        QWidget::setTabOrder(btn_data_run, data_log);
+        QWidget::setTabOrder(data_log, line_vgears_exe);
+        QWidget::setTabOrder(line_vgears_exe, btn_vgears_exe);
+        QWidget::setTabOrder(btn_vgears_exe, line_vgears_config);
+        QWidget::setTabOrder(line_vgears_config, btn_vgears_config);
+        QWidget::setTabOrder(btn_vgears_config, btn_vgears_launch);
 
         menuBar->addAction(menuFile->menuAction());
+        menuFile->addAction(actionABout);
+        menuFile->addAction(actionHelp);
+        menuFile->addSeparator();
         menuFile->addAction(actionExit);
 
         retranslateUi(MainWindow);
@@ -494,11 +540,13 @@ public:
     {
         MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "VGears Installer", nullptr));
         actionExit->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr));
+        actionABout->setText(QCoreApplication::translate("MainWindow", "About", nullptr));
+        actionHelp->setText(QCoreApplication::translate("MainWindow", "Help", nullptr));
         label_4->setText(QCoreApplication::translate("MainWindow", "Final Fantasy VII ISO file: ", nullptr));
         btn_data_src->setText(QCoreApplication::translate("MainWindow", "...", nullptr));
 #if QT_CONFIG(tooltip)
         help_data_src->setToolTip(QCoreApplication::translate("MainWindow", "\n"
-"              <html><head/><body><b>Select a Final Fantasy VII ISO file.</b><br><br>It must contain (at least) the folders 'kernel' and 'field'.</body></html>\n"
+"              <html><head/><body><b>Path to the extracted FFVII (PC version) data.</b><br><br>It must contain (at least) the folders 'kernel' and 'field'.</body></html>\n"
 "             ", nullptr));
 #endif // QT_CONFIG(tooltip)
         help_data_src->setText(QString());
@@ -514,58 +562,72 @@ public:
         help_data_dest->setText(QString());
         chk_advanced_options->setText(QCoreApplication::translate("MainWindow", "Show advanced options", nullptr));
         advancedOptions->setTitle(QString());
+        advancedLabelBattle->setText(QCoreApplication::translate("MainWindow", "Battle data", nullptr));
+        advancedLabelGame->setText(QCoreApplication::translate("MainWindow", "Game data", nullptr));
+        advancedLabelMedia->setText(QCoreApplication::translate("MainWindow", "Multimedia", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_sounds->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:700;\">Don't extract sound.</span><br/><br/>If checked, game sound efects will not be extracted.<br/><br/>Sound extraction can take a few minutes, and it's ok to skip this step if it has already been installed.</p></body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_sounds->setText(QCoreApplication::translate("MainWindow", "Don't extract sounds", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_battle_data->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract battle data.</b><br><br>If checked, battle data such as enemies, attacks or enemy formations will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.</body></html>", nullptr));
+        chk_music->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:700;\">Don't extract music.</span><br/><br/>If checked, game music tracks will not be extracted.<br/><br/>Music extraction can take a few minutes, and it's ok to skip this step if it has already been installed.</p></body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_battle_data->setText(QCoreApplication::translate("MainWindow", "Don't extract battle data", nullptr));
+        chk_music->setText(QCoreApplication::translate("MainWindow", "Don't extract music", nullptr));
+        advancedLabelFields->setText(QCoreApplication::translate("MainWindow", "Field Maps", nullptr));
+        advancedLabelWorld->setText(QCoreApplication::translate("MainWindow", "World Map", nullptr));
+        advancedLabelOther->setText(QCoreApplication::translate("MainWindow", "Other", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_battle_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract battle models.</b><br><br>If checked, battle 3D models will not be extracted. This includes models for players, enemies, attacks, battle scenarios... Models from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the models.</body></html>", nullptr));
+        chk_clean_install->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:700;\">Delete previously installed files.</span></p><p><br/></p><p>Performs a clean installation, unconditionally removing all previously installed data.</p></body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_battle_models->setText(QCoreApplication::translate("MainWindow", "Don't extract battle models", nullptr));
+        chk_clean_install->setText(QCoreApplication::translate("MainWindow", "Clean install", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_kernel->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract kernel data.</b><br><br>If checked, game data such as items, materia, character information or initial savemap will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.</body></html>", nullptr));
+        chk_wm->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract world map data.</b><br><br>If checked, world map models and data will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the world map data. Skipping world map data installation will also skip the installation of the world map 3D models.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_kernel->setText(QCoreApplication::translate("MainWindow", "Don't extract kernel data", nullptr));
+        chk_wm->setText(QCoreApplication::translate("MainWindow", "Don't extract world map data", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_images->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract images.</b><br><br>If checked, images will not be extracted. This includes window decorations, menus icons, character portraits... It doesn' include backgrounds or textures. Images from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the images.</body></html>", nullptr));
+        chk_ffmpeg->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't use the ffmpeg command.</b><br><br>If checked, sound effects and background music will not be available to V-Gears.<br><br>Sound effects and music indexes will still be built, but you will need to provide your own ogg files.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_images->setText(QCoreApplication::translate("MainWindow", "Don't extract images", nullptr));
+        chk_ffmpeg->setText(QCoreApplication::translate("MainWindow", "Prevent FFmpeg calls", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_sounds->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract sounds.</b><br><br>If checked, sound effects will not be extracted. This doesn' include music tracks. Sounds from previous installations will not be deleted.<br><br>Installing sounds can take a few minutes, and it's OK to skip this step if they are already installed. You can also check this if you are getting errors installing sounds or if FFmpeg is not installed in your system.</body></html>", nullptr));
+        chk_images->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract images.</b><br><br>If checked, images will not be extracted. This includes window decorations, menus icons, character portraits... It doesn' include backgrounds or textures. Images from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the images.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_sounds->setText(QCoreApplication::translate("MainWindow", "Don't extract sounds", nullptr));
+        chk_images->setText(QCoreApplication::translate("MainWindow", "Don't extract images", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_music->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract audio tracks.</b><br><br>If checked, audio tracks will not be extracted. This doesn' include sound effects. Tracks from previous installations will not be deleted.<br><br>Installing music can take a few minutes, and it's OK to skip this step if they are already installed. You can also check this if you are getting errors installing sounds or if FFmpeg or TiMidity are not installed in your system.</body></html>", nullptr));
+        chk_battle_data->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract battle data.</b><br><br>If checked, battle data such as enemies, attacks or enemy formations will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_music->setText(QCoreApplication::translate("MainWindow", "Don't extract music tracks", nullptr));
+        chk_battle_data->setText(QCoreApplication::translate("MainWindow", "Don't extract battle data", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_fields->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract field maps.</b><br><br>If checked, map fields will not be extracted. Field maps from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the fields. Skipping field map installation will also skip the installation of field 3D models.</body></html>", nullptr));
+        chk_wm_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract world map 3D models.</b><br><br>If checked, world map 3D models and textures will not be extracted. Models from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping world map data installation will also skip the installation of world map 3D models.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_fields->setText(QCoreApplication::translate("MainWindow", "Don't extract field maps", nullptr));
+        chk_wm_models->setText(QCoreApplication::translate("MainWindow", "Dont't extract world 3D models", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_field_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract field 3D models.</b><br><br>If checked, field 3D models and textures will not be extracted. Models from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping field map installation will also skip the installation of field 3D models.</body></html>", nullptr));
+        chk_timidity->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't use the timidity command.</b><br><br>If checked, background music will not be available to V-Gears.<br><br>Music indexes will still be built, but you will need to provide your own ogg tracks.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_field_models->setText(QCoreApplication::translate("MainWindow", "Dont't extract field 3D models", nullptr));
+        chk_timidity->setText(QCoreApplication::translate("MainWindow", "Prevent TiMidity calls", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_wm->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract world map data.</b><br><br>If checked, world map models and data will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the world map data. Skipping world map data installation will also skip the installation of the world map 3D models.</body></html>", nullptr));
+        chk_keep_original->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't delete original data after installation.</b><br><br>The installer extracts some original data from the installation disk, such as MIDI sounds, TEX images, and some LGP archives. If checked, this data will not be deleted when the installation is complete.<br><br>This data is never used by V-Gears, and there is usually no need to check this.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_wm->setText(QCoreApplication::translate("MainWindow", "Don't extract world map data", nullptr));
+        chk_keep_original->setText(QCoreApplication::translate("MainWindow", "Preserve original data", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_wm_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract world map 3D models.</b><br><br>If checked, world map 3D models and textures will not be extracted. Models from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping world map data installation will also skip the installation of world map 3D models.</body></html>", nullptr));
+        chk_fields->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract field maps.</b><br><br>If checked, map fields will not be extracted. Field maps from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the fields. Skipping field map installation will also skip the installation of field 3D models.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_wm_models->setText(QCoreApplication::translate("MainWindow", "Dont't extract world 3D models", nullptr));
+        chk_fields->setText(QCoreApplication::translate("MainWindow", "Don't extract field maps", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_ffmpeg->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't use the ffmpeg command.</b><br><br>If checked, sound effects and background music will not be available to V-Gears.<br><br>Sound effects and music indexes will still be built, but you will need to provide your own ogg files.</body></html>", nullptr));
+        chk_kernel->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract kernel data.</b><br><br>If checked, game data such as items, materia, character information or initial savemap will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_ffmpeg->setText(QCoreApplication::translate("MainWindow", "Prevent FFmpeg calls", nullptr));
+        chk_kernel->setText(QCoreApplication::translate("MainWindow", "Don't extract kernel data", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_no_timidity->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't use the timidity command.</b><br><br>If checked, background music will not be available to V-Gears.<br><br>Music indexes will still be built, but you will need to provide your own ogg tracks.</body></html>", nullptr));
+        chk_field_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract field 3D models.</b><br><br>If checked, field 3D models and textures will not be extracted. Models from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping field map installation will also skip the installation of field 3D models.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_no_timidity->setText(QCoreApplication::translate("MainWindow", "Prevent TiMidity calls", nullptr));
+        chk_field_models->setText(QCoreApplication::translate("MainWindow", "Dont't extract field 3D models", nullptr));
 #if QT_CONFIG(tooltip)
-        chk_keep_original->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't delete original data after installation.</b><br><br>The installer extracts some original data from the installation disk, such as MIDI sounds, TEX images, and some LGP archives. If checked, this data will not be deleted when the installation is complete.<br><br>This data is never used by V-Gears, and there is usually no need to check this.</body></html>", nullptr));
+        chk_videos->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract videos.</b><br><br>If checked, FMV videos will not be extracted. Videos from previous installations will not be deleted.<br><br>Installing videos can take a few minutes, and it's OK to skip this step if they are already installed.</body></html>", nullptr));
 #endif // QT_CONFIG(tooltip)
-        chk_keep_original->setText(QCoreApplication::translate("MainWindow", "Preserve original data", nullptr));
+        chk_videos->setText(QCoreApplication::translate("MainWindow", "Don't extract videos", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_battle_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract battle models.</b><br><br>If checked, battle 3D models will not be extracted. This includes models for players, enemies, attacks, battle scenarios... Models from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the models.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_battle_models->setText(QCoreApplication::translate("MainWindow", "Don't extract battle models", nullptr));
         btn_data_run->setText(QCoreApplication::translate("MainWindow", "Install data", nullptr));
         label_percent->setText(QString());
         label_progress->setText(QString());

+ 12 - 0
v-gears-installer.desktop

@@ -0,0 +1,12 @@
+[Desktop Entry]
+Type=Application
+Version=1.0
+Name=V-Gears Installer
+GenericName=V-Gears Installer
+Comment=Install V-Gears game data
+Exec=v-gears-installer
+Icon=v-gears-installer
+StartupWMClass=v-gears-installer
+Terminal=false
+StartupNotify=true
+Categories=Game;Utility;

BIN
v-gears-installer.png


BIN
v-gears.png