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

Merge pull request #111 from paulsapps/installer

Most of field data now being installed correctly
paulsapps 11 жил өмнө
parent
commit
a37071b83c

+ 1 - 0
CIBuild/linux/dependencies.sh

@@ -8,5 +8,6 @@ sudo apt-get install libzzip-dev
 sudo apt-get install libois-dev
 sudo apt-get install libois-dev
 sudo apt-get install libglu-dev
 sudo apt-get install libglu-dev
 sudo apt-get install libqt4-dev
 sudo apt-get install libqt4-dev
+sudo apt-get install libpng
 wget https://github.com/paulsapps/Ogre1.9/releases/download/1.9.0/ogre-build_1.9.0-1_amd64.deb
 wget https://github.com/paulsapps/Ogre1.9/releases/download/1.9.0/ogre-build_1.9.0-1_amd64.deb
 sudo dpkg -i ogre-build_1.9.0-1_amd64.deb
 sudo dpkg -i ogre-build_1.9.0-1_amd64.deb

+ 0 - 1
Tests/FinalFantasy7/CMakeLists.txt

@@ -20,7 +20,6 @@ SET_PROPERTY(TARGET TestFF7FLevelFileSerializer PROPERTY FOLDER "tests")
 
 
 
 
 target_link_libraries(TestFF7FLevelFileSerializer ${LIBRARIES})
 target_link_libraries(TestFF7FLevelFileSerializer ${LIBRARIES})
-# TODO FIX ME - test not currently working
 #add_test(NAME TestFF7FLevelFileSerializer COMMAND TestFF7FLevelFileSerializer)
 #add_test(NAME TestFF7FLevelFileSerializer COMMAND TestFF7FLevelFileSerializer)
 
 
 
 

+ 5 - 2
Tests/FinalFantasy7/src/test_FF7FLevelFileSerializer.cpp

@@ -138,8 +138,10 @@ BOOST_AUTO_TEST_CASE( read_file )
     BOOST_CHECK_EQUAL( 2, camera_matrix->getCount() );
     BOOST_CHECK_EQUAL( 2, camera_matrix->getCount() );
     BOOST_CHECK_EQUAL( 12345, camera_matrix->getFocalLength() );
     BOOST_CHECK_EQUAL( 12345, camera_matrix->getFocalLength() );
 
 
+
     Ogre::Image *image( background->createImage( palette ) );
     Ogre::Image *image( background->createImage( palette ) );
-    image->save( file.getName() + ".png" );
+    // TODO: FIX ME ON OSX
+    //image->save( file.getName() + ".png" );
     delete image;
     delete image;
 
 
     QGears::FLevelFilePtr lzs_file = fmgr.load( "reference_compressed.flevel", "General" ).staticCast<QGears::FLevelFile>();
     QGears::FLevelFilePtr lzs_file = fmgr.load( "reference_compressed.flevel", "General" ).staticCast<QGears::FLevelFile>();
@@ -147,7 +149,8 @@ BOOST_AUTO_TEST_CASE( read_file )
     BOOST_CHECK( !lzs_file->getBackground().isNull() );
     BOOST_CHECK( !lzs_file->getBackground().isNull() );
 
 
     image = lzs_file->getBackground()->createImage( lzs_file->getPalette() );
     image = lzs_file->getBackground()->createImage( lzs_file->getPalette() );
-    image->save( lzs_file->getName() + ".png" );
+    // TODO: FIX ME ON OSX
+    //image->save( lzs_file->getName() + ".png" );
     delete image;
     delete image;
 
 
     lzs_file.setNull();
     lzs_file.setNull();

+ 219 - 60
utilities/q-gears-launcher/src/ff7DataInstaller.cpp

@@ -31,6 +31,8 @@
 #include "decompiler/sudm.h"
 #include "decompiler/sudm.h"
 #include <memory>
 #include <memory>
 
 
+#include <QDir>
+
 FF7DataInstaller::FF7DataInstaller()
 FF7DataInstaller::FF7DataInstaller()
 #ifdef _DEBUG
 #ifdef _DEBUG
     : mApp("plugins_d.cfg", "resources_d.cfg", "install_d.log")
     : mApp("plugins_d.cfg", "resources_d.cfg", "install_d.log")
@@ -48,21 +50,14 @@ FF7DataInstaller::~FF7DataInstaller()
 
 
 void FF7DataInstaller::Convert(std::string inputDir, std::string outputDir, const std::vector<std::string>& files)
 void FF7DataInstaller::Convert(std::string inputDir, std::string outputDir, const std::vector<std::string>& files)
 {
 {
+    // TODO: Just validate required files are present
     for (const auto& file : files)
     for (const auto& file : files)
     {
     {
-        if (file == "field/char.lgp")
-        {
-            /*
-            auto fullPath = inputDir + file;
-            mApp.getRoot()->addResourceLocation(fullPath, "LGP", "FFVII");
-            ConvertFieldModels(fullPath, outputDir);
-            mApp.getRoot()->removeResourceLocation(fullPath, "FFVII");*/
-        }
-        else if (file == "field/flevel.lgp")
+        if (file == "field/flevel.lgp")
         {
         {
             auto fullPath = inputDir + file;
             auto fullPath = inputDir + file;
             mApp.getRoot()->addResourceLocation(fullPath, "LGP", "FFVIIFields");
             mApp.getRoot()->addResourceLocation(fullPath, "LGP", "FFVIIFields");
-            ConvertFields(fullPath, outputDir);
+            ConvertFields(fullPath, inputDir, outputDir);
             mApp.getRoot()->removeResourceLocation(fullPath, "FFVIIFields");
             mApp.getRoot()->removeResourceLocation(fullPath, "FFVIIFields");
         }
         }
     }
     }
@@ -81,12 +76,43 @@ static void exportMesh(std::string outdir, const Ogre::MeshPtr &mesh)
     Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
     Ogre::Mesh::SubMeshIterator it(mesh->getSubMeshIterator());
     Ogre::MaterialSerializer    mat_ser;
     Ogre::MaterialSerializer    mat_ser;
     size_t i(0);
     size_t i(0);
+    std::set<std::string> textures;
     while (it.hasMoreElements())
     while (it.hasMoreElements())
     {
     {
         Ogre::SubMesh *sub_mesh(it.getNext());
         Ogre::SubMesh *sub_mesh(it.getNext());
         Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(sub_mesh->getMaterialName()));
         Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(sub_mesh->getMaterialName()));
         if (!mat.isNull())
         if (!mat.isNull())
         {
         {
+            for (size_t techNum = 0; techNum < mat->getNumTechniques(); techNum++)
+            {
+                Ogre::Technique* tech = mat->getTechnique(techNum);
+                if (tech)
+                {
+                    for (size_t passNum = 0; passNum < tech->getNumPasses(); passNum++)
+                    {
+                        Ogre::Pass* pass = tech->getPass(passNum);
+                        if (pass)
+                        {
+                            for (size_t textureUnitNum = 0; textureUnitNum < pass->getNumTextureUnitStates(); textureUnitNum++)
+                            {
+                                Ogre::TextureUnitState* unit = pass->getTextureUnitState(textureUnitNum);
+                                if (unit)
+                                {
+                                    if (unit->getTextureName().empty() == false)
+                                    {
+                                        // Ensure the output material script references png files rather than tex files
+                                        textures.insert(unit->getTextureName());
+                                        Ogre::String baseName;
+                                        QGears::StringUtil::splitBase(unit->getTextureName(), baseName);
+                                        unit->setTextureName(baseName + ".png");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
             mat_ser.queueForExport(mat);
             mat_ser.queueForExport(mat);
         }
         }
         ++i;
         ++i;
@@ -94,44 +120,42 @@ static void exportMesh(std::string outdir, const Ogre::MeshPtr &mesh)
     QGears::String base_name;
     QGears::String base_name;
     QGears::StringUtil::splitFull(mesh->getName(), base_name);
     QGears::StringUtil::splitFull(mesh->getName(), base_name);
     mat_ser.exportQueued(outdir + base_name + QGears::EXT_MATERIAL);
     mat_ser.exportQueued(outdir + base_name + QGears::EXT_MATERIAL);
-}
 
 
-
-void FF7DataInstaller::ConvertFieldModels(std::string archive, std::string outDir)
-{
-    Ogre::StringVectorPtr resources = mApp.ResMgr()->listResourceNames("FFVII", "*.hrc");
-    for (auto& resourceName : *resources)
+    for (auto& textureName : textures)
     {
     {
-        if (QGears::StringUtil::endsWith(resourceName, ".hrc"))
-        {
-            //try
-            {
-                Ogre::ResourcePtr hrc = QGears::HRCFileManager::getSingleton().load(resourceName, "FFVII");
+        Ogre::TexturePtr texturePtr = Ogre::TextureManager::getSingleton().load(textureName.c_str(), "FFVII");
+        Ogre::Image image;
+        texturePtr->convertToImage(image);
 
 
-                Ogre::String baseName;
-                QGears::StringUtil::splitBase(resourceName, baseName);
+        Ogre::String baseName;
+        QGears::StringUtil::splitBase(textureName, baseName);
+        image.save(outdir + baseName + ".png");
+    }
+}
 
 
-                auto meshName = QGears::FF7::NameLookup::model(baseName) + ".mesh";
+static std::string FieldName(const std::string& name)
+{
+    return "ffvii_" + name;
+}
 
 
-                Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(meshName, "FFVII"));
-                Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
-                exportMesh(outDir, mesh);
-            }
-           // catch (const Ogre::Exception& ex)
-            {
-               // std::cout << "ERROR converting: " << resourceName << " Exception: " << ex.what() << std::endl;
-            }
-        }
+void FF7DataInstaller::CreateDir(const std::string& path)
+{
+    QString target = QString::fromStdString(mOutputDir + path);
+    QDir dir(target);
+    if (!dir.mkpath("."))
+    {
+        throw std::runtime_error("Failed to mkpath");
     }
     }
+}
 
 
+static std::string FieldMapDir()
+{
+    return "maps/ffvii/field";
+}
 
 
-    /*
-    TODO:
-    To figure out what .a files relate to which .hrc files, we need to enumerate every field
-    and check its model loader data. These are always Idle, Walk, Run and then field specific
-    ordering.
-    Model loader is section 3 of the field file.
-    */
+static std::string FieldModelDir()
+{
+    return "models/ffvii/field/units";
 }
 }
 
 
 class FF7FieldScriptFormatter : public SUDM::IScriptFormatter
 class FF7FieldScriptFormatter : public SUDM::IScriptFormatter
@@ -213,6 +237,51 @@ public:
 };
 };
 typedef std::map<u16, SpawnPointDb> FieldSpawnPointsMap;
 typedef std::map<u16, SpawnPointDb> FieldSpawnPointsMap;
 
 
+typedef std::map<std::string, std::set<std::string>> ModelAnimationMap;
+class ModelsAndAnimationsDb
+{
+public:
+    std::string NormalizeAnimationName(const std::string& name)
+    {
+        Ogre::String baseName;
+        QGears::StringUtil::splitBase(name, baseName);
+        std::transform(baseName.begin(), baseName.end(), baseName.begin(), ::tolower);
+        return baseName + ".a";
+    }
+
+    std::set<std::string>& ModelAnimations(const std::string model)
+    {
+        // HACK FIX LGP READING
+        std::string modelLower = model;
+        std::transform(modelLower.begin(), modelLower.end(), modelLower.begin(), ::tolower);
+
+        return mMap[modelLower];
+    }
+
+    std::string ModelMetaDataName(const std::string& modelName)
+    {
+        // TODO: Convert to name thats in the meta data
+
+        // If not in meta data then just replace .hrc with .mesh
+        Ogre::String baseName;
+        QGears::StringUtil::splitBase(modelName, baseName);
+
+        return baseName + ".mesh";
+    }
+
+    std::string AnimationMetaDataName(const std::string& /*modelName*/, const std::string& animationName)
+    {
+        // TODO: Same as above
+        return animationName;
+    }
+
+//private:
+    ModelAnimationMap mMap;
+};
+
+typedef std::set<std::string> MapCollection;
+
+
 static size_t FieldId(const std::string& name, const std::vector<std::string>& fieldIdToNameLookup)
 static size_t FieldId(const std::string& name, const std::vector<std::string>& fieldIdToNameLookup)
 {
 {
     for (size_t i = 0; i < fieldIdToNameLookup.size(); i++)
     for (size_t i = 0; i < fieldIdToNameLookup.size(); i++)
@@ -225,11 +294,15 @@ static size_t FieldId(const std::string& name, const std::vector<std::string>& f
     throw std::runtime_error("No Id found for field name");
     throw std::runtime_error("No Id found for field name");
 }
 }
 
 
-static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::string& outDir, const std::vector<std::string>& fieldIdToNameLookup, const FieldSpawnPointsMap& spawnMap)
+static void FF7PcFieldToQGearsField(
+    QGears::FLevelFilePtr& field,
+    const std::string& outDir,
+    const std::vector<std::string>& fieldIdToNameLookup,
+    const FieldSpawnPointsMap& spawnMap,
+    ModelsAndAnimationsDb& modelAnimationDb,
+    MapCollection& maps)
 {
 {
-
-    // TODO: Insert triggers into LUA script
-  
+    // Generate triggers script to insert into main decompiled FF7 field -> LUA script
     std::string gatewayScriptData;
     std::string gatewayScriptData;
     const QGears::TriggersFilePtr& triggers = field->getTriggers();
     const QGears::TriggersFilePtr& triggers = field->getTriggers();
     const auto& gateways = triggers->GetGateways();
     const auto& gateways = triggers->GetGateways();
@@ -240,9 +313,7 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
         if (gateway.destinationFieldId != kInactiveGateWayId)
         if (gateway.destinationFieldId != kInactiveGateWayId)
         {
         {
             const std::string gatewayEntityName = "Gateway" + std::to_string(i);
             const std::string gatewayEntityName = "Gateway" + std::to_string(i);
-
-            // TODO: Obtain correct params
-            gatewayScriptData += CreateGateWayScript(gatewayEntityName, "ffvii_" + fieldIdToNameLookup.at(gateway.destinationFieldId), "Spawn_" + field->getName() + "_" + std::to_string(i));
+            gatewayScriptData += CreateGateWayScript(gatewayEntityName, FieldName(fieldIdToNameLookup.at(gateway.destinationFieldId)), "Spawn_" + field->getName() + "_" + std::to_string(i));
         }
         }
     }
     }
 
 
@@ -255,7 +326,7 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
         // Decompile to LUA
         // Decompile to LUA
         FF7FieldScriptFormatter formatter;
         FF7FieldScriptFormatter formatter;
         decompiled = SUDM::FF7::Field::Decompile(field->getName(), rawFieldData, formatter, gatewayScriptData, "EntityContainer = {}\n\n");
         decompiled = SUDM::FF7::Field::Decompile(field->getName(), rawFieldData, formatter, gatewayScriptData, "EntityContainer = {}\n\n");
-        std::ofstream scriptFile(outDir + "/" + field->getName() + ".lua");
+        std::ofstream scriptFile(outDir + "/" + FieldMapDir() + "/" + field->getName() + "/script.lua");
         if (scriptFile.is_open())
         if (scriptFile.is_open())
         {
         {
             scriptFile << decompiled.luaScript;
             scriptFile << decompiled.luaScript;
@@ -278,17 +349,17 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
 
 
         // Script
         // Script
         std::unique_ptr<TiXmlElement> xmlScriptElement(new TiXmlElement("script"));
         std::unique_ptr<TiXmlElement> xmlScriptElement(new TiXmlElement("script"));
-        xmlScriptElement->SetAttribute("file_name", field->getName() + ".lua");
+        xmlScriptElement->SetAttribute("file_name", FieldMapDir() + "/" + field->getName() + "/script.lua");
         element->LinkEndChild(xmlScriptElement.release());
         element->LinkEndChild(xmlScriptElement.release());
 
 
         // Background
         // Background
         std::unique_ptr<TiXmlElement> xmlBackground2d(new TiXmlElement("background2d"));
         std::unique_ptr<TiXmlElement> xmlBackground2d(new TiXmlElement("background2d"));
-        xmlBackground2d->SetAttribute("file_name", field->getName() + "_bg.xml");
+        xmlBackground2d->SetAttribute("file_name", FieldMapDir() + "/" + field->getName() + "/bg.xml");
         element->LinkEndChild(xmlBackground2d.release());
         element->LinkEndChild(xmlBackground2d.release());
         
         
         // walkmesh
         // walkmesh
         std::unique_ptr<TiXmlElement> xmlWalkmesh(new TiXmlElement("walkmesh"));
         std::unique_ptr<TiXmlElement> xmlWalkmesh(new TiXmlElement("walkmesh"));
-        xmlWalkmesh->SetAttribute("file_name", field->getName() + "_wm.xml");
+        xmlWalkmesh->SetAttribute("file_name", FieldMapDir() + "/" + field->getName() + "/wm.xml");
         element->LinkEndChild(xmlWalkmesh.release());
         element->LinkEndChild(xmlWalkmesh.release());
 
 
         // Angle player moves when "up" is pressed
         // Angle player moves when "up" is pressed
@@ -304,11 +375,17 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
             {
             {
                 const QGears::ModelListFile::ModelDescription& desc = models->getModels().at(charId);
                 const QGears::ModelListFile::ModelDescription& desc = models->getModels().at(charId);
 
 
+                auto& animVec = modelAnimationDb.ModelAnimations(desc.hrc_name);
+                for (auto& anim : desc.animations)
+                {
+                    animVec.insert(modelAnimationDb.NormalizeAnimationName(anim.name));
+                }
+
                 std::unique_ptr<TiXmlElement> xmlEntityScript(new TiXmlElement("entity_model"));
                 std::unique_ptr<TiXmlElement> xmlEntityScript(new TiXmlElement("entity_model"));
                 xmlEntityScript->SetAttribute("name", it.first);
                 xmlEntityScript->SetAttribute("name", it.first);
 
 
                 // TODO: Add to list of HRC's to convert, obtain name of target converted .mesh file
                 // TODO: Add to list of HRC's to convert, obtain name of target converted .mesh file
-                xmlEntityScript->SetAttribute("file_name", desc.hrc_name);
+                xmlEntityScript->SetAttribute("file_name", FieldModelDir() + "/" + modelAnimationDb.ModelMetaDataName(desc.hrc_name));
 
 
                 // TODO: entity_model - name, file_name,  position, direction
                 // TODO: entity_model - name, file_name,  position, direction
                 // We set char 1 position to be position of first entity_point so player is spawned in sane
                 // We set char 1 position to be position of first entity_point so player is spawned in sane
@@ -393,14 +470,14 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
         }
         }
 
 
         doc.LinkEndChild(element.release());
         doc.LinkEndChild(element.release());
-        doc.SaveFile(outDir + "/" + field->getName() + ".xml");
+        doc.SaveFile(outDir + "/" + FieldMapDir() + "/" + field->getName() + "/map.xml");
     }
     }
 
 
 
 
     const QGears::PaletteFilePtr& pal = field->getPalette();
     const QGears::PaletteFilePtr& pal = field->getPalette();
     const QGears::BackgroundFilePtr& bg = field->getBackground();
     const QGears::BackgroundFilePtr& bg = field->getBackground();
     std::unique_ptr<Ogre::Image> bgImage(bg->createImage(pal));
     std::unique_ptr<Ogre::Image> bgImage(bg->createImage(pal));
-    bgImage->save(outDir + "/" + field->getName() + ".png");
+    bgImage->save(outDir + "/" + FieldMapDir() + "/" + field->getName() + "/tiles.png");
 
 
     {
     {
         TiXmlDocument doc;
         TiXmlDocument doc;
@@ -426,7 +503,7 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
         const int max_x = triggers->getCameraRange().right * kScaleUpFactor;
         const int max_x = triggers->getCameraRange().right * kScaleUpFactor;
         const int max_y = triggers->getCameraRange().bottom * kScaleUpFactor;
         const int max_y = triggers->getCameraRange().bottom * kScaleUpFactor;
 
 
-        element->SetAttribute("image", field->getName() + ".png");
+        element->SetAttribute("image", FieldMapDir() + "/" + field->getName() + "/tiles.png");
         element->SetAttribute("position", Ogre::StringConverter::toString(position));
         element->SetAttribute("position", Ogre::StringConverter::toString(position));
         element->SetAttribute("orientation", Ogre::StringConverter::toString(orientation));
         element->SetAttribute("orientation", Ogre::StringConverter::toString(orientation));
         element->SetAttribute("fov", Ogre::StringConverter::toString(fov));
         element->SetAttribute("fov", Ogre::StringConverter::toString(fov));
@@ -493,7 +570,7 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
             }
             }
         }
         }
         doc.LinkEndChild(element.release());
         doc.LinkEndChild(element.release());
-        doc.SaveFile(outDir + "/" + field->getName() + "_bg.xml");
+        doc.SaveFile(outDir + "/" + FieldMapDir() + "/" + field->getName() +"/bg.xml");
     }
     }
 
 
     {
     {
@@ -514,8 +591,10 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
             element->LinkEndChild(xmlElement.release());
             element->LinkEndChild(xmlElement.release());
         }
         }
         doc.LinkEndChild(element.release());
         doc.LinkEndChild(element.release());
-        doc.SaveFile(outDir + "/" + field->getName() + "_wm.xml");
+        doc.SaveFile(outDir + "/" + FieldMapDir() + "/" + field->getName() + "/wm.xml");
     }
     }
+
+    maps.insert(field->getName());
 }
 }
 
 
 
 
@@ -566,8 +645,33 @@ static bool IsAFieldFile(Ogre::String& resourceName)
         && resourceName != "maplist");
         && resourceName != "maplist");
 }
 }
 
 
-void FF7DataInstaller::ConvertFields(std::string archive, std::string outDir)
+static bool IsTestField(Ogre::String& resourceName)
 {
 {
+    if (
+        resourceName == "startmap" ||
+        resourceName == "md1stin" ||
+        resourceName == "md1_1" ||
+        resourceName == "md1_2" ||
+        resourceName == "nrthmk" ||
+        resourceName == "nmkin_1" ||
+        resourceName == "elevtr1" ||
+        resourceName == "nmkin_2" ||
+        resourceName == "nmkin_3" ||
+        resourceName == "tin_2"
+        )
+    {
+        return true;
+    }
+    return false;
+}
+
+void FF7DataInstaller::ConvertFields(std::string archive, std::string inputDir, std::string outDir)
+{
+    mOutputDir = outDir;
+
+    CreateDir(FieldMapDir());
+    CreateDir(FieldModelDir());
+
     // List whats in the LGP archive
     // List whats in the LGP archive
     Ogre::StringVectorPtr resources = mApp.ResMgr()->listResourceNames("FFVIIFields", "*");
     Ogre::StringVectorPtr resources = mApp.ResMgr()->listResourceNames("FFVIIFields", "*");
 
 
@@ -586,18 +690,73 @@ void FF7DataInstaller::ConvertFields(std::string archive, std::string outDir)
         }
         }
     }
     }
 
 
+    ModelsAndAnimationsDb modelAnimationDb;
+    MapCollection maps;
+
     // Now we can do the full conversion with the collated data
     // Now we can do the full conversion with the collated data
     for (auto& resourceName : *resources)
     for (auto& resourceName : *resources)
     {
     {
         // Exclude things that are not fields
         // Exclude things that are not fields
         if (IsAFieldFile(resourceName))
         if (IsAFieldFile(resourceName))
         {
         {
-            if (resourceName == "md1_2") // Testing conversion only on this field for now
+            if (IsTestField(resourceName))
             {
             {
+                CreateDir(FieldMapDir() + "/" + resourceName);
+
                 QGears::FLevelFilePtr field = QGears::LZSFLevelFileManager::getSingleton().load(resourceName, "FFVIIFields").staticCast<QGears::FLevelFile>();
                 QGears::FLevelFilePtr field = QGears::LZSFLevelFileManager::getSingleton().load(resourceName, "FFVIIFields").staticCast<QGears::FLevelFile>();
-                FF7PcFieldToQGearsField(field, outDir, mapList->GetMapList(), spawnPoints);
+                FF7PcFieldToQGearsField(field, outDir, mapList->GetMapList(), spawnPoints, modelAnimationDb, maps);
             }
             }
         }
         }
     }
     }
     
     
+    {
+        // Write out maps.xml
+        TiXmlDocument doc;
+        std::unique_ptr<TiXmlElement> element(new TiXmlElement("maps"));
+
+        // TODO: Probably need to inject "empty" and "test" fields
+
+        for (const auto& map : maps)
+        {
+            std::unique_ptr<TiXmlElement> xmlElement(new TiXmlElement("map"));
+            xmlElement->SetAttribute("name", FieldName(map));
+            xmlElement->SetAttribute("file_name", FieldMapDir() + "/" + map + "/map.xml");
+            element->LinkEndChild(xmlElement.release());
+        }
+        doc.LinkEndChild(element.release());
+        doc.SaveFile(outDir + "/maps.xml");
+    }
+
+    // TODO: Convert models and animations in modelAnimationDb
+    auto fullPath = inputDir + "field/char.lgp";
+    mApp.getRoot()->addResourceLocation(fullPath, "LGP", "FFVII");
+    Ogre::StringVectorPtr resources2 = mApp.ResMgr()->listResourceNames("FFVII", "*");
+
+
+    for (auto& name : modelAnimationDb.mMap)
+    {
+        Ogre::ResourcePtr hrc = QGears::HRCFileManager::getSingleton().load(name.first, "FFVII");
+
+        Ogre::String baseName;
+        QGears::StringUtil::splitBase(name.first, baseName);
+
+        auto meshName = QGears::FF7::NameLookup::model(baseName) + ".mesh";
+
+        Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(meshName, "FFVII"));
+        Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
+        
+        for (auto& anim : name.second)
+        {
+            QGears::AFileManager       &afl_mgr(QGears::AFileManager::getSingleton());
+            QGears::AFilePtr  a = afl_mgr.load(anim, "FFVII").staticCast<QGears::AFile>();
+            a->addTo(skeleton, anim); // TODO: Set "friendly" name, also update this name in the scripts
+        }
+
+        exportMesh(outDir + "/" + FieldModelDir() + "/", mesh);
+
+    }
+
+    mApp.getRoot()->removeResourceLocation(fullPath, "FFVII");
+
+
 }
 }

+ 3 - 2
utilities/q-gears-launcher/src/ff7DataInstaller.h

@@ -13,8 +13,9 @@ public:
     void Convert(std::string inputDir, std::string outputDir, const std::vector<std::string>& files);
     void Convert(std::string inputDir, std::string outputDir, const std::vector<std::string>& files);
 
 
 private:
 private:
-    void ConvertFieldModels(std::string archive, std::string outDir);
-    void ConvertFields(std::string archive, std::string outDir);
+    void CreateDir(const std::string& dir);
+    void ConvertFields(std::string archive, std::string inDir, std::string outDir);
 
 
+    std::string mOutputDir;
     QGears::Application mApp;
     QGears::Application mApp;
 };
 };