Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/RagTime5Document.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libmwaw
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <algorithm>
35
#include <cctype>
36
#include <iomanip>
37
#include <iostream>
38
#include <limits>
39
#include <set>
40
#include <sstream>
41
42
#include <librevenge/librevenge.h>
43
44
#include "MWAWTextListener.hxx"
45
#include "MWAWFont.hxx"
46
#include "MWAWGraphicStyle.hxx"
47
#include "MWAWGraphicShape.hxx"
48
#include "MWAWHeader.hxx"
49
#include "MWAWParagraph.hxx"
50
#include "MWAWPictBitmap.hxx"
51
#include "MWAWPictMac.hxx"
52
#include "MWAWPosition.hxx"
53
#include "MWAWPrinter.hxx"
54
#include "MWAWRSRCParser.hxx"
55
#include "MWAWStringStream.hxx"
56
57
#include "RagTime5Chart.hxx"
58
#include "RagTime5ClusterManager.hxx"
59
#include "RagTime5Formula.hxx"
60
#include "RagTime5Graph.hxx"
61
#include "RagTime5Layout.hxx"
62
#include "RagTime5Pipeline.hxx"
63
#include "RagTime5StructManager.hxx"
64
#include "RagTime5StyleManager.hxx"
65
#include "RagTime5Spreadsheet.hxx"
66
#include "RagTime5Text.hxx"
67
68
#include "RagTime5Document.hxx"
69
70
/** Internal: the structures of a RagTime5Document */
71
namespace RagTime5DocumentInternal
72
{
73
//! Internal: the helper to read doc info parse
74
struct DocInfoFieldParser final : public RagTime5StructManager::FieldParser {
75
  //! constructor
76
  explicit DocInfoFieldParser(RagTime5Document &doc)
77
2.87k
    : RagTime5StructManager::FieldParser("DocInfo")
78
2.87k
    , m_document(doc)
79
2.87k
  {
80
2.87k
  }
81
  //! destructor
82
  ~DocInfoFieldParser() final;
83
  //! parse a field
84
  bool parseField(RagTime5StructManager::Field &field, RagTime5Zone &zone, int /*n*/, libmwaw::DebugStream &f) final
85
0
  {
86
0
    if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x1f7827) {
87
0
      for (auto const &child : field.m_fieldList) {
88
0
        if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0x32040 && child.m_entry.valid()) {
89
0
          f << child;
90
91
0
          long actPos=zone.getInput()->tell();
92
0
          m_document.readDocInfoClusterData(zone, child.m_entry);
93
0
          zone.getInput()->seek(actPos, librevenge::RVNG_SEEK_SET);
94
0
          return true;
95
0
        }
96
0
        MWAW_DEBUG_MSG(("RagTime5DocumentInternal::DocInfoFieldParser::parseField: find some unknown mainData block\n"));
97
0
        f << "##mainData=" << child << ",";
98
0
      }
99
0
    }
100
0
    else
101
0
      f << field;
102
0
    return true;
103
0
  }
104
105
protected:
106
  //! the main parser
107
  RagTime5Document &m_document;
108
};
109
110
DocInfoFieldParser::~DocInfoFieldParser()
111
2.87k
{
112
2.87k
}
113
114
//! Internal: the helper to read index + unicode string for a RagTime5Document
115
struct IndexUnicodeParser final : public RagTime5StructManager::DataParser {
116
  //! constructor
117
  IndexUnicodeParser(RagTime5Document &, bool readIndex, std::string const &zoneName)
118
174k
    : RagTime5StructManager::DataParser(zoneName)
119
174k
    , m_readIndex(readIndex)
120
174k
    , m_idToStringMap()
121
174k
    , m_indicesMap()
122
174k
  {
123
174k
  }
124
  //! destructor
125
  ~IndexUnicodeParser() final;
126
  //! try to parse a data
127
  bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int n, libmwaw::DebugStream &f) final
128
351k
  {
129
351k
    long pos=input->tell();
130
351k
    int id=n;
131
351k
    if (m_readIndex) {
132
4.65k
      if (endPos-pos<4) {
133
6
        MWAW_DEBUG_MSG(("RagTime5DocumentInternal::IndexUnicodeParser::parse: bad data size\n"));
134
6
        return false;
135
6
      }
136
4.64k
      id=static_cast<int>(input->readULong(4));
137
4.64k
      f << "id=" << id << ",";
138
4.64k
    }
139
346k
    else if (!m_indicesMap.empty()) {
140
274k
      auto it = m_indicesMap.find(n);
141
274k
      if (it != m_indicesMap.end())
142
270k
        id=it->second;
143
3.65k
      else
144
3.65k
        id=0;
145
274k
    }
146
351k
    librevenge::RVNGString str("");
147
351k
    if (endPos==input->tell())
148
260
      ;
149
351k
    else if (!RagTime5StructManager::readUnicodeString(input, endPos, str))
150
1.23k
      f << "###";
151
351k
    f << "\"" << str.cstr() << "\",";
152
351k
    m_idToStringMap[id]=str;
153
351k
    return true;
154
351k
  }
155
156
  //! a flag to know if we need to read the index
157
  bool m_readIndex;
158
  //! the data
159
  std::map<int, librevenge::RVNGString> m_idToStringMap;
160
  //! the map n to index if given
161
  std::map<int,int> m_indicesMap;
162
};
163
164
IndexUnicodeParser::~IndexUnicodeParser()
165
174k
{
166
174k
}
167
168
//! Internal: the helper to read a clustList
169
struct ClustListParser final : public RagTime5StructManager::DataParser {
170
  //! constructor
171
  ClustListParser(RagTime5ClusterManager &clusterManager, int fieldSize, std::string const &zoneName)
172
5.33k
    : RagTime5StructManager::DataParser(zoneName)
173
5.33k
    , m_fieldSize(fieldSize)
174
5.33k
    , m_linkList()
175
5.33k
    , m_idToNameMap()
176
5.33k
    , m_clusterManager(clusterManager)
177
5.33k
  {
178
5.33k
    if (m_fieldSize<4) {
179
0
      MWAW_DEBUG_MSG(("RagTime5DocumentInternal::ClustListParser: bad field size\n"));
180
0
      m_fieldSize=0;
181
0
    }
182
5.33k
  }
183
  //! destructor
184
  ~ClustListParser() final;
185
  //! returns the not null list dataId list
186
  std::vector<int> getIdList() const
187
0
  {
188
0
    std::vector<int> res;
189
0
    for (auto const &lnk : m_linkList) {
190
0
      if (lnk.m_dataId>0)
191
0
        res.push_back(lnk.m_dataId);
192
0
    }
193
0
    return res;
194
0
  }
195
  //! return the cluster name
196
  std::string getClusterDebugName(int id) const
197
5.05k
  {
198
5.05k
    return m_clusterManager.getClusterDebugName(id);
199
5.05k
  }
200
201
  //! try to parse a data
202
  bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int n, libmwaw::DebugStream &f) final
203
6.12k
  {
204
6.12k
    long pos=input->tell();
205
6.12k
    if (m_idToNameMap.find(n)!=m_idToNameMap.end())
206
0
      f << m_idToNameMap.find(n)->second.cstr() << ",";
207
6.12k
    if (endPos-pos!=m_fieldSize) {
208
1
      MWAW_DEBUG_MSG(("RagTime5DocumentInternal::ClustListParser::parse: bad data size\n"));
209
1
      return false;
210
1
    }
211
6.12k
    std::vector<int> listIds;
212
6.12k
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
213
1.06k
      MWAW_DEBUG_MSG(("RagTime5DocumentInternal::ClustListParser::parse: can not read an cluster id\n"));
214
1.06k
      f << "##clusterIds,";
215
1.06k
      return false;
216
1.06k
    }
217
5.06k
    RagTime5StructManager::ZoneLink link;
218
5.06k
    link.m_dataId=listIds[0];
219
5.06k
    if (listIds[0])
220
      // a e,2003,200b, ... cluster
221
5.05k
      f << getClusterDebugName(listIds[0]) << ",";
222
5.06k
    if (m_fieldSize>=10) {
223
1.80k
      link.m_subZoneId[0]=long(input->readULong(4));
224
1.80k
      link.m_subZoneId[1]=long(input->readLong(2));
225
1.80k
    }
226
5.06k
    f << link;
227
5.06k
    m_linkList.push_back(link);
228
5.06k
    return true;
229
6.12k
  }
230
231
  //! the field size
232
  int m_fieldSize;
233
  //! the list of read cluster
234
  std::vector<RagTime5StructManager::ZoneLink> m_linkList;
235
  //! the name
236
  std::map<int, librevenge::RVNGString> m_idToNameMap;
237
private:
238
  //! the main zone manager
239
  RagTime5ClusterManager &m_clusterManager;
240
  //! copy constructor, not implemented
241
  ClustListParser(ClustListParser &orig);
242
  //! copy operator, not ximplemented
243
  ClustListParser &operator=(ClustListParser &orig);
244
};
245
246
ClustListParser::~ClustListParser()
247
5.33k
{
248
5.33k
}
249
250
////////////////////////////////////////
251
//! Internal: the state of a RagTime5Document
252
struct State {
253
  //! constructor
254
  State()
255
159k
    : m_version(5)
256
159k
    , m_zonesEntry()
257
159k
    , m_zonesList()
258
159k
    , m_zoneIdToTypeMap()
259
159k
    , m_zoneInfo()
260
159k
    , m_mainClusterId(0)
261
159k
    , m_mainTypeId(0)
262
159k
    , m_buttonFormulaLink()
263
159k
    , m_dataIdZoneMap()
264
159k
    , m_pageZonesIdMap()
265
159k
    , m_sendZoneSet()
266
159k
    , m_hasLayout(false)
267
159k
    , m_numPages(0)
268
159k
    , m_headerHeight(0)
269
159k
    , m_footerHeight(0)
270
159k
  {
271
159k
  }
272
273
  //! the document version
274
  int m_version;
275
  //! the main zone entry
276
  MWAWEntry m_zonesEntry;
277
  //! the zone list
278
  std::vector<std::shared_ptr<RagTime5Zone> > m_zonesList;
279
  //! a map id to type string
280
  std::map<int, std::string> m_zoneIdToTypeMap;
281
  //! the zone info zone (ie. the first zone)
282
  std::shared_ptr<RagTime5Zone> m_zoneInfo;
283
  //! the main cluster id
284
  int m_mainClusterId;
285
  //! the main type id
286
  int m_mainTypeId;
287
  //! the buttons formula link
288
  RagTime5ClusterManager::Link m_buttonFormulaLink;
289
  //! a map: data id->entry (datafork)
290
  std::map<int, std::shared_ptr<RagTime5Zone> > m_dataIdZoneMap;
291
  //! a map: page->main zone id
292
  std::map<int, std::vector<int> > m_pageZonesIdMap;
293
  //! a set used to avoid looping when sending zone
294
  std::set<int> m_sendZoneSet;
295
  //! a flag to know if the file has some layout
296
  bool m_hasLayout;
297
  int m_numPages /** the number of page of the final document */;
298
299
  int m_headerHeight /** the header height if known */,
300
      m_footerHeight /** the footer height if known */;
301
};
302
303
}
304
305
306
////////////////////////////////////////////////////////////
307
// constructor/destructor, ...
308
////////////////////////////////////////////////////////////
309
RagTime5Document::RagTime5Document(MWAWParser &parser)
310
66.2k
  : m_parser(&parser)
311
66.2k
  , m_parserState(parser.getParserState())
312
66.2k
  , m_state()
313
66.2k
  , m_chartParser()
314
66.2k
  , m_formulaParser()
315
66.2k
  , m_graphParser()
316
66.2k
  , m_layoutParser()
317
66.2k
  , m_pipelineParser()
318
66.2k
  , m_spreadsheetParser()
319
66.2k
  , m_textParser()
320
66.2k
  , m_clusterManager()
321
66.2k
  , m_structManager()
322
66.2k
  , m_styleManager()
323
324
66.2k
  , m_newPage(nullptr)
325
66.2k
  , m_sendFootnote(nullptr)
326
66.2k
{
327
66.2k
  init();
328
66.2k
}
329
330
RagTime5Document::~RagTime5Document()
331
66.2k
{
332
66.2k
}
333
334
void RagTime5Document::init()
335
66.2k
{
336
66.2k
  m_structManager.reset(new RagTime5StructManager(*this));
337
66.2k
  m_clusterManager.reset(new RagTime5ClusterManager(*this));
338
66.2k
  m_styleManager.reset(new RagTime5StyleManager(*this));
339
340
66.2k
  m_chartParser.reset(new RagTime5Chart(*this));
341
66.2k
  m_formulaParser.reset(new RagTime5Formula(*this));
342
66.2k
  m_graphParser.reset(new RagTime5Graph(*this));
343
66.2k
  m_layoutParser.reset(new RagTime5Layout(*this));
344
66.2k
  m_pipelineParser.reset(new RagTime5Pipeline(*this));
345
66.2k
  m_spreadsheetParser.reset(new RagTime5Spreadsheet(*this));
346
66.2k
  m_textParser.reset(new RagTime5Text(*this));
347
348
66.2k
  m_state.reset(new RagTime5DocumentInternal::State);
349
66.2k
}
350
351
librevenge::RVNGPropertyList RagTime5Document::getDocumentMetaData() const
352
24.7k
{
353
24.7k
  return librevenge::RVNGPropertyList();
354
24.7k
}
355
356
int RagTime5Document::version() const
357
62.9k
{
358
62.9k
  return m_state->m_version;
359
62.9k
}
360
361
void RagTime5Document::setVersion(int vers)
362
90.8k
{
363
90.8k
  m_state->m_version=vers;
364
90.8k
}
365
366
int RagTime5Document::numPages() const
367
49.4k
{
368
49.4k
  if (m_state->m_numPages<=0) {
369
24.7k
    if (m_parserState->m_kind==MWAWDocument::MWAW_K_SPREADSHEET)
370
0
      m_state->m_numPages=1;
371
24.7k
    else {
372
24.7k
      int nPages=m_layoutParser->numPages();
373
24.7k
      if (nPages<=0)
374
15.0k
        nPages=1;
375
9.61k
      else
376
9.61k
        m_state->m_hasLayout=true;
377
24.7k
      m_state->m_numPages=nPages;
378
24.7k
    }
379
24.7k
  }
380
49.4k
  return m_state->m_numPages;
381
49.4k
}
382
383
void RagTime5Document::updatePageSpanList(std::vector<MWAWPageSpan> &spanList)
384
24.7k
{
385
24.7k
  MWAWPageSpan ps(m_parser->getPageSpan());
386
24.7k
  ps.setPageSpan(numPages());
387
24.7k
  spanList.push_back(ps);
388
24.7k
}
389
390
bool RagTime5Document::sendButtonZoneAsText(MWAWListenerPtr listener, int buttonId)
391
82
{
392
82
  return m_graphParser->sendButtonZoneAsText(listener, buttonId);
393
82
}
394
395
std::shared_ptr<RagTime5ClusterManager> RagTime5Document::getClusterManager()
396
94.3k
{
397
94.3k
  return m_clusterManager;
398
94.3k
}
399
400
std::shared_ptr<RagTime5StructManager> RagTime5Document::getStructManager()
401
529k
{
402
529k
  return m_structManager;
403
529k
}
404
405
std::shared_ptr<RagTime5StyleManager> RagTime5Document::getStyleManager()
406
264k
{
407
264k
  return m_styleManager;
408
264k
}
409
410
std::shared_ptr<RagTime5Formula> RagTime5Document::getFormulaParser()
411
771k
{
412
771k
  return m_formulaParser;
413
771k
}
414
415
std::shared_ptr<RagTime5Graph> RagTime5Document::getGraphParser()
416
0
{
417
0
  return m_graphParser;
418
0
}
419
420
std::shared_ptr<RagTime5Spreadsheet> RagTime5Document::getSpreadsheetParser()
421
2.54k
{
422
2.54k
  return m_spreadsheetParser;
423
2.54k
}
424
425
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readButtonCluster(RagTime5Zone &zone, int zoneType)
426
1.21k
{
427
1.21k
  return m_graphParser->readButtonCluster(zone, zoneType);
428
1.21k
}
429
430
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readChartCluster(RagTime5Zone &zone, int zoneType)
431
976
{
432
976
  return m_chartParser->readChartCluster(zone, zoneType);
433
976
}
434
435
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readGraphicCluster(RagTime5Zone &zone, int zoneType)
436
17.7k
{
437
17.7k
  return m_graphParser->readGraphicCluster(zone, zoneType);
438
17.7k
}
439
440
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readLayoutCluster(RagTime5Zone &zone, int zoneType)
441
24.5k
{
442
24.5k
  return m_layoutParser->readLayoutCluster(zone, zoneType);
443
24.5k
}
444
445
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readPipelineCluster(RagTime5Zone &zone, int zoneType)
446
18.1k
{
447
18.1k
  return m_pipelineParser->readPipelineCluster(zone, zoneType);
448
18.1k
}
449
450
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readPictureCluster(RagTime5Zone &zone, int zoneType)
451
3.74k
{
452
3.74k
  return m_graphParser->readPictureCluster(zone, zoneType);
453
3.74k
}
454
455
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readSpreadsheetCluster(RagTime5Zone &zone, int zoneType)
456
5.93k
{
457
5.93k
  return m_spreadsheetParser->readSpreadsheetCluster(zone, zoneType);
458
5.93k
}
459
460
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Document::readTextCluster(RagTime5Zone &zone, int zoneType)
461
16.2k
{
462
16.2k
  return m_textParser->readTextCluster(zone, zoneType);
463
16.2k
}
464
465
std::shared_ptr<RagTime5Zone> RagTime5Document::getDataZone(int dataId) const
466
1.26M
{
467
1.26M
  if (m_state->m_dataIdZoneMap.find(dataId)==m_state->m_dataIdZoneMap.end())
468
176k
    return std::shared_ptr<RagTime5Zone>();
469
1.09M
  return m_state->m_dataIdZoneMap.find(dataId)->second;
470
1.26M
}
471
472
RagTime5ClusterManager::Cluster::Type RagTime5Document::getClusterType(int zId) const
473
28.2k
{
474
28.2k
  return m_clusterManager->getClusterType(zId);
475
28.2k
}
476
477
RagTime5ClusterManager::Cluster::Type RagTime5Document::getPipelineContainerType(int pipelineId) const
478
5.05k
{
479
5.05k
  return m_pipelineParser->getContainerType(pipelineId);
480
5.05k
}
481
482
////////////////////////////////////////////////////////////
483
// new page
484
////////////////////////////////////////////////////////////
485
void RagTime5Document::newPage(int number, bool softBreak)
486
0
{
487
0
  if (!m_parser || !m_newPage) {
488
0
    MWAW_DEBUG_MSG(("RagTime5Document::newPage: can not find newPage callback\n"));
489
0
    return;
490
0
  }
491
0
  (m_parser->*m_newPage)(number, softBreak);
492
0
}
493
494
////////////////////////////////////////////////////////////
495
//
496
// Intermediate level
497
//
498
////////////////////////////////////////////////////////////
499
500
bool RagTime5Document::createZones()
501
27.5k
{
502
27.5k
  int const vers=version();
503
27.5k
  if (vers<5) {
504
0
    MWAW_DEBUG_MSG(("RagTime5Document::createZones: must not be called for v%d document\n", vers));
505
0
    return false;
506
0
  }
507
508
27.5k
  if (m_state->m_zonesList.empty()) {
509
27.5k
    if (!findZones(m_state->m_zonesEntry))
510
171
      return false;
511
27.3k
    ascii().addPos(m_state->m_zonesEntry.end());
512
27.3k
    ascii().addNote("FileHeader-End");
513
27.3k
  }
514
515
27.3k
  if (m_state->m_zonesList.size()<20) {
516
    // even an empty file seems to have almost ~80 zones, so...
517
1.14k
    MWAW_DEBUG_MSG(("RagTime5Document::createZones: the zone list seems too short\n"));
518
1.14k
    return false;
519
1.14k
  }
520
  // we need to find the string's zones and update the map zoneId to string data
521
26.2k
  m_state->m_zoneInfo=m_state->m_zonesList[0];
522
26.2k
  if (!findZonesKind())
523
0
    return false;
524
  // now, we can update all the zones: kinds, input, ...
525
2.11M
  for (size_t i=1; i<m_state->m_zonesList.size(); ++i)
526
2.09M
    updateZone(m_state->m_zonesList[i]);
527
528
26.2k
  if (!useMainZoneInfoData()) return false;
529
530
  // now, parse the formula in spreadsheet and in button
531
24.7k
  m_spreadsheetParser->parseSpreadsheetFormulas();
532
24.7k
  if (!m_state->m_buttonFormulaLink.empty())
533
2.80k
    m_formulaParser->readFormulaClusters(m_state->m_buttonFormulaLink, -1);
534
535
  // check for unread clusters
536
2.05M
  for (auto const &zone : m_state->m_zonesList) {
537
2.05M
    if (!zone || zone->m_isParsed || zone->getKindLastPart(zone->m_kinds[1].empty())!="Cluster")
538
1.84M
      continue;
539
206k
    if (zone->m_entry.valid()) {
540
106k
      MWAW_DEBUG_MSG(("RagTime5Document::createZones: find unparsed cluster zone %d\n", zone->m_ids[0]));
541
106k
    }
542
206k
    readClusterZone(*zone);
543
206k
  }
544
  // now read the screen rep list zone: CHECKME: can we remove this check, now ?
545
2.05M
  for (auto const &zone : m_state->m_zonesList) {
546
2.05M
    if (!zone || zone->m_isParsed || (!zone->m_entry.valid()&&zone->m_variableD[0]!=1) || zone->getKindLastPart(zone->m_kinds[1].empty())!="ScreenRepList")
547
2.03M
      continue;
548
13.2k
    m_graphParser->readPictureList(*zone);
549
13.2k
  }
550
551
24.7k
  return true;
552
26.2k
}
553
554
bool RagTime5Document::findZonesKind()
555
59.1k
{
556
59.1k
  libmwaw::DebugStream f;
557
59.1k
  if (!m_state->m_zoneIdToTypeMap.empty())
558
0
    return true;
559
4.68M
  for (size_t i=1; i<m_state->m_zonesList.size(); ++i) {
560
4.62M
    if (!m_state->m_zonesList[i])
561
0
      continue;
562
4.62M
    auto &zone=*m_state->m_zonesList[i];
563
    // id=0 correspond to the file header already read, so ignored it
564
4.62M
    if (zone.m_ids[0]==0 && zone.m_level==1) {
565
61.5k
      zone.m_isParsed=true;
566
61.5k
      continue;
567
61.5k
    }
568
569
4.56M
    std::string what("");
570
4.56M
    if (zone.m_idsFlag[1]!=0 || (zone.m_ids[1]!=23 && zone.m_ids[1]!=24) || zone.m_ids[2]!=21)
571
3.49M
      continue;
572
    // normally a string, update the zone input(always uncompressed) and read the zone
573
1.06M
    if (!updateZoneInput(zone) || !readString(zone, what) || what.empty())
574
128k
      continue;
575
941k
    if (m_state->m_zoneIdToTypeMap.find(zone.m_ids[0])!=m_state->m_zoneIdToTypeMap.end()) {
576
28.1k
      MWAW_DEBUG_MSG(("RagTime5Document::findZonesKind: a type with id=%d already exists\n", zone.m_ids[0]));
577
28.1k
    }
578
912k
    else {
579
912k
      m_state->m_zoneIdToTypeMap[zone.m_ids[0]]=what;
580
912k
      f.str("");
581
912k
      f << what << ",";
582
912k
      ascii().addPos(zone.m_defPosition);
583
912k
      ascii().addNote(f.str().c_str());
584
912k
    }
585
941k
  }
586
59.1k
  return true;
587
59.1k
}
588
589
bool RagTime5Document::parseMainZoneInfoData(RagTime5Zone const &zoneInfo)
590
57.6k
{
591
57.6k
  if (zoneInfo.m_isParsed)
592
0
    return true;
593
594
57.6k
  zoneInfo.m_isParsed=true;
595
326k
  for (auto const &it : zoneInfo.m_childIdToZoneMap) {
596
326k
    std::shared_ptr<RagTime5Zone> zone=it.second;
597
326k
    if (!zone) continue;
598
326k
    zone->m_isParsed=true;
599
326k
    switch (it.first) {
600
53.7k
    case 3: // alway with gd=[1,_]
601
53.7k
      if (zone->m_variableD[0]==1 && zone->m_variableD[1]) {
602
490
        MWAW_DEBUG_MSG(("RagTime5Document::parseMainZoneInfoData: find a zone 3\n"));
603
490
        ascii().addPos(zone->m_defPosition);
604
490
        ascii().addNote("###");
605
490
      }
606
53.7k
      break;
607
53.5k
    case 4: // list of zones limits, safe to ignore
608
107k
    case 5: // file limits, safe to ignore
609
107k
      break;
610
54.2k
    case 6: // alway with gd=[_,_]
611
54.2k
      if (zone->m_variableD[1]) {
612
126
        MWAW_DEBUG_MSG(("RagTime5Document::parseMainZoneInfoData: find a zone 6\n"));
613
126
        ascii().addPos(zone->m_defPosition);
614
126
        ascii().addNote("###");
615
126
      }
616
54.2k
      break;
617
54.3k
    case 10: { // the type zone
618
54.3k
      if (zone->m_variableD[0]!=1) {
619
508
        MWAW_DEBUG_MSG(("RagTime5Document::parseMainZoneInfoData: the type zone seems bads\n"));
620
508
        break;
621
508
      }
622
53.8k
      m_state->m_mainTypeId=zone->m_variableD[1];
623
53.8k
      break;
624
54.3k
    }
625
54.1k
    case 11:
626
54.1k
      if (zone->m_variableD[0]!=1) {
627
431
        MWAW_DEBUG_MSG(("RagTime5Document::parseMainZoneInfoData: the main cluster zone seems bads\n"));
628
431
        break;
629
431
      }
630
53.7k
      m_state->m_mainClusterId=zone->m_variableD[1];
631
53.7k
      break;
632
2.31k
    default:
633
2.31k
      MWAW_DEBUG_MSG(("RagTime5Document::parseMainZoneInfoData: find unknown main zone %d\n", it.first));
634
2.31k
      ascii().addPos(zone->m_defPosition);
635
2.31k
      ascii().addNote("###");
636
2.31k
      break;
637
326k
    }
638
326k
  }
639
57.6k
  if (!m_state->m_mainClusterId) {
640
3.90k
    MWAW_DEBUG_MSG(("RagTime5Document::parseMainZoneInfoData: can not find the cluster id try 13\n"));
641
3.90k
    m_state->m_mainClusterId=13;
642
3.90k
  }
643
57.6k
  return true;
644
57.6k
}
645
646
bool RagTime5Document::useMainZoneInfoData()
647
26.1k
{
648
26.1k
  if (!m_state->m_zoneInfo || m_state->m_zoneInfo->m_ids[0]!=1) {
649
1.42k
    MWAW_DEBUG_MSG(("RagTime5Document::useMainZoneInfoData: can not find the zone information zone, impossible to continue\n"));
650
1.42k
    return false;
651
1.42k
  }
652
24.7k
  parseMainZoneInfoData(*m_state->m_zoneInfo);
653
654
  // the type id
655
24.7k
  if (m_state->m_mainTypeId) {
656
24.3k
    auto dZone=getDataZone(m_state->m_mainTypeId);
657
24.3k
    if (!dZone || !dZone->m_entry.valid()) {
658
16.2k
      MWAW_DEBUG_MSG(("RagTime5Document::useMainZoneInfoData: can not find the type zone\n"));
659
16.2k
    }
660
8.12k
    else {
661
8.12k
      if (dZone->getKindLastPart()!="ItemData" || !m_structManager->readTypeDefinitions(*dZone)) {
662
2.15k
        MWAW_DEBUG_MSG(("RagTime5Document::useMainZoneInfoData: unexpected list of block type\n"));
663
2.15k
      }
664
8.12k
    }
665
24.3k
  }
666
  // the main cluster
667
24.7k
  auto dZone=getDataZone(m_state->m_mainClusterId);
668
24.7k
  if (!dZone) {
669
1.05k
    MWAW_DEBUG_MSG(("RagTime5Document::useMainZoneInfoData: can not find the main cluster zone\n"));
670
1.05k
    return true;
671
1.05k
  }
672
23.6k
  dZone->m_extra+="main,";
673
23.6k
  if (dZone->getKindLastPart(dZone->m_kinds[1].empty())!="Cluster" || !readClusterZone(*dZone, 0)) {
674
9.25k
    MWAW_DEBUG_MSG(("RagTime5Document::useMainZoneInfoData: unexpected main cluster zone type\n"));
675
9.25k
  }
676
23.6k
  return true;
677
24.7k
}
678
679
bool RagTime5Document::readZoneData(RagTime5Zone &zone)
680
0
{
681
0
  if (!zone.m_entry.valid()) {
682
0
    MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: can not find the entry\n"));
683
0
    return false;
684
0
  }
685
0
  libmwaw::DebugStream f;
686
0
  int usedId=zone.m_kinds[1].empty() ? 0 : 1;
687
0
  std::string actType=zone.getKindLastPart(usedId==0);
688
689
0
  std::string kind=zone.getKindLastPart();
690
  // the "RagTime" string
691
0
  if (kind=="CodeName") {
692
0
    std::string what;
693
0
    if (zone.m_kinds[1]!="BESoftware:7BitASCII:Type" || !readString(zone, what)) {
694
0
      MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: can not read codename for zone %d\n", zone.m_ids[0]));
695
0
      zone.m_isParsed=true;
696
0
      f << "Entries(CodeName)[" << zone << "]:###";
697
0
      libmwaw::DebugFile &ascFile=zone.ascii();
698
0
      ascFile.addPos(zone.m_entry.begin());
699
0
      ascFile.addNote(f.str().c_str());
700
0
    }
701
0
    for (auto const &it : zone.m_childIdToZoneMap) {
702
0
      std::shared_ptr<RagTime5Zone> child=it.second;
703
0
      if (!child || child->m_isParsed) continue;
704
0
      if (child->getKindLastPart()=="DocuVersion" && readDocumentVersion(*child))
705
0
        continue;
706
0
      if (child->getKindLastPart()=="7BitASCII") {
707
0
        child->m_isParsed=true;
708
0
        ascii().addPos(child->m_defPosition);
709
0
        ascii().addNote("codeName[type]");
710
0
        continue;
711
0
      }
712
0
      MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find unknown child for codename for zone %d\n", zone.m_ids[0]));
713
0
      ascii().addPos(child->m_defPosition);
714
0
      ascii().addNote("###unkCodeName");
715
0
    }
716
0
    return true;
717
0
  }
718
  //
719
  // first test for picture data
720
  //
721
722
  // checkme: find how we can retrieve the next data without parsing unparsed data
723
0
  if (kind=="ScreenRepMatchData" || kind=="ScreenRepMatchDataColor") {
724
0
    MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find unexpected %s for zone %d\n", kind.c_str(), zone.m_ids[0]));
725
0
    return m_graphParser->readPictureMatch(zone, kind=="ScreenRepMatchDataColor");
726
0
  }
727
0
  if (kind=="DocuVersion") {
728
0
    MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find unexpected docuVersion\n"));
729
0
    return readDocumentVersion(zone);
730
0
  }
731
0
  if (kind=="Thumbnail")
732
0
    return m_graphParser->readPictureData(zone);
733
0
  if (m_graphParser->readPictureData(zone)) {
734
0
    MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find some unparsed picture %d\n", zone.m_ids[0]));
735
0
    ascii().addPos(zone.m_defPosition);
736
0
    ascii().addNote("###unparsed");
737
0
    return true;
738
0
  }
739
0
  if (kind=="ScriptComment" || kind=="ScriptName") {
740
0
    MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find unexpected %s\n", kind.c_str()));
741
0
    return readScriptComment(zone);
742
0
  }
743
0
  std::string name("");
744
0
  if (kind=="OSAScript" || kind=="TCubics") {
745
0
    MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find unexpected %s\n", kind.c_str()));
746
0
    name=kind;
747
0
  }
748
0
  else if (kind=="ItemData" || kind=="Unicode") {
749
0
    actType=zone.getKindLastPart(zone.m_kinds[1].empty());
750
0
    if (actType=="Unicode" || kind=="Unicode") {
751
      // hilo/lohi is not always set, so this can cause problem....
752
0
      if (readUnicodeString(zone))
753
0
        return true;
754
0
      MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: can not read a unicode zone %d\n", zone.m_ids[0]));
755
0
      f << "Entries(StringUnicode)[" << zone << "]:###";
756
0
      zone.m_isParsed=true;
757
0
      libmwaw::DebugFile &ascFile=zone.ascii();
758
0
      ascFile.addPos(zone.m_entry.begin());
759
0
      ascFile.addNote(f.str().c_str());
760
0
      return true;
761
0
    }
762
0
    if (zone.m_entry.length()==164 && zone.m_level==1)
763
0
      name="ZoneUnkn0";
764
0
    else {
765
0
      name="ItemDta";
766
      // checkme: often Data22 is not parsed, but there can be others
767
0
      MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find a unparsed %s zone %d\n", zone.m_level==1 ? "data" : "main", zone.m_ids[0]));
768
0
    }
769
0
  }
770
0
  else {
771
0
    MWAW_DEBUG_MSG(("RagTime5Document::readZoneData: find a unknown type for zone=%d\n", zone.m_ids[0]));
772
0
    name="UnknownZone";
773
0
  }
774
0
  libmwaw::DebugFile &ascFile=zone.ascii();
775
0
  f << "Entries(" << name << "):" << zone;
776
0
  zone.m_isParsed=true;
777
0
  ascFile.addPos(zone.m_entry.begin());
778
0
  ascFile.addNote(f.str().c_str());
779
0
  ascFile.addPos(zone.m_entry.end());
780
0
  ascFile.addNote("_");
781
0
  return true;
782
0
}
783
784
////////////////////////////////////////////////////////////
785
// parse the different zones
786
////////////////////////////////////////////////////////////
787
bool RagTime5Document::readString(RagTime5Zone &zone, std::string &text)
788
1.06M
{
789
1.06M
  if (!zone.m_entry.valid()) return false;
790
1.06M
  MWAWInputStreamPtr input=zone.getInput();
791
1.06M
  libmwaw::DebugFile &ascFile=zone.ascii();
792
1.06M
  libmwaw::DebugStream f;
793
1.06M
  f << "Entries(StringZone)[" << zone << "]:";
794
1.06M
  input->seek(zone.m_entry.begin(), librevenge::RVNG_SEEK_SET);
795
1.06M
  text="";
796
23.3M
  for (long i=0; i<zone.m_entry.length(); ++i) {
797
23.3M
    auto c=char(input->readULong(1));
798
23.3M
    if (c==0 && i+1==zone.m_entry.length()) break;
799
22.4M
    if (c<0x1f)
800
127k
      return false;
801
22.2M
    text+=c;
802
22.2M
  }
803
941k
  f << "\"" << text << "\",";
804
941k
  if (input->tell()!=zone.m_entry.end()) {
805
75
    MWAW_DEBUG_MSG(("RagTime5Document::readString: find extra data\n"));
806
75
    f << "###";
807
75
    ascFile.addDelimiter(input->tell(),'|');
808
75
  }
809
941k
  zone.m_isParsed=true;
810
941k
  ascFile.addPos(zone.m_entry.begin());
811
941k
  ascFile.addNote(f.str().c_str());
812
941k
  ascFile.addPos(zone.m_entry.end());
813
941k
  ascFile.addNote("_");
814
941k
  return true;
815
1.06M
}
816
817
bool RagTime5Document::readUnicodeString(RagTime5Zone &zone, std::string const &what)
818
0
{
819
0
  if (zone.m_entry.length()==0) return true;
820
0
  MWAWInputStreamPtr input=zone.getInput();
821
0
  libmwaw::DebugFile &ascFile=zone.ascii();
822
0
  libmwaw::DebugStream f;
823
0
  if (what.empty())
824
0
    f << "Entries(StringUnicode)[" << zone << "]:";
825
0
  else
826
0
    f << "Entries(" << what << ")[" << zone << "]:";
827
0
  input->setReadInverted(!zone.m_hiLoEndian);
828
0
  input->seek(zone.m_entry.begin(), librevenge::RVNG_SEEK_SET);
829
0
  librevenge::RVNGString string;
830
0
  if (!m_structManager->readUnicodeString(input, zone.m_entry.end(), string))
831
0
    f << "###";
832
0
  else
833
0
    f << string.cstr();
834
0
  zone.m_isParsed=true;
835
0
  ascFile.addPos(zone.m_entry.begin());
836
0
  ascFile.addNote(f.str().c_str());
837
0
  ascFile.addPos(zone.m_entry.end());
838
0
  ascFile.addNote("_");
839
0
  input->setReadInverted(false);
840
0
  return true;
841
0
}
842
843
bool RagTime5Document::readUnicodeStringList(RagTime5ClusterManager::NameLink const &nameLink, std::map<int, librevenge::RVNGString> &idToStringMap)
844
159k
{
845
159k
  RagTime5DocumentInternal::IndexUnicodeParser dataParser(*this, false, "UnicodeNames");
846
159k
  std::vector<long> posToNames[2];
847
477k
  for (int i=0; i<2; ++i) {
848
318k
    if (!nameLink.m_posToNames[i].empty())
849
245k
      posToNames[i]=nameLink.m_posToNames[i];
850
72.5k
    else if (!nameLink.m_posToNamesLinks[i].empty())
851
1
      readLongList(nameLink.m_posToNamesLinks[i], posToNames[i]);
852
318k
  }
853
159k
  long numPosToNames=long(posToNames[1].size());
854
382k
  for (auto const &id : posToNames[0]) {
855
382k
    if (id>=0 && id<numPosToNames)
856
376k
      dataParser.m_indicesMap[int(posToNames[1][size_t(id)])]=int(id);
857
382k
  }
858
159k
  RagTime5ClusterManager::Link link;
859
159k
  link.m_ids=nameLink.m_ids;
860
159k
  link.m_longList=nameLink.m_decalList;
861
159k
  if (!readListZone(link, dataParser))
862
41.7k
    return false;
863
117k
  idToStringMap=dataParser.m_idToStringMap;
864
117k
  return true;
865
159k
}
866
867
bool RagTime5Document::readLongListWithSize(int dataId, int fSz, std::vector<long> &listPosition, std::string const &zoneName)
868
13.5k
{
869
13.5k
  listPosition.clear();
870
13.5k
  if (!dataId || fSz<=0 || fSz>4)
871
0
    return false;
872
873
13.5k
  auto zone=getDataZone(dataId);
874
13.5k
  if (!zone || !zone->m_entry.valid() || (zone->m_entry.length()%fSz) ||
875
9.62k
      zone->getKindLastPart(zone->m_kinds[1].empty())!="ItemData") {
876
9.62k
    MWAW_DEBUG_MSG(("RagTime5Document::readLongListWithSize: the zone %d seems bad\n", dataId));
877
9.62k
    return false;
878
9.62k
  }
879
3.95k
  MWAWEntry entry=zone->m_entry;
880
3.95k
  MWAWInputStreamPtr input=zone->getInput();
881
3.95k
  input->setReadInverted(!zone->m_hiLoEndian);
882
3.95k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
883
884
3.95k
  zone->m_isParsed=true;
885
3.95k
  libmwaw::DebugStream f;
886
887
3.95k
  if (!zoneName.empty()) {
888
3.95k
    std::string zName(zoneName);
889
3.95k
    if (zName[0]>='a'&&zName[0]<='z')
890
1.23k
      zName[0]=char(zName[0]+'A'-'a');
891
3.95k
    f << "Entries(" << zName << ")[" << *zone << "]:";
892
3.95k
  }
893
0
  else
894
0
    f << "Entries(ListLong" << fSz << ")[" << *zone << "]:";
895
3.95k
  auto N=int(entry.length()/fSz);
896
137k
  for (int i=0; i<N; ++i) {
897
133k
    long ptr=input->readLong(fSz);
898
133k
    listPosition.push_back(ptr);
899
133k
    if (ptr==-2147483648) // 80000000
900
0
      f << "inf,";
901
133k
    else if (ptr)
902
114k
      f << ptr << ",";
903
18.7k
    else
904
18.7k
      f << "_,";
905
133k
  }
906
3.95k
  input->setReadInverted(false);
907
3.95k
  zone->ascii().addPos(entry.begin());
908
3.95k
  zone->ascii().addNote(f.str().c_str());
909
3.95k
  zone->ascii().addPos(entry.end());
910
3.95k
  zone->ascii().addNote("_");
911
3.95k
  return true;
912
13.5k
}
913
914
bool RagTime5Document::readLongList(RagTime5ClusterManager::Link const &link, std::vector<long> &list)
915
79.7k
{
916
79.7k
  if (!link.m_ids.empty() && link.m_ids[0] &&
917
3.01k
      readLongListWithSize(link.m_ids[0], link.m_fieldSize, list, link.m_name))
918
1.23k
    return true;
919
78.4k
  list=link.m_longList;
920
78.4k
  return !list.empty();
921
79.7k
}
922
923
bool RagTime5Document::readPositions(int posId, std::vector<long> &listPosition)
924
10.5k
{
925
10.5k
  return readLongListWithSize(posId, 4, listPosition, "Positions");
926
10.5k
}
927
928
////////////////////////////////////////////////////////////
929
// Cluster
930
////////////////////////////////////////////////////////////
931
bool RagTime5Document::readClusterRootData(RagTime5ClusterManager::ClusterRoot &cluster)
932
20.9k
{
933
  // first read the list of child cluster and update the list of cluster for the cluster manager
934
20.9k
  std::vector<int> listClusters;
935
1.68M
  for (auto const &zone : m_state->m_zonesList) {
936
1.68M
    if (!zone || zone->m_isParsed || !zone->m_entry.valid() || zone->getKindLastPart(zone->m_kinds[1].empty())!="Cluster")
937
1.52M
      continue;
938
154k
    listClusters.push_back(zone->m_ids[0]);
939
154k
  }
940
941
20.9k
  if (cluster.m_listClusterId==0) {
942
7.20k
    MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterRootData: cluster list id is not set, try zone id+1\n"));
943
7.20k
    cluster.m_listClusterId=cluster.m_zoneId+1;
944
7.20k
  }
945
20.9k
  std::vector<int> listChilds;
946
20.9k
  m_clusterManager->readClusterMainList(cluster, listChilds, listClusters);
947
20.9k
  std::set<int> seens;
948
  // the list of graphic type
949
20.9k
  if (!cluster.m_graphicTypeLink.empty() && m_graphParser->readGraphicTypes(cluster.m_graphicTypeLink)) {
950
9.83k
    if (cluster.m_graphicTypeLink.m_ids.size()>2 && cluster.m_graphicTypeLink.m_ids[1])
951
0
      seens.insert(cluster.m_graphicTypeLink.m_ids[1]);
952
9.83k
  }
953
  // the different styles ( beginning with colors, then graphic styles and text styles )
954
188k
  for (int i=0; i<8; ++i) {
955
167k
    int const order[]= {7, 6, 1, 2, 0, 4, 3, 5};
956
167k
    int cId=cluster.m_styleClusterIds[order[i]];
957
167k
    if (!cId) continue;
958
959
111k
    int const wh[]= {0x480, 0x480, 0x480, 0x480, 0x480, -1, 0x480, 0x8042};
960
111k
    auto dZone= getDataZone(cId);
961
111k
    if (!dZone || dZone->getKindLastPart(dZone->m_kinds[1].empty())!="Cluster" || !readClusterZone(*dZone, wh[order[i]])) {
962
59.0k
      MWAW_DEBUG_MSG(("RagTime5Document::readClusterRootData: can not find cluster style zone %d\n", cId));
963
59.0k
      continue;
964
59.0k
    }
965
52.5k
    seens.insert(cId);
966
52.5k
  }
967
  // the formula def cluster list
968
20.9k
  if (!cluster.m_listClusterLink[1].empty()) {
969
2.85k
    RagTime5DocumentInternal::ClustListParser parser(*m_clusterManager.get(), 4, "FormulaList");
970
2.85k
    readFixedSizeZone(cluster.m_listClusterLink[1], parser);
971
    // TODO: read the field cluster's data here
972
2.85k
  }
973
  // list of style
974
20.9k
  if (!cluster.m_listClusterLink[2].empty()) {
975
0
    RagTime5DocumentInternal::ClustListParser parser(*m_clusterManager.get(), 4, "RootUnknALst2");
976
0
    readFixedSizeZone(cluster.m_listClusterLink[2], parser);
977
0
  }
978
  // now the main cluster list
979
41.9k
  for (int i=0; i<1; ++i) {
980
20.9k
    int cId=cluster.m_clusterIds[i];
981
20.9k
    if (cId==0) continue;
982
18.5k
    auto data=getDataZone(cId);
983
18.5k
    if (!data || !data->m_entry.valid() || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
984
12.7k
      MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterRootData: the cluster zone %d seems bad\n", cId));
985
12.7k
      continue;
986
12.7k
    }
987
5.80k
    int const wh[]= {0x10000};
988
5.80k
    if (readClusterZone(*data, wh[i]))
989
5.72k
      seens.insert(cId);
990
5.80k
  }
991
20.9k
  if (!cluster.m_functionNameLink.empty())
992
14.7k
    m_formulaParser->readFunctionNames(cluster.m_functionNameLink);
993
20.9k
  m_state->m_buttonFormulaLink=cluster.m_formulaLink;
994
20.9k
  for (auto const &lnk : cluster.m_settingLinks) {
995
15.7k
    if (lnk.empty()) continue;
996
15.7k
    RagTime5StructManager::FieldParser defaultParser("Settings");
997
15.7k
    readStructZone(lnk, defaultParser, 0);
998
15.7k
  }
999
20.9k
  if (!cluster.m_docInfoLink.empty()) {
1000
2.87k
    RagTime5DocumentInternal::DocInfoFieldParser parser(*this);
1001
2.87k
    readStructZone(cluster.m_docInfoLink, parser, 18);
1002
2.87k
  }
1003
20.9k
  if (!cluster.m_listUnicodeLink.empty()) {
1004
15.0k
    RagTime5DocumentInternal::IndexUnicodeParser parser(*this, true, "RootUnicodeLst");
1005
15.0k
    readListZone(cluster.m_listUnicodeLink, parser);
1006
15.0k
  }
1007
1008
  // unknown link
1009
20.9k
  if (!cluster.m_linkUnknown.empty()) { // find always an empty list
1010
17.1k
    RagTime5StructManager::DataParser parser("RootUnknC");
1011
17.1k
    readListZone(cluster.m_linkUnknown, parser);
1012
17.1k
  }
1013
  // now read the not parsed childs
1014
162k
  for (auto cId : listChilds) {
1015
162k
    if (cId==0 || seens.find(cId)!=seens.end())
1016
32.8k
      continue;
1017
129k
    auto dZone= getDataZone(cId);
1018
129k
    if (!dZone || dZone->getKindLastPart(dZone->m_kinds[1].empty())!="Cluster" || !readClusterZone(*dZone)) {
1019
77.2k
      MWAW_DEBUG_MSG(("RagTime5Document::readClusterRootData: can not find cluster zone %d\n", cId));
1020
77.2k
      continue;
1021
77.2k
    }
1022
52.5k
    seens.insert(cId);
1023
52.5k
  }
1024
1025
48.7k
  for (auto const &link : cluster.m_linksList) {
1026
48.7k
    if (link.m_type==RagTime5ClusterManager::Link::L_List) {
1027
1.25k
      readListZone(link);
1028
1.25k
      continue;
1029
1.25k
    }
1030
47.4k
    else if (link.m_type==RagTime5ClusterManager::Link::L_LongList) {
1031
47.0k
      std::vector<long> list;
1032
47.0k
      readLongList(link, list);
1033
47.0k
      continue;
1034
47.0k
    }
1035
388
    else if (link.m_type==RagTime5ClusterManager::Link::L_UnknownClusterC) {
1036
161
      m_clusterManager->readUnknownClusterC(link);
1037
161
      continue;
1038
161
    }
1039
1040
227
    if (link.empty()) continue;
1041
227
    std::shared_ptr<RagTime5Zone> data=getDataZone(link.m_ids[0]);
1042
227
    if (!data || data->m_isParsed) {
1043
160
      MWAW_DEBUG_MSG(("RagTime5Document::readClusterRootData: can not find data zone %d\n", link.m_ids[0]));
1044
160
      continue;
1045
160
    }
1046
67
    data->m_hiLoEndian=cluster.m_hiLoEndian;
1047
67
    if (link.m_fieldSize==0 && !data->m_entry.valid())
1048
0
      continue;
1049
67
    switch (link.m_type) {
1050
0
    case RagTime5ClusterManager::Link::L_FieldsList:
1051
0
    case RagTime5ClusterManager::Link::L_List:
1052
0
    case RagTime5ClusterManager::Link::L_LongList:
1053
0
    case RagTime5ClusterManager::Link::L_UnicodeList:
1054
0
    case RagTime5ClusterManager::Link::L_UnknownClusterC:
1055
0
      break;
1056
0
    case RagTime5ClusterManager::Link::L_ClusterLink: {
1057
0
      std::vector<RagTime5StructManager::ZoneLink> links;
1058
0
      readClusterLinkList(*data, link, links);
1059
0
      break;
1060
0
    }
1061
67
    case RagTime5ClusterManager::Link::L_Unknown:
1062
#if !defined(__clang__)
1063
    default:
1064
#endif
1065
67
      readFixedSizeZone(link, "");
1066
67
      break;
1067
67
    }
1068
67
  }
1069
1070
20.9k
  return true;
1071
20.9k
}
1072
1073
bool RagTime5Document::readChildList(RagTime5ClusterManager::Link const &link, std::vector<RagTime5StructManager::ZoneLink> &childList, bool findN)
1074
6.04k
{
1075
6.04k
  if (link.m_ids.empty())
1076
1.98k
    return true;
1077
4.05k
  auto dataZone=getDataZone(link.m_ids[0]);
1078
4.05k
  if (!dataZone || dataZone->m_entry.length()<=0) // ok, empty list
1079
1.83k
    return true;
1080
2.22k
  if (!dataZone->m_entry.valid() ||
1081
2.22k
      dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData") {
1082
148
    MWAW_DEBUG_MSG(("RagTime5Document::readChildList: the child zone %d seems bad\n",
1083
148
                    link.m_ids[0]));
1084
148
    return false;
1085
148
  }
1086
2.07k
  if (findN) {
1087
0
    if (dataZone->m_entry.length()%12) {
1088
0
      MWAW_DEBUG_MSG(("RagTime5Document::readChildList: can not compute the number of child for zone %d\n",
1089
0
                      link.m_ids[0]));
1090
0
      return false;
1091
0
    }
1092
0
    auto finalLink=link;
1093
0
    finalLink.m_N=int(dataZone->m_entry.length()/12);
1094
0
    if (!readClusterLinkList(*dataZone, finalLink, childList))
1095
0
      return false;
1096
0
  }
1097
2.07k
  else if (!readClusterLinkList(*dataZone, link, childList))
1098
0
    return false;
1099
2.07k
  checkClusterList(childList);
1100
2.07k
  return true;
1101
2.07k
}
1102
1103
bool RagTime5Document::checkClusterList(std::vector<int> const &list)
1104
419k
{
1105
419k
  bool ok=true;
1106
419k
  for (auto cId : list) {
1107
69.2k
    if (cId==0) continue;
1108
69.2k
    auto data=getDataZone(cId);
1109
69.2k
    if (!data || !data->m_entry.valid() || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
1110
24.5k
      MWAW_DEBUG_MSG(("RagTime5ClusterManager::checkClusterList: the cluster zone %d seems bad\n", cId));
1111
24.5k
      ok=false;
1112
24.5k
    }
1113
69.2k
  }
1114
419k
  return ok;
1115
419k
}
1116
1117
bool RagTime5Document::checkClusterList(std::vector<RagTime5StructManager::ZoneLink> const &list)
1118
17.8k
{
1119
17.8k
  bool ok=true;
1120
17.8k
  for (auto const &lnk : list) {
1121
17.6k
    int cId=lnk.m_dataId;
1122
17.6k
    if (cId==0) continue;
1123
14.5k
    auto data=getDataZone(cId);
1124
14.5k
    if (!data || !data->m_entry.valid() || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
1125
5.30k
      MWAW_DEBUG_MSG(("RagTime5ClusterManager::checkClusterList: the cluster zone %d seems bad\n", cId));
1126
5.30k
      ok=false;
1127
5.30k
    }
1128
14.5k
  }
1129
17.8k
  return ok;
1130
17.8k
}
1131
1132
bool RagTime5Document::readClusterZone(RagTime5Zone &zone, int zoneType)
1133
425k
{
1134
425k
  std::shared_ptr<RagTime5ClusterManager::Cluster> cluster;
1135
425k
  if (!m_clusterManager->readCluster(zone, cluster, zoneType) || !cluster)
1136
79.0k
    return false;
1137
346k
  checkClusterList(cluster->m_clusterIdsList);
1138
1139
346k
  switch (cluster->m_type) {
1140
  // main zone
1141
1.21k
  case RagTime5ClusterManager::Cluster::C_ButtonZone:
1142
2.18k
  case RagTime5ClusterManager::Cluster::C_ChartZone:
1143
4.97k
  case RagTime5ClusterManager::Cluster::C_FormulaDef:
1144
8.31k
  case RagTime5ClusterManager::Cluster::C_FormulaPos:
1145
26.1k
  case RagTime5ClusterManager::Cluster::C_GraphicZone:
1146
26.2k
  case RagTime5ClusterManager::Cluster::C_GroupZone:
1147
40.3k
  case RagTime5ClusterManager::Cluster::C_Layout:
1148
44.1k
  case RagTime5ClusterManager::Cluster::C_PictureZone:
1149
59.2k
  case RagTime5ClusterManager::Cluster::C_Pipeline:
1150
65.1k
  case RagTime5ClusterManager::Cluster::C_SpreadsheetZone:
1151
65.2k
  case RagTime5ClusterManager::Cluster::C_Sound:
1152
81.4k
  case RagTime5ClusterManager::Cluster::C_TextZone: // parsing already done
1153
81.4k
    return true;
1154
8.31k
  case RagTime5ClusterManager::Cluster::C_ClusterGProp:
1155
8.31k
    return readClusterGProp(*cluster);
1156
156
  case RagTime5ClusterManager::Cluster::C_ClusterC:
1157
156
    return readUnknownClusterCData(*cluster);
1158
1.13k
  case RagTime5ClusterManager::Cluster::C_ColorPattern:
1159
1.13k
    return m_graphParser->readColorPatternZone(*cluster);
1160
20.9k
  case RagTime5ClusterManager::Cluster::C_Root: {
1161
20.9k
    auto *root=dynamic_cast<RagTime5ClusterManager::ClusterRoot *>(cluster.get());
1162
20.9k
    if (!root) {
1163
0
      MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterZone: can not find the root pointer\n"));
1164
0
      return false;
1165
0
    }
1166
20.9k
    readClusterRootData(*root);
1167
20.9k
    return true;
1168
20.9k
  }
1169
1170
  // style
1171
17.2k
  case RagTime5ClusterManager::Cluster::C_FormatStyles:
1172
17.2k
    return m_styleManager->readFormats(*cluster);
1173
21.3k
  case RagTime5ClusterManager::Cluster::C_ColorStyles:
1174
21.3k
    return m_styleManager->readGraphicColors(*cluster);
1175
22.3k
  case RagTime5ClusterManager::Cluster::C_GraphicStyles:
1176
22.3k
    return m_styleManager->readGraphicStyles(*cluster);
1177
24.0k
  case RagTime5ClusterManager::Cluster::C_TextStyles:
1178
24.0k
    return m_styleManager->readTextStyles(*cluster);
1179
43.5k
  case RagTime5ClusterManager::Cluster::C_UnitStyles: {
1180
43.5k
    RagTime5StructManager::FieldParser defaultParser("Units");
1181
43.5k
    return readStructZone(cluster->m_dataLink, defaultParser, 14, &cluster->m_nameLink);
1182
20.9k
  }
1183
1184
92.2k
  case RagTime5ClusterManager::Cluster::C_Empty:
1185
105k
  case RagTime5ClusterManager::Cluster::C_Unknown:
1186
#if !defined(__clang__)
1187
  default:
1188
#endif
1189
105k
    break;
1190
346k
  }
1191
1192
105k
  if (!cluster->m_nameLink.empty()) {
1193
2.12k
    std::map<int, librevenge::RVNGString> idToStringMap;
1194
2.12k
    readUnicodeStringList(cluster->m_nameLink, idToStringMap);
1195
2.12k
  }
1196
1197
105k
  for (auto const &link : cluster->m_linksList) {
1198
0
    if (link.m_type==RagTime5ClusterManager::Link::L_List)
1199
0
      readListZone(link);
1200
0
    else
1201
0
      readFixedSizeZone(link, "");
1202
0
  }
1203
105k
  return true;
1204
346k
}
1205
1206
bool RagTime5Document::readClusterLinkList
1207
(RagTime5ClusterManager::Link const &link, std::vector<RagTime5StructManager::ZoneLink> &list, std::string const &name)
1208
2.47k
{
1209
2.47k
  RagTime5DocumentInternal::ClustListParser parser(*m_clusterManager.get(), 10, !name.empty() ? name : link.getZoneName());
1210
2.47k
  if (!link.empty())
1211
2.39k
    readListZone(link, parser);
1212
2.47k
  list=parser.m_linkList;
1213
2.47k
  checkClusterList(list);
1214
2.47k
  return true;
1215
2.47k
}
1216
1217
bool RagTime5Document::readClusterLinkList(RagTime5Zone &zone, RagTime5ClusterManager::Link const &link,
1218
    std::vector<RagTime5StructManager::ZoneLink> &listLinks)
1219
12.1k
{
1220
12.1k
  listLinks.clear();
1221
12.1k
  if (!zone.m_entry.valid()) {
1222
844
    if (link.m_N && link.m_fieldSize) {
1223
842
      MWAW_DEBUG_MSG(("RagTime5Document::readClusterLinkList: can not find data zone %d\n", link.m_ids[0]));
1224
842
    }
1225
844
    return false;
1226
844
  }
1227
1228
11.3k
  MWAWInputStreamPtr input=zone.getInput();
1229
11.3k
  bool const hiLo=zone.m_hiLoEndian;
1230
11.3k
  input->setReadInverted(!hiLo);
1231
11.3k
  input->seek(zone.m_entry.begin(), librevenge::RVNG_SEEK_SET);
1232
11.3k
  zone.m_isParsed=true;
1233
1234
11.3k
  libmwaw::DebugFile &ascFile=zone.ascii();
1235
11.3k
  libmwaw::DebugStream f;
1236
11.3k
  std::string zoneName=link.m_name.empty() ? "ClustLink" : link.m_name;
1237
11.3k
  zoneName[0]=char(std::toupper((unsigned char) zoneName[0]));
1238
11.3k
  f << "Entries(" << zoneName << ")[" << zone << "]:";
1239
11.3k
  if (link.m_N*link.m_fieldSize>zone.m_entry.length() || link.m_N*link.m_fieldSize < 0 ||
1240
10.7k
      link.m_N>zone.m_entry.length() || link.m_fieldSize!=12) {
1241
614
    MWAW_DEBUG_MSG(("RagTime5Document::readClusterLinkList: bad fieldSize/N for zone %d\n", link.m_ids[0]));
1242
614
    f << "###";
1243
614
    ascFile.addPos(zone.m_entry.begin());
1244
614
    ascFile.addNote(f.str().c_str());
1245
614
    return true;
1246
614
  }
1247
10.7k
  ascFile.addPos(zone.m_entry.begin());
1248
10.7k
  ascFile.addNote(f.str().c_str());
1249
1250
10.7k
  listLinks.resize(size_t(link.m_N)+1);
1251
31.5k
  for (int i=0; i<link.m_N; ++i) {
1252
20.8k
    long pos=input->tell();
1253
20.8k
    f.str("");
1254
20.8k
    f << zoneName << "-" << i+1 << ":";
1255
20.8k
    RagTime5StructManager::ZoneLink cLink;
1256
1257
20.8k
    std::vector<int> listIds;
1258
20.8k
    if (!m_structManager->readDataIdList(input, 1, listIds)) {
1259
2.43k
      MWAW_DEBUG_MSG(("RagTime5Document::readClusterLinkList: a link seems bad\n"));
1260
2.43k
      f << "###id,";
1261
2.43k
      ascFile.addPos(pos);
1262
2.43k
      ascFile.addNote(f.str().c_str());
1263
2.43k
      input->seek(pos+12, librevenge::RVNG_SEEK_SET);
1264
2.43k
      continue;
1265
2.43k
    }
1266
18.3k
    else if (listIds[0]==0) {
1267
713
      ascFile.addPos(pos);
1268
713
      ascFile.addNote("_");
1269
713
      input->seek(pos+12, librevenge::RVNG_SEEK_SET);
1270
713
      continue;
1271
713
    }
1272
1273
17.6k
    cLink.m_dataId=listIds[0];
1274
17.6k
    f << m_clusterManager->getClusterDebugName(listIds[0]) << ",";
1275
17.6k
    cLink.m_subZoneId[0]=long(input->readULong(4)); // 0 or 80000000 and a small int
1276
17.6k
    cLink.m_subZoneId[1]=long(input->readLong(4)); // small int
1277
17.6k
    f << cLink;
1278
17.6k
    listLinks[size_t(i+1)]=cLink;
1279
17.6k
    ascFile.addPos(pos);
1280
17.6k
    ascFile.addNote(f.str().c_str());
1281
17.6k
    input->seek(pos+12, librevenge::RVNG_SEEK_SET);
1282
17.6k
  }
1283
10.7k
  if (input->tell()!=zone.m_entry.end()) {
1284
2.10k
    f.str("");
1285
2.10k
    f << zoneName << ":end";
1286
2.10k
    ascFile.addPos(input->tell());
1287
2.10k
    ascFile.addNote(f.str().c_str());
1288
2.10k
  }
1289
1290
10.7k
  return true;
1291
11.3k
}
1292
1293
////////////////////////////////////////////////////////////
1294
// structured zone
1295
////////////////////////////////////////////////////////////
1296
bool RagTime5Document::readDocInfoClusterData(RagTime5Zone &zone, MWAWEntry const &entry)
1297
0
{
1298
0
  if (!entry.valid() || entry.length()<160) {
1299
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocInfoClusterData: the entry does not seems valid\n"));
1300
0
    return false;
1301
0
  }
1302
0
  libmwaw::DebugFile &ascFile=zone.ascii();
1303
0
  libmwaw::DebugStream f;
1304
0
  MWAWInputStreamPtr input=zone.getInput();
1305
0
  long pos=entry.begin();
1306
0
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1307
1308
0
  f << "DocInfo[dataA]:";
1309
  // checkme the field data seems always in hilo endian...
1310
0
  bool actEndian=input->readInverted();
1311
0
  input->setReadInverted(false);
1312
1313
0
  auto val=static_cast<int>(input->readULong(2)); // always 0
1314
0
  if (val) f << "f0=" << val;
1315
0
  auto dataSz=long(input->readULong(4));
1316
0
  if (pos+dataSz>entry.end()) {
1317
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocInfoClusterData: the main data size seems bad\n"));
1318
0
    f << "###dSz=" << dataSz << ",";
1319
0
    ascFile.addDelimiter(input->tell(),'|');
1320
0
    ascFile.addPos(pos);
1321
0
    ascFile.addNote(f.str().c_str());
1322
0
    input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1323
0
    input->setReadInverted(actEndian);
1324
0
    return true;
1325
0
  }
1326
0
  for (int i=0; i<2; ++i) { // f1=2
1327
0
    val=static_cast<int>(input->readULong(2));
1328
0
    if (val) f << "f" << i << "=" << val << ",";
1329
0
  }
1330
0
  auto sSz=static_cast<int>(input->readULong(1));
1331
0
  long actPos=input->tell();
1332
0
  if (sSz>25) {
1333
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocInfoClusterData: the dataA string size seems bad\n"));
1334
0
    f << "###sSz=" << sSz << ",";
1335
0
    sSz=0;
1336
0
  }
1337
0
  std::string text("");
1338
0
  for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
1339
0
  f << text << ",";
1340
0
  input->seek(actPos+25, librevenge::RVNG_SEEK_SET);
1341
0
  f << "IDS=["; // maybe some char
1342
0
  for (int i=0; i<7; ++i) { // _, ?, ?, ?, 0, 0|4, ?
1343
0
    val=static_cast<int>(input->readULong(2));
1344
0
    if (val) f << std::hex << val << std::dec << ",";
1345
0
    else f << "_,";
1346
0
  }
1347
0
  f << "],";
1348
0
  sSz=static_cast<int>(input->readULong(1));
1349
0
  actPos=input->tell();
1350
0
  if (sSz>62) {
1351
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocInfoClusterData: the dataA string2 size seems bad\n"));
1352
0
    f << "###sSz2=" << sSz << ",";
1353
0
    sSz=0;
1354
0
  }
1355
0
  text=("");
1356
0
  for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
1357
0
  f << text << ",";
1358
0
  input->seek(actPos+63, librevenge::RVNG_SEEK_SET);
1359
0
  ascFile.addPos(pos);
1360
0
  ascFile.addNote(f.str().c_str());
1361
1362
0
  pos=input->tell();
1363
0
  f.str("");
1364
0
  f << "DocInfo[dataB]:";
1365
0
  f << "IDS=["; // maybe some char
1366
0
  for (int i=0; i<8; ++i) {
1367
0
    val=static_cast<int>(input->readULong(2));
1368
0
    if (val) f << std::hex << val << std::dec << ",";
1369
0
    else f << "_,";
1370
0
  }
1371
0
  f << "],";
1372
0
  for (int i=0; i<11; ++i) { // f0=-1|2|6, f1=-1|2|4, f3=0|17|21,
1373
0
    val=static_cast<int>(input->readLong(2));
1374
0
    if (val) f << "f" << i << "=" << val << ",";
1375
0
  }
1376
0
  val=static_cast<int>(input->readLong(1)); // 0
1377
0
  if (val) f << "f11=" << val << ",";
1378
0
  sSz=static_cast<int>(input->readULong(1));
1379
0
  if (sSz>64||pos+sSz+4>entry.end()) {
1380
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocInfoClusterData: the string size for dataB data seems bad\n"));
1381
0
    f << "###sSz3=" << sSz << ",";
1382
0
    ascFile.addDelimiter(input->tell(),'|');
1383
0
    ascFile.addPos(pos);
1384
0
    ascFile.addNote(f.str().c_str());
1385
0
    input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1386
0
    input->setReadInverted(actEndian);
1387
0
    return true;
1388
0
  }
1389
0
  text=("");
1390
0
  for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
1391
0
  f << text << ",";
1392
0
  if ((sSz%2)==1)
1393
0
    input->seek(1, librevenge::RVNG_SEEK_CUR);
1394
0
  ascFile.addPos(pos);
1395
0
  ascFile.addNote(f.str().c_str());
1396
1397
0
  pos=input->tell();
1398
0
  f.str("");
1399
0
  f << "DocInfo[dataC]:";
1400
0
  if (input->readLong(2)!=1 || (val=static_cast<int>(input->readLong(2)))<=0 || (val%4) || pos+6+val>entry.end()) {
1401
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocInfoClusterData: oops something is bad[dataC]\n"));
1402
0
    f << "###val=" << val << ",";
1403
0
    ascFile.addPos(pos);
1404
0
    ascFile.addNote(f.str().c_str());
1405
0
    input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1406
0
    input->setReadInverted(actEndian);
1407
0
    return true;
1408
0
  }
1409
0
  int N=val/4;
1410
0
  f << "list=[";
1411
0
  for (int i=0; i<N; ++i) {
1412
0
    val=static_cast<int>(input->readLong(4));
1413
0
    if (val)
1414
0
      f << std::hex << val << std::dec << ",";
1415
0
    else
1416
0
      f << "_,";
1417
0
  }
1418
0
  f << "],";
1419
0
  val=static_cast<int>(input->readLong(2)); // always 2
1420
0
  if (val!=2) f << "f0=" << val << ",";
1421
0
  sSz=static_cast<int>(input->readULong(2));
1422
0
  if (input->tell()+sSz+4>entry.end()) {
1423
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocInfoClusterData: string size seems bad[dataC]\n"));
1424
0
    f << "###sSz=" << sSz << ",";
1425
0
    ascFile.addPos(pos);
1426
0
    ascFile.addNote(f.str().c_str());
1427
0
    input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1428
0
    input->setReadInverted(actEndian);
1429
0
    return true;
1430
0
  }
1431
0
  text=("");
1432
0
  for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
1433
0
  f << text << ",";
1434
0
  if ((sSz%2)==1)
1435
0
    input->seek(1, librevenge::RVNG_SEEK_CUR);
1436
0
  ascFile.addPos(pos);
1437
0
  ascFile.addNote(f.str().c_str());
1438
1439
0
  pos=input->tell();
1440
0
  f.str("");
1441
0
  f << "DocInfo[dataD]:";
1442
0
  ascFile.addPos(pos);
1443
0
  ascFile.addNote(f.str().c_str());
1444
1445
0
  input->setReadInverted(actEndian);
1446
0
  return true;
1447
0
}
1448
1449
bool RagTime5Document::readScriptComment(RagTime5Zone &zone)
1450
0
{
1451
0
  if (!zone.m_entry.valid() ||
1452
0
      zone.getKindLastPart(zone.m_kinds[1].empty())!="Unicode") {
1453
0
    zone.addErrorInDebugFile("ScriptComment");
1454
0
    MWAW_DEBUG_MSG(("RagTime5Document::readScriptComment: the script comment zone %d seems bad\n", zone.m_ids[0]));
1455
0
    return true;
1456
0
  }
1457
0
  readUnicodeString(zone, "ScriptComment");
1458
0
  libmwaw::DebugStream f;
1459
0
  for (auto const &it : zone.m_childIdToZoneMap) {
1460
0
    auto child=it.second;
1461
0
    if (!child || child->m_isParsed) continue;
1462
0
    child->m_isParsed=true;
1463
0
    switch (it.first) {
1464
0
    case 3:  // find one time with no data
1465
0
      if (child->m_entry.valid()) {
1466
0
        MWAW_DEBUG_MSG(("RagTime5Document::readScriptComment: find data with child3\n"));
1467
0
        libmwaw::DebugFile &ascFile=child->ascii();
1468
0
        f.str("");
1469
0
        f << "ScriptComment[" << *child << "child3]:";
1470
0
        ascFile.addPos(child->m_entry.begin());
1471
0
        ascFile.addNote(f.str().c_str());
1472
0
        ascFile.addPos(child->m_entry.end());
1473
0
        ascFile.addNote("_");
1474
0
      }
1475
0
      break;
1476
0
    case 8:
1477
0
      ascii().addPos(child->m_defPosition);
1478
0
      ascii().addNote("scriptComment[refCount]");
1479
0
      break;
1480
0
    default: {
1481
0
      std::string kind=child->getKindLastPart();
1482
0
      if (kind=="Unicode") { // the script name
1483
0
        child->m_hiLoEndian=zone.m_hiLoEndian;
1484
0
        readUnicodeString(*child, "ScriptNameData");
1485
0
        break;
1486
0
      }
1487
0
      if (kind=="32Bit") {
1488
0
        if (child->m_variableD[0]!=0 || child->m_variableD[1]!=1) { // do not show in meny
1489
0
          MWAW_DEBUG_MSG(("RagTime5Document::readScriptComment: find unknown flag\n"));
1490
0
          ascii().addPos(child->m_defPosition);
1491
0
          ascii().addNote("scriptData[showInMenu]:###");
1492
0
        }
1493
0
        if (child->m_entry.valid()) {
1494
0
          libmwaw::DebugFile &ascFile=child->ascii();
1495
0
          f.str("");
1496
0
          f << "Entries(ScriptData)[" << *child << "]:###";
1497
0
          MWAW_DEBUG_MSG(("RagTime5Document::readScriptComment: find unknown script data\n"));
1498
0
          ascFile.addPos(child->m_entry.begin());
1499
0
          ascFile.addNote(f.str().c_str());
1500
0
          ascFile.addPos(child->m_entry.end());
1501
0
          ascFile.addNote("_");
1502
0
        }
1503
0
        break;
1504
0
      }
1505
0
      if (kind=="OSAScript") {
1506
0
        if (child->m_entry.valid()) {
1507
0
          libmwaw::DebugFile &ascFile=child->ascii();
1508
0
          f.str("");
1509
0
          f << "Entries(OSAScript)[" << *child << "]:";
1510
0
          ascFile.addPos(child->m_entry.begin());
1511
0
          ascFile.addNote(f.str().c_str());
1512
0
          ascFile.addPos(child->m_entry.end());
1513
0
          ascFile.addNote("_");
1514
0
        }
1515
0
        break;
1516
0
      }
1517
0
      MWAW_DEBUG_MSG(("RagTime5Document::readScriptComment: find unknown child zone\n"));
1518
0
      child->addErrorInDebugFile("ScriptComment");
1519
0
      break;
1520
0
    }
1521
0
    }
1522
0
  }
1523
0
  return true;
1524
0
}
1525
1526
bool RagTime5Document::readClusterGProp(RagTime5ClusterManager::Cluster &cluster)
1527
8.31k
{
1528
8.31k
  RagTime5ClusterManager::Link const &link=cluster.m_dataLink;
1529
8.31k
  if (link.m_ids.size()<2 || !link.m_ids[1]) {
1530
125
    MWAW_DEBUG_MSG(("RagTime5Document::readClusterGProp: can not find the main data\n"));
1531
125
    return false;
1532
125
  }
1533
  // probably a cluster with only on field, so...
1534
8.19k
  RagTime5StructManager::GObjPropFieldParser defaultParser("RootGObjProp");
1535
8.19k
  if (!readStructZone(link, defaultParser, 8, &cluster.m_nameLink)) {
1536
1.58k
    auto dataZone=getDataZone(link.m_ids[1]);
1537
1.58k
    if (dataZone)
1538
1.20k
      dataZone->addErrorInDebugFile("RootGObjProp");
1539
1.58k
    MWAW_DEBUG_MSG(("RagTime5Document::readClusterGProp: unexpected type for zone %d\n", link.m_ids[1]));
1540
1.58k
  }
1541
1542
8.19k
  for (auto const &lnk : cluster.m_linksList) {
1543
0
    MWAW_DEBUG_MSG(("RagTime5Document::readClusterGProp: find extra data\n"));
1544
0
    RagTime5StructManager::DataParser defParser("UnknBUnknown2");
1545
0
    readFixedSizeZone(lnk, defParser);
1546
0
  }
1547
1548
8.19k
  return true;
1549
8.31k
}
1550
1551
bool RagTime5Document::readUnknownClusterCData(RagTime5ClusterManager::Cluster const &cluster)
1552
156
{
1553
156
  RagTime5ClusterManager::Link const &link=cluster.m_dataLink;
1554
156
  if (link.m_ids.empty()) {
1555
128
    MWAW_DEBUG_MSG(("RagTime5Document::readUnknownClusterCData: can not find the main data\n"));
1556
128
    return false;
1557
128
  }
1558
28
  std::stringstream s;
1559
28
  s << "UnknC_" << char('A'+link.m_fileType[0]) << "_";
1560
28
  std::string zoneName=s.str();
1561
1562
28
  if (link.m_type==RagTime5ClusterManager::Link::L_List) {
1563
28
    if (link.m_fileType[1]==0x310) {
1564
      // find id=8,"Rechenblatt 1": spreadsheet name ?
1565
0
      RagTime5DocumentInternal::IndexUnicodeParser parser(*this, true, zoneName+"0");
1566
0
      readListZone(link, parser);
1567
0
    }
1568
28
    else {
1569
28
      RagTime5StructManager::DataParser parser(zoneName+"0");
1570
28
      readListZone(link, parser);
1571
28
    }
1572
28
  }
1573
0
  else {
1574
0
    RagTime5StructManager::DataParser defaultParser(zoneName+"0");
1575
0
    readFixedSizeZone(link, defaultParser);
1576
0
  }
1577
28
  for (auto const &lnk : cluster.m_linksList) {
1578
0
    RagTime5StructManager::DataParser parser(zoneName+"1");
1579
0
    readFixedSizeZone(lnk, parser);
1580
0
  }
1581
1582
28
  return true;
1583
156
}
1584
1585
bool RagTime5Document::readListZone(RagTime5ClusterManager::Link const &link)
1586
4.30k
{
1587
4.30k
  RagTime5StructManager::DataParser parser(link.getZoneName());
1588
4.30k
  return readListZone(link, parser);
1589
4.30k
}
1590
1591
bool RagTime5Document::readListZone(RagTime5ClusterManager::Link const &link, RagTime5StructManager::DataParser &parser)
1592
248k
{
1593
248k
  if (link.m_ids.size()<2 || !link.m_ids[1])
1594
16
    return false;
1595
1596
248k
  std::vector<long> decal;
1597
248k
  if (link.m_ids[0])
1598
2.57k
    readPositions(link.m_ids[0], decal);
1599
248k
  if (decal.empty())
1600
247k
    decal=link.m_longList;
1601
1602
248k
  int const dataId=link.m_ids[1];
1603
248k
  auto dataZone=getDataZone(dataId);
1604
248k
  auto N=int(decal.size());
1605
1606
248k
  if (!dataZone || !dataZone->m_entry.valid() ||
1607
177k
      dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData" || N<=1) {
1608
89.6k
    if (N==1 && dataZone && !dataZone->m_entry.valid()) {
1609
      // a zone with 0 zone is ok...
1610
11.3k
      dataZone->m_isParsed=true;
1611
11.3k
      libmwaw::DebugStream f;
1612
11.3k
      f << "[" << parser.getZoneName() << "]";
1613
11.3k
      ascii().addPos(dataZone->m_defPosition);
1614
11.3k
      ascii().addNote(f.str().c_str());
1615
11.3k
      return true;
1616
11.3k
    }
1617
78.2k
    MWAW_DEBUG_MSG(("RagTime5Document::readListZone: the data zone %d seems bad\n", dataId));
1618
78.2k
    return false;
1619
89.6k
  }
1620
1621
158k
  dataZone->m_isParsed=true;
1622
158k
  MWAWEntry entry=dataZone->m_entry;
1623
158k
  libmwaw::DebugFile &ascFile=dataZone->ascii();
1624
158k
  libmwaw::DebugStream f;
1625
158k
  f << "Entries(" << parser.getZoneName() << ")[" << *dataZone << "]:";
1626
158k
  ascFile.addPos(entry.end());
1627
158k
  ascFile.addNote("_");
1628
158k
  ascFile.addPos(entry.begin());
1629
158k
  ascFile.addNote(f.str().c_str());
1630
1631
158k
  MWAWInputStreamPtr input=dataZone->getInput();
1632
158k
  input->setReadInverted(!dataZone->m_hiLoEndian);
1633
158k
  long debPos=entry.begin();
1634
158k
  long endPos=entry.end();
1635
1636
860k
  for (int i=0; i<N-1; ++i) {
1637
701k
    long pos=decal[size_t(i)], lastPos=decal[size_t(i+1)];
1638
701k
    if (pos==lastPos) continue;
1639
596k
    if (pos<0 || pos>lastPos || debPos+lastPos>endPos) {
1640
62.7k
      MWAW_DEBUG_MSG(("RagTime5Document::readListZone: can not read the data zone %d-%d seems bad\n", dataId, i));
1641
62.7k
      continue;
1642
62.7k
    }
1643
534k
    input->seek(debPos+pos, librevenge::RVNG_SEEK_SET);
1644
534k
    f.str("");
1645
534k
    f << parser.getZoneName(i+1) << ":";
1646
534k
    if (!parser.parseData(input, debPos+lastPos, *dataZone, i+1, f))
1647
2.93k
      f << "###";
1648
534k
    ascFile.addPos(debPos+pos);
1649
534k
    ascFile.addNote(f.str().c_str());
1650
534k
    ascFile.addPos(debPos+lastPos);
1651
534k
    ascFile.addNote("_");
1652
534k
  }
1653
1654
158k
  input->setReadInverted(false);
1655
158k
  return true;
1656
248k
}
1657
1658
bool RagTime5Document::readFixedSizeZone(RagTime5ClusterManager::Link const &link, std::string const &name)
1659
5.29k
{
1660
5.29k
  RagTime5StructManager::DataParser parser(name.empty() ? link.getZoneName() : name);
1661
5.29k
  return readFixedSizeZone(link, parser);
1662
5.29k
}
1663
1664
bool RagTime5Document::readFixedSizeZone(RagTime5ClusterManager::Link const &link, RagTime5StructManager::DataParser &parser)
1665
39.8k
{
1666
39.8k
  if (link.m_ids.empty() || !link.m_ids[0])
1667
1.54k
    return false;
1668
1669
38.3k
  int const dataId=link.m_ids[0];
1670
38.3k
  auto dataZone=getDataZone(dataId);
1671
1672
38.3k
  if (!dataZone || !dataZone->m_entry.valid() ||
1673
30.5k
      dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData" ||
1674
28.1k
      link.m_fieldSize<=0 || link.m_N>dataZone->m_entry.length()/link.m_fieldSize ||
1675
25.9k
      link.m_N>dataZone->m_entry.length() || link.m_N<0) {
1676
12.6k
    if ((link.m_N==0 || link.m_fieldSize==0) && dataZone && !dataZone->m_entry.valid()) {
1677
      // a zone with 0 zone is ok...
1678
1.78k
      dataZone->m_isParsed=true;
1679
1.78k
      return true;
1680
1.78k
    }
1681
10.8k
    MWAW_DEBUG_MSG(("RagTime5Document::readFixedSizeZone: the data zone %d seems bad\n", dataId));
1682
10.8k
    if (dataZone) dataZone->addErrorInDebugFile(parser.getZoneName());
1683
10.8k
    return false;
1684
12.6k
  }
1685
1686
25.7k
  dataZone->m_isParsed=true;
1687
25.7k
  MWAWEntry entry=dataZone->m_entry;
1688
25.7k
  libmwaw::DebugFile &ascFile=dataZone->ascii();
1689
25.7k
  libmwaw::DebugStream f;
1690
25.7k
  f << "Entries(" << parser.getZoneName() << ")[" << *dataZone << "]:";
1691
25.7k
  ascFile.addPos(entry.end());
1692
25.7k
  ascFile.addNote("_");
1693
25.7k
  ascFile.addPos(entry.begin());
1694
25.7k
  ascFile.addNote(f.str().c_str());
1695
1696
25.7k
  MWAWInputStreamPtr input=dataZone->getInput();
1697
25.7k
  input->setReadInverted(!dataZone->m_hiLoEndian);
1698
25.7k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1699
25.7k
  long endPos=entry.end();
1700
1701
60.7k
  for (int i=0; i<link.m_N; ++i) {
1702
35.0k
    long pos=input->tell();
1703
35.0k
    f.str("");
1704
35.0k
    f << parser.getZoneName(i+1) << ":";
1705
35.0k
    if (!parser.parseData(input, pos+link.m_fieldSize, *dataZone, i+1, f))
1706
1.28k
      f << "###";
1707
35.0k
    ascFile.addPos(pos);
1708
35.0k
    ascFile.addNote(f.str().c_str());
1709
35.0k
    input->seek(pos+link.m_fieldSize, librevenge::RVNG_SEEK_SET);
1710
35.0k
  }
1711
25.7k
  long pos=input->tell();
1712
25.7k
  if (pos!=endPos) {
1713
2.56k
    f.str("");
1714
2.56k
    f << parser.getZoneName() << ":#end";
1715
2.56k
    ascFile.addPos(pos);
1716
2.56k
    ascFile.addNote(f.str().c_str());
1717
2.56k
  }
1718
25.7k
  input->setReadInverted(false);
1719
25.7k
  return true;
1720
38.3k
}
1721
1722
bool RagTime5Document::readStructZone(RagTime5ClusterManager::Link const &link, RagTime5StructManager::FieldParser &parser, int headerSz, RagTime5ClusterManager::NameLink *nameLink)
1723
153k
{
1724
153k
  if (link.m_ids.size()<2 || !link.m_ids[1])
1725
1.35k
    return false;
1726
1727
152k
  std::map<int, librevenge::RVNGString> idToNameMap;
1728
152k
  if (nameLink && !nameLink->empty()) {
1729
107k
    readUnicodeStringList(*nameLink, idToNameMap);
1730
107k
    *nameLink=RagTime5ClusterManager::NameLink();
1731
107k
  }
1732
152k
  std::vector<long> decal;
1733
152k
  if (link.m_ids[0])
1734
7.98k
    readPositions(link.m_ids[0], decal);
1735
152k
  if (decal.empty())
1736
150k
    decal=link.m_longList;
1737
152k
  int const dataId=link.m_ids[1];
1738
152k
  auto dataZone=getDataZone(dataId);
1739
152k
  if (!dataZone || !dataZone->m_entry.valid() ||
1740
90.7k
      dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData") {
1741
68.4k
    if (decal.size()==1) {
1742
      // a zone with 0 zone is ok...
1743
13.3k
      if (dataZone)
1744
6.03k
        dataZone->m_isParsed=true;
1745
13.3k
      return true;
1746
13.3k
    }
1747
55.1k
    MWAW_DEBUG_MSG(("RagTime5Document::readStructZone: the data zone %d seems bad\n", dataId));
1748
55.1k
    return false;
1749
68.4k
  }
1750
84.0k
  dataZone->m_isParsed=true;
1751
84.0k
  MWAWEntry entry=dataZone->m_entry;
1752
84.0k
  libmwaw::DebugFile &ascFile=dataZone->ascii();
1753
84.0k
  libmwaw::DebugStream f;
1754
84.0k
  f << "Entries(" << parser.getZoneName() << ")[" << *dataZone << "]:";
1755
84.0k
  ascFile.addPos(entry.end());
1756
84.0k
  ascFile.addNote("_");
1757
84.0k
  ascFile.addPos(entry.begin());
1758
84.0k
  ascFile.addNote(f.str().c_str());
1759
1760
84.0k
  auto N=int(decal.size());
1761
84.0k
  MWAWInputStreamPtr input=dataZone->getInput();
1762
84.0k
  input->setReadInverted(!dataZone->m_hiLoEndian);
1763
84.0k
  long debPos=entry.begin();
1764
84.0k
  long endPos=entry.end();
1765
84.0k
  if (N==0) {
1766
7.36k
    MWAW_DEBUG_MSG(("RagTime5Document::readStructZone: can not find decal list for zone %d, let try to continue\n", dataId));
1767
7.36k
    input->seek(debPos, librevenge::RVNG_SEEK_SET);
1768
7.36k
    int n=0;
1769
618k
    while (input->tell()+8 < endPos) {
1770
612k
      long pos=input->tell();
1771
612k
      int id=++n;
1772
612k
      librevenge::RVNGString name("");
1773
612k
      if (idToNameMap.find(id)!=idToNameMap.end())
1774
22.7k
        name=idToNameMap.find(id)->second;
1775
612k
      if (!readStructData(*dataZone, endPos, id, headerSz, parser, name)) {
1776
940
        input->seek(pos, librevenge::RVNG_SEEK_SET);
1777
940
        break;
1778
940
      }
1779
612k
    }
1780
7.36k
    if (input->tell()!=endPos) {
1781
3.34k
      static bool first=true;
1782
3.34k
      if (first) {
1783
25
        MWAW_DEBUG_MSG(("RagTime5Document::readStructZone: can not read some block\n"));
1784
25
        first=false;
1785
25
      }
1786
3.34k
      ascFile.addPos(debPos);
1787
3.34k
      ascFile.addNote("###");
1788
3.34k
    }
1789
7.36k
  }
1790
76.6k
  else {
1791
593k
    for (int i=0; i<N-1; ++i) {
1792
517k
      long pos=decal[size_t(i)];
1793
517k
      long nextPos=decal[size_t(i+1)];
1794
517k
      if (pos<0 || debPos+pos>endPos) {
1795
32.8k
        MWAW_DEBUG_MSG(("RagTime5Document::readStructZone: can not read the data zone %d-%d seems bad\n", dataId, i));
1796
32.8k
        continue;
1797
32.8k
      }
1798
484k
      librevenge::RVNGString name("");
1799
484k
      if (idToNameMap.find(i+1)!=idToNameMap.end())
1800
142k
        name=idToNameMap.find(i+1)->second;
1801
484k
      input->seek(debPos+pos, librevenge::RVNG_SEEK_SET);
1802
484k
      readStructData(*dataZone, debPos+nextPos, i+1, headerSz, parser, name);
1803
484k
      if (input->tell()!=debPos+nextPos) {
1804
81.6k
        static bool first=true;
1805
81.6k
        if (first) {
1806
25
          MWAW_DEBUG_MSG(("RagTime5Document::readStructZone: can not read some block\n"));
1807
25
          first=false;
1808
25
        }
1809
81.6k
        ascFile.addPos(debPos+pos);
1810
81.6k
        ascFile.addNote("###");
1811
81.6k
      }
1812
484k
    }
1813
76.6k
  }
1814
84.0k
  return true;
1815
152k
}
1816
1817
bool RagTime5Document::readStructData(RagTime5Zone &zone, long endPos, int n, int headerSz,
1818
                                      RagTime5StructManager::FieldParser &parser, librevenge::RVNGString const &dataName)
1819
1.14M
{
1820
1.14M
  MWAWInputStreamPtr input=zone.getInput();
1821
1.14M
  long pos=input->tell();
1822
1.14M
  if ((headerSz && pos+headerSz>endPos) || (headerSz==0 && pos+5>endPos)) return false;
1823
1.13M
  libmwaw::DebugFile &ascFile=zone.ascii();
1824
1.13M
  libmwaw::DebugStream f;
1825
1.13M
  std::string const zoneName=parser.getZoneName(n);
1826
1.13M
  int m=0;
1827
1.13M
  if (headerSz>0) {
1828
902k
    f << zoneName << "[A]:";
1829
902k
    if (!dataName.empty()) f << dataName.cstr() << ",";
1830
902k
    int val;
1831
902k
    if (headerSz==14) {
1832
894k
      val=static_cast<int>(input->readLong(4));
1833
894k
      if (val!=1) f << "numUsed=" << val << ",";
1834
894k
      f << "f1=" << std::hex << input->readULong(2) << std::dec << ",";
1835
894k
      val=static_cast<int>(input->readLong(2)); // sometimes form an increasing sequence but not always
1836
894k
      if (val!=n) f << "id=" << val << ",";
1837
1838
894k
      RagTime5StructManager::Field field;
1839
894k
      field.m_fileType=input->readULong(4);
1840
894k
      field.m_type=RagTime5StructManager::Field::T_Long;
1841
894k
      field.m_longValue[0]=input->readLong(2);
1842
894k
      parser.parseHeaderField(field, zone, n, f);
1843
894k
    }
1844
7.41k
    else if (headerSz==8) {
1845
7.41k
      val=static_cast<int>(input->readLong(2));
1846
7.41k
      if (val!=1) f << "numUsed=" << val << ",";
1847
7.41k
      val=static_cast<int>(input->readLong(2)); // sometimes form an increasing sequence but not always
1848
7.41k
      if (val!=n) f << "id=" << val << ",";
1849
7.41k
      f << "type=" << std::hex << input->readULong(4) << std::dec << ","; // 0 or 01458042
1850
7.41k
    }
1851
0
    else if (headerSz==18) { // docinfo header
1852
0
      val=static_cast<int>(input->readLong(4)); // 1 or 3
1853
0
      if (val!=1) f << "numUsed?=" << val << ",";
1854
0
      val=static_cast<int>(input->readLong(4)); // always 0
1855
0
      if (val) f << "f0=" << val << ",";
1856
0
      f << "ID=" << std::hex << input->readULong(4) << ","; // a big number
1857
0
      val=static_cast<int>(input->readLong(4));
1858
0
      if (val!=0x1f6817) // doc info type
1859
0
        f << "type=" << std::hex << val << std::dec << ",";
1860
0
      val=static_cast<int>(input->readLong(2)); // always 0
1861
0
      if (val) f << "f1=" << val << ",";
1862
0
      input->seek(pos+headerSz, librevenge::RVNG_SEEK_SET);
1863
0
    }
1864
0
    else {
1865
0
      MWAW_DEBUG_MSG(("RagTime5Document::readStructData: find unknown header size\n"));
1866
0
      f << "###hSz";
1867
0
      input->seek(pos+headerSz, librevenge::RVNG_SEEK_SET);
1868
0
    }
1869
902k
    ascFile.addPos(pos);
1870
902k
    ascFile.addNote(f.str().c_str());
1871
902k
  }
1872
1.13M
  pos=input->tell();
1873
1.13M
  if (parser.m_regroupFields) {
1874
150k
    f.str("");
1875
150k
    f << zoneName << "[B]:";
1876
150k
    if (headerSz==0 && !dataName.empty()) f << dataName.cstr() << ",";
1877
150k
  }
1878
2.56M
  while (!input->isEnd()) {
1879
2.50M
    long actPos=input->tell();
1880
2.50M
    if (actPos>=endPos) break;
1881
1882
2.11M
    if (!parser.m_regroupFields) {
1883
1.81M
      f.str("");
1884
1.81M
      f << zoneName << "[B" << ++m << "]:";
1885
1.81M
      if (m==1 && headerSz==0 && !dataName.empty()) f << dataName.cstr() << ",";
1886
1.81M
    }
1887
2.11M
    RagTime5StructManager::Field field;
1888
2.11M
    if (!m_structManager->readField(input, endPos, ascFile, field, headerSz ? 0 : endPos-actPos)) {
1889
687k
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
1890
687k
      break;
1891
687k
    }
1892
1.42M
    if (!parser.parseField(field, zone, n, f))
1893
0
      f << "#" << field;
1894
1.42M
    if (!parser.m_regroupFields) {
1895
1.20M
      ascFile.addPos(actPos);
1896
1.20M
      ascFile.addNote(f.str().c_str());
1897
1.20M
    }
1898
1.42M
  }
1899
1.13M
  if (parser.m_regroupFields && pos!=input->tell()) {
1900
123k
    ascFile.addPos(pos);
1901
123k
    ascFile.addNote(f.str().c_str());
1902
123k
  }
1903
1.13M
  return true;
1904
1.14M
}
1905
1906
////////////////////////////////////////////////////////////
1907
// zone unpack/create ascii file, ...
1908
////////////////////////////////////////////////////////////
1909
bool RagTime5Document::updateZone(std::shared_ptr<RagTime5Zone> &zone)
1910
2.13M
{
1911
2.13M
  if (!zone || zone->m_isInitialised || zone->m_isParsed)
1912
455k
    return true;
1913
1914
1.68M
  zone->m_isInitialised=true;
1915
  // update the kinds of this zone
1916
5.05M
  for (int j=1; j<3; ++j) {
1917
3.36M
    if (!zone->m_ids[j]) continue;
1918
3.12M
    if (m_state->m_zoneIdToTypeMap.find(zone->m_ids[j])==m_state->m_zoneIdToTypeMap.end()) {
1919
      // the main zone seems to point to a cluster id...
1920
611k
      if (zone->m_ids[0]<=6) continue;
1921
468k
      MWAW_DEBUG_MSG(("RagTime5Document::updateZone: can not find the type for %d:%d\n", zone->m_ids[0],j));
1922
468k
      ascii().addPos(zone->m_defPosition);
1923
468k
      ascii().addNote("###type,");
1924
468k
    }
1925
2.51M
    else {
1926
2.51M
      zone->m_kinds[j-1]=m_state->m_zoneIdToTypeMap.find(zone->m_ids[j])->second;
1927
2.51M
      libmwaw::DebugStream f;
1928
2.51M
      f << zone->m_kinds[j-1] << ",";
1929
2.51M
      ascii().addPos(zone->m_defPosition);
1930
2.51M
      ascii().addNote(f.str().c_str());
1931
2.51M
    }
1932
3.12M
  }
1933
1934
  // update the zone input
1935
1.68M
  if (!zone->m_entriesList.empty() && !updateZoneInput(*zone))
1936
2.14k
    return false;
1937
1938
  // check for pack zones and unpack them
1939
1.68M
  int usedId=zone->m_kinds[1].empty() ? 0 : 1;
1940
1.68M
  std::string actType=zone->getKindLastPart(usedId==0);
1941
1.68M
  if (actType=="Pack") {
1942
1.07M
    if (zone->m_entry.valid() && !unpackZone(*zone)) {
1943
292k
      MWAW_DEBUG_MSG(("RagTime5Document::updateZone: can not unpack the zone %d\n", zone->m_ids[0]));
1944
292k
      libmwaw::DebugStream f;
1945
292k
      libmwaw::DebugFile &ascFile=zone->ascii();
1946
292k
      f << "Entries(BADPACK)[" << zone << "]:###" << zone->m_kinds[usedId];
1947
292k
      ascFile.addPos(zone->m_entry.begin());
1948
292k
      ascFile.addNote(f.str().c_str());
1949
292k
      zone->m_entry=MWAWEntry();
1950
292k
    }
1951
1.07M
    size_t length=zone->m_kinds[usedId].size();
1952
1.07M
    if (length>5)
1953
1.07M
      zone->m_kinds[usedId].resize(length-5);
1954
344
    else
1955
344
      zone->m_kinds[usedId]="";
1956
1.07M
  }
1957
1958
  // check hilo flag
1959
1.68M
  usedId=zone->m_kinds[1].empty() ? 0 : 1;
1960
1.68M
  actType=zone->getKindLastPart(usedId==0);
1961
1.68M
  if (actType=="HiLo" || actType=="LoHi") {
1962
1.14M
    zone->m_hiLoEndian=actType=="HiLo";
1963
1.14M
    size_t length=zone->m_kinds[usedId].size();
1964
1.14M
    if (length>5)
1965
1.14M
      zone->m_kinds[usedId].resize(length-5);
1966
0
    else
1967
0
      zone->m_kinds[usedId]="";
1968
1.14M
  }
1969
  // update the zone kind
1970
1.68M
  std::string kind=zone->getKindLastPart();
1971
1.68M
  if (kind=="Type") {
1972
20.5k
    size_t length=zone->m_kinds[0].size();
1973
20.5k
    if (length>5)
1974
20.5k
      zone->m_kinds[0].resize(length-5);
1975
0
    else
1976
0
      zone->m_kinds[0]="";
1977
20.5k
    zone->m_extra += "type,";
1978
20.5k
  }
1979
1980
1.68M
  return true;
1981
1.68M
}
1982
bool RagTime5Document::updateZoneInput(RagTime5Zone &zone)
1983
2.59M
{
1984
2.59M
  if (zone.getInput() || zone.m_entriesList.empty())
1985
53.2k
    return true;
1986
2.53M
  std::stringstream s;
1987
2.53M
  s << "Zone" << std::hex << zone.m_entriesList[0].begin() << std::dec;
1988
2.53M
  zone.setAsciiFileName(s.str());
1989
1990
2.53M
  MWAWInputStreamPtr input = m_parser->getInput();
1991
2.53M
  if (zone.m_entriesList.size()==1) {
1992
2.36M
    zone.setInput(input);
1993
2.36M
    zone.m_entry=zone.m_entriesList[0];
1994
2.36M
    return true;
1995
2.36M
  }
1996
1997
173k
  libmwaw::DebugStream f;
1998
173k
  f << "Entries(" << zone.getZoneName() << "):";
1999
173k
  std::shared_ptr<MWAWStringStream> newStream;
2000
173k
  int n=0;
2001
387k
  for (auto const &entry : zone.m_entriesList) {
2002
387k
    if (!entry.valid() || !input->checkPosition(entry.end())) {
2003
2.14k
      MWAW_DEBUG_MSG(("RagTime5Document::updateZoneInput: can not read some data\n"));
2004
2.14k
      f << "###";
2005
2.14k
      ascii().addPos(entry.begin());
2006
2.14k
      ascii().addNote(f.str().c_str());
2007
2.14k
      return false;
2008
2.14k
    }
2009
385k
    input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2010
2011
385k
    unsigned long read;
2012
385k
    const unsigned char *dt = input->read(static_cast<unsigned long>(entry.length()), read);
2013
385k
    if (!dt || long(read) != entry.length()) {
2014
0
      MWAW_DEBUG_MSG(("RagTime5Document::updateZoneInput: can not read some data\n"));
2015
0
      f << "###";
2016
0
      ascii().addPos(entry.begin());
2017
0
      ascii().addNote(f.str().c_str());
2018
0
      return false;
2019
0
    }
2020
385k
    ascii().skipZone(entry.begin(), entry.end()-1);
2021
385k
    if (n++==0)
2022
172k
      newStream.reset(new MWAWStringStream(dt, static_cast<unsigned int>(entry.length())));
2023
213k
    else
2024
213k
      newStream->append(dt, static_cast<unsigned int>(entry.length()));
2025
385k
  }
2026
2027
171k
  MWAWInputStreamPtr newInput(new MWAWInputStream(newStream, false));
2028
171k
  zone.setInput(newInput);
2029
171k
  zone.m_entry.setBegin(0);
2030
171k
  zone.m_entry.setLength(newInput->size());
2031
2032
171k
  return true;
2033
173k
}
2034
2035
bool RagTime5Document::unpackZone(RagTime5Zone &zone, MWAWEntry const &entry, std::vector<unsigned char> &data)
2036
1.06M
{
2037
1.06M
  if (!entry.valid())
2038
0
    return false;
2039
2040
1.06M
  MWAWInputStreamPtr input=zone.getInput();
2041
1.06M
  long pos=entry.begin(), endPos=entry.end();
2042
1.06M
  if (entry.length()<4 || !input || !input->checkPosition(endPos)) {
2043
9.04k
    MWAW_DEBUG_MSG(("RagTime5Document::unpackZone: the input seems bad\n"));
2044
9.04k
    return false;
2045
9.04k
  }
2046
2047
1.05M
  bool actEndian=input->readInverted();
2048
1.05M
  input->setReadInverted(false);
2049
1.05M
  input->seek(pos, librevenge::RVNG_SEEK_SET);
2050
2051
1.05M
  data.resize(0);
2052
1.05M
  auto sz=static_cast<unsigned long>(input->readULong(4));
2053
1.05M
  if (sz==0) {
2054
13.5k
    input->setReadInverted(actEndian);
2055
13.5k
    return true;
2056
13.5k
  }
2057
1.04M
  auto flag=int(sz>>24);
2058
1.04M
  sz &= 0xFFFFFF;
2059
1.04M
  if ((flag&0xf) || (flag&0xf0)==0 || !(sz&0xFFFFFF)) {
2060
119k
    input->setReadInverted(actEndian);
2061
119k
    return false;
2062
119k
  }
2063
2064
924k
  int nBytesRead=0, szField=9;
2065
924k
  unsigned int read=0;
2066
924k
  size_t mapPos=0;
2067
924k
  data.reserve(size_t(sz));
2068
924k
  std::vector<std::vector<unsigned char> > mapToString;
2069
924k
  mapToString.reserve(size_t(entry.length()-6));
2070
924k
  bool ok=false;
2071
130M
  while (!input->isEnd()) {
2072
130M
    if (static_cast<int>(mapPos)==(1<<szField)-0x102)
2073
137k
      ++szField;
2074
130M
    if (input->tell()>=endPos) {
2075
5.44k
      MWAW_DEBUG_MSG(("RagTime5Document::unpackZone: oops can not find last data\n"));
2076
5.44k
      ok=false;
2077
5.44k
      break;
2078
5.44k
    }
2079
155M
    do {
2080
155M
      read = (read<<8)+static_cast<unsigned int>(input->readULong(1));
2081
155M
      nBytesRead+=8;
2082
155M
    }
2083
155M
    while (nBytesRead<szField);
2084
130M
    unsigned int val=(read >> (nBytesRead-szField));
2085
130M
    nBytesRead-=szField;
2086
130M
    read &= ((1<<nBytesRead)-1);
2087
2088
130M
    if (val<0x100) {
2089
58.4M
      auto c=static_cast<unsigned char>(val);
2090
58.4M
      data.push_back(c);
2091
58.4M
      if (mapPos>= mapToString.size())
2092
58.4M
        mapToString.resize(mapPos+1);
2093
58.4M
      mapToString[mapPos++]=std::vector<unsigned char>(1,c);
2094
58.4M
      continue;
2095
58.4M
    }
2096
72.4M
    if (val==0x100) { // begin
2097
898k
      if (!data.empty()) {
2098
        // data are reset when mapPos=3835, so it is ok
2099
10.0k
        mapPos=0;
2100
10.0k
        mapToString.resize(0);
2101
10.0k
        szField=9;
2102
10.0k
      }
2103
898k
      continue;
2104
898k
    }
2105
71.5M
    if (val==0x101) {
2106
824k
      ok=read==0;
2107
824k
      if (!ok) {
2108
2.08k
        MWAW_DEBUG_MSG(("RagTime5Document::unpackZone: find 0x101 in bad position\n"));
2109
2.08k
      }
2110
824k
      break;
2111
824k
    }
2112
70.7M
    auto readPos=size_t(val-0x102);
2113
70.7M
    if (readPos >= mapToString.size()) {
2114
91.4k
      MWAW_DEBUG_MSG(("RagTime5Document::unpackZone: find bad position\n"));
2115
91.4k
      ok = false;
2116
91.4k
      break;
2117
91.4k
    }
2118
70.6M
    std::vector<unsigned char> final=mapToString[readPos++];
2119
70.6M
    if (readPos==mapToString.size())
2120
3.38M
      final.push_back(final[0]);
2121
67.2M
    else
2122
67.2M
      final.push_back(mapToString[readPos][0]);
2123
70.6M
    data.insert(data.end(), final.begin(), final.end());
2124
70.6M
    if (mapPos>= mapToString.size())
2125
70.6M
      mapToString.resize(mapPos+1);
2126
70.6M
    mapToString[mapPos++]=final;
2127
70.6M
  }
2128
2129
924k
  if (ok && data.size()!=size_t(sz)) {
2130
48.3k
    MWAW_DEBUG_MSG(("RagTime5Document::unpackZone: oops the data file is bad\n"));
2131
48.3k
    ok=false;
2132
48.3k
  }
2133
924k
  if (!ok) {
2134
149k
    MWAW_DEBUG_MSG(("RagTime5Document::unpackZone: stop with mapPos=%ld and totalSize=%ld/%ld\n", long(mapPos), long(data.size()), long(sz)));
2135
149k
  }
2136
924k
  input->setReadInverted(actEndian);
2137
924k
  return ok;
2138
1.04M
}
2139
2140
bool RagTime5Document::unpackZone(RagTime5Zone &zone)
2141
1.06M
{
2142
1.06M
  if (!zone.m_entry.valid())
2143
0
    return false;
2144
2145
1.06M
  std::vector<unsigned char> newData;
2146
1.06M
  if (!unpackZone(zone, zone.m_entry, newData))
2147
277k
    return false;
2148
788k
  long pos=zone.m_entry.begin(), endPos=zone.m_entry.end();
2149
788k
  MWAWInputStreamPtr input=zone.getInput();
2150
788k
  if (input->tell()!=endPos) {
2151
14.5k
    MWAW_DEBUG_MSG(("RagTime5Document::unpackZone: find some extra data\n"));
2152
14.5k
    return false;
2153
14.5k
  }
2154
773k
  if (newData.empty()) {
2155
    // empty zone
2156
2
    zone.ascii().addPos(pos);
2157
2
    zone.ascii().addNote("_");
2158
2
    zone.m_entry.setLength(0);
2159
2
    zone.m_extra += "packed,";
2160
2
    return true;
2161
2
  }
2162
2163
773k
  if (input.get()==m_parser->getInput().get())
2164
686k
    ascii().skipZone(pos, endPos-1);
2165
2166
773k
  std::shared_ptr<MWAWStringStream> newStream(new MWAWStringStream(&newData[0], static_cast<unsigned int>(newData.size())));
2167
773k
  MWAWInputStreamPtr newInput(new MWAWInputStream(newStream, false));
2168
773k
  zone.setInput(newInput);
2169
773k
  zone.m_entry.setBegin(0);
2170
773k
  zone.m_entry.setLength(newInput->size());
2171
773k
  zone.m_extra += "packed,";
2172
773k
  return true;
2173
773k
}
2174
2175
////////////////////////////////////////////////////////////
2176
// read the different zones
2177
////////////////////////////////////////////////////////////
2178
bool RagTime5Document::readDocumentVersion(RagTime5Zone &zone)
2179
0
{
2180
0
  MWAWInputStreamPtr input = zone.getInput();
2181
0
  MWAWEntry &entry=zone.m_entry;
2182
2183
0
  zone.m_isParsed=true;
2184
0
  ascii().addPos(zone.m_defPosition);
2185
0
  ascii().addNote("doc[version],");
2186
2187
0
  libmwaw::DebugFile &ascFile=zone.ascii();
2188
0
  libmwaw::DebugStream f;
2189
0
  f << "Entries(DocVersion):";
2190
0
  ascFile.addPos(entry.end());
2191
0
  ascFile.addNote("_");
2192
0
  if ((entry.length())%6!=2) {
2193
0
    MWAW_DEBUG_MSG(("RagTime5Document::readDocumentVersion: the entry size seem bads\n"));
2194
0
    f << "###";
2195
0
    ascFile.addPos(entry.begin());
2196
0
    ascFile.addNote(f.str().c_str());
2197
0
    return true;
2198
0
  }
2199
0
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2200
0
  auto val=static_cast<int>(input->readLong(1)); // find 2-4
2201
0
  f << "f0=" << val << ",";
2202
0
  val=static_cast<int>(input->readLong(1)); // always 0
2203
0
  if (val)
2204
0
    f << "f1=" << val << ",";
2205
0
  auto N=int(entry.length()/6);
2206
0
  for (int i=0; i<N; ++i) {
2207
    // v0: last used version, v1: first used version, ... ?
2208
0
    f << "v" << i << "=" << input->readLong(1);
2209
0
    val = static_cast<int>(input->readULong(1));
2210
0
    if (val)
2211
0
      f << "." << val;
2212
0
    val = static_cast<int>(input->readULong(1)); // 20|60|80
2213
0
    if (val != 0x80)
2214
0
      f << ":" << std::hex << val << std::dec;
2215
0
    for (int j=0; j<3; ++j) { // often 0 or small number
2216
0
      val = static_cast<int>(input->readULong(1));
2217
0
      if (val)
2218
0
        f << ":" << val << "[" << j << "]";
2219
0
    }
2220
0
    f << ",";
2221
0
  }
2222
0
  ascFile.addPos(entry.begin());
2223
0
  ascFile.addNote(f.str().c_str());
2224
0
  return true;
2225
0
}
2226
2227
////////////////////////////////////////////////////////////
2228
// find the different zones in a OLE1 struct files
2229
////////////////////////////////////////////////////////////
2230
bool RagTime5Document::findZones(MWAWEntry const &entry)
2231
63.1k
{
2232
63.1k
  libmwaw::DebugStream f;
2233
63.1k
  MWAWInputStreamPtr input = m_parser->getInput();
2234
63.1k
  long pos=entry.begin();
2235
63.1k
  if (!input->checkPosition(entry.end())) {
2236
543
    MWAW_DEBUG_MSG(("RagTime5Document::findZones: main entry seems too bad\n"));
2237
543
    f << "###";
2238
543
    ascii().addPos(pos);
2239
543
    ascii().addNote(f.str().c_str());
2240
543
    return false;
2241
543
  }
2242
2243
62.6k
  int n=0;
2244
62.6k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
2245
2246
62.6k
  std::shared_ptr<RagTime5Zone> actualZone, actualChildZone; // the actual zone
2247
4.82M
  while (!input->isEnd()) {
2248
4.82M
    pos=input->tell();
2249
4.82M
    if (pos>=entry.end()) break;
2250
4.81M
    auto level=static_cast<int>(input->readULong(1));
2251
4.81M
    if (level==0x18) {
2252
586k
      while (input->tell()<entry.end()) {
2253
578k
        if (input->readULong(1)==0xFF)
2254
492k
          continue;
2255
85.3k
        input->seek(-1, librevenge::RVNG_SEEK_CUR);
2256
85.3k
        break;
2257
578k
      }
2258
93.3k
      ascii().addPos(pos);
2259
93.3k
      ascii().addNote("_");
2260
93.3k
      continue;
2261
93.3k
    }
2262
4.72M
    f.str("");
2263
    // create a new zone, set the default input and default ascii file
2264
4.72M
    std::shared_ptr<RagTime5Zone> zone(new RagTime5Zone(input, ascii()));
2265
4.72M
    zone->m_defPosition=pos;
2266
4.72M
    zone->m_level=level;
2267
    // level=3: 0001, 59-78 + sometimes g4=[_,1]
2268
4.72M
    if (pos+4>entry.end() || level < 1 || level > 3) {
2269
24.5k
      zone->m_extra=f.str();
2270
24.5k
      if (n++==0)
2271
213
        f << "Entries(Zones)[1]:";
2272
24.3k
      else
2273
24.3k
        f << "Zones-" << n << ":";
2274
24.5k
      f << *zone << "###";
2275
24.5k
      MWAW_DEBUG_MSG(("RagTime5Document::findZones: find unknown level\n"));
2276
24.5k
      ascii().addPos(pos);
2277
24.5k
      ascii().addNote(f.str().c_str());
2278
24.5k
      break;
2279
24.5k
    }
2280
18.2M
    for (int i=0; i<4-level; ++i) {
2281
13.5M
      zone->m_idsFlag[i]=static_cast<int>(input->readULong(2)); // alway 0/1?
2282
13.5M
      zone->m_ids[i]=static_cast<int>(input->readULong(2));
2283
13.5M
    }
2284
4.70M
    bool ok=true;
2285
5.33M
    do {
2286
5.33M
      auto type2=static_cast<int>(input->readULong(1));
2287
5.33M
      switch (type2) {
2288
132k
      case 4: // always 0, 1
2289
133k
      case 0xa: // always 0, 0: never seens in v5 but frequent in v6
2290
140k
      case 0xb: { // find in some pc file
2291
140k
        ok = input->tell()+4+(type2==4 ? 1 : 0)<=entry.end();
2292
140k
        if (!ok) break;
2293
140k
        int data[2];
2294
140k
        for (int &i : data)
2295
280k
          i=static_cast<int>(input->readULong(2));
2296
140k
        if (type2==4) {
2297
132k
          if (data[0]==0 && data[1]==1)
2298
125k
            f << "selected,";
2299
6.99k
          else if (data[0]==0)
2300
1.28k
            f << "#selected=" << data[1] << ",";
2301
5.71k
          else
2302
5.71k
            f << "#selected=[" << data[0] << "," << data[1] << "],";
2303
132k
        }
2304
7.49k
        else
2305
7.49k
          f << "g" << std::hex << type2 << std::dec << "=[" << data[0] << "," << data[1] << "],";
2306
140k
        break;
2307
140k
      }
2308
4.27M
      case 5:
2309
4.74M
      case 6: { // 6 entry followed by other data
2310
4.74M
        ok = input->tell()+8+(type2==6 ? 1 : 0)<=entry.end();
2311
4.74M
        if (!ok) break;
2312
4.73M
        MWAWEntry zEntry;
2313
4.73M
        zEntry.setBegin(long(input->readULong(4)));
2314
4.73M
        zEntry.setLength(long(input->readULong(4)));
2315
4.73M
        zone->m_entriesList.push_back(zEntry);
2316
4.73M
        break;
2317
4.74M
      }
2318
78.2k
      case 9:
2319
78.2k
        ok=input->tell()<=entry.end();
2320
78.2k
        break;
2321
316k
      case 0xd: // always 0 || c000
2322
316k
        ok = input->tell()+4<=entry.end();
2323
316k
        if (!ok) break;
2324
316k
        for (int &i : zone->m_variableD)
2325
632k
          i=static_cast<int>(input->readULong(2));
2326
316k
        break;
2327
32.9k
      case 0x18:
2328
58.1k
        while (input->tell()<entry.end()) {
2329
58.0k
          if (input->readULong(1)==0xFF)
2330
25.2k
            continue;
2331
32.8k
          input->seek(-1, librevenge::RVNG_SEEK_CUR);
2332
32.8k
          break;
2333
58.0k
        }
2334
32.9k
        ok=input->tell()+1<entry.end();
2335
32.9k
        break;
2336
22.9k
      default:
2337
22.9k
        ok=false;
2338
22.9k
        MWAW_DEBUG_MSG(("RagTime5Document::findZones: find unknown type2=%d\n", type2));
2339
22.9k
        f << "type2=" << type2 << ",";
2340
22.9k
        break;
2341
5.33M
      }
2342
5.33M
      if (!ok || (type2&1) || (type2==0xa))
2343
4.70M
        break;
2344
5.33M
    }
2345
4.70M
    while (1);
2346
4.70M
    switch (zone->m_level) {
2347
4.16M
    case 1:
2348
4.16M
      actualZone=zone;
2349
4.16M
      actualChildZone.reset();
2350
4.16M
      break;
2351
523k
    case 2:
2352
523k
      if (!actualZone || actualZone->m_childIdToZoneMap.find(zone->m_ids[0])!=actualZone->m_childIdToZoneMap.end()) {
2353
18.8k
        MWAW_DEBUG_MSG(("RagTime5Document::findZones: can not add child to a zone %d\n", zone->m_ids[0]));
2354
18.8k
        f << "##badChild";
2355
18.8k
      }
2356
504k
      else {
2357
504k
        zone->m_parentName=actualZone->getZoneName();
2358
504k
        actualZone->m_childIdToZoneMap[zone->m_ids[0]]=zone;
2359
504k
      }
2360
523k
      actualChildZone=zone;
2361
523k
      break;
2362
17.5k
    case 3:
2363
17.5k
      if (!actualChildZone || actualChildZone->m_childIdToZoneMap.find(zone->m_ids[0])!=actualChildZone->m_childIdToZoneMap.end()) {
2364
        // checkme: can happen in 6.0 files after a jpeg picture with level 1, ...
2365
10.6k
        MWAW_DEBUG_MSG(("RagTime5Document::findZones: can not add child to a zone %d\n", zone->m_ids[0]));
2366
10.6k
        f << "#noparent";
2367
10.6k
      }
2368
6.87k
      else {
2369
6.87k
        zone->m_parentName=actualChildZone->getZoneName();
2370
6.87k
        actualChildZone->m_childIdToZoneMap[zone->m_ids[0]]=zone;
2371
6.87k
      }
2372
17.5k
      break;
2373
0
    default:
2374
0
      break;
2375
4.70M
    }
2376
2377
    // store 1 level zone (expect the first one which is the main info zone)
2378
4.70M
    if (!m_state->m_zonesList.empty() && zone->m_level==1) {
2379
4.10M
      if (m_state->m_dataIdZoneMap.find(zone->m_ids[0])!=m_state->m_dataIdZoneMap.end()) {
2380
221k
        MWAW_DEBUG_MSG(("RagTime5Document::findZonesKind: data zone with id=%d already exists\n", zone->m_ids[0]));
2381
221k
      }
2382
3.88M
      else
2383
3.88M
        m_state->m_dataIdZoneMap[zone->m_ids[0]]=zone;
2384
4.10M
    }
2385
2386
4.70M
    m_state->m_zonesList.push_back(zone);
2387
4.70M
    zone->m_extra=f.str();
2388
4.70M
    f.str("");
2389
4.70M
    if (n++==0)
2390
62.3k
      f << "Entries(Zones)[1]:";
2391
4.63M
    else
2392
4.63M
      f << "Zones-" << n << ":";
2393
4.70M
    f << *zone;
2394
2395
2396
4.70M
    if (!ok) {
2397
29.4k
      MWAW_DEBUG_MSG(("RagTime5Document::findZones: find unknown data\n"));
2398
29.4k
      f << "###";
2399
29.4k
      if (input->tell()!=pos)
2400
29.4k
        ascii().addDelimiter(input->tell(),'|');
2401
29.4k
      ascii().addPos(pos);
2402
29.4k
      ascii().addNote(f.str().c_str());
2403
29.4k
      break;
2404
29.4k
    }
2405
4.67M
    ascii().addPos(pos);
2406
4.67M
    ascii().addNote(f.str().c_str());
2407
4.67M
  }
2408
62.6k
  return true;
2409
62.6k
}
2410
2411
////////////////////////////////////////////////////////////
2412
//
2413
// Low level
2414
//
2415
////////////////////////////////////////////////////////////
2416
2417
////////////////////////////////////////////////////////////
2418
// color map
2419
////////////////////////////////////////////////////////////
2420
2421
////////////////////////////////////////////////////////////
2422
// read the header
2423
////////////////////////////////////////////////////////////
2424
bool RagTime5Document::checkIsSpreadsheet()
2425
35.6k
{
2426
35.6k
  if (m_state->m_zonesList.empty() && !findZones(m_state->m_zonesEntry))
2427
372
    return false;
2428
35.2k
  if (m_state->m_zonesList.size()<20)
2429
2.35k
    return false;
2430
32.9k
  if (!m_state->m_zonesList[0] || !findZonesKind())
2431
0
    return false;
2432
32.9k
  if (!parseMainZoneInfoData(*m_state->m_zonesList[0]))
2433
0
    return false;
2434
2435
32.9k
  auto dZone=getDataZone(m_state->m_mainClusterId);
2436
32.9k
  if (!dZone)
2437
1.73k
    return false;
2438
31.1k
  updateZone(dZone);
2439
31.1k
  std::shared_ptr<RagTime5ClusterManager::Cluster> cluster=m_clusterManager->readRootCluster(*dZone);
2440
31.1k
  if (!cluster)
2441
9.60k
    return false;
2442
21.5k
  auto root=std::dynamic_pointer_cast<RagTime5ClusterManager::ClusterRoot>(cluster);
2443
21.5k
  if (!root || !root->m_listClusterId)
2444
8.08k
    return false;
2445
13.4k
  auto lZone=getDataZone(root->m_listClusterId);
2446
13.4k
  if (!lZone)
2447
238
    return false;
2448
13.2k
  updateZone(lZone);
2449
13.2k
  if (!lZone || lZone->getKindLastPart(lZone->m_kinds[1].empty())!="ItemData" ||
2450
12.3k
      lZone->m_entry.length()<24 || (lZone->m_entry.length()%8))
2451
3.71k
    return false;
2452
2453
9.52k
  MWAWEntry const &entry=lZone->m_entry;
2454
9.52k
  MWAWInputStreamPtr input=lZone->getInput();
2455
9.52k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2456
9.52k
  input->setReadInverted(!lZone->m_hiLoEndian);
2457
9.52k
  auto N=int(entry.length()/8);
2458
9.52k
  bool firstFound=false;
2459
  // look a file which begins by a spreadsheet and which has no layout, no other spreadsheet, ...
2460
8.73M
  for (int i=0; i<N; ++i) {
2461
8.73M
    long pos=input->tell();
2462
8.73M
    std::vector<int> listIds;
2463
8.73M
    if (!m_structManager->readDataIdList(input, 1, listIds) || listIds.empty() || listIds[0]==0) {
2464
8.67M
      input->seek(pos+8, librevenge::RVNG_SEEK_SET);
2465
8.67M
      continue;
2466
8.67M
    }
2467
53.2k
    auto val=static_cast<int>(input->readULong(2)); // the type
2468
53.2k
    input->seek(2, librevenge::RVNG_SEEK_CUR);
2469
53.2k
    bool needCheck=false;
2470
53.2k
    switch ((val&0xfff3fd7)) {
2471
1.71k
    case 0: // root
2472
1.83k
    case 2: // script
2473
1.84k
    case 0x42: // color pattern
2474
1.85k
    case 0x104: // pipeline
2475
1.94k
    case 0x204: // pipeline
2476
43.5k
    case 0x480: // style
2477
43.5k
      break;
2478
7.27k
    case 1: // layout
2479
7.27k
      return false;
2480
2.43k
    default:
2481
2.43k
      needCheck=true;
2482
2.43k
      break;
2483
53.2k
    }
2484
45.9k
    if (!needCheck) continue;
2485
2.43k
    auto clustZone=getDataZone(listIds[0]);
2486
2.43k
    if (!clustZone)
2487
567
      return false;
2488
1.86k
    updateZone(clustZone);
2489
1.86k
    int type=m_clusterManager->getClusterType(*clustZone, val);
2490
1.86k
    if (type==1) // a layout
2491
0
      return false;
2492
1.86k
    if ((type&0x40000)==0x40000) { // a shape
2493
977
      if (!firstFound) {
2494
977
        if (type!=0x40002) return false; // first is not a spreadsheet
2495
0
        firstFound=true;
2496
0
      }
2497
0
      else if (type==0x40002) // too many spreadsheets
2498
0
        return false;
2499
977
    }
2500
887
    input->seek(pos+8, librevenge::RVNG_SEEK_SET);
2501
887
  }
2502
706
  if (!firstFound)
2503
449
    return false;
2504
257
  return true;
2505
706
}
2506
2507
bool RagTime5Document::checkHeader(MWAWHeader *header, bool strict)
2508
93.7k
{
2509
93.7k
  *m_state = RagTime5DocumentInternal::State();
2510
2511
93.7k
  MWAWInputStreamPtr input = m_parser->getInput();
2512
93.7k
  if (!input || !input->hasDataFork())
2513
0
    return false;
2514
2515
93.7k
  libmwaw::DebugStream f;
2516
93.7k
  f << "FileHeader:";
2517
93.7k
  if (!input->checkPosition(32)) {
2518
184
    MWAW_DEBUG_MSG(("RagTime5Document::checkHeader: file is too short\n"));
2519
184
    return false;
2520
184
  }
2521
93.5k
  input->seek(0,librevenge::RVNG_SEEK_SET);
2522
93.5k
  if (input->readULong(4)!=0x43232b44 || input->readULong(4)!=0xa4434da5
2523
93.5k
      || input->readULong(4)!=0x486472d7)
2524
558
    return false;
2525
93.0k
  int val;
2526
372k
  for (int i=0; i<3; ++i) {
2527
279k
    val=static_cast<int>(input->readLong(2));
2528
279k
    if (val!=i) f << "f" << i << "=" << val << ",";
2529
279k
  }
2530
93.0k
  val=static_cast<int>(input->readLong(2)); // always 0?
2531
93.0k
  if (val) f << "f3=" << val << ",";
2532
93.0k
  m_state->m_zonesEntry.setBegin(long(input->readULong(4)));
2533
93.0k
  m_state->m_zonesEntry.setLength(long(input->readULong(4)));
2534
93.0k
  if (m_state->m_zonesEntry.length()<137 ||
2535
92.9k
      !input->checkPosition(m_state->m_zonesEntry.begin()+137))
2536
1.93k
    return false;
2537
91.0k
  if (strict && !input->checkPosition(m_state->m_zonesEntry.end()))
2538
380
    return false;
2539
90.7k
  val=static_cast<int>(input->readLong(1));
2540
90.7k
  if (val==1)
2541
1.77k
    f << "compacted,";
2542
88.9k
  else if (val)
2543
6.36k
    f << "g0=" << val << ",";
2544
90.7k
  val=static_cast<int>(input->readLong(1));
2545
90.7k
  setVersion(5);
2546
90.7k
  switch (val) {
2547
83.2k
  case 0:
2548
83.2k
    f << "vers=5,";
2549
83.2k
    break;
2550
92
  case 4:
2551
92
    f << "vers=6.5,";
2552
92
    setVersion(6);
2553
92
    break;
2554
7.39k
  default:
2555
7.39k
    f << "#vers=" << val << ",";
2556
7.39k
    break;
2557
90.7k
  }
2558
272k
  for (int i=0; i<2; ++i) {
2559
181k
    val=static_cast<int>(input->readLong(1));
2560
181k
    if (val) f << "g" << i+1 << "=" << val << ",";
2561
181k
  }
2562
  // ok, we can finish initialization
2563
90.7k
  if (header) {
2564
35.6k
    bool isSpreadsheet=checkIsSpreadsheet();
2565
35.6k
    header->reset(MWAWDocument::MWAW_T_RAGTIME, version(), isSpreadsheet ? MWAWDocument::MWAW_K_SPREADSHEET : MWAWDocument::MWAW_K_TEXT);
2566
35.6k
  }
2567
90.7k
  ascii().addPos(0);
2568
90.7k
  ascii().addNote(f.str().c_str());
2569
2570
90.7k
  ascii().addPos(input->tell());
2571
90.7k
  ascii().addNote("_");
2572
2573
90.7k
  return true;
2574
90.7k
}
2575
2576
////////////////////////////////////////////////////////////
2577
// send data to the listener
2578
////////////////////////////////////////////////////////////
2579
2580
bool RagTime5Document::sendZones(MWAWListenerPtr listener)
2581
24.7k
{
2582
24.7k
  if (!listener) {
2583
0
    MWAW_DEBUG_MSG(("RagTime5Document::sendZones: can not find the listener\n"));
2584
0
    return false;
2585
0
  }
2586
24.7k
  if (m_state->m_hasLayout)
2587
9.61k
    m_layoutParser->sendPageContents();
2588
15.0k
  else {
2589
15.0k
    MWAW_DEBUG_MSG(("RagTime5Document::sendZones: no layout, try to send the main zones\n"));
2590
15.0k
    m_clusterManager->sendClusterMainList();
2591
15.0k
  }
2592
24.7k
  return true;
2593
24.7k
}
2594
2595
bool RagTime5Document::sendSpreadsheet(MWAWListenerPtr listener)
2596
0
{
2597
0
  if (!listener) {
2598
0
    MWAW_DEBUG_MSG(("RagTime5Document::sendSpreadsheet: can not find the listener\n"));
2599
0
    return false;
2600
0
  }
2601
0
  std::vector<int> sheetIds=m_spreadsheetParser->getSheetIdList();
2602
0
  if (sheetIds.size()!=1) {
2603
0
    MWAW_DEBUG_MSG(("RagTime5Document::sendSpreadsheet: Oops, %d spreadsheets exist\n", int(sheetIds.size())));
2604
0
    return false;
2605
0
  }
2606
0
  return send(sheetIds[0], listener, MWAWPosition());
2607
0
}
2608
2609
bool RagTime5Document::send(int zoneId, MWAWListenerPtr listener, MWAWPosition const &pos, int partId, int cellId, double totalWidth)
2610
30.6k
{
2611
30.6k
  if (m_state->m_sendZoneSet.find(zoneId)!=m_state->m_sendZoneSet.end()) {
2612
0
    MWAW_DEBUG_MSG(("RagTime5Document::send: argh zone %d is already in the sent set\n", zoneId));
2613
0
    return false;
2614
0
  }
2615
2616
30.6k
  m_state->m_sendZoneSet.insert(zoneId);
2617
30.6k
  auto type=m_clusterManager->getClusterType(zoneId);
2618
30.6k
  bool ok=false;
2619
30.6k
  if (type==RagTime5ClusterManager::Cluster::C_ButtonZone || type==RagTime5ClusterManager::Cluster::C_GraphicZone || type==RagTime5ClusterManager::Cluster::C_PictureZone)
2620
13.2k
    ok=m_graphParser->send(zoneId, listener, pos);
2621
17.4k
  else if (type==RagTime5ClusterManager::Cluster::C_TextZone)
2622
8.33k
    ok=m_textParser->send(zoneId, listener, partId, cellId, totalWidth);
2623
9.11k
  else if (type==RagTime5ClusterManager::Cluster::C_SpreadsheetZone)
2624
4.14k
    ok=m_spreadsheetParser->send(zoneId, listener, pos, partId);
2625
4.97k
  else if (type==RagTime5ClusterManager::Cluster::C_Pipeline)
2626
4.27k
    ok=m_pipelineParser->send(zoneId, listener, pos, partId, totalWidth);
2627
30.6k
  m_state->m_sendZoneSet.erase(zoneId);
2628
30.6k
  if (ok)
2629
24.7k
    return true;
2630
5.95k
  static bool first=true;
2631
5.95k
  if (first) {
2632
25
    MWAW_DEBUG_MSG(("RagTime5Document::send: not fully implemented\n"));
2633
25
    first=false;
2634
25
  }
2635
5.95k
  return false;
2636
30.6k
}
2637
2638
void RagTime5Document::flushExtra(MWAWListenerPtr listener, bool onlyCheck)
2639
0
{
2640
0
  if (!listener) {
2641
0
    MWAW_DEBUG_MSG(("RagTime5Document::flushExtra: can not find the listener\n"));
2642
0
    return;
2643
0
  }
2644
0
  m_textParser->flushExtra(onlyCheck);
2645
0
  m_graphParser->flushExtra(onlyCheck);
2646
0
  m_spreadsheetParser->flushExtra(onlyCheck);
2647
2648
  // look for unparsed data
2649
0
  int notRead=0;
2650
0
  for (auto const &zone : m_state->m_zonesList) {
2651
0
    if (!zone || zone->m_isParsed || !zone->m_entry.valid())
2652
0
      continue;
2653
0
    ascii().addPos(zone->m_defPosition);
2654
0
    ascii().addNote("[notParsed]");
2655
0
    readZoneData(*zone);
2656
0
    ++notRead;
2657
0
  }
2658
0
  if (notRead) {
2659
0
    MWAW_DEBUG_MSG(("RagTime5Document::flushExtra: find %d/%d unparsed data\n", notRead, static_cast<int>(m_state->m_zonesList.size())));
2660
0
  }
2661
0
}
2662
2663
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: