Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/GreatWksDBParser.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 <iomanip>
36
#include <iostream>
37
#include <cmath>
38
#include <limits>
39
#include <set>
40
#include <sstream>
41
42
#include <librevenge/librevenge.h>
43
44
#include "MWAWFont.hxx"
45
#include "MWAWFontConverter.hxx"
46
#include "MWAWGraphicStyle.hxx"
47
#include "MWAWHeader.hxx"
48
#include "MWAWParagraph.hxx"
49
#include "MWAWPosition.hxx"
50
#include "MWAWPrinter.hxx"
51
#include "MWAWRSRCParser.hxx"
52
#include "MWAWSection.hxx"
53
#include "MWAWSpreadsheetListener.hxx"
54
#include "MWAWSubDocument.hxx"
55
56
#include "GreatWksDocument.hxx"
57
#include "GreatWksGraph.hxx"
58
#include "GreatWksText.hxx"
59
60
#include "GreatWksDBParser.hxx"
61
62
/** Internal: the structures of a GreatWksDBParser */
63
namespace GreatWksDBParserInternal
64
{
65
/**a big zone header of a GreatWksDBParser */
66
struct BlockHeader {
67
  /** constructor */
68
  BlockHeader()
69
64.9M
    : m_name("")
70
64.9M
  {
71
194M
    for (auto &ptr : m_ptr) ptr=0;
72
64.9M
  }
73
  /** returns true if the block is empty */
74
  bool isEmpty() const
75
35.9M
  {
76
103M
    for (auto ptr : m_ptr) if (ptr) return false;
77
33.4M
    return true;
78
35.9M
  }
79
  /** returns true if the entry has many block */
80
  bool isComplex() const
81
12.6k
  {
82
12.6k
    return m_ptr[0]!=m_ptr[1] || m_ptr[0]!=m_ptr[2];
83
12.6k
  }
84
  //! operator<<
85
  friend std::ostream &operator<<(std::ostream &o, BlockHeader const &entry)
86
0
  {
87
0
    o << std::hex << entry.m_ptr[0];
88
0
    if (entry.isComplex()) o << "[" << entry.m_ptr[1] << "," << entry.m_ptr[2] << "]";
89
0
    o << std::dec;
90
0
    if (!entry.m_name.empty()) o << ":" << entry.m_name;
91
0
    return o;
92
0
  }
93
94
  /** the list of pointer: first, next, end? */
95
  long m_ptr[3];
96
  /** the block name */
97
  std::string m_name;
98
};
99
100
/** a big block of a GreatWksDBParser */
101
struct Block {
102
  //! a small block of a GreatWksDBParserInternal::Block
103
  struct Zone {
104
    /** constructor */
105
    Zone()
106
52.7k
      : m_ptr(0)
107
52.7k
      , m_N(0)
108
52.7k
      , m_dataSize(0)
109
52.7k
    {
110
52.7k
    }
111
    /** the begin zone pointer */
112
    long m_ptr;
113
    /** the number of sub data */
114
    int m_N;
115
    /** the data size */
116
    long m_dataSize;
117
  };
118
  //! constructor
119
  explicit Block(BlockHeader const &header)
120
51.9k
    : m_header(header)
121
51.9k
    , m_zoneList()
122
51.9k
  {
123
51.9k
  }
124
  //! constructor given a zone
125
  Block(BlockHeader const &header, Zone &zone)
126
    : m_header(header)
127
    , m_zoneList()
128
0
  {
129
0
    m_zoneList.push_back(zone);
130
0
  }
131
  //! destructor
132
  ~Block()
133
51.9k
  {
134
51.9k
  }
135
  //! returns true if the zone list is empty
136
  bool isEmpty() const
137
0
  {
138
0
    return m_zoneList.empty();
139
0
  }
140
  //! returns the number of zone
141
  size_t getNumZones() const
142
102k
  {
143
102k
    return m_zoneList.size();
144
102k
  }
145
  //! returns the ith zone
146
  Zone const &getZone(size_t i) const
147
50.3k
  {
148
50.3k
    if (i>=m_zoneList.size()) {
149
0
      MWAW_DEBUG_MSG(("GreatWksDBParserInternal::Block: can not find zone %d\n", int(i)));
150
0
      static Zone empty;
151
0
      return empty;
152
0
    }
153
50.3k
    return m_zoneList[i];
154
50.3k
  }
155
  //! the corresponding entry header
156
  BlockHeader m_header;
157
  //! the zone list
158
  std::vector<Zone> m_zoneList;
159
private:
160
  Block(Block const &orig) = delete;
161
  Block &operator=(Block const &orig) = delete;
162
};
163
164
/** a cell of a GreatWksDBParser */
165
class Cell final : public MWAWCell
166
{
167
public:
168
  /// constructor
169
  Cell()
170
400M
    : m_content()
171
400M
    , m_pictEntry() { }
172
0
  Cell(Cell const &)=default;
173
198M
  Cell &operator=(Cell const &)=default;
174
  //! destructor
175
  ~Cell() final;
176
  //! returns true if the cell do contain any content
177
  bool isEmpty() const
178
197M
  {
179
197M
    return m_content.empty() && !hasBorders() && !m_pictEntry.valid();
180
197M
  }
181
182
  //! the cell content
183
  MWAWCellContent m_content;
184
  //! picture
185
  MWAWEntry m_pictEntry;
186
};
187
188
Cell::~Cell()
189
400M
{
190
400M
}
191
192
/** a field of a GreatWksDBParser */
193
struct Field {
194
  //! the file type
195
  enum Type { F_Unknown, F_Text, F_Number, F_Date, F_Time, F_Memo, F_Picture, F_Formula, F_Summary };
196
  //! constructor
197
  Field()
198
64.9M
    : m_type(F_Unknown)
199
64.9M
    , m_id(-1)
200
64.9M
    , m_name("")
201
64.9M
    , m_format()
202
64.9M
    , m_linkZone(0)
203
64.9M
    , m_recordBlock()
204
64.9M
    , m_formula()
205
64.9M
    , m_summaryType(0)
206
64.9M
    , m_summaryField(0)
207
64.9M
    , m_isSequence(false)
208
64.9M
    , m_firstNumber(1)
209
64.9M
    , m_incrementNumber(1)
210
64.9M
    , m_extra("")
211
64.9M
  {
212
64.9M
  }
213
  //! update the cell to correspond to the final data
214
  bool updateCell(int row, int numRow, Cell &cell) const;
215
  //! operator<<
216
  friend std::ostream &operator<<(std::ostream &o, Field const &field);
217
  //! the field type
218
  Type m_type;
219
  //! the field id
220
  int m_id;
221
  //! the field name
222
  std::string m_name;
223
  //! the field format
224
  MWAWCell::Format m_format;
225
  //! the file position which stores the position link to record zone
226
  long m_linkZone;
227
  //! the block file position which stores the position of the field's record
228
  BlockHeader m_recordBlock;
229
230
  // formula
231
232
  //! the formula
233
  std::vector<MWAWCellContent::FormulaInstruction> m_formula;
234
235
  //! the summary type: 1:average, 2:count, 3:total, 4:minimum, 5:maximum
236
  int m_summaryType;
237
  //! the summary field
238
  int m_summaryField;
239
240
  // sequence (or unique val)
241
242
  //! true if the number is a sequence
243
  bool m_isSequence;
244
  //! the first number (in case of progression sequence)
245
  int m_firstNumber;
246
  //! the increment number (in case of progression sequence)
247
  int m_incrementNumber;
248
249
  //! extra data
250
  std::string m_extra;
251
};
252
253
bool Field::updateCell(int row, int numRow, Cell &cell) const
254
197M
{
255
197M
  std::vector<MWAWCellContent::FormulaInstruction> &formula=cell.m_content.m_formula;
256
197M
  if (m_type==F_Formula) {
257
183k
    if (m_formula.size()==0)
258
178k
      return false;
259
5.18k
    cell.m_content.m_contentType=MWAWCellContent::C_FORMULA;
260
5.18k
    formula=m_formula;
261
5.18k
  }
262
197M
  else if (m_isSequence && !cell.m_content.isValueSet()) {
263
445k
    cell.m_content.m_contentType=MWAWCellContent::C_NUMBER;
264
445k
    cell.m_content.setValue(double(m_firstNumber+row *m_incrementNumber));
265
445k
  }
266
196M
  else if (m_summaryType>0 && m_summaryType<6) {
267
8.38k
    cell.m_content.m_contentType=MWAWCellContent::C_FORMULA;
268
8.38k
    MWAWCellContent::FormulaInstruction instr;
269
8.38k
    instr.m_type=MWAWCellContent::FormulaInstruction::F_Function;
270
8.38k
    char const *wh[]= {"Average", "Count", "Sum", "Min", "Max"};
271
8.38k
    instr.m_content=wh[m_summaryType-1];
272
8.38k
    formula.push_back(instr);
273
8.38k
    instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
274
8.38k
    instr.m_content="(";
275
8.38k
    formula.push_back(instr);
276
8.38k
    instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList;
277
8.38k
    instr.m_position[0][0]=instr.m_position[1][0]=m_summaryField;
278
8.38k
    instr.m_position[0][1]=0;
279
8.38k
    instr.m_position[1][1]=numRow-1;
280
8.38k
    instr.m_positionRelative[0]=instr.m_positionRelative[1]=MWAWVec2b(false,false);
281
8.38k
    formula.push_back(instr);
282
8.38k
    instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
283
8.38k
    instr.m_content=")";
284
8.38k
    formula.push_back(instr);
285
8.38k
    return true;
286
8.38k
  }
287
  // change the reference date from 1/1/1904 to 1/1/1900
288
197M
  if (m_type==F_Date && cell.m_content.isValueSet())
289
2.93k
    cell.m_content.setValue(cell.m_content.m_value+1460.);
290
  // and try to update the 1D formula in 2D
291
197M
  for (auto &instr : formula) {
292
66.9k
    if (instr.m_type==MWAWCellContent::FormulaInstruction::F_Cell)
293
0
      instr.m_position[0][1]=row;
294
66.9k
    else if (instr.m_type==MWAWCellContent::FormulaInstruction::F_CellList)
295
0
      instr.m_position[0][1]=instr.m_position[1][1]=row;
296
66.9k
  }
297
197M
  return true;
298
197M
}
299
300
std::ostream &operator<<(std::ostream &o, Field const &field)
301
0
{
302
0
  switch (field.m_type) {
303
0
  case Field::F_Text:
304
0
    o << "text,";
305
0
    break;
306
0
  case Field::F_Number:
307
0
    o << "number,";
308
0
    break;
309
0
  case Field::F_Date:
310
0
    o << "date,";
311
0
    break;
312
0
  case Field::F_Time:
313
0
    o << "time,";
314
0
    break;
315
0
  case Field::F_Picture:
316
0
    o << "picture,";
317
0
    break;
318
0
  case Field::F_Memo:
319
0
    o << "memo,";
320
0
    break;
321
0
  case Field::F_Formula:
322
0
    o << "formula,";
323
0
    break;
324
0
  case Field::F_Summary:
325
0
    o << "summary,";
326
0
    break;
327
0
  case Field::F_Unknown:
328
0
    break;
329
#if !defined(__clang__)
330
  default:
331
    MWAW_DEBUG_MSG(("GreatWksDBParser::Field:operator<<: find unexpected type\n"));
332
    break;
333
#endif
334
0
  }
335
0
  if (field.m_id>=0) o << "id=" << field.m_id << ",";
336
0
  if (!field.m_name.empty()) o << "name=" << field.m_name << ",";
337
0
  if (field.m_linkZone>0) o << "zone[link]=" << std::hex << field.m_linkZone << std::dec << ",";
338
0
  if (!field.m_recordBlock.isEmpty()) o << "zone[record]=" << field.m_recordBlock << ",";
339
0
  o << field.m_extra;
340
0
  return o;
341
0
}
342
343
/** the database of a GreatWksDBParser */
344
class Database
345
{
346
public:
347
  //! constructor
348
  Database()
349
29.8k
    : m_numRecords(0)
350
29.8k
    , m_rowList()
351
29.8k
    , m_fieldList()
352
29.8k
    , m_widthDefault(75)
353
29.8k
    , m_widthCols()
354
29.8k
    , m_heightDefault(13)
355
29.8k
    , m_heightRows()
356
29.8k
    , m_rowCellsMap()
357
29.8k
    , m_name("Sheet0")
358
29.8k
  {
359
29.8k
  }
360
  //! add a cell data in one given position
361
  bool addCell(MWAWVec2i const &pos, Cell const &cell)
362
5.13M
  {
363
5.13M
    if (pos[0]<0 || pos[0]>=int(m_fieldList.size()) || pos[1]<0) {
364
4.51M
      MWAW_DEBUG_MSG(("GreatWksDBParserInternal::Database::addCell: the cell position seems bad\n"));
365
4.51M
      return false;
366
4.51M
    }
367
620k
    if (m_rowCellsMap.find(pos[1])==m_rowCellsMap.end())
368
127k
      m_rowCellsMap[pos[1]]=std::vector<Cell>();
369
620k
    auto &cells=m_rowCellsMap.find(pos[1])->second;
370
620k
    if (pos[0]>=static_cast<int>(cells.size()))
371
127k
      cells.resize(m_fieldList.size());
372
620k
    cells[size_t(pos[0])]=cell;
373
620k
    return true;
374
5.13M
  }
375
  //! returns the row size in point
376
  int getRowHeight(int row) const
377
0
  {
378
0
    if (row>=0&&row<static_cast<int>(m_heightRows.size()))
379
0
      return m_heightRows[size_t(row)];
380
0
    return m_heightDefault;
381
0
  }
382
  //! convert the m_widthCols in a vector of of point size
383
  std::vector<float> convertInPoint(std::vector<int> const &list) const
384
0
  {
385
0
    size_t numCols=m_fieldList.size();
386
0
    std::vector<float> res;
387
0
    res.resize(numCols);
388
0
    for (size_t i = 0; i < numCols; i++) {
389
0
      if (i>=list.size() || list[i] < 0) res[i] = float(m_widthDefault);
390
0
      else res[i] = float(list[i]);
391
0
    }
392
0
    return res;
393
0
  }
394
  //! the number of records
395
  int m_numRecords;
396
  //! the list of rows data
397
  std::vector<MWAWEntry> m_rowList;
398
  //! the list of field
399
  std::vector<Field> m_fieldList;
400
  /** the default column width */
401
  int m_widthDefault;
402
  /** the column size in points */
403
  std::vector<int> m_widthCols;
404
  /** the default row height */
405
  int m_heightDefault;
406
  /** the row height in points */
407
  std::vector<int> m_heightRows;
408
  /** the map row -> list of cells */
409
  std::map<int, std::vector<Cell> > m_rowCellsMap;
410
  /** the database name */
411
  std::string m_name;
412
protected:
413
};
414
415
////////////////////////////////////////
416
//! Internal: the state of a GreatWksDBParser
417
struct State {
418
  //! constructor
419
  State()
420
29.8k
    : m_database()
421
29.8k
    , m_idZonesMap()
422
29.8k
    , m_blocks()
423
29.8k
    , m_actPage(0)
424
29.8k
    , m_numPages(0)
425
29.8k
    , m_headerBlockHeader()
426
29.8k
    , m_footerBlockHeader()
427
29.8k
    , m_headerPrint(false)
428
29.8k
    , m_footerPrint(false)
429
29.8k
    , m_headerHeight(0)
430
29.8k
    , m_footerHeight(0)
431
29.8k
  {
432
29.8k
  }
433
  /** the database */
434
  Database m_database;
435
  /** a map id to zone used to stored the small zones */
436
  std::map<int, MWAWEntry> m_idZonesMap;
437
  /** the list of big blocks */
438
  std::vector<BlockHeader> m_blocks;
439
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
440
441
  MWAWEntry m_headerBlockHeader /** the header entry (in v2)*/, m_footerBlockHeader/**the footer entry (in v2)*/;
442
  bool m_headerPrint /** the header is printed */, m_footerPrint /* the footer is printed */;
443
  int m_headerHeight /** the header height if known */,
444
      m_footerHeight /** the footer height if known */;
445
};
446
447
////////////////////////////////////////
448
//! Internal: the subdocument of a GreatWksDBParser
449
class SubDocument final : public MWAWSubDocument
450
{
451
public:
452
  SubDocument(GreatWksDBParser &pars, MWAWInputStreamPtr const &input, int zoneId)
453
0
    : MWAWSubDocument(&pars, input, MWAWEntry())
454
0
    , m_id(zoneId) {}
455
456
  //! destructor
457
0
  ~SubDocument() final {}
458
459
  //! operator!=
460
  bool operator!=(MWAWSubDocument const &doc) const final;
461
462
  //! the parser function
463
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
464
465
protected:
466
  //! the subdocument id
467
  int m_id;
468
};
469
470
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type)
471
0
{
472
0
  if (!listener.get()) {
473
0
    MWAW_DEBUG_MSG(("GreatWksDBParserInternal::SubDocument::parse: no listener\n"));
474
0
    return;
475
0
  }
476
0
  if (type!=libmwaw::DOC_HEADER_FOOTER) {
477
0
    MWAW_DEBUG_MSG(("GreatWksDBParserInternal::SubDocument::parse: unknown type\n"));
478
0
    return;
479
0
  }
480
481
0
  auto *parser=dynamic_cast<GreatWksDBParser *>(m_parser);
482
0
  if (!parser) {
483
0
    MWAW_DEBUG_MSG(("GreatWksDBParserInternal::SubDocument::parse: no parser\n"));
484
0
    return;
485
0
  }
486
0
  long pos = m_input->tell();
487
0
  parser->sendHF(m_id);
488
0
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
489
0
}
490
491
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
492
0
{
493
0
  if (MWAWSubDocument::operator!=(doc)) return true;
494
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
495
0
  if (!sDoc) return true;
496
0
  if (m_id != sDoc->m_id) return true;
497
0
  return false;
498
0
}
499
}
500
501
////////////////////////////////////////////////////////////
502
// constructor/destructor, ...
503
////////////////////////////////////////////////////////////
504
GreatWksDBParser::GreatWksDBParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
505
12.8k
  : MWAWSpreadsheetParser(input, rsrcParser, header)
506
12.8k
  , m_state()
507
12.8k
  , m_document()
508
12.8k
{
509
12.8k
  init();
510
12.8k
}
511
512
GreatWksDBParser::~GreatWksDBParser()
513
12.8k
{
514
12.8k
}
515
516
void GreatWksDBParser::init()
517
12.8k
{
518
12.8k
  resetSpreadsheetListener();
519
12.8k
  setAsciiName("main-1");
520
521
12.8k
  m_state.reset(new GreatWksDBParserInternal::State);
522
523
  // reduce the margin (in case, the page is not defined)
524
12.8k
  getPageSpan().setMargins(0.1);
525
526
12.8k
  m_document.reset(new GreatWksDocument(*this));
527
12.8k
}
528
529
////////////////////////////////////////////////////////////
530
// interface with the text parser
531
////////////////////////////////////////////////////////////
532
bool GreatWksDBParser::sendHF(int id)
533
0
{
534
0
  return m_document->getTextParser()->sendTextbox(id==0 ? m_state->m_headerBlockHeader : m_state->m_footerBlockHeader);
535
0
}
536
537
////////////////////////////////////////////////////////////
538
// the parser
539
////////////////////////////////////////////////////////////
540
void GreatWksDBParser::parse(librevenge::RVNGSpreadsheetInterface *docInterface)
541
4.18k
{
542
4.18k
  if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
543
4.18k
  bool ok = false;
544
4.18k
  try {
545
    // create the asciiFile
546
4.18k
    ascii().setStream(getInput());
547
4.18k
    ascii().open(asciiName());
548
549
4.18k
    checkHeader(nullptr);
550
4.18k
    ok = createZones();
551
4.18k
    if (ok) {
552
1.77k
      createDocument(docInterface);
553
1.77k
      sendDatabase();
554
1.77k
    }
555
4.18k
    ascii().reset();
556
4.18k
  }
557
4.18k
  catch (...) {
558
0
    MWAW_DEBUG_MSG(("GreatWksDBParser::parse: exception catched when parsing\n"));
559
0
    ok = false;
560
0
  }
561
562
4.18k
  resetSpreadsheetListener();
563
4.18k
  if (!ok) throw(libmwaw::ParseException());
564
4.18k
}
565
566
////////////////////////////////////////////////////////////
567
// create the document
568
////////////////////////////////////////////////////////////
569
void GreatWksDBParser::createDocument(librevenge::RVNGSpreadsheetInterface *documentInterface)
570
1.77k
{
571
1.77k
  if (!documentInterface) return;
572
1.77k
  if (getSpreadsheetListener()) {
573
0
    MWAW_DEBUG_MSG(("GreatWksDBParser::createDocument: listener already exist\n"));
574
0
    return;
575
0
  }
576
577
  // update the page
578
1.77k
  m_state->m_actPage = 0;
579
580
  // create the page list
581
1.77k
  int numPages = 1;
582
1.77k
  if (m_document->getGraphParser()->numPages() > numPages)
583
0
    numPages = m_document->getGraphParser()->numPages();
584
1.77k
  if (m_document->getTextParser()->numPages() > numPages)
585
0
    numPages = m_document->getTextParser()->numPages();
586
1.77k
  m_state->m_numPages = numPages;
587
588
1.77k
  MWAWPageSpan ps(getPageSpan());
589
1.77k
  std::vector<MWAWPageSpan> pageList;
590
1.77k
  if (m_state->m_headerBlockHeader.valid()) {
591
0
    MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
592
0
    header.m_subDocument.reset(new GreatWksDBParserInternal::SubDocument(*this, getInput(), 0));
593
0
    ps.setHeaderFooter(header);
594
0
  }
595
1.77k
  if (m_state->m_footerBlockHeader.valid()) {
596
0
    MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
597
0
    footer.m_subDocument.reset(new GreatWksDBParserInternal::SubDocument(*this, getInput(), 1));
598
0
    ps.setHeaderFooter(footer);
599
0
  }
600
1.77k
  ps.setPageSpan(numPages);
601
1.77k
  pageList.push_back(ps);
602
1.77k
  MWAWSpreadsheetListenerPtr listen(new MWAWSpreadsheetListener(*getParserState(), pageList, documentInterface));
603
1.77k
  setSpreadsheetListener(listen);
604
1.77k
  listen->startDocument();
605
1.77k
}
606
607
////////////////////////////////////////////////////////////
608
//
609
// Intermediate level
610
//
611
////////////////////////////////////////////////////////////
612
bool GreatWksDBParser::createZones()
613
4.18k
{
614
4.18k
  m_document->readRSRCZones();
615
616
4.18k
  if (!readHeader()) return false;
617
618
4.17k
  MWAWInputStreamPtr input = getInput();
619
4.17k
  libmwaw::DebugStream f;
620
621
20.8k
  for (size_t i=0; i< m_state->m_blocks.size() ; ++i) {
622
16.6k
    auto &header = m_state->m_blocks[i];
623
16.6k
    if (header.isEmpty()) continue;
624
16.6k
    auto block=createBlock(header);
625
16.6k
    if (block) {
626
3.93k
      bool ok=false;
627
3.93k
      switch (i) {
628
664
      case 0: // unknown maybe some flag
629
664
        ok = readFreeList(*block);
630
664
        break;
631
2.18k
      case 1: // a list of record<->id
632
2.18k
        ok = readRowLinks(*block);
633
2.18k
        break;
634
401
      case 2: // a list of 0<->id
635
401
        ok = readBlockHeader2(*block);
636
401
        break;
637
685
      case 3: // the record list
638
685
        ok = readRecordList(*block);
639
685
        break;
640
0
      default:
641
0
        break;
642
3.93k
      }
643
3.93k
      if (ok)
644
3.93k
        continue;
645
3.93k
    }
646
12.6k
    f.str("");
647
12.6k
    f << "Entries(" << header.m_name << ")[" << header << "]:";
648
12.6k
    ascii().addPos(input->tell());
649
12.6k
    ascii().addNote(f.str().c_str());
650
12.6k
    if (!header.isComplex()) continue;
651
34.7k
    for (int j=1; j<3; ++j) {
652
23.1k
      if (!header.m_ptr[j]) continue;
653
21.4k
      ascii().addPos(header.m_ptr[j]);
654
21.4k
      ascii().addNote(f.str().c_str());
655
21.4k
    }
656
11.5k
  }
657
658
38.5k
  for (auto const &it : m_state->m_idZonesMap) {
659
38.5k
    MWAWEntry const &entry=it.second;
660
38.5k
    if (!entry.valid() || entry.isParsed()) continue;
661
38.5k
    bool ok=false;
662
38.5k
    switch (entry.id()) {
663
    // zones which will be parsed in readDatabase
664
3.81k
    case 1: // the list of fields
665
4.03k
    case 10: // the link between field and a zone record
666
4.41k
    case 13: // a version 2 zone which seems to contains added data for each fields
667
4.41k
      ok=true;
668
4.41k
      break;
669
841
    case 2:
670
841
      ok=readFormLinks(it.second);
671
841
      break;
672
616
    case 5: // one time with list=[19, 20], list of computed fields ?
673
1.14k
    case 6: // one time with list=[21, 22], list of summary fields ?
674
1.21k
    case 14: { // only in v2, probably linked to Zone2 zones, find [1,1,1] or [2,2]
675
1.21k
      std::vector<int> list;
676
1.21k
      ok=readIntList(it.second, list);
677
1.21k
      if (!ok) break;
678
926
      f.str("");
679
926
      f << "Entries(" << entry.name() << "):";
680
926
      if (list.size()) {
681
33
        f << "lists=[";
682
66
        for (int i : list) f << i << ",";
683
33
        f << "],";
684
33
      }
685
926
      ascii().addPos(entry.begin());
686
926
      ascii().addNote(f.str().c_str());
687
926
      break;
688
1.21k
    }
689
561
    case 7:  // always find with size=0
690
561
      if (entry.length()!=6) break;
691
484
      ok=true;
692
484
      f.str("");
693
484
      f << "Entries(" << entry.name() << "):";
694
484
      ascii().addPos(entry.begin());
695
484
      ascii().addNote(f.str().c_str());
696
484
      ascii().addPos(entry.end());
697
484
      ascii().addNote("_");
698
484
      break;
699
499
    case 12:
700
499
      ok=readZone12(entry);
701
499
      break;
702
    // zone3: never find with data, dSz=3C in v1 and dSz=3e in v2
703
    // zone4: never find with data, dSz=112, seems one time in v2
704
    // zone8: never seems
705
31.0k
    default:
706
31.0k
      ok=readSmallZone(entry);
707
31.0k
      break;
708
38.5k
    }
709
38.5k
    if (ok || entry.isParsed()) continue;
710
3.15k
    f.str("");
711
3.15k
    f << "Entries(" << entry.name() << "):#";
712
3.15k
    ascii().addPos(entry.begin());
713
3.15k
    ascii().addNote(f.str().c_str());
714
3.15k
    ascii().addPos(entry.end());
715
3.15k
    ascii().addNote("_");
716
3.15k
  }
717
718
4.17k
  if (!readDatabase())
719
2.39k
    return false;
720
721
1.77k
  return true;
722
4.17k
}
723
724
////////////////////////////////////////////////////////////
725
// read a database
726
////////////////////////////////////////////////////////////
727
bool GreatWksDBParser::readDatabase()
728
4.17k
{
729
4.17k
  MWAWInputStreamPtr input = getInput();
730
4.17k
  auto it=m_state->m_idZonesMap.find(1);
731
4.17k
  if (it==m_state->m_idZonesMap.end() || !readFields(it->second)) {
732
436
    MWAW_DEBUG_MSG(("GreatWksDBParser::readDatabase: can not find the list of fields\n"));
733
436
    return false;
734
436
  }
735
3.73k
  if (version()==2) {
736
    // zone13: a v2 zone with dSz=46, content seems constant in a file
737
3.27k
    it=m_state->m_idZonesMap.find(13);
738
3.27k
    if (it!=m_state->m_idZonesMap.end()) {
739
381
      if (!readFieldAuxis(it->second))
740
37
        readSmallZone(it->second);
741
381
    }
742
3.27k
  }
743
3.73k
  auto &database=m_state->m_database;
744
  /* First read the fields' records then the rows' records.  As the
745
     rows' records contain more information than the fields' records,
746
     there will replace the field data if we success to read them
747
     (and if not, we will keep the fields' records).
748
   */
749
22.7M
  for (auto &field : database.m_fieldList) {
750
22.7M
    if (!field.m_linkZone) continue;
751
1.07M
    readFieldLinks(field);
752
1.07M
  }
753
3.73k
  for (auto const &row : database.m_rowList)
754
6.66M
    readRowRecords(row);
755
22.7M
  for (auto &field : database.m_fieldList) {
756
22.7M
    if (field.m_recordBlock.isEmpty()) continue;
757
973k
    readFieldRecords(field);
758
973k
  }
759
3.73k
  if (!database.m_rowCellsMap.empty())
760
1.50k
    return true;
761
  // let check if we can reconstruct something
762
12.2M
  for (auto &field : database.m_fieldList) {
763
12.2M
    if (field.m_recordBlock.isEmpty()) continue;
764
553k
    if (field.m_isSequence) return true;
765
552k
    if (field.m_type==GreatWksDBParserInternal::Field::F_Summary) return true;
766
552k
  }
767
1.96k
  MWAW_DEBUG_MSG(("GreatWksDBParser::readDatabase: can not find any cellule\n"));
768
1.96k
  return false;
769
2.23k
}
770
771
////////////////////////////////////////////////////////////
772
// read the file header
773
////////////////////////////////////////////////////////////
774
bool GreatWksDBParser::readHeader()
775
4.18k
{
776
4.18k
  MWAWInputStreamPtr input = getInput();
777
4.18k
  if (!input->checkPosition(0x200))
778
16
    return false;
779
4.17k
  int const vers=version();
780
4.17k
  libmwaw::DebugStream f;
781
782
4.17k
  f << "Entries(HeaderZone):";
783
4.17k
  long pos=16;
784
4.17k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
785
16.6k
  for (int i=0; i<3; ++i) {
786
12.5k
    GreatWksDBParserInternal::BlockHeader block;
787
12.5k
    if (!readBlockHeader(block)) {
788
0
      MWAW_DEBUG_MSG(("GreatWksDBParser::readHeader: can not read a entry\n"));
789
0
      f << "###";
790
0
      ascii().addPos(pos);
791
0
      ascii().addNote(f.str().c_str());
792
0
      return false;
793
0
    }
794
12.5k
    static char const* const wh[]= {"Free", "RecLink", "Block2"};
795
12.5k
    block.m_name=wh[i];
796
12.5k
    m_state->m_blocks.push_back(block);
797
12.5k
    f << block << ",";
798
62.5k
    for (int j=0; j<4; ++j) {
799
50.0k
      auto val=static_cast<int>(input->readULong(2));
800
50.0k
      if (val)
801
34.1k
        f << "f" << j << "[" << i << "]=" << val << ",";
802
50.0k
    }
803
12.5k
  }
804
  // an odd entry block (only 2 ptr), does this means that this zone has a size less than 512?
805
4.17k
  GreatWksDBParserInternal::BlockHeader block;
806
12.5k
  for (int i=0; i<2; ++i) block.m_ptr[i]=long(input->readULong(4));
807
4.17k
  block.m_ptr[2]=block.m_ptr[1];
808
4.17k
  block.m_name="RecList";
809
4.17k
  m_state->m_blocks.push_back(block);
810
4.17k
  f << block << ",";
811
812
4.17k
  auto val=static_cast<int>(input->readLong(2)); // always 0
813
4.17k
  if (val) f << "f4=" << val << ",";
814
4.17k
  auto numRecords=static_cast<int>(input->readLong(2));
815
4.17k
  m_state->m_database.m_numRecords=numRecords;
816
4.17k
  f << "num[records]=" << numRecords << ",";
817
70.8k
  for (int i=0; i<16; ++i) { // find h1=h9=h11=h13=numRecords, h3=h7=1|11, h14=2, h15=1
818
66.7k
    val=static_cast<int>(input->readLong(2));
819
66.7k
    if (i==1 || i==9 || i==11 || i==13) {
820
16.6k
      if (val==numRecords) continue;
821
14.2k
      if (i==9) f << "act[record]=" << val << ",";
822
10.3k
      else f << "h" << i << "=" << val << ",";
823
14.2k
    }
824
50.0k
    else if (val) f << "h" << i << "=" << val << ",";
825
66.7k
  }
826
4.17k
  ascii().addPos(pos);
827
4.17k
  ascii().addNote(f.str().c_str());
828
829
4.17k
  pos=input->tell();
830
4.17k
  f.str("");
831
4.17k
  f << "Entries(Zones):";
832
4.17k
  std::vector<MWAWEntry> zones;
833
41.7k
  for (int i=0; i<9; ++i) {
834
37.5k
    val=static_cast<int>(input->readLong(2));
835
37.5k
    if (val) f << "f" << i << "=" << val << ",";
836
37.5k
    auto ptr=long(input->readULong(2));
837
37.5k
    MWAWEntry entry;
838
37.5k
    entry.setBegin(ptr);
839
37.5k
    zones.push_back(entry);
840
37.5k
    if (!ptr) continue;
841
    // checkme: does the zone type can be deduced from i?
842
31.3k
    f << "Zone" << i << "A=" << std::hex << ptr << std::dec << ",";
843
31.3k
  }
844
4.17k
  if (vers==1) {
845
21.6k
    for (int i=0; i<40; ++i) {
846
21.1k
      val=static_cast<int>(input->readLong(2));
847
21.1k
      if (val!=-1) f << "g" << i << "=" << val << ",";
848
21.1k
    }
849
529
  }
850
3.64k
  else {
851
    // no sure what is the maximal number of potential added zones
852
76.4k
    for (int i=0; i<20; ++i) {
853
72.8k
      auto ptr=long(input->readULong(2));
854
72.8k
      val=static_cast<int>(input->readLong(2));
855
72.8k
      if (val) f << "g" << i << "=" << val << ",";
856
72.8k
      MWAWEntry entry;
857
72.8k
      entry.setBegin(ptr);
858
72.8k
      zones.push_back(entry);
859
72.8k
      if (!ptr || ptr==0xFFFF) continue;
860
57.8k
      f << "Zone" << 9+i << "A=" << std::hex << ptr << std::dec << ",";
861
57.8k
    }
862
3.64k
  }
863
4.17k
  ascii().addPos(pos);
864
4.17k
  ascii().addNote(f.str().c_str());
865
866
  // from here, probably only junk in v1 and list of 0 + unkn number in v2
867
4.17k
  ascii().addPos(input->tell());
868
4.17k
  ascii().addNote("HeaderZone:end");
869
870
110k
  for (auto &entry : zones) {
871
110k
    if (!entry.begin() || entry.begin()==0xFFFF) continue;
872
83.8k
    if (!checkSmallZone(entry)) {
873
25.6k
      MWAW_DEBUG_MSG(("GreatWksDBParser::readHeader: find a bad zone %d\n", static_cast<int>(entry.id())));
874
25.6k
      entry.setLength(0);
875
25.6k
      continue;
876
25.6k
    }
877
58.2k
    int const id=entry.id();
878
58.2k
    if (m_state->m_idZonesMap.find(id)==m_state->m_idZonesMap.end())
879
38.5k
      m_state->m_idZonesMap[id]=entry;
880
19.6k
    else {
881
19.6k
      MWAW_DEBUG_MSG(("GreatWksDBParser::readHeader: the zone entry %d already exists\n", id));
882
19.6k
      f.str("");
883
19.6k
      f << "Entries(" << entry.name() << "):###";
884
19.6k
      ascii().addPos(entry.begin());
885
19.6k
      ascii().addNote(f.str().c_str());
886
19.6k
      ascii().addPos(entry.end());
887
19.6k
      ascii().addNote("_");
888
19.6k
    }
889
58.2k
  }
890
891
4.17k
  return true;
892
4.17k
}
893
894
////////////////////////////////////////////////////////////
895
//
896
// Low level
897
//
898
////////////////////////////////////////////////////////////
899
900
////////////////////////////////////////////////////////////
901
// block gestion
902
////////////////////////////////////////////////////////////
903
bool GreatWksDBParser::readBlockHeader(GreatWksDBParserInternal::BlockHeader &entry)
904
1.05M
{
905
1.05M
  MWAWInputStreamPtr input = getInput();
906
1.05M
  if (!input->checkPosition(input->tell()+12)) return false;
907
3.16M
  for (auto &ptr : entry.m_ptr) ptr=long(input->readULong(4));
908
1.05M
  return true;
909
1.05M
}
910
911
std::shared_ptr<GreatWksDBParserInternal::Block> GreatWksDBParser::createBlock(GreatWksDBParserInternal::BlockHeader &entry)
912
990k
{
913
990k
  std::shared_ptr<GreatWksDBParserInternal::Block> res;
914
915
990k
  MWAWInputStreamPtr input = getInput();
916
990k
  long pos=entry.m_ptr[0];
917
990k
  if (!pos || !input->checkPosition(pos+10)) return res;
918
919
222k
  libmwaw::DebugStream f;
920
222k
  std::set<long> seens;
921
275k
  while (pos) {
922
227k
    if (!input->checkPosition(pos+256) || seens.find(pos)!=seens.end()) {
923
2.14k
      MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: find an old position\n"));
924
2.14k
      ascii().addPos(pos);
925
2.14k
      ascii().addNote("Entries(BlockHeader):###");
926
2.14k
      return res;
927
2.14k
    }
928
225k
    seens.insert(pos);
929
225k
    f.str("");
930
225k
    f << "Entries(BlockHeader):";
931
225k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
932
225k
    auto type=static_cast<int>(input->readULong(1));
933
225k
    f << "type=" << std::hex << type << std::dec << ",";
934
225k
    if ((type&0xF0) != 0x40) {
935
172k
      MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: find old block type\n"));
936
172k
      f << "####";
937
172k
      ascii().addPos(pos);
938
172k
      ascii().addNote(f.str().c_str());
939
172k
      return res;
940
172k
    }
941
52.7k
    auto val=static_cast<int>(input->readULong(1));
942
52.7k
    if (val!=0x7F) // find fl=0 with type=44
943
51.2k
      f << "fl=" << std::hex << val << std::dec << ",";
944
52.7k
    GreatWksDBParserInternal::Block::Zone zone;
945
52.7k
    zone.m_N = static_cast<int>(input->readULong(2));
946
52.7k
    if (zone.m_N)
947
49.6k
      f << "N=" << zone.m_N << ",";
948
52.7k
    if ((type&0x4)==0)
949
35.8k
      zone.m_dataSize = static_cast<int>(input->readULong(2));
950
52.7k
    if (zone.m_dataSize)
951
24.5k
      f << "dataSize=" << zone.m_dataSize << ",";
952
52.7k
    if (type==0x41 || type==0x44) { // link to type or to flag
953
13.5k
      val=static_cast<int>(input->readULong(4));
954
13.5k
      if (val) f << "prev=" << std::hex << val << std::dec << ",";
955
13.5k
    }
956
52.7k
    long next=static_cast<int>(input->readULong(4));
957
52.7k
    if (next) {
958
32.4k
      f << "next=" << std::hex << next << std::dec << ",";
959
32.4k
      if ((next&0x1FF)) {
960
22.8k
        MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: the next zone seems bad(ignored it)\n"));
961
22.8k
        f << "###";
962
22.8k
        next=0;
963
22.8k
      }
964
32.4k
    }
965
966
52.7k
    zone.m_ptr=input->tell();
967
52.7k
    if (!res) res.reset(new GreatWksDBParserInternal::Block(entry));
968
52.7k
    switch (type) {
969
2.38k
    case 0x40: // a root with no data?
970
2.38k
      ascii().addPos(input->tell());
971
2.38k
      ascii().addNote("_");
972
2.38k
      break;
973
2.60k
    case 0x41: // ok
974
13.5k
    case 0x44: // no dataSize
975
13.5k
      res->m_zoneList.push_back(zone);
976
13.5k
      break;
977
36.8k
    default:
978
36.8k
      res->m_zoneList.push_back(zone);
979
36.8k
      MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: find some unexpected type %d, try to continue\n", type));
980
36.8k
      f << "###";
981
      // let's add this zone, but ignored potential follower
982
36.8k
      next=0;
983
36.8k
      break;
984
52.7k
    }
985
52.7k
    ascii().addPos(pos);
986
52.7k
    ascii().addNote(f.str().c_str());
987
52.7k
    pos=next;
988
52.7k
  }
989
47.7k
  return res;
990
222k
}
991
992
////////////////////////////////////////////////////////////
993
// functions to read the known block structure
994
////////////////////////////////////////////////////////////
995
bool GreatWksDBParser::readRecordList(GreatWksDBParserInternal::Block const &block)
996
685
{
997
685
  MWAWInputStreamPtr input = getInput();
998
685
  auto const &header=block.m_header;
999
685
  libmwaw::DebugStream f;
1000
1.35k
  for (size_t z=0; z<block.getNumZones(); ++z) {
1001
671
    auto const &zone=block.getZone(z);
1002
671
    long pos=zone.m_ptr;
1003
671
    if (!pos || !input->checkPosition(pos+zone.m_N*4)) {
1004
60
      MWAW_DEBUG_MSG(("GreatWksDBParser::readRecordList: the zone seems too short\n"));
1005
60
      f.str("");
1006
60
      f << "Entries(" << header.m_name << ")[" << header << "]:###";
1007
60
      ascii().addPos(pos);
1008
60
      ascii().addNote(f.str().c_str());
1009
60
      continue;
1010
60
    }
1011
611
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1012
611
    f.str("");
1013
611
    f << "Entries(" << header.m_name << "):list=[";
1014
176k
    for (int i=0; i< zone.m_N; ++i) f << input->readLong(4) << ",";
1015
611
    f << "]";
1016
611
    ascii().addPos(pos);
1017
611
    ascii().addNote(f.str().c_str());
1018
611
    ascii().addPos(input->tell());
1019
611
    ascii().addNote("_");
1020
611
  }
1021
685
  return true;
1022
685
}
1023
1024
bool GreatWksDBParser::readFreeList(GreatWksDBParserInternal::Block &block)
1025
664
{
1026
664
  MWAWInputStreamPtr input = getInput();
1027
664
  auto const &header=block.m_header;
1028
664
  libmwaw::DebugStream f;
1029
1.30k
  for (size_t z=0; z<block.getNumZones(); ++z) {
1030
642
    auto const &zone=block.getZone(z);
1031
642
    long pos=zone.m_ptr;
1032
642
    if (!pos || !input->checkPosition(pos+zone.m_N*8)) {
1033
142
      MWAW_DEBUG_MSG(("GreatWksDBParser::readFreeList: the zone seems too short\n"));
1034
142
      f.str("");
1035
142
      f << "Entries(" << header.m_name << ")[" << header << "]:###";
1036
142
      ascii().addPos(pos);
1037
142
      ascii().addNote(f.str().c_str());
1038
142
      continue;
1039
142
    }
1040
500
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1041
500
    f.str("");
1042
500
    f << "Entries(" << header.m_name << "):zones=[";
1043
397k
    for (int i=0; i<zone.m_N; ++i) {
1044
      /* a ptr followed by 400000000|size or the oposite
1045
         normally the content of a free block seems to be 0x8000, dataSz
1046
         excepted on time where it is 8, dataSz
1047
       */
1048
396k
      unsigned long values[2];
1049
793k
      for (auto &value : values) value=static_cast<unsigned long>(input->readULong(4));
1050
396k
      if ((values[0]&0xFF000000)==0x40000000)
1051
1.49k
        std::swap(values[0],values[1]);
1052
396k
      if ((values[0]&0xFF000000)==0 && (values[1]&0xFF000000)==0x40000000 &&
1053
832
          input->checkPosition(long(values[0]+(values[1]&0xFFFFFF)))) {
1054
318
        ascii().addPos(long(values[0]));
1055
318
        ascii().addNote("Free");
1056
318
        ascii().addPos(long(values[0]+(values[1]&0xFFFFFF)));
1057
318
        ascii().addNote("_");
1058
318
        f << std::hex << values[0] << ":" << (values[1]&0xFFFFFF) << std::dec << ",";
1059
318
      }
1060
396k
      else {
1061
396k
        MWAW_DEBUG_MSG(("GreatWksDBParser::readFreeList: find an odd free zone\n"));
1062
396k
        f << "###" << std::hex << values[0] << ":" << values[1] << std::dec << ",";
1063
396k
      }
1064
396k
    }
1065
500
    f << "]";
1066
500
    ascii().addPos(pos);
1067
500
    ascii().addNote(f.str().c_str());
1068
500
    ascii().addPos(input->tell());
1069
500
    ascii().addNote("_");
1070
500
  }
1071
664
  return true;
1072
664
}
1073
1074
bool GreatWksDBParser::readRowLinks(GreatWksDBParserInternal::Block &block)
1075
2.18k
{
1076
2.18k
  MWAWInputStreamPtr input = getInput();
1077
2.18k
  auto const &header=block.m_header;
1078
2.18k
  auto &database=m_state->m_database;
1079
2.18k
  libmwaw::DebugStream f;
1080
4.35k
  for (size_t z=0; z<block.getNumZones(); ++z) {
1081
2.17k
    auto const &zone=block.getZone(z);
1082
2.17k
    long pos=zone.m_ptr;
1083
2.17k
    if (!pos || !input->checkPosition(pos+zone.m_N*8)) {
1084
246
      MWAW_DEBUG_MSG(("GreatWksDBParser::readRowLinks: the zone seems too short\n"));
1085
246
      f.str("");
1086
246
      f << "Entries(" << header.m_name << ")[" << header << "]:###";
1087
246
      ascii().addPos(pos);
1088
246
      ascii().addNote(f.str().c_str());
1089
246
      continue;
1090
246
    }
1091
1.92k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1092
1.92k
    f.str("");
1093
1.92k
    f << "Entries(" << header.m_name << "):ptrs=[";
1094
8.40M
    for (int i=0; i<zone.m_N; ++i) {
1095
8.40M
      auto ptr=long(input->readULong(4));
1096
8.40M
      auto id=static_cast<int>(input->readLong(4));
1097
8.40M
      if (ptr) {
1098
6.75M
        MWAWEntry entry;
1099
6.75M
        entry.setBegin(ptr);
1100
6.75M
        entry.setId(id);
1101
6.75M
        database.m_rowList.push_back(entry);
1102
6.75M
      }
1103
1.65M
      else {
1104
1.65M
        MWAW_DEBUG_MSG(("GreatWksDBParser::readRowLinks: find an empty record zone\n"));
1105
1.65M
        f << "###";
1106
1.65M
      }
1107
8.40M
      f << std::hex << ptr << std::dec << ":" << id << ",";
1108
8.40M
    }
1109
1.92k
    f << "]";
1110
1.92k
    ascii().addPos(pos);
1111
1.92k
    ascii().addNote(f.str().c_str());
1112
1.92k
    ascii().addPos(input->tell());
1113
1.92k
    ascii().addNote("_");
1114
1.92k
  }
1115
2.18k
  return true;
1116
2.18k
}
1117
1118
bool GreatWksDBParser::readBlockHeader2(GreatWksDBParserInternal::Block const &block)
1119
401
{
1120
401
  MWAWInputStreamPtr input = getInput();
1121
401
  auto const &header=block.m_header;
1122
401
  libmwaw::DebugStream f;
1123
795
  for (size_t z=0; z<block.getNumZones(); ++z) {
1124
394
    auto const &zone=block.getZone(z);
1125
394
    long pos=zone.m_ptr;
1126
394
    if (!pos || !input->checkPosition(pos+zone.m_N*8)) {
1127
13
      MWAW_DEBUG_MSG(("GreatWksDBParser::readBlockHeader2: the zone seems too short\n"));
1128
13
      f.str("");
1129
13
      f << "Entries(" << header.m_name << ")[" << header << "]:###";
1130
13
      ascii().addPos(pos);
1131
13
      ascii().addNote(f.str().c_str());
1132
13
      continue;
1133
13
    }
1134
381
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1135
381
    f.str("");
1136
381
    f << "Entries(" << header.m_name << "):unkn=[";
1137
4.33k
    for (int i=0; i<zone.m_N; ++i) {
1138
3.95k
      f << input->readULong(4) << ":"; // always 0?
1139
3.95k
      f << input->readLong(4) << ","; // ids
1140
3.95k
    }
1141
381
    f << "]";
1142
381
    ascii().addPos(pos);
1143
381
    ascii().addNote(f.str().c_str());
1144
381
    ascii().addPos(input->tell());
1145
381
    ascii().addNote("_");
1146
381
  }
1147
401
  return true;
1148
401
}
1149
1150
bool GreatWksDBParser::readBlock(GreatWksDBParserInternal::Block const &block, int fieldSize)
1151
0
{
1152
0
  MWAWInputStreamPtr input = getInput();
1153
0
  auto const &header=block.m_header;
1154
0
  libmwaw::DebugStream f;
1155
0
  for (size_t z=0; z<block.getNumZones(); ++z) {
1156
0
    auto const &zone=block.getZone(z);
1157
0
    long pos=zone.m_ptr;
1158
0
    if (fieldSize<=0 || !pos || !input->checkPosition(pos+zone.m_N*fieldSize)) {
1159
0
      MWAW_DEBUG_MSG(("GreatWksDBParser::readBlock: the fieldSize seems bad\n"));
1160
0
      f.str("");
1161
0
      f << "Entries(" << header.m_name << ")[" << header << "]:###";
1162
0
      ascii().addPos(pos);
1163
0
      ascii().addNote(f.str().c_str());
1164
0
      continue;
1165
0
    }
1166
0
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1167
0
    for (int i=0; i<zone.m_N; ++i) {
1168
0
      pos=input->tell();
1169
0
      f.str("");
1170
0
      f << "Entries(" << header.m_name << ")[" << i << "]:";
1171
0
      input->seek(pos+fieldSize, librevenge::RVNG_SEEK_SET);
1172
0
      ascii().addPos(pos);
1173
0
      ascii().addNote(f.str().c_str());
1174
0
    }
1175
0
    ascii().addPos(input->tell());
1176
0
    ascii().addNote("_");
1177
0
  }
1178
0
  return true;
1179
0
}
1180
1181
////////////////////////////////////////////////////////////
1182
// check if entry.begin() corresponds or not to a small zone
1183
////////////////////////////////////////////////////////////
1184
bool GreatWksDBParser::checkSmallZone(MWAWEntry &entry)
1185
84.8k
{
1186
84.8k
  if (entry.begin()<=0) return false;
1187
84.8k
  MWAWInputStreamPtr input = getInput();
1188
84.8k
  if (!input->checkPosition(entry.begin()+6)) return false;
1189
1190
59.1k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1191
59.1k
  auto id=static_cast<int>(input->readLong(2));
1192
59.1k
  entry.setId(id);
1193
  /* the zone with id=10 is in fact a list of zone with id=10, so
1194
     its length will be underestimate */
1195
59.1k
  entry.setLength(6+long(input->readULong(4)));
1196
59.1k
  if (id>=0 && id<15) {
1197
25.3k
    static char const* const names[]= {
1198
25.3k
      "Zone0A", "Field", "FrmLink", "Zone3A", "Zone4A", "ListFrmula", "ListSummary", "Zone7A",
1199
25.3k
      "Zone8A", "Form", "FldLink", "Zone11A", "Zone12A", "FldAuxi", "Zone14A"
1200
25.3k
    };
1201
25.3k
    entry.setName(names[id]);
1202
25.3k
  }
1203
33.8k
  else {
1204
33.8k
    std::stringstream s;
1205
33.8k
    s << "Zone" << id << "A";
1206
33.8k
    entry.setName(s.str());
1207
33.8k
  }
1208
59.1k
  return input->checkPosition(entry.begin()+6);
1209
84.8k
}
1210
1211
////////////////////////////////////////////////////////////
1212
// read the row zone
1213
////////////////////////////////////////////////////////////
1214
bool GreatWksDBParser::readRowRecords(MWAWEntry const &entry)
1215
6.66M
{
1216
6.66M
  MWAWInputStreamPtr input = getInput();
1217
6.66M
  long pos=entry.begin();
1218
6.66M
  if (!pos || !input->checkPosition(pos+6)) {
1219
5.27M
    MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not find a row position\n"));
1220
5.27M
    if (pos) {
1221
5.27M
      ascii().addPos(pos);
1222
5.27M
      ascii().addNote("Entries(RowRec):###");
1223
5.27M
    }
1224
5.27M
    return false;
1225
5.27M
  }
1226
1227
1.38M
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1228
1.38M
  libmwaw::DebugStream f;
1229
1.38M
  f << "Entries(RowRec):";
1230
  // probably type=0 followed by ?
1231
1.38M
  auto val=static_cast<int>(input->readULong(2));
1232
1.38M
  if (val!=0xFF) f << "f0=" << std::hex << val << std::dec << ",";
1233
1.38M
  auto dSz=long(input->readULong(4));
1234
1.38M
  long endPos=pos+6+dSz;
1235
1.38M
  if (dSz<0 || !input->checkPosition(endPos)) {
1236
899k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the size seems bad\n"));
1237
899k
    f << "###";
1238
899k
    ascii().addPos(pos);
1239
899k
    ascii().addNote(f.str().c_str());
1240
899k
    return false;
1241
899k
  }
1242
488k
  ascii().addPos(pos);
1243
488k
  ascii().addNote(f.str().c_str());
1244
488k
  ascii().addPos(endPos);
1245
488k
  ascii().addNote("_");
1246
1247
488k
  auto &database=m_state->m_database;
1248
1.71M
  for (size_t fl=0; fl<database.m_fieldList.size(); ++fl) {
1249
1.71M
    pos=input->tell();
1250
1.71M
    f.str("");
1251
1.71M
    f << "RowRec-" << fl << ":";
1252
1.71M
    if (pos >= endPos) {
1253
288k
      MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: actual pos seems bad\n"));
1254
288k
      f << "###";
1255
288k
      ascii().addPos(pos);
1256
288k
      ascii().addNote(f.str().c_str());
1257
288k
      return true;
1258
288k
    }
1259
1.42M
    auto const &field=database.m_fieldList[fl];
1260
1.42M
    bool ok=true;
1261
1262
1.42M
    GreatWksDBParserInternal::Cell cell;
1263
1.42M
    cell.setFormat(field.m_format);
1264
1.42M
    switch (field.m_type) {
1265
6.06k
    case GreatWksDBParserInternal::Field::F_Text: {
1266
6.06k
      auto sSz=static_cast<int>(input->readULong(1));
1267
6.06k
      if (pos+1+sSz>endPos) {
1268
813
        MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the string length seems bad\n"));
1269
813
        f << "###";
1270
813
        ascii().addPos(pos);
1271
813
        ascii().addNote(f.str().c_str());
1272
813
      }
1273
6.06k
      cell.m_content.m_contentType=MWAWCellContent::C_TEXT;
1274
6.06k
      cell.m_content.m_textEntry.setBegin(input->tell());
1275
6.06k
      cell.m_content.m_textEntry.setLength(sSz);
1276
6.06k
      database.addCell(MWAWVec2i(field.m_id, entry.id()), cell);
1277
1278
6.06k
      std::string text("");
1279
234k
      for (int i=0; i<sSz; ++i) text+=char(input->readULong(1));
1280
6.06k
      f << text << ",";
1281
      // realign to a multiple of 2
1282
6.06k
      if ((sSz%2)==0) input->seek(1,librevenge::RVNG_SEEK_CUR);
1283
6.06k
      break;
1284
0
    }
1285
221k
    case GreatWksDBParserInternal::Field::F_Summary: {
1286
221k
      if (pos+10>endPos) {
1287
1.54k
        MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not read a summary zone\n"));
1288
1.54k
        f << "###";
1289
1.54k
        ascii().addPos(pos);
1290
1.54k
        ascii().addNote(f.str().c_str());
1291
1.54k
        return false;
1292
1.54k
      }
1293
      // formula will be will later
1294
219k
      cell.m_content.m_contentType=MWAWCellContent::C_FORMULA;
1295
219k
      database.addCell(MWAWVec2i(field.m_id, entry.id()), cell);
1296
219k
      f << "summary,";
1297
219k
      input->seek(pos+10, librevenge::RVNG_SEEK_SET);
1298
219k
      break;
1299
221k
    }
1300
834k
    case GreatWksDBParserInternal::Field::F_Number:
1301
837k
    case GreatWksDBParserInternal::Field::F_Date:
1302
838k
    case GreatWksDBParserInternal::Field::F_Time: {
1303
838k
      if (pos+10>endPos) {
1304
76.6k
        MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not read a number zone\n"));
1305
76.6k
        f << "###";
1306
76.6k
        ascii().addPos(pos);
1307
76.6k
        ascii().addNote(f.str().c_str());
1308
76.6k
        return false;
1309
76.6k
      }
1310
762k
      double value;
1311
762k
      bool isNan;
1312
      /*fixme: this does not work for time's field as value is a very
1313
        big number and the double precision is too small, ie. we do not
1314
        retrieve the decimal part -> time is always 0:0:0 */
1315
762k
      if (!input->readDouble10(value, isNan)) {
1316
36.5k
        static bool first=true;
1317
36.5k
        if (first) {
1318
          // can be normal, ie. 7fff403200000000000000 means no value which generates an error
1319
5
          MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not read a number, maybe a undef number\n"));
1320
5
          first=false;
1321
5
        }
1322
36.5k
        f << "#";
1323
36.5k
      }
1324
725k
      else {
1325
725k
        cell.m_content.m_contentType=MWAWCellContent::C_NUMBER;
1326
725k
        cell.m_content.setValue(value);
1327
725k
        database.addCell(MWAWVec2i(field.m_id, entry.id()), cell);
1328
725k
        f << value;
1329
725k
      }
1330
762k
      input->seek(pos+10, librevenge::RVNG_SEEK_SET);
1331
762k
      break;
1332
838k
    }
1333
25.7k
    case GreatWksDBParserInternal::Field::F_Picture: {
1334
25.7k
      long pSz=static_cast<int>(input->readULong(4));
1335
25.7k
      if (pos+4+pSz>endPos) {
1336
12.3k
        MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the picture length seems bad\n"));
1337
12.3k
        f << "###";
1338
12.3k
        ascii().addPos(pos);
1339
12.3k
        ascii().addNote(f.str().c_str());
1340
12.3k
      }
1341
25.7k
      if (pSz) {
1342
21.1k
        cell.m_pictEntry.setBegin(pos+4);
1343
21.1k
        cell.m_pictEntry.setLength(pSz);
1344
21.1k
        database.addCell(MWAWVec2i(field.m_id, entry.id()), cell);
1345
#ifdef DEBUG_WITH_FILES
1346
        ascii().skipZone(pos+4,pos+4+pSz-1);
1347
        librevenge::RVNGBinaryData file;
1348
        input->seek(pos+4,librevenge::RVNG_SEEK_SET);
1349
        input->readDataBlock(pSz, file);
1350
1351
        static int volatile pictName = 0;
1352
        libmwaw::DebugStream f2;
1353
        f2 << "DATA-" << ++pictName << ".pct";
1354
        libmwaw::Debug::dumpFile(file, f2.str().c_str());
1355
#endif
1356
21.1k
      }
1357
25.7k
      input->seek(pos+4+pSz, librevenge::RVNG_SEEK_SET);
1358
25.7k
      break;
1359
838k
    }
1360
202k
    case GreatWksDBParserInternal::Field::F_Memo: {
1361
202k
      long mSz=static_cast<int>(input->readULong(4));
1362
202k
      if (pos+4+mSz>endPos) {
1363
139k
        MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the memo length seems bad\n"));
1364
139k
        f << "###";
1365
139k
        ascii().addPos(pos);
1366
139k
        ascii().addNote(f.str().c_str());
1367
139k
      }
1368
202k
      cell.m_content.m_contentType=MWAWCellContent::C_TEXT;
1369
202k
      cell.m_content.m_textEntry.setBegin(input->tell());
1370
202k
      cell.m_content.m_textEntry.setLength(mSz);
1371
202k
      database.addCell(MWAWVec2i(field.m_id, entry.id()), cell);
1372
      // probably a simple textbox ( see sendSimpleTextbox )
1373
202k
      if (mSz) f << "memo,";
1374
202k
      input->seek(pos+4+mSz, librevenge::RVNG_SEEK_SET);
1375
202k
      break;
1376
838k
    }
1377
37.4k
    case GreatWksDBParserInternal::Field::F_Formula: {
1378
37.4k
      std::string extra("");
1379
37.4k
      ok=readFormulaResult(endPos, cell, extra);
1380
37.4k
      if (ok)
1381
15.3k
        database.addCell(MWAWVec2i(field.m_id, entry.id()), cell);
1382
37.4k
      f << extra;
1383
37.4k
      break;
1384
838k
    }
1385
95.3k
    case GreatWksDBParserInternal::Field::F_Unknown:
1386
#if !defined(__clang__)
1387
    default:
1388
#endif
1389
95.3k
      ok=false;
1390
95.3k
      break;
1391
1.42M
    }
1392
1.34M
    if (!ok)
1393
117k
      break;
1394
1.23M
    ascii().addPos(pos);
1395
1.23M
    ascii().addNote(f.str().c_str());
1396
1.23M
  }
1397
122k
  pos=input->tell();
1398
122k
  if (pos+2==endPos) {
1399
    // seems ok to find 0 here
1400
1.52k
    val=static_cast<int>(input->readLong(2));
1401
1.52k
    if (val) {
1402
713
      f.str("");
1403
713
      f << "RowRec[end]:" << val << ",";
1404
713
      ascii().addPos(pos);
1405
713
      ascii().addNote(f.str().c_str());
1406
713
    }
1407
813
    else {
1408
813
      ascii().addPos(pos);
1409
813
      ascii().addNote("_");
1410
813
    }
1411
1.52k
  }
1412
120k
  else if (pos!=endPos) {
1413
118k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: find some extra data\n"));
1414
118k
    ascii().addPos(pos);
1415
118k
    ascii().addNote("RowRec[end]:###");
1416
118k
  }
1417
1418
122k
  return true;
1419
488k
}
1420
1421
bool GreatWksDBParser::readFormula(long endPos, std::vector<MWAWCellContent::FormulaInstruction> &formula)
1422
2.08k
{
1423
2.08k
  formula.resize(0);
1424
2.08k
  MWAWInputStreamPtr input = getInput();
1425
2.08k
  long pos=input->tell();
1426
2.08k
  auto dSz=long(input->readULong(2));
1427
2.08k
  long endHeader=pos+2+dSz;
1428
2.08k
  if (dSz<2 || endHeader > endPos) {
1429
1.08k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFormula: can not read a formula\n"));
1430
1.08k
    return false;
1431
1.08k
  }
1432
996
  libmwaw::DebugStream f;
1433
996
  f << "Entries(Formula):";
1434
996
  std::string error("");
1435
996
  if (!m_document->readFormula(MWAWVec2i(0,0),endHeader,formula, error))
1436
960
    f << "###";
1437
996
  f << "[";
1438
996
  for (const auto &l : formula)
1439
372
    f << l;
1440
996
  f << "]" << error << ",";
1441
1442
996
  input->seek(endHeader, librevenge::RVNG_SEEK_SET);
1443
996
  auto N=static_cast<int>(input->readULong(2));
1444
996
  if (endHeader+2*(N+1)>endPos) {
1445
1
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFormula: can not read a formula field(II)\n"));
1446
1
    return false;
1447
1
  }
1448
995
  auto val=static_cast<int>(input->readLong(2)); // Here I find always 2, the field size ?
1449
995
  if (val!=2) f << "g0=" << val << ",";
1450
995
  std::vector<int> varList;
1451
995
  if (N) {
1452
664
    f << "list[varId]=[";
1453
5.18M
    for (int i=0; i<N; ++i) {
1454
5.18M
      val=static_cast<int>(input->readLong(2));
1455
5.18M
      varList.push_back(val);
1456
5.18M
      f << val << ",";
1457
5.18M
    }
1458
664
    f << "],";
1459
664
  }
1460
  // time to update the formula
1461
995
  int id=0;
1462
995
  for (auto &instr : formula) {
1463
372
    if (instr.m_type!=MWAWCellContent::FormulaInstruction::F_Cell)
1464
372
      continue;
1465
0
    if (id>=static_cast<int>(varList.size()) || varList[size_t(id)]<=0) {
1466
0
      MWAW_DEBUG_MSG(("GreatWksDBParser::readFormula: can not associated a cell with field\n"));
1467
0
      f << "###";
1468
0
      formula.resize(0);
1469
0
      break;
1470
0
    }
1471
0
    instr.m_position[0][0]=varList[size_t(id++)]-1;
1472
0
  }
1473
995
  ascii().addPos(pos);
1474
995
  ascii().addNote(f.str().c_str());
1475
995
  return true;
1476
996
}
1477
1478
/* read the result of a formula.
1479
1480
   note: we fill the cell content, which can overidden later if we find a formula to associate with the cell */
1481
bool GreatWksDBParser::readFormulaResult(long endPos, GreatWksDBParserInternal::Cell &cell, std::string &extra)
1482
37.4k
{
1483
37.4k
  libmwaw::DebugStream f;
1484
37.4k
  MWAWInputStreamPtr input = getInput();
1485
37.4k
  long pos=input->tell();
1486
37.4k
  if (pos+2>endPos || !input->checkPosition(endPos)) return false;
1487
34.8k
  auto type=static_cast<int>(input->readLong(2));
1488
34.8k
  switch (type) {
1489
12.2k
  case 0: // average
1490
13.9k
  case 1: // summary
1491
14.5k
  case 5: // number
1492
14.8k
  case 9: // date
1493
15.2k
  case 0xa: { // time
1494
15.2k
    if (pos+12>endPos) return false;
1495
13.7k
    double value;
1496
13.7k
    bool isNan;
1497
13.7k
    if (!input->readDouble10(value, isNan))
1498
1.63k
      f << "#double,";
1499
12.1k
    else {
1500
12.1k
      cell.m_content.m_contentType=MWAWCellContent::C_NUMBER;
1501
12.1k
      cell.m_content.setValue(value);
1502
12.1k
      f << value << ",";
1503
12.1k
    }
1504
13.7k
    input->seek(pos+12, librevenge::RVNG_SEEK_SET);
1505
13.7k
    break;
1506
15.2k
  }
1507
862
  case 7: {
1508
    // checkme
1509
862
    auto sSz=static_cast<int>(input->readULong(1));
1510
862
    if (pos+3+sSz>endPos) {
1511
84
      MWAW_DEBUG_MSG(("GreatWksSSParser::readFormulaResult: can not read a string value\n"));
1512
84
      return false;
1513
84
    }
1514
778
    cell.m_content.m_contentType=MWAWCellContent::C_TEXT;
1515
778
    cell.m_content.m_textEntry.setBegin(input->tell());
1516
778
    cell.m_content.m_textEntry.setLength(sSz);
1517
    // also modify the cell format in text
1518
778
    auto format=cell.getFormat();
1519
778
    format.m_format=MWAWCell::F_TEXT;
1520
778
    cell.setFormat(format);
1521
778
    std::string text("");
1522
134k
    for (int i=0; i<sSz; ++i)
1523
133k
      text += char(input->readULong(1));
1524
778
    f << "\"" << text << "\",";
1525
778
    if ((sSz%2)==0)
1526
235
      input->seek(1, librevenge::RVNG_SEEK_CUR);
1527
778
    break;
1528
862
  }
1529
822
  case 8: { // bool or long
1530
822
    if (pos+4>endPos) return false;
1531
645
    auto val=static_cast<int>(input->readLong(2));
1532
645
    cell.m_content.m_contentType=MWAWCellContent::C_NUMBER;
1533
645
    cell.m_content.setValue(double(val));
1534
645
    f << input->readLong(2) << ",";
1535
645
    break;
1536
822
  }
1537
557
  case 0xf: // nan
1538
557
    if (pos+6>endPos) return false;
1539
132
    cell.m_content.m_contentType=MWAWCellContent::C_NUMBER;
1540
132
    cell.m_content.setValue(std::numeric_limits<double>::quiet_NaN());
1541
132
    f << "nan" << input->readLong(4) << ",";
1542
132
    break;
1543
17.3k
  default:
1544
17.3k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFormulaResult: find unknown type %d\n", type));
1545
17.3k
    f << "#type=" << type << ",";
1546
17.3k
    extra=f.str();
1547
17.3k
    return false;
1548
34.8k
  }
1549
15.3k
  extra=f.str();
1550
15.3k
  return true;
1551
34.8k
}
1552
1553
////////////////////////////////////////////////////////////
1554
// read the fields zone
1555
////////////////////////////////////////////////////////////
1556
bool GreatWksDBParser::readFields(MWAWEntry const &entry)
1557
3.81k
{
1558
3.81k
  if (!entry.valid() || entry.length()<10) {
1559
10
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: the entry length seems bad\n"));
1560
10
    return false;
1561
10
  }
1562
3.80k
  int const vers=version();
1563
3.80k
  entry.setParsed(true);
1564
3.80k
  ascii().addPos(entry.end());
1565
3.80k
  ascii().addNote("_");
1566
3.80k
  MWAWInputStreamPtr input = getInput();
1567
3.80k
  libmwaw::DebugStream f;
1568
3.80k
  f << "Entries(Field):";
1569
3.80k
  input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET);
1570
3.80k
  auto N=static_cast<int>(input->readULong(2));
1571
3.80k
  f << "N=" << N << ",";
1572
3.80k
  auto dSz=static_cast<int>(input->readULong(2));
1573
3.80k
  if ((vers==1 && dSz<0x3c) || (vers==2 && dSz<0x3e) || entry.length()<10+long(N)*dSz) {
1574
75
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: the number of data seems bad\n"));
1575
75
    f << "###";
1576
75
    ascii().addPos(entry.begin());
1577
75
    ascii().addNote(f.str().c_str());
1578
75
    return false;
1579
75
  }
1580
3.73k
  ascii().addPos(entry.begin());
1581
3.73k
  ascii().addNote(f.str().c_str());
1582
3.73k
  long pos;
1583
21.8M
  for (int i=0; i<N; ++i) {
1584
21.8M
    pos=input->tell();
1585
21.8M
    f.str("");
1586
21.8M
    f << "Field-" << i << ":";
1587
21.8M
    GreatWksDBParserInternal::Field field;
1588
21.8M
    if (readField(field))
1589
1.55M
      f << field;
1590
20.3M
    else {
1591
20.3M
      field = GreatWksDBParserInternal::Field();
1592
20.3M
      MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: can not read a field\n"));
1593
20.3M
      f << "###";
1594
20.3M
    }
1595
21.8M
    m_state->m_database.m_fieldList.push_back(field);
1596
21.8M
    ascii().addPos(pos);
1597
21.8M
    ascii().addNote(f.str().c_str());
1598
21.8M
    input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
1599
21.8M
  }
1600
13.8M
  for (auto &field : m_state->m_database.m_fieldList) {
1601
13.8M
    pos=input->tell();
1602
13.8M
    if (field.m_type==GreatWksDBParserInternal::Field::F_Summary) {
1603
1.14k
      if (pos+18>entry.end()) {
1604
0
        MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: can not read a summary field\n"));
1605
0
        break;
1606
0
      }
1607
1.14k
      f.str("");
1608
1.14k
      f << "Field[summary]:";
1609
1.14k
      auto val=static_cast<int>(input->readULong(2)); // 10a or 180
1610
1.14k
      if (val!=0x10a) f << "fl=" << std::hex << val << std::dec << ",";
1611
1.14k
      field.m_summaryType=static_cast<int>(input->readULong(2));
1612
1.14k
      if (field.m_summaryType > 0 && field.m_summaryType < 6) {
1613
47
        static char const* const wh[] = { "", "average", "count", "total", "minimum", "maximum" };
1614
47
        f << wh[field.m_summaryType] << ",";
1615
47
      }
1616
1.09k
      else
1617
1.09k
        f << "#type=" << field.m_summaryType << ",";
1618
1.14k
      field.m_summaryField=static_cast<int>(input->readLong(2))-1;
1619
1.14k
      f << "field=" << field.m_summaryField << ",";
1620
1.14k
      val=static_cast<int>(input->readULong(2)); // 1e0 or 14
1621
1.14k
      if (val!=0x1e0) f << "f0=" << std::hex << val << std::dec << ",";
1622
6.87k
      for (int i=0; i<5; ++i) { // always 0
1623
5.73k
        val=static_cast<int>(input->readULong(2));
1624
5.73k
        if (val)  f << "f" << i+1 << "=" << val << ",";
1625
5.73k
      }
1626
1627
1.14k
      ascii().addPos(pos);
1628
1.14k
      ascii().addNote(f.str().c_str());
1629
1.14k
      input->seek(pos+18, librevenge::RVNG_SEEK_SET);
1630
1.14k
      continue;
1631
1.14k
    }
1632
13.8M
    if (field.m_type!=GreatWksDBParserInternal::Field::F_Formula)
1633
13.8M
      continue;
1634
2.08k
    if (!readFormula(entry.end(), field.m_formula)) {
1635
1.09k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1636
1.09k
      break;
1637
1.09k
    }
1638
2.08k
  }
1639
3.73k
  pos=input->tell();
1640
3.73k
  if (input->tell()!=entry.end()) { // now some string or formula?
1641
3.41k
    ascii().addPos(pos);
1642
3.41k
    ascii().addNote("Field:end");
1643
3.41k
  }
1644
1645
3.73k
  return true;
1646
3.80k
}
1647
1648
bool GreatWksDBParser::readField(GreatWksDBParserInternal::Field &field)
1649
21.8M
{
1650
21.8M
  field=GreatWksDBParserInternal::Field();
1651
21.8M
  MWAWInputStreamPtr input = getInput();
1652
21.8M
  long pos=input->tell();
1653
21.8M
  if (!input->checkPosition(pos+0x3c)) return false;
1654
1.55M
  auto type=static_cast<int>(input->readLong(2));
1655
1.55M
  libmwaw::DebugStream f;
1656
1.55M
  auto &format = field.m_format;
1657
1.55M
  switch (type) {
1658
10.7k
  case 5:
1659
10.7k
    field.m_type=GreatWksDBParserInternal::Field::F_Number;
1660
10.7k
    format.m_format=MWAWCell::F_NUMBER;
1661
10.7k
    format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
1662
10.7k
    break;
1663
5.78k
  case 7:
1664
5.78k
    field.m_type=GreatWksDBParserInternal::Field::F_Text;
1665
5.78k
    format.m_format=MWAWCell::F_TEXT;
1666
5.78k
    break;
1667
2.90k
  case 9:
1668
2.90k
    field.m_type=GreatWksDBParserInternal::Field::F_Date;
1669
2.90k
    format.m_format=MWAWCell::F_DATE;
1670
2.90k
    break;
1671
4.91k
  case 0xa:
1672
4.91k
    field.m_type=GreatWksDBParserInternal::Field::F_Time;
1673
4.91k
    format.m_format=MWAWCell::F_TIME;
1674
4.91k
    break;
1675
3.40k
  case 0xc:
1676
3.40k
    field.m_type=GreatWksDBParserInternal::Field::F_Memo;
1677
3.40k
    format.m_format=MWAWCell::F_TEXT;
1678
3.40k
    break;
1679
2.07k
  case 0xd:
1680
2.07k
    field.m_type=GreatWksDBParserInternal::Field::F_Picture;
1681
2.07k
    break;
1682
5.12k
  case 0xFF:
1683
5.12k
    field.m_type=GreatWksDBParserInternal::Field::F_Formula;
1684
5.12k
    format.m_format=MWAWCell::F_NUMBER;
1685
5.12k
    format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
1686
5.12k
    break;
1687
1.61k
  case 0xFE:
1688
1.61k
    field.m_type=GreatWksDBParserInternal::Field::F_Summary;
1689
1.61k
    format.m_format=MWAWCell::F_NUMBER;
1690
1.61k
    format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
1691
1.61k
    break;
1692
1.51M
  default:
1693
1.51M
    MWAW_DEBUG_MSG(("GreatWksDBParser::readField: find unknown file type\n"));
1694
1.51M
    f << "#type=" << type << ",";
1695
1.55M
  }
1696
1.55M
  int val;
1697
1.55M
  if (version()==2) { // always 5?
1698
1.53M
    val=static_cast<int>(input->readLong(2));
1699
1.53M
    if (val!=5) f << "f0=" << val << ",";
1700
1.53M
  }
1701
13.9M
  for (int i=0; i<8; ++i) { // fl0-6=0|1, fl7=2c|38|ee|c4
1702
12.4M
    val=static_cast<int>(input->readULong(1));
1703
12.4M
    if (val) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
1704
12.4M
  }
1705
1.55M
  val=static_cast<int>(input->readLong(2)); // always 0
1706
1.55M
  if (val) f << "f1=" << val << ",";
1707
  /* f2=f3=f4=0 excepted
1708
     for formula find 8,5bb2b4,1b or 4,5bb200,5
1709
     for summary find 0,0,05bb254 and 0,0,5bb268
1710
  */
1711
1.55M
  val=static_cast<int>(input->readLong(2));
1712
1.55M
  if (val) f << "f2=" << val << ",";
1713
4.65M
  for (int i=0; i<2; ++i) {
1714
3.10M
    val=static_cast<int>(input->readULong(4));
1715
3.10M
    if (val) f << "f" << 3+i << "=" << std::hex << val << std::dec << ",";
1716
3.10M
  }
1717
1.55M
  val=static_cast<int>(input->readLong(2)); // always 0
1718
1.55M
  if (val) f << "f5=" << val << ",";
1719
1.55M
  field.m_linkZone=long(input->readULong(2));
1720
1.55M
  field.m_id=static_cast<int>(input->readLong(2))-1;
1721
1.55M
  auto fSz=static_cast<int>(input->readULong(1));
1722
1.55M
  if (fSz>31) {
1723
541k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readField: the name field size seems to big\n"));
1724
541k
    f << "###";
1725
541k
    ascii().addDelimiter(input->tell(),'|');
1726
541k
  }
1727
1.01M
  else {
1728
1.01M
    std::string name("");
1729
3.61M
    for (int i=0; i<fSz; ++i) name+=char(input->readULong(1));
1730
1.01M
    field.m_name=name;
1731
1.01M
  }
1732
1.55M
  field.m_extra=f.str();
1733
1.55M
  return true;
1734
1.55M
}
1735
1736
bool GreatWksDBParser::readFieldAuxis(MWAWEntry const &entry)
1737
381
{
1738
381
  if (!entry.valid() || entry.length()<10) {
1739
8
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldAuxis: the entry length seems bad\n"));
1740
8
    return false;
1741
8
  }
1742
373
  int const vers=version();
1743
373
  entry.setParsed(true);
1744
373
  ascii().addPos(entry.end());
1745
373
  ascii().addNote("_");
1746
373
  MWAWInputStreamPtr input = getInput();
1747
373
  libmwaw::DebugStream f;
1748
373
  f << "Entries(Field):";
1749
373
  input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET);
1750
373
  auto N=static_cast<int>(input->readULong(2));
1751
373
  f << "N=" << N << ",";
1752
373
  auto dSz=static_cast<int>(input->readULong(2));
1753
373
  if (vers==1 || (vers==2 && dSz<0x46) || entry.length()<10+long(N)*dSz) {
1754
29
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldAuxis: the number of data seems bad\n"));
1755
29
    f << "###";
1756
29
    ascii().addPos(entry.begin());
1757
29
    ascii().addNote(f.str().c_str());
1758
29
    return false;
1759
29
  }
1760
344
  ascii().addPos(entry.begin());
1761
344
  ascii().addNote(f.str().c_str());
1762
344
  auto &database=m_state->m_database;
1763
344
  if (N>static_cast<int>(database.m_fieldList.size())) {
1764
76
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldAuxis: the field list seems too short\n"));
1765
76
    database.m_fieldList.resize(size_t(N));
1766
76
  }
1767
344
  int n=0;
1768
2.70M
  for (auto &field : database.m_fieldList) {
1769
2.70M
    long pos=input->tell();
1770
2.70M
    f.str("");
1771
2.70M
    f << "FldAuxi-" << n++ << ":";
1772
2.70M
    auto val=static_cast<int>(input->readLong(2));
1773
2.70M
    if (val) {
1774
      // checkme: find val=10 for a sequence with increment=10, so...
1775
45.2k
      f << "type[increment]=" << val << ",";
1776
45.2k
      field.m_isSequence=true;
1777
45.2k
    }
1778
2.70M
    val=static_cast<int>(input->readLong(2));
1779
2.70M
    if (val==1) {
1780
1.57k
      f << "isUnique[number],";
1781
      // let use a sequence
1782
1.57k
      field.m_isSequence=true;
1783
1.57k
    }
1784
2.70M
    else if (val)
1785
43.6k
      f << "isUnique[number]=" << val << ",";
1786
8.11M
    for (int j=0; j<2; ++j) { // always 0?
1787
5.40M
      val=static_cast<int>(input->readLong(2));
1788
5.40M
      if (val) f << "f" << j << "=" << val << ",";
1789
5.40M
    }
1790
2.70M
    field.m_firstNumber=static_cast<int>(input->readLong(2));
1791
2.70M
    if (field.m_firstNumber!=1)
1792
2.70M
      f << "first[sequence]=" << field.m_firstNumber << ",";
1793
2.70M
    val=static_cast<int>(input->readLong(2)); // always 0
1794
2.70M
    if (val) f << "f2=" << val << ",";
1795
2.70M
    field.m_incrementNumber=static_cast<int>(input->readLong(2));
1796
2.70M
    if (field.m_incrementNumber!=1)
1797
2.70M
      f << "increment[sequence]=" << field.m_incrementNumber << ",";
1798
78.4M
    for (int j=0; j < 28; ++j) { // always 0
1799
75.7M
      val=static_cast<int>(input->readLong(2));
1800
75.7M
      if (val) f << "f" << j << "=" << val << ",";
1801
75.7M
    }
1802
2.70M
    input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
1803
2.70M
    ascii().addPos(pos);
1804
2.70M
    ascii().addNote(f.str().c_str());
1805
2.70M
  }
1806
344
  return true;
1807
373
}
1808
////////////////////////////////////////////////////////////
1809
// read a list of records corresponding to a field
1810
////////////////////////////////////////////////////////////
1811
bool GreatWksDBParser::readFieldRecords(GreatWksDBParserInternal::Field &field)
1812
973k
{
1813
973k
  if (field.m_recordBlock.isEmpty()) {
1814
0
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a record zone\n"));
1815
0
    return false;
1816
0
  }
1817
973k
  auto block=createBlock(field.m_recordBlock);
1818
973k
  if (!block) {
1819
925k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not find the records input\n"));
1820
925k
    return false;
1821
925k
  }
1822
48.0k
  auto &database=m_state->m_database;
1823
48.0k
  MWAWInputStreamPtr input = getInput();
1824
48.0k
  libmwaw::DebugStream f;
1825
94.5k
  for (size_t z=0; z<block->getNumZones(); ++z) {
1826
46.4k
    auto const &zone=block->getZone(z);
1827
46.4k
    long pos=zone.m_ptr;
1828
46.4k
    f.str("");
1829
46.4k
    f << "Entries(FldRec)[" << field.m_id << "]:type=" << int(field.m_type) << ",";
1830
46.4k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1831
46.4k
    int const N=zone.m_N;
1832
46.4k
    auto const dSz=static_cast<int>(zone.m_dataSize);
1833
46.4k
    if (field.m_type==GreatWksDBParserInternal::Field::F_Text) {
1834
1.90k
      if (!input->checkPosition(pos+dSz+N*5)) {
1835
252
        MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a text zone\n"));
1836
252
        f << "###";
1837
252
        ascii().addPos(pos);
1838
252
        ascii().addNote(f.str().c_str());
1839
252
        continue;
1840
252
      }
1841
11.5k
      for (int i=0; i<6; ++i) { // always 0?
1842
9.91k
        auto val=static_cast<int>(input->readLong(2));
1843
9.91k
        if (val) f << "f" << i << "=" << val << ",";
1844
9.91k
      }
1845
1.65k
      std::vector<int> positions, rows;
1846
      // first read the list of positions
1847
1.65k
      input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
1848
2.61M
      for (int i=0; i<N; ++i)
1849
2.61M
        positions.push_back(static_cast<int>(input->readULong(1)));
1850
      // now read a list of small int: the row number
1851
2.61M
      for (int i=0; i<N; ++i)
1852
2.61M
        rows.push_back(static_cast<int>(input->readULong(4)));
1853
1.65k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1854
1.65k
      int actPos=0;
1855
13.5k
      for (size_t i=0; i<size_t(N); ++i) {
1856
13.2k
        int nextPos= i+1==size_t(N) ? dSz : positions[i+1];
1857
13.2k
        if (actPos!=positions[i] || nextPos>dSz) {
1858
1.35k
          MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a text zone, pb with a position\n"));
1859
1.35k
          f << "###";
1860
1.35k
          break;
1861
1.35k
        }
1862
11.8k
        GreatWksDBParserInternal::Cell cell;
1863
11.8k
        cell.setFormat(field.m_format);
1864
11.8k
        cell.m_content.m_contentType=MWAWCellContent::C_TEXT;
1865
11.8k
        cell.m_content.m_textEntry.setBegin(input->tell());
1866
11.8k
        std::string text("");
1867
11.8k
        bool findEmptyChar=false;
1868
340k
        while (actPos<nextPos) {
1869
328k
          auto c=char(input->readULong(1));
1870
          // c==0 can indicate an empty field
1871
328k
          if (c) text += c;
1872
86.9k
          else findEmptyChar=true;
1873
328k
          ++actPos;
1874
328k
        }
1875
11.8k
        cell.m_content.m_textEntry.setEnd(input->tell()-(findEmptyChar ? 1 : 0));
1876
11.8k
        database.addCell(MWAWVec2i(field.m_id, rows[i]), cell);
1877
11.8k
        f << "\"" << text << "\":" << rows[i] << ",";
1878
11.8k
      }
1879
1.65k
      input->seek(pos+12+dSz+5*N, librevenge::RVNG_SEEK_SET);
1880
1.65k
    }
1881
44.5k
    else if (field.m_type==GreatWksDBParserInternal::Field::F_Number ||
1882
44.0k
             field.m_type==GreatWksDBParserInternal::Field::F_Date ||
1883
43.7k
             field.m_type==GreatWksDBParserInternal::Field::F_Time) {
1884
1.77k
      if (!input->checkPosition(pos+14*N)) {
1885
790
        MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a number zone\n"));
1886
790
        f << "###";
1887
790
        ascii().addPos(pos);
1888
790
        ascii().addNote(f.str().c_str());
1889
790
        continue;
1890
790
      }
1891
4.13M
      for (int i=0; i<N; ++i) {
1892
4.13M
        long fPos=input->tell();
1893
4.13M
        auto row=static_cast<int>(input->readLong(4));
1894
4.13M
        double value;
1895
4.13M
        bool isNan;
1896
4.13M
        if (!input->readDouble10(value, isNan)) {
1897
198k
          static bool first=true;
1898
198k
          if (first) {
1899
            // can be normal, ie. 7fff403200000000000000 means no value which generates an error
1900
6
            MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a number, maybe a undef number\n"));
1901
6
            first=false;
1902
6
          }
1903
198k
          f << "#";
1904
198k
          input->seek(fPos+14, librevenge::RVNG_SEEK_SET);
1905
198k
        }
1906
3.93M
        else {
1907
3.93M
          GreatWksDBParserInternal::Cell cell;
1908
3.93M
          cell.setFormat(field.m_format);
1909
3.93M
          cell.m_content.m_contentType=MWAWCellContent::C_NUMBER;
1910
3.93M
          cell.m_content.setValue(value);
1911
3.93M
          database.addCell(MWAWVec2i(field.m_id, row), cell);
1912
3.93M
          f << value;
1913
3.93M
        }
1914
4.13M
        f << ":" << row << ",";
1915
4.13M
      }
1916
986
    }
1917
42.8k
    else {
1918
42.8k
      MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: does not know how to read list for type=%d\n", int(field.m_type)));
1919
42.8k
      f << "###";
1920
42.8k
    }
1921
45.4k
    ascii().addPos(pos);
1922
45.4k
    ascii().addNote(f.str().c_str());
1923
45.4k
    ascii().addPos(input->tell());
1924
45.4k
    ascii().addNote("_");
1925
45.4k
  }
1926
48.0k
  return true;
1927
973k
}
1928
1929
////////////////////////////////////////////////////////////
1930
// read the links between fields and record zone
1931
////////////////////////////////////////////////////////////
1932
bool GreatWksDBParser::readFieldLinks(GreatWksDBParserInternal::Field &field)
1933
1.07M
{
1934
1.07M
  MWAWInputStreamPtr input = getInput();
1935
1.07M
  if (field.m_linkZone<=0 || !input->checkPosition(field.m_linkZone+32)) {
1936
44.1k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldLinks: can not read a link between field to records\n"));
1937
44.1k
    return false;
1938
44.1k
  }
1939
1.02M
  input->seek(field.m_linkZone, librevenge::RVNG_SEEK_SET);
1940
1.02M
  libmwaw::DebugStream f;
1941
1.02M
  f << "Entries(FldLink)[" << field.m_id << "]:";
1942
1.02M
  auto val=static_cast<int>(input->readLong(2));
1943
1.02M
  if (val!=0xa) f << "#type=" << val << ",";
1944
1.02M
  val=static_cast<int>(input->readLong(2)); // always 0
1945
1.02M
  if (val) f << "f0=" << val << ",";
1946
1.02M
  val=static_cast<int>(input->readLong(2));
1947
1.02M
  if (val!=0x14) f << "f1=" << val << ",";
1948
1.02M
  if (!readBlockHeader(field.m_recordBlock)) {
1949
0
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldLinks: the zone position seems bad\n"));
1950
0
    f << "###block,";
1951
0
  }
1952
1.02M
  f << "block=" << field.m_recordBlock << ",";
1953
1.02M
  ascii().addDelimiter(input->tell(),'|');
1954
1.02M
  ascii().addPos(field.m_linkZone);
1955
1.02M
  ascii().addNote(f.str().c_str());
1956
1.02M
  ascii().addPos(field.m_linkZone+32);
1957
1.02M
  ascii().addNote("_");
1958
1.02M
  return true;
1959
1.07M
}
1960
1961
////////////////////////////////////////////////////////////
1962
// read a list of int
1963
////////////////////////////////////////////////////////////
1964
bool GreatWksDBParser::readIntList(MWAWEntry const &entry, std::vector<int> &list)
1965
1.21k
{
1966
1.21k
  list.resize(0);
1967
1.21k
  if (!entry.valid() || entry.length()<10) {
1968
33
    MWAW_DEBUG_MSG(("GreatWksDBParser::readIntList: the entry length seems bad\n"));
1969
33
    return false;
1970
33
  }
1971
1.18k
  MWAWInputStreamPtr input = getInput();
1972
1.18k
  input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET);
1973
1.18k
  auto N=static_cast<int>(input->readULong(2));
1974
1.18k
  auto dSz=static_cast<int>(input->readULong(2));
1975
1.18k
  if (dSz!=2 || entry.length()!=10+N*dSz) {
1976
257
    MWAW_DEBUG_MSG(("GreatWksDBParser::readIntList: the N or dSz value seems bad\n"));
1977
257
    return false;
1978
257
  }
1979
1980
926
  entry.setParsed(true);
1981
926
  ascii().addPos(entry.end());
1982
926
  ascii().addNote("_");
1983
1984
992
  for (int i=0; i<N; ++i)
1985
66
    list.push_back(static_cast<int>(input->readLong(2)));
1986
1987
926
  return true;
1988
1.18k
}
1989
1990
////////////////////////////////////////////////////////////
1991
// read a list of form
1992
////////////////////////////////////////////////////////////
1993
bool GreatWksDBParser::readFormLinks(MWAWEntry const &entry)
1994
841
{
1995
841
  if (!entry.valid() || entry.length()<10) {
1996
47
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFormLinks: the entry length seems bad\n"));
1997
47
    return false;
1998
47
  }
1999
794
  entry.setParsed(true);
2000
794
  ascii().addPos(entry.end());
2001
794
  ascii().addNote("_");
2002
794
  MWAWInputStreamPtr input = getInput();
2003
794
  libmwaw::DebugStream f;
2004
794
  f << "Entries(" << entry.name() << "):";
2005
794
  input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET);
2006
794
  auto N=static_cast<int>(input->readULong(2));
2007
794
  f << "N=" << N << ",";
2008
794
  auto dSz=static_cast<int>(input->readULong(2));
2009
794
  if (entry.length()!=10+long(N)*dSz || dSz<4) {
2010
314
    MWAW_DEBUG_MSG(("GreatWksDBParser::readFormLinks: the number of data seems bad\n"));
2011
314
    f << "###";
2012
314
    ascii().addPos(entry.begin());
2013
314
    ascii().addNote(f.str().c_str());
2014
314
    return false;
2015
314
  }
2016
480
  ascii().addPos(entry.begin());
2017
480
  ascii().addNote(f.str().c_str());
2018
480
  std::vector<MWAWEntry> listZones;
2019
1.43k
  for (int i=0; i<N; ++i) {
2020
956
    long pos=input->tell();
2021
956
    f.str("");
2022
956
    f << entry.name() << "-" << i << ":";
2023
956
    auto val=static_cast<int>(input->readLong(2)); // always 0?
2024
956
    if (val) f << "f0=" << val << ",";
2025
956
    auto ptr=static_cast<int>(input->readULong(2));
2026
956
    if (ptr) {
2027
953
      MWAWEntry zone;
2028
953
      zone.setBegin(ptr);
2029
953
      if (checkSmallZone(zone))
2030
942
        listZones.push_back(zone);
2031
11
      else
2032
11
        f << "###";
2033
953
      f << "ptr" << i << "=" << std::hex << ptr << std::dec << ",";
2034
953
    }
2035
956
    input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
2036
956
    ascii().addPos(pos);
2037
956
    ascii().addNote(f.str().c_str());
2038
956
  }
2039
480
  for (auto const &zone : listZones)
2040
942
    readForm(zone);
2041
480
  return true;
2042
794
}
2043
2044
////////////////////////////////////////////////////////////
2045
// read zone 2 link zone
2046
////////////////////////////////////////////////////////////
2047
bool GreatWksDBParser::readForm(MWAWEntry const &entry)
2048
942
{
2049
942
  int const vers=version();
2050
  /* not really sure what may be the header size, ie.
2051
     in v1: 264 seems ok,
2052
     in v2: 276 seems ok,
2053
     but some v1 file converted in v2 seems to keep intact the v1 structure :-~
2054
2055
     This must be fixed if we want to export the form...
2056
  */
2057
942
  int const headerSize=vers==1 ? 264 : 276;
2058
942
  MWAWInputStreamPtr input = getInput();
2059
942
  long pos=entry.begin();
2060
942
  libmwaw::DebugStream f;
2061
942
  f << "Entries(" << entry.name() << "):";
2062
942
  ascii().addPos(entry.end());
2063
942
  ascii().addNote("_");
2064
942
  if (entry.id()!=9 || entry.length() < 6+headerSize) {
2065
484
    f << "###";
2066
484
    MWAW_DEBUG_MSG(("GreatWksDBParser::readForm: the entry seems bad\n"));
2067
484
    ascii().addPos(pos);
2068
484
    ascii().addNote(f.str().c_str());
2069
484
    return false;
2070
484
  }
2071
458
  input->seek(pos+6, librevenge::RVNG_SEEK_SET);
2072
  // a big number 5bafac : an id?
2073
458
  f << "f0=" << std::hex << input->readULong(4) << std::dec << ",";
2074
  // small number between 1 and 4
2075
458
  f << "id=" << input->readLong(2) << ",";
2076
458
  auto sSz=static_cast<int>(input->readULong(1));
2077
458
  if (sSz>32) {
2078
5
    f << "###";
2079
5
    MWAW_DEBUG_MSG(("GreatWksDBParser::readForm: the string size seems bad\n"));
2080
5
    ascii().addPos(pos);
2081
5
    ascii().addNote(f.str().c_str());
2082
5
    return false;
2083
5
  }
2084
453
  std::string text("");
2085
5.76k
  for (int i=0; i<sSz; ++i) text+=char(input->readULong(1));
2086
453
  f << text << ",";
2087
453
  input->seek(pos+44, librevenge::RVNG_SEEK_SET);
2088
453
  ascii().addDelimiter(input->tell(),'|');
2089
453
  ascii().addPos(pos);
2090
453
  ascii().addNote(f.str().c_str());
2091
2092
453
  input->seek(pos+headerSize, librevenge::RVNG_SEEK_SET);
2093
453
  pos=input->tell();
2094
453
  if (!m_document->getGraphParser()->readPageFrames())
2095
306
    input->seek(pos, librevenge::RVNG_SEEK_SET);
2096
453
  pos=input->tell();
2097
453
  if (pos!=entry.end()) {
2098
364
    ascii().addPos(pos);
2099
364
    ascii().addNote("Form:end");
2100
364
  }
2101
453
  return true;
2102
458
}
2103
2104
////////////////////////////////////////////////////////////
2105
// read a unknown zone of zone
2106
////////////////////////////////////////////////////////////
2107
bool GreatWksDBParser::readZone12(MWAWEntry const &entry)
2108
499
{
2109
499
  int const vers=version();
2110
499
  if (!entry.valid() || entry.length()!=10+2*vers) {
2111
118
    MWAW_DEBUG_MSG(("GreatWksDBParser::readZone12: the entry length seems bad\n"));
2112
118
    return false;
2113
118
  }
2114
381
  MWAWInputStreamPtr input = getInput();
2115
381
  libmwaw::DebugStream f;
2116
381
  f << "Entries(" << entry.name() << "):";
2117
381
  input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET);
2118
2119
381
  entry.setParsed(true);
2120
381
  ascii().addPos(entry.end());
2121
381
  ascii().addNote("_");
2122
2123
381
  int val;
2124
1.90k
  for (int i=0; i<4; ++i) { // f0=f1=0, f2=f3=1
2125
1.52k
    val=static_cast<int>(input->readLong(1));
2126
1.52k
    if (val) f << "f" << i << "=" << val << ",";
2127
1.52k
  }
2128
381
  if (vers==2) // b0|2a
2129
37
    f << "g0=" << input->readLong(2) << ",";
2130
381
  val=static_cast<int>(input->readLong(2));
2131
381
  if (val) // 66|67|7a
2132
379
    f << "g1=" << val << ",";
2133
381
  ascii().addPos(entry.begin());
2134
381
  ascii().addNote(f.str().c_str());
2135
2136
381
  return true;
2137
499
}
2138
2139
////////////////////////////////////////////////////////////
2140
// read unknown small zone
2141
////////////////////////////////////////////////////////////
2142
bool GreatWksDBParser::readSmallZone(MWAWEntry const &entry)
2143
31.0k
{
2144
31.0k
  if (!entry.valid() || entry.length()<10) {
2145
2.62k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readSmallZone: the entry length seems bad\n"));
2146
2.62k
    return false;
2147
2.62k
  }
2148
28.4k
  entry.setParsed(true);
2149
28.4k
  ascii().addPos(entry.end());
2150
28.4k
  ascii().addNote("_");
2151
28.4k
  MWAWInputStreamPtr input = getInput();
2152
28.4k
  libmwaw::DebugStream f;
2153
28.4k
  f << "Entries(" << entry.name() << "):";
2154
28.4k
  input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET);
2155
28.4k
  auto N=static_cast<int>(input->readULong(2));
2156
28.4k
  f << "N=" << N << ",";
2157
28.4k
  long dSz=static_cast<int>(input->readULong(2));
2158
28.4k
  if (entry.length()!=10+long(N)*dSz) {
2159
27.4k
    MWAW_DEBUG_MSG(("GreatWksDBParser::readSmallZone: the number of data seems bad\n"));
2160
27.4k
    f << "###";
2161
27.4k
    ascii().addPos(entry.begin());
2162
27.4k
    ascii().addNote(f.str().c_str());
2163
27.4k
    return false;
2164
27.4k
  }
2165
1.01k
  ascii().addPos(entry.begin());
2166
1.01k
  ascii().addNote(f.str().c_str());
2167
201k
  for (int i=0; i<N; ++i) {
2168
200k
    long pos=input->tell();
2169
200k
    f.str("");
2170
200k
    f << entry.name() << "-" << i << ":";
2171
200k
    ascii().addPos(pos);
2172
200k
    ascii().addNote(f.str().c_str());
2173
200k
    input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
2174
200k
  }
2175
2176
1.01k
  return true;
2177
28.4k
}
2178
2179
////////////////////////////////////////////////////////////
2180
////////////////////////////////////////////////////////////
2181
bool GreatWksDBParser::sendDatabase()
2182
1.77k
{
2183
1.77k
  MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener();
2184
1.77k
  if (!listener) {
2185
0
    MWAW_DEBUG_MSG(("GreatWksDBParser::sendDatabase: I can not find the listener\n"));
2186
0
    return false;
2187
0
  }
2188
1.77k
  MWAWInputStreamPtr input=getInput();
2189
1.77k
  auto const &database=m_state->m_database;
2190
1.77k
  auto const &fields = database.m_fieldList;
2191
1.77k
  size_t numFields=fields.size();
2192
  // fixme: use first layout colWidth here
2193
1.77k
  listener->openSheet(std::vector<float>(1,76), librevenge::RVNG_POINT, std::vector<int>(1,static_cast<int>(numFields)), "Sheet0");
2194
1.77k
  int r=-1, numRows=static_cast<int>(database.m_rowCellsMap.size());
2195
127k
  for (auto const &rIt : database.m_rowCellsMap) {
2196
127k
    auto const &row=rIt.second;
2197
127k
    listener->openSheetRow(12, librevenge::RVNG_POINT);
2198
127k
    ++r;
2199
197M
    for (size_t c=0; c< numFields; ++c) {
2200
197M
      auto const &field=fields[c];
2201
197M
      GreatWksDBParserInternal::Cell cell;
2202
197M
      if (c<row.size()) cell=row[c];
2203
197M
      field.updateCell(int(r), numRows, cell);
2204
197M
      if (cell.isEmpty()) continue;
2205
2206
621k
      auto const &content=cell.m_content;
2207
621k
      cell.setPosition(MWAWVec2i(int(c),r));
2208
621k
      listener->openSheetCell(cell, content);
2209
621k
      if (field.m_type==GreatWksDBParserInternal::Field::F_Text && content.m_textEntry.valid()) {
2210
1.48k
        input->seek(content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
2211
35.0k
        while (!input->isEnd() && input->tell()<content.m_textEntry.end()) {
2212
33.8k
          auto ch=static_cast<unsigned char>(input->readULong(1));
2213
33.8k
          if (ch==0xd)
2214
24
            listener->insertEOL();
2215
33.8k
          else if (ch<30) {
2216
257
            MWAW_DEBUG_MSG(("GreatWksDBParser::sendDatabase: find some odd character\n"));
2217
257
            break;
2218
257
          }
2219
33.5k
          else
2220
33.5k
            listener->insertCharacter(ch);
2221
33.8k
        }
2222
1.48k
      }
2223
620k
      else if (field.m_type==GreatWksDBParserInternal::Field::F_Memo && content.m_textEntry.valid())
2224
45.3k
        m_document->getTextParser()->sendTextbox(content.m_textEntry, listener);
2225
574k
      else if (field.m_type==GreatWksDBParserInternal::Field::F_Picture && cell.m_pictEntry.valid()) {
2226
1.44k
        librevenge::RVNGBinaryData pict;
2227
1.44k
        input->seek(cell.m_pictEntry.begin(),librevenge::RVNG_SEEK_SET);
2228
1.44k
        input->readDataBlock(cell.m_pictEntry.length(), pict);
2229
2230
1.44k
        std::string cellName("Sheet0.");
2231
1.44k
        cellName+=MWAWCell::getBasicCellName(cell.position()+MWAWVec2i(1,1));
2232
1.44k
        MWAWPosition position(MWAWVec2f(0,0), MWAWVec2f(76,12), librevenge::RVNG_POINT); // changeme
2233
1.44k
        position.setAnchorToCell(librevenge::RVNGString(cellName.c_str()));
2234
1.44k
        listener->insertPicture(position, MWAWEmbeddedObject(pict));
2235
1.44k
      }
2236
621k
      listener->closeSheetCell();
2237
621k
    }
2238
127k
    listener->closeSheetRow();
2239
127k
  }
2240
1.77k
  listener->closeSheet();
2241
1.77k
  return true;
2242
1.77k
}
2243
2244
////////////////////////////////////////////////////////////
2245
// read the header
2246
////////////////////////////////////////////////////////////
2247
bool GreatWksDBParser::checkHeader(MWAWHeader *header, bool strict)
2248
17.0k
{
2249
17.0k
  MWAWInputStreamPtr input = getInput();
2250
17.0k
  *m_state = GreatWksDBParserInternal::State();
2251
17.0k
  if (!m_document->checkHeader(header, strict) || !input) return false;
2252
16.6k
  if (getParserState()->m_kind!=MWAWDocument::MWAW_K_DATABASE)
2253
0
    return false;
2254
16.6k
  if (!strict) return true;
2255
2256
  // let check that the 3 header are defined
2257
6.27k
  input->seek(16, librevenge::RVNG_SEEK_SET);
2258
19.4k
  for (int i=0; i<3; ++i) {
2259
15.2k
    GreatWksDBParserInternal::BlockHeader block;
2260
15.2k
    if (!readBlockHeader(block) || block.m_ptr[0]==0 || (block.m_ptr[0]&0xFF)) return false;
2261
13.1k
    input->seek(8, librevenge::RVNG_SEEK_CUR);
2262
13.1k
  }
2263
4.21k
  return true;
2264
6.27k
}
2265
2266
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: