diff --git a/data/kml_test_data/BACRNKMZ.kmz b/data/kml_test_data/BACRNKMZ.kmz new file mode 100644 index 0000000000..ca3769742f Binary files /dev/null and b/data/kml_test_data/BACRNKMZ.kmz differ diff --git a/map/bookmark_helpers.cpp b/map/bookmark_helpers.cpp index 9d684a4cd2..85085923aa 100644 --- a/map/bookmark_helpers.cpp +++ b/map/bookmark_helpers.cpp @@ -21,6 +21,7 @@ #include "base/file_name_utils.hpp" #include "base/string_utils.hpp" +#include #include #include @@ -345,71 +346,83 @@ std::unique_ptr LoadKmlFile(std::string const & file, KmlFileType return kmlData; } -std::string GetKMLorGPXPath(std::string const & filePath) +std::vector GetKMLOrGPXFilesPathsToLoad(std::string const & filePath) { std::string const fileExt = GetLowercaseFileExt(filePath); - std::string fileSavePath; if (fileExt == kKmlExtension) { - // Copy input file to temp output KML with unique name. - fileSavePath = GenerateValidAndUniqueFilePathForKML(base::FileNameFromFullPath(filePath)); - if (!base::CopyFileX(filePath, fileSavePath)) - return {}; + return GetFilePathsToLoadFromKml(filePath); } else if (fileExt == kGpxExtension) { - // Copy input file to temp GPX with unique name. - fileSavePath = GenerateValidAndUniqueFilePathForGPX(base::FileNameFromFullPath(filePath)); - if (!base::CopyFileX(filePath, fileSavePath)) - return {}; + return GetFilePathsToLoadFromGpx(filePath); } else if (fileExt == kKmbExtension) { - // Convert input file and save to temp KML with unique name. - auto kmlData = LoadKmlFile(filePath, KmlFileType::Binary); - if (kmlData == nullptr) - return {}; - - fileSavePath = GenerateValidAndUniqueFilePathForKML(base::FileNameFromFullPath(filePath)); - if (!SaveKmlFileByExt(*kmlData, fileSavePath)) - return {}; + return GetFilePathsToLoadFromKmb(filePath); } else if (fileExt == kKmzExtension) { - // Extract KML file from KMZ archive and save to temp KML with unique name. - try - { - ZipFileReader::FileList files; - ZipFileReader::FilesList(filePath, files); - std::string kmlFileName; - std::string ext; - for (size_t i = 0; i < files.size(); ++i) - { - ext = GetLowercaseFileExt(files[i].first); - if (ext == kKmlExtension) - { - kmlFileName = files[i].first; - break; - } - } - if (kmlFileName.empty()) - return {}; - - fileSavePath = GenerateValidAndUniqueFilePathForKML(kmlFileName); - ZipFileReader::UnzipFile(filePath, kmlFileName, fileSavePath); - } - catch (RootException const & e) - { - LOG(LWARNING, ("Error unzipping file", filePath, e.Msg())); - return {}; - } + return GetFilePathsToLoadFromKmz(filePath); } else { LOG(LWARNING, ("Unknown file type", filePath)); return {}; } - return fileSavePath; +} + +std::vector GetFilePathsToLoadFromKmz(std::string const & filePath) +{ // Extract KML files from KMZ archive and save to temp KMLs with unique name. + std::vector kmlFilePaths; + try + { + ZipFileReader::FileList files; + ZipFileReader::FilesList(filePath, files); + files.erase(std::remove_if(files.begin(), files.end(), + [](auto const & file){ return GetLowercaseFileExt(file.first) != kKmlExtension; }), + files.end()); + for (auto const & [kmlFileInZip, size] : files) + { + auto const name = base::FileNameFromFullPath(kmlFileInZip); + auto fileSavePath = GenerateValidAndUniqueFilePathForKML(kmlFileInZip); + ZipFileReader::UnzipFile(filePath, kmlFileInZip, fileSavePath); + kmlFilePaths.push_back(std::move(fileSavePath)); + } + } + catch (RootException const & e) + { + LOG(LWARNING, ("Error unzipping file", filePath, e.Msg())); + } + return kmlFilePaths; +} + +std::vector GetFilePathsToLoadFromKmb(std::string const & filePath) +{ // Convert input file and save to temp KML with unique name. + auto kmlData = LoadKmlFile(filePath, KmlFileType::Binary); + if (kmlData == nullptr) + return {}; + + auto fileSavePath = GenerateValidAndUniqueFilePathForKML(base::FileNameFromFullPath(filePath)); + if (!SaveKmlFileByExt(*kmlData, fileSavePath)) + return {}; + return {std::move(fileSavePath)}; +} + +std::vector GetFilePathsToLoadFromGpx(std::string const & filePath) +{ // Copy input file to temp GPX with unique name. + auto fileSavePath = GenerateValidAndUniqueFilePathForGPX(base::FileNameFromFullPath(filePath)); + if (!base::CopyFileX(filePath, fileSavePath)) + return {}; + return {std::move(fileSavePath)}; +} + +std::vector GetFilePathsToLoadFromKml(std::string const & filePath) +{ // Copy input file to temp output KML with unique name. + auto fileSavePath = GenerateValidAndUniqueFilePathForKML(base::FileNameFromFullPath(filePath)); + if (!base::CopyFileX(filePath, fileSavePath)) + return {}; + return {std::move(fileSavePath)}; } std::unique_ptr LoadKmlData(Reader const & reader, KmlFileType fileType) diff --git a/map/bookmark_helpers.hpp b/map/bookmark_helpers.hpp index a309e1a1f5..fdff3078e8 100644 --- a/map/bookmark_helpers.hpp +++ b/map/bookmark_helpers.hpp @@ -103,7 +103,11 @@ std::string GenerateValidAndUniqueFilePathForGPX(std::string const & fileName); std::unique_ptr LoadKmlFile(std::string const & file, KmlFileType fileType); std::unique_ptr LoadKmlData(Reader const & reader, KmlFileType fileType); -std::string GetKMLorGPXPath(std::string const & filePath); +std::vector GetKMLOrGPXFilesPathsToLoad(std::string const & filePath); +std::vector GetFilePathsToLoadFromKml(std::string const & filePath); +std::vector GetFilePathsToLoadFromGpx(std::string const & filePath); +std::vector GetFilePathsToLoadFromKmb(std::string const & filePath); +std::vector GetFilePathsToLoadFromKmz(std::string const & filePath); std::string GetLowercaseFileExt(std::string const & filePath); bool SaveKmlFileSafe(kml::FileData & kmlData, std::string const & file, KmlFileType fileType); diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index 18ea5bbaea..1ec2ed2f47 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -1841,15 +1841,14 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT auto collection = std::make_shared(); // Convert KML/KMZ/KMB files to temp KML file and GPX to temp GPX file. - std::string fileSavePath = GetKMLorGPXPath(filePath); - if (!fileSavePath.empty()) + for (auto const & fileToLoad : GetKMLOrGPXFilesPathsToLoad(filePath)) { - auto const ext = GetLowercaseFileExt(fileSavePath); + auto const ext = GetLowercaseFileExt(fileToLoad); std::unique_ptr kmlData; if (ext == kKmlExtension) - kmlData = LoadKmlFile(fileSavePath, KmlFileType::Text); + kmlData = LoadKmlFile(fileToLoad, KmlFileType::Text); else if (ext == kGpxExtension) - kmlData = LoadKmlFile(fileSavePath, KmlFileType::Gpx); + kmlData = LoadKmlFile(fileToLoad, KmlFileType::Gpx); else { ASSERT(false, ("Unsupported bookmarks extension", ext)); @@ -1858,15 +1857,15 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT if (m_needTeardown) return; - base::DeleteFileX(fileSavePath); - if (kmlData) + base::DeleteFileX(fileToLoad); + if (kmlData && (!kmlData->m_tracksData.empty() || !kmlData->m_bookmarksData.empty())) { - fileSavePath = GenerateValidAndUniqueFilePathForKML(base::GetNameFromFullPathWithoutExt(std::move(fileSavePath))); + auto kmlFileToLoad = GenerateValidAndUniqueFilePathForKML(base::GetNameFromFullPathWithoutExt(fileToLoad)); - if (!SaveKmlFileSafe(*kmlData, fileSavePath, KmlFileType::Text)) - base::DeleteFileX(fileSavePath); + if (!SaveKmlFileSafe(*kmlData, kmlFileToLoad, KmlFileType::Text)) + base::DeleteFileX(kmlFileToLoad); else - collection->emplace_back(std::move(fileSavePath), std::move(kmlData)); + collection->emplace_back(std::move(kmlFileToLoad), std::move(kmlData)); } } diff --git a/map/map_tests/kmz_unarchive_test.cpp b/map/map_tests/kmz_unarchive_test.cpp index 70268350d5..07eb5fbee7 100644 --- a/map/map_tests/kmz_unarchive_test.cpp +++ b/map/map_tests/kmz_unarchive_test.cpp @@ -10,8 +10,9 @@ UNIT_TEST(KMZ_UnzipTest) { std::string const kmzFile = GetPlatform().TestsDataPathForFile("kml_test_data/test.kmz"); - std::string const filePath = GetKMLorGPXPath(kmzFile); - + auto const filePaths = GetKMLOrGPXFilesPathsToLoad(kmzFile); + TEST_EQUAL(1, filePaths.size(), ()); + std::string const filePath = filePaths[0]; TEST(!filePath.empty(), ()); SCOPE_GUARD(fileGuard, std::bind(&base::DeleteFileX, filePath)); @@ -39,3 +40,31 @@ UNIT_TEST(KMZ_UnzipTest) TEST_EQUAL(bm.GetScale(), 0, ("KML wrong scale!")); } } + +UNIT_TEST(Multi_KML_KMZ_UnzipTest) +{ + std::string const kmzFile = GetPlatform().TestsDataPathForFile("kml_test_data/BACRNKMZ.kmz"); + auto const filePaths = GetKMLOrGPXFilesPathsToLoad(kmzFile); + std::vector expectedFileNames = + { + "BACRNKMZfilesCampgrounds 26may2022 green and tree icon", + "BACRNKMZfilesIndoor Accommodations 26may2022 placemark purple and bed icon", + "BACRNKMZfilesRoute 1 Canada - West-East Daily Segments", + "BACRNKMZfilesRoute 2 Canada - West-East Daily Segments", + "BACRNKMZfilesRoute Connector Canada - West-East Daily Segments", + "BACRNKMZdoc" + + }; + TEST_EQUAL(expectedFileNames.size(), filePaths.size(), ()); + for (auto const & filePath : filePaths) + { + auto matched = false; + for (auto const & expectedFileName : expectedFileNames) + { + matched = filePath.find(expectedFileName) != std::string::npos; + if (matched) + break; + } + TEST(matched, ("Unexpected file path: " + filePath)); + } +}