Kaynağa Gözat

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 1 ay önce
ebeveyn
işleme
15437fc9b1

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

+ 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})
 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")

+ 3 - 6
src/installer/DataInstaller.cpp

@@ -280,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:
@@ -302,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:
@@ -409,8 +407,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_.skip_wm_models) write_output_line_("Skipping world map model installation...", 2, true);
             else world_installer_->ProcessModels();
             installation_state_ = CLEAN;
             return CalcProgress();

+ 48 - 107
src/installer/FieldDataInstaller.cpp

@@ -52,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"
@@ -67,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");
 }
 
@@ -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 == "del3" // Segmentation fault.
       || resource_name == "elmin4_2" // boost::bad_format_string: format-string is ill-formed
-    ){return true;}
+    ) return true;
     return false;
 }
 
@@ -138,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;
 }
 
@@ -266,11 +266,8 @@ void FieldDataInstaller::Convert(int field_index){
             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;
         }
     }
 }
@@ -323,9 +320,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;
@@ -351,16 +346,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;
     }
 }
 
@@ -373,9 +364,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());
@@ -399,25 +388,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.
@@ -446,8 +428,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;
@@ -462,8 +444,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;
 }
 
@@ -497,8 +478,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)
             );
         }
@@ -512,39 +492,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;
     }
@@ -606,17 +575,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 ("
@@ -627,17 +592,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());
@@ -671,19 +634,16 @@ 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);
                 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.
@@ -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
                     // 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{
@@ -726,26 +680,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.
@@ -784,11 +732,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;
@@ -834,15 +780,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.
@@ -859,9 +802,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");
         }
     }
     {

+ 89 - 111
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"
@@ -341,7 +342,6 @@ 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");
     }
 }
@@ -349,74 +349,60 @@ void WorldInstaller::GenerateMaterials(){
 void WorldInstaller::ProcessModels(){
     // Open 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");
     world_lgp.open(*disk_image_.fileExists("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 +425,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 +449,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 +463,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 +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[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 +586,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 +594,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 +635,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");
@@ -671,26 +648,33 @@ 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 +687,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;

+ 35 - 56
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,7 +89,6 @@ 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;
@@ -107,7 +108,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);
         }
     }
@@ -122,8 +133,9 @@ 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;
+        throw std::runtime_error(
+            (boost::format("Request for instruction at unknown address 0x%08x") % address).str()
+        );
     }
     return it->second;
 }
@@ -195,11 +207,7 @@ void ControlFlow::CreateGroups(){
         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;
@@ -366,11 +374,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,11 +401,7 @@ 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);
@@ -415,24 +415,18 @@ void ControlFlow::DetectContinue(){
                 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))
+                if (is_continue && ValidateBreakOrContinue(gr, target_gr)){
                     gr->type = GROUP_TYPE_CONTINUE;
+                }
             }
         }
     }
@@ -450,9 +444,7 @@ 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){
@@ -474,9 +466,7 @@ bool ControlFlow::ValidateBreakOrContinue(GroupPtr group, GroupPtr condition_gro
                 }
                 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 +491,9 @@ 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()))
+        if (gr->type == GROUP_TYPE_NORMAL && ((*gr->end)->IsCondJump())){
             gr->type = GROUP_TYPE_IF;
+        }
     }
 }
 
@@ -527,16 +518,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 +536,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){
@@ -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.
-        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...
         if ((*cursor->start)->IsUncondJump()) continue;
@@ -586,8 +566,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]...

+ 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_);

+ 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 <QtGui/QIcon>
 
 #include "MainWindow.h"
 
@@ -25,8 +26,18 @@
  * @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();
 }

+ 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