Introduced V9MM version. It has the same format as V8MM but with extra flag in tracks data.

Signed-off-by: Sergiy Kozyr <s.trump@gmail.com>
This commit is contained in:
Sergiy Kozyr 2024-08-14 11:17:34 +03:00
parent fcbf955aea
commit e0e59da62b
5 changed files with 169 additions and 9 deletions

View file

@ -22,6 +22,7 @@ set(SRC
types_v8.hpp
types_v8mm.hpp
types_v9.hpp
types_v9mm.hpp
visitors.hpp
)

View file

@ -26,9 +26,12 @@ enum class Version : uint8_t
// tags to kml
V9 = 9, // 01 October 2020: add minZoom to bookmarks
Latest = V9,
V8MM = 10 // 27 July 2023: MapsMe release version v15.0.71617. Technically it's version is 8
// (first byte is 0x08), but it's not compatible with V8 from this repo. It has
// no compilations.
V8MM = 10, // 27 July 2023: MapsMe release version v15.0.71617. Technically it's version is 8
// (first byte is 0x08), but it's not compatible with V8 from this repo. It has
// no compilations.
V9MM = 11 // In July 2024 MapsMe release version with new KMB format. Technically its version is 9
// (first byte is 0x09), but it's not compatible with OrganicMaps V9 from this repo. It has
// extra flag in tracks data.
};
struct Header

View file

@ -865,7 +865,7 @@ UNIT_TEST(Kml_Deserialization_From_Bin_V8_And_V8MM)
TEST_EQUAL(dataFromBinV8.m_tracksData, dataFromBinV8MM.m_tracksData, ());
}
UNIT_TEST(Kml_Deserialization_From_KMB_V9MM)
UNIT_TEST(Kml_Deserialization_From_KMB_V8_And_V9MM)
{
kml::FileData dataFromBinV8;
try
@ -903,7 +903,10 @@ UNIT_TEST(Kml_Deserialization_From_KMB_V9MM)
TEST_EQUAL(dataFromBinV8.m_categoryData.m_tags, dataFromBinV9MM.m_categoryData.m_tags, ());
TEST_EQUAL(dataFromBinV8.m_categoryData.m_properties, dataFromBinV9MM.m_categoryData.m_properties, ());
dataFromBinV8.m_bookmarksData[0].m_id = dataFromBinV9MM.m_bookmarksData[0].m_id; // V8 and V9MM bookmarks have different IDs. Fix ID value manually.
TEST_EQUAL(dataFromBinV8.m_bookmarksData, dataFromBinV9MM.m_bookmarksData, ());
dataFromBinV8.m_tracksData[0].m_id = dataFromBinV9MM.m_tracksData[0].m_id; // V8 and V9MM tracks have different IDs. Fix ID value manually.
TEST_EQUAL(dataFromBinV8.m_tracksData, dataFromBinV9MM.m_tracksData, ());
}

View file

@ -6,6 +6,7 @@
#include "kml/types_v7.hpp"
#include "kml/types_v8.hpp"
#include "kml/types_v8mm.hpp"
#include "kml/types_v9mm.hpp"
#include "kml/types.hpp"
#include "kml/visitors.hpp"
@ -195,6 +196,16 @@ public:
m_data = dataV8MM.ConvertToLatestVersion();
break;
}
case Version::V9MM:
{
FileDataV9MM dataV9MM;
dataV9MM.m_deviceId = m_data.m_deviceId;
dataV9MM.m_serverId = m_data.m_serverId;
DeserializeFileData(subReader, dataV9MM);
m_data = dataV9MM.ConvertToLatestVersion();
break;
}
case Version::V7:
{
FileDataV7 dataV7;
@ -251,12 +262,10 @@ private:
NonOwningReaderSource source(reader);
m_header.Deserialize(source);
// The recent MapsMe update increased the version number, but it is not clear yet what changed/added in a newer version.
// Revise V9 in case of discovered crashes.
if (m_header.m_version == Version::V8 || m_header.m_version == Version::V9)
if (m_header.m_version == Version::V8)
{
// Check if file has Opensource V8 or MapsMe V8.
// Actual V8 format has 6 offset (uint64_t) in header. While V8MM has 5 offsets.
// Check if file has Opensource V8 or MapsMe V8 format.
// Actual V8 format has 6 offsets (uint64_t) in header. While V8MM has 5 offsets.
// It means that first section (usually categories) has offset 0x28 = 40 = 5 * 8.
if (m_header.m_categoryOffset == 0x28 || m_header.m_bookmarksOffset == 0x28 ||
m_header.m_tracksOffset == 0x28 || m_header.m_stringsOffset == 0x28 ||
@ -268,6 +277,21 @@ private:
m_header.m_stringsOffset = m_header.m_compilationsOffset;
}
}
if (m_header.m_version == Version::V9)
{
// Check if file has Opensource V9 or MapsMe V9 format.
// Actual V9 format has 6 offsets (uint64_t) in header. While V9MM has 5 offsets.
// It means that first section (usually categories) has offset 0x28 = 40 = 5 * 8.
if (m_header.m_categoryOffset == 0x28 || m_header.m_bookmarksOffset == 0x28 ||
m_header.m_tracksOffset == 0x28 || m_header.m_stringsOffset == 0x28 ||
m_header.m_compilationsOffset == 0x28)
{
LOG(LWARNING, ("KMB file has version V9MM"));
m_header.m_version = Version::V9MM;
m_header.m_eosOffset = m_header.m_stringsOffset;
m_header.m_stringsOffset = m_header.m_compilationsOffset;
}
}
m_initialized = true;
}

129
kml/types_v9mm.hpp Normal file
View file

@ -0,0 +1,129 @@
#pragma once
#include "kml/types.hpp"
#include "kml/types_v8mm.hpp"
namespace kml
{
struct TrackDataV9MM
{
DECLARE_VISITOR_AND_DEBUG_PRINT(TrackDataV9MM, visitor(m_id, "id"),
visitor(m_localId, "localId"),
visitor(m_name, "name"),
visitor(m_description, "description"),
visitor(m_layers, "layers"),
visitor(m_timestamp, "timestamp"),
visitor(m_flag1, "flag1"), // Extra field introduced in V9MM.
visitor(m_geometry, "geometry"),
visitor(m_visible, "visible"),
visitor(m_constant1, "constant1"),
visitor(m_constant2, "constant2"),
visitor(m_constant3, "constant3"),
visitor(m_nearestToponyms, "nearestToponyms"),
visitor(m_properties, "properties"),
VISITOR_COLLECTABLE)
DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_nearestToponyms, m_properties)
bool operator==(TrackDataV9MM const & data) const
{
return m_id == data.m_id && m_localId == data.m_localId && m_name == data.m_name &&
m_description == data.m_description && m_layers == data.m_layers &&
IsEqual(m_timestamp, data.m_timestamp) && m_geometry == data.m_geometry &&
m_visible == data.m_visible && m_nearestToponyms == data.m_nearestToponyms &&
m_properties == data.m_properties;
}
bool operator!=(TrackDataV9MM const & data) const { return !operator==(data); }
TrackData ConvertToLatestVersion() const
{
TrackData data;
data.m_id = m_id;
data.m_localId = m_localId;
data.m_name = m_name;
data.m_description = m_description;
data.m_layers = m_layers;
data.m_timestamp = m_timestamp;
data.m_geometry = m_geometry;
data.m_visible = m_visible;
data.m_nearestToponyms = m_nearestToponyms;
data.m_properties = m_properties;
return data;
}
// Unique id (it will not be serialized in text files).
TrackId m_id = kInvalidTrackId;
// Local track id.
LocalId m_localId = 0;
// Track's name.
LocalizableString m_name;
// Track's description.
LocalizableString m_description;
// Layers.
std::vector<TrackLayer> m_layers;
// Creation timestamp.
TimestampMillis m_timestamp{};
MultiGeometry m_geometry;
// Visibility.
bool m_visible = true;
// These constants were introduced in KMB V8MM. Usually have value 0. Don't know its purpose.
uint8_t m_constant1 = 0;
uint8_t m_constant2 = 0;
uint8_t m_constant3 = 0;
// Nearest toponyms.
std::vector<std::string> m_nearestToponyms;
// Key-value properties.
Properties m_properties;
// Extra field introduced in V9MM.
bool m_flag1 = true;
};
// FileDataV8MM contains the same sections as FileDataV8MM but with changed m_tracksData format
struct FileDataV9MM
{
DECLARE_VISITOR_AND_DEBUG_PRINT(FileDataV9MM, visitor(m_serverId, "serverId"),
visitor(m_categoryData, "category"),
visitor(m_bookmarksData, "bookmarks"),
visitor(m_tracksData, "tracks"))
bool operator==(FileDataV9MM const & data) const
{
return m_serverId == data.m_serverId && m_categoryData == data.m_categoryData &&
m_bookmarksData == data.m_bookmarksData && m_tracksData == data.m_tracksData;
}
bool operator!=(FileDataV9MM const & data) const { return !operator==(data); }
FileData ConvertToLatestVersion()
{
FileData data;
data.m_deviceId = m_deviceId;
data.m_serverId = m_serverId;
data.m_categoryData = m_categoryData.ConvertToLatestVersion();
data.m_bookmarksData.reserve(m_bookmarksData.size());
for (auto & d : m_bookmarksData)
data.m_bookmarksData.emplace_back(d.ConvertToLatestVersion());
data.m_tracksData.reserve(m_tracksData.size());
for (auto & t : m_tracksData)
data.m_tracksData.emplace_back(t.ConvertToLatestVersion());
return data;
}
// Device id (it will not be serialized in text files).
std::string m_deviceId;
// Server id.
std::string m_serverId;
// Category's data.
CategoryDataV8MM m_categoryData;
// Bookmarks collection.
std::vector<BookmarkDataV8MM> m_bookmarksData;
// Tracks collection.
std::vector<TrackDataV9MM> m_tracksData;
};
} // namespace kml