Просмотр исходного кода

The installer now extracts battle models, textures and (partially) animations.

The animations are all wrong, excepts for their first frame. The battle module remains unimplemented, but battle models can be used in the field!
Iñigo Valentin 3 лет назад
Родитель
Сommit
fee1cd1a11

+ 1 - 3
src/common/VGearsApplication.cpp

@@ -138,9 +138,7 @@ namespace VGears{
             while(it != end){
             while(it != end){
                 archive_type = it->first;
                 archive_type = it->first;
                 archive_name = it->second;
                 archive_name = it->second;
-                res_mgr_->addResourceLocation(
-                  archive_name, archive_type, section_name, true
-                );
+                res_mgr_->addResourceLocation(archive_name, archive_type, section_name, true);
                 ++ it;
                 ++ it;
             }
             }
         }
         }

+ 5 - 1
src/core/EntityModel.cpp

@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  * GNU General Public License for more details.
  */
  */
 
 
+#include <iostream>
 #include <OgreEntity.h>
 #include <OgreEntity.h>
 #include <OgreRoot.h>
 #include <OgreRoot.h>
 #include "core/EntityModel.h"
 #include "core/EntityModel.h"
@@ -50,7 +51,10 @@ void EntityModel::PlayAnimation(
   Entity::AnimationPlayType play_type, const float start, const float end
   Entity::AnimationPlayType play_type, const float start, const float end
 ){
 ){
     if (animation_current_ != nullptr) animation_current_->setEnabled(false);
     if (animation_current_ != nullptr) animation_current_->setEnabled(false);
-    if (model_->getAllAnimationStates()->hasAnimationState(animation) == true){
+    if (
+      model_->getAllAnimationStates() != nullptr
+      && model_->getAllAnimationStates()->hasAnimationState(animation) == true
+    ){
         animation_current_name_ = animation;
         animation_current_name_ = animation;
         animation_current_ = model_->getAnimationState(animation);
         animation_current_ = model_->getAnimationState(animation);
         animation_current_->setLoop((play_type == Entity::PLAY_LOOPED) ? true : false);
         animation_current_->setLoop((play_type == Entity::PLAY_LOOPED) ? true : false);

+ 3 - 7
src/data/VGearsAFileSerializer.cpp

@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  * GNU General Public License for more details.
  */
  */
 
 
+#include <iostream>
 #include <OgreException.h>
 #include <OgreException.h>
 #include <OgreLogManager.h>
 #include <OgreLogManager.h>
 #include "data/VGearsAFileSerializer.h"
 #include "data/VGearsAFileSerializer.h"
@@ -42,20 +43,15 @@ namespace VGears{
         flipFromLittleEndian(&header_, 4, header_size / 4);
         flipFromLittleEndian(&header_, 4, header_size / 4);
     }
     }
 
 
-    void AFileSerializer::readObject(
-      Ogre::DataStreamPtr &stream, AFile::Frame &dest
-    ){
+    void AFileSerializer::readObject(Ogre::DataStreamPtr &stream, AFile::Frame &dest){
         readObject(stream, dest.root_rotation);
         readObject(stream, dest.root_rotation);
         readObject(stream, dest.root_translation);
         readObject(stream, dest.root_translation);
         dest.root_translation = dest.root_translation / HRCFile::DOWN_SCALER;
         dest.root_translation = dest.root_translation / HRCFile::DOWN_SCALER;
         ReadVector(stream, dest.bone_rotations, header_.bone_count);
         ReadVector(stream, dest.bone_rotations, header_.bone_count);
     }
     }
 
 
-    void AFileSerializer::ImportAFile(
-      Ogre::DataStreamPtr &stream, AFile* dest
-    ){
+    void AFileSerializer::ImportAFile(Ogre::DataStreamPtr &stream, AFile* dest){
         ReadFileHeader(stream);
         ReadFileHeader(stream);
-
         if(header_.version != 1){
         if(header_.version != 1){
             OGRE_EXCEPT(
             OGRE_EXCEPT(
               Ogre::Exception::ERR_INVALIDPARAMS,
               Ogre::Exception::ERR_INVALIDPARAMS,

+ 9 - 0
src/data/VGearsHRCFileSerializer.cpp

@@ -88,7 +88,16 @@ namespace VGears{
     void HRCFileSerializer::readObject(Ogre::DataStreamPtr &stream, Bone &dest){
     void HRCFileSerializer::readObject(Ogre::DataStreamPtr &stream, Bone &dest){
         Block bone_block;
         Block bone_block;
         ReadBlock(stream, bone_block);
         ReadBlock(stream, bone_block);
+
         Block::const_iterator it(bone_block.begin());
         Block::const_iterator it(bone_block.begin());
+        if (bone_block.size() < 2){
+            dest.length = 0;
+            dest.parent = "";
+            dest.name = "";
+            dest.rsd_names.clear();
+            return;
+        }
+
         dest.name = *(it ++);
         dest.name = *(it ++);
         dest.parent = *(it ++);
         dest.parent = *(it ++);
         dest.length
         dest.length

+ 44 - 33
src/data/VGearsPFile.cpp

@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  * GNU General Public License for more details.
  */
  */
 
 
+#include <iostream>
 #include <OgreBone.h>
 #include <OgreBone.h>
 #include <OgreLogManager.h>
 #include <OgreLogManager.h>
 #include <OgreStringConverter.h>
 #include <OgreStringConverter.h>
@@ -28,9 +29,8 @@ namespace VGears{
     const Ogre::Quaternion PFile::STATIC_ROTATION(PFile::CreateStaticRotation());
     const Ogre::Quaternion PFile::STATIC_ROTATION(PFile::CreateStaticRotation());
 
 
     PFile::PFile(
     PFile::PFile(
-      Ogre::ResourceManager *creator, const String &name,
-      Ogre::ResourceHandle handle, const String &group, bool is_manual,
-      Ogre::ManualResourceLoader *loader
+      Ogre::ResourceManager *creator, const String &name, Ogre::ResourceHandle handle,
+      const String &group, bool is_manual, Ogre::ManualResourceLoader *loader
     ) : Ogre::Resource(creator, name, handle, group, is_manual, loader)
     ) : Ogre::Resource(creator, name, handle, group, is_manual, loader)
     {createParamDictionary(RESOURCE_TYPE);}
     {createParamDictionary(RESOURCE_TYPE);}
 
 
@@ -39,9 +39,7 @@ namespace VGears{
     void PFile::loadImpl(){
     void PFile::loadImpl(){
         PFileSerializer serializer;
         PFileSerializer serializer;
         Ogre::DataStreamPtr stream(
         Ogre::DataStreamPtr stream(
-          Ogre::ResourceGroupManager::getSingleton().openResource(
-            mName, mGroup, true, this
-          )
+          Ogre::ResourceGroupManager::getSingleton().openResource(mName, mGroup, true, this)
         );
         );
         serializer.ImportPFile(stream, this);
         serializer.ImportPFile(stream, this);
     }
     }
@@ -59,6 +57,30 @@ namespace VGears{
         bounding_boxes_.clear();
         bounding_boxes_.clear();
     }
     }
 
 
+    PFile::VertexList& PFile::GetVertices(){return vertices_;}
+
+    PFile::NormalList& PFile::GetNormals(){return normals_;}
+
+    PFile::Unkown1List& PFile::GetUnknown1(){return unknown_1_;}
+
+    PFile::TextureCoordinateList& PFile::GetTextureCoordinates(){
+        return texture_coordinates_;
+    }
+
+    PFile::VertexColorList& PFile::GetVertexColors(){return vertex_colours_;}
+
+    PFile::PolygonColorList& PFile::GetPolygonColors(){return polygon_colours_;}
+
+    PFile::EdgeList& PFile::GetEdges(){return edges_;}
+
+    PFile::PolygonDefinitionList& PFile::GetPolygonDefinitions(){
+        return polygon_definitions_;
+    }
+
+    PFile::GroupList& PFile::GetGroups(){return groups_;}
+
+    PFile::BBoxList& PFile::GetBBoxes(){return bounding_boxes_;}
+
     size_t PFile::calculateSize() const{
     size_t PFile::calculateSize() const{
         return
         return
           vertices_.size() * sizeof(vertices_.front())
           vertices_.size() * sizeof(vertices_.front())
@@ -84,21 +106,18 @@ namespace VGears{
             const PolygonDefinition& def(polygon_definitions_[p]);
             const PolygonDefinition& def(polygon_definitions_[p]);
             for (int i(3); i --;){
             for (int i(3); i --;){
                 if (def.vertex[i] >= vertex_count){
                 if (def.vertex[i] >= vertex_count){
-                    log << "Error: index to vertex is out of Bounds "
-                      << " polygon_definitions_[" << p << "]"
-                      << ".vertex[" << i << "]: " << def.vertex[i];
+                    log << "Error: index to vertex is out of Bounds: polygon_definitions_[" << p
+                      << "].vertex[" << i << "]: " << def.vertex[i];
                     return false;
                     return false;
                 }
                 }
                 if (def.normal[i] >= normal_count){
                 if (def.normal[i] >= normal_count){
-                    log << "Error: index to normal is out of Bounds "
-                      << " polygon_definitions_[" << p << "]"
-                      << ".normal[" << i << "]: " << def.normal[i];
+                    log << "Error: index to normal is out of Bounds: polygon_definitions_[" << p
+                      << "].normal[" << i << "]: " << def.normal[i];
                     return false;
                     return false;
                 }
                 }
                 if (def.edge[i] >= edge_count){
                 if (def.edge[i] >= edge_count){
-                    log << "Error: index to edge is out of Bounds "
-                      << " polygon_definitions_[" << p << "]"
-                      << ".edge[" << i << "]: " << def.edge[i];
+                    log << "Error: index to edge is out of Bounds: polygon_definitions_[" << p
+                      << "].edge[" << i << "]: " << def.edge[i];
                     return false;
                     return false;
                 }
                 }
             }
             }
@@ -106,9 +125,7 @@ namespace VGears{
         return true;
         return true;
     }
     }
 
 
-    void PFile::AddGroups(
-      Ogre::Mesh *mesh, const String &bone_name, const RSDFilePtr &rsd
-    ) const{
+    void PFile::AddGroups(Ogre::Mesh *mesh, const String &bone_name, const RSDFilePtr &rsd) const{
         const Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
         const Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
         const String material_base_name(rsd->GetMaterialBaseName());
         const String material_base_name(rsd->GetMaterialBaseName());
         String rsd_base;
         String rsd_base;
@@ -116,13 +133,9 @@ namespace VGears{
         ManualObject mo(mesh);
         ManualObject mo(mesh);
         for (size_t g(0); g < groups_.size(); ++ g){
         for (size_t g(0); g < groups_.size(); ++ g){
             const String sub_name(
             const String sub_name(
-              bone_name + "/" + rsd_base + "/"
-              + Ogre::StringConverter::toString(g)
-            );
-            AddGroup(
-              groups_[g], mo, sub_name, material_base_name,
-              skeleton->getBone(bone_name)
+              bone_name + "/" + rsd_base + "/" + Ogre::StringConverter::toString(g)
             );
             );
+            AddGroup(groups_[g], mo, sub_name, material_base_name, skeleton->getBone(bone_name));
         }
         }
     }
     }
 
 
@@ -142,7 +155,9 @@ namespace VGears{
     ) const{
     ) const{
         size_t material_index(0);
         size_t material_index(0);
         if (group.has_texture) material_index = group.texture_index + 1;
         if (group.has_texture) material_index = group.texture_index + 1;
-        String material_name(material_base_name + "/" + Ogre::StringConverter::toString(material_index));
+        String material_name(
+          material_base_name + "/" + Ogre::StringConverter::toString(material_index)
+        );
         const uint16 bone_handle(bone->getHandle());
         const uint16 bone_handle(bone->getHandle());
         const Ogre::Vector3 bone_position(GetPosition(bone));
         const Ogre::Vector3 bone_position(GetPosition(bone));
         size_t index(0);
         size_t index(0);
@@ -155,16 +170,12 @@ namespace VGears{
             for (int i(3); i --;){
             for (int i(3); i --;){
                 uint32 v(group.vertex_start_index + polygon.vertex[i]);
                 uint32 v(group.vertex_start_index + polygon.vertex[i]);
                 uint32 n(0 + polygon.normal[i]);
                 uint32 n(0 + polygon.normal[i]);
-                uint32 t(
-                  group.texture_coordinate_start_index + polygon.vertex[i]
-                );
+                uint32 t(group.texture_coordinate_start_index + polygon.vertex[i]);
                 Ogre::Vector3 pos(vertices_[v]);
                 Ogre::Vector3 pos(vertices_[v]);
-                mo.position(
-                  (STATIC_ROTATION * (pos / HRCFile::DOWN_SCALER))
-                  + bone_position
-                );
+                mo.position((STATIC_ROTATION * (pos / HRCFile::DOWN_SCALER)) + bone_position);
                 mo.colour(vertex_colours_[v]);
                 mo.colour(vertex_colours_[v]);
-                mo.normal(STATIC_ROTATION * normals_[n]);
+                if (n < normals_.size()) mo.normal(STATIC_ROTATION * normals_[n]);
+                else mo.normal(STATIC_ROTATION * Ogre::Vector3(1.0f, 1.0f, 1.0f));
                 if (group.has_texture) mo.textureCoord(texture_coordinates_[t]);
                 if (group.has_texture) mo.textureCoord(texture_coordinates_[t]);
                 mo.bone(index, bone_handle);
                 mo.bone(index, bone_handle);
                 mo.index(index++);
                 mo.index(index++);

+ 34 - 46
src/data/VGearsPFile.h

@@ -29,11 +29,10 @@ namespace VGears{
     /**
     /**
      * Handles P files.
      * Handles P files.
      *
      *
-     * P files are binary files containing data which form 3D model. The files
-     * specify model's vertices, polygons, colors, texture coordinates and
-     * model sub-groups. The files do not specify references to the texture
-     * files, animations, model skeleton or anything else. P-files are used as
-     * parts of field models, battle models, battle locations on PC version of
+     * P files are binary files containing data which form 3D model. The files specify model's
+     * vertices, polygons, colors, texture coordinates and model sub-groups. The files do not
+     * specify references to the texture files, animations, model skeleton or anything else.
+     * P-files are used as parts of field models, battle models, battle locations on PC version of
      * FF7.
      * FF7.
      */
      */
     class PFile : public Ogre::Resource{
     class PFile : public Ogre::Resource{
@@ -43,25 +42,20 @@ namespace VGears{
             /**
             /**
              * Constructor.
              * Constructor.
              *
              *
-             * @param[in] creator Pointer to the ResourceManager that is
-             * creating this resource.
+             * @param[in] creator Pointer to the ResourceManager that is creating this resource.
              * @param[in] name The unique name of the resource.
              * @param[in] name The unique name of the resource.
              * @param[in] handle @todo Understand and document.
              * @param[in] handle @todo Understand and document.
-             * @param[in] group The name of the resource group to which this
-             * resource belong.
-             * @param[in] is_manual True if the resource is manually loaded,
-             * false otherwise.
-             * @param[in] loader Pointer to a ManualResourceLoader
-             * implementation which will be called when the Resource wishes to
-             * load (should be supplied if is_manual is set to true). It can be
-             * null, but the Resource will never be able to reload if anything
-             * ever causes it to unload. Therefore provision of a proper
-             * ManualResourceLoader instance is strongly recommended.
+             * @param[in] group The name of the resource group to which this resource belong.
+             * @param[in] is_manual True if the resource is manually loaded, false otherwise.
+             * @param[in] loader Pointer to a ManualResourceLoader implementation which will be
+             * called when the Resource wishes to load (should be supplied if is_manual is set to
+             * true). It can be null, but the Resource will never be able to reload if anything
+             * ever causes it to unload. Therefore provision of a proper ManualResourceLoader
+             * instance is strongly recommended.
              */
              */
             PFile(
             PFile(
-              Ogre::ResourceManager* creator, const String &name,
-              Ogre::ResourceHandle handle, const String& group,
-              bool is_manual = false,
+              Ogre::ResourceManager* creator, const String &name, Ogre::ResourceHandle handle,
+              const String& group, bool is_manual = false,
               Ogre::ManualResourceLoader* loader = nullptr
               Ogre::ManualResourceLoader* loader = nullptr
             );
             );
 
 
@@ -80,20 +74,18 @@ namespace VGears{
             /**
             /**
              * Indicates if the poligons definitions are valid.
              * Indicates if the poligons definitions are valid.
              *
              *
-             * @return True if all the poligon definitions are valid, false
-             * otherwise.
+             * @return True if all the poligon definitions are valid, false otherwise.
              */
              */
             virtual bool IsPolygonDefinitionListValid();
             virtual bool IsPolygonDefinitionListValid();
 
 
             /**
             /**
              * Adds a resource group.
              * Adds a resource group.
              *
              *
-             * A resource group includes a bone, a mesh and a graphical
-             * resource.
+             * A resource group includes a bone, a mesh and a graphical resource.
              *
              *
              * @param[in] mesh The mesh to add to the group.
              * @param[in] mesh The mesh to add to the group.
-             * @param[in] bone_name The bone in the skeleton to which to add
-             * the group. The bone must be in a skeleton assigned to MESH.
+             * @param[in] bone_name The bone in the skeleton to which to add the group. The bone
+             * must be in a skeleton assigned to MESH.
              * @param[in] rsd File with the resource to add to the group.
              * @param[in] rsd File with the resource to add to the group.
              */
              */
             virtual void AddGroups(
             virtual void AddGroups(
@@ -274,67 +266,66 @@ namespace VGears{
 
 
             typedef std::vector<BBoxEntry> BBoxList;
             typedef std::vector<BBoxEntry> BBoxList;
 
 
+            /**
+             * The type of resource.
+             */
+            static const String RESOURCE_TYPE;
+
             /**
             /**
              * Retrieves the vertices in the file.
              * Retrieves the vertices in the file.
              *
              *
              * @return The vertices in the file.
              * @return The vertices in the file.
              */
              */
-            virtual VertexList& GetVertices(){return vertices_;}
+            virtual VertexList& GetVertices();
 
 
             /**
             /**
              * Retrieves the normals in the file.
              * Retrieves the normals in the file.
              *
              *
              * @return The normals in the file.
              * @return The normals in the file.
              */
              */
-            virtual NormalList& GetNormals(){return normals_;}
+            virtual NormalList& GetNormals();
 
 
             /**
             /**
              * Retrieves unknown data from the file.
              * Retrieves unknown data from the file.
              *
              *
              * @return Unknown data.
              * @return Unknown data.
              */
              */
-            virtual Unkown1List& GetUnknown1(){return unknown_1_;}
+            virtual Unkown1List& GetUnknown1();
 
 
             /**
             /**
              * Retrieves the texture coordinates in the file.
              * Retrieves the texture coordinates in the file.
              *
              *
              * @return The texture coordinates in the file.
              * @return The texture coordinates in the file.
              */
              */
-            virtual TextureCoordinateList& GetTextureCoordinates(){
-                return texture_coordinates_;
-            }
+            virtual TextureCoordinateList& GetTextureCoordinates();
 
 
             /**
             /**
              * Retrieves the vertex colurs in the file.
              * Retrieves the vertex colurs in the file.
              *
              *
              * @return The vertex colurs in the file.
              * @return The vertex colurs in the file.
              */
              */
-            virtual VertexColorList& GetVertexColors(){return vertex_colours_;}
+            virtual VertexColorList& GetVertexColors();
 
 
             /**
             /**
              * Retrieves the polygon colours in the file.
              * Retrieves the polygon colours in the file.
              *
              *
              * @return The polygon colours in the file.
              * @return The polygon colours in the file.
              */
              */
-            virtual PolygonColorList& GetPolygonColors(){
-                return polygon_colours_;
-            }
+            virtual PolygonColorList& GetPolygonColors();
 
 
             /**
             /**
              * Retrieves the edges in the file.
              * Retrieves the edges in the file.
              *
              *
              * @return The edgesvertices in the file.
              * @return The edgesvertices in the file.
              */
              */
-            virtual EdgeList& GetEdges(){return edges_;}
+            virtual EdgeList& GetEdges();
 
 
             /**
             /**
              * Retrieves the polygon definitions in the file.
              * Retrieves the polygon definitions in the file.
              *
              *
              * @return The polygon definitions in the file.
              * @return The polygon definitions in the file.
              */
              */
-            virtual PolygonDefinitionList& GetPolygonDefinitions(){
-                return polygon_definitions_;
-            }
+            virtual PolygonDefinitionList& GetPolygonDefinitions();
 
 
             /**
             /**
              * Retrieves the groups in the file.
              * Retrieves the groups in the file.
@@ -343,16 +334,14 @@ namespace VGears{
              *
              *
              * @return The groups in the file.
              * @return The groups in the file.
              */
              */
-            virtual GroupList& GetGroups(){return groups_;}
+            virtual GroupList& GetGroups();
 
 
             /**
             /**
              * Retrieves the bounding boxes in the file.
              * Retrieves the bounding boxes in the file.
              *
              *
              * @return The vertices in the file.
              * @return The vertices in the file.
              */
              */
-            virtual BBoxList& GetBBoxes(){return bounding_boxes_;}
-
-            static const String RESOURCE_TYPE;
+            virtual BBoxList& GetBBoxes();
 
 
         protected:
         protected:
             /**
             /**
@@ -376,8 +365,7 @@ namespace VGears{
             /**
             /**
              * Add a group to the file.
              * Add a group to the file.
              *
              *
-             * A resource group includes a bone, a mesh and a graphical
-             * resource.
+             * A resource group includes a bone, a mesh and a graphical resource.
              *
              *
              * @param[in] group The group to add to the file.
              * @param[in] group The group to add to the file.
              * @param[in,out] mo The object to add to the group.
              * @param[in,out] mo The object to add to the group.

+ 477 - 7
src/installer/BattleDataInstaller.cpp

@@ -17,9 +17,17 @@
  */
  */
 
 
 #include <iostream>
 #include <iostream>
+#include <fstream>
 #include <zlib.h>
 #include <zlib.h>
 #include <tinyxml.h>
 #include <tinyxml.h>
+#include <boost/filesystem.hpp>
+#include <OgreMesh.h>
 #include "BattleDataInstaller.h"
 #include "BattleDataInstaller.h"
+#include "TexFile.h"
+#include "data/VGearsHRCFileManager.h"
+#include "data/VGearsAFileManager.h"
+#include "common/VGearsStringUtil.h"
+#include "common/FinalFantasy7/FF7NameLookup.h"
 
 
 BattleDataInstaller::BattleDataInstaller(const std::string input_dir, const std::string output_dir):
 BattleDataInstaller::BattleDataInstaller(const std::string input_dir, const std::string output_dir):
   input_dir_(input_dir), output_dir_(output_dir),
   input_dir_(input_dir), output_dir_(output_dir),
@@ -28,10 +36,12 @@ BattleDataInstaller::BattleDataInstaller(const std::string input_dir, const std:
 
 
 BattleDataInstaller::~BattleDataInstaller(){}
 BattleDataInstaller::~BattleDataInstaller(){}
 
 
-unsigned int BattleDataInstaller::Initialize(){
+unsigned int BattleDataInstaller::InitializeScenes(){
     next_scene_ = 0;
     next_scene_ = 0;
     scenes_.clear();
     scenes_.clear();
     enemies_.clear();
     enemies_.clear();
+    attacks_.clear();
+    // Get all scenes from scene.bin.
     for (int i = 0; i < 32; i ++){
     for (int i = 0; i < 32; i ++){
         File scene_bin_block(&scene_bin_, i * 0x2000, 0x2000);
         File scene_bin_block(&scene_bin_, i * 0x2000, 0x2000);
         for (int o = 0; o < 10; o ++){
         for (int o = 0; o < 10; o ++){
@@ -55,8 +65,32 @@ unsigned int BattleDataInstaller::Initialize(){
     return total_scenes_;
     return total_scenes_;
 }
 }
 
 
+unsigned int BattleDataInstaller::InitializeModels(){
+    next_model_to_process_ = 0;
+    next_model_to_convert_ = 0;
+    battle_lgp_files_.clear();
+    battle_lgp_file_names_.clear();
+    models_.clear();
+    // Open battle.lgp
+    File battle_lgp_file(input_dir_ + "data/battle/battle.lgp");
+    // Also, open it as a LGP archive.
+    VGears::LGPArchive battle_lgp(input_dir_ + "data/battle/battle.lgp", "LGP");
+    battle_lgp.open(input_dir_ + "data/battle/battle.lgp", true);
+    battle_lgp.load();
+    VGears::LGPArchive::FileList file_list = battle_lgp.GetFiles();
+    for (int i = 0; i < file_list.size(); i ++){
+        VGears::LGPArchive::FileEntry f = file_list.at(i);
+        if (f.data_offset + f.data_size <= battle_lgp_file.GetFileSize()){
+            File b_lgp_file(&battle_lgp_file, f.data_offset, f.data_size);
+            battle_lgp_files_.push_back(b_lgp_file);
+            battle_lgp_file_names_.push_back("btl_" + f.file_name);
+        }
+    }
+    return battle_lgp_files_.size();
+}
+
 unsigned int BattleDataInstaller::ProcessScene(){
 unsigned int BattleDataInstaller::ProcessScene(){
-    if (next_scene_ >= scenes_uncompressed_.size()) return false;
+    if (next_scene_ >= scenes_uncompressed_.size()) return total_scenes_;
     BattleSceneFile scene(next_scene_, scenes_uncompressed_[next_scene_]);
     BattleSceneFile scene(next_scene_, scenes_uncompressed_[next_scene_]);
     scenes_.push_back(scene);
     scenes_.push_back(scene);
     // Add new enemies to the list.
     // Add new enemies to the list.
@@ -95,9 +129,169 @@ unsigned int BattleDataInstaller::ProcessScene(){
     return next_scene_;
     return next_scene_;
 }
 }
 
 
+unsigned int BattleDataInstaller::ProcessModel(){
+    if (next_model_to_process_ >= battle_lgp_files_.size()) return battle_lgp_files_.size();
+    // TODO: Do something with the files.
+    // A little explanation. Each file has a 4 letter name, for example: 1234
+    // 12 is the model identifier.
+    // 34 if the type of file:
+    //  - aa: Skeleton file (.hrc)
+    //  - ab: Unknown
+    //  - ac - al: Textures (.tex)
+    //  - am - cz: Polygon files (.p)
+    //  - da: Animations (.anim)
+    // Note that the names in battle_lgp_file_names_ are prefixed with "btl_"
+    std::string id = battle_lgp_file_names_[next_model_to_process_].substr(4, 2);
+    if (id != "at"){next_model_to_process_ ++; return next_model_to_process_;} // TODO: DEBUG
+    std::string type = battle_lgp_file_names_[next_model_to_process_].substr(6, 2);
+    if (type == "aa"){
+        // HRC file.
+        battle_lgp_files_[next_model_to_process_].WriteFile(
+          output_dir_ + "temp/battle_models/"
+          + battle_lgp_file_names_[next_model_to_process_] + ".hrcbin"
+        );
+        bool found = false;
+        for (int m = 0; m < models_.size(); m ++){
+            if (models_[m].id == id){
+                models_[m].hrc = battle_lgp_file_names_[next_model_to_process_] + ".hrc";
+                found = true;
+                break;
+            }
+        }
+        if (found == false){
+            Model model;
+            model.id = id;
+            model.hrc = battle_lgp_file_names_[next_model_to_process_] + ".hrc";
+            models_.push_back(model);
+        }
+    }
+    else if (type == "ab"){
+        battle_lgp_files_[next_model_to_process_].WriteFile(
+          output_dir_ + "temp/battle_models/"
+          + battle_lgp_file_names_[next_model_to_process_] + ".unknown"
+        );
+    } // Unknown and not needed.
+    else if (type == "da"){
+        // .a Animation file.
+        battle_lgp_files_[next_model_to_process_].WriteFile(
+          output_dir_ + "temp/battle_models/"
+          + battle_lgp_file_names_[next_model_to_process_] + ".anim"
+        );
+        bool found = false;
+        for (int m = 0; m < models_.size(); m ++){
+            if (models_[m].id == id){
+                models_[m].anim = battle_lgp_file_names_[next_model_to_process_] + ".anim";
+                found = true;
+                break;
+            }
+        }
+        if (found == false){
+            Model model;
+            model.id = id;
+            model.anim = battle_lgp_file_names_[next_model_to_process_] + ".anim";
+            models_.push_back(model);
+        }
+    }
+    else if (
+      type == "ac" || type == "ad" || type == "ae" || type == "af" || type == "ag"
+      || type == "ah"|| type == "ai" || type == "aj" || type == "ak" || type == "al"
+    ){
+        // .tex texture files. Save directly to their directory.
+        battle_lgp_files_[next_model_to_process_].WriteFile(
+          output_dir_ + "temp/battle_models/"
+          + battle_lgp_file_names_[next_model_to_process_] + ".tex"
+        );
+        TexFile tex(battle_lgp_files_[next_model_to_process_]);
+        tex.SavePng(
+          output_dir_ + "models/battle/entities/"
+            + battle_lgp_file_names_[next_model_to_process_] + ".png",
+          0
+          );
+        bool found = false;
+        for (int m = 0; m < models_.size(); m ++){
+            if (models_[m].id == id){
+                models_[m].tex.push_back(battle_lgp_file_names_[next_model_to_process_] + ".tex");
+                found = true;
+                break;
+            }
+        }
+        if (found == false){
+            Model model;
+            model.id = id;
+            model.tex.push_back(battle_lgp_file_names_[next_model_to_process_] + ".tex");
+            models_.push_back(model);
+        }
+    }
+    else{
+        // .p polygon file.
+        battle_lgp_files_[next_model_to_process_].WriteFile(
+          output_dir_ + "temp/battle_models/"
+          + battle_lgp_file_names_[next_model_to_process_] + ".p"
+        );
+        bool found = false;
+        for (int m = 0; m < models_.size(); m ++){
+            if (models_[m].id == id){
+                models_[m].p.push_back(battle_lgp_file_names_[next_model_to_process_] + ".p");
+                found = true;
+                break;
+            }
+        }
+        if (found == false){
+            Model model;
+            model.id = id;
+            model.p.push_back(battle_lgp_file_names_[next_model_to_process_] + ".p");
+            models_.push_back(model);
+        }
+    }
+    next_model_to_process_ ++;
+    return next_model_to_process_;
+}
+
+unsigned int BattleDataInstaller::ConvertModelsInit(){
+    next_model_to_convert_ = 0;
+    return models_.size();
+}
+
+unsigned int BattleDataInstaller::ConvertModel(){
+    if (next_model_to_convert_ >= models_.size()) return models_.size();
+    Model model = models_[next_model_to_convert_];
+    try{
+        DecompileHrc(File(output_dir_ + "temp/battle_models/" + model.hrc + "bin"), model, output_dir_ + "temp/battle_models/" + model.hrc);
+        GenerateRsdFiles(model, output_dir_ + "temp/battle_models/");
+        ExtractAFilesFromDAFile(File(output_dir_ + "temp/battle_models/" + model.anim), &model, output_dir_ + "temp/battle_models/");
+        Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(model.hrc, "FFVII");
+        Ogre::String base_name;
+        VGears::StringUtil::splitBase(model.hrc, base_name);
+        auto mesh_name = VGears::NameLookup::model(base_name) + ".mesh";
+        Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(mesh_name, "FFVII"));
+        Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
+        for (std::string anim : model.a){
+        //if (model.anim != ""){
+            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_ + "models/battle/entities/", mesh);
+    }
+    catch (const Ogre::Exception& ex){
+        std::cerr << "[ERROR] Ogre exception converting battle model "
+          << model.hrc <<": " << ex.what() << std::endl;
+    }
+    catch (const std::exception& ex){
+        std::cerr << "[ERROR] Exception converting battle model "
+          << model.hrc << ": " << ex.what() << std::endl;
+    }
+    next_model_to_convert_ ++;
+    return next_model_to_convert_;
+}
+
 void BattleDataInstaller::WriteEnemies(){
 void BattleDataInstaller::WriteEnemies(){
     for (Enemy enemy : enemies_){
     for (Enemy enemy : enemies_){
-        //std::cout << "Writting enemy " << enemy.id << ": " << (output_dir_ + "game/enemy/" + std::to_string(enemy.id) + ".xml") << "\n";
         TiXmlDocument xml;
         TiXmlDocument xml;
         std::unique_ptr<TiXmlElement> container(new TiXmlElement("Enemy"));
         std::unique_ptr<TiXmlElement> container(new TiXmlElement("Enemy"));
         container->SetAttribute("id", enemy.id);
         container->SetAttribute("id", enemy.id);
@@ -201,7 +395,6 @@ void BattleDataInstaller::WriteEnemies(){
 }
 }
 
 
 void BattleDataInstaller::WriteAttacks(){
 void BattleDataInstaller::WriteAttacks(){
-    //std::cout << "WRITE ATTACKS TOTAL: " << attacks_.size() << "\n";
     for (Attack attack : attacks_){
     for (Attack attack : attacks_){
         TiXmlDocument xml;
         TiXmlDocument xml;
         std::unique_ptr<TiXmlElement> container(new TiXmlElement("Attack"));
         std::unique_ptr<TiXmlElement> container(new TiXmlElement("Attack"));
@@ -348,7 +541,6 @@ void BattleDataInstaller::WriteAttacks(){
 
 
 void BattleDataInstaller::WriteFormations(){
 void BattleDataInstaller::WriteFormations(){
     for (Formation formation : formations_){
     for (Formation formation : formations_){
-        //std::cout << "Writting enemy " << enemy.id << ": " << (output_dir_ + "game/enemy/" + std::to_string(enemy.id) + ".xml") << "\n";
         TiXmlDocument xml;
         TiXmlDocument xml;
         std::unique_ptr<TiXmlElement> container(new TiXmlElement("Formation"));
         std::unique_ptr<TiXmlElement> container(new TiXmlElement("Formation"));
         container->SetAttribute("id", formation.id);
         container->SetAttribute("id", formation.id);
@@ -459,7 +651,6 @@ File* BattleDataInstaller::ExtractGZipScene(File file){
     u8* extract_buffer = static_cast<u8*>(malloc(extract_size));
     u8* extract_buffer = static_cast<u8*>(malloc(extract_size));
     int ret;
     int ret;
     z_stream strm;
     z_stream strm;
-
     strm.zalloc = Z_NULL; // Used to allocate the internal state.
     strm.zalloc = Z_NULL; // Used to allocate the internal state.
     strm.zfree = Z_NULL; // Used to free the internal state.
     strm.zfree = Z_NULL; // Used to free the internal state.
     strm.opaque = Z_NULL; // Private data object passed to zalloc and zfree.
     strm.opaque = Z_NULL; // Private data object passed to zalloc and zfree.
@@ -506,7 +697,6 @@ File* BattleDataInstaller::ExtractGZipScene(File file){
                 std::cout << "Warning: inflate - Z_MEM_ERROR in file " << std::endl;
                 std::cout << "Warning: inflate - Z_MEM_ERROR in file " << std::endl;
                 return NULL;
                 return NULL;
         }
         }
-
         if (ret != Z_STREAM_END){
         if (ret != Z_STREAM_END){
             extract_buffer = static_cast<u8*>(realloc(extract_buffer, extract_size * 2));
             extract_buffer = static_cast<u8*>(realloc(extract_buffer, extract_size * 2));
             if (extract_buffer == NULL){
             if (extract_buffer == NULL){
@@ -528,3 +718,283 @@ File* BattleDataInstaller::ExtractGZipScene(File file){
     File* out_file = new File(extract_buffer, 0, extract_size);
     File* out_file = new File(extract_buffer, 0, extract_size);
     return out_file;
     return out_file;
 }
 }
+
+void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh){
+    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("models/battle/entities/" + base_mesh_name + ".skeleton");
+    mesh_serializer.exportMesh(mesh.getPointer(), outdir + mesh->getName());
+    Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
+    Ogre::MaterialSerializer mat_ser;
+    size_t i(0);
+    std::set<std::string> textures;
+    while (it.hasMoreElements()){
+        Ogre::SubMesh *sub_mesh(it.getNext());
+        Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(
+          sub_mesh->getMaterialName())
+        );
+        if (mat != nullptr){
+            for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){
+                Ogre::Technique* tech = mat->getTechnique(techs);
+                if (tech){
+                    for (size_t pass_num = 0; pass_num < tech->getNumPasses(); pass_num ++){
+                        Ogre::Pass* pass = tech->getPass(pass_num);
+                        if (pass){
+                            for (
+                              size_t texture_unit_num = 0;
+                              texture_unit_num < pass->getNumTextureUnitStates();
+                              texture_unit_num ++
+                            ){
+                                Ogre::TextureUnitState* unit  = pass->getTextureUnitState(
+                                  texture_unit_num
+                                );
+                                if (unit && unit->getTextureName().empty() == false){
+                                     // 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_name + ".png");
+                                    textures.insert(unit->getTextureName());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
+                 << sub_mesh->getMaterialName() << std::endl;
+                mat_ser.queueForExport(mat);
+                materials_.push_back(sub_mesh->getMaterialName());
+            }
+        }
+        ++ i;
+    }
+    mat_ser.exportQueued(outdir + base_mesh_name + VGears::EXT_MATERIAL);
+    for (auto& texture_name : textures){
+        std::string tex_name = texture_name.c_str();
+        Ogre::String base_name;
+        VGears::StringUtil::splitBase(
+          tex_name, base_name
+        );
+        base_name += ".png";
+        try{
+            Ogre::TexturePtr texture_ptr
+              = Ogre::TextureManager::getSingleton().load(base_name, "FFVIITextures");
+            Ogre::Image image;
+            texture_ptr->convertToImage(image);
+            Ogre::String base_name;
+            VGears::StringUtil::splitBase(texture_name, base_name);
+            image.save(outdir + base_mesh_name + "_" + base_name + ".png");
+        }
+        catch (std::exception const& ex){
+            std::cerr << "[ERROR] Exception: " << ex.what() << std::endl;
+        }
+    }
+}
+
+void BattleDataInstaller::GenerateRsdFiles(Model model, std::string path){
+    std::sort(model.tex.begin(), model.tex.end());
+    // For each p file, generate a rsd file.
+    for (std::string p : model.p){
+        std::string base_name = p.substr(0, 8);
+        std::string content = "@RSD940102\n";
+
+        content += "PLY=" + base_name + ".PLY\n";
+        content += "MAT=" + base_name + ".MAT\n";
+        content += "GRP=" + base_name + ".GRP\n";
+        content += "NTEX=" + std::to_string(model.tex.size()) + "\n";
+        for (int t = 0; t < model.tex.size(); t ++)
+            content += "TEX[" + std::to_string(t) + "]=" + model.tex[t] + "\n";
+        content += "\n";
+        std::ofstream out(path + base_name + ".rsd");
+        out << content;
+        out.close();
+    }
+}
+
+void BattleDataInstaller::DecompileHrc(File compiled, Model model, std::string path){
+    compiled.SetOffset(0);
+    compiled.readU32LE();
+    compiled.readU32LE();
+    compiled.readU32LE();
+    std::sort(model.p.begin(), model.p.end());
+    std::string decompiled(":HEADER_BLOCK 2\n:SKELETON " + model.id + "aa\n");
+    int bones = compiled.readU32LE();
+    compiled.SetOffset(compiled.GetFileSize() - (bones * 12));
+    decompiled += ":BONES " + std::to_string(bones) + "\n\n";
+    int p_file_index = 0;
+    for (int b = 0; b < bones; b ++){
+        decompiled += "bone" + std::to_string(b) + "\n";
+        int parent = compiled.readU32LE();
+        if (parent == 0xFFFFFFFF) decompiled += "root\n";
+        else decompiled += "bone" + std::to_string(parent) + "\n";
+        union {
+            float f;
+            unsigned long ul;
+            unsigned char b[4];
+        } u;
+        u.b[0] = compiled.readU8();
+        u.b[1] = compiled.readU8();
+        u.b[2] = compiled.readU8();
+        u.b[3] = compiled.readU8();
+        decompiled += std::to_string(u.f * -1) + "\n";
+        int offset = compiled.readU32LE();
+        if (offset == 0) decompiled += "0\n\n";
+        else {
+            decompiled += std::to_string(offset) + " ";
+            if (p_file_index < model.p.size()){
+                decompiled += model.p[p_file_index].substr(0, 8);
+            }
+            p_file_index ++;
+            decompiled += "\n\n";
+        }
+    }
+    std::ofstream out(path);
+    out << decompiled;
+    out.close();
+}
+
+void BattleDataInstaller::ExtractAFilesFromDAFile(File da, Model* model, std::string path){
+
+    struct Bone{
+        int raw[3]; // Z, Y, X.
+        float angle[3]; // Z, Y, X.
+    };
+
+    da.SetOffset(0);
+    int num_animations = da.readU32LE();
+    for (int anim = 0; anim < num_animations; anim ++){
+        std::string file_index_name = std::to_string(anim);
+        while (file_index_name.size() < 2) file_index_name = "0" + file_index_name;
+        std::ofstream a(
+          path + model->id + "_" + file_index_name + ".a", std::ios::out | std::ios::binary
+        );
+        model->a.push_back(path + model->id + "_" + file_index_name + ".a");
+        u32 zero = 0x00000000;
+        u32 version = 0x00000001;
+        u32 rotation_order = 0x00020001;
+        u32 bone_count = da.readU32LE() - 1;
+        u32 frames = da.readU32LE();
+        Bone bones[frames][bone_count];
+        a.write(reinterpret_cast<const char*>(&version), 4);
+        a.write(reinterpret_cast<const char*>(&frames), 4);
+        a.write(reinterpret_cast<const char*>(&bone_count), 4);
+        a.write(reinterpret_cast<const char*>(&rotation_order), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        int next_offset = da.GetCurrentOffset() + da.readU32LE() + 4;
+        da.readU16LE(); // Frames, again.
+        da.readU16LE(); // Size, again.
+        int key = da.readU8(); // Scale decoding key.
+        da.readU8(); // ???
+        da.readU32LE(); // ???
+        da.readU32LE(); // ???
+        int frame = 0;
+        while (frame < frames){
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            int bone = 0;
+            while (bone < bone_count){
+                if (bone == 0){
+                    // Uncompressed, 3 bytes per bone rotations. Read one bone.
+                    for (int d = 0; d < 3; d ++){ // Read Z, Y, X.
+                        int delta = da.readU16LE();
+                        delta = delta << key;
+                        bones[frame][bone].raw[d] = delta;
+                        bones[frame][bone].angle[d] = delta;
+                        if (bones[frame][bone].angle[d] < 0) bones[frame][bone].angle[d] += 0x1000;
+                        bones[frame][bone].angle[d]
+                          = bones[frame][bone].angle[d] / 4096.0f * 360.0f;
+                        a.write(reinterpret_cast<const char*>(&bones[frame][bone].angle[d]), 4);
+                    }
+                    bone ++;
+                }
+                else if (key == 0){
+                    // 12 bits per bone rotation, 36 bits per bone. Read two bones (9 bytes).
+                    u8 data[9];
+                    for (int d = 0; d < 9; d ++) data[d] = da.readU8();
+                    // Read two bones.
+                    float rot[6]; // Z, Y, X, Z, Y, X
+                    bones[frame][bone].raw[0] = data[0] * 16 + (data[1] >> 4);
+                    bones[frame][bone].raw[1] = (data[1] & 15) * 256 + data[2];
+                    bones[frame][bone].raw[2] = data[3] * 16 + (data[4] >> 4);
+                    bones[frame][bone + 1].raw[0] = (data[4] & 15) * 256 + data[5];
+                    bones[frame][bone + 1].raw[1] = data[6] * 16 + (data[7] >> 4);
+                    bones[frame][bone + 1].raw[2] = (data[7] & 15) * 256 + data[8];
+                    if (frame > 0){ // Frames 1 and up, sum to the previous frame.
+                        bones[frame][bone].raw[0] += bones[frame - 1][bone].raw[0];
+                        bones[frame][bone].raw[1] += bones[frame - 1][bone].raw[1];
+                        bones[frame][bone].raw[2] += bones[frame - 1][bone].raw[2];
+                        bones[frame][bone + 1].raw[0] += bones[frame - 1][bone + 1].raw[0];
+                        bones[frame][bone + 1].raw[1] += bones[frame - 1][bone + 1].raw[1];
+                        bones[frame][bone + 1].raw[2] += bones[frame - 1][bone + 1].raw[2];
+                    }
+
+                    // Quaternion to degree.
+                    for (int b = bone; b < bone + 2; b ++){
+                        for (int r = 0; r < 3; r ++){
+                            bones[frame][b].angle[r] = bones[frame][b].raw[r];
+                            if (bones[frame][b].angle[r] < 0) bones[frame][b].angle[r] += 0x1000;
+                            bones[frame][b].angle[r] = bones[frame][b].angle[r] / 4096.0f * 360.0f;
+                            a.write(reinterpret_cast<const char*>(&bones[frame][b].angle[r]), 4);
+                        }
+                    }
+                    bone += 2;
+
+                }
+                else if (key == 2){
+                    // 10 bits per bone rotation, 30 bits per bone. Read four bones (15 bytes).
+                    std::cout << "TODO: Decode bones with key 2.\n";
+                    // TODO
+                    bone += 4;
+                }
+                else if (key == 4){
+                    // 8 bits per bone rotation, 24 bits per bone. Read one bone (3 bytes).
+                    std::cout << "TODO: Decode bones with key 4.\n";
+                    // TODO: Fix this and do the same as key 0.
+                    u8 data[3];
+                    for (int d = 0; d < 3; d ++){
+                        if (bone + d < bone_count) data[d] = da.readU8();
+                        else data[d] = 0;
+                    }
+                    // Read two bones.
+                    float rot[3]; // Z, Y, X
+                    rot[0] = data[0];
+                    rot[1] = data[1];
+                    rot[2] = data[3];
+                    // Quaternion to degree.
+                    for (int d = 0; d < 3; d ++) rot[d] = rot[d] * 360 / 4096;
+                    // Write.
+                    if (bone < bone_count){
+                        a.write(reinterpret_cast<const char*>(&rot[0]), 4);
+                        a.write(reinterpret_cast<const char*>(&rot[1]), 4);
+                        a.write(reinterpret_cast<const char*>(&rot[2]), 4);
+                        bone ++;
+                    }
+                }
+                else{
+                    // This should not happen.
+                    bone ++;
+                }
+            }
+            frame ++;
+        }
+        a.close();
+        da.SetOffset(next_offset);
+    }
+}

+ 146 - 3
src/installer/BattleDataInstaller.h

@@ -21,6 +21,7 @@
 #include "common/BinGZipFile.h"
 #include "common/BinGZipFile.h"
 #include "common/TypeDefine.h"
 #include "common/TypeDefine.h"
 #include "data/BattleSceneFile.h"
 #include "data/BattleSceneFile.h"
+#include "data/VGearsLGPArchive.h"
 
 
 /**
 /**
  * The battle data installer.
  * The battle data installer.
@@ -44,11 +45,18 @@ class BattleDataInstaller{
         ~BattleDataInstaller();
         ~BattleDataInstaller();
 
 
         /**
         /**
-         * Prepares the installer.
+         * Prepares the installer for scene processing.
          *
          *
-         * @return The total number of scenes.
+         * @return The total number of scenes to process.
          */
          */
-        unsigned int Initialize();
+        unsigned int InitializeScenes();
+
+        /**
+         * Prepares the installer for 3D model processing.
+         *
+         * @return The total number of models to process.
+         */
+        unsigned int InitializeModels();
 
 
         /**
         /**
          * Processes the next battle scene.
          * Processes the next battle scene.
@@ -57,6 +65,30 @@ class BattleDataInstaller{
          */
          */
         unsigned int ProcessScene();
         unsigned int ProcessScene();
 
 
+        /**
+         * Processes the 3D model.
+         *
+         * Saves .hrc, .a and .p files for later conversion. .tex files are converted directly to
+         * png.
+         *
+         * @return The total number of processed models.
+         */
+        unsigned int ProcessModel();
+
+        /**
+         * Prepres the installer for model conversion.
+         *
+         * @return The total number of models to convert.
+         */
+        unsigned int ConvertModelsInit();
+
+        /**
+         * Converts a model.
+         *
+         * @return The total number of converted models.
+         */
+        unsigned int ConvertModel();
+
         /**
         /**
          * Writes enemy data to files.
          * Writes enemy data to files.
          */
          */
@@ -74,6 +106,52 @@ class BattleDataInstaller{
 
 
     private:
     private:
 
 
+        /**
+         * Files to compose a model.
+         */
+        struct Model{
+
+            /**
+             * Model identifier, two letters.
+             */
+            std::string id;
+
+            /**
+             * Name of the model .hrc file
+             */
+            std::string hrc;
+
+            /**
+             * Name of the "da" animation file.
+             */
+            std::string anim;
+
+            /**
+             * List of .p polygon files associated to the model
+             */
+            std::vector<std::string> p;
+
+            /**
+             * List of .tex texture files associated to the model
+             */
+            std::vector<std::string> tex;
+
+            /**
+             * List of .a animation files associated to the model
+             */
+            std::vector<std::string> a;
+
+            /**
+             * Constructor, initializes default values.
+             */
+            Model(): id(""), hrc(""), anim(""){
+                p.clear();
+                tex.clear();
+                a.clear();
+            }
+
+        };
+
         /**
         /**
          * Extract a gzipped file.
          * Extract a gzipped file.
          *
          *
@@ -107,6 +185,35 @@ class BattleDataInstaller{
          */
          */
         std::string BuildAttackFileName(Attack attack);
         std::string BuildAttackFileName(Attack attack);
 
 
+        /**
+         * Exports a mesh to a file.
+         *
+         * The file will have the mesh name.
+         *
+         * @param[in] outdir Path to the directory where the file will be saved.
+         * @param[in] mesh The mesh to export.
+         */
+        void ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh);
+
+        /**
+         * Generates all required .rsd models for a model.
+         */
+        void GenerateRsdFiles(Model model, std::string path);
+
+        /**
+         * Decompiles a compiled HRC file.
+         *
+         * Note that this is not a general purpose HRC decompiler, it only works for files in
+         * battle.lgp.
+         *
+         * @param[in] compiled The compiles HRC file.
+         * @param[in] model The model.
+         * @param[in] path PAth to save the decompiled file to.
+         */
+        void DecompileHrc(File compiled, Model model, std::string path);
+
+        void ExtractAFilesFromDAFile(File da, Model* model, std::string path);
+
         /**
         /**
          * The path to the directory from which to read the PC game data.
          * The path to the directory from which to read the PC game data.
          */
          */
@@ -117,6 +224,11 @@ class BattleDataInstaller{
          */
          */
         std::string output_dir_;
         std::string output_dir_;
 
 
+        /**
+         * Function used to print text to the log output, line by line.
+         */
+        std::function<void(std::string)> write_output_line_;
+
         /**
         /**
          * Next scene file to process;
          * Next scene file to process;
          */
          */
@@ -172,4 +284,35 @@ class BattleDataInstaller{
          */
          */
         std::map<int, std::string> formation_map_;
         std::map<int, std::string> formation_map_;
 
 
+        /**
+         * The files in the original battle.lgp file.
+         */
+        std::vector<File> battle_lgp_files_;
+
+        /**
+         * File names for the files in {@see battle_lgp_files_}.
+         */
+        std::vector<std::string> battle_lgp_file_names_;
+
+        /**
+         * Next model to process;
+         */
+        unsigned int next_model_to_process_;
+
+        /**
+         * Next model to convert.
+         */
+        unsigned int next_model_to_convert_;
+
+        /**
+         * List of models files found in battle.lgp
+         */
+        std::vector<Model> models_;
+
+        /**
+         * List of model materials;
+         */
+        std::vector<std::string> materials_;
+
+
 };
 };

+ 49 - 16
src/installer/DataInstaller.cpp

@@ -39,7 +39,6 @@ DataInstaller::DataInstaller(
     // Assign weights.
     // Assign weights.
     for (int i = IDLE; i < STATE_COUNT; i ++) step_weight_[i] = 1;
     for (int i = IDLE; i < STATE_COUNT; i ++) step_weight_[i] = 1;
     step_weight_[IDLE] = 0;
     step_weight_[IDLE] = 0;
-    step_weight_[BATTLE_SCENES] = 1;
     step_weight_[MEDIA_IMAGES] = 3;
     step_weight_[MEDIA_IMAGES] = 3;
     step_weight_[MEDIA_SOUNDS] = 8;
     step_weight_[MEDIA_SOUNDS] = 8;
     step_weight_[MEDIA_MUSICS] = 9;
     step_weight_[MEDIA_MUSICS] = 9;
@@ -70,40 +69,58 @@ float DataInstaller::Progress(){
               options_.no_ffmpeg, options_.no_timidity
               options_.no_ffmpeg, options_.no_timidity
             );
             );
             field_installer_ = std::make_unique<FieldDataInstaller>(input_dir_, output_dir_);
             field_installer_ = std::make_unique<FieldDataInstaller>(input_dir_, output_dir_);
-            std::cout << "CREATE BATTLE INSTALLER: " << (input_dir_ + "data/battle/scene.bin") << "\n";
             battle_installer_ = std::make_unique<BattleDataInstaller>(input_dir_, output_dir_);
             battle_installer_ = std::make_unique<BattleDataInstaller>(input_dir_, output_dir_);
-            installation_state_ = BATTLE_INIT;
+            installation_state_ = BATTLE_SCENES_INIT;
             return CalcProgress();
             return CalcProgress();
-        case BATTLE_INIT:
+        case BATTLE_SCENES_INIT:
             write_output_line_("Parsing battle scenes...", 2, true);
             write_output_line_("Parsing battle scenes...", 2, true);
-            substeps_ = battle_installer_->Initialize();
+            substeps_ = battle_installer_->InitializeScenes();
             cur_substep_ = 0;
             cur_substep_ = 0;
-            installation_state_ = BATTLE_SCENES;
+            installation_state_ = BATTLE_SCENES_PROCESS;
             return CalcProgress();
             return CalcProgress();
-        case BATTLE_SCENES:
+        case BATTLE_SCENES_PROCESS:
             cur_substep_ = battle_installer_->ProcessScene();
             cur_substep_ = battle_installer_->ProcessScene();
-            if (cur_substep_ >= substeps_) installation_state_ = BATTLE_WRITE_ATTACKS;
+            if (cur_substep_ >= substeps_) installation_state_ = BATTLE_SCENES_WRITE_ATTACKS;
             return CalcProgress();
             return CalcProgress();
-        case BATTLE_WRITE_ATTACKS:
+        case BATTLE_SCENES_WRITE_ATTACKS:
             write_output_line_("Saving attacks...", 2, true);
             write_output_line_("Saving attacks...", 2, true);
             cur_substep_ = 0;
             cur_substep_ = 0;
             substeps_ = 0;
             substeps_ = 0;
             battle_installer_->WriteAttacks();
             battle_installer_->WriteAttacks();
-            installation_state_ = BATTLE_WRITE_ENEMIES;
+            installation_state_ = BATTLE_SCENES_WRITE_ENEMIES;
             return CalcProgress();
             return CalcProgress();
-        case BATTLE_WRITE_ENEMIES:
+        case BATTLE_SCENES_WRITE_ENEMIES:
             write_output_line_("Saving enemies...", 2, true);
             write_output_line_("Saving enemies...", 2, true);
             cur_substep_ = 0;
             cur_substep_ = 0;
             substeps_ = 0;
             substeps_ = 0;
             battle_installer_->WriteEnemies();
             battle_installer_->WriteEnemies();
-            installation_state_ = BATTLE_WRITE_FORMATIONS;
+            installation_state_ = BATTLE_SCENES_WRITE_FORMATIONS;
             return CalcProgress();
             return CalcProgress();
-        case BATTLE_WRITE_FORMATIONS:
+        case BATTLE_SCENES_WRITE_FORMATIONS:
             write_output_line_("Saving enemy formations...", 2, true);
             write_output_line_("Saving enemy formations...", 2, true);
             cur_substep_ = 0;
             cur_substep_ = 0;
             substeps_ = 0;
             substeps_ = 0;
             battle_installer_->WriteFormations();
             battle_installer_->WriteFormations();
-            installation_state_ = KERNEL_PRICES;
+            installation_state_ = BATTLE_MODELS_INIT;
+            return CalcProgress();
+        case BATTLE_MODELS_INIT:
+            write_output_line_("Extracting battle models...", 2, true);
+            substeps_ = battle_installer_->InitializeModels();
+            cur_substep_ = 0;
+            installation_state_ = BATTLE_MODELS_PROCESS;
+            return CalcProgress();
+        case BATTLE_MODELS_PROCESS:
+            cur_substep_ = battle_installer_->ProcessModel();
+            if (cur_substep_ >= substeps_) installation_state_ = BATTLE_MODELS_CONVERT_INIT;
+            return CalcProgress();
+        case BATTLE_MODELS_CONVERT_INIT:
+            cur_substep_ = 0;
+            substeps_ =  battle_installer_->ConvertModelsInit();
+            installation_state_ = BATTLE_MODELS_CONVERT;
+            return CalcProgress();
+        case BATTLE_MODELS_CONVERT:
+            cur_substep_ = battle_installer_->ConvertModel();
+            if (cur_substep_ >= substeps_) installation_state_ = KERNEL_PRICES;
             return CalcProgress();
             return CalcProgress();
         case KERNEL_PRICES:
         case KERNEL_PRICES:
             // Skip kernel data if option is set.
             // Skip kernel data if option is set.
@@ -351,6 +368,8 @@ const float DataInstaller::CalcProgress(){
 
 
 void DataInstaller::CreateDirectories(){
 void DataInstaller::CreateDirectories(){
     CreateDir("temp");
     CreateDir("temp");
+    CreateDir("temp/char");
+    CreateDir("temp/battle_models");
     CreateDir("game");
     CreateDir("game");
     CreateDir("game/enemy");
     CreateDir("game/enemy");
     CreateDir("game/attack");
     CreateDir("game/attack");
@@ -362,10 +381,24 @@ void DataInstaller::CreateDirectories(){
     CreateDir("images/reels");
     CreateDir("images/reels");
     CreateDir("images/window");
     CreateDir("images/window");
     CreateDir("models/fields/entities");
     CreateDir("models/fields/entities");
+    CreateDir("models/battle/entities");
     CreateDir("audio/sound");
     CreateDir("audio/sound");
     CreateDir("audio/music");
     CreateDir("audio/music");
-    application_.ResMgr()->addResourceLocation("data/temp/char/", "FileSystem", "FFVII", true, true);
-    application_.ResMgr()->addResourceLocation("data/models/", "FileSystem", "FFVII", true, true);
+    application_.ResMgr()->addResourceLocation(
+      output_dir_ + "temp/char/", "FileSystem", "FFVII", true, true
+    );
+    application_.ResMgr()->addResourceLocation(
+      output_dir_ + "temp/battle_models/", "FileSystem", "FFVII", true, true
+    );
+    application_.ResMgr()->addResourceLocation(
+      output_dir_ + "models/", "FileSystem", "FFVII", true, true
+    );
+    application_.ResMgr()->addResourceLocation(
+      output_dir_ + "models/fields/entities/", "FileSystem", "FFVIITextures", true, true
+    );
+    application_.ResMgr()->addResourceLocation(
+      output_dir_ + "models/battle/entities/", "FileSystem", "FFVIITextures", true, true
+    );
     fields_lgp_ = std::make_unique<ScopedLgp>(
     fields_lgp_ = std::make_unique<ScopedLgp>(
       application_.getRoot(), input_dir_ + "data/field/flevel.lgp", "LGP", "FFVIIFields"
       application_.getRoot(), input_dir_ + "data/field/flevel.lgp", "LGP", "FFVIIFields"
     );
     );

+ 26 - 6
src/installer/DataInstaller.h

@@ -166,29 +166,49 @@ class DataInstaller{
             INITIALIZE,
             INITIALIZE,
 
 
             /**
             /**
-             * Initializes the battle installer.
+             * Initializes the battle installer for scene processing.
              */
              */
-            BATTLE_INIT,
+            BATTLE_SCENES_INIT,
 
 
             /**
             /**
              * Extracts battle scenes.
              * Extracts battle scenes.
              */
              */
-            BATTLE_SCENES,
+            BATTLE_SCENES_PROCESS,
 
 
             /**
             /**
              * Writes attack data.
              * Writes attack data.
              */
              */
-            BATTLE_WRITE_ATTACKS,
+            BATTLE_SCENES_WRITE_ATTACKS,
 
 
             /**
             /**
              * Writes enemy data.
              * Writes enemy data.
              */
              */
-            BATTLE_WRITE_ENEMIES,
+            BATTLE_SCENES_WRITE_ENEMIES,
 
 
             /**
             /**
              * Writes enemy formation data.
              * Writes enemy formation data.
              */
              */
-            BATTLE_WRITE_FORMATIONS,
+            BATTLE_SCENES_WRITE_FORMATIONS,
+
+            /**
+             * Initializes the battle installer for 3D model processing.
+             */
+            BATTLE_MODELS_INIT,
+
+            /**
+             * Extracts 3D battle models.
+             */
+            BATTLE_MODELS_PROCESS,
+
+            /**
+             * Prepares for 3D battle model conversion.
+             */
+            BATTLE_MODELS_CONVERT_INIT,
+
+            /**
+             * Converts 3D battle models.
+             */
+            BATTLE_MODELS_CONVERT,
 
 
             /**
             /**
              * Parses item and materia prices from ff7.exe.
              * Parses item and materia prices from ff7.exe.

+ 7 - 12
src/installer/FieldDataInstaller.cpp

@@ -219,8 +219,8 @@ int FieldDataInstaller::CollectSpawnAndScaleFactorsInit(Ogre::ResourceGroupManag
     flevel_file_list_ = res_mgr->listResourceNames("FFVIIFields", "*");
     flevel_file_list_ = res_mgr->listResourceNames("FFVIIFields", "*");
     // Load the map list field.
     // Load the map list field.
     VGears::MapListFilePtr map_list = VGears::MapListFileManager::GetSingleton().load(
     VGears::MapListFilePtr map_list = VGears::MapListFileManager::GetSingleton().load(
-        "maplist", "FFVIIFields"
-      ).staticCast<VGears::MapListFile>();
+      "maplist", "FFVIIFields"
+    ).staticCast<VGears::MapListFile>();
     map_list_ = map_list->GetMapList();
     map_list_ = map_list->GetMapList();
     return flevel_file_list_->size();
     return flevel_file_list_->size();
 }
 }
@@ -258,7 +258,6 @@ void FieldDataInstaller::Convert(int field_index){
         // TODO: DEBUG: Only test fields
         // TODO: DEBUG: Only test fields
         if (IsTestField(resource_name) && !WillCrash(resource_name)){
         if (IsTestField(resource_name) && !WillCrash(resource_name)){
             //write_output_line_("Converting field " + resource_name);
             //write_output_line_("Converting field " + resource_name);
-            std::cout << " - Converting field: " << resource_name << std::endl;
             CreateDir(FIELD_MAPS_DIR + "/" + resource_name);
             CreateDir(FIELD_MAPS_DIR + "/" + resource_name);
             VGears::FLevelFilePtr field = VGears::LZSFLevelFileManager::GetSingleton().load(
             VGears::FLevelFilePtr field = VGears::LZSFLevelFileManager::GetSingleton().load(
                 resource_name, "FFVIIFields"
                 resource_name, "FFVIIFields"
@@ -301,7 +300,6 @@ void FieldDataInstaller::WriteEnd(){
 }
 }
 
 
 std::vector<std::string> FieldDataInstaller::ConvertModelsInit(){
 std::vector<std::string> FieldDataInstaller::ConvertModelsInit(){
-
     std::vector<std::string> models;
     std::vector<std::string> models;
 
 
     // Open char_lgp as a lgp archive
     // Open char_lgp as a lgp archive
@@ -324,7 +322,9 @@ 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 +351,13 @@ void FieldDataInstaller::ConvertModels(std::string model_name){
     }
     }
     catch (const Ogre::Exception& ex){
     catch (const Ogre::Exception& ex){
         write_output_line_(
         write_output_line_(
-          "[ERROR] Ogre exception converting model "
-          + model->first + ": " + ex.what()
+          "[ERROR] Ogre exception converting model " + model->first + ": " + ex.what()
         );
         );
         std::cerr << "[ERROR] Ogre exception converting model "
         std::cerr << "[ERROR] Ogre exception converting model "
           << model->first <<": " << ex.what() << std::endl;
           << 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()
-        );
+        write_output_line_("[ERROR] Exception converting model " + model->first + ": " + ex.what());
         std::cerr << "[ERROR] Exception converting model "
         std::cerr << "[ERROR] Exception converting model "
           << model->first << ": " << ex.what() << std::endl;
           << model->first << ": " << ex.what() << std::endl;
     }
     }
@@ -440,8 +437,6 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
                 }
                 }
             }
             }
             if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
             if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
-                //std::cout << "[MATERIAL] Writting material "
-                // << sub_mesh->getMaterialName() << std::endl;
                 mat_ser.queueForExport(mat);
                 mat_ser.queueForExport(mat);
                 materials_.push_back(sub_mesh->getMaterialName());
                 materials_.push_back(sub_mesh->getMaterialName());
             }
             }

+ 5 - 1
src/installer/MainWindow.cpp

@@ -240,7 +240,10 @@ void MainWindow::on_btn_data_run_clicked(){
             );
             );
             OnInstallStarted();
             OnInstallStarted();
         }
         }
-        catch (const std::exception& ex){OnInstallStopped();}
+        catch (const std::exception& ex){
+            std::cout << "Exception: " << ex.what() << std::endl;
+            OnInstallStopped();
+        }
     }
     }
 }
 }
 
 
@@ -277,6 +280,7 @@ void MainWindow::DoProgress(){
         if (progress >= 100) OnInstallStopped();
         if (progress >= 100) OnInstallStopped();
     }
     }
     catch (const std::exception& ex){
     catch (const std::exception& ex){
+        std::cout << "Exception: " << ex.what() << std::endl;
         OnInstallStopped();
         OnInstallStopped();
         QMessageBox::critical(this, tr("Data conversion exception"), ex.what());
         QMessageBox::critical(this, tr("Data conversion exception"), ex.what());
     }
     }

+ 44 - 9
src/installer/TexFile.cpp

@@ -17,12 +17,25 @@
 #include <iostream>
 #include <iostream>
 #include <OgreImage.h>
 #include <OgreImage.h>
 #include <OgreColourValue.h>
 #include <OgreColourValue.h>
-#include "common/File.h"
 #include "TexFile.h"
 #include "TexFile.h"
 
 
 TexFile::TexFile(std::string path){
 TexFile::TexFile(std::string path){
     File file(path);
     File file(path);
+    Read(file);
+}
+
+TexFile::TexFile(File file){
+    Read(file);
+}
+
+void TexFile::Read(File file){
+    file.SetOffset(0);
     version_ = file.readU32LE();
     version_ = file.readU32LE();
+    if (version_ != 0x00000001){
+        std::cout << "[WARNING] Tried to read an invalid TEX file to png. Version ID: "
+          << version_ << std::endl;
+        return;
+    }
     unknown_0_ = file.readU32LE();
     unknown_0_ = file.readU32LE();
     colour_key_ = file.readU32LE();
     colour_key_ = file.readU32LE();
     unknown_1_ = file.readU32LE();
     unknown_1_ = file.readU32LE();
@@ -83,6 +96,8 @@ TexFile::TexFile(std::string path){
     unknown_9_ = file.readU32LE();
     unknown_9_ = file.readU32LE();
     if (has_palette_ == 0){
     if (has_palette_ == 0){
         // Non palleted images.
         // Non palleted images.
+        // Skip unused pallete data.
+        for (int c = 0; c < palette_size_; c ++) file.readU32LE();
         // Read 32 byte colours in format BGRA.
         // Read 32 byte colours in format BGRA.
         for (int p = 0; p < width_ * height_; p ++){
         for (int p = 0; p < width_ * height_; p ++){
             float b = file.readU8() / 255.0f;
             float b = file.readU8() / 255.0f;
@@ -95,11 +110,11 @@ TexFile::TexFile(std::string path){
     }
     }
     else {
     else {
         // Palleted_images.
         // Palleted_images.
-
+        palette_count_ = palette_size_ / palette_colour_count_;
         // Read palette data.
         // Read palette data.
         for (int c = 0; c < palette_count_; c ++){
         for (int c = 0; c < palette_count_; c ++){
             std::vector<Ogre::ColourValue> palette_colours;
             std::vector<Ogre::ColourValue> palette_colours;
-            for (int p = 0; p < colours_per_palette_; p ++){
+            for (int p = 0; p < palette_colour_count_; p ++){
                 float b = file.readU8() / 255.0f;
                 float b = file.readU8() / 255.0f;
                 float g = file.readU8() / 255.0f;
                 float g = file.readU8() / 255.0f;
                 float r = file.readU8() / 255.0f;
                 float r = file.readU8() / 255.0f;
@@ -110,8 +125,7 @@ TexFile::TexFile(std::string path){
             palettes_.push_back(palette_colours);
             palettes_.push_back(palette_colours);
         }
         }
         // Read bytes as references to a palette colour.
         // Read bytes as references to a palette colour.
-        for (int p = 0; p < width_ * height_; p ++)
-            pixel_ref_.push_back(file.readU8());
+        for (int p = 0; p < width_ * height_; p ++) pixel_ref_.push_back(file.readU8());
     }
     }
 }
 }
 
 
@@ -121,6 +135,12 @@ void TexFile::SavePng(
   std::string file_name, unsigned int x, unsigned int y, unsigned int w, unsigned int h,
   std::string file_name, unsigned int x, unsigned int y, unsigned int w, unsigned int h,
   unsigned int palette
   unsigned int palette
 ){
 ){
+    if (version_ != 0x00000001){
+        std::cout
+          << "[WARNING] Tried to convert an invalid TEX file to png. Version ID: "
+          << version_ << std::endl;
+        return;
+    }
     // TODO: Warn if wrong palette index.
     // TODO: Warn if wrong palette index.
     Ogre::Image* image = new Ogre::Image(Ogre::PF_R8G8B8A8_UINT, w, h);
     Ogre::Image* image = new Ogre::Image(Ogre::PF_R8G8B8A8_UINT, w, h);
     int target_x = 0;
     int target_x = 0;
@@ -139,10 +159,19 @@ void TexFile::SavePng(
                 a = pixel_colour_.at(i).a;
                 a = pixel_colour_.at(i).a;
             }
             }
             else{
             else{
-                r = palettes_.at(palette).at(pixel_ref_.at(i)).r;
-                g = palettes_.at(palette).at(pixel_ref_.at(i)).g;
-                b = palettes_.at(palette).at(pixel_ref_.at(i)).b;
-                a = palettes_.at(palette).at(pixel_ref_.at(i)).a;
+                if (palettes_.at(palette).size() <= pixel_ref_.at(i)){
+                    // If invalid index, use alpha
+                    r = 0;
+                    g = 0;
+                    b = 0;
+                    a = 0;
+                }
+                else{
+                    r = palettes_.at(palette).at(pixel_ref_.at(i)).r;
+                    g = palettes_.at(palette).at(pixel_ref_.at(i)).g;
+                    b = palettes_.at(palette).at(pixel_ref_.at(i)).b;
+                    a = palettes_.at(palette).at(pixel_ref_.at(i)).a;
+                }
             }
             }
             image->setColourAt(Ogre::ColourValue(r, g, b, a), target_x, target_y, 0);
             image->setColourAt(Ogre::ColourValue(r, g, b, a), target_x, target_y, 0);
             target_x ++;
             target_x ++;
@@ -163,6 +192,12 @@ void TexFile::SavePng(
   std::string file_name, unsigned int x1, unsigned int x2, unsigned int y1, unsigned int y2,
   std::string file_name, unsigned int x1, unsigned int x2, unsigned int y1, unsigned int y2,
   unsigned int w1, unsigned int w2, unsigned int h, unsigned int palette
   unsigned int w1, unsigned int w2, unsigned int h, unsigned int palette
 ){
 ){
+    if (version_ != 0x00000001){
+        std::cout
+          << "[WARNING] Tried to convert an invalid TEX file to png. Version ID: "
+          << version_ << std::endl;
+        return;
+    }
     // TODO: Warn if wrong palette index.
     // TODO: Warn if wrong palette index.
     Ogre::Image* image = new Ogre::Image(Ogre::PF_R8G8B8A8_UINT, w1 + w2, h);
     Ogre::Image* image = new Ogre::Image(Ogre::PF_R8G8B8A8_UINT, w1 + w2, h);
     int target_x = 0;
     int target_x = 0;

+ 26 - 2
src/installer/TexFile.h

@@ -16,13 +16,30 @@
 #pragma once
 #pragma once
 
 
 #include "common/TypeDefine.h"
 #include "common/TypeDefine.h"
+#include "common/File.h"
 
 
 class TexFile{
 class TexFile{
 
 
     public:
     public:
 
 
+        /**
+         * Constructor.
+         *
+         * @param[in,out] File with the tex data. The file data will not be modified, but it's
+         * offset will.
+         */
+        TexFile(File file);
+
+        /**
+         * Constructor.
+         *
+         * @param[in] path Path to the tex file.
+         */
         TexFile(std::string path);
         TexFile(std::string path);
 
 
+        /**
+         * Destructor.
+         */
         ~TexFile();
         ~TexFile();
 
 
         /**
         /**
@@ -69,12 +86,19 @@ class TexFile{
          */
          */
         void SavePng(
         void SavePng(
           std::string file_name, unsigned int x1, unsigned int x2, unsigned int y1, unsigned int y2,
           std::string file_name, unsigned int x1, unsigned int x2, unsigned int y1, unsigned int y2,
-          unsigned int w1, unsigned int w2, unsigned int h, unsigned int palette
+          unsigned int w1, unsigned int w2, unsigned int h, unsigned int palette = 0
         );
         );
 
 
     private:
     private:
 
 
-        // HEADER
+        /**
+         * Reads data from a file.
+         *
+         * Called from constructors.
+         *
+         * @param[in] file The file to read from.
+         */
+        void Read(File file);
 
 
         /**
         /**
          * File format version. Always 1. 4 bytes.
          * File format version. Always 1. 4 bytes.

+ 1 - 4
src/main.cpp

@@ -54,7 +54,6 @@
  */
  */
 int main(int argc, char *argv[]){
 int main(int argc, char *argv[]){
     try{
     try{
-        std::cout << "V-Gears Init" << std::endl;
         VGears::Application app(argc, argv);
         VGears::Application app(argc, argv);
         if (!app.initOgre()) return 0;
         if (!app.initOgre()) return 0;
         Ogre::Root *root(app.getRoot());
         Ogre::Root *root(app.getRoot());
@@ -72,9 +71,7 @@ int main(int argc, char *argv[]){
         // TODO: Why is this used twice?
         // TODO: Why is this used twice?
         scene_manager->setAmbientLight(Ogre::ColourValue(1, 1, 1));
         scene_manager->setAmbientLight(Ogre::ColourValue(1, 1, 1));
         scene_manager->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
         scene_manager->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
-        Ogre::Light *directionalLight(
-          scene_manager->createLight("directionalLight")
-        );
+        Ogre::Light *directionalLight(scene_manager->createLight("directionalLight"));
         directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
         directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
         directionalLight->setDiffuseColour(Ogre::ColourValue(0.5, 0.5, 0.5));
         directionalLight->setDiffuseColour(Ogre::ColourValue(0.5, 0.5, 0.5));
         directionalLight->setSpecularColour(Ogre::ColourValue(0.0, 0.0, 0.0));
         directionalLight->setSpecularColour(Ogre::ColourValue(0.0, 0.0, 0.0));