Browse Source

add progress to data installer, still barfs on some bone error, now "bone with name XYZ not found" is thrown

Paul 11 năm trước cách đây
mục cha
commit
5e85833eea

+ 18 - 9
QGearsMain/src/data/QGearsAFile.cpp

@@ -20,6 +20,7 @@ GNU General Public License for more details.
 #include <OgreLogManager.h>
 
 #include "data/QGearsAFileSerializer.h"
+#include "core/Logger.h"
 
 namespace QGears
 {
@@ -120,16 +121,24 @@ namespace QGears
         }
         for( uint32 i(0); i < m_bone_count; ++i )
         {
-            bone = skeleton->getBone( i + 1 );
-            track = anim->createNodeTrack( track_handle++, bone );
-            time = 0;
-            for( FrameList::const_iterator frame( m_frames.begin())
-                ;frame != m_frames.end(); ++frame )
+            if (i + 1 >= skeleton->getNumBones())
             {
-                const Ogre::Vector3& rot( frame->bone_rotations[i] );
-                Ogre::TransformKeyFrame* key_frame( track->createNodeKeyFrame( time ) );
-                setFrameRotation( key_frame, rot );
-                time += FRAME_DURATION;
+                // TODO: Figure out why this happens/fix it
+                LOG_ERROR("Bone " + std::to_string(i + 1) + "  is out of bounds " + std::to_string(skeleton->getNumBones()) + " for: " + name + " in: " + skeleton->getName());
+            }
+            else
+            {
+                bone = skeleton->getBone(i + 1);
+                track = anim->createNodeTrack(track_handle++, bone);
+                time = 0;
+                for (FrameList::const_iterator frame(m_frames.begin())
+                    ; frame != m_frames.end(); ++frame)
+                {
+                    const Ogre::Vector3& rot(frame->bone_rotations[i]);
+                    Ogre::TransformKeyFrame* key_frame(track->createNodeKeyFrame(time));
+                    setFrameRotation(key_frame, rot);
+                    time += FRAME_DURATION;
+                }
             }
         }
     }

+ 182 - 127
utilities/q-gears-launcher/src/ff7DataInstaller.cpp

@@ -24,23 +24,27 @@
 #include "map/QGearsWalkmeshFileManager.h"
 #include "data/FF7ModelListFileManager.h"
 #include "data/QGearsLGPArchiveFactory.h"
-#include "common/QGearsStringUtil.h"
 #include "common/FF7NameLookup.h"
 #include "data/QGearsTexCodec.h"
 #include "data/QGearsMapListFile.h"
 #include "decompiler/sudm.h"
 #include <memory>
-
 #include <QDir>
+#include "common/make_unique.h"
 
-FF7DataInstaller::FF7DataInstaller()
+FF7DataInstaller::FF7DataInstaller(std::string inputDir, std::string outputDir)
 #ifdef _DEBUG
-    : mApp("plugins_d.cfg", "resources_d.cfg", "install_d.log")
+    : mApp("plugins_d.cfg", "resources_d.cfg", "install_d.log"),
 #else
-    : mApp("plugins.cfg", "resources.cfg", "install.log")
+    : mApp("plugins.cfg", "resources.cfg", "install.log"),
 #endif
+    mInputDir(inputDir), mOutputDir(outputDir)
 {
-    mApp.initOgre(true);
+    if (!mApp.initOgre(true))
+    {
+        throw std::exception("Ogre init failure");
+    }
+
 }
 
 FF7DataInstaller::~FF7DataInstaller()
@@ -48,18 +52,51 @@ FF7DataInstaller::~FF7DataInstaller()
 
 }
 
-void FF7DataInstaller::Convert(std::string inputDir, std::string outputDir, const std::vector<std::string>& files)
+int FF7DataInstaller::CalcProgress()
+{
+    // TODO: Make more accurate with mIteratorCounter and mProgressStepNumElements
+    float curStep = mState / static_cast<float>(eMaxStates);
+    curStep = curStep * 100.0f;
+    return static_cast<int>(curStep);
+}
+
+int FF7DataInstaller::Progress()
 {
-    // TODO: Just validate required files are present
-    for (const auto& file : files)
+    switch (mState)
     {
-        if (file == "field/flevel.lgp")
-        {
-            auto fullPath = inputDir + file;
-            mApp.getRoot()->addResourceLocation(fullPath, "LGP", "FFVIIFields");
-            ConvertFields(fullPath, inputDir, outputDir);
-            mApp.getRoot()->removeResourceLocation(fullPath, "FFVIIFields");
-        }
+    case eIdle:
+        mProgressStepNumElements = 1;
+        mIteratorCounter = 0;
+        mFieldsLgp = std::make_unique<ScopedLgp>(mApp.getRoot(), mInputDir + "field/flevel.lgp", "LGP", "FFVIIFields");
+        mState = eInitCollectFieldSpawnPointsAndScaleFactors;
+        return CalcProgress();
+    case eInitCollectFieldSpawnPointsAndScaleFactors:
+        InitCollectSpawnAndScaleFactors();
+        return CalcProgress();
+    case eCollectFieldSpawnPointsAndScaleFactors:
+        CollectionFieldSpawnAndScaleFactors();
+        return CalcProgress();
+    case eConvertFieldsIteration:
+        ConvertFieldsIteration();
+        return CalcProgress();
+    case eWriteMapListOfConvertedFieldsStart:
+        WriteMapsXmlBegin();
+        return CalcProgress();
+    case eWriteMapListOfConvertedFieldsIteration:
+        WriteMapsXmlIteration();
+        return CalcProgress();
+    case eWriteMapListOfConvertedFieldsEnd:
+        EndWriteMapsXml();
+        return CalcProgress();
+    case eConvertFieldModelsBegin:
+        ConvertFieldModelsBegin();
+        return CalcProgress();
+    case eConvertFieldModelsIteration:
+        ConvertFieldModelsIteration();
+        return CalcProgress();
+    case eDone:
+    default:
+        return 100;
     }
 }
 
@@ -165,37 +202,6 @@ static std::string FieldMapDir()
     return "maps/ffvii/field";
 }
 
-typedef std::set<std::string> MapCollection;
-
-class SpawnPointDb
-{
-public:
-    // Id of the field the gateways records from N other number of fields are linking to
-    u16 mTargetFieldId = 0;
-
-    class Record
-    {
-    public:
-        // Field that wants to link to mTargetFieldId
-        u16 mFieldId = 0;
-
-        // Index of the gateway in mFieldId
-        u32 GatewayIndexOrMapJumpAddress = 0;
-
-        // Gateway data
-        QGears::TriggersFile::Gateway mGateway;
-
-        bool mFromScript = false;
-
-        // Only used from script calls
-        std::string mEntityName;
-        std::string mScriptFunctionName;
-    };
-
-    std::vector<Record> mGatewaysToThisField;
-};
-typedef std::map<u16, SpawnPointDb> FieldSpawnPointsMap;
-
 class BaseFF7FieldScriptFormatter : public SUDM::IScriptFormatter
 {
 public:
@@ -321,42 +327,6 @@ static std::string CreateGateWayScript(const std::string& gatewayEntityName, con
 const int kInactiveGateWayId = 32767;
 
 
-typedef std::map<u16, float> FieldScaleFactorMap;
-
-
-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)
-    {
-        // If not in meta data then just replace .hrc with .mesh
-        Ogre::String baseName;
-        QGears::StringUtil::splitBase(modelName, baseName);
-        return QGears::FF7::NameLookup::model(baseName) + ".mesh";
-    }
-
-//private:
-    ModelAnimationMap mMap;
-};
-
 
 
 static size_t FieldId(const std::string& name, const std::vector<std::string>& fieldIdToNameLookup)
@@ -896,95 +866,178 @@ static void CollectFieldScaleFactors(QGears::FLevelFilePtr& field, FieldScaleFac
     scaleFactors[FieldId(field->getName(), fieldIdToNameLookup)] = ::SUDM::FF7::Field::ScaleFactor(field->getRawScript());
 }
 
-void FF7DataInstaller::ConvertFields(std::string archive, std::string inputDir, std::string outDir)
+void FF7DataInstaller::InitCollectSpawnAndScaleFactors()
 {
-    mOutputDir = outDir;
+    mProgressStepNumElements = 1;
 
     CreateDir(FieldMapDir());
     CreateDir(FieldModelDir());
 
     // List whats in the LGP archive
-    Ogre::StringVectorPtr resources = mApp.ResMgr()->listResourceNames("FFVIIFields", "*");
+    mFLevelFileList = mApp.ResMgr()->listResourceNames("FFVIIFields", "*");
 
     // Load the map list field
     QGears::MapListFilePtr mapList = QGears::MapListFileManager::getSingleton().load("maplist", "FFVIIFields").staticCast<QGears::MapListFile>();
+    mMapList = mapList->GetMapList();
+
+    mIteratorCounter = 0;
+    mConversionStep = 0;
+    mState = eCollectFieldSpawnPointsAndScaleFactors;
+}
 
+void FF7DataInstaller::CollectionFieldSpawnAndScaleFactors()
+{
     // On the first pass collate required field information
-    FieldSpawnPointsMap spawnPoints;
-    FieldScaleFactorMap scaleFactors;
-    for (auto& resourceName : *resources)
+    mProgressStepNumElements = mFLevelFileList->size();
+    if (mIteratorCounter < mFLevelFileList->size())
     {
+        auto resourceName = (*mFLevelFileList)[mIteratorCounter];
         // Exclude things that are not fields
-        if (IsAFieldFile(resourceName) && /*IsTestField(resourceName) &&*/ !WillCrash(resourceName))
+        if (IsAFieldFile(resourceName) /*&& IsTestField(resourceName)*/)
         {
-            QGears::FLevelFilePtr field = QGears::LZSFLevelFileManager::getSingleton().load(resourceName, "FFVIIFields").staticCast<QGears::FLevelFile>();
-            CollectSpawnPoints(field, mapList->GetMapList(), spawnPoints);
-            CollectFieldScaleFactors(field, scaleFactors, mapList->GetMapList());
+            mConversionStep++;
+
+            if (mConversionStep == 1)
+            {
+                mField = QGears::LZSFLevelFileManager::getSingleton().load(resourceName, "FFVIIFields").staticCast<QGears::FLevelFile>();
+                return;
+            }
+
+            if (mConversionStep == 2)
+            {
+                CollectSpawnPoints(mField, mMapList, mCollectedSpawnPoints);
+                return;
+            }
+           
+            if (mConversionStep == 3)
+            {
+                CollectFieldScaleFactors(mField, mCollectedScaleFactors, mMapList);
+                mConversionStep = 0;
+                mIteratorCounter++;
+                return;
+            }
+        }
+        else
+        {
+            mIteratorCounter++;
         }
     }
+    else
+    {
+        mField.setNull();
+        mIteratorCounter = 0;
+        mState = eConvertFieldsIteration;
+    }
+}
 
-    ModelsAndAnimationsDb modelAnimationDb;
-    MapCollection maps;
-
+void FF7DataInstaller::ConvertFieldsIteration()
+{
     // Now we can do the full conversion with the collated data
-    for (auto& resourceName : *resources)
+    mProgressStepNumElements = mFLevelFileList->size();
+    if (mIteratorCounter < mFLevelFileList->size())
     {
+        auto resourceName = (*mFLevelFileList)[mIteratorCounter];
+
         // Exclude things that are not fields
         if (IsAFieldFile(resourceName))
         {
-            if (IsTestField(resourceName) &&
+            mIteratorCounter++;
+
+            if (/*IsTestField(resourceName) &&*/
                 !WillCrash(resourceName))
             {
                 std::cout << "Converting: " << resourceName << std::endl;
                 CreateDir(FieldMapDir() + "/" + resourceName);
 
                 QGears::FLevelFilePtr field = QGears::LZSFLevelFileManager::getSingleton().load(resourceName, "FFVIIFields").staticCast<QGears::FLevelFile>();
-                FF7PcFieldToQGearsField(field, outDir, mapList->GetMapList(), spawnPoints, scaleFactors, modelAnimationDb, maps);
+                FF7PcFieldToQGearsField(field, mOutputDir, mMapList, mCollectedSpawnPoints, mCollectedScaleFactors, mModelsAndAnimationsUsedByConvertedFields, mConvertedMapList);
             }
             else
             {
                 std::cout << "Skip: " << resourceName << " as it has a crash or hang issue" << std::endl;
             }
         }
+        else
+        {
+            mIteratorCounter++;
+        }
     }
-    
+    else
     {
-        // Write out maps.xml
-        TiXmlDocument doc;
-        std::unique_ptr<TiXmlElement> element(new TiXmlElement("maps"));
+        mIteratorCounter = 0;
+        mState = eWriteMapListOfConvertedFieldsStart;
+    }
+}
 
-        // TODO: Probably need to inject "empty" and "test" fields
+void FF7DataInstaller::WriteMapsXmlBegin()
+{
+    // Write out maps.xml
+    mProgressStepNumElements = 1;
+    mDoc = std::make_unique<TiXmlDocument>();
+    mElement = std::make_unique<TiXmlElement>("maps");
+    mIteratorCounter = 0;
+    mState = eWriteMapListOfConvertedFieldsIteration;
+    mIteratorCounter = 0;
+    mConvertedMapListIt = mConvertedMapList.begin();
+}
 
-        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");
+void FF7DataInstaller::WriteMapsXmlIteration()
+{
+    // TODO: Probably need to inject "empty" and "test" fields
+    mProgressStepNumElements = mConvertedMapList.size();
+    if (mConvertedMapListIt != mConvertedMapList.end())
+    {
+        auto map = *mConvertedMapListIt;
+        std::unique_ptr<TiXmlElement> xmlElement(new TiXmlElement("map"));
+        xmlElement->SetAttribute("name", FieldName(map));
+        xmlElement->SetAttribute("file_name", FieldMapDir() + "/" + map + "/map.xml");
+        mElement->LinkEndChild(xmlElement.release());
+
+        mIteratorCounter++;
+        mConvertedMapListIt++;
     }
+    else
+    {
+        mState = eWriteMapListOfConvertedFieldsEnd;
+    }
+}
 
-    // 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", "*");
+void FF7DataInstaller::EndWriteMapsXml()
+{
+    mProgressStepNumElements = 1;
+    mDoc->LinkEndChild(mElement.release());
+    mDoc->SaveFile(mOutputDir + "/maps.xml");
+    mState = eConvertFieldModelsBegin;
+}
 
+void FF7DataInstaller::ConvertFieldModelsBegin()
+{
+    // TODO: Convert models and animations in modelAnimationDb
+    mProgressStepNumElements = 1;
+    mFieldModelsLgp = std::make_unique<ScopedLgp>(mApp.getRoot(), mInputDir + "field/char.lgp", "LGP", "FFVII");
+    mFieldModelFileList = mApp.ResMgr()->listResourceNames("FFVII", "*");
+    mIteratorCounter = 0;
+    mState = eConvertFieldModelsIteration;
+    mIteratorCounter = 0;
+    mModelAnimationMapIterator = mModelsAndAnimationsUsedByConvertedFields.mMap.begin();
+}
 
-    for (auto& name : modelAnimationDb.mMap)
+void FF7DataInstaller::ConvertFieldModelsIteration()
+{
+    mProgressStepNumElements = mModelsAndAnimationsUsedByConvertedFields.mMap.size();
+    if (mModelAnimationMapIterator != mModelsAndAnimationsUsedByConvertedFields.mMap.end())
     {
-        Ogre::ResourcePtr hrc = QGears::HRCFileManager::getSingleton().load(name.first, "FFVII");
+        Ogre::ResourcePtr hrc = QGears::HRCFileManager::getSingleton().load(mModelAnimationMapIterator->first, "FFVII");
 
         Ogre::String baseName;
-        QGears::StringUtil::splitBase(name.first, baseName);
+        QGears::StringUtil::splitBase(mModelAnimationMapIterator->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)
+
+        for (auto& anim : mModelAnimationMapIterator->second)
         {
             QGears::AFileManager       &afl_mgr(QGears::AFileManager::getSingleton());
             QGears::AFilePtr  a = afl_mgr.load(anim, "FFVII").staticCast<QGears::AFile>();
@@ -995,11 +1048,13 @@ void FF7DataInstaller::ConvertFields(std::string archive, std::string inputDir,
             a->addTo(skeleton, QGears::FF7::NameLookup::animation(baseName));
         }
 
-        exportMesh(outDir + "/" + FieldModelDir() + "/", mesh);
+        exportMesh(mOutputDir + "/" + FieldModelDir() + "/", mesh);
 
+        mIteratorCounter++;
+        mModelAnimationMapIterator++;
+    }
+    else
+    {
+        mState = eDone;
     }
-
-    mApp.getRoot()->removeResourceLocation(fullPath, "FFVII");
-
-
 }

+ 154 - 5
utilities/q-gears-launcher/src/ff7DataInstaller.h

@@ -4,18 +4,167 @@
 #include <vector>
 #include "common/make_unique.h"
 #include "common/QGearsApplication.h"
+#include "common/FF7NameLookup.h"
+#include "data/QGearsTriggersFile.h"
+#include "common/QGearsStringUtil.h"
+#include "data/QGearsFLevelFile.h"
+
+class ScopedLgp
+{
+public:
+    ScopedLgp(const ScopedLgp&) = delete;
+    ScopedLgp& operator = (const ScopedLgp&) = delete;
+    ScopedLgp(Ogre::Root* root, std::string fullPath, std::string type, std::string group)
+        : mRoot(root), mFullPath(fullPath), mGroup(group)
+    {
+        if (mRoot)
+        {
+            mRoot->addResourceLocation(mFullPath, type, mGroup);
+        }
+    }
+
+    ~ScopedLgp()
+    {
+        if (mRoot)
+        {
+            mRoot->removeResourceLocation(mFullPath, mGroup);
+        }
+    }
+
+private:
+    Ogre::Root* mRoot;
+    std::string mFullPath;
+    std::string mGroup;
+};
+
+
+typedef std::set<std::string> MapCollection;
+
+class SpawnPointDb
+{
+public:
+    // Id of the field the gateways records from N other number of fields are linking to
+    u16 mTargetFieldId = 0;
+
+    class Record
+    {
+    public:
+        // Field that wants to link to mTargetFieldId
+        u16 mFieldId = 0;
+
+        // Index of the gateway in mFieldId
+        u32 GatewayIndexOrMapJumpAddress = 0;
+
+        // Gateway data
+        QGears::TriggersFile::Gateway mGateway;
+
+        bool mFromScript = false;
+
+        // Only used from script calls
+        std::string mEntityName;
+        std::string mScriptFunctionName;
+    };
+
+    std::vector<Record> mGatewaysToThisField;
+};
+
+typedef std::map<u16, SpawnPointDb> FieldSpawnPointsMap;
+typedef std::map<u16, float> FieldScaleFactorMap;
+
+
+
+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)
+    {
+        // If not in meta data then just replace .hrc with .mesh
+        Ogre::String baseName;
+        QGears::StringUtil::splitBase(modelName, baseName);
+        return QGears::FF7::NameLookup::model(baseName) + ".mesh";
+    }
+
+    //private:
+    ModelAnimationMap mMap;
+};
 
 class FF7DataInstaller
 {
 public:
-    FF7DataInstaller();
+    FF7DataInstaller(std::string inputDir, std::string outputDir);
     ~FF7DataInstaller();
-    void Convert(std::string inputDir, std::string outputDir, const std::vector<std::string>& files);
-
+    int Progress();
 private:
+    int CalcProgress();
     void CreateDir(const std::string& dir);
-    void ConvertFields(std::string archive, std::string inDir, std::string outDir);
+    void InitCollectSpawnAndScaleFactors();
+    void CollectionFieldSpawnAndScaleFactors();
+    void ConvertFieldsIteration();
+    void WriteMapsXmlBegin();
+    void WriteMapsXmlIteration();
+    void EndWriteMapsXml();
+    void ConvertFieldModelsBegin();
+    void ConvertFieldModelsIteration();
 
+    enum eStates
+    {
+        eIdle,
+        eInitCollectFieldSpawnPointsAndScaleFactors,
+        eCollectFieldSpawnPointsAndScaleFactors,
+        eConvertFieldsIteration,
+        eWriteMapListOfConvertedFieldsStart,
+        eWriteMapListOfConvertedFieldsIteration,
+        eWriteMapListOfConvertedFieldsEnd,
+        eConvertFieldModelsBegin,
+        eConvertFieldModelsIteration,
+        eMaxStates,
+        eDone,
+    };
+    eStates mState = eIdle;
+    std::string mInputDir;
     std::string mOutputDir;
-    QGears::Application mApp;
+    QGears::Application mApp; // Singleton so can't recreate, will crash 2nd time round
+
+    std::unique_ptr<ScopedLgp> mFieldsLgp;
+    std::unique_ptr<ScopedLgp> mFieldModelsLgp;
+
+
+    // Kept in scope as progress is incremented per call rather than one big blocking method
+    Ogre::StringVectorPtr mFLevelFileList;
+    std::vector<std::string> mMapList;
+    FieldSpawnPointsMap mCollectedSpawnPoints;
+    FieldScaleFactorMap mCollectedScaleFactors;
+    ModelsAndAnimationsDb mModelsAndAnimationsUsedByConvertedFields;
+    MapCollection mConvertedMapList;
+    MapCollection::iterator mConvertedMapListIt;
+    size_t mIteratorCounter;
+
+    size_t mConversionStep;
+    size_t mProgressStepNumElements;
+    QGears::FLevelFilePtr mField;
+
+    Ogre::StringVectorPtr mFieldModelFileList;
+
+    std::unique_ptr<TiXmlDocument> mDoc;
+    std::unique_ptr<TiXmlElement> mElement;
+
+    ModelAnimationMap::iterator mModelAnimationMapIterator;
 };

+ 137 - 68
utilities/q-gears-launcher/src/mainwindow.cpp

@@ -6,87 +6,91 @@
 #include <QDir>
 #include <QSettings>
 #include <QMessageBox>
+#include <QTimer>
 
-MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent), ui(new Ui::MainWindow)
+MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent), mUi(new Ui::MainWindow)
 {
-    ui->setupUi(this);
+    mUi->setupUi(this);
     initSettings();
 
-    ui->lineConfigDir->setText(settings->value("ConfigDir").toString());
-    ui->lineDataDir->setText(settings->value("DataDir").toString());
-    ui->lineQGearsExe->setText(settings->value("QGearsEXE").toString());
+    mUi->lineConfigDir->setText(mSettings->value("ConfigDir").toString());
+    mUi->lineDataDir->setText(mSettings->value("DataDir").toString());
+    mUi->lineQGearsExe->setText(mSettings->value("QGearsEXE").toString());
 
 #ifdef _DEBUG
     // Hard coded prebaked paths for debugging to save time
-    ui->lineInput->setText("C:\\Games\\FF7\\data");
-    ui->lineDataDir->setText("C:\\Users\\paul\\Desktop\\q-gears\\output\\data");
+    mUi->lineInput->setText("C:\\Games\\FF7\\data");
+    mUi->lineDataDir->setText("C:\\Users\\paul\\Desktop\\q-gears\\output\\data");
 #endif
+
+    mTimer = new QTimer(this);
+    connect(mTimer, SIGNAL(timeout()), this, SLOT(DoProgress()));
 }
 
 MainWindow::~MainWindow()
 {
-	delete ui;
+	delete mUi;
 }
 void MainWindow::initSettings(void)
 {
     bool win=false;
     #ifdef Q_OS_WIN
-        settings= new QSettings(QCoreApplication::applicationDirPath() +"/" + "launcherSettings.ini",QSettings::IniFormat);
+        mSettings= new QSettings(QCoreApplication::applicationDirPath() +"/" + "launcherSettings.ini",QSettings::IniFormat);
         win=true;
     #else
-        settings= new QSettings(QSettings::NativeFormat,QSettings::UserScope,"q-gears","launcher",0);
+        mSettings= new QSettings(QSettings::NativeFormat,QSettings::UserScope,"q-gears","launcher",0);
     #endif
     //Check settings
-    if(settings->value("ConfigDir").isNull())
+    if(mSettings->value("ConfigDir").isNull())
     {
-        if(win){settings->setValue("ConfigDir",QString("%1/q-gears").arg(QDir::homePath()));}
-        else{settings->setValue("ConfigDir",QString("%1/.q-gears").arg(QDir::homePath()));}
+        if(win){mSettings->setValue("ConfigDir",QString("%1/q-gears").arg(QDir::homePath()));}
+        else{mSettings->setValue("ConfigDir",QString("%1/.q-gears").arg(QDir::homePath()));}
     }
 
-    if(settings->value("DataDir").isNull())
+    if(mSettings->value("DataDir").isNull())
     {
-        if(win){settings->setValue("DataDir",QString("%1/q-gears/data").arg(QDir::homePath()));}
-        else{settings->setValue("DataDir",QString("%1/.q-gears/data").arg(QDir::homePath()));}
+        if(win){mSettings->setValue("DataDir",QString("%1/q-gears/data").arg(QDir::homePath()));}
+        else{mSettings->setValue("DataDir",QString("%1/.q-gears/data").arg(QDir::homePath()));}
     }
 
-    if(settings->value("QGearsEXE").isNull())
+    if(mSettings->value("QGearsEXE").isNull())
     {
-        if(win){settings->setValue("QGearsEXE",QString("%1/q-gears.exe").arg(QCoreApplication::applicationDirPath()));}
-        else{settings->setValue("QGearsEXE",QString("/usr/games/q-gears"));}
+        if(win){mSettings->setValue("QGearsEXE",QString("%1/q-gears.exe").arg(QCoreApplication::applicationDirPath()));}
+        else{mSettings->setValue("QGearsEXE",QString("/usr/games/q-gears"));}
     }
 }
 
-void MainWindow::on_lineConfigDir_editingFinished(){settings->setValue("ConfigDir",ui->lineConfigDir->text());}
+void MainWindow::on_lineConfigDir_editingFinished(){mSettings->setValue("ConfigDir",mUi->lineConfigDir->text());}
 void MainWindow::on_btnConfigDir_clicked()
 {
-    QString temp = QFileDialog::getExistingDirectory(this,tr("Select Location of QGears Configuration Data,"),settings->value("ConfigDir").toString());
+    QString temp = QFileDialog::getExistingDirectory(this,tr("Select Location of QGears Configuration Data,"),mSettings->value("ConfigDir").toString());
     if(!temp.isNull())
     {
-        settings->setValue("ConfigDir",temp);
-        ui->lineConfigDir->setText(temp);
+        mSettings->setValue("ConfigDir",temp);
+        mUi->lineConfigDir->setText(temp);
     }
 
 }
-void MainWindow::on_lineQGearsExe_editingFinished(){settings->setValue("QGearsEXE",ui->lineQGearsExe->text());}
+void MainWindow::on_lineQGearsExe_editingFinished(){mSettings->setValue("QGearsEXE",mUi->lineQGearsExe->text());}
 void MainWindow::on_btnQGearsExe_clicked()
 {
     QString temp = QFileDialog::getOpenFileName(this,tr("Location of QGears Executable,"),QDir::rootPath());
     if(!temp.isNull())
     {
-        settings->setValue("QGearsEXE",temp);
-        ui->lineQGearsExe->setText(temp);
+        mSettings->setValue("QGearsEXE",temp);
+        mUi->lineQGearsExe->setText(temp);
     }
 }
 
 void MainWindow::on_btnLaunch_clicked()
 {
-	QString configDir(ui->lineConfigDir->text());
-	QString exe(ui->lineQGearsExe->text());
+	QString configDir(mUi->lineConfigDir->text());
+	QString exe(mUi->lineQGearsExe->text());
 
 	QStringList args;
-	args.append(QString("--resources-file=%1/resources.cfg").arg(ui->lineConfigDir->text()));
-	args.append(QString("--config-file=%1/q-gears.cfg").arg(ui->lineConfigDir->text()));
-	args.append(QString("--plugins-file=/%1/plugins.cfg").arg(ui->lineConfigDir->text()));
+	args.append(QString("--resources-file=%1/resources.cfg").arg(mUi->lineConfigDir->text()));
+	args.append(QString("--config-file=%1/q-gears.cfg").arg(mUi->lineConfigDir->text()));
+	args.append(QString("--plugins-file=/%1/plugins.cfg").arg(mUi->lineConfigDir->text()));
 
 	// Check that the config dir is set up correctly
 
@@ -99,17 +103,17 @@ void MainWindow::on_btnLaunch_clicked()
 void MainWindow::on_btnInput_clicked()
 {
     QString temp = QFileDialog::getExistingDirectory(this,tr("Location of Game Data),"),QDir::homePath());
-	ui->lineInput->setText(temp);
+	mUi->lineInput->setText(temp);
 }
 
-void MainWindow::on_lineDataDir_editingFinished(){settings->setValue("DataDir",ui->lineDataDir->text());}
+void MainWindow::on_lineDataDir_editingFinished(){mSettings->setValue("DataDir",mUi->lineDataDir->text());}
 void MainWindow::on_btnDataDir_clicked()
 {
-    QString temp = QFileDialog::getExistingDirectory(this,tr("Location of QGears Data),"),settings->value("DataDir").toString());
+    QString temp = QFileDialog::getExistingDirectory(this,tr("Location of QGears Data),"),mSettings->value("DataDir").toString());
     if(!temp.isNull())
     {
-        settings->setValue("DataDir",temp);
-        ui->lineDataDir->setText(temp);
+        mSettings->setValue("DataDir",temp);
+        mUi->lineDataDir->setText(temp);
     }
 }
 
@@ -117,37 +121,102 @@ void MainWindow::on_btnDataDir_clicked()
 
 void MainWindow::on_btnGO_clicked()
 {
-	if (ui->lineInput->text().isEmpty())
-	{
-		QMessageBox::critical(this,tr("Input Error"),tr("No Input Provided"));
-	}
-	else if(ui->lineDataDir->text().isEmpty())
-	{
-		QMessageBox::critical(this,tr("Output Error"),tr("No Output Path Provided"));
-	}
-	else
-	{
-		//QMessageBox::information(this,tr("Converting Data"),tr("Attempt to convert with \n Input: %1 \n Output: %2").arg(ui->lineInput->text(),ui->lineDataDir->text()));
-
-		QString input = QDir::fromNativeSeparators(ui->lineInput->text());
-		if (!input.endsWith("/"))
-		{
-			input += "/";
-		}
-
-		QString output = QDir::fromNativeSeparators(ui->lineDataDir->text());
-		if (!output.endsWith("/"))
-		{
-			output += "/";
-		}
-
-		// TODO: Enumerate files or find some better way to do this :)
-		std::vector<std::string> vec;
-		vec.push_back(QString("field/char.lgp").toStdString());
-		vec.push_back(QString("field/flevel.lgp").toStdString());
-
-		FF7DataInstaller conversion;
-		conversion.Convert(QDir::toNativeSeparators(input).toStdString(), QDir::toNativeSeparators(output).toStdString(), vec);
-	}
+    if (mUi->lineInput->text().isEmpty())
+    {
+        QMessageBox::critical(this, tr("Input error"), tr("No input to installed FF7 PC data provided"));
+    }
+    else if (mUi->lineDataDir->text().isEmpty())
+    {
+        QMessageBox::critical(this, tr("Output error"), tr("No output path provided"));
+    }
+    else
+    {
+        // Normalize the paths so its in / format seperators
+        QString input = QDir::fromNativeSeparators(mUi->lineInput->text());
+        if (!input.endsWith("/"))
+        {
+            input += "/";
+        }
+
+        QString output = QDir::fromNativeSeparators(mUi->lineDataDir->text());
+        if (!output.endsWith("/"))
+        {
+            output += "/";
+        }
+
+        // TODO: Enumerate files or find some better way to do this :)
+        const std::vector<std::string> requiredFiles = 
+        {
+            "field/char.lgp",
+            "field/flevel.lgp"
+        };
+
+        // Ensure required files are in the input dir
+        for (auto& file : requiredFiles)
+        {
+            QString fullPath = input + QString::fromStdString(file);
+            if (!QFile::exists(fullPath))
+            {
+                QMessageBox::critical(this, tr("Missing input file"), tr("File not found: ") + fullPath);
+                return;
+            }
+        }
+
+        // Start data conversion
+        try
+        {
+            mInstaller = std::make_unique<FF7DataInstaller>(QDir::toNativeSeparators(input).toStdString(), QDir::toNativeSeparators(output).toStdString());
+            OnInstallStarted();
+        }
+        catch (const std::exception&)
+        {
+            OnInstallStopped();
+        }
+    }
+}
+
+void MainWindow::EnableUi(bool enable)
+{
+    mUi->btnGO->setEnabled(enable);
+    mUi->lineInput->setEnabled(enable);
+    mUi->btnInput->setEnabled(enable);
+    mUi->lineDataDir->setEnabled(enable);
+    mUi->btnDataDir->setEnabled(enable);
+    mUi->progressBar->setValue(0);
+    if (!enable)
+    {
+        mTimer->start(0);
+    }
+    else
+    {
+        mTimer->stop();
+    }
+}
+
+void MainWindow::OnInstallStarted()
+{
+    EnableUi(false);
+}
+
+void MainWindow::OnInstallStopped()
+{
+    EnableUi(true);
 }
 
+void MainWindow::DoProgress()
+{
+    try
+    {
+        const int progress = mInstaller->Progress();
+        mUi->progressBar->setValue(progress);
+        if (progress >= 100)
+        {
+            OnInstallStopped();
+        }
+    }
+    catch (const std::exception& ex)
+    {
+        OnInstallStopped();
+        QMessageBox::critical(this, tr("Data conversion exception"), ex.what());
+    }
+}

+ 29 - 22
utilities/q-gears-launcher/src/mainwindow.h

@@ -3,7 +3,7 @@
 
 #include <QMainWindow>
 #include <QSettings>
-
+#include <memory>
 
 namespace Ui {
 class MainWindow;
@@ -11,34 +11,41 @@ class MainWindow;
 
 class MainWindow : public QMainWindow
 {
-	Q_OBJECT
+    Q_OBJECT
 
 public:
     explicit MainWindow(QWidget *parent = 0);
-	~MainWindow();
+    ~MainWindow();
+
+    private slots:
+    void on_lineConfigDir_editingFinished();
+    void on_btnConfigDir_clicked();
+
+    void on_lineQGearsExe_editingFinished();
+    void on_btnQGearsExe_clicked();
+
+    void on_btnLaunch_clicked();
+
+    void on_btnInput_clicked();
+
+    void on_lineDataDir_editingFinished();
+    void on_btnDataDir_clicked();
+
+    void on_btnGO_clicked();
 
 private slots:
-	void on_lineConfigDir_editingFinished();
-	void on_btnConfigDir_clicked();
-	
-	void on_lineQGearsExe_editingFinished();
-	void on_btnQGearsExe_clicked();
-	
-	void on_btnLaunch_clicked();
-	
-	void on_btnInput_clicked();
-	
-	void on_lineDataDir_editingFinished();
-	void on_btnDataDir_clicked();
-	
-	void on_btnGO_clicked();
+    void DoProgress();
 
 private:
-	//private functions;
-	void initSettings(void);
-	//private vars
-	Ui::MainWindow *ui;
-	QSettings *settings;
+    void EnableUi(bool enable);
+    void OnInstallStarted();
+    void OnInstallStopped();
+    void initSettings(void);
+private:
+    Ui::MainWindow* mUi;
+    QTimer* mTimer;
+    QSettings *mSettings;
+    std::unique_ptr<class FF7DataInstaller> mInstaller;
 };
 
 #endif // MAINWINDOW_H

+ 15 - 5
utilities/q-gears-launcher/src/mainwindow.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>400</width>
-    <height>172</height>
+    <width>734</width>
+    <height>217</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -18,7 +18,7 @@
     <item>
      <widget class="QTabWidget" name="tabWidget">
       <property name="currentIndex">
-       <number>0</number>
+       <number>1</number>
       </property>
       <widget class="QWidget" name="tab">
        <attribute name="title">
@@ -159,6 +159,16 @@
           </item>
          </layout>
         </item>
+        <item>
+         <widget class="QProgressBar" name="progressBar">
+          <property name="value">
+           <number>0</number>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignCenter</set>
+          </property>
+         </widget>
+        </item>
         <item>
          <widget class="QPushButton" name="btnGO">
           <property name="text">
@@ -177,8 +187,8 @@
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>400</width>
-     <height>20</height>
+     <width>734</width>
+     <height>21</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">