Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/Multiplan.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2006, 2007 Andrew Ziem
11
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 */
21
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include <cmath>
26
#include <set>
27
#include <sstream>
28
29
#include <librevenge-stream/librevenge-stream.h>
30
31
#include "libwps_internal.h"
32
#include "libwps_tools_win.h"
33
34
#include "WKSContentListener.h"
35
#include "WKSSubDocument.h"
36
37
#include "WPSCell.h"
38
#include "WPSEntry.h"
39
#include "WPSFont.h"
40
#include "WPSHeader.h"
41
#include "WPSPageSpan.h"
42
#include "WPSStringStream.h"
43
#include "WPSTable.h"
44
45
#include "Multiplan.h"
46
47
namespace libwps
48
{
49
50
//! Internal: namespace to define internal class of MultiplanParser
51
namespace MultiplanParserInternal
52
{
53
//! the font of a MultiplanParser
54
struct Font final : public WPSFont
55
{
56
  //! constructor
57
  explicit Font(libwps_tools_win::Font::Type type) : WPSFont(), m_type(type)
58
0
  {
59
0
  }
60
  //! destructor
61
  ~Font() final;
62
  //! font encoding type
63
  libwps_tools_win::Font::Type m_type;
64
};
65
66
Font::~Font()
67
0
{
68
0
}
69
70
//! a cellule of a Lotus spreadsheet
71
class Cell final : public WPSCell
72
{
73
public:
74
  /// constructor
75
1.48M
  Cell() : WPSCell() { }
76
  //! call when a cell must be send
77
  bool send(WPSListenerPtr &/*listener*/) final;
78
79
  //! call when the content of a cell must be send
80
  bool sendContent(WPSListenerPtr &/*listener*/) final
81
0
  {
82
0
    WPS_DEBUG_MSG(("MultiplanInternal::Cell::sendContent: must not be called\n"));
83
0
    return false;
84
0
  }
85
};
86
bool Cell::send(WPSListenerPtr &/*listener*/)
87
0
{
88
0
  WPS_DEBUG_MSG(("MultiplanInternal::Cell::send: must not be called\n"));
89
0
  return false;
90
0
}
91
//! a data cell zone
92
struct Zone
93
{
94
  //! the different enum type
95
  enum Type { Zone0=0, Link, FileName, SharedData, Name,
96
              CellData, CellPosition, Undef
97
            };
98
  //! constructor
99
  Zone()
100
12.1k
    : m_entry()
101
12.1k
    , m_positionsSet()
102
12.1k
  {
103
12.1k
  }
104
  //! returns true if the zone entry is valid
105
  bool isValid() const
106
0
  {
107
0
    return m_entry.valid();
108
0
  }
109
  //! returns the name corresponding to a type
110
  static std::string getName(int type)
111
26.1k
  {
112
26.1k
    char const *wh[]= { "Zone0", "Link", "FileName", "SharedData", "Names",
113
26.1k
                        "CellData", "CellPos", "UndefZone"
114
26.1k
                      };
115
26.1k
    if (type<0 || type>=int(WPS_N_ELEMENTS(wh)))
116
0
    {
117
0
      WPS_DEBUG_MSG(("MultiplanInternal::Zone::getName: called with unexpected type=%d\n", type));
118
0
      return "UndefZone";
119
0
    }
120
26.1k
    return wh[type];
121
26.1k
  }
122
  //! the zone data
123
  WPSEntry m_entry;
124
  //! list of position in the zone
125
  std::set<int> m_positionsSet;
126
};
127
//! the state of MultiplanParser
128
struct State
129
{
130
  //! constructor
131
  State(libwps_tools_win::Font::Type fontType, char const *password)
132
9.51k
    : m_eof(-1)
133
9.51k
    , m_version(-1)
134
9.51k
    , m_fontType(fontType)
135
9.51k
    , m_maximumCell()
136
9.51k
    , m_columnWidths()
137
9.51k
    , m_zonesList(1, Zone())
138
9.51k
    , m_cellPositionsMap()
139
9.51k
    , m_posToLinkMap()
140
9.51k
    , m_posToNameMap()
141
9.51k
    , m_posToSharedDataSeen()
142
143
9.51k
    , m_password(password)
144
9.51k
    , m_hash(0)
145
9.51k
    , m_checksum(0)
146
9.51k
  {
147
152k
    for (auto &k : m_keys) k=0;
148
9.51k
  }
149
  //! return the default font style
150
  libwps_tools_win::Font::Type getDefaultFontType() const
151
64.9k
  {
152
64.9k
    if (m_fontType != libwps_tools_win::Font::UNKNOWN)
153
0
      return m_fontType;
154
64.9k
    return libwps_tools_win::Font::CP_437;
155
64.9k
  }
156
157
  //! returns a default font (Courier12) with file's version to define the default encoding */
158
  static WPSFont getDefaultFont()
159
1.48M
  {
160
1.48M
    WPSFont res;
161
1.48M
    res.m_name="Courier";
162
1.48M
    res.m_size=12;
163
1.48M
    return res;
164
1.48M
  }
165
  //! returns the column width in point
166
  std::vector<WPSColumnFormat> getColumnsWidth() const;
167
168
  //! the last file position
169
  long m_eof;
170
  //! the file version
171
  int m_version;
172
  //! the user font type
173
  libwps_tools_win::Font::Type m_fontType;
174
  //! the maximumCell
175
  Vec2i m_maximumCell;
176
  //! the columns width in char, 0 means default
177
  std::vector<int> m_columnWidths;
178
  //! the list of cell's data zone
179
  std::vector<Zone> m_zonesList;
180
  //! the positions of each cell: a vector for each row
181
  std::map<int, std::vector<int> > m_cellPositionsMap;
182
  //! the different main spreadsheet zones
183
  WPSEntry m_entries[5];
184
  //! the list of link instruction
185
  std::map<int, WKSContentListener::FormulaInstruction> m_posToLinkMap;
186
  //! the map name's pos to name's cell instruction
187
  std::map<int, WKSContentListener::FormulaInstruction> m_posToNameMap;
188
  //! a set a shared data already seen
189
  std::set<int> m_posToSharedDataSeen;
190
191
  //! the password (if known)
192
  char const *m_password;
193
  //! the file hash
194
  int m_hash;
195
  //! the file checksum
196
  int m_checksum;
197
  //! the list of decoding keys
198
  uint8_t m_keys[16];
199
private:
200
  State(State const &) = delete;
201
  State &operator=(State const &) = delete;
202
};
203
204
std::vector<WPSColumnFormat> State::getColumnsWidth() const
205
8.40k
{
206
8.40k
  std::vector<WPSColumnFormat> res;
207
8.40k
  WPSColumnFormat const defFormat(64);
208
8.40k
  for (auto p : m_columnWidths)
209
1.02M
  {
210
1.02M
    if (p<0 || p>=255)
211
616k
      res.push_back(defFormat);
212
405k
    else
213
405k
      res.push_back(WPSColumnFormat(float(8*p)));
214
1.02M
  }
215
8.40k
  if (res.size()<64) res.resize(64, defFormat);
216
8.40k
  return res;
217
8.40k
}
218
}
219
220
// constructor, destructor
221
MultiplanParser::MultiplanParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
222
                                 libwps_tools_win::Font::Type encoding, char const *password)
223
9.51k
  : WKSParser(input, header)
224
9.51k
  , m_listener()
225
9.51k
  , m_state(new MultiplanParserInternal::State(encoding, password))
226
9.51k
{
227
9.51k
}
228
229
MultiplanParser::~MultiplanParser()
230
9.51k
{
231
9.51k
}
232
233
int MultiplanParser::version() const
234
1.76M
{
235
1.76M
  return m_state->m_version;
236
1.76M
}
237
238
bool MultiplanParser::checkFilePosition(long pos)
239
340k
{
240
340k
  if (m_state->m_eof < 0)
241
9.51k
  {
242
9.51k
    RVNGInputStreamPtr input = getInput();
243
9.51k
    long actPos = input->tell();
244
9.51k
    input->seek(0, librevenge::RVNG_SEEK_END);
245
9.51k
    m_state->m_eof=input->tell();
246
9.51k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
247
9.51k
  }
248
340k
  return pos <= m_state->m_eof;
249
340k
}
250
251
libwps_tools_win::Font::Type MultiplanParser::getDefaultFontType() const
252
0
{
253
0
  return m_state->getDefaultFontType();
254
0
}
255
256
////////////////////////////////////////////////////////////
257
// main function to parse the document
258
////////////////////////////////////////////////////////////
259
void MultiplanParser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
260
9.51k
{
261
9.51k
  RVNGInputStreamPtr input=getInput();
262
9.51k
  if (!input)
263
0
  {
264
0
    WPS_DEBUG_MSG(("MultiplanParser::parse: does not find input!!!\n"));
265
0
    throw (libwps::ParseException());
266
0
  }
267
268
9.51k
  if (!checkHeader(nullptr)) throw(libwps::ParseException());
269
270
9.48k
  bool ok=false;
271
9.48k
  try
272
9.48k
  {
273
9.48k
    ascii().setStream(input);
274
9.48k
    ascii().open("MN0");
275
276
9.48k
    if (checkHeader(nullptr) && readZones())
277
8.40k
      m_listener=createListener(documentInterface);
278
9.48k
    if (m_listener)
279
8.40k
    {
280
8.40k
      m_listener->startDocument();
281
8.40k
      sendSpreadsheet();
282
8.40k
      m_listener->endDocument();
283
8.40k
      ok = true;
284
8.40k
    }
285
9.48k
  }
286
9.48k
  catch (libwps::PasswordException())
287
9.48k
  {
288
0
    ascii().reset();
289
0
    WPS_DEBUG_MSG(("MultiplanParser::parse: password exception catched when parsing MN0\n"));
290
0
    throw (libwps::PasswordException());
291
0
  }
292
9.48k
  catch (...)
293
9.48k
  {
294
103
    WPS_DEBUG_MSG(("MultiplanParser::parse: exception catched when parsing MN0\n"));
295
103
    throw (libwps::ParseException());
296
103
  }
297
298
9.38k
  m_listener.reset();
299
9.38k
  ascii().reset();
300
9.38k
  if (!ok)
301
983
    throw(libwps::ParseException());
302
9.38k
}
303
304
std::shared_ptr<WKSContentListener> MultiplanParser::createListener(librevenge::RVNGSpreadsheetInterface *interface)
305
8.40k
{
306
8.40k
  std::vector<WPSPageSpan> pageList;
307
8.40k
  WPSPageSpan ps;
308
8.40k
  pageList.push_back(ps);
309
8.40k
  return std::shared_ptr<WKSContentListener>(new WKSContentListener(pageList, interface));
310
8.40k
}
311
312
////////////////////////////////////////////////////////////
313
// low level
314
////////////////////////////////////////////////////////////
315
// read the header
316
////////////////////////////////////////////////////////////
317
bool MultiplanParser::checkHeader(WPSHeader *header, bool strict)
318
19.0k
{
319
19.0k
  libwps::DebugStream f;
320
321
19.0k
  RVNGInputStreamPtr input = getInput();
322
19.0k
  if (!checkFilePosition(0x29a))
323
29
  {
324
29
    WPS_DEBUG_MSG(("MultiplanParser::checkHeader: file is too short\n"));
325
29
    return false;
326
29
  }
327
328
18.9k
  input->seek(0,librevenge::RVNG_SEEK_SET);
329
18.9k
  int fileSign=int(libwps::readU16(input));
330
18.9k
  auto &vers=m_state->m_version;
331
18.9k
  if (fileSign==0xe708)
332
11.8k
    vers=1;
333
7.09k
  else if (fileSign==0xec0c)
334
1.12k
    vers=2;
335
5.96k
  else if (fileSign==0xed0c)
336
5.96k
    vers=3;
337
0
  else
338
0
    return false;
339
18.9k
  f << "FileHeader:vers=" << vers << ",";
340
18.9k
  if (vers==3)
341
5.96k
  {
342
5.96k
    input->seek(22, librevenge::RVNG_SEEK_SET);
343
5.96k
    m_state->m_hash=int(libwps::readU16(input));
344
5.96k
    m_state->m_checksum=int(libwps::readU16(input));
345
5.96k
    if (m_state->m_hash || m_state->m_checksum)
346
4.98k
    {
347
4.98k
      WPS_DEBUG_MSG(("MultiplanParser::checkHeader: the file is protected with a password\n"));
348
4.98k
    }
349
5.96k
  }
350
18.9k
  long const endHeader=vers== 1 ? 0xfa : 0x112;
351
18.9k
  if (strict)
352
0
  {
353
    // read the spreadsheet dimension
354
0
    input->seek(endHeader, librevenge::RVNG_SEEK_SET);
355
0
    int dim[2];
356
0
    for (auto &d : dim) d=int(libwps::readU16(input));
357
0
    if (dim[0]==0 || dim[0]>=(vers==1 ? 256 : 4096) || dim[1]==0 || dim[1]>=256)
358
0
    {
359
0
      WPS_DEBUG_MSG(("MultiplanParser::checkHeader: the spreadsheet dimension is bad\n"));
360
0
      return false;
361
0
    }
362
0
    if (vers==1)
363
0
    {
364
      // read the last zone list position and check that it corresponds to a valid position
365
0
      input->seek(0x28a, librevenge::RVNG_SEEK_SET);
366
0
      int lastPos=0;
367
0
      for (int i=0; i<8; ++i)
368
0
      {
369
0
        int newPos=int(libwps::readU16(input));
370
0
        if (i==4) newPos+=lastPos; // length
371
0
        if (i==5)
372
0
        {
373
0
          lastPos=newPos;
374
0
          continue;
375
0
        }
376
0
        if (newPos<lastPos)
377
0
        {
378
0
          WPS_DEBUG_MSG(("MultiplanParser::checkHeader: find bad position\n"));
379
0
          return false;
380
0
        }
381
0
        if (i==1 && newPos-lastPos!=2*dim[0]*dim[1])
382
0
        {
383
0
          WPS_DEBUG_MSG(("MultiplanParser::checkHeader: the first zone size seems bad\n"));
384
0
          return false;
385
0
        }
386
0
        lastPos=newPos;
387
0
      }
388
0
      if (lastPos<4 || !checkFilePosition(0x29a+lastPos))
389
0
      {
390
0
        WPS_DEBUG_MSG(("MultiplanParser::checkHeader: can not find last spreadsheet position\n"));
391
0
        return false;
392
0
      }
393
0
    }
394
0
    else
395
0
    {
396
      // check if we can find the spreadsheet's data zone
397
0
      if (!checkFilePosition(0x3c7))
398
0
      {
399
0
        WPS_DEBUG_MSG(("MultiplanParser::checkHeader: can not find the data main position\n"));
400
0
        return false;
401
0
      }
402
0
      input->seek(0x3c5, librevenge::RVNG_SEEK_SET);
403
0
      long dataPos=long(libwps::readU16(input));
404
0
      if (dataPos<0x3c7 || !checkFilePosition(dataPos+20))
405
0
      {
406
0
        WPS_DEBUG_MSG(("MultiplanParser::checkHeader: the data main position seems bad\n"));
407
0
        return false;
408
0
      }
409
0
      input->seek(dataPos+2, librevenge::RVNG_SEEK_SET);
410
0
      int actType=6;
411
      // check if we can read the main spreadsheet zone
412
0
      while (!input->isEnd())
413
0
      {
414
0
        long pos=input->tell();
415
0
        bool ok=checkFilePosition(pos+6);
416
0
        int val=ok ? int(libwps::readU16(input)) : 0;
417
0
        if (val==6 && actType==12)   // row zone, ok if we have find some cell's data zones
418
0
        {
419
0
          input->seek(pos, librevenge::RVNG_SEEK_SET);
420
0
          break;
421
0
        }
422
0
        if (val<=12 && (val>actType || val==12))   // we can have main cell data's zone
423
0
        {
424
0
          actType=val;
425
0
          input->seek(2, librevenge::RVNG_SEEK_CUR);
426
0
          int dSz=int(libwps::readU16(input));
427
0
          if (dSz>=6 && checkFilePosition(pos+dSz))
428
0
          {
429
0
            input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
430
0
            continue;
431
0
          }
432
0
        }
433
0
        WPS_DEBUG_MSG(("MultiplanParser::checkHeader: can not read some zone\n"));
434
0
        return false;
435
0
      }
436
0
    }
437
0
  }
438
18.9k
  ascii().addPos(0);
439
18.9k
  ascii().addNote(f.str().c_str());
440
18.9k
  input->seek(vers==1 ? 0x2 : 0x1a, librevenge::RVNG_SEEK_SET);
441
170k
  for (int i=0; i<8; ++i)
442
151k
  {
443
151k
    long pos=input->tell();
444
151k
    f.str("");
445
151k
    f << "Entries(LinkFiles)[" << i << "]:";
446
151k
    std::string file;
447
587k
    for (int c=0; c<0x1f; ++c)
448
581k
    {
449
581k
      char ch=char(libwps::readU8(input));
450
581k
      if (!ch) break;
451
435k
      file += ch;
452
435k
    }
453
151k
    f << file;
454
151k
    ascii().addPos(pos);
455
151k
    if (file.empty())
456
102k
      ascii().addNote("_");
457
49.5k
    else
458
49.5k
      ascii().addNote(f.str().c_str());
459
151k
    input->seek(pos+0x1f, librevenge::RVNG_SEEK_SET);
460
151k
  }
461
18.9k
  if (header)
462
0
  {
463
0
    header->setMajorVersion(uint8_t(vers));
464
0
    header->setCreator(libwps::WPS_MULTIPLAN);
465
0
    header->setKind(libwps::WPS_SPREADSHEET);
466
0
    header->setNeedEncoding(true);
467
0
    header->setIsEncrypted(m_state->m_hash || m_state->m_checksum);
468
0
  }
469
18.9k
  return true;
470
18.9k
}
471
472
bool MultiplanParser::readZones()
473
9.48k
{
474
9.48k
  int const vers=version();
475
9.48k
  if (!readZoneB()) return false;
476
9.48k
  auto input = getInput();
477
9.48k
  long pos=input->tell();
478
9.48k
  libwps::DebugStream f;
479
9.48k
  long const zoneCSz=vers==1 ? 22 : 28;
480
9.48k
  if (!checkFilePosition(pos+zoneCSz*8))
481
16
  {
482
16
    WPS_DEBUG_MSG(("MultiplanParser::readZones: can not read zone C\n"));
483
16
    ascii().addPos(pos);
484
16
    ascii().addNote("Entries(Unknown):###extra");
485
16
    return false;
486
16
  }
487
9.47k
  ascii().addPos(pos);
488
9.47k
  ascii().addNote("Entries(ZoneC):");
489
85.2k
  for (int i=0; i<8; ++i)
490
75.7k
  {
491
75.7k
    pos=input->tell();
492
75.7k
    f.str("");
493
75.7k
    f << "ZoneC" << i << ":";
494
75.7k
    ascii().addPos(pos);
495
75.7k
    ascii().addNote(f.str().c_str());
496
75.7k
    input->seek(pos+zoneCSz, librevenge::RVNG_SEEK_SET);
497
75.7k
  }
498
9.47k
  if (!readColumnsWidth())
499
5
    return false;
500
9.46k
  pos=input->tell();
501
9.46k
  if (!checkFilePosition(pos+29))
502
15
  {
503
15
    WPS_DEBUG_MSG(("MultiplanParser::readZones: can not read zone D\n"));
504
15
    ascii().addPos(pos);
505
15
    ascii().addNote("Entries(Unknown):###extra");
506
15
    return false;
507
15
  }
508
9.45k
  f.str("");
509
9.45k
  f << "Entries(ZoneD):";
510
9.45k
  if (m_state->m_version==1)
511
5.94k
  {
512
5.94k
    ascii().addPos(pos);
513
5.94k
    ascii().addNote(f.str().c_str());
514
5.94k
    input->seek(pos+29, librevenge::RVNG_SEEK_SET);
515
5.94k
  }
516
3.51k
  else
517
3.51k
  {
518
3.51k
    input->seek(pos+27, librevenge::RVNG_SEEK_SET);
519
3.51k
    long newPos=int(libwps::readU16(input));
520
3.51k
    f << "pos=" << std::hex << newPos << std::dec << ",";
521
3.51k
    ascii().addPos(pos);
522
3.51k
    ascii().addNote(f.str().c_str());
523
3.51k
    if (newPos<input->tell() || !checkFilePosition(newPos))
524
10
    {
525
10
      WPS_DEBUG_MSG(("MultiplanParser::readZones: bad position\n"));
526
10
      ascii().addPos(pos);
527
10
      ascii().addNote("###");
528
10
      return false;
529
10
    }
530
116k
    while (!input->isEnd())
531
116k
    {
532
116k
      pos=input->tell();
533
116k
      if (pos>=newPos) break;
534
112k
      ascii().addPos(pos);
535
112k
      ascii().addNote("Entries(ZoneD):");
536
112k
      input->seek(pos+50, librevenge::RVNG_SEEK_SET);
537
112k
    }
538
3.50k
    input->seek(newPos, librevenge::RVNG_SEEK_SET);
539
3.50k
    if (!readZonesListV2())
540
694
      return false;
541
2.80k
    input=getInput();
542
2.80k
    if (vers==2)
543
323
      readFunctionNamesList();
544
2.48k
    else if (checkFilePosition(input->tell()+12*92))
545
210
    {
546
      // unsure, seems like a list of block of size 12 mixed
547
      // with other block of different sizes...
548
      //
549
      // this zone probably contains many junk data
550
210
      ascii().addPos(input->tell());
551
210
      ascii().addNote("Entries(ZoneE):");
552
19.7k
      for (int i=0; i<93; ++i)
553
19.5k
      {
554
19.5k
        pos=input->tell();
555
19.5k
        f.str("");
556
19.5k
        f << "ZoneE" << i << ":";
557
19.5k
        ascii().addPos(pos);
558
19.5k
        ascii().addNote(f.str().c_str());
559
19.5k
        input->seek(pos+12, librevenge::RVNG_SEEK_SET);
560
19.5k
      }
561
210
    }
562
2.80k
    if (!input->isEnd())
563
2.58k
    {
564
2.58k
      ascii().addPos(input->tell());
565
2.58k
      ascii().addNote("Entries(Unknown):###extra");
566
2.58k
      WPS_DEBUG_MSG(("MultiplanParser::readZones: find extra data\n"));
567
2.58k
    }
568
2.80k
    return !m_state->m_cellPositionsMap.empty();
569
3.50k
  }
570
5.94k
  if (!readZonesList())
571
105
    return false;
572
5.83k
  if (!input->isEnd())
573
5.79k
  {
574
5.79k
    WPS_DEBUG_MSG(("MultiplanParser::readZones: find extra data\n"));
575
5.79k
    ascii().addPos(input->tell());
576
5.79k
    ascii().addNote("Entries(Unknown):###extra");
577
5.79k
  }
578
5.83k
  return true;
579
5.94k
}
580
581
bool MultiplanParser::readColumnsWidth()
582
9.47k
{
583
9.47k
  auto input = getInput();
584
9.47k
  long pos = input->tell();
585
9.47k
  int const vers=version();
586
9.47k
  int const numCols=vers==1 ? 63 : 255;
587
9.47k
  if (!checkFilePosition(pos+numCols))
588
5
  {
589
5
    WPS_DEBUG_MSG(("MultiplanParser::readColumnsWidth: the zone seems too short\n"));
590
5
    return false;
591
5
  }
592
9.46k
  libwps::DebugStream f;
593
9.46k
  f << "Entries(ColWidth):width=[";
594
1.28M
  for (int i=0; i<numCols; ++i)
595
1.27M
  {
596
1.27M
    int val=int(libwps::readU8(input));
597
1.27M
    if (vers<=2 && val==0) val=255;
598
1.27M
    m_state->m_columnWidths.push_back(val);
599
1.27M
    if (val!=255)
600
524k
      f << m_state->m_columnWidths.back() << ",";
601
748k
    else
602
748k
      f << "_,";
603
1.27M
  }
604
9.46k
  f << "],";
605
9.46k
  ascii().addPos(pos);
606
9.46k
  ascii().addNote(f.str().c_str());
607
9.46k
  return true;
608
9.47k
}
609
610
bool MultiplanParser::readZonesList()
611
5.94k
{
612
5.94k
  auto input = getInput();
613
5.94k
  long pos = input->tell();
614
5.94k
  if (!checkFilePosition(pos+16))
615
0
  {
616
0
    WPS_DEBUG_MSG(("MultiplanParser::readZonesList: the zone seems too short\n"));
617
0
    return false;
618
0
  }
619
5.94k
  libwps::DebugStream f;
620
5.94k
  f << "Entries(ZonesList):";
621
5.94k
  int lastPos=0;
622
5.94k
  f << "zones=[";
623
5.94k
  WPSEntry cellPosEntry;
624
53.4k
  for (int i=0; i<8; ++i)
625
47.5k
  {
626
47.5k
    int newPos=int(libwps::readU16(input));
627
47.5k
    if (i==4) newPos+=lastPos; // length
628
47.5k
    if (i==5)
629
5.94k
    {
630
5.94k
      lastPos=newPos;
631
5.94k
      continue;
632
5.94k
    }
633
41.5k
    if (newPos>lastPos)
634
25.0k
    {
635
25.0k
      if (!checkFilePosition(pos+16+newPos))
636
4.74k
      {
637
4.74k
        WPS_DEBUG_MSG(("MultiplanParser::readZonesList: find a bad position"));
638
4.74k
        f << "###";
639
4.74k
      }
640
20.2k
      else
641
20.2k
      {
642
20.2k
        WPSEntry entry;
643
20.2k
        entry.setBegin(pos+16+lastPos);
644
20.2k
        entry.setEnd(pos+16+newPos);
645
20.2k
        MultiplanParserInternal::Zone::Type const what[]=
646
20.2k
        {
647
20.2k
          MultiplanParserInternal::Zone::Zone0,
648
20.2k
          MultiplanParserInternal::Zone::CellPosition,
649
20.2k
          MultiplanParserInternal::Zone::Link,
650
20.2k
          MultiplanParserInternal::Zone::FileName,
651
20.2k
          MultiplanParserInternal::Zone::CellData,
652
20.2k
          MultiplanParserInternal::Zone::Undef, // beg of shared data
653
20.2k
          MultiplanParserInternal::Zone::SharedData,
654
20.2k
          MultiplanParserInternal::Zone::Name,
655
20.2k
        };
656
20.2k
        auto w=what[i];
657
20.2k
        if (w==MultiplanParserInternal::Zone::CellData)
658
5.47k
        {
659
5.47k
          m_state->m_zonesList[0].m_entry=entry;
660
5.47k
          ascii().addPos(pos+16+lastPos);
661
5.47k
          ascii().addNote("Entries(CellData)");
662
5.47k
        }
663
14.8k
        else if (w==MultiplanParserInternal::Zone::CellPosition)
664
5.85k
          cellPosEntry=entry;
665
8.96k
        else
666
8.96k
          m_state->m_entries[w]=entry;
667
20.2k
      }
668
25.0k
      f << std::hex << lastPos << "<->" << newPos << std::dec << ",";
669
25.0k
      lastPos=newPos;
670
25.0k
    }
671
16.5k
    else
672
16.5k
      f << "_,";
673
41.5k
  }
674
5.94k
  f << "],";
675
5.94k
  ascii().addPos(pos);
676
5.94k
  ascii().addNote(f.str().c_str());
677
5.94k
  bool ok=readCellDataPosition(cellPosEntry);
678
35.6k
  for (int i=0; i<5; ++i)
679
29.7k
  {
680
29.7k
    if (!m_state->m_entries[i].valid()) continue;
681
8.96k
    f.str("");
682
8.96k
    f << "Entries(" << MultiplanParserInternal::Zone::getName(i) << "):";
683
8.96k
    ascii().addPos(m_state->m_entries[i].begin());
684
8.96k
    ascii().addNote(f.str().c_str());
685
8.96k
    ascii().addPos(m_state->m_entries[i].end());
686
8.96k
    ascii().addNote("_");
687
8.96k
    input->seek(m_state->m_entries[i].end(), librevenge::RVNG_SEEK_SET);
688
8.96k
  }
689
5.94k
  return ok;
690
5.94k
}
691
692
bool MultiplanParser::readZonesListV2()
693
3.50k
{
694
3.50k
  auto input = getInput();
695
3.50k
  long pos = input->tell();
696
3.50k
  if (!checkFilePosition(pos+2+5*6))
697
43
  {
698
43
    WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: the zone seems too short\n"));
699
43
    return false;
700
43
  }
701
3.45k
  libwps::DebugStream f;
702
3.45k
  f << "Entries(ZonesList):";
703
3.45k
  int N=int(libwps::readU16(input));
704
3.45k
  f << "N[row]=" << N << ",";
705
3.45k
  ascii().addPos(pos);
706
3.45k
  ascii().addNote(f.str().c_str());
707
708
  // time to decode the different crypted zones
709
3.45k
  if (m_state->m_hash || m_state->m_checksum)
710
2.44k
  {
711
2.44k
    if (!m_state->m_password || !checkPassword(m_state->m_password))
712
2.44k
    {
713
2.44k
      if (!retrievePasswordKeys())
714
103
      {
715
103
        WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find the password to decode data\n"));
716
103
        throw (libwps::PasswordException());
717
103
      }
718
2.44k
    }
719
2.33k
    input->seek(pos+2, librevenge::RVNG_SEEK_SET);
720
2.33k
    auto newInput=decodeStream(input);
721
2.33k
    if (!newInput) throw (libwps::ParseException());
722
2.33k
    input=newInput;
723
2.33k
    resetInput(newInput);
724
2.33k
    ascii().setStream(newInput);
725
2.33k
  }
726
727
17.5k
  for (int i=0; i<6; ++i)
728
17.5k
  {
729
17.5k
    pos=input->tell();
730
17.5k
    if (!checkFilePosition(pos+6))
731
19
    {
732
19
      WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find zone %d\n", i));
733
19
      return false;
734
19
    }
735
17.5k
    int val=int(libwps::readU16(input));
736
17.5k
    if (val<7 || val>12)
737
344
    {
738
344
      input->seek(pos, librevenge::RVNG_SEEK_SET);
739
344
      break;
740
344
    }
741
17.1k
    int type=val-7;
742
17.1k
    MultiplanParserInternal::Zone::Type const what[]=
743
17.1k
    {
744
17.1k
      MultiplanParserInternal::Zone::Zone0,
745
17.1k
      MultiplanParserInternal::Zone::Link,
746
17.1k
      MultiplanParserInternal::Zone::FileName,
747
17.1k
      MultiplanParserInternal::Zone::Name,
748
17.1k
      MultiplanParserInternal::Zone::SharedData,
749
17.1k
      MultiplanParserInternal::Zone::CellData
750
17.1k
    };
751
17.1k
    auto wh=what[type];
752
17.1k
    auto &entry=wh==MultiplanParserInternal::Zone::CellData ?
753
14.2k
      m_state->m_zonesList[0].m_entry : m_state->m_entries[wh];
754
17.1k
    if (entry.valid())
755
22
    {
756
22
      input->seek(pos, librevenge::RVNG_SEEK_SET);
757
22
      break;
758
22
    }
759
17.1k
    f.str("");
760
17.1k
    f << "Entries(" << MultiplanParserInternal::Zone::getName(wh) << "):";
761
17.1k
    val=int(libwps::readU16(input));
762
17.1k
    f << "f0=" << std::hex << val << std::dec << ",";
763
17.1k
    int dSz=int(libwps::readU16(input));
764
17.1k
    if (dSz<6 || !checkFilePosition(pos+dSz))
765
75
    {
766
75
      WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find zone %d\n", i));
767
75
      return false;
768
75
    }
769
17.0k
    entry.setBegin(pos);
770
17.0k
    entry.setLength(dSz);
771
17.0k
    ascii().addPos(pos);
772
17.0k
    ascii().addNote(f.str().c_str());
773
17.0k
    input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
774
17.0k
    if (type==5) break;
775
17.0k
  }
776
5.93k
  while (checkFilePosition(input->tell()+6))
777
5.91k
  {
778
5.91k
    pos=input->tell();
779
5.91k
    if (libwps::readU16(input)!=0xc)
780
3.22k
    {
781
3.22k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
782
3.22k
      break;
783
3.22k
    }
784
2.69k
    f.str("");
785
2.69k
    f << "Entries(CellData)[extra]:";
786
2.69k
    int val=int(libwps::readU16(input));
787
2.69k
    f << "f0=" << std::hex << val << std::dec << ",";
788
2.69k
    int dSz=int(libwps::readU16(input));
789
2.69k
    if (dSz<6 || !checkFilePosition(pos+dSz))
790
22
    {
791
22
      WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find extra data cell zone\n"));
792
22
      return false;
793
22
    }
794
2.67k
    m_state->m_zonesList.push_back(MultiplanParserInternal::Zone());
795
2.67k
    auto &entry=m_state->m_zonesList.back().m_entry;
796
2.67k
    entry.setBegin(pos);
797
2.67k
    entry.setLength(dSz);
798
2.67k
    ascii().addPos(pos);
799
2.67k
    ascii().addNote(f.str().c_str());
800
2.67k
    input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
801
2.67k
  }
802
3.23k
  pos=input->tell();
803
3.23k
  if (!checkFilePosition(pos+2)||int(libwps::readU16(input))!=6)
804
180
  {
805
180
    WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find row header\n"));
806
180
    return false;
807
180
  }
808
3.05k
  f.str("");
809
3.05k
  f << "Entries(Row):";
810
3.05k
  ascii().addPos(pos);
811
3.05k
  ascii().addNote(f.str().c_str());
812
24.1k
  for (int i=0; i<N; ++i)
813
22.1k
  {
814
22.1k
    pos=input->tell();
815
22.1k
    if (!checkFilePosition(pos+6))
816
44
    {
817
44
      WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find row %d\n", i));
818
44
      return false;
819
44
    }
820
22.1k
    f.str("");
821
22.1k
    f << "Row" << i << ":";
822
22.1k
    int val=int(libwps::readU16(input));
823
22.1k
    if (val!=6) f << "f0=" << val << ",";
824
22.1k
    int dSz=int(libwps::readU16(input));
825
22.1k
    if (dSz<4 || !checkFilePosition(pos+2*dSz))
826
224
    {
827
224
      WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find row %d\n", i));
828
224
      return false;
829
224
    }
830
21.8k
    int num=int(libwps::readU16(input));
831
21.8k
    if (8+3*num>2*dSz)
832
87
    {
833
87
      WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: can not find row %d\n", i));
834
87
      return false;
835
87
    }
836
21.8k
    int r=int(libwps::readU16(input));
837
21.8k
    if (m_state->m_cellPositionsMap.find(r)!=m_state->m_cellPositionsMap.end())
838
664
    {
839
664
      WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: oops, row %d already exists\n", r));
840
664
      return !m_state->m_cellPositionsMap.empty();
841
664
    }
842
21.1k
    f << "row=" << r << ",";
843
21.1k
    std::vector<int> cellPos;
844
21.1k
    f << "data=[";
845
5.40M
    for (int d=0; d<num; ++d)
846
5.38M
    {
847
5.38M
      val=int(libwps::readU16(input));
848
5.38M
      int z=int(libwps::readU8(input));
849
5.38M
      val+=0x10000*z;
850
5.38M
      if (z >= int(m_state->m_zonesList.size()))
851
2.63M
      {
852
2.63M
        WPS_DEBUG_MSG(("MultiplanParser::readZonesListV2: oops, find bad cell pos for row%d\n", r));
853
2.63M
        f << "##" << std::hex << val << std::dec << ",";
854
2.63M
        cellPos.push_back(0);
855
2.63M
        continue;
856
2.63M
      }
857
2.74M
      cellPos.push_back(val);
858
2.74M
      if (val==0)
859
1.33M
      {
860
1.33M
        f << "_,";
861
1.33M
        continue;
862
1.33M
      }
863
1.41M
      f << std::hex << val << std::dec << ",";
864
1.41M
      m_state->m_zonesList[size_t(z)].m_positionsSet.insert(val&0xffff);
865
1.41M
    }
866
21.1k
    m_state->m_cellPositionsMap[r]=cellPos;
867
21.1k
    if (input->tell()!=pos+2*dSz)
868
21.0k
      ascii().addDelimiter(input->tell(),'|');
869
21.1k
    ascii().addPos(pos);
870
21.1k
    ascii().addNote(f.str().c_str());
871
21.1k
    input->seek(pos+2*dSz, librevenge::RVNG_SEEK_SET);
872
21.1k
  }
873
2.04k
  return true;
874
3.05k
}
875
876
bool MultiplanParser::readFunctionNamesList()
877
323
{
878
323
  auto input = getInput();
879
323
  if (input->isEnd()) return true;
880
316
  long pos=input->tell();
881
316
  libwps::DebugStream f;
882
316
  f << "Entries(NamFunctions):";
883
3.85k
  while (!input->isEnd())
884
3.84k
  {
885
3.84k
    long actPos=input->tell();
886
3.84k
    int cSz=int(libwps::readU8(input));
887
3.84k
    if (cSz==0 || !checkFilePosition(actPos+1+cSz))
888
302
    {
889
302
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
890
302
      break;
891
302
    }
892
3.54k
    std::string name;
893
122k
    for (int i=0; i<cSz; ++i) name+=char(libwps::readU8(input));
894
3.54k
    f << name << ",";
895
3.54k
  }
896
316
  ascii().addPos(pos);
897
316
  ascii().addNote(f.str().c_str());
898
316
  return true;
899
323
}
900
901
////////////////////////////////////////////////////////////
902
// double
903
////////////////////////////////////////////////////////////
904
bool MultiplanParser::readDouble(long double &value)
905
113k
{
906
113k
  auto input = getInput();
907
113k
  long pos=input->tell();
908
113k
  value=0;
909
113k
  if (!checkFilePosition(pos+8))
910
0
  {
911
0
    WPS_DEBUG_MSG(("MultiplanParser::readDouble: the zone is too short\n"));
912
0
    return false;
913
0
  }
914
113k
  if (version()>=2)
915
67.0k
  {
916
67.0k
    bool isNan;
917
67.0k
    if (!libwps::readDouble8(input,value,isNan))
918
2.34k
      input->seek(pos+8, librevenge::RVNG_SEEK_SET);
919
67.0k
    return true;
920
67.0k
  }
921
46.5k
  int exponant=int(libwps::readU8(input));
922
46.5k
  long double sign=1;
923
46.5k
  if (exponant&0x80)
924
21.8k
  {
925
21.8k
    exponant&=0x7f;
926
21.8k
    sign=-1;
927
21.8k
  }
928
46.5k
  bool ok=true;
929
46.5k
  long double factor=1;
930
341k
  for (int i=0; i<7; ++i)
931
301k
  {
932
301k
    int val=int(libwps::readU8(input));
933
894k
    for (int d=0; d<2; ++d)
934
600k
    {
935
600k
      int v= d==0 ? (val>>4) : (val&0xf);
936
600k
      if (v>=10)
937
7.07k
      {
938
7.07k
        WPS_DEBUG_MSG(("MultiplanParser::readDouble: oops find a bad digits\n"));
939
7.07k
        ok=false;
940
7.07k
        break;
941
7.07k
      }
942
593k
      factor/=10.;
943
593k
      value+=factor*v;
944
593k
    }
945
301k
    if (!ok) break;
946
301k
  }
947
46.5k
  value *= sign*std::powl(10.,exponant-0x40);
948
46.5k
  input->seek(pos+8, librevenge::RVNG_SEEK_SET);
949
46.5k
  return ok;
950
113k
}
951
952
////////////////////////////////////////////////////////////
953
// formula
954
////////////////////////////////////////////////////////////
955
namespace MultiplanParserInternal
956
{
957
struct Functions
958
{
959
  char const *m_name;
960
  int m_arity;
961
};
962
963
static Functions const s_listOperators[] =
964
{
965
  // 0
966
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
967
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
968
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
969
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
970
  // 1
971
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
972
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
973
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
974
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
975
  // 2
976
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
977
  { "", -2}, { ":", 2}, { "", -2}, { "", -2},
978
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
979
  { "", -2}, { ":", 2}, { "", -2}, { "", -2},
980
  // 3
981
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
982
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
983
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
984
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
985
  // 4
986
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
987
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
988
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
989
  { "", -2}, { ":", 2}, { "", -2}, { "", -2},
990
  // 5
991
  { "&", 2}, { "", -2}, { "", -2}, { "", -2},
992
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
993
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
994
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
995
  // 6
996
  { "<", 2}, { "", -2}, { "<=", 2}, { "", -2},
997
  { "=", 2}, { "", -2}, { ">=", 2}, { "", -2},
998
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
999
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1000
  // 7
1001
  { ">", 2}, { "", -2}, { "<>", 2}, { "", -2},
1002
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1003
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1004
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1005
  // 8
1006
  { "", -2}, { "", -2}, { "+", 2}, { "", -2},
1007
  { "-", 2}, { "", -2}, { "*", 2}, { "", -2},
1008
  { "/", 2}, { "", -2}, { "^", 2}, { "", -2},
1009
  { "+", 1}, { "", -2}, { "-", 1}, { "", -2},
1010
  // 9
1011
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1012
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1013
  { "%", 1}, { "", -2}, { "", -2}, { "", -2},
1014
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1015
};
1016
1017
static char const *const s_listFunctions[]=
1018
{
1019
  // 0
1020
  "Count", "If", "IsNA", "IsError",
1021
  "Sum", "Average", "Min", "Max",
1022
  "Row", "Column", "NA", "NPV",
1023
  "Stdev", "Dollar", "Fixed", "Sin",
1024
  // 1
1025
  "Cos", "Tan", "Atan", "Pi",
1026
  "Sqrt", "Exp", "Ln", "Log",
1027
  "Abs", "Int", "Sign", "Round",
1028
  "Lookup", "Index", "Rept", "Mid",
1029
  // 2
1030
  "Len", "Value", "True", "False",
1031
  "And", "Or", "Not", "Mod",
1032
  // mac v1 new functions
1033
  "IterCnt", "Delta", "PV", "FV",
1034
  "NPer", "PMT", "Rate", "MIRR",
1035
  // 3
1036
  "Irr", nullptr, nullptr, nullptr,
1037
  nullptr, nullptr, nullptr, nullptr,
1038
  nullptr, nullptr, nullptr, nullptr,
1039
  nullptr, nullptr, nullptr, nullptr,
1040
};
1041
}
1042
1043
bool MultiplanParser::readFormula(Vec2i const &cellPos, std::vector<WKSContentListener::FormulaInstruction> &formula, long endPos, std::string &error)
1044
119k
{
1045
119k
  formula.clear();
1046
119k
  auto input = getInput();
1047
119k
  std::vector<std::vector<WKSContentListener::FormulaInstruction> > stack;
1048
119k
  auto const &listOperators=MultiplanParserInternal::s_listOperators;
1049
119k
  int const numOperators=int(WPS_N_ELEMENTS(listOperators));
1050
119k
  auto const &listFunctions=MultiplanParserInternal::s_listFunctions;
1051
119k
  int const numFunctions=int(WPS_N_ELEMENTS(listFunctions));
1052
119k
  bool ok=true;
1053
119k
  int closeDelayed=0;
1054
119k
  bool checkForClose=false;
1055
471k
  while (input->tell()<=endPos)
1056
471k
  {
1057
471k
    long pos=input->tell();
1058
471k
    int wh=pos==endPos ? -1 : int(libwps::readU8(input));
1059
471k
    bool needCloseParenthesis=closeDelayed && (checkForClose || pos==endPos);
1060
471k
    ok=true;
1061
471k
    if (closeDelayed && !needCloseParenthesis && wh!=0x3c)
1062
7.38k
      needCloseParenthesis=wh>=numOperators || listOperators[wh].m_arity!=2;
1063
473k
    while (needCloseParenthesis && closeDelayed>0)
1064
8.79k
    {
1065
8.79k
      auto len=stack.size();
1066
8.79k
      if (len<2)
1067
3.18k
      {
1068
3.18k
        error="##closedParenthesis,";
1069
3.18k
        ok=false;
1070
3.18k
        break;
1071
3.18k
      }
1072
5.60k
      auto &dParenthesisFunc=stack[len-2];
1073
5.60k
      if (dParenthesisFunc.size()!=1 || dParenthesisFunc[0].m_type!=dParenthesisFunc[0].F_Operator ||
1074
2.35k
        dParenthesisFunc[0].m_content!="(")
1075
3.93k
      {
1076
3.93k
        error="##closedParenthesis,";
1077
3.93k
        ok=false;
1078
3.93k
        break;
1079
3.93k
      }
1080
1.66k
      dParenthesisFunc.insert(dParenthesisFunc.end(),stack.back().begin(), stack.back().end());
1081
1.66k
      WKSContentListener::FormulaInstruction instr;
1082
1.66k
      instr.m_type=instr.F_Operator;
1083
1.66k
      instr.m_content=")";
1084
1.66k
      dParenthesisFunc.push_back(instr);
1085
1.66k
      stack.resize(len-1);
1086
1.66k
      --closeDelayed;
1087
1.66k
    }
1088
471k
    if (!ok || pos==endPos)
1089
21.2k
      break;
1090
450k
    int arity=0;
1091
450k
    WKSContentListener::FormulaInstruction instr;
1092
450k
    ok=false;
1093
450k
    bool noneInstr=false, closeFunction=false;
1094
450k
    switch (wh)
1095
450k
    {
1096
47.8k
    case 0:
1097
47.8k
    {
1098
47.8k
      if (pos+3>endPos)
1099
5.23k
        break;
1100
42.6k
      ok=readLink(int(libwps::readU16(input)), instr);
1101
42.6k
      break;
1102
47.8k
    }
1103
35.9k
    case 0x12:
1104
35.9k
    {
1105
35.9k
      if (pos+2>endPos)
1106
1.29k
        break;
1107
34.6k
      ok=true;
1108
34.6k
      instr.m_type=instr.F_Function;
1109
34.6k
      int id=int(libwps::readU8(input));
1110
34.6k
      if (id<numFunctions && listFunctions[id])
1111
28.2k
        instr.m_content=listFunctions[id];
1112
6.40k
      else
1113
6.40k
      {
1114
6.40k
        std::stringstream s;
1115
6.40k
        s << "Funct" << std::hex << id << std::dec;
1116
6.40k
        instr.m_content=s.str();
1117
6.40k
      }
1118
34.6k
      std::vector<WKSContentListener::FormulaInstruction> child;
1119
34.6k
      child.push_back(instr);
1120
34.6k
      stack.push_back(child);
1121
34.6k
      instr.m_type=instr.F_Operator;
1122
34.6k
      instr.m_content="(";
1123
34.6k
      break;
1124
35.9k
    }
1125
4.12k
    case 0x67:
1126
6.33k
    case 0x87:
1127
18.8k
    case 0xc7:
1128
18.8k
      closeFunction=ok=true;
1129
18.8k
      break;
1130
6.17k
    case 0x1c: // use before %
1131
10.1k
    case 0x1e: // use for <> A 1e B "code <>"
1132
24.8k
    case 0x34: // use for <=,>= ... A 34 B "code <=,..."
1133
38.0k
    case 0x36: // use before -unary
1134
47.2k
    case 0x38: // use before +unary
1135
47.2k
      noneInstr=ok=true;
1136
47.2k
      break;
1137
16.5k
    case 0x3a:
1138
16.5k
      ok=true;
1139
16.5k
      instr.m_type=instr.F_Operator;
1140
16.5k
      instr.m_content=";";
1141
16.5k
      break;
1142
82.9k
    case 0x3c:
1143
82.9k
      noneInstr=ok=true;
1144
82.9k
      ++closeDelayed;
1145
82.9k
      break;
1146
15.4k
    case 0x3e:
1147
15.4k
      ok=true;
1148
15.4k
      instr.m_type=instr.F_Operator;
1149
15.4k
      instr.m_content="(";
1150
15.4k
      break;
1151
3.74k
    case 0x56:
1152
3.74k
    {
1153
3.74k
      int dSz=int(libwps::readU8(input));
1154
3.74k
      if (pos+2+dSz>endPos)
1155
1.30k
        break;
1156
2.43k
      instr.m_type=instr.F_Text;
1157
2.43k
      instr.m_content=libwps_tools_win::Font::unicodeString(input.get(), unsigned(dSz), m_state->getDefaultFontType()).cstr();
1158
2.43k
      ok=true;
1159
2.43k
      break;
1160
3.74k
    }
1161
2.25k
    case 0x2d:
1162
3.59k
    case 0xed: // example C2
1163
3.59k
      if (pos+5>endPos)
1164
1.62k
        break;
1165
1.97k
      WPS_DEBUG_MSG(("MultiplanParser::readFormula: find column/row solitary definition\n"));
1166
1.97k
      error="###RorC";
1167
1.97k
      ok=false;
1168
1.97k
      break;
1169
1.18k
    case 0xe1: // example C2 R1
1170
1.18k
      WPS_DEBUG_MSG(("MultiplanParser::readFormula: find union operator\n"));
1171
1.18k
      error="###union";
1172
1.18k
      ok=false;
1173
1.18k
      break;
1174
5.61k
    case 0x37: // use for list cell
1175
7.34k
    case 0x53:
1176
9.59k
    case 0x73:
1177
11.5k
    case 0x93: // basic cell
1178
13.3k
    case 0xf3:
1179
13.3k
    {
1180
13.3k
      if (pos+3>endPos)
1181
2.12k
        break;
1182
11.2k
      instr.m_type=instr.F_Cell;
1183
11.2k
      instr.m_positionRelative[0]=Vec2b(true,true);
1184
11.2k
      int val=int(libwps::readU16(input));
1185
11.2k
      auto &newPos=instr.m_position[0];
1186
11.2k
      if (val&0x8000)
1187
2.44k
        newPos[1]=cellPos[1]-(val&0xff);
1188
8.82k
      else
1189
8.82k
        newPos[1]=cellPos[1]+(val&0xff);
1190
11.2k
      if (val&0x4000)
1191
3.63k
        newPos[0]=cellPos[0]-((val>>8)&0x3f);
1192
7.64k
      else
1193
7.64k
        newPos[0]=cellPos[0]+((val>>8)&0x3f);
1194
11.2k
      ok=newPos[0]>=0 && newPos[1]>=0;
1195
11.2k
      break;
1196
13.3k
    }
1197
32.0k
    case 0x94:
1198
32.0k
      if (pos+9>endPos || !readDouble(instr.m_doubleValue))
1199
3.20k
        break;
1200
28.7k
      instr.m_type=instr.F_Double;
1201
28.7k
      ok=true;
1202
28.7k
      break;
1203
37.7k
    case 0x35:
1204
39.4k
    case 0x8f:
1205
41.7k
    case 0xef:
1206
41.7k
      if (pos+3>endPos)
1207
1.88k
        break;
1208
39.8k
      instr.m_type=instr.F_Cell;
1209
39.8k
      instr.m_positionRelative[0]=Vec2b(false,false);
1210
39.8k
      instr.m_position[0][1]=int(libwps::readU8(input));
1211
39.8k
      instr.m_position[0][0]=int(libwps::readU8(input));
1212
39.8k
      ok=(instr.m_position[0][0]<63) && (instr.m_position[0][1]<255);
1213
39.8k
      if (!ok)
1214
2.77k
      {
1215
2.77k
        error="###RorC";
1216
2.77k
        WPS_DEBUG_MSG(("MultiplanParser::readFormula: find only row/column reference\n"));
1217
2.77k
      }
1218
39.8k
      break;
1219
15.0k
    case 0xeb:
1220
15.0k
      if (pos+3>endPos || !readName(int(libwps::readU16(input)), instr))
1221
7.71k
        break;
1222
7.31k
      ok=true;
1223
7.31k
      break;
1224
74.9k
    default:
1225
74.9k
      if (wh<numOperators && listOperators[wh].m_arity!=-2)
1226
41.7k
      {
1227
41.7k
        instr.m_content=listOperators[wh].m_name;
1228
41.7k
        instr.m_type=instr.F_Function;
1229
41.7k
        arity=listOperators[wh].m_arity;
1230
41.7k
      }
1231
74.9k
      if (instr.m_content.empty())
1232
33.2k
      {
1233
33.2k
        WPS_DEBUG_MSG(("MultiplanParser::readFormula: find unknown type %x\n", wh));
1234
33.2k
        std::stringstream s;
1235
33.2k
        s << "##unkn[func]=" << std::hex << wh << std::dec << ",";
1236
33.2k
        error=s.str();
1237
33.2k
        break;
1238
33.2k
      }
1239
41.7k
      ok=true;
1240
41.7k
      break;
1241
450k
    }
1242
450k
    if (!ok)
1243
89.2k
    {
1244
89.2k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1245
89.2k
      break;
1246
89.2k
    }
1247
361k
    checkForClose=!noneInstr && closeDelayed>0;
1248
361k
    if (noneInstr) continue;
1249
231k
    if (closeFunction)
1250
18.8k
    {
1251
18.8k
      ok=false;
1252
18.8k
      if (stack.empty())
1253
2.36k
      {
1254
2.36k
        error="##closed,";
1255
2.36k
        break;
1256
2.36k
      }
1257
16.4k
      auto it=stack.end();
1258
16.4k
      --it;
1259
53.4k
      for (; it!=stack.begin(); --it)
1260
49.0k
      {
1261
49.0k
        if (it->size()!=1) continue;
1262
38.0k
        auto const &dInstr=(*it)[0];
1263
38.0k
        if (dInstr.m_type!=dInstr.F_Operator || dInstr.m_content!="(") continue;
1264
15.5k
        auto fIt=it;
1265
15.5k
        --fIt;
1266
15.5k
        auto &functionStack=*fIt;
1267
15.5k
        if (functionStack.size()!=1 || functionStack[0].m_type!=functionStack[0].F_Function) continue;
1268
11.9k
        ok=true;
1269
40.3k
        for (; it!=stack.end(); ++it)
1270
28.3k
          functionStack.insert(functionStack.end(), it->begin(), it->end());
1271
11.9k
        ++fIt;
1272
11.9k
        stack.erase(fIt, stack.end());
1273
11.9k
        break;
1274
15.5k
      }
1275
16.4k
      if (!ok)
1276
4.47k
      {
1277
4.47k
        error="##closed";
1278
4.47k
        break;
1279
4.47k
      }
1280
11.9k
      instr.m_type=instr.F_Operator;
1281
11.9k
      instr.m_content=")";
1282
11.9k
      stack.back().push_back(instr);
1283
11.9k
      continue;
1284
16.4k
    }
1285
212k
    if (instr.m_type!=WKSContentListener::FormulaInstruction::F_Function)
1286
170k
    {
1287
170k
      std::vector<WKSContentListener::FormulaInstruction> child;
1288
170k
      child.push_back(instr);
1289
170k
      stack.push_back(child);
1290
170k
      continue;
1291
170k
    }
1292
41.7k
    size_t numElt = stack.size();
1293
41.7k
    if (static_cast<int>(numElt) < arity)
1294
2.51k
    {
1295
2.51k
      std::stringstream s;
1296
2.51k
      s << instr.m_content << "[##" << arity << "]";
1297
2.51k
      error=s.str();
1298
2.51k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1299
2.51k
      ok=false;
1300
2.51k
      break;
1301
2.51k
    }
1302
39.2k
    if (arity==1)
1303
28.8k
    {
1304
28.8k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
1305
28.8k
      if (instr.m_content=="%")
1306
24.0k
        stack[numElt-1].push_back(instr);
1307
4.85k
      else
1308
4.85k
        stack[numElt-1].insert(stack[numElt-1].begin(), instr);
1309
28.8k
      continue;
1310
28.8k
    }
1311
10.3k
    if (arity==2)
1312
10.3k
    {
1313
10.3k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
1314
10.3k
      stack[numElt-2].push_back(instr);
1315
10.3k
      stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
1316
10.3k
      stack.resize(numElt-1);
1317
10.3k
      continue;
1318
10.3k
    }
1319
0
    ok=false;
1320
0
    error = "### unexpected arity";
1321
0
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1322
0
    break;
1323
10.3k
  }
1324
119k
  long pos=input->tell();
1325
119k
  if (pos!=endPos || !ok || closeDelayed || stack.size()!=1 || stack[0].empty())
1326
108k
  {
1327
108k
    WPS_DEBUG_MSG(("MultiplanParser::readFormula: can not read a formula\n"));
1328
108k
    ascii().addDelimiter(pos, '|');
1329
108k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
1330
1331
108k
    std::stringstream s;
1332
108k
    if (!error.empty())
1333
55.6k
      s << error;
1334
52.8k
    else
1335
52.8k
      s << "##unknownError";
1336
108k
    s << "[";
1337
108k
    for (auto const &i : stack)
1338
153k
    {
1339
153k
      for (auto const &j : i)
1340
231k
        s << j << ",";
1341
153k
    }
1342
108k
    s << "],";
1343
108k
    error=s.str();
1344
108k
    return true;
1345
108k
  }
1346
11.3k
  formula=stack[0];
1347
11.3k
  return true;
1348
119k
}
1349
1350
namespace MultiplanParserInternal
1351
{
1352
static Functions const s_listOperatorsV2[] =
1353
{
1354
  // 0
1355
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1356
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1357
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1358
  { "", -2}, { "-", 1}, { "+", 1}, { "%", 1},
1359
  // 1
1360
  { "", -2}, { "+", 2}, { "-", 2}, { "*", 2},
1361
  { "/", 2}, { "^", 2}, { "<", 2}, { ">", 2},
1362
  { "<=", 2}, { ">=", 2}, { "=", 2}, { "<>", 2},
1363
  { "&", 2}, { "", -2}, { "", -2}, { "", -2},
1364
  // 2
1365
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1366
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1367
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1368
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1369
};
1370
1371
static char const *const s_listFunctionsV2[]=
1372
{
1373
  // 0
1374
  nullptr /* special Row */, nullptr/* special Column*/, nullptr/* checkme: unknown*/, nullptr/* checkme: unknown*/,
1375
  nullptr /* special Index */, nullptr/* special LookUp*/, "Count", "If",
1376
  "IsNa", "IsError", "Sum", "Average",
1377
  "Min", "Max", "NA", "NPV",
1378
  // 1
1379
  "StDev", "Dollar", "Fixed", "Sin",
1380
  "Cos", "Tan", "Atan", "Pi",
1381
  "Sqrt", "Exp", "Ln", "Log10",
1382
  "Abs", "Int", "Sign", "Round",
1383
  // 2
1384
  "Rept", "Mid", "Len", "Value",
1385
  "True", "False", "And", "Or",
1386
  "Not", "Mod", "PV", "NV",
1387
  "NPER", "PMT", "Rate", "Mirr",
1388
  // 3
1389
  "Irr", nullptr /* special Now */, nullptr /* checkme: unknown */, "Date",
1390
  "Time", "Day", "Month", "WeekDay",
1391
  "Year", "Hour", "Minute", "Second",
1392
  nullptr, nullptr, nullptr, nullptr,
1393
};
1394
}
1395
1396
bool MultiplanParser::readFormulaV2(Vec2i const &cellPos, std::vector<WKSContentListener::FormulaInstruction> &formula, long endZonePos, std::string &error)
1397
88.6k
{
1398
88.6k
  formula.clear();
1399
88.6k
  auto input = getInput();
1400
88.6k
  long pos=input->tell();
1401
88.6k
  int dSz=int(libwps::readU8(input));
1402
88.6k
  long endPos=pos+2*dSz;
1403
88.6k
  if (dSz==0 || endPos>endZonePos)
1404
13.3k
  {
1405
13.3k
    WPS_DEBUG_MSG(("MultiplanParser::readFormulaV2: the zone seems bad\n"));
1406
13.3k
    return false;
1407
13.3k
  }
1408
75.3k
  if (endZonePos!=endPos)
1409
68.8k
    ascii().addDelimiter(endPos, '|');
1410
75.3k
  long dataPos=-1;
1411
75.3k
  long dataSize=0;
1412
75.3k
  if (endZonePos>endPos+2)
1413
34.7k
  {
1414
34.7k
    input->seek(endPos+1, librevenge::RVNG_SEEK_SET);
1415
34.7k
    dSz=int(libwps::readU8(input));
1416
34.7k
    if (dSz && endPos+2+2*dSz<=endZonePos)
1417
23.4k
    {
1418
23.4k
      dataSize=dSz;
1419
23.4k
      dataPos=endPos+2;
1420
23.4k
    }
1421
34.7k
  }
1422
75.3k
  std::vector<std::vector<WKSContentListener::FormulaInstruction> > stack;
1423
75.3k
  auto const &listOperators=MultiplanParserInternal::s_listOperatorsV2;
1424
75.3k
  int const numOperators=int(WPS_N_ELEMENTS(MultiplanParserInternal::s_listOperatorsV2));
1425
75.3k
  auto const &listFunctions=MultiplanParserInternal::s_listFunctionsV2;
1426
75.3k
  int const numFunctions=int(WPS_N_ELEMENTS(MultiplanParserInternal::s_listFunctionsV2));
1427
75.3k
  bool ok=true;
1428
75.3k
  input->seek(pos+1, librevenge::RVNG_SEEK_SET);
1429
246k
  while (input->tell()<endPos)
1430
233k
  {
1431
233k
    pos=input->tell();
1432
233k
    int code=int(libwps::readU8(input));
1433
233k
    if (code==0) break;
1434
211k
    WKSContentListener::FormulaInstruction instr;
1435
211k
    ok=false;
1436
211k
    int arity=0, cellId=-1;
1437
211k
    bool noneInstr=false;
1438
211k
    switch (code)
1439
211k
    {
1440
50.5k
    case 1:
1441
50.5k
    {
1442
50.5k
      if (pos+1>endPos)
1443
0
        break;
1444
50.5k
      dSz=int(libwps::readU8(input));
1445
50.5k
      if (dSz==0 || pos+1+dSz>endPos)
1446
3.36k
        break;
1447
47.1k
      double mantisse = 0;
1448
71.4k
      for (int i = 0; i < dSz-2; i++)
1449
24.2k
        mantisse = mantisse/256 + double(readU8(input));
1450
47.1k
      auto mantExp = dSz>=2 ? int(readU8(input)) : 0;
1451
47.1k
      mantisse = (mantisse/256 + double(0x10+(mantExp & 0x0F)))/16;
1452
47.1k
      int exp = ((mantExp&0xF0)>>4)+int(readU8(input)<<4);
1453
47.1k
      int sign = 1;
1454
47.1k
      if (exp & 0x800)
1455
2.52k
      {
1456
2.52k
        exp &= 0x7ff;
1457
2.52k
        sign = -1;
1458
2.52k
      }
1459
47.1k
      exp -= 0x3ff;
1460
47.1k
      instr.m_type=instr.F_Double;
1461
47.1k
      instr.m_doubleValue = sign * std::ldexp(mantisse, exp);
1462
47.1k
      ok=true;
1463
47.1k
      break;
1464
50.5k
    }
1465
4.44k
    case 2:
1466
4.44k
    {
1467
4.44k
      if (pos+1>endPos)
1468
0
        break;
1469
4.44k
      dSz=int(libwps::readU8(input));
1470
4.44k
      if (pos+1+dSz>endPos)
1471
1.16k
        break;
1472
3.27k
      instr.m_type=instr.F_Text;
1473
3.27k
      instr.m_content=libwps_tools_win::Font::unicodeString(input.get(), unsigned(dSz), m_state->getDefaultFontType()).cstr();
1474
3.27k
      ok=true;
1475
3.27k
      break;
1476
4.44k
    }
1477
6.20k
    case 0x4: // cell in function
1478
20.9k
    case 0x6: // cell in basic formula
1479
20.9k
      if (pos+2>endPos)
1480
648
        break;
1481
20.2k
      cellId=libwps::readU8(input);
1482
20.2k
      break;
1483
8.77k
    case 0x7: // function without argument
1484
30.4k
    case 0x8: // function with argument
1485
39.1k
    case 0x9: // special
1486
49.4k
    case 0xa:   // special with arg
1487
49.4k
    {
1488
49.4k
      if (pos+2>endPos)
1489
1.33k
        break;
1490
48.0k
      instr.m_type=instr.F_Function;
1491
48.0k
      int id=int(libwps::readU8(input));
1492
48.0k
      if (code==9 || code==0xa)
1493
18.4k
      {
1494
18.4k
        int fId=-1;
1495
18.4k
        if (id+1<=dataSize)
1496
5.78k
        {
1497
5.78k
          input->seek(dataPos+2*id, librevenge::RVNG_SEEK_SET);
1498
5.78k
          int val=int(libwps::readU8(input));
1499
5.78k
          if (val==6) fId=int(libwps::readU8(input));
1500
5.78k
          input->seek(pos+2, librevenge::RVNG_SEEK_SET);
1501
5.78k
        }
1502
18.4k
        char const *wh[]= { "Row", "Column", nullptr, nullptr, "Index", "LookUp" };
1503
18.4k
        if (fId>=0 && fId<int(WPS_N_ELEMENTS(wh)) && wh[fId])
1504
1.76k
          instr.m_content=wh[fId];
1505
16.6k
        else if (fId==0x31)
1506
110
          instr.m_content="Now";
1507
16.5k
        else
1508
16.5k
        {
1509
16.5k
          WPS_DEBUG_MSG(("MultiplanParser::readFormulaV2: can not find a function id\n"));
1510
16.5k
          error += "###fId";
1511
16.5k
          std::stringstream s;
1512
16.5k
          s << "FunctId" << fId;
1513
16.5k
          instr.m_content=s.str();
1514
16.5k
        }
1515
18.4k
      }
1516
29.6k
      else if (id<numFunctions && listFunctions[id])
1517
20.5k
        instr.m_content=listFunctions[id];
1518
9.05k
      else
1519
9.05k
      {
1520
9.05k
        WPS_DEBUG_MSG(("MultiplanParser::readFormulaV2: can not find a function %d\n", id));
1521
9.05k
        error += "###fId";
1522
9.05k
      }
1523
48.0k
      if (instr.m_content.empty())
1524
9.05k
      {
1525
9.05k
        std::stringstream s;
1526
9.05k
        s << "Funct" << std::hex << id << std::dec;
1527
9.05k
        instr.m_content=s.str();
1528
9.05k
      }
1529
48.0k
      std::vector<WKSContentListener::FormulaInstruction> child;
1530
48.0k
      child.push_back(instr);
1531
48.0k
      instr.m_type=instr.F_Operator;
1532
48.0k
      instr.m_content="(";
1533
48.0k
      if ((code%2)==0)
1534
30.6k
      {
1535
30.6k
        stack.push_back(child);
1536
30.6k
        ok=true;
1537
30.6k
        break;
1538
30.6k
      }
1539
17.4k
      child.push_back(instr);
1540
17.4k
      instr.m_content=")";
1541
17.4k
      child.push_back(instr);
1542
17.4k
      stack.push_back(child);
1543
17.4k
      ok=noneInstr=true;
1544
17.4k
      break;
1545
48.0k
    }
1546
7.49k
    case 0xb: // close 0x8: 1 arg
1547
15.7k
    case 0xc:   // close 0x8: >=2 arg
1548
15.7k
    {
1549
15.7k
      if (code==0xc)   // ignore the num arg
1550
8.27k
      {
1551
8.27k
        if (pos+2>endPos)
1552
549
          break;
1553
7.72k
        input->seek(1, librevenge::RVNG_SEEK_CUR);
1554
7.72k
      }
1555
15.2k
      if (stack.empty())
1556
1.89k
      {
1557
1.89k
        error="##closed,";
1558
1.89k
        break;
1559
1.89k
      }
1560
13.3k
      auto it=stack.end();
1561
13.3k
      --it;
1562
47.3k
      for (; it!=stack.begin(); --it)
1563
45.9k
      {
1564
45.9k
        if (it->size()!=1) continue;
1565
42.1k
        auto const &dInstr=(*it)[0];
1566
42.1k
        if (dInstr.m_type!=dInstr.F_Operator || dInstr.m_content!="(") continue;
1567
11.9k
        auto fIt=it;
1568
11.9k
        --fIt;
1569
11.9k
        auto &functionStack=*fIt;
1570
11.9k
        if (functionStack.size()!=1 || functionStack[0].m_type!=functionStack[0].F_Function) continue;
1571
11.9k
        ok=true;
1572
55.8k
        for (; it!=stack.end(); ++it)
1573
43.9k
          functionStack.insert(functionStack.end(), it->begin(), it->end());
1574
11.9k
        ++fIt;
1575
11.9k
        stack.erase(fIt, stack.end());
1576
11.9k
        break;
1577
11.9k
      }
1578
13.3k
      if (!ok)
1579
1.38k
      {
1580
1.38k
        error="##closed";
1581
1.38k
        break;
1582
1.38k
      }
1583
11.9k
      instr.m_type=instr.F_Operator;
1584
11.9k
      instr.m_content=")";
1585
11.9k
      stack.back().push_back(instr);
1586
11.9k
      noneInstr=true;
1587
11.9k
      break;
1588
13.3k
    }
1589
19.8k
    case 0x21:
1590
19.8k
      instr.m_type=instr.F_Operator;
1591
19.8k
      instr.m_content=";";
1592
19.8k
      ok=true;
1593
19.8k
      break;
1594
2.19k
    case 0x22:
1595
2.19k
      ok=true;
1596
2.19k
      arity=1;
1597
2.19k
      instr.m_type=instr.F_Function;
1598
2.19k
      instr.m_content=")";
1599
2.19k
      break;
1600
2.44k
    case 3: // begin of parenthesis
1601
3.75k
    case 0x23: // associate with -
1602
4.56k
    case 0x24: // associate with +
1603
6.67k
    case 0x25:
1604
7.46k
    case 0x26:
1605
7.46k
      noneInstr=ok=true;
1606
7.46k
      break;
1607
3.03k
    case 0x28:
1608
3.03k
      if (pos+3>endPos)
1609
262
        break;
1610
2.76k
      ok=readLink(int(libwps::readU16(input)), instr);
1611
2.76k
      break;
1612
37.6k
    default:
1613
37.6k
    {
1614
37.6k
      if (code<numOperators && listOperators[code].m_arity!=-2)
1615
21.0k
      {
1616
21.0k
        instr.m_content=listOperators[code].m_name;
1617
21.0k
        instr.m_type=instr.F_Function;
1618
21.0k
        arity=listOperators[code].m_arity;
1619
21.0k
      }
1620
37.6k
      if (!instr.m_content.empty())
1621
21.0k
      {
1622
21.0k
        ok=true;
1623
21.0k
        break;
1624
21.0k
      }
1625
16.6k
      if (code>=0x80)
1626
11.7k
      {
1627
11.7k
        cellId=code-0x80;
1628
11.7k
        break;
1629
11.7k
      }
1630
4.84k
      WPS_DEBUG_MSG(("MultiplanParser::readFormulaV2: find unknown type %x\n", code));
1631
4.84k
      std::stringstream s;
1632
4.84k
      s << "##unkn[func]=" << std::hex << code << std::dec << ",";
1633
4.84k
      error=s.str();
1634
4.84k
      break;
1635
16.6k
    }
1636
211k
    }
1637
211k
    if (!ok && cellId>=0 && cellId+2<=dataSize)
1638
23.9k
    {
1639
23.9k
      long actPos=input->tell();
1640
23.9k
      input->seek(dataPos+2*cellId, librevenge::RVNG_SEEK_SET);
1641
23.9k
      int headerVal=int(libwps::readU8(input));
1642
23.9k
      int numCell=(headerVal&5)==5 ? 2 : 1;
1643
23.9k
      if (numCell==2 && actPos+6>=endPos)
1644
770
      {
1645
770
        WPS_DEBUG_MSG(("MultiplanParser::readFormulaV2: oops can not reserve extra space\n"));
1646
770
        error="###extraSz,";
1647
770
      }
1648
23.1k
      else
1649
23.1k
      {
1650
23.1k
        if (numCell==2)
1651
10.3k
          endPos-=6;
1652
23.1k
        int actCellId=2*cellId+1;
1653
23.1k
        if (numCell==2)   // small number maybe rel/abs
1654
10.3k
        {
1655
10.3k
          input->seek(1, librevenge::RVNG_SEEK_CUR);
1656
10.3k
          ++actCellId;
1657
10.3k
        }
1658
23.1k
        instr.m_type=numCell==1 ? instr.F_Cell : instr.F_CellList;
1659
38.9k
        for (int c=0; c<numCell; ++c)
1660
29.5k
        {
1661
29.5k
          int val=headerVal;
1662
29.5k
          if (numCell==2)
1663
16.7k
          {
1664
16.7k
            val=int(libwps::readU8(input));
1665
16.7k
            ++actCellId;
1666
16.7k
          }
1667
29.5k
          if ((val&3)==1 || (val&3)==2)   // 1: row, 2:col
1668
2.30k
          {
1669
2.30k
            error="##RorC";
1670
2.30k
            WPS_DEBUG_MSG(("MultiplanParser::readFormulaV2: find only row/column reference\n"));
1671
2.30k
            break;
1672
2.30k
          }
1673
27.2k
          if (val&4)
1674
4.86k
          {
1675
4.86k
            if (actCellId+3>2*dataSize) break;
1676
4.57k
            input->seek(1, librevenge::RVNG_SEEK_CUR);
1677
4.57k
            WKSContentListener::FormulaInstruction nameInstr;
1678
4.57k
            if (!readName(int(libwps::readU16(input)), nameInstr))
1679
1.92k
              break;
1680
2.64k
            if (nameInstr.m_type==nameInstr.F_Text)
1681
1.86k
              instr=nameInstr;
1682
782
            else
1683
782
            {
1684
1.66k
              for (int d=0; d+c<2; ++d)
1685
1.55k
              {
1686
1.55k
                if (d==1)
1687
771
                {
1688
771
                  if (nameInstr.m_type!=nameInstr.F_CellList || numCell==2)
1689
672
                    break;
1690
99
                  instr.m_type=instr.F_CellList;
1691
99
                }
1692
881
                instr.m_position[c+d]=nameInstr.m_position[d];
1693
881
                instr.m_positionRelative[c+d]=nameInstr.m_positionRelative[d];
1694
881
              }
1695
782
            }
1696
2.64k
            ok=c+1==numCell;
1697
2.64k
            continue;
1698
4.57k
          }
1699
22.3k
          if (actCellId+3>2*dataSize) break;
1700
21.8k
          int col=int(libwps::readU8(input));
1701
21.8k
          val=int(libwps::readU16(input));
1702
21.8k
          if (val&0x4000)
1703
9.16k
          {
1704
9.16k
            if (val&0x1000)
1705
3.79k
              instr.m_position[c][0]=cellPos[0]-col;
1706
5.37k
            else
1707
5.37k
              instr.m_position[c][0]=cellPos[0]+col;
1708
9.16k
            instr.m_positionRelative[c][0]=true;
1709
9.16k
          }
1710
12.7k
          else
1711
12.7k
          {
1712
12.7k
            instr.m_position[c][0]=col;
1713
12.7k
            instr.m_positionRelative[c][0]=false;
1714
12.7k
          }
1715
21.8k
          int row=val&0xfff;
1716
21.8k
          if (val&0x8000)
1717
9.83k
          {
1718
9.83k
            if (val&0x2000)
1719
6.79k
              instr.m_position[c][1]=cellPos[1]-row;
1720
3.04k
            else
1721
3.04k
              instr.m_position[c][1]=cellPos[1]+row;
1722
9.83k
            instr.m_positionRelative[c][1]=true;
1723
9.83k
          }
1724
12.0k
          else
1725
12.0k
          {
1726
12.0k
            instr.m_position[c][1]=row;
1727
12.0k
            instr.m_positionRelative[c][1]=false;
1728
12.0k
          }
1729
21.8k
          if (instr.m_position[c][0]<0 || instr.m_position[c][0]>=255 ||
1730
14.4k
            instr.m_position[c][1]<0 || instr.m_position[c][1]>=4095)
1731
8.80k
            break;
1732
13.0k
          ok=c+1==numCell;
1733
13.0k
        }
1734
23.1k
      }
1735
23.9k
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
1736
23.9k
      if (!ok)
1737
14.6k
      {
1738
14.6k
        WPS_DEBUG_MSG(("MultiplanParser::readFormulaV2: can not find a cell id[%d]\n", cellId));
1739
14.6k
        std::stringstream s;
1740
14.6k
        s << "###cell" << std::hex << code << std::dec;
1741
14.6k
        error+=s.str();
1742
14.6k
        break;
1743
14.6k
      }
1744
23.9k
    }
1745
196k
    if (!ok)
1746
24.3k
    {
1747
24.3k
      input->seek(pos,librevenge::RVNG_SEEK_SET);
1748
24.3k
      break;
1749
24.3k
    }
1750
172k
    if (noneInstr) continue;
1751
135k
    if (instr.m_type!=WKSContentListener::FormulaInstruction::F_Function)
1752
112k
    {
1753
112k
      std::vector<WKSContentListener::FormulaInstruction> child;
1754
112k
      child.push_back(instr);
1755
112k
      stack.push_back(child);
1756
112k
      continue;
1757
112k
    }
1758
23.2k
    size_t numElt = stack.size();
1759
23.2k
    if (static_cast<int>(numElt) < arity)
1760
1.42k
    {
1761
1.42k
      std::stringstream s;
1762
1.42k
      s << instr.m_content << "[##" << arity << "]";
1763
1.42k
      error=s.str();
1764
1.42k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1765
1.42k
      ok=false;
1766
1.42k
      break;
1767
1.42k
    }
1768
21.8k
    if (arity==1)
1769
6.13k
    {
1770
6.13k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
1771
6.13k
      if (instr.m_content=="%")
1772
1.81k
        stack[numElt-1].push_back(instr);
1773
4.31k
      else if (instr.m_content==")")
1774
2.10k
      {
1775
2.10k
        stack[numElt-1].push_back(instr);
1776
2.10k
        instr.m_content="(";
1777
2.10k
        stack[numElt-1].insert(stack[numElt-1].begin(), instr);
1778
2.10k
      }
1779
2.21k
      else
1780
2.21k
        stack[numElt-1].insert(stack[numElt-1].begin(), instr);
1781
6.13k
      continue;
1782
6.13k
    }
1783
15.6k
    if (arity==2)
1784
15.6k
    {
1785
15.6k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
1786
15.6k
      stack[numElt-2].push_back(instr);
1787
15.6k
      stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
1788
15.6k
      stack.resize(numElt-1);
1789
15.6k
      continue;
1790
15.6k
    }
1791
0
    ok=false;
1792
0
    error = "### unexpected arity";
1793
0
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1794
0
    break;
1795
15.6k
  }
1796
75.3k
  pos=input->tell();
1797
75.3k
  if (pos!=endPos || !ok || stack.size()!=1 || stack[0].empty())
1798
50.8k
  {
1799
50.8k
    WPS_DEBUG_MSG(("MultiplanParser::readFormula: can not read a formula\n"));
1800
50.8k
    ascii().addDelimiter(pos, '|');
1801
50.8k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
1802
1803
50.8k
    std::stringstream s;
1804
50.8k
    if (!error.empty())
1805
32.9k
      s << error;
1806
17.8k
    else
1807
17.8k
      s << "##unknownError";
1808
50.8k
    s << "[";
1809
50.8k
    for (auto const &i : stack)
1810
76.1k
    {
1811
76.1k
      for (auto const &j : i)
1812
121k
        s << j << ",";
1813
76.1k
    }
1814
50.8k
    s << "],";
1815
50.8k
    error=s.str();
1816
50.8k
    return true;
1817
50.8k
  }
1818
24.5k
  formula=stack[0];
1819
24.5k
  return true;
1820
75.3k
}
1821
////////////////////////////////////////////////////////////
1822
//   generic
1823
////////////////////////////////////////////////////////////
1824
bool MultiplanParser::readCellDataPosition(WPSEntry const &entry)
1825
5.94k
{
1826
5.94k
  if (m_state->m_maximumCell[0]<=0 || m_state->m_maximumCell[1]<=0 ||
1827
5.91k
    entry.length()/2/m_state->m_maximumCell[0]<m_state->m_maximumCell[1])
1828
105
  {
1829
105
    WPS_DEBUG_MSG(("MultiplanParser::readCellDataPosition: the zone seems bad\n"));
1830
105
    return false;
1831
105
  }
1832
5.83k
  auto input = getInput();
1833
5.83k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1834
5.83k
  libwps::DebugStream f;
1835
5.83k
  f << "Entries(CellPos):";
1836
5.83k
  auto &posSet=m_state->m_zonesList[0].m_positionsSet;
1837
440k
  for (int i=0; i<m_state->m_maximumCell[0]; ++i)
1838
434k
  {
1839
434k
    f << "[" << std::hex;
1840
434k
    bool hasValues=false;
1841
434k
    std::vector<int> cellPos;
1842
2.45M
    for (int j=0; j<m_state->m_maximumCell[1]; ++j)
1843
2.01M
    {
1844
2.01M
      cellPos.push_back(int(libwps::readU16(input)));
1845
2.01M
      posSet.insert(cellPos.back());
1846
2.01M
      if (cellPos.back())
1847
1.61M
      {
1848
1.61M
        hasValues=true;
1849
1.61M
        f << cellPos.back() << ",";
1850
1.61M
      }
1851
404k
      else
1852
404k
        f << "_,";
1853
2.01M
    }
1854
434k
    f << std::dec << "],";
1855
434k
    if (hasValues)
1856
384k
      m_state->m_cellPositionsMap[i]=cellPos;
1857
434k
  }
1858
5.83k
  if (input->tell()!=entry.end())
1859
4.20k
  {
1860
4.20k
    WPS_DEBUG_MSG(("MultiplanParser::readCellDataPosition: find extra data\n"));
1861
4.20k
    f << "###extra";
1862
4.20k
    ascii().addDelimiter(input->tell(),'|');
1863
4.20k
  }
1864
5.83k
  ascii().addPos(entry.begin());
1865
5.83k
  ascii().addNote(f.str().c_str());
1866
5.83k
  return true;
1867
5.94k
}
1868
1869
bool MultiplanParser::readLink(int pos, WKSContentListener::FormulaInstruction &instruction)
1870
45.3k
{
1871
45.3k
  auto it=m_state->m_posToLinkMap.find(pos);
1872
45.3k
  if (it!=m_state->m_posToLinkMap.end())
1873
10.3k
  {
1874
10.3k
    instruction=it->second;
1875
10.3k
    return true;
1876
10.3k
  }
1877
35.0k
  int const vers=version();
1878
35.0k
  auto const &entry=m_state->m_entries[MultiplanParserInternal::Zone::Link];
1879
35.0k
  if (!entry.valid() || pos<0 || pos+9>entry.length())
1880
13.6k
  {
1881
13.6k
    WPS_DEBUG_MSG(("MultiplanParser::readLink: the pos %d seems bad\n", pos));
1882
13.6k
    return false;
1883
13.6k
  }
1884
21.4k
  auto input = getInput();
1885
21.4k
  long actPos=input->tell();
1886
21.4k
  long begPos=entry.begin()+pos;
1887
21.4k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
1888
21.4k
  libwps::DebugStream f;
1889
21.4k
  f << "Link-" << std::hex << pos << std::dec << "[pos]:";
1890
21.4k
  int val=libwps::readU16(input);
1891
21.4k
  int dSz=(val&0x1f);
1892
21.4k
  int type=(val>>5);
1893
21.4k
  if ((val>>5)!=2) f << "type=" << (val>>5) << ",";
1894
21.4k
  val=libwps::readU16(input);
1895
21.4k
  librevenge::RVNGString filename;
1896
21.4k
  if (pos+(vers==1 ? 8 : 10)+dSz>entry.length() || !readFilename(val, filename))
1897
8.91k
  {
1898
8.91k
    WPS_DEBUG_MSG(("MultiplanParser::readLink: the pos %d seems bad\n", pos));
1899
8.91k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
1900
8.91k
    return false;
1901
8.91k
  }
1902
12.4k
  int rows[2];
1903
24.9k
  for (auto &r : rows) r=vers==1 ? int(libwps::readU8(input)) : int(libwps::readU16(input));
1904
12.4k
  int cols[2];
1905
24.9k
  for (auto &c : cols) c=int(libwps::readU8(input));
1906
12.4k
  if (rows[0]==rows[1] && cols[0]==cols[1]) // used position?
1907
4.82k
    f << "pos=" << Vec2i(cols[0],rows[0]) << ",";
1908
7.65k
  else
1909
7.65k
    f << "pos=" << WPSBox2i(Vec2i(cols[0],rows[0]),Vec2i(cols[1],rows[1])) << ",";
1910
12.4k
  bool ok=true;
1911
12.4k
  switch (type)
1912
12.4k
  {
1913
302
  case 2: // file + cell's name
1914
    // we can not a named reference to a link, so store the reference in a text zone
1915
302
    filename.append(':');
1916
302
    filename.append(libwps_tools_win::Font::unicodeString(input.get(), unsigned(dSz), m_state->getDefaultFontType()));
1917
302
    instruction.m_type=instruction.F_Text;
1918
302
    instruction.m_content=filename.cstr();
1919
302
    break;
1920
2.19k
  case 3: // file + cell's reference
1921
2.19k
    if ((vers==1 && dSz!=4) || (vers>1 && dSz!=6))
1922
1.88k
    {
1923
1.88k
      WPS_DEBUG_MSG(("MultiplanParser::readLink: unexpected size\n"));
1924
1.88k
      f << "##";
1925
1.88k
      ok=false;
1926
1.88k
      break;
1927
1.88k
    }
1928
305
    instruction.m_fileName=filename;
1929
305
    instruction.m_sheetName[0]=instruction.m_sheetName[1]="Sheet0";
1930
610
    for (auto &r : rows) r=vers==1 ? int(libwps::readU8(input)) : int(libwps::readU16(input));
1931
610
    for (auto &c : cols) c=int(libwps::readU8(input));
1932
915
    for (int i=0; i<2; ++i)
1933
610
    {
1934
610
      instruction.m_position[i]=Vec2i(cols[i], rows[i]);
1935
610
      instruction.m_positionRelative[i]=Vec2b(false,false);
1936
610
    }
1937
305
    instruction.m_type=instruction.m_position[0]==instruction.m_position[1] ? instruction.F_Cell : instruction.F_CellList;
1938
305
    f << instruction << ",";
1939
305
    break;
1940
9.99k
  default:
1941
9.99k
    WPS_DEBUG_MSG(("MultiplanParser::readFilename: unknown type\n"));
1942
9.99k
    f << "##";
1943
9.99k
    ok=false;
1944
9.99k
    break;
1945
12.4k
  }
1946
12.4k
  if (ok)
1947
607
    m_state->m_posToLinkMap[pos]=instruction;
1948
12.4k
  ascii().addPos(begPos);
1949
12.4k
  ascii().addNote(f.str().c_str());
1950
12.4k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
1951
12.4k
  return true;
1952
12.4k
}
1953
1954
bool MultiplanParser::readFilename(int pos, librevenge::RVNGString &filename)
1955
19.9k
{
1956
19.9k
  filename.clear();
1957
19.9k
  auto const &entry=m_state->m_entries[MultiplanParserInternal::Zone::FileName];
1958
19.9k
  if (!entry.valid() || pos<0 || pos+3>entry.length())
1959
4.73k
  {
1960
4.73k
    WPS_DEBUG_MSG(("MultiplanParser::readFilename: the pos %d seems bad\n", pos));
1961
4.73k
    return false;
1962
4.73k
  }
1963
15.2k
  auto input = getInput();
1964
15.2k
  long actPos=input->tell();
1965
15.2k
  long begPos=entry.begin()+pos;
1966
15.2k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
1967
15.2k
  libwps::DebugStream f;
1968
15.2k
  f << "FileName-" << std::hex << pos << std::dec << ":";
1969
15.2k
  int val=libwps::readU16(input);
1970
15.2k
  int dSz=(val&0x1f);
1971
15.2k
  if (pos+2+dSz>entry.length())
1972
1.31k
  {
1973
1.31k
    WPS_DEBUG_MSG(("MultiplanParser::readFilename: the pos %d seems bad\n", pos));
1974
1.31k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
1975
1.31k
    return false;
1976
1.31k
  }
1977
13.9k
  if (val>>5) f << "f0=" << (val>>5) << ",";
1978
13.9k
  filename=libwps_tools_win::Font::unicodeString(input.get(), unsigned(dSz), m_state->getDefaultFontType());
1979
13.9k
  ascii().addDelimiter(input->tell(),'|');
1980
13.9k
  ascii().addPos(begPos);
1981
13.9k
  ascii().addNote(f.str().c_str());
1982
13.9k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
1983
13.9k
  return !filename.empty();
1984
15.2k
}
1985
1986
bool MultiplanParser::readName(int pos, WKSContentListener::FormulaInstruction &instruction)
1987
18.1k
{
1988
18.1k
  int const vers=version();
1989
18.1k
  auto it=m_state->m_posToNameMap.find(pos);
1990
18.1k
  if (it!=m_state->m_posToNameMap.end())
1991
7.60k
  {
1992
7.60k
    instruction=it->second;
1993
7.60k
    return true;
1994
7.60k
  }
1995
10.5k
  auto const &entry=m_state->m_entries[MultiplanParserInternal::Zone::Name];
1996
10.5k
  if (!entry.valid() || pos<0 || pos+9>=entry.length())
1997
4.32k
  {
1998
4.32k
    WPS_DEBUG_MSG(("MultiplanParser::readName: the pos %d seeems bad\n", pos));
1999
4.32k
    return false;
2000
4.32k
  }
2001
6.21k
  auto input = getInput();
2002
6.21k
  long actPos=input->tell();
2003
6.21k
  long begPos=entry.begin()+pos;
2004
6.21k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
2005
6.21k
  libwps::DebugStream f;
2006
6.21k
  f << "Names-" << std::hex << pos << std::dec << ":";
2007
6.21k
  int val;
2008
6.21k
  if (vers==1)
2009
3.64k
  {
2010
3.64k
    val=int(libwps::readU16(input));
2011
3.64k
    f << "unkn=" << std::hex << val << std::dec << ",";
2012
3.64k
  }
2013
6.21k
  val=int(libwps::readU8(input));
2014
6.21k
  int dSz=(val&0x1f);
2015
6.21k
  if (dSz<=0 || begPos+8+dSz>entry.end())
2016
3.85k
  {
2017
3.85k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
2018
3.85k
    WPS_DEBUG_MSG(("MultiplanParser::readName: the pos %d seeems bad\n", pos));
2019
3.85k
    return false;
2020
3.85k
  }
2021
2.35k
  if (val>>5) f << "f0=" << (val>>5) << ",";
2022
2.35k
  val=int(libwps::readU8(input)); // v1: 0|40, v2: 0|4
2023
2.35k
  if (val) f << "f1=" << std::hex << val << std::dec << ",";
2024
2.35k
  int type=6;
2025
2.35k
  if (vers>=2)
2026
1.52k
  {
2027
1.52k
    type=int(libwps::read16(input));
2028
1.52k
    if (type!=6) f << "type=" << type << ",";
2029
1.52k
    f << "unk=[";
2030
4.57k
    for (int i=0; i<2; ++i)   // _,_
2031
3.05k
    {
2032
3.05k
      val=int(libwps::read16(input));
2033
3.05k
      if (val)
2034
2.15k
        f << val << ",";
2035
899
      else
2036
899
        f << "_,";
2037
3.05k
    }
2038
1.52k
    f << "],";
2039
1.52k
  }
2040
2.35k
  bool ok=true;
2041
2.35k
  if (type==6)
2042
1.09k
  {
2043
1.09k
    if (input->tell()+(vers==1 ? 4 : 6)+dSz<=entry.end())
2044
1.06k
    {
2045
1.06k
      int rows[2];
2046
2.12k
      for (auto &r : rows) r=vers==1 ? int(libwps::readU8(input)) : int(libwps::readU16(input));
2047
1.06k
      int cols[2];
2048
2.12k
      for (auto &c : cols) c=int(libwps::readU8(input));
2049
3.18k
      for (int i=0; i<2; ++i)
2050
2.12k
      {
2051
2.12k
        instruction.m_position[i]=Vec2i(cols[i], rows[i]);
2052
2.12k
        instruction.m_positionRelative[i]=Vec2b(false,false);
2053
2.12k
      }
2054
1.06k
      instruction.m_type=instruction.m_position[0]==instruction.m_position[1] ? instruction.F_Cell : instruction.F_CellList;
2055
1.06k
      f << instruction << ",";
2056
1.06k
    }
2057
35
    else
2058
35
    {
2059
35
      f << "###";
2060
35
      WPS_DEBUG_MSG(("MultiplanParser::readName: the pos %d seeems bad\n", pos));
2061
35
    }
2062
1.09k
  }
2063
2.35k
  std::string name;
2064
36.4k
  for (int c=0; c<dSz; ++c) name+=char(libwps::readU8(input));
2065
2.35k
  f << name << ",";
2066
2.35k
  switch (type)
2067
2.35k
  {
2068
183
  case 0: // bad name?
2069
183
    instruction.m_type=instruction.F_Text;
2070
183
    instruction.m_content=name;
2071
183
    break;
2072
1.09k
  case 6: // cell list
2073
1.09k
    break;
2074
1.07k
  default:
2075
1.07k
    f << "###";
2076
1.07k
    WPS_DEBUG_MSG(("MultiplanParser::readName: unknown type for pos %d\n", pos));
2077
1.07k
    ok=false;
2078
1.07k
    break;
2079
2.35k
  }
2080
2.35k
  if (ok)
2081
1.28k
    m_state->m_posToNameMap[pos]=instruction;
2082
2.35k
  ascii().addPos(begPos);
2083
2.35k
  ascii().addNote(f.str().c_str());
2084
2085
2.35k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
2086
2.35k
  return true;
2087
2.35k
}
2088
2089
bool MultiplanParser::readSharedData(int pos, int cellType, Vec2i const &cellPos, WKSContentListener::CellContent &content)
2090
78.5k
{
2091
78.5k
  int const vers=version();
2092
78.5k
  auto const &entry=m_state->m_entries[MultiplanParserInternal::Zone::SharedData];
2093
78.5k
  if (!entry.valid() || pos<0 || pos+(vers==1 ? 3 : 4) >entry.length())
2094
17.8k
  {
2095
17.8k
    WPS_DEBUG_MSG(("MultiplanParser::readSharedData: the pos %d seems bad\n", pos));
2096
17.8k
    return false;
2097
17.8k
  }
2098
60.6k
  auto input = getInput();
2099
60.6k
  long actPos=input->tell();
2100
60.6k
  long begPos=entry.begin()+pos;
2101
60.6k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
2102
60.6k
  libwps::DebugStream f;
2103
60.6k
  f << "SharedData-" << std::hex << pos << std::dec << ":";
2104
60.6k
  int val=int(libwps::readU16(input)), N=val;
2105
60.6k
  int type=0;
2106
60.6k
  if (vers==1)
2107
29.3k
  {
2108
29.3k
    type=(val>>15);
2109
29.3k
    N &= 0x7fff;
2110
29.3k
  }
2111
60.6k
  if (type) f << "type=" << std::hex << type << std::dec << ",";
2112
60.6k
  if (N!=2) f << "used=" << N << ",";
2113
60.6k
  if (vers>=2)
2114
31.2k
  {
2115
    // checkme
2116
31.2k
    val=int(libwps::readU8(input));
2117
31.2k
    f << "type=" << std::hex << val << std::dec << ",";
2118
31.2k
    type = (val&4) ? 1 : 0;
2119
31.2k
  }
2120
60.6k
  int dSz=int(libwps::readU8(input));
2121
60.6k
  if (vers>=2) dSz*=2;
2122
60.6k
  long endPos=input->tell()+dSz;
2123
60.6k
  if (endPos>entry.end())
2124
3.20k
  {
2125
3.20k
    WPS_DEBUG_MSG(("MultiplanParser::readSharedData: the pos %d seems bad\n", pos));
2126
3.20k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
2127
3.20k
    return false;
2128
3.20k
  }
2129
57.4k
  bool ok=true;
2130
57.4k
  switch (type)
2131
57.4k
  {
2132
28.4k
  case 0:
2133
28.4k
    switch (cellType&3)
2134
28.4k
    {
2135
16.3k
    case 0:
2136
16.3k
    {
2137
16.3k
      long double value;
2138
16.3k
      if (dSz!=8 || !readDouble(value))
2139
7.47k
        ok=false;
2140
8.83k
      else
2141
8.83k
      {
2142
8.83k
        content.m_contentType=content.C_NUMBER;
2143
8.83k
        content.setValue(value);
2144
8.83k
        f << value << ",";
2145
8.83k
      }
2146
16.3k
      break;
2147
0
    }
2148
5.72k
    case 1:
2149
5.72k
    {
2150
5.72k
      content.m_contentType=content.C_TEXT;
2151
5.72k
      content.m_textEntry.setBegin(input->tell());
2152
5.72k
      content.m_textEntry.setLength(dSz);
2153
5.72k
      std::string name;
2154
158k
      for (int c=0; c<dSz; ++c)
2155
154k
      {
2156
154k
        char ch=char(libwps::readU8(input));
2157
154k
        if (vers>=2 && ch==0 && c+1==dSz)
2158
1.66k
        {
2159
1.66k
          content.m_textEntry.setEnd(input->tell()-1);
2160
1.66k
          break;
2161
1.66k
        }
2162
152k
        name+=ch;
2163
152k
      }
2164
5.72k
      f << name << ",";
2165
5.72k
      break;
2166
0
    }
2167
2.86k
    case 2:
2168
2.86k
      if (dSz!=8)
2169
1.63k
        ok=false;
2170
1.22k
      else
2171
1.22k
      {
2172
1.22k
        f << "Nan" << libwps::readU8(input) << ",";
2173
1.22k
        input->seek(7, librevenge::RVNG_SEEK_CUR);
2174
1.22k
        content.m_contentType=content.C_NUMBER;
2175
1.22k
        content.setValue(std::nan(""));
2176
1.22k
      }
2177
2.86k
      break;
2178
3.59k
    case 3:
2179
3.59k
    default:
2180
3.59k
      if (dSz!=8)
2181
2.28k
        ok=false;
2182
1.31k
      else
2183
1.31k
      {
2184
1.31k
        val=int(libwps::readU8(input));
2185
1.31k
        content.m_contentType=content.C_NUMBER;
2186
1.31k
        content.setValue(val);
2187
1.31k
        if (val==0)
2188
930
          f << "false,";
2189
380
        else if (val==1)
2190
0
          f << "true,";
2191
380
        else
2192
380
          f << "##bool=" << val << ",";
2193
1.31k
        input->seek(7, librevenge::RVNG_SEEK_CUR);
2194
1.31k
      }
2195
3.59k
      break;
2196
28.4k
    }
2197
28.4k
    break;
2198
28.9k
  case 1:
2199
28.9k
  {
2200
28.9k
    std::string err;
2201
28.9k
    if ((vers==1 && !readFormula(cellPos, content.m_formula, endPos, err)) ||
2202
28.9k
            (vers>=2 && !readFormulaV2(cellPos, content.m_formula, endPos, err)))
2203
2.60k
      f << "###";
2204
26.3k
    else
2205
26.3k
      content.m_contentType=content.C_FORMULA;
2206
28.9k
    for (auto const &fo : content.m_formula) f << fo;
2207
28.9k
    f << ",";
2208
28.9k
    f << err;
2209
28.9k
    break;
2210
28.4k
  }
2211
0
  default:
2212
0
    ok=false;
2213
0
    break;
2214
57.4k
  }
2215
57.4k
  if (!ok)
2216
11.4k
  {
2217
11.4k
    WPS_DEBUG_MSG(("MultiplanParser::readSharedData: can not read data for the pos %d\n", pos));
2218
11.4k
    f << "###";
2219
11.4k
  }
2220
57.4k
  if (m_state->m_posToSharedDataSeen.find(pos)==m_state->m_posToSharedDataSeen.end())
2221
7.97k
  {
2222
7.97k
    m_state->m_posToSharedDataSeen.insert(pos);
2223
7.97k
    if (input->tell()!=endPos)
2224
4.18k
      ascii().addDelimiter(input->tell(),'|');
2225
7.97k
    ascii().addPos(begPos);
2226
7.97k
    ascii().addNote(f.str().c_str());
2227
7.97k
  }
2228
57.4k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
2229
57.4k
  return true;
2230
57.4k
}
2231
////////////////////////////////////////////////////////////
2232
//   Unknown
2233
////////////////////////////////////////////////////////////
2234
bool MultiplanParser::readZoneB()
2235
9.48k
{
2236
9.48k
  auto input = getInput();
2237
9.48k
  long pos = input->tell();
2238
9.48k
  int const vers=version();
2239
9.48k
  long endPos=pos+(vers==1 ? 0x84 : 0xb9);
2240
9.48k
  if (!checkFilePosition(endPos))
2241
0
  {
2242
0
    WPS_DEBUG_MSG(("MultiplanParser::readZoneB: the zone seems too short\n"));
2243
0
    return false;
2244
0
  }
2245
9.48k
  libwps::DebugStream f;
2246
9.48k
  f << "Entries(ZoneB):";
2247
9.48k
  int dim[2];
2248
18.9k
  for (auto &d : dim) d=int(libwps::readU16(input));
2249
9.48k
  m_state->m_maximumCell=Vec2i(dim[0],dim[1]);
2250
9.48k
  f << "cell[max]=" << m_state->m_maximumCell << ",";
2251
9.48k
  int val;
2252
113k
  for (int i=0; i<11; ++i)
2253
104k
  {
2254
104k
    if (i==2 && vers==1) continue;
2255
98.4k
    val=int(libwps::read16(input));
2256
98.4k
    int const expected[]= {0,0,0xfff, 0xff, 0, 0, 5, 6, 0x46, 0x36, 0x42};
2257
98.4k
    if (val!=expected[i])
2258
67.0k
      f << "f" << i << "=" << val << ",";
2259
98.4k
  }
2260
131k
  for (int i=0; i<(vers==1 ? 11 : 16); ++i)
2261
122k
  {
2262
122k
    val=int(libwps::read16(input));
2263
122k
    if (!val) continue;
2264
73.8k
    f << "g" << i << "=" << val << ",";
2265
73.8k
  }
2266
9.48k
  if (vers>=2)
2267
3.54k
  {
2268
3.54k
    val=int(libwps::read8(input));
2269
3.54k
    if (val)
2270
1.65k
      f << "h0=" << val << ",";
2271
3.54k
  }
2272
9.48k
  ascii().addPos(pos);
2273
9.48k
  ascii().addNote(f.str().c_str());
2274
2275
9.48k
  pos=input->tell();
2276
9.48k
  f.str("");
2277
9.48k
  f << "ZoneB[II]:";
2278
85.4k
  for (int i=0; i<8; ++i)
2279
75.9k
  {
2280
75.9k
    if ((i==3||i==5) && vers==1) continue;
2281
64.0k
    val=int(libwps::read8(input));
2282
64.0k
    int const expected[]= {1, 0, 0, 0, -2, 0xf, 0, 0x3e};
2283
64.0k
    if (val!=expected[i]) f << "f" << i << "=" << val << ",";
2284
64.0k
  }
2285
456k
  for (int i=0; i<(vers==1 ? 40 : 59); ++i)   // 0
2286
446k
  {
2287
446k
    val=int(libwps::read16(input));
2288
446k
    if (val) f << "g" << i << "=" << val << ",";
2289
446k
  }
2290
9.48k
  if (input->tell()!=endPos)
2291
0
  {
2292
0
    ascii().addDelimiter(input->tell(),'|');
2293
0
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
2294
0
  }
2295
9.48k
  ascii().addPos(pos);
2296
9.48k
  ascii().addNote(f.str().c_str());
2297
9.48k
  return true;
2298
9.48k
}
2299
2300
////////////////////////////////////////////////////////////
2301
// send data
2302
////////////////////////////////////////////////////////////
2303
bool MultiplanParser::sendCell(Vec2i const &cellPos, int p)
2304
1.49M
{
2305
1.49M
  if (!m_listener)
2306
0
  {
2307
0
    WPS_DEBUG_MSG(("MultiplanParser::sendCell: I can not find the m_listener\n"));
2308
0
    return false;
2309
0
  }
2310
1.49M
  int const vers=version();
2311
1.49M
  int zoneId=(p>>16);
2312
1.49M
  if (zoneId<0 || zoneId>=int(m_state->m_zonesList.size()))
2313
0
  {
2314
0
    WPS_DEBUG_MSG(("MultiplanParser::sendCell: can not find the zone data zone for position %d\n", p));
2315
0
    return false;
2316
0
  }
2317
1.49M
  p&=0xffff;
2318
1.49M
  auto const &zone=m_state->m_zonesList[size_t(zoneId)];
2319
1.49M
  auto const &entry=zone.m_entry;
2320
1.49M
  auto it=zone.m_positionsSet.find(p);
2321
1.49M
  if (it!=zone.m_positionsSet.end()) ++it;
2322
1.49M
  long endPos=it!=zone.m_positionsSet.end() ? entry.begin()+*it : entry.end();
2323
1.49M
  if (p<=0 || p>entry.length())
2324
2.70k
  {
2325
2.70k
    WPS_DEBUG_MSG(("MultiplanParser::sendCell: unexpected position %d\n", p));
2326
2.70k
    return false;
2327
2.70k
  }
2328
1.48M
  libwps::DebugStream f;
2329
1.48M
  f << "CellData[C" << cellPos[0]+1 << "R" << cellPos[1]+1 << "]:";
2330
1.48M
  MultiplanParserInternal::Cell cell;
2331
1.48M
  WKSContentListener::CellContent content;
2332
1.48M
  cell.setPosition(cellPos);
2333
1.48M
  cell.setFont(m_state->getDefaultFont());
2334
1.48M
  long pos=entry.begin()+p;
2335
1.48M
  if (endPos-pos<4)
2336
725k
  {
2337
725k
    WPS_DEBUG_MSG(("MultiplanParser::sendCell: a cell %d seems to short\n", p));
2338
725k
    f << "###";
2339
725k
    ascii().addPos(pos);
2340
725k
    ascii().addNote(f.str().c_str());
2341
725k
    return false;
2342
725k
  }
2343
764k
  auto input=getInput();
2344
764k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
2345
764k
  int formSize=int(libwps::readU8(input));
2346
764k
  if (vers>=2) formSize*=2;
2347
764k
  if (formSize) f << "form[size]=" << std::hex << formSize << std::dec << ",";
2348
764k
  int val=int(libwps::readU8(input));
2349
764k
  int digits=(val>>4);
2350
764k
  if (digits) f << "decimal=" << digits << ",";
2351
764k
  int form=(val>>1)&7;
2352
764k
  int subFormat=0;
2353
764k
  switch (form)
2354
764k
  {
2355
450k
  case 0: // default
2356
450k
    break;
2357
51.8k
  case 1: // cont
2358
51.8k
    subFormat=1;
2359
51.8k
    cell.setDigits(digits);
2360
51.8k
    f << "decimal,";
2361
51.8k
    break;
2362
61.5k
  case 2:
2363
61.5k
    subFormat=2;
2364
61.5k
    cell.setDigits(digits);
2365
61.5k
    f << "scientific,";
2366
61.5k
    break;
2367
29.5k
  case 3:
2368
29.5k
    subFormat=6;
2369
29.5k
    cell.setDigits(digits);
2370
29.5k
    f << "fixed,";
2371
29.5k
    break;
2372
40.8k
  case 4: // generic
2373
40.8k
    break;
2374
38.0k
  case 5:
2375
38.0k
    subFormat=4;
2376
38.0k
    cell.setDigits(digits);
2377
38.0k
    f << "currency,";
2378
38.0k
    break;
2379
28.4k
  case 6: // a bar
2380
28.4k
    f << "bar,";
2381
28.4k
    break;
2382
62.7k
  case 7:
2383
62.7k
    subFormat=3;
2384
62.7k
    cell.setDigits(digits);
2385
62.7k
    f << "percent,";
2386
62.7k
    break;
2387
0
  default:
2388
0
    f << "format=" << form << ",";
2389
0
    break;
2390
764k
  }
2391
764k
  if (val&1)
2392
196k
  {
2393
196k
    cell.setProtected(true);
2394
196k
    f << "protected,";
2395
196k
  }
2396
764k
  int dSz=0, extraSize=(vers>=2 && formSize) ? 4 : 0;
2397
764k
  if (vers==1)
2398
418k
  {
2399
418k
    val=int(libwps::readU8(input));
2400
418k
    dSz=int(libwps::readU8(input));
2401
418k
  }
2402
345k
  else
2403
345k
  {
2404
345k
    dSz=int(libwps::readU8(input));
2405
345k
    val=int(libwps::readU8(input));
2406
345k
  }
2407
764k
  if (endPos<pos+4+dSz+extraSize)
2408
258k
  {
2409
258k
    WPS_DEBUG_MSG(("MultiplanParser::sendCell: a cell seems to short\n"));
2410
258k
    f << "###";
2411
258k
    ascii().addPos(pos);
2412
258k
    ascii().addNote(f.str().c_str());
2413
258k
    return false;
2414
258k
  }
2415
505k
  int type=(val>>6)&3;
2416
505k
  switch (type)
2417
505k
  {
2418
370k
  case 0:
2419
370k
    f << "double,";
2420
370k
    cell.setFormat(WPSCell::F_NUMBER, subFormat);
2421
370k
    content.m_contentType=content.C_NUMBER;
2422
370k
    break;
2423
56.1k
  case 1:
2424
56.1k
    cell.setFormat(WPSCell::F_TEXT);
2425
56.1k
    content.m_contentType=content.C_TEXT;
2426
56.1k
    f << "text,";
2427
56.1k
    break;
2428
35.2k
  case 2:
2429
35.2k
    cell.setFormat(WPSCell::F_NUMBER);
2430
35.2k
    content.m_contentType=content.C_NUMBER;
2431
35.2k
    f << "nan,";
2432
35.2k
    break;
2433
43.7k
  case 3: // or nothing
2434
43.7k
    cell.setFormat(WPSCell::F_BOOLEAN);
2435
43.7k
    content.m_contentType=content.C_NUMBER;
2436
43.7k
    f << "bool,";
2437
43.7k
    break;
2438
0
  default: // impossible
2439
0
    break;
2440
505k
  }
2441
505k
  int align=(val>>3)&7;
2442
505k
  bool hasTimeDate=false;
2443
505k
  switch (align)
2444
505k
  {
2445
31.0k
  case 1:
2446
31.0k
    cell.setHAlignment(cell.HALIGN_CENTER);
2447
31.0k
    f << "center,";
2448
31.0k
    break;
2449
307k
  case 0: // default
2450
336k
  case 2: // generic
2451
336k
    break;
2452
17.8k
  case 3:
2453
17.8k
    cell.setHAlignment(cell.HALIGN_LEFT);
2454
17.8k
    f << "left,";
2455
17.8k
    break;
2456
18.7k
  case 4:
2457
18.7k
    cell.setHAlignment(cell.HALIGN_RIGHT);
2458
18.7k
    f << "right,";
2459
18.7k
    break;
2460
60.1k
  case 7:
2461
60.1k
    if (vers>=3)
2462
41.3k
    {
2463
41.3k
      f << "timeDate,";
2464
41.3k
      hasTimeDate=true;
2465
41.3k
      break;
2466
41.3k
    }
2467
18.7k
    WPS_FALLTHROUGH;
2468
59.5k
  default:
2469
59.5k
    f << "#align=" << align << ",";
2470
59.5k
    break;
2471
505k
  }
2472
505k
  bool hasShared=false;
2473
505k
  if (val&2)
2474
127k
  {
2475
127k
    f << "shared,";
2476
127k
    hasShared=true;
2477
127k
  }
2478
505k
  if ((val&4)==0) f << "no[4],";
2479
505k
  val &= 0x1;
2480
505k
  if (val) f << "f1=" << std::hex << val << std::dec << ",";
2481
505k
  if (hasTimeDate && pos+4+dSz+extraSize+4 <= endPos)
2482
36.7k
  {
2483
36.7k
    endPos-=4;
2484
36.7k
    long actPos=input->tell();
2485
36.7k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
2486
36.7k
    f << "dateTime=[";
2487
36.7k
    val=int(libwps::readU8(input));
2488
36.7k
    if (val!=2) f << "f0=" << val << ",";
2489
36.7k
    val=int(libwps::readU8(input));
2490
36.7k
    switch (val&7)
2491
36.7k
    {
2492
3.32k
    case 1:
2493
3.32k
      cell.setHAlignment(cell.HALIGN_CENTER);
2494
3.32k
      f << "center,";
2495
3.32k
      break;
2496
22.6k
    case 0: // default
2497
23.5k
    case 2: // generic
2498
23.5k
      break;
2499
4.06k
    case 3:
2500
4.06k
      cell.setHAlignment(cell.HALIGN_LEFT);
2501
4.06k
      f << "left,";
2502
4.06k
      break;
2503
3.07k
    case 4:
2504
3.07k
      cell.setHAlignment(cell.HALIGN_RIGHT);
2505
3.07k
      f << "right,";
2506
3.07k
      break;
2507
2.77k
    default:
2508
2.77k
      f << "#align=" << (val&7) << ",";
2509
2.77k
      break;
2510
36.7k
    }
2511
36.7k
    val &=0xf8;
2512
36.7k
    if (val!=0x90) f << "f1=" << std::hex << val << std::dec << ",";
2513
36.7k
    int format=int(libwps::readU16(input));
2514
36.7k
    char const *dtFormat[]=
2515
36.7k
    {
2516
36.7k
      nullptr, "%m/%d/%y", "%m/%d", "%d-%b-%y",
2517
36.7k
      "%d-%b", "%b-%y", "%I:%M%p", "%I:%M:%S%p",
2518
36.7k
      "%H:%M", "%H:%M:%S", "%m/%d/%y %H:%M"
2519
36.7k
    };
2520
36.7k
    switch (format)
2521
36.7k
    {
2522
2.24k
    case 1:
2523
3.82k
    case 2:
2524
9.36k
    case 3:
2525
10.3k
    case 4:
2526
12.2k
    case 5:
2527
13.4k
    case 10:
2528
13.4k
      cell.setDTFormat(WPSCell::F_DATE, dtFormat[format]);
2529
13.4k
      f << dtFormat[format] << ",";
2530
13.4k
      break;
2531
4.41k
    case 6:
2532
5.29k
    case 7:
2533
8.88k
    case 8:
2534
11.4k
    case 9:
2535
11.4k
      cell.setDTFormat(WPSCell::F_TIME, dtFormat[format]);
2536
11.4k
      f << dtFormat[format] << ",";
2537
11.4k
      break;
2538
11.8k
    default:
2539
11.8k
      WPS_DEBUG_MSG(("MultiplanParser::sendCell: unknown data format\n"));
2540
11.8k
      f << "###format=" << format << ",";
2541
36.7k
    }
2542
36.7k
    f << "],";
2543
36.7k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
2544
36.7k
  }
2545
468k
  else if (hasTimeDate)
2546
4.55k
  {
2547
4.55k
    f << "###";
2548
4.55k
    WPS_DEBUG_MSG(("MultiplanParser::sendCell: can not find the time value data\n"));
2549
4.55k
  }
2550
505k
  if (vers>=2 && formSize)
2551
136k
  {
2552
136k
    f << "form=[";
2553
410k
    for (int i=0; i<2; ++i) f << libwps::read16(input) << ",";
2554
136k
    f << "],";
2555
136k
  }
2556
505k
  if (type==0 && dSz==8)
2557
73.0k
  {
2558
73.0k
    long double value;
2559
73.0k
    if (!readDouble(value))
2560
4.14k
      f << "###";
2561
68.8k
    else
2562
68.8k
      content.setValue(value);
2563
73.0k
    f << value << ",";
2564
73.0k
  }
2565
432k
  else if (type==1 && dSz && pos+4+dSz+extraSize+(hasShared ? 2 : 0) <= endPos)
2566
42.9k
  {
2567
42.9k
    content.m_textEntry.setBegin(input->tell());
2568
42.9k
    content.m_textEntry.setLength(dSz);
2569
42.9k
    std::string name;
2570
1.43M
    for (int c=0; c<dSz; ++c) name+=char(libwps::readU8(input));
2571
42.9k
    f << name << ",";
2572
42.9k
  }
2573
389k
  else if (type==2 && dSz==8)
2574
11.1k
  {
2575
11.1k
    content.setValue(std::nan(""));
2576
11.1k
    f << "Nan" << int(libwps::readU8(input)) << ",";
2577
11.1k
    input->seek(7, librevenge::RVNG_SEEK_CUR);
2578
11.1k
  }
2579
378k
  else if (type==3 && dSz==8)
2580
10.8k
  {
2581
10.8k
    val=int(libwps::readU8(input));
2582
10.8k
    content.setValue(val);
2583
10.8k
    if (val==0)
2584
6.43k
      f << "false,";
2585
4.40k
    else if (val==1)
2586
4.15k
      f << "true,";
2587
256
    else
2588
256
      f << "##bool=" << val << ",";
2589
10.8k
    input->seek(7, librevenge::RVNG_SEEK_CUR);
2590
10.8k
  }
2591
505k
  if (hasShared && input->tell()+2<=endPos && (formSize==0 || formSize==2))
2592
78.5k
  {
2593
78.5k
    if ((input->tell()-pos)%2)
2594
2.61k
      input->seek(1, librevenge::RVNG_SEEK_CUR);
2595
78.5k
    int nPos=int(libwps::readU16(input));
2596
78.5k
    if (!readSharedData(nPos, type, cellPos, content))
2597
21.0k
      f << "###";
2598
78.5k
    f << "sharedData-" << std::hex << nPos << std::dec << ",";
2599
78.5k
  }
2600
426k
  else if (!hasShared && formSize && input->tell()+formSize<=endPos)
2601
179k
  {
2602
179k
    auto endFPos=input->tell()+formSize;
2603
179k
    std::string err;
2604
179k
    if ((vers==1 && !readFormula(cellPos, content.m_formula, endFPos, err)) ||
2605
179k
            (vers>=2 && !readFormulaV2(cellPos, content.m_formula, endFPos, err)))
2606
10.7k
    {
2607
10.7k
      ascii().addDelimiter(input->tell(),'|');
2608
10.7k
      f << "###";
2609
10.7k
    }
2610
168k
    else
2611
168k
      content.m_contentType=content.C_FORMULA;
2612
2613
179k
    for (auto const &fo : content.m_formula) f << fo;
2614
179k
    f << ",";
2615
179k
    f << err;
2616
179k
    input->seek(endFPos, librevenge::RVNG_SEEK_SET);
2617
179k
  }
2618
247k
  else if (formSize)
2619
112k
  {
2620
112k
    WPS_DEBUG_MSG(("MultiplanParser::sendCell: can not read a formula\n"));
2621
112k
    f << "###form";
2622
112k
  }
2623
505k
  m_listener->openSheetCell(cell, content);
2624
505k
  if (content.m_textEntry.valid())
2625
45.0k
  {
2626
45.0k
    auto fontType=m_state->getDefaultFontType();
2627
45.0k
    m_listener->setFont(cell.getFont());
2628
45.0k
    input->seek(content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
2629
45.0k
    std::string text;
2630
1.52M
    while (input->tell()<=content.m_textEntry.end())
2631
1.52M
    {
2632
1.52M
      bool last=input->isEnd() || input->tell()>=content.m_textEntry.end();
2633
1.52M
      auto c=last ? '\0' : char(libwps::readU8(input));
2634
1.52M
      if ((c==0 || c==0x9 || c==0xa || c==0xd) && !text.empty())
2635
183k
      {
2636
183k
        m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
2637
183k
        text.clear();
2638
183k
      }
2639
1.52M
      if (last) break;
2640
1.47M
      if (c==0x9)
2641
9.60k
        m_listener->insertTab();
2642
1.46M
      else if (c==0xa || c==0xd)
2643
13.6k
        m_listener->insertEOL();
2644
1.45M
      else if (c)
2645
1.12M
        text.push_back(c);
2646
1.47M
    }
2647
45.0k
  }
2648
505k
  m_listener->closeSheetCell();
2649
505k
  if (vers==1 && input->tell()!=endPos)
2650
274k
    ascii().addDelimiter(input->tell(),'|');
2651
2652
505k
  ascii().addPos(pos);
2653
505k
  ascii().addNote(f.str().c_str());
2654
505k
  return true;
2655
505k
}
2656
2657
bool MultiplanParser::sendSpreadsheet()
2658
8.40k
{
2659
8.40k
  if (!m_listener)
2660
0
  {
2661
0
    WPS_DEBUG_MSG(("MultiplanParser::sendSpreadsheet: I can not find the m_listener\n"));
2662
0
    return false;
2663
0
  }
2664
8.40k
  for (auto &z : m_state->m_zonesList)
2665
9.90k
    z.m_positionsSet.insert(int(z.m_entry.length()));
2666
8.40k
  m_listener->openSheet(m_state->getColumnsWidth(), "Sheet0");
2667
8.40k
  WPSRowFormat rowFormat(16);
2668
8.40k
  rowFormat.m_isMinimalHeight=true;
2669
8.40k
  int lastRow=0;
2670
8.40k
  for (auto const &it : m_state->m_cellPositionsMap)
2671
403k
  {
2672
403k
    int r=it.first;
2673
403k
    auto const &row = it.second;
2674
403k
    if (r>lastRow)
2675
27.4k
    {
2676
27.4k
      m_listener->openSheetRow(rowFormat,r-lastRow);
2677
27.4k
      m_listener->closeSheetRow();
2678
27.4k
    }
2679
403k
    lastRow=r+1;
2680
403k
    m_listener->openSheetRow(rowFormat);
2681
7.60M
    for (size_t col=0; col<row.size(); ++col)
2682
7.20M
    {
2683
7.20M
      int zId=(row[col]>>24);
2684
7.20M
      int p=row[col]&0xffff;
2685
7.20M
      if (zId<0 || zId>=int(m_state->m_zonesList.size()) ||
2686
7.20M
        p<0 || p>=m_state->m_zonesList[size_t(zId)].m_entry.length())
2687
1.51M
      {
2688
1.51M
        WPS_DEBUG_MSG(("MultiplanParser::sendSpreadsheet: find some bad data\n"));
2689
1.51M
        continue;
2690
1.51M
      }
2691
5.68M
      if (!p) continue;
2692
1.49M
      Vec2i cellPos(static_cast<int>(col), static_cast<int>(r));
2693
1.49M
      sendCell(cellPos, row[col]);
2694
1.49M
    }
2695
403k
    m_listener->closeSheetRow();
2696
403k
  }
2697
8.40k
  m_listener->closeSheet();
2698
8.40k
  return true;
2699
8.40k
}
2700
2701
////////////////////////////////////////////////////////////
2702
// password
2703
////////////////////////////////////////////////////////////
2704
bool MultiplanParser::checkPassword(char const *password) const
2705
4.84k
{
2706
4.84k
  char const endPassword[]=
2707
4.84k
  {
2708
4.84k
    0x0A, 0x4E, 0x51, 0x6F, 0x6E, 0x61, 0x70, 0x32, 0x33, 0x71, 0x5B,
2709
4.84k
    0x30, 0x23, 0x7A
2710
4.84k
  };
2711
4.84k
  if (!password || !password[0])
2712
2.44k
  {
2713
2.44k
    WPS_DEBUG_MSG(("MultiplanParser::checkPassword: can not find the password\n"));
2714
2.44k
    return false;
2715
2.44k
  }
2716
  // set password length to 15 by adding endPassword's data if needed
2717
2.40k
  char pw[16];
2718
2.40k
  int w=0;
2719
13.9k
  while (*password)
2720
11.5k
  {
2721
11.5k
    if (w>=15) break;
2722
11.5k
    pw[w++]=*(password++);
2723
11.5k
  }
2724
26.8k
  for (int r=0; w<15;) pw[w++]=char(endPassword[r++]);
2725
2.40k
  pw[15]=char(0);
2726
2727
  // permutate the solution
2728
2.40k
  int const which=(m_state->m_hash&0xf);
2729
2.40k
  if (which!=15) ++pw[which]; // never seem which=15, maybe a special case
2730
2.40k
  int const perm[]= { 9, 4, 1, 3, 14, 11, 6, 0, 12, 7, 2, 10, 8, 13, 5 };
2731
2.40k
  uint8_t res[16];
2732
38.4k
  for (int i=0; i<15; ++i) res[i]=uint8_t(pw[perm[(i+which)%15]]);
2733
2.40k
  res[15]=0;
2734
2735
2.40k
  int len=0;
2736
38.3k
  while (res[len]) ++len;
2737
2.40k
  if (len!=15)
2738
5
  {
2739
5
    WPS_DEBUG_MSG(("MultiplanParser::checkPassword: unexpected size for the password\n"));
2740
5
    return false;
2741
5
  }
2742
2743
  // time to check if the checksum is ok or not
2744
2.39k
  int const data[]=
2745
2.39k
  {
2746
2.39k
    0x4ec3, 0xaefc, 0x4dd9, 0x9bb2, 0x2745, 0x4e8a, 0x9d14, 0x2a09,
2747
2.39k
    0x7b61, 0xf6c2, 0xfda5, 0xeb6b, 0xc6f7, 0x9dcf, 0x2bbf, 0x4563,
2748
2.39k
    0x8ac6, 0x05ad, 0x0b5a, 0x16b4, 0x2d68, 0x5ad0, 0x0375, 0x06ea,
2749
2.39k
    0x0dd4, 0x1ba8, 0x3750, 0x6ea0, 0xdd40, 0xd849, 0xa0b3, 0x5147,
2750
2.39k
    0xa28e, 0x553d, 0xaa7a, 0x44d5, 0x6f45, 0xde8a, 0xad35, 0x4a4b,
2751
2.39k
    0x9496, 0x390d, 0x721a, 0xeb23, 0xc667, 0x9cef, 0x29ff, 0x53fe,
2752
2.39k
    0xa7fc, 0x5fd9, 0x47d3, 0x8fa6, 0x0f6d, 0x1eda, 0x3db4, 0x7b68,
2753
2.39k
    0xf6d0, 0xb861, 0x60e3, 0xc1c6, 0x93ad, 0x377b, 0x6ef6, 0xddec,
2754
2.39k
    0x45a0, 0x8b40, 0x06a1, 0x0d42, 0x1a84, 0x3508, 0x6a10, 0xaa51,
2755
2.39k
    0x4483, 0x8906, 0x022d, 0x045a, 0x08b4, 0x1168, 0x76b4, 0xed68,
2756
2.39k
    0xcaf1, 0x85c3, 0x1ba7, 0x374e, 0x6e9c, 0x3730, 0x6e60, 0xdcc0,
2757
2.39k
    0xa9a1, 0x4363, 0x86c6, 0x1dad, 0x3331, 0x6662, 0xccc4, 0x89a9,
2758
2.39k
    0x0373, 0x06e6, 0x0dcc, 0x1021, 0x2042, 0x4084, 0x8108, 0x1231,
2759
2.39k
    0x2462, 0x48c4
2760
2.39k
  };
2761
2.39k
  int const *dataPtr=data;
2762
2.39k
  int val=*(dataPtr++);
2763
2.39k
  for (auto r : res)
2764
38.3k
  {
2765
306k
    for (int byte=1, dec=0; dec<7; byte<<=1, ++dec)
2766
268k
    {
2767
268k
      if (r&byte)
2768
133k
        val^=*dataPtr;
2769
268k
      dataPtr++;
2770
268k
    }
2771
38.3k
  }
2772
2.39k
  if (val!=m_state->m_checksum)
2773
57
  {
2774
57
    WPS_DEBUG_MSG(("MultiplanParser::checkPassword: can not check the password\n"));
2775
57
    return false;
2776
57
  }
2777
  // create the decoding keys
2778
2.33k
  res[15]=uint8_t(0xbb);
2779
39.7k
  for (int i=0; i<16; ++i)
2780
37.4k
  {
2781
37.4k
    auto v=res[i]^((i%2)==0 ? (m_state->m_checksum&0xFF) : (m_state->m_checksum>>8));
2782
37.4k
    m_state->m_keys[i]=uint8_t((v>>1)|(v<<7));
2783
37.4k
  }
2784
  // original password in 14d6:2220, final keys 14d6:49cb
2785
2.33k
  return true;
2786
2.39k
}
2787
2788
bool MultiplanParser::retrievePasswordKeys()
2789
2.44k
{
2790
2.44k
  auto input=getInput();
2791
2.44k
  long actPos=input->tell();
2792
  // too short or not the zone0 zone
2793
2.44k
  if (!checkFilePosition(actPos+6) || libwps::readU16(input)!=7)
2794
20
  {
2795
20
    input->seek(actPos,librevenge::RVNG_SEEK_SET);
2796
20
    return false;
2797
20
  }
2798
2.42k
  input->seek(2,librevenge::RVNG_SEEK_CUR);
2799
2.42k
  int dSz=int(libwps::readU16(input));
2800
  // not enough data to retrieve the keys final block
2801
2.42k
  if (dSz<22 || !checkFilePosition(actPos+dSz))
2802
10
  {
2803
10
    input->seek(actPos,librevenge::RVNG_SEEK_SET);
2804
10
    return false;
2805
10
  }
2806
2.41k
  uint8_t res[16];
2807
40.9k
  for (int i=0; i<16; ++i)
2808
38.5k
  {
2809
38.5k
    auto key=uint8_t(readU8(input));
2810
38.5k
    if (i==0) key^=8; // first value is normally 8, other 0
2811
38.5k
    auto r=uint8_t((key<<1)|(key>>7));
2812
38.5k
    res[(i+6)&0xf]=uint8_t(r^((i%2)==0 ? (m_state->m_checksum&0xFF) : (m_state->m_checksum>>8)));
2813
38.5k
  }
2814
2.41k
  if (res[15]!=0xbb)   // last value must be bb
2815
5
  {
2816
5
    input->seek(actPos,librevenge::RVNG_SEEK_SET);
2817
5
    return false;
2818
5
  }
2819
  // inverse the initial permutation
2820
2.40k
  char pw[16];
2821
2.40k
  int const perm[]= { 9, 4, 1, 3, 14, 11, 6, 0, 12, 7, 2, 10, 8, 13, 5 };
2822
2.40k
  int const which=(m_state->m_hash&0xf);
2823
38.4k
  for (int i=0; i<15; ++i) pw[perm[(i+which)%15]]=char(res[i]);
2824
2.40k
  if (which!=15) --pw[which];
2825
2.40k
  pw[15]=0;
2826
2.40k
  std::string password;
2827
2.40k
  for (auto c : pw)
2828
13.9k
  {
2829
13.9k
    if (c==0 || c==0xa) break;
2830
11.5k
    password+=c;
2831
11.5k
  }
2832
2.40k
  bool ok=!password.empty() && checkPassword(password.c_str());
2833
2.40k
  input->seek(actPos,librevenge::RVNG_SEEK_SET);
2834
2.40k
  return ok;
2835
2.41k
}
2836
2837
RVNGInputStreamPtr MultiplanParser::decodeStream(RVNGInputStreamPtr input)
2838
2.33k
{
2839
2.33k
  if (!input)
2840
0
  {
2841
0
    WPS_DEBUG_MSG(("MultiplanParser::decodeStream: the arguments seems bad\n"));
2842
0
    return RVNGInputStreamPtr();
2843
0
  }
2844
2.33k
  long actPos=input->tell();
2845
2.33k
  input->seek(0,librevenge::RVNG_SEEK_SET);
2846
2.33k
  librevenge::RVNGBinaryData data;
2847
2.33k
  if (!libwps::readDataToEnd(input, data) || !data.getDataBuffer())
2848
0
  {
2849
0
    WPS_DEBUG_MSG(("MultiplanParser::decodeStream: can not read the original input\n"));
2850
0
    return RVNGInputStreamPtr();
2851
0
  }
2852
2.33k
  auto *buf=const_cast<unsigned char *>(data.getDataBuffer());
2853
2.33k
  input->seek(actPos,librevenge::RVNG_SEEK_SET);
2854
2.33k
  auto const &keys=m_state->m_keys;
2855
16.2k
  while (!input->isEnd())
2856
16.2k
  {
2857
16.2k
    long pos=input->tell();
2858
16.2k
    if (!checkFilePosition(pos+6)) break;
2859
16.2k
    int type=int(libwps::readU16(input));
2860
16.2k
    if (type<7 || type>12) break;
2861
14.0k
    input->seek(2,  librevenge::RVNG_SEEK_CUR);
2862
14.0k
    int dSz=int(libwps::readU16(input));
2863
14.0k
    if (dSz<6 || !checkFilePosition(pos+dSz)) break;
2864
13.9k
    if (dSz==6) continue;
2865
1.27M
    for (int i=6; i<dSz; ++i)
2866
1.27M
      buf[pos+i]^=keys[i&0xf];
2867
7.04k
    input->seek(dSz-6,librevenge::RVNG_SEEK_CUR);
2868
7.04k
  }
2869
2.33k
  RVNGInputStreamPtr res(new WPSStringStream(data.getDataBuffer(), static_cast<unsigned int>(data.size())));
2870
2.33k
  res->seek(actPos, librevenge::RVNG_SEEK_SET);
2871
2.33k
  return res;
2872
2.33k
}
2873
2874
}
2875
2876
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */