Explorar el Código

Worls map models extracted from the ISO. The installer can now run untill the end using an ISO file. Added icon and menu entries to the installer.

Iñigo Valentin hace 1 mes
padre
commit
15437fc9b1

+ 17 - 0
CMakeLists.txt

@@ -58,4 +58,21 @@ if(BUILD_TESTS)
     add_subdirectory(test)
     add_subdirectory(test)
 endif()
 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...)
 # TODO: Generate Installer (.msi, .deb, .appImage...)

+ 2 - 0
debian/install

@@ -1,4 +1,6 @@
 v-gears.desktop /usr/share/applications
 v-gears.desktop /usr/share/applications
 v-gears.png /usr/share/pixmaps
 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
 v-gears-launcher /usr/bin
 output/resources.cfg /usr/share/v-gears
 output/resources.cfg /usr/share/v-gears

+ 64 - 8
src/data/VGearsHRCMeshLoader.cpp

@@ -14,6 +14,7 @@
  */
  */
 
 
 #include <OgreMesh.h>
 #include <OgreMesh.h>
+#include <vector>
 #include "data/VGearsHRCMeshLoader.h"
 #include "data/VGearsHRCMeshLoader.h"
 #include "common/VGearsStringUtil.h"
 #include "common/VGearsStringUtil.h"
 #include "data/VGearsPFile.h"
 #include "data/VGearsPFile.h"
@@ -23,6 +24,49 @@
 
 
 namespace VGears{
 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::Bone Bone;
 
 
     typedef HRCFile::BoneList BoneList;
     typedef HRCFile::BoneList BoneList;
@@ -52,21 +96,33 @@ namespace VGears{
         const String &bone_name(bone.name);
         const String &bone_name(bone.name);
         RSDNameList::const_iterator it(bone.rsd_names.begin());
         RSDNameList::const_iterator it(bone.rsd_names.begin());
         RSDNameList::const_iterator end(bone.rsd_names.end());
         RSDNameList::const_iterator end(bone.rsd_names.end());
-        String rsd_base, rsdfile_name_, pfile_name_;
+        String rsd_base, rsdfile_name_;
         while (it != end){
         while (it != end){
             rsd_base = path + *it;
             rsd_base = path + *it;
             StringUtil::toLowerCase(rsd_base);
             StringUtil::toLowerCase(rsd_base);
             rsdfile_name_ = rsd_base + EXT_RSD;
             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);
             assert(rsd_file != nullptr);
-            pfile_name_ = path + rsd_file->GetPolygonName();
+            String pfile_name_ = path + rsd_file->GetPolygonName();
             StringUtil::toLowerCase(pfile_name_);
             StringUtil::toLowerCase(pfile_name_);
             pfile_name_ = StringUtil::replaceAll(pfile_name_, EXT_PLY, EXT_P);
             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);
             assert(p_file != nullptr);
             p_file->AddGroups(mesh, bone_name, rsd_file);
             p_file->AddGroups(mesh, bone_name, rsd_file);
             ++ it;
             ++ it;

+ 3 - 1
src/installer/CMakeLists.txt

@@ -110,6 +110,8 @@ set(v-gears-installer_UIS MainWindow.ui)
 QT5_WRAP_UI(UIS ${v-gears-installer_UIS})
 QT5_WRAP_UI(UIS ${v-gears-installer_UIS})
 set(v-gears-installer_MOCS MainWindow.h)
 set(v-gears-installer_MOCS MainWindow.h)
 QT5_WRAP_CPP(MOCS ${v-gears-installer_MOCS})
 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.
 # Compiler options.
@@ -119,7 +121,7 @@ endif()
 
 
 
 
 # Generate v-gears-installer executable.
 # 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")
 SET_PROPERTY(TARGET v-gears-installer PROPERTY FOLDER "build/v-gears-installer")
 if (APPLE)
 if (APPLE)
     target_link_libraries(v-gears-installer "-framework CoreFoundation -framework Cocoa -framework IOKit")
     target_link_libraries(v-gears-installer "-framework CoreFoundation -framework Cocoa -framework IOKit")

+ 3 - 6
src/installer/DataInstaller.cpp

@@ -280,8 +280,7 @@ float DataInstaller::Progress(){
             cur_substep_ = 0;
             cur_substep_ = 0;
             return CalcProgress();
             return CalcProgress();
         case MEDIA_SOUNDS:
         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_ ++;
             else cur_substep_ ++;
             return CalcProgress();
             return CalcProgress();
         case MEDIA_SOUNDS_INDEX:
         case MEDIA_SOUNDS_INDEX:
@@ -302,8 +301,7 @@ float DataInstaller::Progress(){
             cur_substep_ = 0;
             cur_substep_ = 0;
             return CalcProgress();
             return CalcProgress();
         case MEDIA_MUSICS:
         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_ ++;
             else cur_substep_ ++;
             return CalcProgress();
             return CalcProgress();
         case MEDIA_MUSICS_HQ:
         case MEDIA_MUSICS_HQ:
@@ -409,8 +407,7 @@ float DataInstaller::Progress(){
             }
             }
             return CalcProgress();
             return CalcProgress();
         case WM_MODELS:
         case WM_MODELS:
-            if (options_.skip_wm_models)
-                write_output_line_("Skipping world map model installation...", 2, true);
+            if (options_.skip_wm_models) write_output_line_("Skipping world map model installation...", 2, true);
             else world_installer_->ProcessModels();
             else world_installer_->ProcessModels();
             installation_state_ = CLEAN;
             installation_state_ = CLEAN;
             return CalcProgress();
             return CalcProgress();

+ 48 - 107
src/installer/FieldDataInstaller.cpp

@@ -52,8 +52,7 @@ std::string FieldDataInstaller::CreateGateWayScript(
       "    on_near = function(self, entity)\n"
       "    on_near = function(self, entity)\n"
       "        if entity == \"Cloud\" then\n"
       "        if entity == \"Cloud\" then\n"
       "            if not Data.DisableGateways 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"
       "        end\n"
       "        end\n"
       "        return 0\n"
       "        return 0\n"
@@ -67,8 +66,9 @@ std::string FieldDataInstaller::CreateGateWayScript(
 size_t FieldDataInstaller::GetFieldId(
 size_t FieldDataInstaller::GetFieldId(
   const std::string& name, const std::vector<std::string>& field_id_to_name_lookup
   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;
         if (field_id_to_name_lookup[i] == name) return i;
+    }
     throw std::runtime_error("No Id found for field name");
     throw std::runtime_error("No Id found for field name");
 }
 }
 
 
@@ -95,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 == "coloin1" // boost::bad_format_string: format-string is ill-formed
       || resource_name == "del3" // Segmentation fault.
       || resource_name == "del3" // Segmentation fault.
       || resource_name == "elmin4_2" // boost::bad_format_string: format-string is ill-formed
       || resource_name == "elmin4_2" // boost::bad_format_string: format-string is ill-formed
-    ){return true;}
+    ) return true;
     return false;
     return false;
 }
 }
 
 
@@ -138,7 +138,7 @@ bool FieldDataInstaller::IsTestField(const Ogre::String& resource_name){
       || resource_name == "mds7_w2"
       || resource_name == "mds7_w2"
       || resource_name == "mds7_w3"
       || resource_name == "mds7_w3"
       || resource_name == "mds7"
       || resource_name == "mds7"
-    ){return true;}
+    )return true;
     return false;
     return false;
 }
 }
 
 
@@ -266,11 +266,8 @@ void FieldDataInstaller::Convert(int field_index){
             PcFieldToVGearsField(field);
             PcFieldToVGearsField(field);
         }
         }
         else{
         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;
         }
         }
     }
     }
 }
 }
@@ -323,9 +320,7 @@ std::vector<std::string> FieldDataInstaller::ConvertModelsInit(){
         out.close();
         out.close();
         //field_model_file_list_->push_back(f.file_name);
         //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);
         models.push_back(it->first);
     }
     }
     return models;
     return models;
@@ -351,16 +346,12 @@ void FieldDataInstaller::ConvertModels(std::string model_name){
         ExportMesh(output_dir_ + FIELD_MODELS_DIR + "/", mesh);
         ExportMesh(output_dir_ + FIELD_MODELS_DIR + "/", mesh);
     }
     }
     catch (const Ogre::Exception& ex){
     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){
     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;
     }
     }
 }
 }
 
 
@@ -373,9 +364,7 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
     Ogre::MeshSerializer mesh_serializer;
     Ogre::MeshSerializer mesh_serializer;
     Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
     Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
     Ogre::SkeletonSerializer skeleton_serializer;
     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->setSkeletonName(base_mesh_name + ".skeleton");
     mesh_serializer.exportMesh(mesh.getPointer(), outdir + mesh->getName());
     mesh_serializer.exportMesh(mesh.getPointer(), outdir + mesh->getName());
     Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
     Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
@@ -399,25 +388,18 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
                               texture_unit_num < pass->getNumTextureUnitStates();
                               texture_unit_num < pass->getNumTextureUnitStates();
                               texture_unit_num ++
                               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){
                                 if (unit && unit->getTextureName().empty() == false){
                                     // Convert the texture from .tex to .png.
                                     // 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
                                     // Ensure the output material script references png files
                                     // rather than tex files.
                                     // rather than tex files.
                                     Ogre::String base_name;
                                     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");
                                     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
                                     // Copy subtexture (xxxx.png) to model_xxxx.png
                                     // TODO: obtain the "data" folder
                                     // TODO: obtain the "data" folder
                                     // programatically.
                                     // programatically.
@@ -446,8 +428,8 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
     for (auto& texture_name : textures){
     for (auto& texture_name : textures){
         std::string tex_name = texture_name.c_str();
         std::string tex_name = texture_name.c_str();
         try{
         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;
             Ogre::Image image;
             texture_ptr->convertToImage(image);
             texture_ptr->convertToImage(image);
             Ogre::String base_name;
             Ogre::String base_name;
@@ -462,8 +444,7 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
 
 
 float FieldDataInstaller::GetFieldScaleFactor(size_t field_id){
 float FieldDataInstaller::GetFieldScaleFactor(size_t field_id){
     auto it = scale_factors_.find(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;
     return it->second;
 }
 }
 
 
@@ -497,8 +478,7 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
         if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
         if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
             const std::string gateway_entity_name = "Gateway" + std::to_string(i);
             const std::string gateway_entity_name = "Gateway" + std::to_string(i);
             gateway_script_data += CreateGateWayScript(
             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)
               "Spawn_" + field->getName() + "_" + std::to_string(i)
             );
             );
         }
         }
@@ -512,39 +492,28 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
 
 
         // Decompile to LUA.
         // Decompile to LUA.
         decompiled = FieldDecompiler::Decompile(
         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()){
         if (script_file.is_open()){
             script_file << decompiled.luaScript;
             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());}
             try{field_text_writer_.Write(raw_field_data, field->getName());}
             catch (const std::out_of_range& ex){
             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 "
                 std::cerr << "[ERROR] Failed to read texts from field "
                   << field->getName() << ": " << ex.what() << std::endl;
                   << field->getName() << ": " << ex.what() << std::endl;
             }
             }
             field_text_writer_.End();
             field_text_writer_.End();
         }
         }
         else{
         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 "
             std::cerr << "[ERROR] Failed to open script file from field "
               << field->getName() << "for writing." << std::endl;
               << field->getName() << "for writing." << std::endl;
         }
         }
     }
     }
     catch (const ::DecompilerException& ex){
     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 "
         std::cerr << "[ERROR] Internal decompiler error in field "
           << field->getName() << ": " << ex.what() << std::endl;
           << field->getName() << ": " << ex.what() << std::endl;
     }
     }
@@ -606,17 +575,13 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                         + std::to_string(spawn_point_records[i].gateway_index_or_map_jump_address)
                         + 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
                 // 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
                 // complex. Now "get the Z value of the triangle with that ID". Note that ID
                 // actually just means index.
                 // actually just means index.
                 unsigned int triangle_index = static_cast<unsigned int>(gateway.destination.z);
                 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_(
                     write_output_line_(
                       "[WARNING] In field " + field->getName() + ": Map jump triangle ("
                       "[WARNING] In field " + field->getName() + ": Map jump triangle ("
                       + std::to_string(triangle_index) + ") out of bounds ("
                       + std::to_string(triangle_index) + ") out of bounds ("
@@ -627,17 +592,15 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                       << field->GetWalkmesh()->GetTriangles().size() << ")" << std::endl;
                       << field->GetWalkmesh()->GetTriangles().size() << ")" << std::endl;
                     triangle_index = 0;
                     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(
                 const Ogre::Vector3 position(
                   gateway.destination.x / downscaler_next, gateway.destination.y / downscaler_next,
                   gateway.destination.x / downscaler_next, gateway.destination.y / downscaler_next,
                   z_of_triangle_with_id
                   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;
                     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;
                 const float rotation = (360.0f * static_cast<float>(gateway.dir)) / 255.0f;
                 xml_entity_point->SetAttribute("rotation", std::to_string(rotation));
                 xml_entity_point->SetAttribute("rotation", std::to_string(rotation));
                 element->LinkEndChild(xml_entity_point.release());
                 element->LinkEndChild(xml_entity_point.release());
@@ -671,19 +634,16 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
         for (FieldDecompiler::FieldEntity entity : decompiled.entities){
         for (FieldDecompiler::FieldEntity entity : decompiled.entities){
             // If the entity has been added as a line, skip.
             // If the entity has been added as a line, skip.
             if (line_entities.size() > 0){
             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;
             const int char_id = entity.char_id;
             if (char_id != -1){
             if (char_id != -1){
                 const VGears::ModelListFile::ModelDescription& desc
                 const VGears::ModelListFile::ModelDescription& desc
                   = models->GetModels().at(char_id);
                   = models->GetModels().at(char_id);
                 auto& animations = used_models_and_anims_.ModelAnimations(desc.hrc_name);
                 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));
                     animations.insert(used_models_and_anims_.NormalizeAnimationName(anim.name));
+                }
                 std::unique_ptr<TiXmlElement> xml_entity_script(new TiXmlElement("entity_model"));
                 std::unique_ptr<TiXmlElement> xml_entity_script(new TiXmlElement("entity_model"));
                 xml_entity_script->SetAttribute("name", entity.name);
                 xml_entity_script->SetAttribute("name", entity.name);
                 // TODO: Add to list of HRC's to convert, obtain name of converted .mesh file.
                 // TODO: Add to list of HRC's to convert, obtain name of converted .mesh file.
@@ -697,22 +657,16 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                     // For player models the position is set manually in the xml because if a map
                     // 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
                     // is loaded manually this is where the player will end up. Hence we set the
                     // position to the first entity_point that we have.
                     // 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{
                 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");
                 xml_entity_script->SetAttribute("direction", "0");
                 // TODO: This isn't quite right, the models animation
                 // TODO: This isn't quite right, the models animation
                 // translation seems to be inverted?
                 // translation seems to be inverted?
                 xml_entity_script->SetAttribute("scale", "0.03125 0.03125 0.03125");
                 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());
                 element->LinkEndChild(xml_entity_script.release());
             }
             }
             else{
             else{
@@ -726,26 +680,20 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
             const VGears::TriggersFile::Gateway& gateway = gateways[i];
             const VGears::TriggersFile::Gateway& gateway = gateways[i];
             // If non-inactive gateway:
             // If non-inactive gateway:
             if (gateway.destination_field_id != INACTIVE_GATEWAY_ID){
             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("name", "Gateway" + std::to_string(i));
                 xml_entity_trigger->SetAttribute(
                 xml_entity_trigger->SetAttribute(
                   "point1",
                   "point1",
                   Ogre::StringConverter::toString(
                   Ogre::StringConverter::toString(
                     Ogre::Vector3(
                     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(
                 xml_entity_trigger->SetAttribute(
                   "point2",
                   "point2",
                   Ogre::StringConverter::toString(
                   Ogre::StringConverter::toString(
                     Ogre::Vector3(
                     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.
                 // Enabled is hard coded to true.
@@ -784,11 +732,9 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
             const int width = bg_image->getWidth();
             const int width = bg_image->getWidth();
             const int height = bg_image->getHeight();
             const int height = bg_image->getHeight();
             const VGears::CameraMatrixFilePtr& camera_matrix = field->GetCameraMatrix();
             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::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_x = triggers->GetCameraRange().left * BG_SCALE_UP_FACTOR;
             const int min_y = triggers->GetCameraRange().top * BG_SCALE_UP_FACTOR;
             const int min_y = triggers->GetCameraRange().top * BG_SCALE_UP_FACTOR;
             const int max_x = triggers->GetCameraRange().right * BG_SCALE_UP_FACTOR;
             const int max_x = triggers->GetCameraRange().right * BG_SCALE_UP_FACTOR;
@@ -834,15 +780,12 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                     const float v1 = static_cast<float>(sprite.src.y + sprite.height) / height;
                     const float v1 = static_cast<float>(sprite.src.y + sprite.height) / height;
                     xml_element->SetAttribute(
                     xml_element->SetAttribute(
                       "uv",
                       "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?
                     // TODO: It works (on FFVII PC), but why this number?
                     xml_element->SetAttribute(
                     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.
                     // TODO: Copied from DatFile::AddTile.
                     // Add to common method.
                     // Add to common method.
@@ -859,9 +802,7 @@ void FieldDataInstaller::PcFieldToVGearsField(VGears::FLevelFilePtr& field){
                 //}
                 //}
             }
             }
             bg_doc.LinkEndChild(bg_element.release());
             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");
         }
         }
     }
     }
     {
     {

+ 89 - 111
src/installer/WorldInstaller.cpp

@@ -23,6 +23,7 @@
 #include "WorldInstaller.h"
 #include "WorldInstaller.h"
 #include "TexFile.h"
 #include "TexFile.h"
 #include "common/Lzs.h"
 #include "common/Lzs.h"
+#include "common/FinalFantasy7/FF7NameLookup.h"
 #include "common/VGearsStringUtil.h"
 #include "common/VGearsStringUtil.h"
 #include "data/VGearsLGPArchive.h"
 #include "data/VGearsLGPArchive.h"
 #include "data/WorldMapWalkmesh.h"
 #include "data/WorldMapWalkmesh.h"
@@ -341,7 +342,6 @@ void WorldInstaller::GenerateMaterials(){
         Ogre::Technique* tech = mat->getTechnique(0);
         Ogre::Technique* tech = mat->getTechnique(0);
         Ogre::Pass* pass = tech->getPass(0);
         Ogre::Pass* pass = tech->getPass(0);
         pass->setDiffuse(0.7, 0.7, 0.7, 0.7);
         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");
         mSer.exportMaterial(mat, output_dir_ + "models/world/" + t.name + ".material");
     }
     }
 }
 }
@@ -349,74 +349,60 @@ void WorldInstaller::GenerateMaterials(){
 void WorldInstaller::ProcessModels(){
 void WorldInstaller::ProcessModels(){
     // Open world_us.lgp
     // Open world_us.lgp
     File world_file(*disk_image_.fileExists("data/wm/world_us.lgp"));
     File world_file(*disk_image_.fileExists("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_.fileExists("data/wm/world_us.lgp"), "LGP");
     VGears::LGPArchive world_lgp(*disk_image_.fileExists("data/wm/world_us.lgp"), "LGP");
     world_lgp.open(*disk_image_.fileExists("data/wm/world_us.lgp"), true);
     world_lgp.open(*disk_image_.fileExists("data/wm/world_us.lgp"), true);
     world_lgp.load();
     world_lgp.load();
     VGears::LGPArchive::FileList file_list = world_lgp.GetFiles();
     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 ++){
     for (int i = 0; i < file_list.size(); i ++){
         VGears::LGPArchive::FileEntry f = file_list.at(i);
         VGears::LGPArchive::FileEntry f = file_list.at(i);
         if (f.data_offset + f.data_size <= world_file.GetFileSize()){
         if (f.data_offset + f.data_size <= world_file.GetFileSize()){
             File w_lgp_file(&world_file, f.data_offset, f.data_size);
             File w_lgp_file(&world_file, f.data_offset, f.data_size);
             std::string file_name = f.file_name;
             std::string file_name = f.file_name;
+            VGears::StringUtil::toLowerCase(file_name);
             w_lgp_file.WriteFile(output_dir_ + "temp/world_models/" + 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(){
 bool WorldInstaller::ProcessMap(){
-    //std::cout << "[WI] Processing map " << processed_maps_ << "/" << wm_map_.size() << std::endl;
     if (processed_maps_ >= wm_map_.size()) return true;
     if (processed_maps_ >= wm_map_.size()) return true;
     // Initialize the xml file.
     // Initialize the xml file.
     TiXmlDocument doc;
     TiXmlDocument doc;
@@ -439,7 +425,7 @@ bool WorldInstaller::ProcessMap(){
     xml->LinkEndChild(xml_walkmesh.release());
     xml->LinkEndChild(xml_walkmesh.release());
     // Terrain (don't close yet).
     // Terrain (don't close yet).
     std::unique_ptr<TiXmlElement> xml_terrain(new TiXmlElement("terrain"));
     std::unique_ptr<TiXmlElement> xml_terrain(new TiXmlElement("terrain"));
-
+    
     // Initialize the walkmesh data.
     // Initialize the walkmesh data.
     WorldMapWalkmesh walkmesh(processed_maps_, processed_maps_ == 0 ? true : false);
     WorldMapWalkmesh walkmesh(processed_maps_, processed_maps_ == 0 ? true : false);
 
 
@@ -463,8 +449,7 @@ bool WorldInstaller::ProcessMap(){
             u32 mesh_size = wm_map_[processed_maps_].readU32LE();
             u32 mesh_size = wm_map_[processed_maps_].readU32LE();
             // Add size to lzss
             // Add size to lzss
             wm_map_[processed_maps_].SetOffset(mesh_offset);
             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());
             for (int d = 0; d < mesh_size ; d ++) lzss.push_back(wm_map_[processed_maps_].readU8());
             // Extract the lzss file.
             // Extract the lzss file.
             std::vector<unsigned char> data = Lzs::Decompress(lzss);
             std::vector<unsigned char> data = Lzs::Decompress(lzss);
@@ -478,8 +463,7 @@ bool WorldInstaller::ProcessMap(){
                 if (processed_maps_ >= wm_map_.size()) return true;
                 if (processed_maps_ >= wm_map_.size()) return true;
                 else return false;
                 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();
             dat.close();
 
 
             // Open and read the decompressed file.
             // Open and read the decompressed file.
@@ -587,9 +571,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[1] << ", "
                 //  << (int) map.blocks[b].mesh[m].triangles[t].vertex_index[2] << std::endl;
                 //  << (int) map.blocks[b].mesh[m].triangles[t].vertex_index[2] << std::endl;
                 // TODO: El index deberia incrementar por triangulo por cada material
                 // 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(
                 man.triangle(
                   map.blocks[b].mesh[m].triangles[t].vertex_index[0],
                   map.blocks[b].mesh[m].triangles[t].vertex_index[0],
                   map.blocks[b].mesh[m].triangles[t].vertex_index[1],
                   map.blocks[b].mesh[m].triangles[t].vertex_index[1],
@@ -604,8 +586,7 @@ bool WorldInstaller::ProcessMap(){
         //  << std::endl;
         //  << std::endl;
         mesh_serializer.exportMesh(
         mesh_serializer.exportMesh(
           mesh.getPointer(),
           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
         // 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
         // 68 blocks but only 63 are used at a time
@@ -613,12 +594,9 @@ bool WorldInstaller::ProcessMap(){
             std::unique_ptr<TiXmlElement> xml_block(new TiXmlElement("block"));
             std::unique_ptr<TiXmlElement> xml_block(new TiXmlElement("block"));
             xml_block->SetAttribute("index", std::to_string(b));
             xml_block->SetAttribute("index", std::to_string(b));
             xml_block->SetAttribute(
             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){
             if (processed_maps_ == 0){
                 // TODO: Correct history points for each block.
                 // TODO: Correct history points for each block.
                 switch (b){
                 switch (b){
@@ -657,8 +635,7 @@ bool WorldInstaller::ProcessMap(){
     doc.LinkEndChild(xml.release());
     doc.LinkEndChild(xml.release());
     // TODO: Add models, entities...
     // TODO: Add models, entities...
     doc.SaveFile(
     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
     // Generate the walkmesh xml file
     walkmesh.generate(output_dir_ + "/world/" + std::to_string(processed_maps_) + "/wm.xml");
     walkmesh.generate(output_dir_ + "/world/" + std::to_string(processed_maps_) + "/wm.xml");
@@ -671,26 +648,33 @@ bool WorldInstaller::ProcessMap(){
 
 
 void WorldInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh){
 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
     // TODO: Share function with pc model exporter
     VGears::String base_mesh_name;
     VGears::String base_mesh_name;
     VGears::StringUtil::splitFull(mesh->getName(), base_mesh_name);
     VGears::StringUtil::splitFull(mesh->getName(), base_mesh_name);
     Ogre::MeshSerializer mesh_serializer;
     Ogre::MeshSerializer mesh_serializer;
     Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
     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());
     mesh_serializer.exportMesh(mesh.getPointer(), outdir + mesh->getName());
     Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
     Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
     Ogre::MaterialSerializer mat_ser;
     Ogre::MaterialSerializer mat_ser;
     size_t i(0);
     size_t i(0);
     std::set<std::string> textures;
     std::set<std::string> textures;
+    std::set<std::string> queued_materials;
+    bool has_queued_materials = false;
     while (it.hasMoreElements()){
     while (it.hasMoreElements()){
         Ogre::SubMesh *sub_mesh(it.getNext());
         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){
         if (mat != nullptr){
             for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){
             for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){
                 Ogre::Technique* tech = mat->getTechnique(techs);
                 Ogre::Technique* tech = mat->getTechnique(techs);
@@ -703,57 +687,51 @@ void WorldInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &m
                               texture_unit_num < pass->getNumTextureUnitStates();
                               texture_unit_num < pass->getNumTextureUnitStates();
                               texture_unit_num ++
                               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){
                                 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);
                 mat_ser.queueForExport(mat);
-                materials_.push_back(sub_mesh->getMaterialName());
-            }*/
+                has_queued_materials = true;
+            }
 
 
         }
         }
         ++ i;
         ++ 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){
     for (auto& texture_name : textures){
         std::string tex_name = texture_name.c_str();
         std::string tex_name = texture_name.c_str();
         try{
         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;
             Ogre::Image image;
             texture_ptr->convertToImage(image);
             texture_ptr->convertToImage(image);
             Ogre::String base_name;
             Ogre::String base_name;

+ 35 - 56
src/installer/decompiler/ControlFlow.cpp

@@ -16,6 +16,7 @@
 #include <algorithm>
 #include <algorithm>
 #include <iostream>
 #include <iostream>
 #include <set>
 #include <set>
+#include <stdexcept>
 #include <boost/format.hpp>
 #include <boost/format.hpp>
 #include "decompiler/ControlFlow.h"
 #include "decompiler/ControlFlow.h"
 
 
@@ -62,6 +63,7 @@
 #define GET_EDGE(edge) (boost::get(boost::edge_attribute, graph_, edge))
 #define GET_EDGE(edge) (boost::get(boost::edge_attribute, graph_, edge))
 
 
 ControlFlow::ControlFlow(InstVec& insts, Engine& engine): insts_(insts),engine_(engine){
 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
     // Automatically add a function if we're not supposed to look for more functions
     // and no functions are defined.
     // and no functions are defined.
@@ -87,7 +89,6 @@ ControlFlow::ControlFlow(InstVec& insts, Engine& engine): insts_(insts),engine_(
         prev = GET(cur);
         prev = GET(cur);
     }
     }
     // Add regular edges.
     // Add regular edges.
-    FuncMap::iterator fn;
     GraphVertex last ={};
     GraphVertex last ={};
     bool add_edge = false;
     bool add_edge = false;
     prev = NULL;
     prev = NULL;
@@ -107,7 +108,17 @@ ControlFlow::ControlFlow(InstVec& insts, Engine& engine): insts_(insts),engine_(
     // Add jump edges.
     // Add jump edges.
     for (InstIterator it = insts.begin(); it != insts.end(); ++ it){
     for (InstIterator it = insts.begin(); it != insts.end(); ++ it){
         if ((*it)->IsJump()){
         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);
             PUT_EDGE(e, true);
         }
         }
     }
     }
@@ -122,8 +133,9 @@ GraphVertex ControlFlow::Find(ConstInstIterator it){return addr_map_[(*it)->GetA
 GraphVertex ControlFlow::Find(uint32 address){
 GraphVertex ControlFlow::Find(uint32 address){
     std::map<uint32, GraphVertex>::iterator it = addr_map_.find(address);
     std::map<uint32, GraphVertex>::iterator it = addr_map_.find(address);
     if (it == addr_map_.end()){
     if (it == addr_map_.end()){
-        std::cerr << "Request for instruction at unknown address "
-          << boost::format("0x%08x") % address << std::endl;
+        throw std::runtime_error(
+            (boost::format("Request for instruction at unknown address 0x%08x") % address).str()
+        );
     }
     }
     return it->second;
     return it->second;
 }
 }
@@ -195,11 +207,7 @@ void ControlFlow::CreateGroups(){
         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);
         SetStackLevel(fn->second.vertex, 0);
     }
     }
     ConstInstIterator cur_inst, next_inst;
     ConstInstIterator cur_inst, next_inst;
@@ -366,11 +374,7 @@ void ControlFlow::DetectBreak(){
     for (VertexIterator v = vertex_range.first; v != vertex_range.second; ++ v){
     for (VertexIterator v = vertex_range.first; v != vertex_range.second; ++ v){
         GroupPtr gr = GET(*v);
         GroupPtr gr = GET(*v);
         // Undetermined block with unconditional jump...
         // 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;
             OutEdgeIterator oe = boost::out_edges(*v, graph_).first;
             GraphVertex target = boost::target(*oe, graph_);
             GraphVertex target = boost::target(*oe, graph_);
             GroupPtr target_gr = GET(target);
             GroupPtr target_gr = GET(target);
@@ -397,11 +401,7 @@ void ControlFlow::DetectContinue(){
     for (VertexIterator v = vertex_range.first; v != vertex_range.second; ++ v){
     for (VertexIterator v = vertex_range.first; v != vertex_range.second; ++ v){
         GroupPtr gr = GET(*v);
         GroupPtr gr = GET(*v);
         // Undetermined block with unconditional jump...
         // 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;
             OutEdgeIterator oe = boost::out_edges(*v, graph_).first;
             GraphVertex target = boost::target(*oe, graph_);
             GraphVertex target = boost::target(*oe, graph_);
             GroupPtr target_gr = GET(target);
             GroupPtr target_gr = GET(target);
@@ -415,24 +415,18 @@ void ControlFlow::DetectContinue(){
                 bool after_jump_jargets = true;
                 bool after_jump_jargets = true;
                 for (OutEdgeIterator toe = toer.first; toe != toer.second; ++ toe){
                 for (OutEdgeIterator toe = toer.first; toe != toer.second; ++ toe){
                     // ...it is targeting a while condition which jumps to the next sequential group
                     // ...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;
                         is_continue = false;
                     }
                     }
                     // ...or the instruction is placed after all jump targets from condition.
                     // ...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;
                         after_jump_jargets = false;
                     }
                     }
                 }
                 }
                 if (after_jump_jargets) is_continue = false;
                 if (after_jump_jargets) is_continue = false;
-
-                if (is_continue && ValidateBreakOrContinue(gr, target_gr))
+                if (is_continue && ValidateBreakOrContinue(gr, target_gr)){
                     gr->type = GROUP_TYPE_CONTINUE;
                     gr->type = GROUP_TYPE_CONTINUE;
+                }
             }
             }
         }
         }
     }
     }
@@ -450,9 +444,7 @@ bool ControlFlow::ValidateBreakOrContinue(GroupPtr group, GroupPtr condition_gro
         to = group;
         to = group;
         from = condition_group->next;
         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.
     // Verify that destination deals with innermost while/do-while.
     for (cursor = from; cursor->next != NULL && cursor != to; cursor = cursor->next){
     for (cursor = from; cursor->next != NULL && cursor != to; cursor = cursor->next){
         if (cursor->type == condition_group->type){
         if (cursor->type == condition_group->type){
@@ -474,9 +466,7 @@ bool ControlFlow::ValidateBreakOrContinue(GroupPtr group, GroupPtr condition_gro
                 }
                 }
                 InEdgeRange ier_validate = boost::in_edges(v_validate, graph_);
                 InEdgeRange ier_validate = boost::in_edges(v_validate, graph_);
                 for (
                 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_));
                     GroupPtr ig_validate = GET(boost::source(*ie_validate, graph_));
                     // All loops of other type going into range must be placed within range.
                     // All loops of other type going into range must be placed within range.
@@ -501,8 +491,9 @@ void ControlFlow::DetectIf(){
     for (VertexIterator v = vr.first; v != vr.second; ++v){
     for (VertexIterator v = vr.first; v != vr.second; ++v){
         GroupPtr gr = GET(*v);
         GroupPtr gr = GET(*v);
         // If: Undetermined block with conditional jump.
         // If: Undetermined block with conditional jump.
-        if (gr->type == GROUP_TYPE_NORMAL && ((*gr->end)->IsCondJump()))
+        if (gr->type == GROUP_TYPE_NORMAL && ((*gr->end)->IsCondJump())){
             gr->type = GROUP_TYPE_IF;
             gr->type = GROUP_TYPE_IF;
+        }
     }
     }
 }
 }
 
 
@@ -527,16 +518,11 @@ void ControlFlow::DetectElse(){
             // Else: Jump target of if immediately preceded by an unconditional jump...
             // Else: Jump target of if immediately preceded by an unconditional jump...
             if (!(*target_gr->prev->end)->IsUncondJump()) continue;
             if (!(*target_gr->prev->end)->IsUncondJump()) continue;
             // ...which is not a break or a 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;
                 continue;
             }
             }
             // ...to later in the code.
             // ...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_));
             GroupPtr target_target_gr = GET(boost::target(*toe, graph_));
             if ((*target_target_gr->start)->GetAddress() > (*target_gr->end)->GetAddress()){
             if ((*target_target_gr->start)->GetAddress() > (*target_gr->end)->GetAddress()){
                 if (ValidateElseBlock(gr, target_gr, target_target_gr)){
                 if (ValidateElseBlock(gr, target_gr, target_target_gr)){
@@ -550,11 +536,7 @@ void ControlFlow::DetectElse(){
 
 
 bool ControlFlow::ValidateElseBlock(GroupPtr if_group, GroupPtr start, GroupPtr end){
 bool ControlFlow::ValidateElseBlock(GroupPtr if_group, GroupPtr start, GroupPtr end){
     for (GroupPtr cursor = start; cursor != end; cursor = cursor->next){
     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.
             // Validate outgoing edges of conditions.
             OutEdgeRange oer = boost::out_edges(Find(cursor->start), graph_);
             OutEdgeRange oer = boost::out_edges(Find(cursor->start), graph_);
             for (OutEdgeIterator oe = oer.first; oe != oer.second; ++ oe){
             for (OutEdgeIterator oe = oer.first; oe != oer.second; ++ oe){
@@ -570,12 +552,10 @@ bool ControlFlow::ValidateElseBlock(GroupPtr if_group, GroupPtr start, GroupPtr
             }
             }
         }
         }
         // If previous group ends an else, that else must start inside the range.
         // 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
-        ){
-            if ((*(*it)->start)->GetAddress() < (*start->start)->GetAddress()) return false;
+        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...
         // Unless group is a simple unconditional jump...
         if ((*cursor->start)->IsUncondJump()) continue;
         if ((*cursor->start)->IsUncondJump()) continue;
@@ -586,8 +566,7 @@ bool ControlFlow::ValidateElseBlock(GroupPtr if_group, GroupPtr start, GroupPtr
             GroupPtr source_gr = GET(source);
             GroupPtr source_gr = GET(source);
             // Edges going to conditions...
             // Edges going to conditions...
             if (
             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
               || source_gr->type == GROUP_TYPE_DO_WHILE
             ){
             ){
                 // ...must not come from outside the range [start, end]...
                 // ...must not come from outside the range [start, end]...

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

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

+ 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>

+ 11 - 0
src/installer/main.cpp

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

+ 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