Explorar el Código

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

Paul hace 11 años
padre
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">