Coverage Report

Created: 2026-06-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/WKS4.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 <sstream>
27
28
#include <librevenge-stream/librevenge-stream.h>
29
30
#include "libwps_internal.h"
31
#include "libwps_tools_win.h"
32
33
#include "WKSContentListener.h"
34
#include "WKSSubDocument.h"
35
36
#include "WPSEntry.h"
37
#include "WPSFont.h"
38
#include "WPSHeader.h"
39
#include "WPSPageSpan.h"
40
#include "WPSStringStream.h"
41
#include "WPSTable.h"
42
43
#include "WKS4Chart.h"
44
#include "WKS4Format.h"
45
#include "WKS4Spreadsheet.h"
46
47
#include "WKS4.h"
48
49
using namespace libwps;
50
51
//! Internal: namespace to define internal class of WKS4Parser
52
namespace WKS4ParserInternal
53
{
54
//! the font of a WKS4Parser
55
struct Font final : public WPSFont
56
{
57
  //! constructor
58
16.1k
  explicit Font(libwps_tools_win::Font::Type type) : WPSFont(), m_type(type)
59
16.1k
  {
60
16.1k
  }
61
37.9k
  Font(Font const &)=default;
62
  //! destructor
63
  ~Font() final;
64
  //! font encoding type
65
  libwps_tools_win::Font::Type m_type;
66
};
67
68
Font::~Font()
69
54.1k
{
70
54.1k
}
71
72
//! Internal: the subdocument of a WPS4Parser
73
class SubDocument final : public WKSSubDocument
74
{
75
public:
76
  //! constructor for a text entry
77
  SubDocument(RVNGInputStreamPtr const &input, WKS4Parser &pars, bool header) :
78
22
    WKSSubDocument(input, &pars), m_header(header) {}
79
  //! destructor
80
0
  ~SubDocument() final {}
81
82
  //! operator==
83
  virtual bool operator==(std::shared_ptr<WKSSubDocument> const &doc) const final
84
0
  {
85
0
    if (!doc || !WKSSubDocument::operator==(doc))
86
0
      return false;
87
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
88
0
    if (!sDoc) return false;
89
0
    return m_header == sDoc->m_header;
90
0
  }
91
92
  //! the parser function
93
  void parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
94
  //! a flag to known if we need to send the header or the footer
95
  bool m_header;
96
};
97
98
void SubDocument::parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType)
99
22
{
100
22
  if (!listener.get())
101
0
  {
102
0
    WPS_DEBUG_MSG(("WKS4ParserInternal::SubDocument::parse: no listener\n"));
103
0
    return;
104
0
  }
105
22
  if (!dynamic_cast<WKSContentListener *>(listener.get()))
106
0
  {
107
0
    WPS_DEBUG_MSG(("WKS4ParserInternal::SubDocument::parse: bad listener\n"));
108
0
    return;
109
0
  }
110
111
22
  WKS4Parser *pser = m_parser ? dynamic_cast<WKS4Parser *>(m_parser) : nullptr;
112
22
  if (!pser)
113
0
  {
114
0
    listener->insertCharacter(' ');
115
0
    WPS_DEBUG_MSG(("WKS4ParserInternal::SubDocument::parse: bad parser\n"));
116
0
    return;
117
0
  }
118
22
  pser->sendHeaderFooter(m_header);
119
22
}
120
121
//! the state of WKS4Parser
122
struct State
123
{
124
  //! constructor
125
  State(libwps_tools_win::Font::Type fontType, char const *password)
126
43.0k
    : m_eof(-1)
127
43.0k
    , m_creator(libwps::WPS_MSWORKS)
128
43.0k
    , m_isSpreadsheet(true)
129
43.0k
    , m_fontType(fontType)
130
43.0k
    , m_version(-1)
131
43.0k
    , m_hasLICSCharacters(false)
132
43.0k
    , m_fontsList()
133
43.0k
    , m_pageSpan()
134
43.0k
    , m_actPage(0)
135
43.0k
    , m_numPages(0)
136
43.0k
    , m_headerString("")
137
43.0k
    , m_footerString("")
138
43.0k
    , m_password(password)
139
43.0k
    , m_isEncrypted(false)
140
43.0k
    , m_isDecoded(false)
141
43.0k
  {
142
43.0k
  }
143
  //! returns a color corresponding to an id
144
  bool getColor(int id, WPSColor &color) const;
145
  //! return the default font style
146
  libwps_tools_win::Font::Type getDefaultFontType() const
147
679k
  {
148
679k
    if (m_hasLICSCharacters && m_version<=2)
149
52.9k
      return libwps_tools_win::Font::LICS;
150
626k
    if (m_fontType != libwps_tools_win::Font::UNKNOWN)
151
0
      return m_fontType;
152
626k
    if (m_version>2)
153
63.4k
      return libwps_tools_win::Font::WIN3_WEUROPE;
154
563k
    return m_creator==libwps::WPS_MSWORKS ? libwps_tools_win::Font::DOS_850 : libwps_tools_win::Font::CP_437;
155
626k
  }
156
157
  //! returns a default font (Courier12) with file's version to define the default encoding */
158
  WPSFont getDefaultFont() const
159
22
  {
160
22
    WPSFont res;
161
22
    if (m_version <= 2)
162
14
      res.m_name="Courier";
163
8
    else
164
8
      res.m_name="Times New Roman";
165
22
    res.m_size=12;
166
22
    return res;
167
22
  }
168
169
  //! the last file position
170
  long m_eof;
171
  //! the application
172
  libwps::WPSCreator m_creator;
173
  //! boolean to know if the file is a spreadsheet file or a database file
174
  bool m_isSpreadsheet;
175
  //! the user font type
176
  libwps_tools_win::Font::Type m_fontType;
177
  //! the file version
178
  int m_version;
179
  //! flag to know if the character
180
  bool m_hasLICSCharacters;
181
  //! the fonts list
182
  std::vector<Font> m_fontsList;
183
  //! the actual document size
184
  WPSPageSpan m_pageSpan;
185
  int m_actPage /** the actual page*/, m_numPages /* the number of pages */;
186
  //! the header string
187
  librevenge::RVNGString m_headerString;
188
  //! the footer string
189
  librevenge::RVNGString m_footerString;
190
191
  //! the password (if known)
192
  char const *m_password;
193
  //! true if the file is encrypted
194
  bool m_isEncrypted;
195
  //! true if the main stream has been decoded
196
  bool m_isDecoded;
197
198
private:
199
  State(State const &) = delete;
200
  State &operator=(State const &) = delete;
201
};
202
203
bool State::getColor(int id, WPSColor &color) const
204
106k
{
205
106k
  if (m_version<=2)
206
82.3k
  {
207
82.3k
    static const uint32_t colorDosMap[]=
208
82.3k
    {
209
82.3k
      0x0, 0xFF0000, 0x00FF00, 0x0000FF,
210
82.3k
      0x00FFFF, 0xFF00FF, 0xFFFF00
211
82.3k
    };
212
82.3k
    if (id < 0 || id >= 7)
213
58.2k
    {
214
58.2k
      WPS_DEBUG_MSG(("WKS4ParserInternal::State::getColor(): unknown Dos color id: %d\n",id));
215
58.2k
      return false;
216
58.2k
    }
217
24.0k
    color=WPSColor(colorDosMap[id]);
218
24.0k
    return true;
219
82.3k
  }
220
24.6k
  static const uint32_t colorMap[]=
221
24.6k
  {
222
    // 0x00RRGGBB
223
24.6k
    0, // auto
224
24.6k
    0,
225
24.6k
    0x0000FF, 0x00FFFF,
226
24.6k
    0x00FF00, 0xFF00FF, 0xFF0000, 0xFFFF00,
227
24.6k
    0x808080, 0xFFFFFF, 0x000080, 0x008080,
228
24.6k
    0x008000, 0x800080, 0x808000, 0xC0C0C0
229
24.6k
  };
230
24.6k
  if (id < 0 || id >= 16)
231
0
  {
232
0
    WPS_DEBUG_MSG(("WKS4ParserInternal::State::getColor(): unknown color id: %d\n",id));
233
0
    return false;
234
0
  }
235
24.6k
  color = WPSColor(colorMap[id]);
236
24.6k
  return true;
237
24.6k
}
238
239
}
240
241
// constructor, destructor
242
WKS4Parser::WKS4Parser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
243
                       libwps_tools_win::Font::Type encoding, char const *password)
244
14.4k
  : WKSParser(input, header)
245
14.4k
  , m_listener()
246
14.4k
  , m_state(new WKS4ParserInternal::State(encoding, password))
247
14.4k
  , m_chartParser(new WKS4Chart(*this))
248
14.4k
  , m_spreadsheetParser(new WKS4Spreadsheet(*this))
249
14.4k
{
250
14.4k
}
251
252
WKS4Parser::~WKS4Parser()
253
14.4k
{
254
14.4k
}
255
256
int WKS4Parser::version() const
257
24.2k
{
258
24.2k
  return m_state->m_version;
259
24.2k
}
260
261
libwps::WPSCreator WKS4Parser::creator() const
262
29.0k
{
263
29.0k
  return m_state->m_creator;
264
29.0k
}
265
266
void WKS4Parser::resetMainInput(RVNGInputStreamPtr newInput)
267
1.09k
{
268
1.09k
  resetInput(newInput);
269
1.09k
  ascii().setStream(newInput);
270
1.09k
  m_chartParser->resetInput(newInput);
271
1.09k
  m_spreadsheetParser->resetInput(newInput);
272
1.09k
}
273
274
bool WKS4Parser::checkFilePosition(long pos)
275
2.41M
{
276
2.41M
  if (m_state->m_eof < 0)
277
28.5k
  {
278
28.5k
    RVNGInputStreamPtr input = getInput();
279
28.5k
    long actPos = input->tell();
280
28.5k
    input->seek(0, librevenge::RVNG_SEEK_END);
281
28.5k
    m_state->m_eof=input->tell();
282
28.5k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
283
28.5k
  }
284
2.41M
  return pos <= m_state->m_eof;
285
2.41M
}
286
287
libwps_tools_win::Font::Type WKS4Parser::getDefaultFontType() const
288
679k
{
289
679k
  return m_state->getDefaultFontType();
290
679k
}
291
292
libwps::WPSCreator WKS4Parser::getCreator() const
293
0
{
294
0
  return m_state->m_creator;
295
0
}
296
297
//////////////////////////////////////////////////////////////////////
298
// interface with WKS4Spreadsheet
299
//////////////////////////////////////////////////////////////////////
300
bool WKS4Parser::getColor(int id, WPSColor &color) const
301
93.9k
{
302
93.9k
  return m_state->getColor(id, color);
303
93.9k
}
304
305
bool WKS4Parser::getFont(int id, WPSFont &font, libwps_tools_win::Font::Type &type) const
306
47.4k
{
307
47.4k
  if (id < 0 || id>=int(m_state->m_fontsList.size()))
308
45.8k
  {
309
45.8k
    WPS_DEBUG_MSG(("WKS4Parser::getFont: can not find font %d\n", id));
310
45.8k
    return false;
311
45.8k
  }
312
1.59k
  auto const &ft=m_state->m_fontsList[size_t(id)];
313
1.59k
  font=ft;
314
1.59k
  type=ft.m_type;
315
1.59k
  return true;
316
47.4k
}
317
318
librevenge::RVNGString WKS4Parser::getSheetName(int id) const
319
1.04M
{
320
1.04M
  return m_spreadsheetParser->getSheetName(id);
321
1.04M
}
322
323
// main function to parse the document
324
void WKS4Parser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
325
14.4k
{
326
14.4k
  RVNGInputStreamPtr input=getInput();
327
14.4k
  if (!input)
328
0
  {
329
0
    WPS_DEBUG_MSG(("WKS4Parser::parse: does not find main ole\n"));
330
0
    throw (libwps::ParseException());
331
0
  }
332
333
14.4k
  if (!checkHeader(nullptr)) throw(libwps::ParseException());
334
335
14.1k
  bool ok=false;
336
14.1k
  try
337
14.1k
  {
338
14.1k
    ascii().setStream(input);
339
14.1k
    ascii().open("MN0");
340
341
14.1k
    if (checkHeader(nullptr) && readZones())
342
8.52k
    {
343
8.52k
      parseFormatStream();
344
8.52k
      m_listener=createListener(documentInterface);
345
8.52k
    }
346
14.1k
    if (m_listener)
347
8.52k
    {
348
8.52k
      m_chartParser->setListener(m_listener);
349
8.52k
      m_spreadsheetParser->setListener(m_listener);
350
351
8.52k
      m_listener->startDocument();
352
8.52k
      int numSheet=m_spreadsheetParser->getNumSpreadsheets();
353
8.52k
      if (numSheet==0) ++numSheet;
354
17.0k
      for (int i=0; i<numSheet; ++i)
355
8.52k
        m_spreadsheetParser->sendSpreadsheet(i);
356
8.52k
      if (m_state->m_isSpreadsheet && m_chartParser->getNumCharts())
357
2.28k
      {
358
2.28k
        std::vector<WPSColumnFormat> widths;
359
2.28k
        WPSColumnFormat width(72.);
360
2.28k
        width.m_numRepeat=20;
361
2.28k
        widths.push_back(width);
362
2.28k
        m_listener->openSheet(widths, "Charts");
363
2.28k
        m_chartParser->sendCharts();
364
2.28k
        m_listener->closeSheet();
365
2.28k
      }
366
8.52k
      m_listener->endDocument();
367
8.52k
      m_listener.reset();
368
8.52k
      ok = true;
369
8.52k
    }
370
14.1k
  }
371
14.1k
  catch (libwps::PasswordException())
372
14.1k
  {
373
0
    ascii().reset();
374
0
    WPS_DEBUG_MSG(("WKS4Parser::parse: password exception catched when parsing MN0\n"));
375
0
    throw (libwps::PasswordException());
376
0
  }
377
14.1k
  catch (...)
378
14.1k
  {
379
56
    WPS_DEBUG_MSG(("WKS4Parser::parse: exception catched when parsing MN0\n"));
380
56
    throw (libwps::ParseException());
381
56
  }
382
383
14.0k
  ascii().reset();
384
14.0k
  if (!ok)
385
5.56k
    throw(libwps::ParseException());
386
14.0k
}
387
388
std::shared_ptr<WKSContentListener> WKS4Parser::createListener(librevenge::RVNGSpreadsheetInterface *interface)
389
8.52k
{
390
8.52k
  std::vector<WPSPageSpan> pageList;
391
8.52k
  WPSPageSpan ps(m_state->m_pageSpan);
392
8.52k
  if (!m_state->m_headerString.empty())
393
11
  {
394
11
    WPSSubDocumentPtr subdoc(new WKS4ParserInternal::SubDocument
395
11
                             (getInput(), *this, true));
396
11
    ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::ALL, subdoc);
397
11
  }
398
8.52k
  if (!m_state->m_footerString.empty())
399
11
  {
400
11
    WPSSubDocumentPtr subdoc(new WKS4ParserInternal::SubDocument
401
11
                             (getInput(), *this, false));
402
11
    ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::ALL, subdoc);
403
11
  }
404
8.52k
  pageList.push_back(ps);
405
8.52k
  return std::shared_ptr<WKSContentListener>(new WKSContentListener(pageList, interface));
406
8.52k
}
407
408
////////////////////////////////////////////////////////////
409
// low level
410
////////////////////////////////////////////////////////////
411
// read the header
412
////////////////////////////////////////////////////////////
413
bool WKS4Parser::checkHeader(WPSHeader *header, bool strict)
414
28.5k
{
415
28.5k
  m_state.reset(new WKS4ParserInternal::State(m_state->m_fontType, m_state->m_password));
416
28.5k
  libwps::DebugStream f;
417
418
28.5k
  RVNGInputStreamPtr input = getInput();
419
28.5k
  if (!checkFilePosition(12))
420
108
  {
421
108
    WPS_DEBUG_MSG(("WKS4Parser::checkHeader: file is too short\n"));
422
108
    return false;
423
108
  }
424
425
28.4k
  input->seek(0,librevenge::RVNG_SEEK_SET);
426
28.4k
  auto firstOffset = int(libwps::readU8(input));
427
28.4k
  auto type = int(libwps::read8(input));
428
28.4k
  bool needEncoding=true;
429
28.4k
  f << "FileHeader:";
430
28.4k
  if ((firstOffset == 0 && type == 0) ||
431
15.3k
          (firstOffset == 0x20 && type == 0x54))
432
22.8k
  {
433
22.8k
    m_state->m_version=1;
434
22.8k
    f << "DOS,";
435
22.8k
  }
436
5.66k
  else if (firstOffset == 0xff)
437
5.66k
  {
438
5.66k
    f << "Windows,";
439
5.66k
    m_state->m_version=3;
440
5.66k
    needEncoding=false;
441
5.66k
  }
442
2
  else
443
2
  {
444
2
    WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find unexpected first data\n"));
445
2
    return false;
446
2
  }
447
28.4k
  auto creatorId=libwps::WPS_MSWORKS;
448
28.4k
  auto kind=libwps::WPS_SPREADSHEET;
449
28.4k
  bool isSpreadsheet=true;
450
28.4k
  if (type == 0x54)
451
12.5k
  {
452
12.5k
    isSpreadsheet=false;
453
12.5k
    kind=libwps::WPS_DATABASE;
454
12.5k
    f << "database,";
455
12.5k
  }
456
15.8k
  else if (type == 0)
457
15.8k
    f << "spreadsheet,";
458
0
  else
459
0
  {
460
0
    WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find unexpected type file\n"));
461
0
    return false;
462
0
  }
463
28.4k
  auto val=int(libwps::read16(input));
464
28.4k
  if (val==2)
465
28.4k
  {
466
    // version
467
28.4k
    val=int(libwps::readU16(input));
468
28.4k
    if (isSpreadsheet)
469
15.8k
    {
470
15.8k
      if (val==0x404)
471
7.56k
      {
472
7.56k
      }
473
8.31k
      else if (val==0x405)
474
514
      {
475
514
        f << "symphony,";
476
514
        creatorId=libwps::WPS_SYMPHONY;
477
514
      }
478
7.79k
      else if (val==0x406)
479
7.71k
      {
480
7.71k
        m_state->m_version=1;
481
7.71k
        f << "lotus,";
482
7.71k
        creatorId=libwps::WPS_LOTUS;
483
7.71k
      }
484
85
      else if (val==0x5120 || val==0x5121)
485
1
      {
486
1
        WPS_DEBUG_MSG(("WKS4Parser::checkHeader: must not be called with a DOS Quattro file\n"));
487
1
        return false;
488
1
      }
489
84
      else if (val==0x8006)
490
0
      {
491
0
        WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find lotus file format, sorry parsing this format is not implemented\n"));
492
0
        return false;
493
0
      }
494
84
      else
495
84
      {
496
#ifdef DEBUG
497
        f << "vers=" << std::hex << val << std::dec << ",";
498
#else
499
84
        WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find unknown file version\n"));
500
84
        return false;
501
84
#endif
502
84
      }
503
15.8k
    }
504
12.5k
    else if (val)
505
45
      return false;
506
28.4k
  }
507
38
  else
508
38
  {
509
38
    WPS_DEBUG_MSG(("WKS4Parser::checkHeader: header contain unexpected size field data\n"));
510
38
    return false;
511
38
  }
512
513
28.3k
  m_state->m_creator=creatorId;
514
28.3k
  input->seek(0, librevenge::RVNG_SEEK_SET);
515
28.3k
  if (strict && m_state->m_version<1000)
516
0
  {
517
0
    for (int i=0; i < 4; ++i)
518
0
    {
519
0
      if (!readZone()) return false;
520
0
      if (m_state->m_isEncrypted) break;
521
0
    }
522
0
  }
523
28.3k
  ascii().addPos(0);
524
28.3k
  ascii().addNote(f.str().c_str());
525
526
28.3k
  m_state->m_isSpreadsheet=isSpreadsheet;
527
28.3k
  if (header)
528
0
  {
529
0
    header->setMajorVersion(uint8_t(m_state->m_version));
530
0
    header->setCreator(creatorId);
531
0
    header->setKind(kind);
532
0
    header->setNeedEncoding(needEncoding);
533
0
    header->setIsEncrypted(m_state->m_isEncrypted);
534
0
  }
535
28.3k
  return true;
536
28.3k
}
537
538
bool WKS4Parser::parseFormatStream()
539
8.52k
{
540
8.52k
  RVNGInputStreamPtr file=getFileInput();
541
8.52k
  if (!file || !file->isStructured() || !m_state->m_isSpreadsheet)
542
8.52k
    return false;
543
544
0
  RVNGInputStreamPtr formatInput(file->getSubStreamByName("FMT"));
545
0
  if (!formatInput)
546
0
  {
547
0
    WPS_DEBUG_MSG(("WKS4Parser::parseFormatStream: can not find the format stream\n"));
548
0
    return false;
549
0
  }
550
0
  WKS4Format formatManager(*this, formatInput);
551
0
  return formatManager.parse();
552
0
}
553
554
bool WKS4Parser::readZones()
555
14.1k
{
556
14.1k
  RVNGInputStreamPtr input = getInput();
557
14.1k
  input->seek(0, librevenge::RVNG_SEEK_SET);
558
14.1k
  if (version()>=1000)
559
0
  {
560
    // error ok, we do no known how to parsed this structure
561
0
    while (!input->isEnd())
562
0
    {
563
0
      if (!readZoneQuattro())
564
0
        break;
565
0
    }
566
567
0
    ascii().addPos(input->tell());
568
0
    ascii().addNote("Entries(UnknownZone):");
569
0
    return false;
570
0
  }
571
572
1.90M
  while (readZone())
573
1.89M
  {
574
1.89M
    if (m_state->m_isEncrypted && !m_state->m_isDecoded)
575
56
      throw(libwps::PasswordException());
576
1.89M
  }
577
578
  //
579
  // look for ending
580
  //
581
14.0k
  input = getInput();
582
14.0k
  long pos = input->tell();
583
14.0k
  if (!checkFilePosition(pos+4))
584
5.39k
  {
585
5.39k
    WPS_DEBUG_MSG(("WKS4Parser::readZones: cell header is too short\n"));
586
5.39k
    return m_spreadsheetParser->getNumSpreadsheets()>0;
587
5.39k
  }
588
8.70k
  auto type = int(libwps::readU16(input)); // 1
589
8.70k
  auto length = int(libwps::readU16(input));
590
8.70k
  if (length)
591
7.42k
  {
592
7.42k
    WPS_DEBUG_MSG(("WKS4Parser::readZones: parse breaks before ending\n"));
593
7.42k
    ascii().addPos(pos);
594
7.42k
    ascii().addNote("Entries(BAD):###");
595
7.42k
    return m_spreadsheetParser->getNumSpreadsheets()>0;
596
7.42k
  }
597
598
1.27k
  ascii().addPos(pos);
599
1.27k
  if (type != 1)
600
1.20k
  {
601
1.20k
    WPS_DEBUG_MSG(("WKS4Parser::readZones: odd end cell type: %d\n", type));
602
1.20k
    ascii().addNote("Entries(BAD):###");
603
1.20k
  }
604
78
  else
605
78
    ascii().addNote("__End");
606
607
1.27k
  return true;
608
8.70k
}
609
610
bool WKS4Parser::readZone()
611
1.90M
{
612
1.90M
  libwps::DebugStream f;
613
1.90M
  RVNGInputStreamPtr input = getInput();
614
1.90M
  long pos = input->tell();
615
1.90M
  auto id = int(libwps::readU8(input));
616
1.90M
  auto type = int(libwps::read8(input));
617
1.90M
  auto sz = long(libwps::readU16(input));
618
1.90M
  if (sz<0 || !checkFilePosition(pos+4+sz))
619
12.0k
  {
620
12.0k
    WPS_DEBUG_MSG(("WKS4Parser::readZone: size is bad\n"));
621
12.0k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
622
12.0k
    return false;
623
12.0k
  }
624
625
1.89M
  f << "Entries(Struct";
626
1.89M
  if (type == 0x54) f << "A";
627
1.89M
  f << std::hex << id << std::dec << "E):";
628
1.89M
  bool ok = true, isParsed = false, needWriteInAscii = false;
629
1.89M
  int val;
630
1.89M
  input->seek(pos, librevenge::RVNG_SEEK_SET);
631
1.89M
  switch (type)
632
1.89M
  {
633
1.28M
  case 0:
634
1.28M
    switch (id)
635
1.28M
    {
636
    /* also
637
       32: symphony windows settings(144)
638
       37: password checksum(4)
639
       3c: query(127)
640
       3d: query name(16)
641
       3e: symphony print record (679)
642
       3f: printer name(16)
643
       40: symphony graph record (499)
644
       42: zoom(9)
645
       43: number of split windows(2)
646
       44: number of screen row(2)
647
       45: number of screen column(2)
648
       46: name ruler range(25)
649
       47: name sheet range(25)
650
       48: autoload comm(65)
651
       49: autoexecutute macro adress(8)
652
       4a: query parse information
653
     */
654
867k
    case 0:
655
867k
      if (sz!=2) break;
656
52.2k
      f.str("");
657
52.2k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
658
52.2k
      f << "version=" << std::hex << libwps::readU16(input) << std::dec << ",";
659
52.2k
      isParsed=needWriteInAscii=true;
660
52.2k
      break;
661
86
    case 0x1: // EOF
662
86
      ok = false;
663
86
      break;
664
    // boolean
665
75.5k
    case 0x2: // Calculation mode 0 or FF
666
78.1k
    case 0x3: // Calculation order
667
84.0k
    case 0x4: // Split window type
668
87.0k
    case 0x5: // Split window syn
669
88.0k
    case 0x29: // label format 22|27|5e (spreadsheet)
670
88.5k
    case 0x30: // formatted/unformatted print 0|ff (spreadsheet)
671
89.3k
    case 0x31: // cursor/location 1|2
672
89.4k
    case 0x38: // lock
673
89.4k
      f.str("");
674
89.4k
      f << "Entries(Byte" << std::hex << id << std::dec << "Z):";
675
89.4k
      if (sz!=1) break;
676
14.9k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
677
14.9k
      val=int(libwps::readU8(input));
678
14.9k
      if (id==0x29)
679
464
        f << "val=" << std::hex << val << std::dec << ",";
680
14.4k
      else if (id==0x31)
681
439
      {
682
439
        if (val!=1) f << val << ",";
683
439
      }
684
14.0k
      else
685
14.0k
      {
686
14.0k
        if (val==0xFF) f << "true,";
687
3.85k
        else if (val) f << "#val=" << val << ",";
688
14.0k
      }
689
14.9k
      isParsed=needWriteInAscii=true;
690
14.9k
      break;
691
8.76k
    case 0x6: // active worksheet range
692
8.76k
      ok = m_spreadsheetParser->readSheetSize();
693
8.76k
      isParsed = true;
694
8.76k
      break;
695
4.49k
    case 0x7: // window 1 record
696
5.09k
    case 0x9: // window 2 record
697
5.09k
      ok = readWindowRecord();
698
5.09k
      isParsed=true;
699
5.09k
      break;
700
4.39k
    case 0x8: // col width
701
4.39k
      ok = m_spreadsheetParser->readColumnSize();
702
4.39k
      isParsed = true;
703
4.39k
      break;
704
869
    case 0xa: // col width (window 2)
705
869
      if (sz!=3) break;
706
29
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
707
      // varies in this file from 0 to 5
708
29
      f << "id=" << libwps::read16(input) << ",";
709
      // small number 9-13: a dim?
710
29
      f << "dim?=" << libwps::read8(input) << ",";
711
29
      isParsed=needWriteInAscii=true;
712
29
      break;
713
5.51k
    case 0xb: // named range
714
5.51k
      ok = readFieldName();
715
5.51k
      isParsed=true;
716
5.51k
      break;
717
13.9k
    case 0xc: // blank cell
718
62.2k
    case 0xd: // integer cell
719
113k
    case 0xe: // floating cell
720
155k
    case 0xf: // label cell
721
180k
    case 0x10: // formula cell
722
183k
    case 0x36: // continue label
723
183k
      ok = m_spreadsheetParser->readCell();
724
183k
      isParsed = true;
725
183k
      break;
726
411
    case 0x33:  // value of string formula
727
411
      ok = m_spreadsheetParser->readCellFormulaResult();
728
411
      isParsed = true;
729
411
      break;
730
    // some spreadsheet zone ( mainly flags )
731
454
    case 0x18: // data table range
732
1.34k
    case 0x19: // query range
733
2.64k
    case 0x20: // distribution range
734
3.99k
    case 0x27: // print setup
735
4.67k
    case 0x2a: // print borders
736
4.67k
      ok = readUnknown1();
737
4.67k
      isParsed=true;
738
4.67k
      break;
739
2.29k
    case 0x1a: // print range
740
3.69k
    case 0x1b: // sort range
741
4.52k
    case 0x1c: // fill range
742
5.95k
    case 0x1d: // primary sort key range
743
7.31k
    case 0x23: // secondary sort key range
744
7.31k
    {
745
7.31k
      int expectedSz=8;
746
7.31k
      f.str("");
747
7.31k
      switch (id)
748
7.31k
      {
749
2.29k
      case 0x1a: // only in spreadsheet?
750
2.29k
        f << "Entries(PrintRange):";
751
2.29k
        break;
752
1.40k
      case 0x1b: // a dimension or also some big selection? 31999=infinity?, related to report?
753
1.40k
        f << "Entries(SortRange):";
754
1.40k
        break;
755
828
      case 0x1c: // a dimension or also some big selection? only in spreadsheet
756
828
        f << "Entries(FillRange):";
757
828
        break;
758
1.42k
      case 0x1d:
759
1.42k
        f << "Entries(PrimSort):";
760
1.42k
        expectedSz=9;
761
1.42k
        break;
762
1.36k
      case 0x23:
763
1.36k
        f << "Entries(SecSort):";
764
1.36k
        expectedSz=9;
765
1.36k
        break;
766
0
      default:
767
0
        break;
768
7.31k
      }
769
7.31k
      if (sz!=expectedSz) break;
770
3.19k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
771
3.19k
      int dim[4];
772
12.7k
      for (int &i : dim) i=int(libwps::read16(input));
773
      // in a spreadsheet, the cell or the cells corresponding to the field
774
      // in a database, col,0,col,0
775
3.19k
      if (dim[0]==-1 && dim[1]==dim[0] && dim[2]==dim[0] && dim[3]==dim[0])
776
1.18k
      {
777
1.18k
      }
778
2.01k
      else if (m_state->m_isSpreadsheet || dim[1] || dim[0]!= dim[2] || dim[3])
779
2.00k
      {
780
2.00k
        f << "cell=" << dim[0] << "x" << dim[1];
781
2.00k
        if (dim[0]!=dim[2] || dim[1]!=dim[3])
782
1.32k
          f << "<->" << dim[2] << "x" << dim[3];
783
2.00k
        f << ",";
784
2.00k
      }
785
10
      else
786
10
        f << "col=" << dim[0] << ",";
787
3.19k
      if (expectedSz==9)
788
1.27k
      {
789
1.27k
        val=int(libwps::readU8(input)); // 0|1|ff
790
1.27k
        if (val==0xFF) f << "true,";
791
975
        else if (val) f << "val=" << val << ",";
792
1.27k
      }
793
3.19k
      isParsed=needWriteInAscii=true;
794
3.19k
      break;
795
7.31k
    }
796
2.67k
    case 0x24: // protection (global)
797
2.67k
      f.str("");
798
2.67k
      f << "Entries(Protection):global,";
799
2.67k
      if (sz!=1) break;
800
1.68k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
801
1.68k
      val=int(libwps::readU8(input));
802
1.68k
      if (val==0)
803
676
      {
804
676
        f.str("");
805
676
        f << "_";
806
676
      }
807
1.00k
      else if (val==0xFF) f << "protected,";
808
157
      else f << "#protected=" << val << ",";
809
1.68k
      isParsed=needWriteInAscii=true;
810
1.68k
      break;
811
1.74k
    case 0x25: // footer
812
3.23k
    case 0x26: // header
813
3.23k
      readHeaderFooter(id==0x26);
814
3.23k
      isParsed = true;
815
3.23k
      break;
816
1.49k
    case 0x28: // print margin
817
1.49k
      if (sz!=10) break;
818
123
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
819
738
      for (int i=0; i<5; ++i)   // f1=4c|96|ac|f0
820
615
      {
821
615
        static const int expected[]= {4, 0x4c, 0x42, 2, 2};
822
615
        val=int(libwps::read16(input));
823
615
        if (val!=expected[i]) f << "f" << i << "=" << val << ",";
824
615
      }
825
123
      isParsed=needWriteInAscii=true;
826
123
      break;
827
40.8k
    case 0x2d: // graph setting
828
45.3k
    case 0x2e: // named graph setting
829
45.3k
      m_chartParser->readChart();
830
45.3k
      isParsed = true;
831
45.3k
      break;
832
3.40k
    case 0x2f: // iteration count: only in dos file Wk1, Wks(dos), Wq[12] ?
833
3.40k
      if (sz!=1) break;
834
2.09k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
835
2.09k
      f.str("");
836
2.09k
      val=int(libwps::readU8(input));
837
2.09k
      f << "Entries(ItCount):dos";
838
2.09k
      if (val!=1) f << "=" << val << ",";
839
543
      else if (m_state->m_version==2)
840
0
        m_state->m_version=1;
841
2.09k
      isParsed = needWriteInAscii = true;
842
2.09k
      break;
843
327
    case 0x41: // graph record name
844
327
      m_chartParser->readChartName();
845
327
      isParsed = true;
846
327
      break;
847
2.39k
    case 0x4b:
848
2.39k
      if (sz==2 && m_state->m_creator==libwps::WPS_LOTUS)
849
1.60k
      {
850
1.60k
        m_state->m_isEncrypted=true;
851
1.60k
        input->seek(pos+4, librevenge::RVNG_SEEK_SET);
852
1.60k
        f.str("");
853
1.60k
        uint16_t fileKey(libwps::readU16(input));
854
1.60k
        f << "Entries(Password):pass=" << std::hex << fileKey << std::dec << ",";
855
1.60k
        isParsed = needWriteInAscii = true;
856
1.60k
        if (!m_state->m_isDecoded)
857
1.15k
        {
858
1.15k
          static uint8_t const defValues[]=
859
1.15k
          {
860
1.15k
            0xbb,0xff, 0xff,0xba, 0xff,0xff, 0xb9,0x80,
861
1.15k
            0,0x0be, 0xf,0, 0xbf,0xf, 0,0
862
1.15k
          };
863
1.15k
          uint16_t key;
864
1.15k
          std::vector<uint8_t> keys;
865
1.15k
          if (m_state->m_password && libwps::encodeLotusPassword(m_state->m_password, key, keys, defValues))
866
1.15k
          {
867
1.15k
            RVNGInputStreamPtr newInput;
868
1.15k
            if (uint16_t(key<<8|key>>8)==fileKey)
869
1.09k
              newInput=decodeStream(input, m_state->m_eof, keys);
870
1.15k
            if (newInput)
871
1.09k
            {
872
              // let's replace the current input by the decoded input
873
1.09k
              m_state->m_isDecoded=true;
874
1.09k
              input=newInput;
875
1.09k
              resetMainInput(newInput);
876
1.09k
            }
877
56
            else
878
56
            {
879
56
              WPS_DEBUG_MSG(("WKS4Parser::parse: the password seems bad\n"));
880
56
            }
881
882
1.15k
          }
883
1.15k
        }
884
1.60k
        break;
885
1.60k
      }
886
788
      else
887
788
      {
888
788
        WPS_DEBUG_MSG(("WKS4Parser::parse: find unexpected password field\n"));
889
788
      }
890
788
      break;
891
788
    case 0x64: // hidden column
892
472
      isParsed = m_spreadsheetParser->readHiddenColumns();
893
472
      break;
894
895
46.3k
    default:
896
46.3k
      break;
897
1.28M
    }
898
1.28M
    break;
899
1.28M
  case 0x54:
900
610k
    switch (id)
901
610k
    {
902
    // always empty ?
903
7.67k
    case 0x25:
904
7.67k
      f.str("");
905
7.67k
      f << "Entries(LICS):";
906
7.67k
      if (sz)
907
435
      {
908
435
        f << "###";
909
435
        WPS_DEBUG_MSG(("WKS4Parser::readZone: find a not empty LICS encoding zone\n"));
910
435
        break;
911
435
      }
912
7.24k
      m_state->m_hasLICSCharacters = true;
913
7.24k
      isParsed = needWriteInAscii = true;
914
7.24k
      break;
915
    // boolean
916
3.65k
    case 0x6f: // always 0
917
3.65k
      f.str("");
918
3.65k
      f << "Entries(ByteA" << std::hex << id << std::dec << "Z):";
919
3.65k
      if (sz!=1) break;
920
2.23k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
921
2.23k
      val=int(libwps::readU8(input));
922
2.23k
      if (val==0xFF) f << "true,";
923
67
      else if (val) f << "#val=" << val << ",";
924
2.23k
      isParsed=needWriteInAscii=true;
925
2.23k
      break;
926
    // small int zone ?
927
254
    case 0x12: // sometimes in spreadsheet (with 0)
928
418
    case 0x1a: // find at at the end the file, after the reports' definition
929
418
      f.str("");
930
418
      f << "Entries(IntSmallA" << std::hex << id << std::dec << "Z):";
931
418
      if (sz!=1) break;
932
104
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
933
104
      val=int(libwps::readU8(input));
934
104
      if (id==0x1a)
935
48
      {
936
48
        f.str("");
937
48
        f << "Entries(Report):act=" << val << ",";
938
48
      }
939
56
      else
940
56
      {
941
56
        if (val) f << "#val=" << val << ",";
942
56
      }
943
104
      isParsed=needWriteInAscii=true;
944
104
      break;
945
    // int zone
946
1.90k
    case 0x26: // always with 0
947
2.25k
    case 0x6a: // filter definition?
948
2.25k
      f.str("");
949
2.25k
      if (id==0x6a)
950
350
        f << "Entries(Filter)[data1]:";
951
1.90k
      else
952
1.90k
        f << "Entries(IntA" << std::hex << id << std::dec << "Z):";
953
2.25k
      if (sz!=2) break;
954
2.08k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
955
2.08k
      val=int(libwps::read16(input));
956
2.08k
      if (val) f << "f0=" << val << ",";
957
2.08k
      isParsed=needWriteInAscii=true;
958
2.08k
      break;
959
    //  zone with 2 ints
960
359
    case 0x32: // find with 00000000 (database)
961
359
      f.str("");
962
359
      f << "Entries(Int2A" << std::hex << id << std::dec << "Z):";
963
359
      if (sz!=4) break;
964
113
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
965
339
      for (int i=0; i<2; ++i)
966
226
      {
967
226
        val=int(libwps::read16(input));
968
226
        if (val) f << "f" << i << "=" << val << ",";
969
226
      }
970
113
      isParsed=needWriteInAscii=true;
971
113
      break;
972
8.90k
    case 0x1: // the last selected cell
973
8.90k
    {
974
8.90k
      f.str("");
975
8.90k
      f << "Entries(SelectCells):";
976
8.90k
      if (sz!=0xc) break;
977
8.23k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
978
8.23k
      val = int(libwps::read16(input)); // always 0?
979
8.23k
      if (val) f << "f0=" << val << ",";
980
8.23k
      int dim[4];
981
32.9k
      for (int &i : dim) i = int(libwps::read16(input));
982
8.23k
      if (dim[2]==dim[0]+1 && dim[3]==dim[1]+1) // almost always true
983
576
        f << "cell?=" << dim[0] << "x" << dim[1] << ",";
984
7.65k
      else
985
7.65k
        f << "cells?=" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ",";
986
8.23k
      val = int(libwps::read16(input)); // always 0|2
987
8.23k
      if (val) f << "f1=" << val << ",";
988
8.23k
      isParsed = needWriteInAscii = true;
989
8.23k
      break;
990
8.90k
    }
991
51.4k
    case 0x2:
992
51.4k
      ok = m_spreadsheetParser->readMsWorksDOSCellProperty();
993
51.4k
      isParsed = true;
994
51.4k
      break;
995
7.54k
    case 0x5:
996
7.54k
      if (sz!=2) break;
997
5.35k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
998
5.35k
      f.str("");
999
5.35k
      f << "Entries(Version):vers=" << std::hex << libwps::readU16(input) << std::dec;
1000
5.35k
      isParsed = needWriteInAscii = true;
1001
5.35k
      break;
1002
4.96k
    case 0x6:
1003
4.96k
      ok = m_spreadsheetParser->readMsWorksDOSFieldProperty();
1004
4.96k
      isParsed = true;
1005
4.96k
      break;
1006
830
    case 0x8: // only in database?, checkme: the structure may be different in dosfile
1007
830
      if (sz!=0x18) break;
1008
187
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1009
1.30k
      for (int i=0; i<6; ++i)   // f0=2|7, f2=0|1|2|4|5|7, f4=0|1|4|5|6|17|19|37|114, f5=3..40
1010
1.12k
      {
1011
1.12k
        val=int(libwps::read16(input));
1012
1.12k
        if (val) f << "f" << i << "=" << val << ",";
1013
1.12k
      }
1014
935
      for (int i=0; i<4; ++i)   // g0=0|12|38|59|71, g1=0|1, g3=1|2|3
1015
748
      {
1016
748
        int const expected[]= {0,1,0,2};
1017
748
        val=int(libwps::read8(input));
1018
748
        if (val!=expected[i]) f << "g" << i << "=" << val << ",";
1019
748
      }
1020
935
      for (int i=0; i<4; ++i)   // h0=0|28, h1=0|9
1021
748
      {
1022
748
        val=int(libwps::read16(input));
1023
748
        if (val) f << "h" << i << "=" << val << ",";
1024
748
      }
1025
187
      isParsed = needWriteInAscii = true;
1026
187
      break;
1027
    /* case 9: 000004002f001e000000bccf000005000f0008000000bccf0000060003000f007404bccf01000600
1028
       1c0000001b000100010007001c0001001e00010000000900300016007404bccf00000b000f0000000000d6ce
1029
       ( database, find one time)
1030
    */
1031
    /* case a: (database)
1032
       CHECKME: a long structure which seems to contain some text, a list of field?
1033
     */
1034
3.58k
    case 0x10:
1035
3.58k
      ok = m_spreadsheetParser->readFilterOpen();
1036
3.58k
      isParsed = true;
1037
3.58k
      break;
1038
4.39k
    case 0x11:
1039
4.39k
      ok = m_spreadsheetParser->readFilterClose();
1040
4.39k
      isParsed = true;
1041
4.39k
      break;
1042
4.16k
    case 0x13:
1043
4.16k
      ok = m_spreadsheetParser->readMsWorksPageBreak();
1044
4.16k
      isParsed = true;
1045
4.16k
      break;
1046
30.9k
    case 0x14:
1047
30.9k
      m_chartParser->readChartAxis();
1048
30.9k
      isParsed = true;
1049
30.9k
      break;
1050
1.34k
    case 0x15:
1051
1.34k
      m_chartParser->readChartSeries();
1052
1.34k
      isParsed = true;
1053
1.34k
      break;
1054
5.82k
    case 0x16:
1055
5.82k
      m_chartParser->readChartSeriesStyles();
1056
5.82k
      isParsed = true;
1057
5.82k
      break;
1058
290k
    case 0x17:
1059
290k
      ok=m_spreadsheetParser->readReportOpen();
1060
290k
      isParsed = true;
1061
290k
      break;
1062
558
    case 0x18:
1063
558
      ok=m_spreadsheetParser->readReportClose();
1064
558
      isParsed = true;
1065
558
      break;
1066
1067
501
    case 0x30: // 30540c00000000000000000000000000 or 30540c00ffff00000000000000000000
1068
501
      f.str("");
1069
501
      f << "Entries(ChartUnknA):";
1070
501
      break;
1071
1.08k
    case 0x31:
1072
1.08k
      m_chartParser->readChartSeriesColorMap();
1073
1.08k
      isParsed = true;
1074
1.08k
      break;
1075
2.23k
    case 0x35:
1076
2.23k
      m_chartParser->readChartDim();
1077
2.23k
      isParsed = true;
1078
2.23k
      break;
1079
254
    case 0x38: // find block of f2 bytes always 0
1080
521
    case 0x39: // find block of f2 bytes always 0
1081
521
      f.str("");
1082
521
      f << "Entries(" << (id==0x38 ? "ChartUnknB" : "ChartUnknC") << "):";
1083
521
      break;
1084
253
    case 0x41: // 41540600000000000000
1085
253
      f.str("");
1086
253
      f << "Entries(ChartUnknD):";
1087
253
      break;
1088
1.25k
    case 0x44:
1089
1.25k
      m_chartParser->readChart3D();
1090
1.25k
      isParsed = true;
1091
1.25k
      break;
1092
    /* find also 83: 835405000[02]ffffffff
1093
       85: block of 6c bytes always 0
1094
       86: 86540600010001000100
1095
       87: 87540c00880c00080a00080a00080a00 or 87540c00880c00880a18880a18880a18
1096
       related to chart in Works 4 and Works 2000 */
1097
1098
676
    case 0x19: // list id<->unkn, found after the column definition and Struct 66:54 in report, with f0=0|5|8|9
1099
1.77k
    case 0x5e: // some time in can be repeated a spreadsheet often with f0=9000
1100
1.77k
      if (id==0x19)
1101
676
      {
1102
676
        f.str("");
1103
676
        f << "Report[data1]:";
1104
676
      }
1105
1.77k
      if (sz!=4) break;
1106
234
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1107
234
      f << "id=" << libwps::read16(input) << ",";
1108
234
      val=int(libwps::readU16(input));
1109
234
      if (val) f << "f0=" << std::hex << val << std::dec << ",";
1110
234
      isParsed=needWriteInAscii=true;
1111
234
      break;
1112
    /* case 1b: 000000000000000000000000000000000000010000000000020000000000000000000000000000000000
1113
       database v1 */
1114
3.62k
    case 0x1c:
1115
3.62k
      m_spreadsheetParser->readMsWorksDOSCellExtraProperty();
1116
3.62k
      isParsed = true;
1117
3.62k
      break;
1118
889
    case 0x1f:
1119
889
    {
1120
      // frequent field, near the beginning of the file
1121
      // find with 050300000005|058000000005|05000000, so maybe:
1122
889
      if (sz<4 || (sz%2)!=0) break;
1123
671
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1124
671
      val=int(libwps::read8(input)); // always 5?
1125
671
      if (val!=5) f << "f0=" << val << ",";
1126
671
      val=int(libwps::readU8(input)); // 0|80
1127
671
      if (val) f << "f1=" << std::hex << val << ",";
1128
2.61k
      for (long i=1; i<sz/2; ++i)
1129
1.94k
      {
1130
1.94k
        val=int(libwps::read16(input));
1131
1.94k
        if (val) f << "f" << i+2 << "=" << val << ",";
1132
1.94k
      }
1133
671
      isParsed=needWriteInAscii=true;
1134
671
      break;
1135
889
    }
1136
314
    case 0x23: // single page ?
1137
2.04k
    case 0x37: // multiple page ?
1138
2.04k
      ok = readPrnt();
1139
2.04k
      isParsed = true;
1140
2.04k
      break;
1141
1.04k
    case 0x24: // font (default)
1142
1.04k
      f.str("");
1143
1.04k
      f << "Entries(FontDef):";
1144
1.04k
      if (sz!=4) break;
1145
567
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1146
567
      val=int(libwps::read16(input));
1147
567
      if (val) f << "fId=" << val << ",";
1148
567
      f << "fSz=" << libwps::read16(input)/2 << ",";
1149
567
      isParsed=needWriteInAscii=true;
1150
567
      break;
1151
1.52k
    case 0x27:
1152
1.52k
      ok = m_spreadsheetParser->readMsWorksDOSPageBreak();
1153
1.52k
      isParsed = true;
1154
1.52k
      break;
1155
625
    case 0x33:
1156
625
      f.str("");
1157
625
      f << "Entries(Protection)[form]:";
1158
625
      if (sz!=1) break;
1159
437
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1160
437
      val=int(libwps::readU8(input));
1161
437
      if (val==0)
1162
350
      {
1163
350
        f.str("");
1164
350
        f << "_";
1165
350
      }
1166
87
      else if (val==0xFF) f << "protected,";
1167
55
      else f << "#protected=" << val << ",";
1168
437
      isParsed=needWriteInAscii=true;
1169
437
      break;
1170
957
    case 0x40:
1171
957
      m_chartParser->readChartFont();
1172
957
      isParsed = true;
1173
957
      break;
1174
    // case 47: big zone, begin by a font name (database)
1175
    // case 50: 010000000000000000000000000000000000
1176
    // case 53: CHECKME: looks like b013cc06d00764000000000001000000 ( database v1)
1177
16.4k
    case 0x56:
1178
16.4k
      ok = readFont();
1179
16.4k
      isParsed = true;
1180
16.4k
      break;
1181
78
    case 0x48: // a fontname + 2 ints? (find one time in a spreadsheet file)
1182
2.17k
    case 0x57:   // int + a fontname + 2 ints? (in database a little after the field name zone)
1183
2.17k
    {
1184
2.17k
      int const headerSize= id==0x57 ? 2 : 0;
1185
2.17k
      f.str("");
1186
2.17k
      f << "Entries(Prefs)[" << std::hex << id << std::dec << "]:";
1187
2.17k
      if (sz!=0x24+headerSize) break;
1188
1.69k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1189
1.69k
      if (id==0x57)
1190
1.67k
      {
1191
1.67k
        val=int(libwps::read16(input)); // always 0?
1192
1.67k
        if (val) f << "f0=" << val << ",";
1193
1.67k
      }
1194
1.69k
      librevenge::RVNGString name;
1195
1.69k
      if (!readCString(name,32))
1196
0
        f << "##name,";
1197
1.69k
      else if (!name.empty())
1198
514
        f << name.cstr() << ",";
1199
1.69k
      input->seek(pos+36+headerSize, librevenge::RVNG_SEEK_SET);
1200
1.69k
      val=int(libwps::read16(input)); // 10|20|30|50: some flags?
1201
1.69k
      if (val!=0x10) f << "f1=" << std::hex << val << std::dec << ",";
1202
1.69k
      val=int(libwps::read16(input)); // 14|18
1203
1.69k
      if (val!=0x18) f << "f2=" << std::hex << val << std::dec << ",";
1204
1.69k
      isParsed=needWriteInAscii=true;
1205
1.69k
      break;
1206
2.17k
    }
1207
377
    case 0x58:
1208
377
    {
1209
377
      f.str("");
1210
377
      f << "Entries(Filter)[name]:";
1211
377
      if (sz!=16) break;
1212
229
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1213
229
      librevenge::RVNGString name;
1214
229
      if (!readCString(name,16))
1215
0
        f << "##name,";
1216
229
      else if (!name.empty())
1217
48
        f << name.cstr() << ",";
1218
229
      isParsed=needWriteInAscii=true;
1219
229
      break;
1220
377
    }
1221
47.4k
    case 0x5a:
1222
47.4k
      ok = m_spreadsheetParser->readMsWorksStyle();
1223
47.4k
      isParsed = true;
1224
47.4k
      break;
1225
5.07k
    case 0x5b:
1226
5.07k
      ok = m_spreadsheetParser->readCell();
1227
5.07k
      isParsed = true;
1228
5.07k
      break;
1229
    // case 5c: a small number 0-8 (database)
1230
584
    case 0x5d: // checkme
1231
584
      f.str("");
1232
584
      f << "FldProperties:";
1233
584
      if (sz!=4) break;
1234
315
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1235
315
      f << "col=" << libwps::read16(input) << ",";
1236
315
      f << "form?=" << std::hex << libwps::readU16(input) << std::dec << ",";
1237
315
      break;
1238
863
    case 0x5f:
1239
863
    {
1240
      // fixme: read end of fields
1241
863
      f.str("");
1242
863
      f << "Entries(FormZones):";
1243
863
      if (sz<0x4d) break;
1244
218
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1245
218
      int zType=libwps::read16(input);
1246
218
      switch (zType)
1247
218
      {
1248
31
      case 1:
1249
31
        f << "field,";
1250
31
        break;
1251
180
      case 2:
1252
180
        f << "textbox,";
1253
180
        break;
1254
0
      case 3:
1255
0
        f << "object,";
1256
0
        break;
1257
0
      case 4:
1258
0
        f << "rectangle,";
1259
0
        break;
1260
7
      default:
1261
7
        WPS_DEBUG_MSG(("WKS4Parser::readZone: find unknown zone type\n"));
1262
7
        f << "##type=" << zType << ",";
1263
7
        break;
1264
218
      }
1265
218
      if (input->tell()!=pos+4+sz)
1266
218
        ascii().addDelimiter(input->tell(),'|');
1267
218
      isParsed=needWriteInAscii=true;
1268
218
      break;
1269
218
    }
1270
1.01k
    case 0x64: // present in database (can to store some block: graphic?)
1271
1.01k
    {
1272
1.01k
      if (sz!=4) break;
1273
755
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1274
755
      auto dataSz=long(libwps::readU32(input));
1275
755
      if (!checkFilePosition(pos+8+dataSz)) break;
1276
508
      if (dataSz) f << "dSz=" << std::hex << dataSz << std::dec << ",";
1277
508
      ascii().addPos(pos);
1278
508
      ascii().addNote(f.str().c_str());
1279
508
      if (dataSz)
1280
189
      {
1281
189
        ascii().addPos(pos+8);
1282
189
        ascii().addNote("Entries(StructA64E)[data]:");
1283
189
        sz += dataSz;
1284
189
      }
1285
508
      isParsed = true;
1286
508
      break;
1287
755
    }
1288
7.29k
    case 0x65:
1289
7.29k
      ok = m_spreadsheetParser->readMsWorksRowSize();
1290
7.29k
      isParsed = true;
1291
7.29k
      break;
1292
    // case 66: ff|12c|13B|1d1, dim/flag? (database)
1293
126
    case 0x67: // single page ?
1294
378
    case 0x82: // multiple page ?
1295
378
      ok = readPrn2();
1296
378
      isParsed = true;
1297
378
      break;
1298
3.32k
    case 0x6b:
1299
3.32k
      ok = m_spreadsheetParser->readMsWorksColumnSize();
1300
3.32k
      isParsed = true;
1301
3.32k
      break;
1302
316
    case 0x6e: // field(series)
1303
316
      f.str("");
1304
316
      f << "Entries(FldSeries):";
1305
316
      if (sz!=8) break;
1306
53
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1307
53
      val=int(libwps::read16(input));
1308
53
      if (val) f << "col=" << val << ",";
1309
53
      f << "act[val]=" << libwps::read16(input) << ",";
1310
53
      val=int(libwps::read16(input)); // always 0 first?
1311
53
      if (val) f << "first=" << val << ",";
1312
53
      val=int(libwps::read16(input));
1313
53
      if (val!=1) f << "increm=" << val << ",";
1314
53
      isParsed=needWriteInAscii=true;
1315
53
      break;
1316
    // case 70: id? (database)
1317
1.46k
    case 0x80:
1318
2.30k
    case 0x81:
1319
2.30k
      m_chartParser->readChartLimit();
1320
2.30k
      isParsed = true;
1321
2.30k
      break;
1322
1.31k
    case 0x84:
1323
1.31k
      m_chartParser->readChart2Font();
1324
1.31k
      isParsed = true;
1325
1.31k
      break;
1326
73.5k
    default:
1327
73.5k
      break;
1328
610k
    }
1329
610k
    break;
1330
610k
  default:
1331
1.51k
    ok = false;
1332
1.51k
    break;
1333
1.89M
  }
1334
1335
1.89M
  if (!ok)
1336
2.01k
  {
1337
2.01k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1338
2.01k
    return false;
1339
2.01k
  }
1340
1.89M
  if (isParsed)
1341
861k
  {
1342
861k
    if (needWriteInAscii)
1343
105k
    {
1344
105k
      ascii().addPos(pos);
1345
105k
      ascii().addNote(f.str().c_str());
1346
105k
    }
1347
861k
    input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1348
861k
    return true;
1349
861k
  }
1350
1351
1.03M
  if (sz && input->tell()!=pos && input->tell()!=pos+4+sz)
1352
151
    ascii().addDelimiter(input->tell(),'|');
1353
1.03M
  input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1354
1.03M
  ascii().addPos(pos);
1355
1.03M
  ascii().addNote(f.str().c_str());
1356
1.03M
  return true;
1357
1.89M
}
1358
1359
////////////////////////////////////////////////////////////
1360
//   other formats
1361
////////////////////////////////////////////////////////////
1362
bool WKS4Parser::readZoneQuattro()
1363
0
{
1364
0
  libwps::DebugStream f;
1365
0
  RVNGInputStreamPtr input = getInput();
1366
0
  long pos = input->tell();
1367
0
  auto id = int(libwps::readU8(input));
1368
0
  auto type = int(libwps::readU8(input));
1369
0
  auto sz = long(libwps::readU16(input));
1370
0
  if (type>5 || sz<0 || !checkFilePosition(pos+4+sz))
1371
0
  {
1372
0
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1373
0
    return false;
1374
0
  }
1375
0
  f << "Entries(Quattro";
1376
0
  if (type) f << type << "A";
1377
0
  f << std::hex << id << std::dec << "E):";
1378
0
  if (sz) ascii().addDelimiter(pos+4,'|');
1379
0
  input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1380
0
  ascii().addPos(pos);
1381
0
  ascii().addNote(f.str().c_str());
1382
0
  return true;
1383
0
}
1384
1385
////////////////////////////////////////////////////////////
1386
//   generic
1387
////////////////////////////////////////////////////////////
1388
bool WKS4Parser::readCString(librevenge::RVNGString &string, long maxSize)
1389
462k
{
1390
462k
  RVNGInputStreamPtr input = getInput();
1391
462k
  long pos = input->tell();
1392
462k
  string.clear();
1393
462k
  if (!checkFilePosition(pos+maxSize))
1394
0
  {
1395
0
    WPS_DEBUG_MSG(("WKS4Parser::readCString: string's size seems bad\n"));
1396
0
    return false;
1397
0
  }
1398
462k
  std::string text;
1399
2.16M
  for (long i=0; i<maxSize; ++i)
1400
2.14M
  {
1401
2.14M
    auto c = char(libwps::readU8(input));
1402
2.14M
    if (c == '\0') break;
1403
1.69M
    text.push_back(c);
1404
1.69M
  }
1405
462k
  if (!text.empty())
1406
222k
    string=libwps_tools_win::Font::unicodeString(text, getDefaultFontType());
1407
462k
  return true;
1408
462k
}
1409
1410
bool WKS4Parser::readFont()
1411
16.4k
{
1412
16.4k
  libwps::DebugStream f;
1413
16.4k
  RVNGInputStreamPtr input = getInput();
1414
16.4k
  long pos = input->tell();
1415
16.4k
  auto type = int(libwps::read16(input));
1416
1417
16.4k
  if (type != 0x5456)
1418
0
  {
1419
0
    WPS_DEBUG_MSG(("WKS4Parser::readFont: not a font zone\n"));
1420
0
    return false;
1421
0
  }
1422
16.4k
  auto sz = long(libwps::readU16(input));
1423
16.4k
  long endPos = pos+4+sz;
1424
16.4k
  if (sz < 32)
1425
294
  {
1426
294
    WPS_DEBUG_MSG(("WKS4Parser::readFont: seems very short\n"));
1427
294
    ascii().addPos(pos);
1428
294
    ascii().addNote("Entries(Font)###");
1429
294
    return true;
1430
294
  }
1431
1432
16.1k
  WKS4ParserInternal::Font font(getDefaultFontType());
1433
16.1k
  auto flags = int(libwps::readU8(input));
1434
16.1k
  uint32_t attributes = 0;
1435
16.1k
  if (flags & 1) attributes |= WPS_BOLD_BIT;
1436
16.1k
  if (flags & 2) attributes |= WPS_ITALICS_BIT;
1437
16.1k
  if (flags & 4) attributes |= WPS_UNDERLINE_BIT;
1438
16.1k
  if (flags & 8) attributes |= WPS_STRIKEOUT_BIT;
1439
1440
16.1k
  font.m_attributes=attributes;
1441
16.1k
  if (flags & 0xF0)
1442
12.9k
  {
1443
12.9k
    if (!m_state->getColor((flags >> 4), font.m_color))
1444
6.35k
    {
1445
6.35k
      WPS_DEBUG_MSG(("WKS4Parser::readFont: unknown color\n"));
1446
6.35k
      f << "##color=" << (flags >> 4) << ",";
1447
6.35k
    }
1448
12.9k
  }
1449
1450
16.1k
  auto val=int(libwps::readU8(input));
1451
16.1k
  if (val) f << "f0=" << std::hex << val << std::dec << ",";
1452
16.1k
  librevenge::RVNGString name("");
1453
217k
  while (int(input->tell()) < endPos-4)
1454
216k
  {
1455
216k
    auto c = char(libwps::readU8(input));
1456
216k
    if (c == '\0') break;
1457
201k
    name.append(c);
1458
201k
  }
1459
1460
16.1k
  font.m_type=libwps_tools_win::Font::getFontType(name);
1461
16.1k
  if (font.m_type==libwps_tools_win::Font::UNKNOWN)
1462
7.07k
    font.m_type=getDefaultFontType();
1463
16.1k
  font.m_name=name;
1464
1465
16.1k
  input->seek(endPos-4, librevenge::RVNG_SEEK_SET);
1466
16.1k
  val = int(libwps::readU16(input)); // always 0x20
1467
16.1k
  if (val!=0x20)  f << "f1=" << std::hex << val << std::dec << ",";
1468
16.1k
  int fSize = libwps::read16(input)/2;
1469
16.1k
  if (fSize >= 1 && fSize <= 50)
1470
1.68k
    font.m_size=double(fSize);
1471
14.4k
  else
1472
14.4k
    f << "###fSize=" << fSize << ",";
1473
16.1k
  if (name.empty())
1474
208
    f << "###noName,";
1475
16.1k
  font.m_extra=f.str();
1476
1477
16.1k
  f.str("");
1478
16.1k
  f << "Entries(Font):";
1479
16.1k
  f << "font" << m_state->m_fontsList.size() << "[" << font << "]";
1480
16.1k
  m_state->m_fontsList.push_back(font);
1481
1482
16.1k
  ascii().addPos(pos);
1483
16.1k
  ascii().addNote(f.str().c_str());
1484
1485
16.1k
  return true;
1486
16.4k
}
1487
1488
// ----------------------------------------------------------------------
1489
// Header/Footer
1490
// ----------------------------------------------------------------------
1491
void WKS4Parser::sendHeaderFooter(bool header)
1492
22
{
1493
22
  if (!m_listener)
1494
0
  {
1495
0
    WPS_DEBUG_MSG(("WKS4Parser::sendHeaderFooter: can not find the listener\n"));
1496
0
    return;
1497
0
  }
1498
1499
22
  m_listener->setFont(m_state->getDefaultFont());
1500
22
  m_listener->insertUnicodeString(header ? m_state->m_headerString : m_state->m_footerString);
1501
22
}
1502
1503
bool WKS4Parser::readHeaderFooter(bool header)
1504
3.23k
{
1505
3.23k
  libwps::DebugStream f;
1506
3.23k
  RVNGInputStreamPtr input = getInput();
1507
3.23k
  long pos = input->tell();
1508
3.23k
  auto type = int(libwps::read16(input));
1509
3.23k
  if (type != 0x0026 && type != 0x0025)
1510
0
  {
1511
0
    WPS_DEBUG_MSG(("WKS4Parser::readHeaderFooter: not a header/footer\n"));
1512
0
    return false;
1513
0
  }
1514
3.23k
  auto sz = long(libwps::readU16(input));
1515
3.23k
  long endPos = pos+4+sz;
1516
1517
3.23k
  f << "Entries(" << (header ? "HeaderText" : "FooterText") << "):";
1518
3.23k
  if (sz==1)
1519
422
  {
1520
    // followed with 0
1521
422
    auto val=int(libwps::read8(input));
1522
422
    if (val) f << "##f0=" << val << ",";
1523
422
    ascii().addPos(pos);
1524
422
    ascii().addNote(f.str().c_str());
1525
422
    return true;
1526
422
  }
1527
2.81k
  if (sz < 0xF2)
1528
2.50k
  {
1529
2.50k
    WPS_DEBUG_MSG(("WKS4Parser::readHeaderFooter: the header/footer size seeem odds\n"));
1530
2.50k
    f << "###";
1531
2.50k
    ascii().addPos(pos);
1532
2.50k
    ascii().addNote(f.str().c_str());
1533
2.50k
    return false;
1534
2.50k
  }
1535
307
  librevenge::RVNGString text;
1536
307
  if (!readCString(text,sz))
1537
0
    f << "##name,";
1538
307
  if (header)
1539
145
    m_state->m_headerString=text;
1540
162
  else
1541
162
    m_state->m_footerString=text;
1542
307
  f << text.cstr();
1543
307
  if (input->tell()!=endPos)
1544
303
    ascii().addDelimiter(input->tell(), '|');
1545
307
  ascii().addPos(pos);
1546
307
  ascii().addNote(f.str().c_str());
1547
1548
307
  return true;
1549
2.81k
}
1550
1551
bool WKS4Parser::readPrnt()
1552
2.04k
{
1553
2.04k
  libwps::DebugStream f;
1554
2.04k
  RVNGInputStreamPtr input = getInput();
1555
2.04k
  long pos = input->tell();
1556
2.04k
  auto type = int(libwps::read16(input));
1557
2.04k
  if (type != 0x5423 && type != 0x5437)
1558
0
  {
1559
0
    WPS_DEBUG_MSG(("WKS4Parser::readPrnt: not a prnt zone\n"));
1560
0
    return false;
1561
0
  }
1562
2.04k
  auto sz = long(libwps::readU16(input));
1563
2.04k
  long endPos = pos+4+sz;
1564
1565
2.04k
  f << "Entries(PRNT):";
1566
2.04k
  if (type==0x5437) f << "chart,";
1567
2.04k
  if (sz >= 12)
1568
402
  {
1569
402
    float dim[6];
1570
402
    for (float &i : dim)
1571
2.41k
      i = float(libwps::read16(input))/1440.f;
1572
402
    f << "dim=" << dim[5] << "x" << dim[4] << ",";
1573
402
    f << "margin=[" << dim[0] << "x" << dim[2] << ","
1574
402
      << dim[3] << "x" << dim[1] << "],";
1575
    // check me
1576
402
    if (type==0x5423)
1577
211
    {
1578
211
      m_state->m_pageSpan.setFormWidth(double(dim[5]));
1579
211
      m_state->m_pageSpan.setFormLength(double(dim[4]));
1580
211
      m_state->m_pageSpan.setMarginLeft(double(dim[0]));
1581
211
      m_state->m_pageSpan.setMarginTop(double(dim[2]));
1582
211
      m_state->m_pageSpan.setMarginRight(double(dim[3]));
1583
211
      m_state->m_pageSpan.setMarginBottom(double(dim[1]));
1584
211
    }
1585
402
  }
1586
2.04k
  int val = libwps::read16(input);
1587
2.04k
  if (val!=1) f << "first[pageNumber]=" << val <<",";
1588
2.04k
  long numElt = (endPos-input->tell())/2;
1589
8.59k
  for (long i = 0; i < numElt; i++)
1590
6.54k
  {
1591
    // f2/3=0x2d0 (dim in inches ? )
1592
6.54k
    val = libwps::read16(input);
1593
6.54k
    if (!val) continue;
1594
4.80k
    f << "f" << i << "=" << std::hex << val << std::dec << ",";
1595
4.80k
  }
1596
1597
2.04k
  ascii().addPos(pos);
1598
2.04k
  ascii().addNote(f.str().c_str());
1599
2.04k
  return true;
1600
2.04k
}
1601
1602
bool WKS4Parser::readPrn2()
1603
378
{
1604
378
  libwps::DebugStream f;
1605
378
  RVNGInputStreamPtr input = getInput();
1606
378
  long pos = input->tell();
1607
378
  long type = libwps::read16(input);
1608
378
  if (type != 0x5482 && type != 0x5467)
1609
0
  {
1610
0
    WPS_DEBUG_MSG(("WKS4Parser::readPrn2: not a prn2 zone\n"));
1611
0
    return false;
1612
0
  }
1613
378
  long sz = libwps::readU16(input);
1614
378
  long endPos = pos+4+sz;
1615
1616
378
  f << "Entries(PRN2):";
1617
378
  if (sz >= 64)
1618
73
  {
1619
73
    float dim[8];
1620
219
    for (int st = 0; st < 2; st++)
1621
146
    {
1622
146
      for (float &i : dim)
1623
1.16k
        i = float(libwps::read32(input))/1440.f;
1624
146
      f << "dim" << st << "=" << dim[5] << "x" << dim[4] << ",";
1625
146
      f << "margin" << st << "=[" << dim[0] << "x" << dim[2] << ","
1626
146
        << dim[3] << "x" << dim[1] << "],";
1627
146
      f << "head/foot" << st << "?=" << dim[7] << "x" << dim[6] << ",";
1628
146
    }
1629
73
  }
1630
378
  long numElt = (endPos-input->tell())/4;
1631
  /*
1632
    in general only f0=1,
1633
    but sometime f0=1,f2=1,f8=64,f42=174,f44=1,f46=175,f48=1
1634
   */
1635
4.73k
  for (long i = 0; i < numElt; i++)
1636
4.35k
  {
1637
4.35k
    auto val = int(libwps::read16(input));
1638
4.35k
    if (!val) continue;
1639
1.02k
    f << "f" << i << "=" << std::hex << val << std::dec << ",";
1640
1.02k
  }
1641
1642
378
  ascii().addPos(pos);
1643
378
  ascii().addNote(f.str().c_str());
1644
378
  return true;
1645
378
}
1646
1647
bool WKS4Parser::readFieldName()
1648
5.51k
{
1649
5.51k
  libwps::DebugStream f;
1650
5.51k
  RVNGInputStreamPtr input = getInput();
1651
1652
5.51k
  long pos = input->tell();
1653
5.51k
  auto type = long(libwps::readU16(input));
1654
5.51k
  if (type != 0xb)
1655
0
  {
1656
0
    WPS_DEBUG_MSG(("WKS4Parser::readFieldName: not a zoneB type\n"));
1657
0
    return false;
1658
0
  }
1659
5.51k
  auto sz = long(libwps::readU16(input));
1660
5.51k
  f << "Entries(FldNames):";
1661
5.51k
  if (sz != 0x18 && sz != 0x1e)
1662
536
  {
1663
    // find also 0x85 a zone with 4 fldnames ?
1664
536
    WPS_DEBUG_MSG(("WKS4Parser::readFieldName: size seems bad\n"));
1665
536
    f << "###";
1666
536
    ascii().addPos(pos);
1667
536
    ascii().addNote(f.str().c_str());
1668
536
    return true;
1669
536
  }
1670
4.98k
  librevenge::RVNGString name;
1671
4.98k
  if (!readCString(name,16))
1672
0
    f << "##name,";
1673
4.98k
  else if (!name.empty())
1674
4.59k
    f << name.cstr() << ",";
1675
1676
4.98k
  input->seek(pos+20, librevenge::RVNG_SEEK_SET);
1677
  // the position
1678
4.98k
  int dim[4];
1679
4.98k
  if (sz==0x18)
1680
3.77k
  {
1681
15.1k
    for (int &i : dim) i=int(libwps::read16(input));
1682
3.77k
  }
1683
1.20k
  else
1684
1.20k
  {
1685
9.63k
    for (int i=0; i<7; ++i)
1686
8.42k
    {
1687
8.42k
      auto val=int(libwps::read16(input));
1688
8.42k
      if (i<2) dim[i]=val;
1689
6.02k
      else if (i>=3 && i<5) dim[i-1]=val;
1690
3.61k
      else if (val) f << "f" << i << "=" << val << ",";
1691
8.42k
    }
1692
1.20k
  }
1693
  // in a spreadsheet, the cell or the cells corresponding to the field
1694
  // in a database, col,0,col,0xFFF
1695
4.98k
  if (m_state->m_isSpreadsheet || dim[1] || dim[0]!= dim[2] || dim[3]!=0xFFF)
1696
4.66k
  {
1697
4.66k
    f << "cell=" << dim[0] << "x" << dim[1];
1698
4.66k
    if (dim[0]!=dim[2] || dim[1]!=dim[3])
1699
3.11k
      f << "<->" << dim[2] << "x" << dim[3];
1700
4.66k
    f << ",";
1701
4.66k
  }
1702
315
  else
1703
315
    f << "col=" << dim[0] << ",";
1704
4.98k
  ascii().addPos(pos);
1705
4.98k
  ascii().addNote(f.str().c_str());
1706
4.98k
  return true;
1707
5.51k
}
1708
1709
////////////////////////////////////////////////////////////
1710
//   Unknown
1711
////////////////////////////////////////////////////////////
1712
1713
1714
/* the zone 0:7 and 0:9 */
1715
bool WKS4Parser::readWindowRecord()
1716
5.09k
{
1717
5.09k
  libwps::DebugStream f;
1718
5.09k
  RVNGInputStreamPtr input = getInput();
1719
1720
5.09k
  long pos = input->tell();
1721
5.09k
  auto type = long(libwps::read16(input));
1722
5.09k
  if (type != 7 && type != 9)
1723
0
  {
1724
0
    WPS_DEBUG_MSG(("WKS4Parser::readWindowRecord: unknown type\n"));
1725
0
    return false;
1726
0
  }
1727
5.09k
  auto sz = long(libwps::readU16(input));
1728
1729
  // normally size=0x1f but one time 0x1e
1730
5.09k
  if (sz < 0x1e)
1731
3.94k
  {
1732
3.94k
    WPS_DEBUG_MSG(("WKS4Parser::readWindowRecord: zone seems too short\n"));
1733
3.94k
    ascii().addPos(pos);
1734
3.94k
    ascii().addNote("Entries(WindowRecord):###");
1735
3.94k
    return true;
1736
3.94k
  }
1737
1738
1.15k
  f << "Entries(WindowRecord)[" << type << "]:";
1739
  // f0=0-a|1f|21, f1=1-3c|1da, f2=0-6|71|7f|f1, f3=4|9|a|c(size?),
1740
  // f4=0|4|6-11, f5=5-2c, f6=0|3|6|10|1f|20, f7=0-3c|1ca (related to f1?)
1741
  // f8=0|1, f9=0|2, f10=f11=0
1742
15.0k
  for (int i=0; i<12; ++i)
1743
13.8k
  {
1744
13.8k
    auto val=int(libwps::read16(input));
1745
13.8k
    if (val) f << "f" << i << "=" << val << ",";
1746
13.8k
  }
1747
3.47k
  for (int i=0; i<2; ++i)   // g0=4, g1=4|a|b|10
1748
2.31k
  {
1749
2.31k
    auto val=int(libwps::read16(input));
1750
2.31k
    if (val!=4) f << "g" << i << "=" << val << ",";
1751
2.31k
  }
1752
1.15k
  auto val=int(libwps::read16(input)); // number between -5 and ad
1753
1.15k
  f << "g2=" << val << ",";
1754
1755
1.15k
  if (sz!=0x1e)
1756
1.13k
    ascii().addDelimiter(input->tell(),'|');
1757
1.15k
  ascii().addPos(pos);
1758
1.15k
  ascii().addNote(f.str().c_str());
1759
1760
1.15k
  return true;
1761
5.09k
}
1762
1763
/* some spreadsheet zones 0:18, 0:19, 0:20, 0:27, 0:2a */
1764
bool WKS4Parser::readUnknown1()
1765
4.67k
{
1766
4.67k
  libwps::DebugStream f;
1767
4.67k
  RVNGInputStreamPtr input = getInput();
1768
1769
4.67k
  long pos = input->tell();
1770
4.67k
  auto type = long(libwps::read16(input));
1771
4.67k
  int expectedSize=0, extraSize=0;
1772
4.67k
  switch (type)
1773
4.67k
  {
1774
454
  case 0x18:
1775
1.34k
  case 0x19:
1776
1.34k
    expectedSize=0x19;
1777
1.34k
    break;
1778
1.29k
  case 0x20:
1779
1.96k
  case 0x2a:
1780
1.96k
    expectedSize=0x10;
1781
1.96k
    break;
1782
1.35k
  case 0x27:
1783
1.35k
    expectedSize=0x19;
1784
1.35k
    extraSize=15;
1785
1.35k
    break;
1786
0
  default:
1787
0
    WPS_DEBUG_MSG(("WKS4Parser::readUnknown1: unexpected type ???\n"));
1788
0
    return false;
1789
4.67k
  }
1790
4.67k
  auto sz = long(libwps::readU16(input));
1791
1792
4.67k
  f << "Entries(Flags" << std::hex << type << std::dec << ")]:";
1793
4.67k
  if (sz != expectedSize+extraSize)
1794
2.96k
  {
1795
    // find also 270001000[01]
1796
2.96k
    if (type==0x27 && sz==1)
1797
170
    {
1798
170
      f << "f0=" << int(libwps::read8(input)) << ",";
1799
170
      ascii().addPos(pos);
1800
170
      ascii().addNote(f.str().c_str());
1801
170
      return true;
1802
170
    }
1803
2.79k
    WPS_DEBUG_MSG(("WKS4Parser::readUnknown1: the zone size seems too bad\n"));
1804
2.79k
    f << "###";
1805
2.79k
    ascii().addPos(pos);
1806
2.79k
    ascii().addNote(f.str().c_str());
1807
2.79k
    return true;
1808
2.96k
  }
1809
1810
  // find always 0xff, excepted for zone 18(f0=0), zone 19(f24=0|3), zone 27(f0=..=f23=0|ff, f24=0|3)
1811
37.4k
  for (int i=0; i<expectedSize; ++i)
1812
35.7k
  {
1813
35.7k
    auto val=int(libwps::read8(input));
1814
35.7k
    if (val!=-1) f << "f" << i << "=" << val << ",";
1815
35.7k
  }
1816
1817
1.71k
  if (type==0x27)
1818
381
  {
1819
381
    auto val=int(libwps::read8(input)); // always 0
1820
381
    if (val) f << "g0=" << val << ",";
1821
3.04k
    for (int i=0; i<7; ++i)   // g1=0|4, g2=0|72, g4=0|20, g5=0|1|4, g6=0|1, g7=0|-1|205|80d8|e9f
1822
2.66k
    {
1823
2.66k
      val=int(libwps::read16(input));
1824
2.66k
      if (val) f << "g" << i+1 << "=" << val << ",";
1825
2.66k
    }
1826
381
  }
1827
1.71k
  ascii().addPos(pos);
1828
1.71k
  ascii().addNote(f.str().c_str());
1829
1830
1.71k
  return true;
1831
4.67k
}
1832
1833
////////////////////////////////////////////////////////////
1834
//   decode
1835
////////////////////////////////////////////////////////////
1836
RVNGInputStreamPtr WKS4Parser::decodeStream(RVNGInputStreamPtr input, long endPos, std::vector<uint8_t> const &key)
1837
1.09k
{
1838
1.09k
  if (!input || key.size()!=16)
1839
0
  {
1840
0
    WPS_DEBUG_MSG(("WKS4Parser::decodeStream: the arguments seems bad\n"));
1841
0
    return RVNGInputStreamPtr();
1842
0
  }
1843
1.09k
  long actPos=input->tell();
1844
1.09k
  input->seek(0,librevenge::RVNG_SEEK_SET);
1845
1.09k
  librevenge::RVNGBinaryData data;
1846
1.09k
  if (!libwps::readDataToEnd(input, data) || long(data.size())!=endPos || !data.getDataBuffer())
1847
0
  {
1848
0
    WPS_DEBUG_MSG(("WKS4Parser::decodeStream: can not read the original input\n"));
1849
0
    return RVNGInputStreamPtr();
1850
0
  }
1851
1.09k
  auto *buf=const_cast<unsigned char *>(data.getDataBuffer());
1852
1.09k
  input->seek(actPos,librevenge::RVNG_SEEK_SET);
1853
1.09k
  uint8_t d7=0;
1854
416k
  while (!input->isEnd())
1855
416k
  {
1856
416k
    long pos=input->tell();
1857
416k
    if (pos+4>endPos) break;
1858
415k
    input->seek(2,librevenge::RVNG_SEEK_CUR);
1859
415k
    auto sSz=int(libwps::readU16(input));
1860
415k
    if (pos+4+sSz>endPos)
1861
779
    {
1862
779
      input->seek(pos,librevenge::RVNG_SEEK_SET);
1863
779
      break;
1864
779
    }
1865
16.4M
    for (int i=0; i<sSz; ++i)
1866
16.0M
    {
1867
16.0M
      auto c=uint8_t(libwps::readU8(input));
1868
16.0M
      c=uint8_t((c<<1)|(c>>7));
1869
16.0M
      c=(c^key[(d7++)&0xf]);
1870
16.0M
      buf[pos+4+i]=uint8_t((c>>6)|(c<<2));
1871
16.0M
    }
1872
415k
  }
1873
1.09k
  if (input->tell()!=endPos)
1874
997
  {
1875
997
    WPS_DEBUG_MSG(("WKS4Parser::decodeStream: can not decode the end of the file, data may be bad %lx %lx\n", static_cast<unsigned long>(input->tell()), static_cast<unsigned long>(endPos)));
1876
997
  }
1877
1.09k
  RVNGInputStreamPtr res(new WPSStringStream(data.getDataBuffer(), static_cast<unsigned int>(endPos)));
1878
1.09k
  res->seek(actPos, librevenge::RVNG_SEEK_SET);
1879
1.09k
  return res;
1880
1.09k
}
1881
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */