1 /*
2 * RpMap Tiled Plugin
3 * Copyright 2020, Christof Petig <christof.petig@arcor.de>
4 *
5 * This file is part of Tiled.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "rpmapplugin.h"
22
23 #include "gidmapper.h"
24 #include "map.h"
25 #include "mapobject.h"
26 #include "savefile.h"
27 #include "tile.h"
28 #include "tiled.h"
29 #include "tilelayer.h"
30 #include "tileset.h"
31 #include "objectgroup.h"
32
33 #include <QCoreApplication>
34 #include <QDir>
35 #include <QFileInfo>
36 #include <QStringList>
37 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
38 #include <QStringView>
39 #endif
40 #include <QTextStream>
41 #include <QXmlStreamWriter>
42 #include <QUuid>
43 #include <QCryptographicHash>
44 #include <kzip.h>
45
46 #include <memory>
47
48 using namespace Tiled;
49
50 namespace RpMap {
51
RpMapPlugin()52 RpMapPlugin::RpMapPlugin()
53 {
54 }
55
56 #if 0 // not implemented for now
57 std::unique_ptr<Tiled::Map> RpMapPlugin::read(const QString &fileName)
58 {
59 // the problem is to either reference an already loaded tileset (how) or to import the included image resources
60 KZip archive(fileName);
61 if (archive.open(QIODevice::ReadOnly)) {
62 const KArchiveDirectory *dir = archive.directory();
63
64 const KArchiveEntry *e = dir->entry("content.xml");
65 if (!e) {
66 //qDebug() << "File not found!";
67 return nullptr;
68 }
69 const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
70 QByteArray arr(f->data());
71 //qDebug() << arr; // the file contents
72
73 // To avoid reading everything into memory in one go, we can use createDevice() instead
74 #if 0
75 QIODevice *dev = f->createDevice();
76 while (!dev->atEnd()) {
77 qDebug() << dev->readLine();
78 }
79 delete dev;
80 #endif
81 }
82 return nullptr;
83 }
84
85 bool RpMapPlugin::supportsFile(const QString &fileName) const
86 {
87 return fileName.endsWith(QLatin1String(".rpmap"), Qt::CaseInsensitive);
88 }
89 #endif
90
nameFilter() const91 QString RpMapPlugin::nameFilter() const
92 {
93 return tr("RpTool MapTool files (*.rpmap)");
94 }
95
shortName() const96 QString RpMapPlugin::shortName() const
97 {
98 return QStringLiteral("rpmap");
99 }
100
errorString() const101 QString RpMapPlugin::errorString() const
102 {
103 return mError;
104 }
105
writeEntry(QXmlStreamWriter & writer,QString const & key,QString const & value)106 static void writeEntry(QXmlStreamWriter &writer, QString const &key, QString const& value)
107 {
108 writer.writeStartElement(QStringLiteral("entry"));
109 writer.writeTextElement(QStringLiteral("string"), key);
110 writer.writeTextElement(QStringLiteral("string"), value);
111 writer.writeEndElement();
112 }
113
writeGUID(QXmlStreamWriter & writer,QString const & key,QUuid const & id)114 static void writeGUID(QXmlStreamWriter &writer, QString const &key, QUuid const& id)
115 {
116 writer.writeStartElement(key);
117 writer.writeTextElement(QStringLiteral("baGUID"), id.toRfc4122().toBase64());
118 writer.writeEndElement();
119 }
120
writeTile(QXmlStreamWriter & writer,int x,int y,QString const & name,int facing,QString const & md5,bool flipx,bool flipy)121 static void writeTile(QXmlStreamWriter &writer, int x, int y, QString const& name, int facing, QString const& md5, bool flipx, bool flipy)
122 {
123 writer.writeStartElement(QStringLiteral("entry"));
124 writeGUID(writer, QStringLiteral("net.rptools.maptool.model.GUID"), QUuid::createUuid());
125 writer.writeStartElement(QStringLiteral("net.rptools.maptool.model.Token"));
126 writer.writeStartElement(QStringLiteral("id")); writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../../net.rptools.maptool.model.GUID")); writer.writeEndElement();
127 //writeGUID(writer, QStringLiteral("exposedAreaGUID"), QUuid::createUuid());
128 writer.writeStartElement(QStringLiteral("imageAssetMap"));
129 writer.writeStartElement(QStringLiteral("entry"));
130 writer.writeEmptyElement(QStringLiteral("null"));
131 writer.writeStartElement(QStringLiteral("net.rptools.lib.MD5Key"));
132 writer.writeTextElement(QStringLiteral("id"), md5);
133 writer.writeEndElement(); // MD5Key
134 writer.writeEndElement(); // entry
135 writer.writeEndElement(); // imageAssetMap
136 writer.writeTextElement(QStringLiteral("x"), QString::number(x));
137 writer.writeTextElement(QStringLiteral("y"), QString::number(y));
138 writer.writeTextElement(QStringLiteral("z"), QString::number(1));
139 writer.writeTextElement(QStringLiteral("anchorX"), QString::number(0));
140 writer.writeTextElement(QStringLiteral("anchorY"), QString::number(0));
141 writer.writeTextElement(QStringLiteral("snapToScale"), QStringLiteral("false"));
142 writer.writeTextElement(QStringLiteral("width"), QString::number(300));
143 writer.writeTextElement(QStringLiteral("height"), QString::number(300));
144 writer.writeTextElement(QStringLiteral("isoWidth"), QString::number(0));
145 writer.writeTextElement(QStringLiteral("isoHeight"), QString::number(0));
146 writer.writeTextElement(QStringLiteral("scaleX"), QString::number(1.0));
147 writer.writeTextElement(QStringLiteral("scaleY"), QString::number(1.0));
148
149 writer.writeStartElement(QStringLiteral("sizeMap"));
150 writer.writeStartElement(QStringLiteral("entry"));
151 writer.writeTextElement(QStringLiteral("java-class"), QStringLiteral("net.rptools.maptool.model.SquareGrid"));
152 writeGUID(writer, QStringLiteral("net.rptools.maptool.model.GUID"), QUuid::createUuid());
153 writer.writeEndElement(); // entry
154 writer.writeEndElement(); // sizeMap
155
156 writer.writeTextElement(QStringLiteral("snapToGrid"), QStringLiteral("true"));
157 writer.writeTextElement(QStringLiteral("isVisible"), QStringLiteral("true"));
158 writer.writeTextElement(QStringLiteral("visibleOnlyToOwner"), QStringLiteral("false"));
159 writer.writeTextElement(QStringLiteral("vblColorSensitivity"), QString::number(-1));
160 writer.writeTextElement(QStringLiteral("alwaysVisibleTolerance"), QString::number(2));
161 writer.writeTextElement(QStringLiteral("isAlwaysVisible"), QStringLiteral("false"));
162 writer.writeTextElement(QStringLiteral("name"), name);
163 writer.writeTextElement(QStringLiteral("ownerType"), QString::number(0));
164 writer.writeTextElement(QStringLiteral("tokenShape"), QStringLiteral("TOP_DOWN"));
165 writer.writeTextElement(QStringLiteral("tokenType"), QStringLiteral("NPC"));
166 writer.writeTextElement(QStringLiteral("layer"), QStringLiteral("BACKGROUND"));
167 writer.writeTextElement(QStringLiteral("propertyType"), QStringLiteral("Basic"));
168 writer.writeTextElement(QStringLiteral("facing"), QString::number(facing));
169 writer.writeTextElement(QStringLiteral("tokenOpacity"), QString::number(1.0));
170 writer.writeTextElement(QStringLiteral("terrainModifier"), QString::number(0.0));
171 writer.writeTextElement(QStringLiteral("terrainModifierOperation"), QStringLiteral("NONE"));
172 writer.writeStartElement(QStringLiteral("terrainModifiersIgnored"));
173 writer.writeTextElement(QStringLiteral("net.rptools.maptool.model.Token_-TerrainModifierOperation"), QStringLiteral("NONE"));
174 writer.writeEndElement(); // terrainModifiersIgnored
175 writer.writeTextElement(QStringLiteral("isFlippedX"), flipx ? QStringLiteral("true") : QStringLiteral("false"));
176 writer.writeTextElement(QStringLiteral("isFlippedY"), flipy ? QStringLiteral("true") : QStringLiteral("false"));
177 writer.writeTextElement(QStringLiteral("sightType"), QStringLiteral("Normal"));
178 writer.writeTextElement(QStringLiteral("hasSight"), QStringLiteral("false"));
179 writer.writeEmptyElement(QStringLiteral("state"));
180 writer.writeStartElement(QStringLiteral("propertyMapCI"));
181 writer.writeEmptyElement(QStringLiteral("store"));
182 writer.writeEndElement(); // propertyMapCI
183 writer.writeEmptyElement(QStringLiteral("macroPropertiesMap"));
184 writer.writeEmptyElement(QStringLiteral("speechMap"));
185
186 writer.writeEndElement(); // net.rptools.maptool.model.Token
187 writer.writeEndElement(); // entry
188 }
189
writeCellShape(QXmlStreamWriter & writer,Tiled::Map const * map)190 static void writeCellShape(QXmlStreamWriter &writer, Tiled::Map const* map)
191 {
192 writer.writeStartElement(QStringLiteral("cellShape"));
193 writer.writeStartElement(QStringLiteral("curves"));
194 writer.writeStartElement(QStringLiteral("sun.awt.geom.Order0"));
195 writer.writeTextElement(QStringLiteral("direction"), QStringLiteral("1"));
196 writer.writeTextElement(QStringLiteral("x"), QStringLiteral("0.0"));
197 writer.writeTextElement(QStringLiteral("y"), QStringLiteral("0.0"));
198 writer.writeEndElement(); // Order0
199 writer.writeStartElement(QStringLiteral("sun.awt.geom.Order1"));
200 writer.writeTextElement(QStringLiteral("direction"), QStringLiteral("1"));
201 writer.writeTextElement(QStringLiteral("x0"), QStringLiteral("0.0"));
202 writer.writeTextElement(QStringLiteral("y0"), QStringLiteral("0.0"));
203 writer.writeTextElement(QStringLiteral("x1"), QStringLiteral("0.0"));
204 writer.writeTextElement(QStringLiteral("y1"), QString::number(map->tileHeight()));
205 writer.writeTextElement(QStringLiteral("xmin"), QStringLiteral("0.0"));
206 writer.writeTextElement(QStringLiteral("xmax"), QStringLiteral("0.0"));
207 writer.writeEndElement(); // Order1
208 writer.writeStartElement(QStringLiteral("sun.awt.geom.Order1"));
209 writer.writeTextElement(QStringLiteral("direction"), QStringLiteral("-1"));
210 writer.writeTextElement(QStringLiteral("x0"), QString::number(map->tileWidth()));
211 writer.writeTextElement(QStringLiteral("y0"), QStringLiteral("0.0"));
212 writer.writeTextElement(QStringLiteral("x1"), QString::number(map->tileWidth()));
213 writer.writeTextElement(QStringLiteral("y1"), QString::number(map->tileHeight()));
214 writer.writeTextElement(QStringLiteral("xmin"), QString::number(map->tileWidth()));
215 writer.writeTextElement(QStringLiteral("xmax"), QString::number(map->tileWidth()));
216 writer.writeEndElement(); // Order1
217 writer.writeEndElement(); // curves
218 writer.writeEndElement(); // cellShape
219 }
220
writeGrid(QXmlStreamWriter & writer,Tiled::Map const * map)221 static void writeGrid(QXmlStreamWriter &writer, Tiled::Map const* map)
222 {
223 writer.writeStartElement(QStringLiteral("grid"));
224 writer.writeAttribute(QStringLiteral("class"), QStringLiteral("net.rptools.maptool.model.SquareGrid"));
225 writer.writeTextElement(QStringLiteral("offsetX"), QString::number(0));
226 writer.writeTextElement(QStringLiteral("offsetY"), QString::number(0));
227 writer.writeTextElement(QStringLiteral("size"), QString::number(std::max(map->tileWidth(),map->tileHeight())));
228 writer.writeStartElement(QStringLiteral("zone")); writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../..")); writer.writeEndElement();
229 writeCellShape(writer, map);
230 writer.writeEndElement(); // grid
231 }
232
writeClass(QXmlStreamWriter & writer,QString const & name,QString const & type)233 static void writeClass(QXmlStreamWriter &writer, QString const &name, QString const& type)
234 {
235 writer.writeStartElement(name);
236 writer.writeAttribute(QStringLiteral("class"), type);
237 writer.writeEndElement();
238 }
239
writeTokenMap(QXmlStreamWriter & writer,Tiled::Map const * map)240 void RpMapPlugin::writeTokenMap(QXmlStreamWriter &writer, Tiled::Map const* map)
241 {
242 const int tileWidth = map->tileWidth();
243 const int tileHeight = map->tileHeight();
244 // const QColor backgroundColor = map->backgroundColor();
245 writer.writeStartElement(QStringLiteral("tokenMap"));
246
247 for (Layer *layer : map->layers()) {
248 if (TileLayer *tileLayer = layer->asTileLayer()) {
249 for (int y = 0; y < tileLayer->height(); ++y) {
250 for (int x = 0; x < tileLayer->width(); ++x) {
251 Cell t = tileLayer->cellAt(x, y);
252 if (t.isEmpty())
253 continue;
254
255 static constexpr uint16_t rotation[8] = { 270, 270, 270, 90, 0, 0, 180, 180 };
256 // in addition to rotation
257 static constexpr bool flip_horiz[8] = { false, false, true, false, true, false, false, true };
258 static constexpr bool flip_vert[8] = { false, true, false, false, false, false, false, false };
259
260 uint8_t rot_index = (t.flippedVertically() ? 1 : 0) | (t.flippedHorizontally() ? 2 : 0) | (t.flippedAntiDiagonally() ? 4 : 0);
261 //int tileid= t.tileId();
262 Tile const* tile = t.tile();
263 QUrl tileurl = tile->imageSource();
264 if (tileurl.isLocalFile()) {
265 QString tilepath = tileurl.toLocalFile();
266 auto it = filename2md5.find(tilepath);
267 if (it == filename2md5.end()) {
268 QFile file(tilepath);
269 if (file.open(QIODevice::ReadOnly)) {
270 QByteArray image = file.readAll();
271 QByteArray md5bin = QCryptographicHash::hash(image, QCryptographicHash::Md5);
272 QString md5string = md5bin.toHex();
273 it = filename2md5.insert(tilepath, md5string);
274 // remember the first element (tile) referencing this file
275 first_used_md5.push_back(number_of_tiles);
276 }
277 else
278 continue;
279 }
280 assert(it != filename2md5.end());
281 QString md5 = it.value();
282 writeTile(writer, x*tileWidth, y*tileHeight, QStringLiteral("token"), rotation[rot_index], md5, flip_horiz[rot_index], flip_vert[rot_index]);
283 ++number_of_tiles;
284 }
285 }
286 }
287 break; // only output first layer (for now)
288 }
289 }
290 //writeTile(writer, 400, 300, QStringLiteral("token"), 180);
291 //writeTile(writer, 400, 600, QStringLiteral("token2"), 0);
292
293 writer.writeEndElement(); // tokenMap
294 }
295
writeTokenOrderedList(QXmlStreamWriter & writer)296 void RpMapPlugin::writeTokenOrderedList(QXmlStreamWriter &writer)
297 {
298 writer.writeStartElement(QStringLiteral("tokenOrderedList"));
299 writer.writeAttribute(QStringLiteral("class"), QStringLiteral("linked-list"));
300
301 writer.writeStartElement(QStringLiteral("net.rptools.maptool.model.Token"));
302 writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../../tokenMap/entry/net.rptools.maptool.model.Token"));
303 writer.writeEndElement(); // Token
304 for (uint32_t i = 1; i < number_of_tiles; ++i) {
305 writer.writeStartElement(QStringLiteral("net.rptools.maptool.model.Token"));
306 writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../../tokenMap/entry[")
307 +QString::number(i+1)
308 +QStringLiteral("]/net.rptools.maptool.model.Token"));
309 writer.writeEndElement(); // Token
310 }
311
312 writer.writeEndElement(); // tokenOrderedList
313 }
314
writeZone2(QXmlStreamWriter & writer)315 static void writeZone2(QXmlStreamWriter &writer)
316 {
317 writer.writeStartElement(QStringLiteral("initiativeList"));
318 writer.writeEmptyElement(QStringLiteral("tokens"));
319 writer.writeTextElement(QStringLiteral("current"), QString::number(-1));
320 writer.writeTextElement(QStringLiteral("round"), QString::number(-1));
321 writer.writeStartElement(QStringLiteral("zoneId")); writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../../id")); writer.writeEndElement();
322 writer.writeTextElement(QStringLiteral("fullUpdate"), QStringLiteral("false"));
323 writer.writeTextElement(QStringLiteral("hideNPC"), QStringLiteral("false"));
324 writer.writeEndElement(); // initiativeList
325
326 writer.writeStartElement(QStringLiteral("exposedArea"));
327 writer.writeEmptyElement(QStringLiteral("curves"));
328 writer.writeEndElement(); // exposedArea
329 writer.writeTextElement(QStringLiteral("hasFog"), QStringLiteral("false"));
330
331 writer.writeStartElement(QStringLiteral("fogPaint"));
332 writer.writeAttribute(QStringLiteral("class"), QStringLiteral("net.rptools.maptool.model.drawing.DrawableColorPaint"));
333 writer.writeTextElement(QStringLiteral("color"), QString::number(-16777216));
334 writer.writeEndElement(); // fogPaint
335
336 writer.writeStartElement(QStringLiteral("topology"));
337 writer.writeStartElement(QStringLiteral("curves"));
338 writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../../exposedArea/curves"));
339 writer.writeEndElement(); // curves
340 writer.writeEndElement(); // topology
341 writer.writeStartElement(QStringLiteral("topologyTerrain"));
342 writer.writeStartElement(QStringLiteral("curves"));
343 writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../../exposedArea/curves"));
344 writer.writeEndElement(); // curves
345 writer.writeEndElement(); // topologyTerrain
346
347 writer.writeStartElement(QStringLiteral("backgroundPaint"));
348 writer.writeAttribute(QStringLiteral("class"), QStringLiteral("net.rptools.maptool.model.drawing.DrawableColorPaint"));
349 writer.writeTextElement(QStringLiteral("color"), QString::number(-16777216));
350 writer.writeEndElement(); // backgroundPaint
351
352 writer.writeStartElement(QStringLiteral("boardPosition"));
353 writer.writeTextElement(QStringLiteral("x"), QString::number(0));
354 writer.writeTextElement(QStringLiteral("y"), QString::number(0));
355 writer.writeEndElement(); // boardPosition
356
357 writer.writeTextElement(QStringLiteral("drawBoard"), QStringLiteral("true"));
358 writer.writeTextElement(QStringLiteral("boardChanged"), QStringLiteral("false"));
359 writer.writeTextElement(QStringLiteral("name"), QStringLiteral("Tiled export"));
360 writer.writeTextElement(QStringLiteral("isVisible"), QStringLiteral("true"));
361 writer.writeTextElement(QStringLiteral("visionType"), QStringLiteral("OFF"));
362 writer.writeTextElement(QStringLiteral("tokenSelection"), QStringLiteral("ALL"));
363 writer.writeTextElement(QStringLiteral("height"), QString::number(0));
364 writer.writeTextElement(QStringLiteral("width"), QString::number(0));
365 }
366
writeMap(QXmlStreamWriter & writer,Tiled::Map const * map)367 void RpMapPlugin::writeMap(QXmlStreamWriter &writer, Tiled::Map const* map)
368 {
369 writer.writeStartElement(QStringLiteral("zone"));
370 writer.writeTextElement(QStringLiteral("creationTime"), QString::number(QDateTime::currentMSecsSinceEpoch()));
371 writeGUID(writer, QStringLiteral("id"), QUuid::createUuid());
372 writeGrid(writer, map);
373 writer.writeTextElement(QStringLiteral("gridColor"), QString::number(-16777216));
374 writer.writeTextElement(QStringLiteral("imageScaleX"), QString::number(1.0));
375 writer.writeTextElement(QStringLiteral("imageScaleY"), QString::number(1.0));
376 writer.writeTextElement(QStringLiteral("tokenVisionDistance"), QString::number(1000));
377 writer.writeTextElement(QStringLiteral("unitsPerCell"), QString::number(5.0));
378 writer.writeTextElement(QStringLiteral("aStarRounding"), QStringLiteral("NONE"));
379 writer.writeTextElement(QStringLiteral("topologyMode"), QStringLiteral("COMBINED"));
380 writeClass(writer, QStringLiteral("drawables"), QStringLiteral("linked-list"));
381 writeClass(writer, QStringLiteral("gmDrawables"), QStringLiteral("linked-list"));
382 writeClass(writer, QStringLiteral("objectDrawables"), QStringLiteral("linked-list"));
383 writeClass(writer, QStringLiteral("backgroundDrawables"), QStringLiteral("linked-list"));
384 writeClass(writer, QStringLiteral("labels"), QStringLiteral("linked-hash-map"));
385 writeTokenMap(writer, map);
386 writer.writeStartElement(QStringLiteral("exposedAreaMeta"));
387 writer.writeEndElement(); // exposedAreaMeta
388 writeTokenOrderedList(writer);
389 writeZone2(writer); // some arbitrary data
390 writer.writeEndElement(); // zone
391
392 writer.writeStartElement(QStringLiteral("assetMap"));
393 for (auto i: first_used_md5) {
394 writer.writeStartElement(QStringLiteral("entry"));
395 writer.writeStartElement(QStringLiteral("net.rptools.lib.MD5Key"));
396 QString item;
397 if (i > 0)
398 item = QStringLiteral("[") + QString::number(i+1) + QStringLiteral("]");
399 writer.writeAttribute(QStringLiteral("reference"), QStringLiteral("../../../zone/tokenMap/entry")
400 +item
401 +QStringLiteral("/net.rptools.maptool.model.Token/imageAssetMap/entry/net.rptools.lib.MD5Key"));
402 writer.writeEndElement(); // net.rptools.lib.MD5Key
403 writer.writeEmptyElement(QStringLiteral("null"));
404 writer.writeEndElement(); // entry
405 }
406 writer.writeEndElement(); // assetMap
407 }
408
write(const Tiled::Map * map,const QString & fileName,Options options)409 bool RpMapPlugin::write(const Tiled::Map *map, const QString &fileName, Options options)
410 {
411 filename2md5.clear();
412 first_used_md5.clear();
413 number_of_tiles = 0;
414
415 Q_UNUSED(options)
416 KZip archive(fileName);
417 if (archive.open(QIODevice::WriteOnly))
418 {
419 {
420 QByteArray properties;
421 QXmlStreamWriter writer(&properties);
422 writer.setAutoFormatting(true);
423 writer.setAutoFormattingIndent(1);
424 writer.writeStartDocument();
425 writer.writeStartElement(QStringLiteral("map"));
426 writeEntry(writer, QStringLiteral("campaignVersion"), QStringLiteral("1.4.1"));
427 writeEntry(writer, QStringLiteral("version"), QStringLiteral("1.7.0"));
428 writer.writeEndElement();
429 writer.writeEndDocument();
430 archive.writeFile(QStringLiteral("properties.xml"), properties);
431 }
432 {
433 QByteArray content;
434 QXmlStreamWriter writer(&content);
435 writer.setAutoFormatting(true);
436 writer.setAutoFormattingIndent(1);
437 writer.writeStartDocument();
438 writer.writeStartElement(QStringLiteral("net.rptools.maptool.util.PersistenceUtil_-PersistedMap"));
439 writeMap(writer, map);
440 writer.writeEndElement(); // PersistedMap
441 writer.writeEndDocument();
442 archive.writeFile(QStringLiteral("content.xml"), content);
443 }
444 archive.close();
445 return true;
446 }
447 return false;
448 }
449
450 } // namespace RpMap
451