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

Merge pull request #103 from paulsapps/installer

Added correct entity_points for installed PC fields
paulsapps 11 лет назад
Родитель
Сommit
0b2d303bf2

+ 2 - 0
QGearsMain/CMakeLists.txt

@@ -149,6 +149,7 @@ include/data/worldmap/MapFile.h
   include/data/QGearsTexCodec.h
   include/data/QGearsTexFile.h
   include/data/QGearsTriggersFile.h
+  include/data/QGearsMapListFile.h
 )
 
 set(SOURCE_FILES_DATA
@@ -198,6 +199,7 @@ set(SOURCE_FILES_DATA
   src/data/QGearsTexCodec.cpp
   src/data/QGearsTexFile.cpp
   src/data/QGearsTriggersFile.cpp
+  src/data/QGearsMapListFile.cpp
 )
 
 set(SOURCE_FILES

+ 61 - 0
QGearsMain/include/data/QGearsMapListFile.h

@@ -0,0 +1,61 @@
+#pragma once
+
+#include <OgreResourceManager.h>
+#include "QGearsPrerequisites.h"
+#include "common/QGearsResource.h"
+#include "QGearsSerializer.h"
+
+namespace QGears
+{
+    class MapListFileManager :
+        public Ogre::ResourceManager,
+        public Ogre::Singleton<MapListFileManager>
+    {
+    public:
+        MapListFileManager();
+        virtual ~MapListFileManager();
+        static MapListFileManager& getSingleton();
+        static MapListFileManager* getSingletonPtr();
+    protected:
+        virtual Ogre::Resource *createImpl(
+            const Ogre::String &name,
+            Ogre::ResourceHandle handle,
+            const Ogre::String& group,
+            bool isManual,
+            Ogre::ManualResourceLoader* loader,
+            const Ogre::NameValuePairList* createParams) override;
+    };
+
+    class MapListFile : public Resource
+    {
+    public:
+        MapListFile(Ogre::ResourceManager* creator,
+            const String &name,
+            Ogre::ResourceHandle handle,
+            const String& group,
+            bool isManual = false,
+            Ogre::ManualResourceLoader* loader = nullptr);
+        virtual ~MapListFile();
+        static const String RESOURCE_TYPE;
+        const std::vector<std::string> GetMapList() const
+        {
+            return mMapList;
+        }
+    protected:
+        virtual void loadImpl(void) override;
+        virtual void unloadImpl(void) override;
+        virtual size_t calculateSize(void) const override;
+    private:
+        friend class MapListFileSerializer;
+        std::vector<std::string> mMapList;
+    };
+
+    class MapListFileSerializer : public Serializer
+    {
+    public:
+        MapListFileSerializer() = default;
+        void importMapListFile(Ogre::DataStreamPtr &stream, MapListFile& dest);
+    };
+
+    typedef Ogre::SharedPtr<MapListFile> MapListFilePtr;
+}

+ 32 - 27
QGearsMain/include/data/QGearsTriggersFile.h

@@ -48,35 +48,18 @@ namespace QGears
             s16 bottom;
         };
 
-        const Range& getCameraRange() const
-        {
-            return mData->camera_range;
-        }
-
-        float MovementRotation() const
-        {
-            // This is the angle in which the player moves when "up" is pressed
-            return 180.0f * (static_cast<float>(mData->control) - 128.0f) / 128.0f;
-        }
-
-    protected:
-        virtual void loadImpl(void) override;
-        virtual void unloadImpl(void) override;
-        virtual size_t calculateSize(void) const override;
-    private:
-
-        struct Vertex_s 
+        struct Vertex_s
         {
             u16 x;
             u16 y;
             u16 z;
         };
 
-        struct Exit
+        struct Gateway
         {
             std::array<Vertex_s, 2> exit_line;
             Vertex_s destination;
-            u16 fieldID;
+            u16 destinationFieldId;
             u8 dir, dir_copy1, dir_copy2, dir_copy3;
         };
 
@@ -106,7 +89,7 @@ namespace QGears
 
         struct TriggerData
         {
-            std::array<char,9> name;
+            std::array<char, 9> name;
             u8 control;
             s16 cameraFocusHeight;
             Range camera_range; // 8 bytes
@@ -118,15 +101,37 @@ namespace QGears
             s16 bg_layer3_height;
             s16 bg_layer4_width;
             s16 bg_layer4_height;
-            std::array<u8,24> unknown;
-            std::array<Exit,12> doors;// 24 * 12 bytes
+            std::array<u8, 24> unknown;
+            std::array<Gateway, 12> doors;// 24 * 12 bytes
             std::array<Trigger, 12> triggers;// 16 * 12 bytes
             // Only in occidental/international version
-            std::array<u8,12> display_arrow;
-            std::array<Arrow,12> arrows;// 16 * 12 bytes
+            std::array<u8, 12> display_arrow;
+            std::array<Arrow, 12> arrows;// 16 * 12 bytes
         };
-        
 
+        const Range& getCameraRange() const
+        {
+            return mData->camera_range;
+        }
+
+        float MovementRotation() const
+        {
+            // This is the angle in which the player moves when "up" is pressed
+            return 180.0f * (static_cast<float>(mData->control) - 128.0f) / 128.0f;
+        }
+
+        const std::array<Gateway, 12>& GetGateways() const
+        {
+            return mData->doors;
+        }
+
+    protected:
+        virtual void loadImpl(void) override;
+        virtual void unloadImpl(void) override;
+        virtual size_t calculateSize(void) const override;
+    private:
+
+ 
         std::unique_ptr<TriggerData> mData;
 
         friend class TriggerFileSerializer;
@@ -140,7 +145,7 @@ namespace QGears
     private:
         void ReadVertex_s(Ogre::DataStreamPtr& stream, TriggersFile::Vertex_s& vertex);
         void ReadRange(Ogre::DataStreamPtr& stream, TriggersFile::Range& range);
-        void ReadExit(Ogre::DataStreamPtr& stream, TriggersFile::Exit& exit);
+        void ReadGateway(Ogre::DataStreamPtr& stream, TriggersFile::Gateway& exit);
         void ReadArrow(Ogre::DataStreamPtr& stream, TriggersFile::Arrow& arrow);
         void ReadTrigger(Ogre::DataStreamPtr& stream, TriggersFile::Trigger& trigger);
     };

+ 2 - 0
QGearsMain/src/common/QGearsApplication.cpp

@@ -36,6 +36,7 @@ GNU General Public License for more details.
 #include "data/FF7ModelListFileManager.h"
 #include "data/QGearsLGPArchiveFactory.h"
 #include "data/QGearsTriggersFile.h"
+#include "data/QGearsMapListFile.h"
 #include "common/make_unique.h"
 #include "qgears_version.h"
 
@@ -281,6 +282,7 @@ namespace QGears
         m_resource_managers.emplace_back( std::make_shared<QGears::BackgroundFileManager>() );
         m_resource_managers.emplace_back( std::make_shared<QGears::Background2DFileManager>() );
         m_resource_managers.emplace_back(std::make_shared<QGears::TriggersFileManager>());
+        m_resource_managers.emplace_back(std::make_shared<QGears::MapListFileManager>());
     }
 
     //--------------------------------------------------------------------------

+ 102 - 0
QGearsMain/src/data/QGearsMapListFile.cpp

@@ -0,0 +1,102 @@
+#include "data/QGearsMapListFile.h"
+
+template<> QGears::MapListFileManager *Ogre::Singleton<QGears::MapListFileManager>::msSingleton = nullptr;
+
+namespace QGears
+{
+    const int kMapNameMaxSize = 32;
+
+    MapListFileManager::MapListFileManager()
+    {
+        mResourceType = MapListFile::RESOURCE_TYPE;
+
+        // low, because it will likely reference other resources
+        mLoadOrder = 30.0f;
+
+        // this is how we register the ResourceManager with OGRE
+        Ogre::ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this);
+    }
+
+    MapListFileManager::~MapListFileManager()
+    {
+        Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
+    }
+
+    /*static*/ MapListFileManager& MapListFileManager::getSingleton()
+    {
+        assert(msSingleton);
+        return(*msSingleton);
+    }
+
+    /*static*/ MapListFileManager* MapListFileManager::getSingletonPtr()
+    {
+        return msSingleton;
+    }
+
+    // ===========================================================================
+
+    MapListFile::MapListFile(Ogre::ResourceManager* creator,
+        const String &name,
+        Ogre::ResourceHandle handle,
+        const String& group,
+        bool isManual,
+        Ogre::ManualResourceLoader* loader)
+        : Resource(creator, name, handle, group, isManual, loader)
+    {
+    }
+
+    MapListFile::~MapListFile()
+    {
+        unload();
+    }
+
+    void MapListFile::loadImpl(void)
+    {
+        MapListFileSerializer serializer;
+        Ogre::DataStreamPtr stream(openResource());
+        serializer.importMapListFile(stream, *this);
+    }
+
+    void MapListFile::unloadImpl(void)
+    {
+        mMapList.clear();
+    }
+
+    size_t MapListFile::calculateSize(void) const
+    {
+        return mMapList.size() * kMapNameMaxSize;
+    }
+
+    /*static*/ const String MapListFile::RESOURCE_TYPE("FF7FieldPCMapList");
+
+    Ogre::Resource* MapListFileManager::createImpl(
+        const Ogre::String &name,
+        Ogre::ResourceHandle handle,
+        const Ogre::String& group,
+        bool isManual,
+        Ogre::ManualResourceLoader* loader,
+        const Ogre::NameValuePairList* createParams)
+    {
+        return new MapListFile(this, name, handle, group, isManual, loader);
+    }
+
+    // ===========================================================================
+
+    void MapListFileSerializer::importMapListFile(Ogre::DataStreamPtr &stream, MapListFile& dest)
+    {
+        uint16 numMaps = 0;
+        readShort(stream, numMaps);
+        for (auto i = 0u; i < numMaps; i++)
+        {
+            // +1 to ensure nullptr termination
+            std::array<char, kMapNameMaxSize + 1> tmpBuffer = {};
+            readChars(stream, tmpBuffer.data(), tmpBuffer.size() - 1);
+            std::string tmp = tmpBuffer.data();
+            if (!tmp.empty())
+            {
+                dest.mMapList.emplace_back(tmp);
+            }
+        }
+    }
+
+}

+ 4 - 4
QGearsMain/src/data/QGearsTriggersFile.cpp

@@ -101,14 +101,14 @@ namespace QGears
         readInt16(stream, range.bottom);
     }
 
-    void TriggerFileSerializer::ReadExit(Ogre::DataStreamPtr& stream, TriggersFile::Exit& exit)
+    void TriggerFileSerializer::ReadGateway(Ogre::DataStreamPtr& stream, TriggersFile::Gateway& exit)
     {
         for (size_t i = 0; i < exit.exit_line.size(); i++)
         {
             ReadVertex_s(stream, exit.exit_line[i]);
         }
         ReadVertex_s(stream, exit.destination);
-        readShort(stream, exit.fieldID);
+        readShort(stream, exit.destinationFieldId);
         readUInt8(stream, exit.dir);
         readUInt8(stream, exit.dir_copy1);
         readUInt8(stream, exit.dir_copy2);
@@ -159,8 +159,8 @@ namespace QGears
 
         for (size_t i = 0; i < data->doors.size(); i++)
         {
-            TriggersFile::Exit& exit = data->doors[i];
-            ReadExit(stream, exit);
+            TriggersFile::Gateway& exit = data->doors[i];
+            ReadGateway(stream, exit);
         }
         
         for (size_t i = 0; i < data->triggers.size(); i++)

+ 174 - 19
utilities/q-gears-launcher/src/ff7DataInstaller.cpp

@@ -27,6 +27,7 @@
 #include "common/QGearsStringUtil.h"
 #include "common/FF7NameLookup.h"
 #include "data/QGearsTexCodec.h"
+#include "data/QGearsMapListFile.h"
 #include "decompiler/sudm.h"
 #include <memory>
 
@@ -150,7 +151,38 @@ public:
     }
 };
 
-static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::string& outDir)
+const int kInactiveGateWayId = 32767;
+
+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:
+        u16 mFieldId = 0;
+        u32 GatewayIndex = 0;
+        QGears::TriggersFile::Gateway mGateway;
+    };
+    std::vector<Record> mGatewaysToThisField;
+};
+typedef std::map<u16, SpawnPointDb> FieldSpawnPointsMap;
+
+static size_t FieldId(const std::string& name, const std::vector<std::string>& fieldIdToNameLookup)
+{
+    for (size_t i = 0; i < fieldIdToNameLookup.size(); i++)
+    {
+        if (fieldIdToNameLookup[i] == name)
+        {
+            return i;
+        }
+    }
+    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)
 {
     // Save out the tiles as a PNG image
     
@@ -210,9 +242,76 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
         element->LinkEndChild(xmlMovementRotation.release());
 
         // TODO: entity_script - name
+        
         // TODO: entity_model - name, file_name,  position, direction
-        // TODO: entity_trigger - name, point1, point2, enabled
-        // TODO: entity_point - name, position, rotation
+        // We set char 1 position to be position of first entity_point so player is spawned in sane
+        // position if map is manually loaded via console.
+        // None player chars set their first position in the init scripts. We know its a entity_model
+        // because it uses PC opcode in init script.
+
+        // entity_manager:get_entity("cl") is done via CHAR opcode
+
+
+        // TODO: Get these scales from the field game data,  1024 for md1_1 and 512 for md1_2, DAT CFG has them as 2 and 1?
+        const float downscaler_next = 128.0f; //  * MapIdToScale( map_id );
+        const float downscaler_this = 128.0f; // * field.scale
+
+        const auto& gateways = triggers->GetGateways();
+        for (size_t i = 0; i < gateways.size(); i++)
+        {
+            const QGears::TriggersFile::Gateway& gateway = gateways[i];
+            // if not inactive gateway
+            if (gateway.destinationFieldId != kInactiveGateWayId)
+            {
+                std::unique_ptr<TiXmlElement> xmlEntityTrigger(new TiXmlElement("entity_trigger"));
+
+                xmlEntityTrigger->SetAttribute("name", "Gateway" + std::to_string(i));
+
+                xmlEntityTrigger->SetAttribute("point1",
+                    Ogre::StringConverter::toString(
+                    Ogre::Vector3(gateway.exit_line[0].x, gateway.exit_line[0].y, gateway.exit_line[0].z) / downscaler_this));
+
+                xmlEntityTrigger->SetAttribute("point2",
+                    Ogre::StringConverter::toString(
+                    Ogre::Vector3(gateway.exit_line[1].x, gateway.exit_line[1].y, gateway.exit_line[1].z) / downscaler_this));
+
+                // enabled hard coded to true
+                xmlEntityTrigger->SetAttribute("enabled", "true");
+                element->LinkEndChild(xmlEntityTrigger.release());
+            }
+        }
+
+        // Get this fields Id
+        const size_t thisFieldId = FieldId(field->getName(), fieldIdToNameLookup);
+
+        // Use that to find the pre-computed list of gateways in all other fields that link to this field
+        auto spawnIterator = spawnMap.find(thisFieldId);
+
+        // If not found that it probably just means no other fields have doors to this one
+        if (spawnIterator != std::end(spawnMap))
+        {
+            const std::vector<SpawnPointDb::Record>& spawnPointRecords = spawnIterator->second.mGatewaysToThisField;
+
+            for (size_t i = 0; i < spawnPointRecords.size(); i++)
+            {
+                const QGears::TriggersFile::Gateway& gateway = spawnPointRecords[i].mGateway;
+                // entity_point
+                std::unique_ptr<TiXmlElement> xmlEntityPoint(new TiXmlElement("entity_point"));
+
+                // Must also include the gateway index for the case where 2 fields have more than one door linking to each other
+                xmlEntityPoint->SetAttribute("name", "Spawn_" + fieldIdToNameLookup.at(spawnPointRecords[i].mFieldId) + "_" + std::to_string(spawnPointRecords[i].GatewayIndex));
+
+
+                xmlEntityPoint->SetAttribute("position",
+                    Ogre::StringConverter::toString(
+                    Ogre::Vector3(gateway.destination.x, gateway.destination.y, gateway.destination.z) / downscaler_next));
+
+                const float rotation = (360.0f * static_cast<float>(gateway.dir)) / 255.0f;
+                xmlEntityPoint->SetAttribute("rotation", std::to_string(rotation));
+
+                element->LinkEndChild(xmlEntityPoint.release());
+            }
+        }
 
         doc.LinkEndChild(element.release());
         doc.SaveFile(outDir + "/" + field->getName() + ".xml");
@@ -305,6 +404,7 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
                     }
                     else
                     {
+                        // TODO: Should probably throw to fail conversion
                         blending_str = "unknown";
                     }
                     xmlElement->SetAttribute("blending", blending_str);
@@ -339,31 +439,86 @@ static void FF7PcFieldToQGearsField(QGears::FLevelFilePtr& field, const std::str
     }
 }
 
+
+
+static void CollectSpawnPoints(QGears::FLevelFilePtr& field, const std::vector<std::string>& fieldIdToNameLookup, FieldSpawnPointsMap& spawnPoints)
+{
+    const size_t thisFieldId = FieldId(field->getName(), fieldIdToNameLookup);
+    const QGears::TriggersFilePtr& triggers = field->getTriggers();
+    const auto& gateways = triggers->GetGateways();
+    for (size_t i = 0; i < gateways.size(); i++)
+    {
+        const QGears::TriggersFile::Gateway& gateway = gateways[i];
+        if (gateway.destinationFieldId != kInactiveGateWayId)
+        {
+            auto it = spawnPoints.find(gateway.destinationFieldId);
+            if (it != std::end(spawnPoints))
+            {
+                // Add to the list of gateways that link to destinationFieldId
+                SpawnPointDb::Record rec;
+                rec.mFieldId = thisFieldId;
+                rec.mGateway = gateway;
+                rec.GatewayIndex = i;
+                it->second.mGatewaysToThisField.push_back(rec);
+            }
+            else
+            {
+                // Create a new record for destinationFieldId
+                SpawnPointDb db;
+                db.mTargetFieldId = gateway.destinationFieldId;
+
+                SpawnPointDb::Record rec;
+                rec.mFieldId = thisFieldId;
+                rec.mGateway = gateway;
+                rec.GatewayIndex = i;
+                db.mGatewaysToThisField.push_back(rec);
+
+                spawnPoints.insert(std::make_pair(db.mTargetFieldId, db));
+            }
+        }
+    }
+}
+
+static bool IsAFieldFile(Ogre::String& resourceName)
+{
+    return (!QGears::StringUtil::endsWith(resourceName, ".tex")
+        && !QGears::StringUtil::endsWith(resourceName, ".tut")
+        && !QGears::StringUtil::endsWith(resourceName, ".siz")
+        && resourceName != "maplist");
+}
+
 void FF7DataInstaller::ConvertFields(std::string archive, std::string outDir)
 {
-    // Everything in here is a field
+    // List whats in the LGP archive
     Ogre::StringVectorPtr resources = mApp.ResMgr()->listResourceNames("FFVIIFields", "*");
+
+    // Load the map list field
+    QGears::MapListFilePtr mapList = QGears::MapListFileManager::getSingleton().load("maplist", "FFVIIFields").staticCast<QGears::MapListFile>();
+
+    // On the first pass collate required field information
+    FieldSpawnPointsMap spawnPoints;
     for (auto& resourceName : *resources)
     {
-        if (!QGears::StringUtil::endsWith(resourceName, ".tex")
-         && !QGears::StringUtil::endsWith(resourceName, ".tut")
-         && !QGears::StringUtil::endsWith(resourceName, ".siz")
-         && resourceName != "maplist" && resourceName == "md1_2")
+        // Exclude things that are not fields
+        if (IsAFieldFile(resourceName))
         {
-            //try
+            QGears::FLevelFilePtr field = QGears::LZSFLevelFileManager::getSingleton().load(resourceName, "FFVIIFields").staticCast<QGears::FLevelFile>();
+            CollectSpawnPoints(field, mapList->GetMapList(), spawnPoints);
+        }
+    }
+
+    // Now we can do the full conversion with the collated data
+    for (auto& resourceName : *resources)
+    {
+        // Exclude things that are not fields
+        if (IsAFieldFile(resourceName))
+        {
+            if (resourceName == "md1_2") // Testing conversion only on this field for now
             {
                 QGears::FLevelFilePtr field = QGears::LZSFLevelFileManager::getSingleton().load(resourceName, "FFVIIFields").staticCast<QGears::FLevelFile>();
-                FF7PcFieldToQGearsField(field, outDir);
+                FF7PcFieldToQGearsField(field, outDir, mapList->GetMapList(), spawnPoints);
             }
-            /*
-            catch (const Ogre::Exception& ex)
-            {
-                std::cout << "ERROR converting: " << resourceName << " Exception: " << ex.what() << std::endl;
-            }
-            catch (const std::exception& ex)
-            {
-                std::cout << "ERROR converting: " << resourceName << " Exception: " << ex.what() << std::endl;
-            }*/
         }
     }
+    
 }