diff --git a/include/imapinfofile.h b/include/imapinfofile.h index 1b961db250..fb3b3cfe2f 100644 --- a/include/imapinfofile.h +++ b/include/imapinfofile.h @@ -42,11 +42,17 @@ class IMapInfoFileModule virtual std::string getName() = 0; /** - * Called before any node is written to the .map file. Use tihs + * Called before any node is written to the .map file. Use this * to prepare the internal structures for exporting. */ virtual void onInfoFileSaveStart() = 0; + /** + * Called before starting the actual node traversal, + * the info file exporter gets a chance to look at the map root. + */ + virtual void onBeginSaveMap(const scene::IMapRootNodePtr& root) = 0; + /** * Called during map export traversal when a single * primitive is about to be written to the .map file. @@ -64,6 +70,11 @@ class IMapInfoFileModule */ virtual void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) = 0; + /** + * Called after node traversal. + */ + virtual void onFinishSaveMap(const scene::IMapRootNodePtr& root) = 0; + /** * Final export function, write the assembled data to the * info file stream. This method should include the block file name diff --git a/libs/KeyValueStore.h b/libs/KeyValueStore.h index 2b53cba117..081b1e155b 100644 --- a/libs/KeyValueStore.h +++ b/libs/KeyValueStore.h @@ -16,6 +16,16 @@ class KeyValueStore : KeyValues _keyValues; public: + std::size_t getPropertyCount() const + { + return _keyValues.size(); + } + + void clearProperties() + { + _keyValues.clear(); + } + virtual std::string getProperty(const std::string& key, const std::string& value) const { auto existing = _keyValues.find(key); diff --git a/radiant/Makefile.am b/radiant/Makefile.am index 6aa288b0dc..794ba9c231 100644 --- a/radiant/Makefile.am +++ b/radiant/Makefile.am @@ -410,6 +410,7 @@ darkradiant_SOURCES = main.cpp \ map/MapPosition.cpp \ map/EditingStopwatch.cpp \ map/EditingStopwatchInfoFileModule.cpp \ + map/MapPropertyInfoFileModule.cpp \ map/FindMapElements.cpp \ map/infofile/InfoFileManager.cpp \ map/infofile/InfoFile.cpp \ diff --git a/radiant/layers/LayerInfoFileModule.cpp b/radiant/layers/LayerInfoFileModule.cpp index 2aac129fd6..56d8d92d48 100644 --- a/radiant/layers/LayerInfoFileModule.cpp +++ b/radiant/layers/LayerInfoFileModule.cpp @@ -38,6 +38,12 @@ void LayerInfoFileModule::onInfoFileSaveStart() _output.clear(); } +void LayerInfoFileModule::onBeginSaveMap(const scene::IMapRootNodePtr& root) +{} + +void LayerInfoFileModule::onFinishSaveMap(const scene::IMapRootNodePtr& root) +{} + void LayerInfoFileModule::onSavePrimitive(const INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) { saveNode(node); diff --git a/radiant/layers/LayerInfoFileModule.h b/radiant/layers/LayerInfoFileModule.h index c0f73c0036..2bb1e3b814 100644 --- a/radiant/layers/LayerInfoFileModule.h +++ b/radiant/layers/LayerInfoFileModule.h @@ -32,6 +32,8 @@ class LayerInfoFileModule : std::string getName() override; void onInfoFileSaveStart() override; + void onBeginSaveMap(const scene::IMapRootNodePtr& root) override; + void onFinishSaveMap(const scene::IMapRootNodePtr& root) override; void onSavePrimitive(const INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) override; void onSaveEntity(const INodePtr& node, std::size_t entityNum) override; void writeBlocks(std::ostream& stream) override; diff --git a/radiant/map/EditingStopwatchInfoFileModule.cpp b/radiant/map/EditingStopwatchInfoFileModule.cpp index 482569ea44..e446f35a54 100644 --- a/radiant/map/EditingStopwatchInfoFileModule.cpp +++ b/radiant/map/EditingStopwatchInfoFileModule.cpp @@ -22,6 +22,12 @@ std::string EditingStopwatchInfoFileModule::getName() void EditingStopwatchInfoFileModule::onInfoFileSaveStart() {} +void EditingStopwatchInfoFileModule::onBeginSaveMap(const scene::IMapRootNodePtr& root) +{} + +void EditingStopwatchInfoFileModule::onFinishSaveMap(const scene::IMapRootNodePtr& root) +{} + void EditingStopwatchInfoFileModule::onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) {} diff --git a/radiant/map/EditingStopwatchInfoFileModule.h b/radiant/map/EditingStopwatchInfoFileModule.h index 074119a090..c628c476f1 100644 --- a/radiant/map/EditingStopwatchInfoFileModule.h +++ b/radiant/map/EditingStopwatchInfoFileModule.h @@ -16,6 +16,8 @@ class EditingStopwatchInfoFileModule : std::string getName() override; void onInfoFileSaveStart() override; + void onBeginSaveMap(const scene::IMapRootNodePtr& root) override; + void onFinishSaveMap(const scene::IMapRootNodePtr& root) override; void onSavePrimitive(const scene::INodePtr & node, std::size_t entityNum, std::size_t primitiveNum) override; void onSaveEntity(const scene::INodePtr & node, std::size_t entityNum) override; void writeBlocks(std::ostream & stream) override; diff --git a/radiant/map/Map.cpp b/radiant/map/Map.cpp index 2de5e0b8c3..d72f41897d 100644 --- a/radiant/map/Map.cpp +++ b/radiant/map/Map.cpp @@ -16,6 +16,7 @@ #include "iradiant.h" #include "imainframe.h" #include "imapresource.h" +#include "imapinfofile.h" #include "iaasfile.h" #include "igame.h" @@ -52,6 +53,7 @@ #include "modulesystem/ModuleRegistry.h" #include "modulesystem/StaticModule.h" #include "RenderableAasFile.h" +#include "MapPropertyInfoFileModule.h" #include #include "algorithm/ChildPrimitives.h" @@ -986,6 +988,7 @@ const StringSet& Map::getDependencies() const _dependencies.insert(MODULE_RADIANT); _dependencies.insert(MODULE_GAMEMANAGER); _dependencies.insert(MODULE_SCENEGRAPH); + _dependencies.insert(MODULE_MAPINFOFILEMANAGER); _dependencies.insert(MODULE_FILETYPES); } @@ -1008,6 +1011,11 @@ void Map::initialiseModule(const ApplicationContext& ctx) _scaledModelExporter.initialise(); MapFileManager::registerFileTypes(); + + // Register an info file module to save the map property bag + GlobalMapInfoFileManager().registerInfoFileModule( + std::make_shared() + ); } void Map::shutdownModule() diff --git a/radiant/map/MapPropertyInfoFileModule.cpp b/radiant/map/MapPropertyInfoFileModule.cpp new file mode 100644 index 0000000000..54533b3a28 --- /dev/null +++ b/radiant/map/MapPropertyInfoFileModule.cpp @@ -0,0 +1,126 @@ +#include "MapPropertyInfoFileModule.h" + +#include "itextstream.h" +#include "parser/DefTokeniser.h" +#include "string/replace.h" + +namespace map +{ + +namespace +{ + const char* const MAP_PROPERTIES = "MapProperties"; + const char* const KEY_VALUE = "KeyValue"; +} + +std::string MapPropertyInfoFileModule::getName() +{ + return "Map Properties"; +} + +void MapPropertyInfoFileModule::onInfoFileSaveStart() +{} + +void MapPropertyInfoFileModule::onBeginSaveMap(const scene::IMapRootNodePtr& root) +{ + // Load all the properties from the map root into a local store + root->foreachProperty([this](const std::string& key, const std::string& value) + { + _store.setProperty(key, value); + }); +} + +void MapPropertyInfoFileModule::onFinishSaveMap(const scene::IMapRootNodePtr& root) +{} + +void MapPropertyInfoFileModule::onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) +{} + +void MapPropertyInfoFileModule::onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) +{} + +void MapPropertyInfoFileModule::writeBlocks(std::ostream& stream) +{ + // Block output + stream << "\t" << MAP_PROPERTIES << std::endl; + + stream << "\t{" << std::endl; + + _store.foreachProperty([&](const std::string& key, const std::string& value) + { + stream << "\t\t" << KEY_VALUE << " { " << + "\"" << string::replace_all_copy(key, "\"", """) << "\"" << + " " << + "\"" << string::replace_all_copy(value, "\"", """) << "\"" << + " } " << std::endl; + }); + + stream << "\t}" << std::endl; + + rMessage() << "Map Properties written." << std::endl; +} + +void MapPropertyInfoFileModule::onInfoFileSaveFinished() +{ + _store.clearProperties(); +} + +void MapPropertyInfoFileModule::onInfoFileLoadStart() +{ + _store.clearProperties(); +} + +bool MapPropertyInfoFileModule::canParseBlock(const std::string& blockName) +{ + return blockName == MAP_PROPERTIES; +} + +void MapPropertyInfoFileModule::parseBlock(const std::string& blockName, parser::DefTokeniser& tok) +{ + // The opening brace + tok.assertNextToken("{"); + + int blockLevel = 1; + + while (tok.hasMoreTokens() && blockLevel > 0) + { + std::string token = tok.nextToken(); + + if (token == KEY_VALUE) + { + // KeyValue { "Key" "Value" } + tok.assertNextToken("{"); + + std::string key = tok.nextToken(); + std::string value = tok.nextToken(); + + string::replace_all(key, """, "\""); + string::replace_all(value, """, "\""); + + tok.assertNextToken("}"); + } + else if (token == "{") + { + blockLevel++; + } + else if (token == "}") + { + blockLevel--; + } + } +} + +void MapPropertyInfoFileModule::applyInfoToScene(const scene::IMapRootNodePtr& root, const NodeIndexMap& nodeMap) +{ + _store.foreachProperty([&](const std::string& key, const std::string& value) + { + root->setProperty(key, value); + }); +} + +void MapPropertyInfoFileModule::onInfoFileLoadFinished() +{ + rMessage() << "[InfoFile]: Parsed " << _store.getPropertyCount() << " map properties." << std::endl; +} + +} diff --git a/radiant/map/MapPropertyInfoFileModule.h b/radiant/map/MapPropertyInfoFileModule.h new file mode 100644 index 0000000000..94bbdad213 --- /dev/null +++ b/radiant/map/MapPropertyInfoFileModule.h @@ -0,0 +1,37 @@ +#pragma once + +#include "imapinfofile.h" +#include "KeyValueStore.h" + +namespace map +{ + +/** +* Info File Module to persist the map property bag to the +* .darkradiant map info file. +*/ +class MapPropertyInfoFileModule : + public IMapInfoFileModule +{ +private: + KeyValueStore _store; + +public: + std::string getName() override; + + void onInfoFileSaveStart() override; + void onBeginSaveMap(const scene::IMapRootNodePtr& root) override; + void onFinishSaveMap(const scene::IMapRootNodePtr& root) override; + void onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) override; + void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) override; + void writeBlocks(std::ostream& stream) override; + void onInfoFileSaveFinished() override; + + void onInfoFileLoadStart() override; + bool canParseBlock(const std::string& blockName) override; + void parseBlock(const std::string& blockName, parser::DefTokeniser& tok) override; + void applyInfoToScene(const scene::IMapRootNodePtr& root, const NodeIndexMap& nodeMap) override; + void onInfoFileLoadFinished() override; +}; + +} diff --git a/radiant/map/algorithm/MapExporter.cpp b/radiant/map/algorithm/MapExporter.cpp index 11249a98b4..9f21ccb6d6 100644 --- a/radiant/map/algorithm/MapExporter.cpp +++ b/radiant/map/algorithm/MapExporter.cpp @@ -6,6 +6,7 @@ #include "ibrush.h" #include "ipatch.h" #include "ientity.h" +#include "imap.h" #include "igroupnode.h" #include "imainframe.h" #include "../../brush/Brush.h" @@ -88,6 +89,16 @@ void MapExporter::exportMap(const scene::INodePtr& root, const GraphTraversalFun try { _writer.beginWriteMap(_mapStream); + + if (_infoFileExporter) + { + auto mapRoot = std::dynamic_pointer_cast(root); + + if (mapRoot) + { + _infoFileExporter->beginSaveMap(mapRoot); + } + } } catch (IMapWriter::FailureException& ex) { @@ -100,6 +111,16 @@ void MapExporter::exportMap(const scene::INodePtr& root, const GraphTraversalFun try { _writer.endWriteMap(_mapStream); + + if (_infoFileExporter) + { + auto mapRoot = std::dynamic_pointer_cast(root); + + if (mapRoot) + { + _infoFileExporter->finishSaveMap(mapRoot); + } + } } catch (IMapWriter::FailureException& ex) { diff --git a/radiant/map/infofile/InfoFileExporter.cpp b/radiant/map/infofile/InfoFileExporter.cpp index 007a3c2e2e..5048abbdfa 100644 --- a/radiant/map/infofile/InfoFileExporter.cpp +++ b/radiant/map/infofile/InfoFileExporter.cpp @@ -41,6 +41,22 @@ InfoFileExporter::~InfoFileExporter() }); } +void InfoFileExporter::beginSaveMap(const scene::IMapRootNodePtr& root) +{ + GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module) + { + module.onBeginSaveMap(root); + }); +} + +void InfoFileExporter::finishSaveMap(const scene::IMapRootNodePtr& root) +{ + GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module) + { + module.onFinishSaveMap(root); + }); +} + void InfoFileExporter::visitEntity(const scene::INodePtr& node, std::size_t entityNum) { GlobalMapInfoFileManager().foreachModule([&](IMapInfoFileModule& module) diff --git a/radiant/map/infofile/InfoFileExporter.h b/radiant/map/infofile/InfoFileExporter.h index 0d382dc16f..1b5b297f49 100644 --- a/radiant/map/infofile/InfoFileExporter.h +++ b/radiant/map/infofile/InfoFileExporter.h @@ -3,6 +3,7 @@ #include #include #include "inode.h" +#include "imap.h" #include namespace map @@ -23,6 +24,8 @@ class InfoFileExporter // Is called by the owning MapExporter // Requirements: node must not be NULL and not a model/particle node. + void beginSaveMap(const scene::IMapRootNodePtr& root); + void finishSaveMap(const scene::IMapRootNodePtr& root); void visitEntity(const scene::INodePtr& node, std::size_t entityNum); void visitPrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum); }; diff --git a/radiant/selection/group/SelectionGroupInfoFileModule.cpp b/radiant/selection/group/SelectionGroupInfoFileModule.cpp index a78e586544..4e39073fce 100644 --- a/radiant/selection/group/SelectionGroupInfoFileModule.cpp +++ b/radiant/selection/group/SelectionGroupInfoFileModule.cpp @@ -35,6 +35,12 @@ void SelectionGroupInfoFileModule::onInfoFileSaveStart() _nodeInfoCount = 0; } +void SelectionGroupInfoFileModule::onBeginSaveMap(const scene::IMapRootNodePtr& root) +{} + +void SelectionGroupInfoFileModule::onFinishSaveMap(const scene::IMapRootNodePtr& root) +{} + void SelectionGroupInfoFileModule::onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) { saveNode(node, entityNum, primitiveNum); diff --git a/radiant/selection/group/SelectionGroupInfoFileModule.h b/radiant/selection/group/SelectionGroupInfoFileModule.h index c916dfc884..5d7a436e22 100644 --- a/radiant/selection/group/SelectionGroupInfoFileModule.h +++ b/radiant/selection/group/SelectionGroupInfoFileModule.h @@ -39,6 +39,8 @@ class SelectionGroupInfoFileModule : std::string getName() override; void onInfoFileSaveStart() override; + void onBeginSaveMap(const scene::IMapRootNodePtr& root) override; + void onFinishSaveMap(const scene::IMapRootNodePtr& root) override; void onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) override; void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) override; void writeBlocks(std::ostream& stream) override; diff --git a/radiant/selection/selectionset/SelectionSetInfoFileModule.cpp b/radiant/selection/selectionset/SelectionSetInfoFileModule.cpp index 89503adfaf..f5bc5474ae 100644 --- a/radiant/selection/selectionset/SelectionSetInfoFileModule.cpp +++ b/radiant/selection/selectionset/SelectionSetInfoFileModule.cpp @@ -21,6 +21,12 @@ std::string SelectionSetInfoFileModule::getName() return "Selection Set Mapping"; } +void SelectionSetInfoFileModule::onBeginSaveMap(const scene::IMapRootNodePtr& root) +{} + +void SelectionSetInfoFileModule::onFinishSaveMap(const scene::IMapRootNodePtr& root) +{} + void SelectionSetInfoFileModule::onInfoFileSaveStart() { _exportInfo.clear(); diff --git a/radiant/selection/selectionset/SelectionSetInfoFileModule.h b/radiant/selection/selectionset/SelectionSetInfoFileModule.h index e21cde4c0d..09bce96d60 100644 --- a/radiant/selection/selectionset/SelectionSetInfoFileModule.h +++ b/radiant/selection/selectionset/SelectionSetInfoFileModule.h @@ -46,6 +46,8 @@ class SelectionSetInfoFileModule : std::string getName() override; void onInfoFileSaveStart() override; + void onBeginSaveMap(const scene::IMapRootNodePtr& root) override; + void onFinishSaveMap(const scene::IMapRootNodePtr& root) override; void onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) override; void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) override; void writeBlocks(std::ostream& stream) override; diff --git a/tools/msvc/DarkRadiant.vcxproj b/tools/msvc/DarkRadiant.vcxproj index 3c14fdb49c..4960aca537 100644 --- a/tools/msvc/DarkRadiant.vcxproj +++ b/tools/msvc/DarkRadiant.vcxproj @@ -287,6 +287,7 @@ + @@ -1143,6 +1144,7 @@ + diff --git a/tools/msvc/DarkRadiant.vcxproj.filters b/tools/msvc/DarkRadiant.vcxproj.filters index 5438fdc492..7e12c51ab2 100644 --- a/tools/msvc/DarkRadiant.vcxproj.filters +++ b/tools/msvc/DarkRadiant.vcxproj.filters @@ -1579,6 +1579,9 @@ src\map\format + + src\map + @@ -3198,6 +3201,9 @@ src\map\format + + src\map +