From d9f4daefc2466edde40ef29ad10f0bf4ffad04a8 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 29 Nov 2020 04:39:40 +0100 Subject: [PATCH] #5108: Add ArchivedMapResource specialisation, which will load the map from any PAK file. Add two virtual method to be overridden by the ArchivedMapResource --- include/imapresource.h | 13 ++++- radiant/ui/prefabselector/PrefabSelector.cpp | 2 +- radiantcore/map/ArchivedMapResource.h | 20 +++++++ radiantcore/map/Map.cpp | 6 ++- radiantcore/map/MapResource.cpp | 55 +++++++++++++------- radiantcore/map/MapResource.h | 13 +++-- radiantcore/map/MapResourceManager.cpp | 13 +++-- radiantcore/map/MapResourceManager.h | 4 +- tools/msvc/DarkRadiantCore.vcxproj | 1 + tools/msvc/DarkRadiantCore.vcxproj.filters | 3 ++ 10 files changed, 96 insertions(+), 34 deletions(-) create mode 100644 radiantcore/map/ArchivedMapResource.h diff --git a/include/imapresource.h b/include/imapresource.h index 3ccf71045f..0c2cc942df 100644 --- a/include/imapresource.h +++ b/include/imapresource.h @@ -83,9 +83,18 @@ class IMapResourceManager : { public: /** - * Load the named map resource from VFS or from a physical path. + * Create a named map resource from VFS or from a physical path. */ - virtual IMapResourcePtr loadFromPath(const std::string& path) = 0; + virtual IMapResourcePtr createFromPath(const std::string& path) = 0; + + /** + * Create a named map resource that is contained within a PAK archive + * outside the VFS. + * archivePath is the absolute path to the archive file, e.g. "/home/greebo/outpost.pk4" + * filePathWithinArchive is the relative path within the archive, e.g. "maps/outpost.map" + */ + virtual IMapResourcePtr createFromArchiveFile(const std::string& archivePath, + const std::string& filePathWithinArchive) = 0; // Signal emitted when a MapExport is starting / is finished typedef sigc::signal ExportEvent; diff --git a/radiant/ui/prefabselector/PrefabSelector.cpp b/radiant/ui/prefabselector/PrefabSelector.cpp index 4404136b68..cca5802278 100644 --- a/radiant/ui/prefabselector/PrefabSelector.cpp +++ b/radiant/ui/prefabselector/PrefabSelector.cpp @@ -402,7 +402,7 @@ void PrefabSelector::onSelectionChanged(wxutil::FileSystemView::SelectionChanged const auto& prefabPath = ev.GetSelectedPath(); - _mapResource = GlobalMapResourceManager().loadFromPath(prefabPath); + _mapResource = GlobalMapResourceManager().createFromPath(prefabPath); if (!_mapResource) { diff --git a/radiantcore/map/ArchivedMapResource.h b/radiantcore/map/ArchivedMapResource.h new file mode 100644 index 0000000000..4f6b88f30c --- /dev/null +++ b/radiantcore/map/ArchivedMapResource.h @@ -0,0 +1,20 @@ +#pragma once + +#include "MapResource.h" + +namespace map +{ + +class ArchivedMapResource : + public MapResource +{ +public: + ArchivedMapResource(const std::string& archivePath, const std::string& filePathWithinArchive) : + MapResource(filePathWithinArchive) + {} + +protected: + +}; + +} diff --git a/radiantcore/map/Map.cpp b/radiantcore/map/Map.cpp index 54cc61669e..a2688ed960 100644 --- a/radiantcore/map/Map.cpp +++ b/radiantcore/map/Map.cpp @@ -101,7 +101,9 @@ void Map::loadMapResourceFromLocation(const MapLocation& location) // Map loading started emitMapEvent(MapLoading); - _resource = GlobalMapResourceManager().loadFromPath(location.path); + _resource = location.isArchive ? + GlobalMapResourceManager().createFromArchiveFile(location.path, location.archiveRelativePath) : + GlobalMapResourceManager().createFromPath(location.path); if (!_resource) { @@ -394,7 +396,7 @@ bool Map::import(const std::string& filename) { bool success = false; - IMapResourcePtr resource = GlobalMapResourceManager().loadFromPath(filename); + IMapResourcePtr resource = GlobalMapResourceManager().createFromPath(filename); try { diff --git a/radiantcore/map/MapResource.cpp b/radiantcore/map/MapResource.cpp index ee9b7c0963..8373fd7fd7 100644 --- a/radiantcore/map/MapResource.cpp +++ b/radiantcore/map/MapResource.cpp @@ -253,11 +253,13 @@ RootNodePtr MapResource::loadMapNode() { RootNodePtr rootNode; - // Build the map path - auto fullpath = getAbsoluteResourcePath(); + // Open a stream - will throw on failure + auto stream = openMapfileStream(); - // Open a stream (from physical file or VFS) - will throw on failure - auto stream = openFileStream(fullpath); + if (!stream || !stream->isOpen()) + { + throw OperationException(_("Could not open map stream")); + } try { @@ -283,23 +285,11 @@ RootNodePtr MapResource::loadMapNode() // Check if an info file is supported by this map format if (format->allowInfoFileCreation()) { - try - { - // Load for an additional info file - auto infoFilename = fullpath.substr(0, fullpath.rfind('.')); - infoFilename += getInfoFileExtension(); + auto infoFileStream = openInfofileStream(); - auto infoFileStream = openFileStream(infoFilename); - - if (infoFileStream->isOpen()) - { - loader.loadInfoFile(infoFileStream->getStream(), rootNode); - } - } - catch (const OperationException& ex) + if (infoFileStream && infoFileStream->isOpen()) { - // Info file load file does not stop us, just issue a warning - rWarning() << ex.what() << std::endl; + loader.loadInfoFile(infoFileStream->getStream(), rootNode); } } } @@ -307,7 +297,7 @@ RootNodePtr MapResource::loadMapNode() { // Re-throw the exception, prepending the map file path to the message (if not cancelled) throw ex.operationCancelled() ? ex : - OperationException(fmt::format(_("Failure reading map file:\n{0}\n\n{1}"), fullpath, ex.what())); + OperationException(fmt::format(_("Failure reading map file:\n{0}\n\n{1}"), getAbsoluteResourcePath(), ex.what())); } return rootNode; @@ -326,6 +316,31 @@ stream::MapResourceStream::Ptr MapResource::openFileStream(const std::string& pa return stream; } +stream::MapResourceStream::Ptr MapResource::openMapfileStream() +{ + return openFileStream(getAbsoluteResourcePath()); +} + +stream::MapResourceStream::Ptr MapResource::openInfofileStream() +{ + try + { + auto fullpath = getAbsoluteResourcePath(); + + // Load for an additional info file + auto infoFilename = fullpath.substr(0, fullpath.rfind('.')); + infoFilename += getInfoFileExtension(); + + return openFileStream(infoFilename); + } + catch (const OperationException& ex) + { + // Info file load file does not stop us, just issue a warning + rWarning() << ex.what() << std::endl; + return stream::MapResourceStream::Ptr(); + } +} + std::string MapResource::getInfoFileExtension() { std::string extension = game::current::getValue(GKEY_INFO_FILE_EXTENSION); diff --git a/radiantcore/map/MapResource.h b/radiantcore/map/MapResource.h index 420f413f27..5d25192315 100644 --- a/radiantcore/map/MapResource.h +++ b/radiantcore/map/MapResource.h @@ -2,7 +2,6 @@ #include "imapresource.h" #include "imapformat.h" -#include "imapinfofile.h" #include "imodel.h" #include "imap.h" #include @@ -50,6 +49,15 @@ class MapResource : static void saveFile(const MapFormat& format, const scene::IMapRootNodePtr& root, const GraphTraversalFunc& traverse, const std::string& filename); +protected: + // Implementation-specific method to open the stream of the primary .map or .mapx file + // May return an empty reference, may throw OperationException on failure + virtual stream::MapResourceStream::Ptr openMapfileStream(); + + // Implementation-specific method to open the info file stream (.darkradiant) file + // May return an empty reference, may throw OperationException on failure + virtual stream::MapResourceStream::Ptr openInfofileStream(); + private: void constructPaths(const std::string& resourcePath); std::string getAbsoluteResourcePath(); @@ -74,8 +82,5 @@ class MapResource : // Checks if file can be overwritten (throws on failure) static void throwIfNotWriteable(const fs::path& path); }; -// Resource pointer types -typedef std::shared_ptr MapResourcePtr; -typedef std::weak_ptr MapResourceWeakPtr; } // namespace map diff --git a/radiantcore/map/MapResourceManager.cpp b/radiantcore/map/MapResourceManager.cpp index 2731f8db71..0c35a0c3e8 100644 --- a/radiantcore/map/MapResourceManager.cpp +++ b/radiantcore/map/MapResourceManager.cpp @@ -6,16 +6,22 @@ #include "os/path.h" #include "module/StaticModule.h" #include "MapResource.h" +#include "ArchivedMapResource.h" namespace map { -IMapResourcePtr MapResourceManager::loadFromPath(const std::string& path) +IMapResourcePtr MapResourceManager::createFromPath(const std::string& path) { - // Create a new MapResource and return it. return std::make_shared(path); } +IMapResourcePtr MapResourceManager::createFromArchiveFile(const std::string& archivePath, + const std::string& filePathWithinArchive) +{ + return std::make_shared(archivePath, filePathWithinArchive); +} + MapResourceManager::ExportEvent& MapResourceManager::signal_onResourceExporting() { return _resourceExporting; @@ -26,7 +32,6 @@ MapResourceManager::ExportEvent& MapResourceManager::signal_onResourceExported() return _resourceExported; } -// RegisterableModule implementation const std::string& MapResourceManager::getName() const { static std::string _name(MODULE_MAPRESOURCEMANAGER); @@ -49,7 +54,7 @@ const StringSet& MapResourceManager::getDependencies() const void MapResourceManager::initialiseModule(const IApplicationContext& ctx) { - rMessage() << "MapResourceManager::initialiseModule called." << std::endl; + rMessage() << getName() << "::initialiseModule called." << std::endl; } // Define the MapResourceManager registerable module diff --git a/radiantcore/map/MapResourceManager.h b/radiantcore/map/MapResourceManager.h index 8db80df128..1160aa97ae 100644 --- a/radiantcore/map/MapResourceManager.h +++ b/radiantcore/map/MapResourceManager.h @@ -14,7 +14,9 @@ class MapResourceManager : ExportEvent _resourceExported; public: - IMapResourcePtr loadFromPath(const std::string& path) override; + IMapResourcePtr createFromPath(const std::string& path) override; + IMapResourcePtr createFromArchiveFile(const std::string& archivePath, + const std::string& filePathWithinArchive) override; ExportEvent& signal_onResourceExporting() override; ExportEvent& signal_onResourceExported() override; diff --git a/tools/msvc/DarkRadiantCore.vcxproj b/tools/msvc/DarkRadiantCore.vcxproj index 2373bd5573..6b03164c4f 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj +++ b/tools/msvc/DarkRadiantCore.vcxproj @@ -803,6 +803,7 @@ + diff --git a/tools/msvc/DarkRadiantCore.vcxproj.filters b/tools/msvc/DarkRadiantCore.vcxproj.filters index c3046ac9bc..5fc2a37698 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj.filters +++ b/tools/msvc/DarkRadiantCore.vcxproj.filters @@ -2052,5 +2052,8 @@ src\map + + src\map + \ No newline at end of file