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 <algorithm>
26 #include <cmath>
27 #include <iterator>
28 #include <map>
29 #include <sstream>
30 #include <stack>
31 #include <utility>
32
33 #include <librevenge-stream/librevenge-stream.h>
34
35 #include "libwps_internal.h"
36 #include "libwps_tools_win.h"
37
38 #include "WKSContentListener.h"
39
40 #include "WPSCell.h"
41 #include "WPSEntry.h"
42 #include "WPSFont.h"
43 #include "WPSHeader.h"
44 #include "WPSOLE1Parser.h"
45 #include "WPSPageSpan.h"
46 #include "WPSStream.h"
47 #include "WPSStringStream.h"
48 #include "WPSTable.h"
49
50 #include "LotusChart.h"
51 #include "LotusGraph.h"
52 #include "LotusSpreadsheet.h"
53 #include "LotusStyleManager.h"
54
55 #include "Lotus.h"
56
57 using namespace libwps;
58
59 //! Internal: namespace to define internal class of LotusParser
60 namespace LotusParserInternal
61 {
62 //! the font of a LotusParser
63 struct Font final : public WPSFont
64 {
65 //! constructor
FontLotusParserInternal::Font66 explicit Font(libwps_tools_win::Font::Type type)
67 : WPSFont()
68 , m_type(type)
69 {
70 }
71 Font(Font const &)=default;
72 ~Font() final;
73 //! font encoding type
74 libwps_tools_win::Font::Type m_type;
75 };
~Font()76 Font::~Font()
77 {
78 }
79 //! the state of LotusParser
80 struct State
81 {
82 //! constructor
StateLotusParserInternal::State83 State(libwps_tools_win::Font::Type fontType, char const *password)
84 : m_fontType(fontType)
85 , m_version(-1)
86 , m_isMacFile(false)
87 , m_inMainContentBlock(false)
88 , m_fontsMap()
89 , m_pageSpan()
90 , m_maxSheet(0)
91 , m_actualZoneId(0)
92 , m_actualZoneParentId(0)
93 , m_sheetZoneIdList()
94 , m_dataZoneIdToSheetZoneIdMap()
95 , m_linkIdToLinkMap()
96 , m_actualLevels()
97 , m_zone1Stack()
98 , m_sheetSubZoneOpened(0x20, false)
99 , m_actPage(0)
100 , m_numPages(0)
101 , m_metaData()
102 , m_password(password)
103 , m_isEncrypted(false)
104 , m_isDecoded(false)
105 {
106 }
107 //! return the default font style
getDefaultFontTypeLotusParserInternal::State108 libwps_tools_win::Font::Type getDefaultFontType() const
109 {
110 if (m_fontType != libwps_tools_win::Font::UNKNOWN)
111 return m_fontType;
112 return libwps_tools_win::Font::WIN3_WEUROPE;
113 }
114
115 //! returns a default font (Courier12) with file's version to define the default encoding */
getDefaultFontLotusParserInternal::State116 WPSFont getDefaultFont() const
117 {
118 WPSFont res;
119 if (m_version <= 2)
120 res.m_name="Courier";
121 else
122 res.m_name="Times New Roman";
123 res.m_size=12;
124 return res;
125 }
126 //! returns the min and max cell
getLevelsLotusParserInternal::State127 void getLevels(WPSVec3i &minC, WPSVec3i &maxC) const
128 {
129 size_t numLevels=m_actualLevels.size();
130 for (size_t i=0; i<3; ++i)
131 {
132 if (i+1<numLevels)
133 {
134 minC[int(i)]=m_actualLevels[i+1][0];
135 maxC[int(i)]=m_actualLevels[i+1][1]-1;
136 }
137 else
138 minC[int(i)]=maxC[int(i)]=-1;
139 }
140 }
141 //! returns a map dataZoneId to sheet final id
getDataZoneIdToSheetIdMapLotusParserInternal::State142 std::map<int,int> getDataZoneIdToSheetIdMap() const
143 {
144 std::map<int,int> zoneIdToSheetMap;
145 for (size_t i=0; i<m_sheetZoneIdList.size(); ++i)
146 zoneIdToSheetMap[m_sheetZoneIdList[i]]=int(i);
147 std::map<int,int> res;
148 for (auto it : m_dataZoneIdToSheetZoneIdMap)
149 {
150 if (zoneIdToSheetMap.find(it.second)==zoneIdToSheetMap.end())
151 {
152 WPS_DEBUG_MSG(("LotusParserInternal::State::getDataZoneIdToSheetIdMap: can not find the sheet corresponding to %d\n", it.second));
153 continue;
154 }
155 res[zoneIdToSheetMap.find(it.second)->second]=it.first;
156 }
157 return res;
158 }
159 //! returns a name corresponding to the actual level(for debugging)
getLevelsDebugNameLotusParserInternal::State160 std::string getLevelsDebugName() const
161 {
162 std::stringstream s;
163 for (size_t i=0; i<m_actualLevels.size(); ++i)
164 {
165 auto const &level=m_actualLevels[i];
166 if (i==0 && level==Vec2i(0,0)) continue;
167 if (i<4)
168 {
169 char const *wh[]= {"Z", "T", "C", "R"};
170 s << wh[i];
171 }
172 else
173 s << "[F" << i << "]";
174 if (level[0]==level[1])
175 s << "_";
176 else if (level[0]==level[1]-1)
177 s << level[0];
178 else
179 s << level[0] << "x" << level[1]-1;
180 }
181 return s.str();
182 }
183 //! returns a name corresponding to the zone1 stack(for debugging)
getZone1StackDebugNameLotusParserInternal::State184 std::string getZone1StackDebugName() const
185 {
186 if (m_zone1Stack.empty())
187 return "";
188 std::stringstream s;
189 s << "ids=[";
190 for (auto const &id : m_zone1Stack)
191 s << std::hex << id << std::dec << ",";
192 s << "],";
193 return s.str();
194 }
195 //! the user font type
196 libwps_tools_win::Font::Type m_fontType;
197 //! the file version
198 int m_version;
199 //! flag to know if this is a mac file
200 bool m_isMacFile;
201 //! a flag used to know if we are in the main block or no
202 bool m_inMainContentBlock;
203 //! the fonts map
204 std::map<int, Font> m_fontsMap;
205 //! the actual document size
206 WPSPageSpan m_pageSpan;
207 //! the last sheet number
208 int m_maxSheet;
209 //! the actual zone id
210 int m_actualZoneId;
211 //! the actual zone parent id
212 int m_actualZoneParentId;
213 //! the list of sheet main zone id
214 std::vector<int> m_sheetZoneIdList;
215 //! a map to retrieve the sheet zone id from the data sheet zone id
216 std::map<int,int> m_dataZoneIdToSheetZoneIdMap;
217 //! a multimap link id to link zone
218 std::multimap<int, LotusParser::Link> m_linkIdToLinkMap;
219 //! the actual zone: (0,0), table list, col list, row list
220 std::vector<Vec2i> m_actualLevels;
221 //! a unknown Zone1 stack of increasing(?) numbers
222 std::vector<unsigned long> m_zone1Stack;
223 //! some sheet sub zones (SheetZone)
224 std::vector<bool> m_sheetSubZoneOpened;
225 int m_actPage /** the actual page*/, m_numPages /* the number of pages */;
226 //! the metadata
227 librevenge::RVNGPropertyList m_metaData;
228
229 //! the password (if known)
230 char const *m_password;
231 //! true if the file is encrypted
232 bool m_isEncrypted;
233 //! true if the main stream has been decoded
234 bool m_isDecoded;
235 private:
236 State(State const &) = delete;
237 State operator=(State const &) = delete;
238 };
239
240 }
241
242 // constructor, destructor
LotusParser(RVNGInputStreamPtr & input,WPSHeaderPtr & header,libwps_tools_win::Font::Type encoding,char const * password)243 LotusParser::LotusParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
244 libwps_tools_win::Font::Type encoding,
245 char const *password)
246 : WKSParser(input, header)
247 , m_listener()
248 , m_state(new LotusParserInternal::State(encoding, password))
249 , m_styleManager()
250 , m_chartParser()
251 , m_graphParser()
252 , m_spreadsheetParser()
253 , m_ole1Parser()
254 {
255 m_styleManager.reset(new LotusStyleManager(*this));
256 m_chartParser.reset(new LotusChart(*this));
257 m_graphParser.reset(new LotusGraph(*this));
258 m_spreadsheetParser.reset(new LotusSpreadsheet(*this));
259 }
260
~LotusParser()261 LotusParser::~LotusParser()
262 {
263 }
264
version() const265 int LotusParser::version() const
266 {
267 return m_state->m_version;
268 }
269
270 //////////////////////////////////////////////////////////////////////
271 // interface
272 //////////////////////////////////////////////////////////////////////
getDefaultFontType() const273 libwps_tools_win::Font::Type LotusParser::getDefaultFontType() const
274 {
275 return m_state->getDefaultFontType();
276 }
277
getFont(int id,WPSFont & font,libwps_tools_win::Font::Type & type) const278 bool LotusParser::getFont(int id, WPSFont &font, libwps_tools_win::Font::Type &type) const
279 {
280 if (m_state->m_fontsMap.find(id)==m_state->m_fontsMap.end())
281 {
282 WPS_DEBUG_MSG(("LotusParser::getFont: can not find font %d\n", id));
283 return false;
284 }
285 auto const &ft=m_state->m_fontsMap.find(id)->second;
286 font=ft;
287 type=ft.m_type;
288 return true;
289 }
290
getLinksList(int lId) const291 std::vector<LotusParser::Link> LotusParser::getLinksList(int lId) const
292 {
293 std::vector<LotusParser::Link> res;
294 auto range = m_state->m_linkIdToLinkMap.equal_range(lId);
295 std::transform(range.first, range.second, std::back_inserter(res), [](std::pair<int,Link> const &element)
296 {
297 return element.second;
298 });
299 return res;
300
301 }
302
hasGraphics(int sheetId) const303 bool LotusParser::hasGraphics(int sheetId) const
304 {
305 return m_graphParser->hasGraphics(sheetId);
306 }
307
sendGraphics(int sheetId)308 void LotusParser::sendGraphics(int sheetId)
309 {
310 m_graphParser->sendGraphics(sheetId);
311 }
312
getLeftTopPosition(Vec2i const & cell,int spreadsheet,Vec2f & pos) const313 bool LotusParser::getLeftTopPosition(Vec2i const &cell, int spreadsheet, Vec2f &pos) const
314 {
315 return m_spreadsheetParser->getLeftTopPosition(cell, spreadsheet, pos);
316 }
317
getSheetName(int id) const318 librevenge::RVNGString LotusParser::getSheetName(int id) const
319 {
320 return m_spreadsheetParser->getSheetName(id);
321 }
322
sendChart(int cId,WPSPosition const & pos,WPSGraphicStyle const & style)323 bool LotusParser::sendChart(int cId, WPSPosition const &pos, WPSGraphicStyle const &style)
324 {
325 return m_chartParser->sendChart(cId, pos, style);
326 }
327
updateEmbeddedObject(int id,WPSEmbeddedObject & object) const328 bool LotusParser::updateEmbeddedObject(int id, WPSEmbeddedObject &object) const
329 {
330 if (!m_ole1Parser)
331 {
332 WPS_DEBUG_MSG(("LotusParser::updateEmbeddedObject: can not find the ole1 parser\n"));
333 return false;
334 }
335 return m_ole1Parser->updateEmbeddedObject(id, object);
336 }
337
338 //////////////////////////////////////////////////////////////////////
339 // parsing
340 //////////////////////////////////////////////////////////////////////
341
342 // main function to parse the document
parse(librevenge::RVNGSpreadsheetInterface * documentInterface)343 void LotusParser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
344 {
345 RVNGInputStreamPtr input=getInput();
346 if (!input)
347 {
348 WPS_DEBUG_MSG(("LotusParser::parse: does not find main ole\n"));
349 throw (libwps::ParseException());
350 }
351
352 if (!checkHeader(nullptr)) throw(libwps::ParseException());
353
354 bool ok=false;
355 try
356 {
357 ascii().setStream(input);
358 ascii().open("MN0");
359 if (checkHeader(nullptr) && createZones())
360 createListener(documentInterface);
361 if (m_listener)
362 {
363 m_styleManager->updateState();
364 m_chartParser->updateState();
365 m_spreadsheetParser->updateState();
366 m_graphParser->updateState(m_state->getDataZoneIdToSheetIdMap(),
367 m_chartParser->getNameToChartIdMap());
368
369 m_chartParser->setListener(m_listener);
370 m_graphParser->setListener(m_listener);
371 m_spreadsheetParser->setListener(m_listener);
372
373 m_listener->startDocument();
374 for (int i=0; i<=m_state->m_maxSheet; ++i)
375 m_spreadsheetParser->sendSpreadsheet(i);
376 if (version()<=1 && !m_state->m_isMacFile && m_chartParser->getNumCharts())
377 {
378 std::vector<WPSColumnFormat> widths;
379 WPSColumnFormat width(72.);
380 width.m_numRepeat=20;
381 widths.push_back(width);
382 m_listener->openSheet(widths, "Charts");
383 m_chartParser->sendCharts();
384 m_listener->closeSheet();
385 }
386 m_listener->endDocument();
387 m_listener.reset();
388 ok = true;
389 }
390 }
391 catch (libwps::PasswordException())
392 {
393 ascii().reset();
394 WPS_DEBUG_MSG(("LotusParser::parse: password exception catched when parsing MN0\n"));
395 throw (libwps::PasswordException());
396 }
397 catch (...)
398 {
399 WPS_DEBUG_MSG(("LotusParser::parse: exception catched when parsing MN0\n"));
400 throw (libwps::ParseException());
401 }
402
403 ascii().reset();
404 if (!ok)
405 throw(libwps::ParseException());
406 }
407
createListener(librevenge::RVNGSpreadsheetInterface * interface)408 bool LotusParser::createListener(librevenge::RVNGSpreadsheetInterface *interface)
409 {
410 std::vector<WPSPageSpan> pageList;
411 WPSPageSpan ps(m_state->m_pageSpan);
412 int numPages=m_state->m_maxSheet+1;
413 if (numPages<=0) numPages=1;
414 for (int i=0; i<numPages; ++i) pageList.push_back(ps);
415 m_listener.reset(new WKSContentListener(pageList, interface));
416 m_listener->setMetaData(m_state->m_metaData);
417 return true;
418 }
419
420 ////////////////////////////////////////////////////////////
421 // low level
422 ////////////////////////////////////////////////////////////
423
424 ////////////////////////////////////////////////////////////
425 // read the header
426 ////////////////////////////////////////////////////////////
checkHeader(WPSHeader * header,bool strict)427 bool LotusParser::checkHeader(WPSHeader *header, bool strict)
428 {
429 m_state.reset(new LotusParserInternal::State(m_state->m_fontType, m_state->m_password));
430 std::shared_ptr<WPSStream> mainStream(new WPSStream(getInput(), ascii()));
431 if (!checkHeader(mainStream, true, strict))
432 return false;
433 if (header)
434 {
435 header->setMajorVersion(uint8_t(100+m_state->m_version));
436 header->setCreator(libwps::WPS_LOTUS);
437 header->setKind(libwps::WPS_SPREADSHEET);
438 header->setNeedEncoding(true);
439 header->setIsEncrypted(m_state->m_isEncrypted);
440 }
441 return true;
442 }
443
checkHeader(std::shared_ptr<WPSStream> stream,bool mainStream,bool strict)444 bool LotusParser::checkHeader(std::shared_ptr<WPSStream> stream, bool mainStream, bool strict)
445 {
446 RVNGInputStreamPtr input = stream->m_input;
447 libwps::DebugFile &ascFile=stream->m_ascii;
448 libwps::DebugStream f;
449
450 if (!stream->checkFilePosition(12))
451 {
452 WPS_DEBUG_MSG(("LotusParser::checkHeader: file is too short\n"));
453 return false;
454 }
455
456 input->seek(0,librevenge::RVNG_SEEK_SET);
457 auto firstOffset = int(libwps::readU8(input));
458 auto type = int(libwps::read8(input));
459 auto val=int(libwps::read16(input));
460 f << "FileHeader:";
461 if (firstOffset == 0 && type == 0 && val==0x1a)
462 {
463 m_state->m_version=1;
464 f << "DOS,";
465 }
466 else
467 {
468 WPS_DEBUG_MSG(("LotusParser::checkHeader: find unexpected first data\n"));
469 return false;
470 }
471 val=int(libwps::readU16(input));
472 if (!mainStream)
473 {
474 if (val!=0x8007)
475 {
476 WPS_DEBUG_MSG(("LotusParser::checkHeader: find unknown lotus file format\n"));
477 return false;
478 }
479 f << "lotus123[FMT],";
480 }
481 else if (val>=0x1000 && val<=0x1005)
482 {
483 WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus123 file\n"));
484 m_state->m_version=(val-0x1000)+1;
485 f << "lotus123[" << m_state->m_version << "],";
486 }
487 #ifdef DEBUG
488 else if (val==0x8007)
489 {
490 WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus file format, sorry parsing this file is only implemented for debugging, not output will be created\n"));
491 f << "lotus123[FMT],";
492 }
493 #endif
494 else
495 {
496 WPS_DEBUG_MSG(("LotusParser::checkHeader: unknown lotus 123 header\n"));
497 return false;
498 }
499
500 input->seek(0, librevenge::RVNG_SEEK_SET);
501 if (strict)
502 {
503 for (int i=0; i < 4; ++i)
504 {
505 if (!readZone(stream)) return false;
506 if (m_state->m_isEncrypted) break;
507 }
508 }
509 ascFile.addPos(0);
510 ascFile.addNote(f.str().c_str());
511 return true;
512 }
513
createZones()514 bool LotusParser::createZones()
515 {
516 RVNGInputStreamPtr input=getInput();
517 if (!input)
518 {
519 WPS_DEBUG_MSG(("LotusParser::createZones: can not find the main input\n"));
520 return false;
521 }
522 m_styleManager->cleanState();
523 m_chartParser->cleanState();
524 m_graphParser->cleanState();
525 m_spreadsheetParser->cleanState();
526
527 int const vers=version();
528
529 std::shared_ptr<WPSStream> mainStream(new WPSStream(input, ascii()));
530 if (vers>=3)
531 {
532 m_ole1Parser.reset(new WPSOLE1Parser(mainStream));
533 m_ole1Parser->createZones();
534 std::shared_ptr<WPSStream> wkStream=m_ole1Parser->getStreamForName(vers==3 ? "WK3" : "123");
535 if (wkStream)
536 {
537 if (!readZones(wkStream)) return false;
538 m_ole1Parser->updateMetaData(m_state->m_metaData, getDefaultFontType());
539 if (vers==3)
540 {
541 std::shared_ptr<WPSStream> fmStream=m_ole1Parser->getStreamForName("FM3");
542 if (fmStream) readZones(fmStream);
543 }
544 return true;
545 }
546 }
547 input->seek(0, librevenge::RVNG_SEEK_SET);
548 if (!readZones(mainStream)) return false;
549 if (vers<=2) parseFormatStream();
550 return true;
551 }
552
parseFormatStream()553 bool LotusParser::parseFormatStream()
554 {
555 RVNGInputStreamPtr file=getFileInput();
556 if (!file || !file->isStructured()) return false;
557
558 RVNGInputStreamPtr formatInput(file->getSubStreamByName("FM3"));
559 if (!formatInput)
560 {
561 WPS_DEBUG_MSG(("LotusParser::parseFormatStream: can not find the format stream\n"));
562 return false;
563 }
564
565 std::shared_ptr<WPSStream> formatStream(new WPSStream(formatInput));
566 formatInput->seek(0, librevenge::RVNG_SEEK_SET);
567 formatStream->m_ascii.open("FM3");
568 formatStream->m_ascii.setStream(formatInput);
569 if (!checkHeader(formatStream, false, false))
570 {
571 WPS_DEBUG_MSG(("LotusParser::parseFormatStream: can not read format stream\n"));
572 return false;
573 }
574 return readZones(formatStream);
575 }
576
readZones(std::shared_ptr<WPSStream> stream)577 bool LotusParser::readZones(std::shared_ptr<WPSStream> stream)
578 {
579 if (!stream)
580 {
581 WPS_DEBUG_MSG(("LotusParser::readZones: can not find the stream\n"));
582 return false;
583 }
584 RVNGInputStreamPtr &input = stream->m_input;
585 libwps::DebugFile &ascFile=stream->m_ascii;
586
587 bool mainDataRead=false;
588 // data, format and ?
589 for (int wh=0; wh<2; ++wh)
590 {
591 if (input->isEnd())
592 break;
593
594 while (readZone(stream))
595 {
596 if (m_state->m_isEncrypted && !m_state->m_isDecoded)
597 throw(libwps::PasswordException());
598 }
599
600 //
601 // look for ending
602 //
603 long pos = input->tell();
604 if (!stream->checkFilePosition(pos+4))
605 break;
606 auto type = int(libwps::readU16(input)); // 1
607 auto length = int(libwps::readU16(input));
608 if (type==1 && length==0)
609 {
610 ascFile.addPos(pos);
611 ascFile.addNote("Entries(EOF)");
612 if (!mainDataRead)
613 mainDataRead=m_state->m_inMainContentBlock;
614 // end of block, look for other blocks
615 continue;
616 }
617 input->seek(pos, librevenge::RVNG_SEEK_SET);
618 break;
619 }
620
621 while (!input->isEnd())
622 {
623 long pos=input->tell();
624 if (pos>=stream->m_eof) break;
625 auto id = int(libwps::readU8(input));
626 auto type = int(libwps::readU8(input));
627 auto sz = long(libwps::readU16(input));
628 if ((type>0x2a) || sz<0 || !stream->checkFilePosition(pos+4+sz))
629 {
630 input->seek(pos, librevenge::RVNG_SEEK_SET);
631 break;
632 }
633 libwps::DebugStream f;
634 f << "Entries(UnknZon" << std::hex << id << "):";
635 ascFile.addPos(pos);
636 ascFile.addNote(f.str().c_str());
637 input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
638 }
639
640 if (!input->isEnd() && input->tell()<stream->m_eof)
641 {
642 ascFile.addPos(input->tell());
643 ascFile.addNote("Entries(Unknown)");
644 }
645
646 return mainDataRead || m_spreadsheetParser->hasSomeSpreadsheetData();
647 }
648
readZone(std::shared_ptr<WPSStream> & stream)649 bool LotusParser::readZone(std::shared_ptr<WPSStream> &stream)
650 {
651 if (!stream)
652 return false;
653 RVNGInputStreamPtr &input = stream->m_input;
654 libwps::DebugFile &ascFile=stream->m_ascii;
655 libwps::DebugStream f;
656 long pos = input->tell();
657 auto id = int(libwps::readU8(input));
658 auto type = int(libwps::readU8(input));
659 auto sz = long(libwps::readU16(input));
660 long endPos=pos+4+sz;
661 if ((type>0x2a) || sz<0 || !stream->checkFilePosition(endPos))
662 {
663 input->seek(pos, librevenge::RVNG_SEEK_SET);
664 return false;
665 }
666 f << "Entries(Lotus";
667 if (type) f << std::hex << type << std::dec << "A";
668 f << std::hex << id << std::dec << "E):";
669 bool ok = true, isParsed = false, needWriteInAscii = false;
670 int val;
671 input->seek(pos, librevenge::RVNG_SEEK_SET);
672 int const vers=version();
673 switch (type)
674 {
675 case 0:
676 switch (id)
677 {
678 case 0:
679 {
680 if (sz!=26)
681 {
682 ok=false;
683 break;
684 }
685 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
686 f.str("");
687 f << "Entries(BOF):";
688 val=int(libwps::readU16(input));
689 m_state->m_inMainContentBlock=false;
690 if (val==0x8007)
691 f << "FMT,";
692 else if (val>=0x1000 && val <= 0x1005)
693 {
694 m_state->m_inMainContentBlock=true;
695 f << "version=" << (val-0x1000) << ",";
696 }
697 else
698 f << "#version=" << std::hex << val << std::dec << ",";
699 for (int i=0; i<4; ++i) // f0=4, f3 a small number
700 {
701 val=int(libwps::read16(input));
702 if (val)
703 f << "f" << i << "=" << val << ",";
704 }
705 val=int(libwps::readU8(input));
706 if (m_state->m_inMainContentBlock)
707 {
708 m_spreadsheetParser->setLastSpreadsheetId(val);
709 m_state->m_maxSheet=val;
710 }
711 if (val && m_state->m_inMainContentBlock)
712 f << "max[sheet]=" << val << ",";
713 else if (val)
714 f << "max[fmt]=" << val << ",";
715
716 for (int i=0; i<7; ++i) // g0/g1=0..fd, g2=0|4, g3=0|5|7|1e|20|30, g4=0|8c|3d, g5=1|10, g6=2|a
717 {
718 val=int(libwps::readU8(input));
719 if (val)
720 f << "g" << i << "=" << std::hex << val << std::dec << ",";
721 }
722 isParsed=needWriteInAscii=true;
723 break;
724 }
725 case 0x1: // EOF
726 ok = false;
727 break;
728 case 0x2:
729 m_state->m_isEncrypted=true;
730 if (sz==16)
731 {
732 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
733 std::vector<uint8_t> fileKeys;
734 for (int i=0; i<16; ++i)
735 {
736 unsigned char c(libwps::readU8(input));
737 fileKeys.push_back(uint8_t(c));
738 }
739 isParsed=needWriteInAscii=true;
740 if (!m_state->m_isDecoded)
741 {
742 static uint8_t const defValues[]=
743 {
744 0xb9,0x5f, 0xd7,0x31, 0xdb,0x75, 9,0x72,
745 0x5d,0x85, 0x32,0x11, 0x5,0x11, 0x58,0
746 };
747 uint16_t key;
748 std::vector<uint8_t> keys;
749 if (m_state->m_password && libwps::encodeLotusPassword(m_state->m_password, key, keys, defValues))
750 {
751 bool passwordOk=fileKeys.size()==keys.size();
752 if (passwordOk)
753 {
754 /* check that the password is ok, normally,
755 all keys must be equal excepted:
756 - fileKey7=key7^(key>>8),
757 - and fileKey13=key13^key.
758
759 This also means that knowing fileKeys,
760 it is possible to retrieve the password
761 if it is short enough.
762 */
763 int numSame=0;
764 for (size_t c=0; c < fileKeys.size(); ++c)
765 {
766 if (keys[c]==fileKeys[c])
767 ++numSame;
768 }
769 passwordOk=numSame>=14;
770 if (!passwordOk)
771 {
772 WPS_DEBUG_MSG(("LotusParser::parse: the password seems bad\n"));
773 }
774 }
775 if (!passwordOk)
776 {
777 keys=retrievePasswordKeys(fileKeys);
778 passwordOk=keys.size()==16;
779 }
780 RVNGInputStreamPtr newInput;
781 if (passwordOk) newInput=decodeStream(input, stream->m_eof, keys);
782 if (newInput)
783 {
784 // let's replace the current input by the decoded input
785 m_state->m_isDecoded=true;
786 stream->m_input=newInput;
787 stream->m_ascii.setStream(newInput);
788 }
789 }
790 }
791 }
792 else
793 {
794 WPS_DEBUG_MSG(("LotusParser::parse: find unexpected password field\n"));
795 throw (libwps::PasswordException());
796 }
797 f.str("");
798 f << "Entries(Password):";
799 break;
800 case 0x3:
801 if (sz!=6)
802 {
803 ok=false;
804 break;
805 }
806 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
807 for (int i=0; i<3; ++i) // f0=1, f2=1|32
808 {
809 val=int(libwps::read16(input));
810 if (val)
811 f << "f" << i << "=" << val << ",";
812 }
813 isParsed=needWriteInAscii=true;
814 break;
815 case 0x4:
816 if (sz!=28)
817 {
818 ok=false;
819 break;
820 }
821 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
822 for (int i=0; i<2; ++i) // f0=1|3
823 {
824 val=int(libwps::read8(input));
825 if (val!=1)
826 f << "f" << i << "=" << val << ",";
827 }
828 for (int i=0; i<2; ++i) // f2=1-3, f1=0|1
829 {
830 val=int(libwps::read16(input));
831 if (val)
832 f << "f" << i+1 << "=" << val << ",";
833 }
834 isParsed=needWriteInAscii=true;
835 break;
836 case 0x5:
837 {
838 f.str("");
839 f << "Entries(SheetUnknA):";
840 if (sz!=16)
841 {
842 ok=false;
843 break;
844 }
845 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
846 val=int(libwps::readU8(input));
847 if (val)
848 f << "sheet[id]=" << val << ",";
849 val=int(libwps::read8(input)); // always 0?
850 if (val)
851 f << "f0=" << val << ",";
852
853 isParsed=needWriteInAscii=true;
854 break;
855 }
856 case 0x6: // one by sheet
857 f.str("");
858 f << "Entries(SheetUnknB):";
859 if (sz!=5)
860 {
861 ok=false;
862 break;
863 }
864 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
865 val=int(libwps::readU8(input));
866 if (val)
867 f << "sheet[id]=" << val << ",";
868 for (int i=0; i<4; ++i) // f0=0, f2=0|1, f3=7-9
869 {
870 val=int(libwps::read8(input)); // always 0?
871 if (val)
872 f << "f" << i << "=" << val << ",";
873 }
874 isParsed=needWriteInAscii=true;
875 break;
876 case 0x7:
877 ok=isParsed=m_spreadsheetParser->readColumnSizes(stream);
878 break;
879 case 0x9:
880 ok=isParsed=m_spreadsheetParser->readCellName(stream);
881 break;
882 case 0xa:
883 ok=isParsed=readLinkZone(stream);
884 break;
885 case 0xb: // 0,1,-1
886 case 0x1e: // always with 0
887 case 0x21:
888 if (sz!=1)
889 {
890 ok=false;
891 break;
892 }
893 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
894 val=int(libwps::read8(input));
895 if (val==1)
896 f << "true,";
897 else if (val)
898 f << "val=" << val << ",";
899 break;
900 case 0xc: // find 0 or 4 int with value 0|1|ff
901 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
902 for (long i=0; i<sz; ++i)
903 {
904 val=int(libwps::read8(input));
905 if (val==1) f << "f" << i << ",";
906 else if (val) f << "f" << i << "=" << val << ",";
907 }
908 isParsed=needWriteInAscii=true;
909 break;
910 case 0xe:
911 if (sz<30)
912 {
913 ok=false;
914 break;
915 }
916 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
917 for (int i=0; i<30; ++i) // f7=0|f, f8=0|60, f9=0|54, f17=80, f18=0|ff, f19=3f|40, f26=0|f8, f27=80|ff, f28=b|c,f29=40
918 {
919 val=int(libwps::read8(input));
920 if (val) f << "f" << i << "=" << val << ",";
921 }
922 if (sz>=32)
923 {
924 val=int(libwps::read16(input)); // always 1?
925 if (val!=1) f << "f30=" << val << ",";
926 }
927 isParsed=needWriteInAscii=true;
928 break;
929 case 0xf:
930 if (sz<0x56)
931 {
932 ok=false;
933 break;
934 }
935 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
936 val=int(libwps::read8(input)); // 1|2
937 if (val!=1) f << "f0=" << val << ",";
938 for (int i=0; i<3; ++i)
939 {
940 long actPos=input->tell();
941 std::string name("");
942 for (int j=0; j<16; ++j)
943 {
944 auto c=char(libwps::readU8(input));
945 if (!c) break;
946 name += c;
947 }
948 if (!name.empty())
949 f << "str" << i << "=" << name << ",";
950 input->seek(actPos+16, librevenge::RVNG_SEEK_SET);
951 }
952 for (int i=0; i<17; ++i) // f2=f11=1,f15=0|1, f16=0|2, f17=0|1|2
953 {
954 val=int(libwps::read8(input));
955 if (val) f << "f" << i+1 << "=" << val << ",";
956 }
957 for (int i=0; i<10; ++i) // g0=0|1,g1=Ø|1, g2=4|a, g3=4c|50|80, g4=g5=0|2, g6=42, g7=41|4c, g8=3c|42|59
958 {
959 val=int(libwps::read16(input));
960 if (val) f << "g" << i << "=" << val << ",";
961 }
962 isParsed=needWriteInAscii=true;
963 break;
964 case 0x10: // CHECKME
965 {
966 if (sz<3)
967 {
968 ok=false;
969 break;
970 }
971 f.str("");
972 f << "Entries(Macro):";
973 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
974 for (int i=0; i<2; ++i)
975 {
976 val=int(libwps::readU8(input));
977 if (val) f << "f" << i << "=" << val << ",";
978 }
979 std::string data("");
980 for (long i=2; i<sz; ++i)
981 {
982 auto c=char(libwps::readU8(input));
983 if (!c) break;
984 data += c;
985 }
986 if (!data.empty())
987 f << "data=" << data << ",";
988 if (input->tell()!=endPos && input->tell()+1!=endPos)
989 {
990 WPS_DEBUG_MSG(("LotusParser::readZone: the string zone %d seems too short\n", id));
991 f << "###";
992 }
993 isParsed=needWriteInAscii=true;
994 break;
995 }
996 case 0x11:
997 ok=isParsed=m_chartParser->readChart(stream);
998 break;
999 case 0x12:
1000 ok=isParsed=m_chartParser->readChartName(stream);
1001 break;
1002 case 0x13:
1003 isParsed=m_spreadsheetParser->readRowFormats(stream);
1004 break;
1005 case 0x15:
1006 case 0x1d:
1007 if (sz!=4)
1008 {
1009 WPS_DEBUG_MSG(("LotusParser::readZone: size of zone%d seems bad\n", id));
1010 f << "###";
1011 break;
1012 }
1013 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1014 val=int(libwps::read16(input)); // small number 6-c maybe a style
1015 if (val) f << "f0=" << val << ",";
1016 for (int i=0; i<2; ++i) // zone15: f1=3, f2=2-5, zone 1d: always 0
1017 {
1018 val=int(libwps::readU8(input));
1019 if (val) f << "f" << i+1 << "=" << val << ",";
1020 }
1021 isParsed=needWriteInAscii=true;
1022 break;
1023 case 0x16: // the cell text
1024 case 0x17: // double10 cell
1025 case 0x18: // uint16 double cell
1026 case 0x19: // double10+formula
1027 case 0x1a: // text formula result cell
1028 case 0x25: // uint32 double cell
1029 case 0x26: // comment cell
1030 case 0x27: // double8 cell
1031 case 0x28: // double8+formula
1032 ok=isParsed=m_spreadsheetParser->readCell(stream);
1033 break;
1034 case 0x1b:
1035 isParsed=readDataZone(stream);
1036 break;
1037 case 0x1c: // always 00002d000000
1038 if (sz!=6)
1039 {
1040 WPS_DEBUG_MSG(("LotusParser::readZone: size of zone%d seems bad\n", id));
1041 f << "###";
1042 break;
1043 }
1044 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1045 for (int i=0; i<6; ++i) // some int
1046 {
1047 val=int(libwps::readU8(input));
1048 if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1049 }
1050 isParsed=needWriteInAscii=true;
1051 break;
1052 case 0x1f:
1053 isParsed=ok=m_spreadsheetParser->readColumnDefinition(stream);
1054 break;
1055 case 0x23:
1056 isParsed=ok=m_spreadsheetParser->readSheetName(stream);
1057 break;
1058 // case 13: big structure
1059
1060 //
1061 // format:
1062 //
1063 case 0x93: // 4
1064 case 0x96: // 0 or FF
1065 case 0x97: // 5F
1066 case 0x98: // 0|2|3
1067 case 0x99: // 0|4 or FF
1068 case 0x9c: // 0
1069 case 0xa3: // 0 or FF
1070 case 0xce: // 1
1071 case 0xcf: // 1
1072 case 0xd0: // 1
1073 if (m_state->m_inMainContentBlock) break;
1074 f.str("");
1075 f << "Entries(FMTByte" << std::hex << id << std::dec << "Z):";
1076 if (sz!=1)
1077 {
1078 f << "###";
1079 break;
1080 }
1081 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1082 val=int(libwps::readU8(input));
1083 if (val==0xFF) f << "true,";
1084 else if (val) f << "val=" << val << ",";
1085 isParsed=needWriteInAscii=true;
1086 break;
1087 case 0x87: // always with 0000
1088 case 0x88: // always with 0000
1089 case 0x8e: // with 57|64
1090 case 0x9a: // with 800
1091 case 0x9b: // with 720
1092 case 0xcd: // with 57|64
1093 if (m_state->m_inMainContentBlock) break;
1094 f.str("");
1095 f << "Entries(FMTInt" << std::hex << id << std::dec << "Z):";
1096 if (sz!=2)
1097 {
1098 f << "###";
1099 break;
1100 }
1101 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1102 val=int(libwps::readU16(input));
1103 if (val) f << "val=" << val << ",";
1104 isParsed=needWriteInAscii=true;
1105 break;
1106 case 0x86:
1107 case 0x89:
1108 case 0xba: // header?
1109 case 0xbb: // footer?
1110 {
1111 if (m_state->m_inMainContentBlock) break;
1112 f.str("");
1113 if (id==0x86)
1114 f << "Entries(FMTPrinter):";
1115 else if (id==0x89)
1116 f << "Entries(FMTPrinter):shortName,";
1117 else if (id==0xba)
1118 f << "Entries(FMTHeader):";
1119 else
1120 f << "Entries(FMTFooter):";
1121
1122 if (sz<1)
1123 {
1124 f << "###";
1125 break;
1126 }
1127 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1128 std::string text;
1129 for (long i=0; i<sz; ++i) text+=char(libwps::readU8(input));
1130 f << text << ",";
1131 isParsed=needWriteInAscii=true;
1132 break;
1133 }
1134 case 0xae:
1135 if (m_state->m_inMainContentBlock) break;
1136 isParsed=m_styleManager->readFMTFontName(stream);
1137 break;
1138 case 0xaf:
1139 case 0xb1:
1140 if (m_state->m_inMainContentBlock) break;
1141 isParsed=m_styleManager->readFMTFontSize(stream);
1142 break;
1143 case 0xb0:
1144 if (m_state->m_inMainContentBlock) break;
1145 isParsed=m_styleManager->readFMTFontId(stream);
1146 break;
1147 case 0xb6:
1148 if (m_state->m_inMainContentBlock) break;
1149 isParsed=readFMTStyleName(stream);
1150 break;
1151 case 0xb8: // always 0101
1152 if (m_state->m_inMainContentBlock) break;
1153 f.str("");
1154 f << "Entries(FMTInts" << std::hex << id << std::dec << "Z):";
1155 if (sz!=2)
1156 {
1157 f << "###";
1158 break;
1159 }
1160 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1161 for (int i=0; i<2; ++i)
1162 {
1163 val=int(libwps::readU8(input));
1164 if (val!=1) f << "f" << i << "=" << val << ",";
1165 }
1166 isParsed=needWriteInAscii=true;
1167 break;
1168 case 0xc3:
1169 if (m_state->m_inMainContentBlock) break;
1170 isParsed=ok=m_spreadsheetParser->readSheetHeader(stream);
1171 break;
1172 case 0xc4: // with 0-8, 5c-15c
1173 case 0xcb: // unsure, seems appeared together a group with 1,1
1174 if (m_state->m_inMainContentBlock) break;
1175 f.str("");
1176 if (id==0xcb)
1177 f << "Entries(FMTGrpData):";
1178 else
1179 f << "Entries(FMTInt2" << std::hex << id << std::dec << "Z):";
1180 if (sz!=4)
1181 {
1182 f << "###";
1183 break;
1184 }
1185 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1186 for (int i=0; i<2; ++i)
1187 {
1188 val=int(libwps::readU16(input));
1189 if (val) f << "f" << i << "=" << val << ",";
1190 }
1191 isParsed=needWriteInAscii=true;
1192 break;
1193 case 0xc5:
1194 if (m_state->m_inMainContentBlock) break;
1195 isParsed=ok=m_spreadsheetParser->readExtraRowFormats(stream);
1196 break;
1197 case 0xc9:
1198 if (m_state->m_inMainContentBlock) break;
1199 isParsed=ok=m_graphParser->readZoneBeginC9(stream);
1200 break;
1201 case 0xca: // a graphic
1202 if (m_state->m_inMainContentBlock) break;
1203 isParsed=ok=m_graphParser->readGraphic(stream);
1204 break;
1205 case 0xcc: // frame of a graphic
1206 if (m_state->m_inMainContentBlock) break;
1207 isParsed=ok=m_graphParser->readFrame(stream);
1208 break;
1209 case 0xd1: // the textbox data
1210 if (m_state->m_inMainContentBlock) break;
1211 isParsed=ok=m_graphParser->readTextBoxDataD1(stream);
1212 break;
1213 case 0xb7:
1214 if (m_state->m_inMainContentBlock) break;
1215 isParsed=ok=m_graphParser->readFMTPictName(stream);
1216 break;
1217 case 0xbf: // variable size, can also contain a name, ...
1218 case 0xc0: // size 1a: dim + a list of id?
1219 case 0xc2: // size 22: a list of id?
1220 if (m_state->m_inMainContentBlock) break;
1221 f.str("");
1222 f << "Entries(FMTPict" << std::hex << id << std::dec << "):";
1223 break;
1224 default:
1225 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1226 if (!m_state->m_inMainContentBlock && id>=0x80)
1227 {
1228 f.str("");
1229 f << "Entries(FMT" << std::hex << id << std::dec << "E):";
1230 }
1231 break;
1232 }
1233 break;
1234 case 1:
1235 if (vers<=2)
1236 {
1237 ok=false;
1238 break;
1239 }
1240 ok = isParsed=readZone1(stream);
1241 break;
1242 case 2:
1243 if (vers<=2)
1244 {
1245 ok=false;
1246 break;
1247 }
1248 ok = isParsed=readSheetZone(stream);
1249 break;
1250 case 3:
1251 if (vers<=2)
1252 {
1253 ok=false;
1254 break;
1255 }
1256 ok = isParsed=m_graphParser->readGraphZone(stream, m_state->m_actualZoneParentId); // sheetZone.Data0
1257 break;
1258 case 4:
1259 if (vers<=2)
1260 {
1261 ok=false;
1262 break;
1263 }
1264 ok = isParsed=readZone4(stream);
1265 break;
1266 case 5:
1267 if (vers<=2)
1268 {
1269 ok=false;
1270 break;
1271 }
1272 ok = isParsed=readChartZone(stream);
1273 break;
1274 case 6:
1275 if (vers<=2)
1276 {
1277 ok=false;
1278 break;
1279 }
1280 ok = isParsed=readRefZone(stream);
1281 break;
1282 case 7:
1283 if (vers<=2)
1284 {
1285 ok=false;
1286 break;
1287 }
1288 ok = isParsed=readZone7(stream);
1289 break;
1290 case 8:
1291 if (vers<=2)
1292 {
1293 ok=false;
1294 break;
1295 }
1296 ok = isParsed=readZone8(stream);
1297 break;
1298 case 0xa:
1299 if (vers<=2)
1300 {
1301 ok=false;
1302 break;
1303 }
1304 ok = isParsed=readVersionZone(stream);
1305 break;
1306 default:
1307 // checkme: maybe <5 is ok
1308 if (vers<=2)
1309 {
1310 ok=false;
1311 break;
1312 }
1313 ok = isParsed=readZoneV3(stream);
1314 break;
1315 }
1316 if (!ok)
1317 {
1318 input->seek(pos, librevenge::RVNG_SEEK_SET);
1319 return false;
1320 }
1321 if (sz && input->tell()!=pos && input->tell()!=endPos)
1322 ascFile.addDelimiter(input->tell(),'|');
1323 input->seek(endPos, librevenge::RVNG_SEEK_SET);
1324 if (!isParsed || needWriteInAscii)
1325 {
1326 ascFile.addPos(pos);
1327 ascFile.addNote(f.str().c_str());
1328 }
1329 return true;
1330 }
1331
readDataZone(std::shared_ptr<WPSStream> stream)1332 bool LotusParser::readDataZone(std::shared_ptr<WPSStream> stream)
1333 {
1334 if (!stream) return false;
1335 RVNGInputStreamPtr &input = stream->m_input;
1336 libwps::DebugFile &ascFile=stream->m_ascii;
1337 libwps::DebugStream f;
1338 long pos = input->tell();
1339 auto type = int(libwps::readU16(input));
1340 auto sz = long(libwps::readU16(input));
1341 long endPos=pos+4+sz;
1342 if (type!=0x1b || sz<2)
1343 {
1344 WPS_DEBUG_MSG(("LotusParser::readDataZone: the zone seems odd\n"));
1345 input->seek(pos, librevenge::RVNG_SEEK_SET);
1346 return false;
1347 }
1348 type = int(libwps::readU16(input));
1349 f << "Entries(Data" << std::hex << type << std::dec << "E):";
1350 bool isParsed=false, needWriteInAscii=false;
1351 sz-=2;
1352 int val;
1353 switch (type)
1354 {
1355 //
1356 // mac windows
1357 //
1358 case 0x7d2:
1359 {
1360 f.str("");
1361 f << "Entries(WindowsMacDef):";
1362 if (sz<26)
1363 {
1364 WPS_DEBUG_MSG(("LotusParser::readDataZone: the windows definition seems bad\n"));
1365 f << "###";
1366 break;
1367 }
1368 val=int(libwps::readU8(input));
1369 if (val) f << "id=" << val << ",";
1370 val=int(libwps::read8(input)); // find 0|2
1371 if (val) f << "f0=" << val << ",";
1372 int dim[4];
1373 for (int i=0; i<4; ++i)
1374 {
1375 dim[i]=int(libwps::read16(input));
1376 val=int(libwps::read16(input));
1377 if (!val) continue;
1378 if (i)
1379 f << "num[split]=" << val << ",";
1380 else
1381 f << "dim" << i << "[h]=" << val << ",";
1382 }
1383 f << "dim=" << WPSBox2i(Vec2i(dim[0],dim[1]),Vec2i(dim[2],dim[3])) << ",";
1384 for (int i=0; i<8; ++i) // small value or 100
1385 {
1386 val=int(libwps::read8(input));
1387 if (val) f << "f" << i+1 << "=" << val << ",";
1388 }
1389 isParsed=needWriteInAscii=true;
1390 auto remain=int(sz-26);
1391 if (remain<=1) break;
1392 std::string name("");
1393 for (int i=0; i<remain; ++i)
1394 name+=char(libwps::readU8(input));
1395 f << name << ",";
1396 break;
1397 }
1398 case 0x7d3:
1399 {
1400 f.str("");
1401 f << "Entries(WindowsMacSplit):";
1402 if (sz<24)
1403 {
1404 WPS_DEBUG_MSG(("LotusParser::readDataZone: the windows split seems bad\n"));
1405 f << "###";
1406 break;
1407 }
1408 val=int(libwps::readU8(input));
1409 if (val) f << "id=" << val << ",";
1410 val=int(libwps::readU8(input));
1411 if (val) f << "split[id]=" << val << ",";
1412 for (int i=0; i<3; ++i) // 0 or 1
1413 {
1414 val=int(libwps::read8(input));
1415 if (val) f << "f" << i+1 << "=" << val << ",";
1416 }
1417 int dim[4];
1418 for (int i=0; i<4; ++i)
1419 {
1420 val=int(libwps::read16(input));
1421 dim[i]=int(libwps::read16(input));
1422 if (val) f << "dim" << i <<"[h]=" << val << ",";
1423 }
1424 f << "dim=" << WPSBox2i(Vec2i(dim[0],dim[1]),Vec2i(dim[2],dim[3])) << ",";
1425 for (int i=0; i<3; ++i)
1426 {
1427 static int const expected[]= {0,-1,25};
1428 val=int(libwps::read8(input));
1429 if (val!=expected[i]) f << "g" << i << "=" << val << ",";
1430 }
1431 isParsed=needWriteInAscii=true;
1432 break;
1433 }
1434 case 0x7d4:
1435 {
1436 f.str("");
1437 f << "Entries(WindowsMacUnkn0)";
1438 if (sz<5)
1439 {
1440 WPS_DEBUG_MSG(("LotusParser::readDataZone: the windows unkn0 seems bad\n"));
1441 f << "###";
1442 break;
1443 }
1444 for (int i=0; i<4; ++i) // always 2,1,1,2 ?
1445 {
1446 val=int(libwps::read8(input));
1447 if (val) f << "f" << i << "=" << val << ",";
1448 }
1449 isParsed=needWriteInAscii=true;
1450 auto remain=int(sz-4);
1451 if (remain<=1) break;
1452 std::string name("");
1453 for (int i=0; i<remain; ++i) // always LMBCS 1.2?
1454 name+=char(libwps::readU8(input));
1455 f << name << ",";
1456 break;
1457 }
1458 case 0x7d5: // frequently followed by Lotus13 block and SheetRow, ...
1459 f.str("");
1460 f << "Entries(SheetBegin):";
1461 if (sz!=11)
1462 {
1463 WPS_DEBUG_MSG(("LotusParser::readDataZone: the sheet begin zone seems bad\n"));
1464 f << "###";
1465 break;
1466 }
1467 // time to update the style manager state
1468 m_styleManager->updateState();
1469
1470 val=int(libwps::readU8(input));
1471 if (val) f << "sheet[id]=" << val << ",";
1472 // then always 0a3fff00ffff508451ff ?
1473 isParsed=needWriteInAscii=true;
1474 break;
1475 case 0x7d7:
1476 isParsed=m_spreadsheetParser->readRowSizes(stream, endPos);
1477 break;
1478 case 0x7d8:
1479 case 0x7d9:
1480 {
1481 f.str("");
1482 int dataSz=type==0x7d8 ? 1 : 2;
1483 if (type==0x7d8)
1484 f << "Entries(ColMacBreak):";
1485 else
1486 f << "Entries(RowMacBreak):";
1487
1488 if (sz<4 || (sz%dataSz))
1489 {
1490 WPS_DEBUG_MSG(("LotusParser::readDataZone: the page mac break seems bad\n"));
1491 f << "###";
1492 break;
1493 }
1494 val=int(libwps::readU8(input));
1495 if (val) f << "sheet[id]=" << val << ",";
1496 val=int(libwps::readU8(input)); // always 0
1497 if (val) f << "f0=" << val << ",";
1498 f << "break=[";
1499 auto N=int((sz-2)/dataSz);
1500 for (int i=0; i<N; ++i)
1501 {
1502 if (dataSz==1)
1503 f << int(libwps::readU8(input)) << ",";
1504 else
1505 f << libwps::readU16(input) << ",";
1506 }
1507 f << "],";
1508 isParsed=needWriteInAscii=true;
1509 break;
1510 }
1511
1512 //
1513 // selection
1514 //
1515 case 0xbb8:
1516 f.str("");
1517 f << "Entries(MacSelect):";
1518
1519 if (sz!=18)
1520 {
1521 WPS_DEBUG_MSG(("LotusParser::readDataZone: the mac selection seems bad\n"));
1522 f << "###";
1523 break;
1524 }
1525 for (int i=0; i<3; ++i) // f0=0, f1=f2=1
1526 {
1527 val=int(libwps::read16(input));
1528 if (val) f << "f" << i << "=" << val << ",";
1529 }
1530 for (int i=0; i<3; ++i)
1531 {
1532 auto row=int(libwps::readU16(input));
1533 auto sheet=int(libwps::readU8(input));
1534 auto col=int(libwps::readU8(input));
1535 f << "C" << col << "-" << row;
1536 if (sheet) f << "[" << sheet << "],";
1537 else f << ",";
1538 }
1539 isParsed=needWriteInAscii=true;
1540 break;
1541
1542 //
1543 // style
1544 //
1545 case 0xfa0: // wk3
1546 isParsed=m_styleManager->readFontStyleA0(stream, endPos);
1547 break;
1548 case 0xfa1: // wk6-wk9, with size 26
1549 f.str("");
1550 f << "Entries(FontStyle):";
1551 break;
1552 case 0xfaa: // 10Style
1553 case 0xfab:
1554 isParsed=m_styleManager->readLineStyle(stream, endPos, type==0xfaa ? 0 : 1);
1555 break;
1556 case 0xfb4: // 20 Style
1557 isParsed=m_styleManager->readColorStyle(stream, endPos);
1558 break;
1559 case 0xfbe: // 30Style
1560 isParsed=m_styleManager->readFormatStyle(stream, endPos);
1561 break;
1562 case 0xfc8: //
1563 isParsed=m_styleManager->readGraphicStyle(stream, endPos);
1564 break;
1565 case 0xfc9: // 40Style: lotus 123
1566 isParsed=m_styleManager->readGraphicStyleC9(stream, endPos);
1567 break;
1568 case 0xfd2: // 50Style
1569 isParsed=m_styleManager->readCellStyleD2(stream, endPos);
1570 break;
1571 case 0xfdc:
1572 isParsed=readMacFontName(stream, endPos);
1573 break;
1574 case 0xfe6: // wk5
1575 if (version()==3)
1576 isParsed=m_styleManager->readCellStyleE6(stream, endPos);
1577 else if (version()>3)
1578 isParsed=m_styleManager->readStyleE6(stream, endPos);
1579 break;
1580 case 0xff0: // wk5
1581 isParsed=m_styleManager->readFontStyleF0(stream, endPos);
1582 break;
1583
1584 // 0xfe6: X X CeId : 60Style
1585
1586 //
1587 // graphic
1588 //
1589
1590 case 0x2328: // begin(wk3mac)
1591 isParsed=m_graphParser->readZoneBegin(stream, endPos);
1592 break;
1593 case 0x2332: // line(wk3mac)
1594 case 0x2346: // rect, rectoval, rect(wk3mac)
1595 case 0x2350: // arc(wk3mac)
1596 case 0x2352: // rect shadow(wk3mac)
1597 case 0x23f0: // frame(wk3mac)
1598 isParsed=m_graphParser->readZoneData(stream, endPos, type);
1599 break;
1600 case 0x23fa: // textbox data(wk3mac)
1601 isParsed=m_graphParser->readTextBoxData(stream, endPos);
1602 break;
1603
1604 case 0x2710: // chart data(wk3mac)
1605 {
1606 int chartId=-1;
1607 isParsed=m_chartParser->readMacHeader(stream, endPos, chartId);
1608 if (isParsed && chartId>=0)
1609 m_graphParser->setChartId(chartId);
1610 break;
1611 }
1612 case 0x2774:
1613 isParsed=m_chartParser->readMacPlacement(stream, endPos);
1614 break;
1615 case 0x277e:
1616 isParsed=m_chartParser->readMacLegend(stream, endPos);
1617 break;
1618 case 0x2788:
1619 isParsed=m_chartParser->readMacPlotArea(stream, endPos);
1620 break;
1621 case 0x27d8:
1622 isParsed=m_chartParser->readMacAxis(stream, endPos);
1623 break;
1624 case 0x27e2:
1625 isParsed=m_chartParser->readMacSerie(stream, endPos);
1626 break;
1627 // case 0x27ec: always 030000
1628 case 0x2846: // only 3d
1629 isParsed=m_chartParser->readMacFloor(stream, endPos);
1630 break;
1631 case 0x2904: // only if manual
1632 isParsed=m_chartParser->readMacPosition(stream, endPos);
1633 break;
1634
1635 //
1636 // chart
1637 //
1638
1639 case 0x2a30: // plot area style?
1640 isParsed=m_chartParser->readPlotArea(stream, endPos);
1641 break;
1642 case 0x2a31: // serie style
1643 isParsed=m_chartParser->readSerie(stream, endPos);
1644 break;
1645 case 0x2a32: // serie name
1646 isParsed=m_chartParser->readSerieName(stream, endPos);
1647 break;
1648 case 0x2a33: // serie width
1649 isParsed=m_chartParser->readSerieWidth(stream, endPos);
1650 break;
1651 case 0x2a34: // serie of font, before 2a35
1652 isParsed=m_chartParser->readFontsStyle(stream, endPos);
1653 break;
1654 case 0x2a35: // list of frames styles
1655 isParsed=m_chartParser->readFramesStyle(stream, endPos);
1656 break;
1657 //
1658 // mac pict
1659 //
1660 case 0x240e:
1661 isParsed=m_graphParser->readPictureDefinition(stream, endPos);
1662 break;
1663 case 0x2410:
1664 isParsed=m_graphParser->readPictureData(stream, endPos);
1665 break;
1666
1667 //
1668 // mac printer
1669 //
1670 case 0x2af8:
1671 isParsed=readDocumentInfoMac(stream, endPos);
1672 break;
1673 case 0x2afa:
1674 f.str("");
1675 f << "Entries(PrinterMacUnkn1):";
1676 if (sz!=3)
1677 {
1678 WPS_DEBUG_MSG(("LotusParser::readDataZone: the printer unkn1 seems bad\n"));
1679 f << "###";
1680 break;
1681 }
1682 for (int i=0; i<3; ++i)
1683 {
1684 val=int(libwps::readU8(input));
1685 static int const expected[]= {0x1f, 0xe0, 0/*or 1*/};
1686 if (val!=expected[i])
1687 f << "f" << i << "=" << val << ",";
1688 }
1689 isParsed=needWriteInAscii=true;
1690 break;
1691 case 0x2afb:
1692 {
1693 f.str("");
1694 f << "Entries(PrinterMacName):";
1695 if (sz<3)
1696 {
1697 WPS_DEBUG_MSG(("LotusParser::readDataZone: the printername seems bad\n"));
1698 f << "###";
1699 break;
1700 }
1701 val=int(libwps::read16(input));
1702 if (val!=20) f << "f0=" << val << ",";
1703 std::string name("");
1704 for (long i=4; i<sz; ++i)
1705 {
1706 auto c=char(libwps::readU8(input));
1707 if (!c) break;
1708 name+=c;
1709 }
1710 f << name << ",";
1711 isParsed=needWriteInAscii=true;
1712 break;
1713 }
1714 case 0x2afc:
1715 f.str("");
1716 f << "Entries(PrintMacInfo):";
1717 if (sz<120)
1718 {
1719 WPS_DEBUG_MSG(("LotusParser::readDataZone: the printinfo seems bad\n"));
1720 f << "###";
1721 break;
1722 }
1723 isParsed=needWriteInAscii=true;
1724 break;
1725
1726 case 0x32e7:
1727 isParsed=m_styleManager->readMenuStyleE7(stream, endPos);
1728 break;
1729
1730 // 32e7: related to style ?
1731 case 0x36b0:
1732 isParsed=m_spreadsheetParser->readSheetName1B(stream, endPos);
1733 break;
1734
1735 //
1736 // 4268, 4269
1737 //
1738
1739 case 0x4a38:
1740 f.str("");
1741 f << "Entries(LinkUnkA):";
1742 break;
1743 case 0x4a39:
1744 f.str("");
1745 f << "Entries(LinkUnkB):";
1746 break;
1747 case 0x6590:
1748 isParsed=m_spreadsheetParser->readNote(stream, endPos);
1749 break;
1750
1751 default:
1752 break;
1753 }
1754 if (!isParsed || needWriteInAscii)
1755 {
1756 ascFile.addPos(pos);
1757 ascFile.addNote(f.str().c_str());
1758 }
1759 if (input->tell()!=endPos)
1760 ascFile.addDelimiter(input->tell(),'|');
1761 input->seek(endPos, librevenge::RVNG_SEEK_SET);
1762 return true;
1763 }
1764
readZoneV3(std::shared_ptr<WPSStream> stream)1765 bool LotusParser::readZoneV3(std::shared_ptr<WPSStream> stream)
1766 {
1767 if (!stream) return false;
1768 RVNGInputStreamPtr &input = stream->m_input;
1769 libwps::DebugFile &ascFile=stream->m_ascii;
1770 libwps::DebugStream f;
1771 long pos = input->tell();
1772 auto type = int(libwps::readU16(input));
1773 auto sz = long(libwps::readU16(input));
1774 long endPos=pos+4+sz;
1775 if (sz<0 || !stream->checkFilePosition(endPos))
1776 {
1777 input->seek(pos, librevenge::RVNG_SEEK_SET);
1778 return false;
1779 }
1780 f << "Entries(Data" << std::hex << type << std::dec << "N):";
1781 ascFile.addPos(pos);
1782 ascFile.addNote(f.str().c_str());
1783 if (input->tell()!=endPos && input->tell()!=pos)
1784 ascFile.addDelimiter(input->tell(),'|');
1785 input->seek(endPos, librevenge::RVNG_SEEK_SET);
1786 return true;
1787 }
1788
readZone1(std::shared_ptr<WPSStream> stream)1789 bool LotusParser::readZone1(std::shared_ptr<WPSStream> stream)
1790 {
1791 if (!stream) return false;
1792 RVNGInputStreamPtr &input = stream->m_input;
1793 libwps::DebugFile &ascFile=stream->m_ascii;
1794 libwps::DebugStream f;
1795 long pos = input->tell();
1796 auto id = int(libwps::readU8(input));
1797 if (libwps::readU8(input) != 1)
1798 {
1799 input->seek(pos, librevenge::RVNG_SEEK_SET);
1800 return false;
1801 }
1802 auto sz = long(libwps::readU16(input));
1803 long endPos=pos+4+sz;
1804 if (sz<0 || !stream->checkFilePosition(endPos))
1805 {
1806 input->seek(pos, librevenge::RVNG_SEEK_SET);
1807 return false;
1808 }
1809 f << "Entries(Zone1):";
1810 int val;
1811 bool isParsed=false;
1812 switch (id)
1813 {
1814 case 0: // list of ids all between 1-N
1815 case 3: // always follow an id zone, with some value between 0 and N
1816 case 0xb: // always follow a level[close] zone, with some value between 0 and N, meaning unsure...
1817 f << (id==0 ? "id=" : id==3 ? "parent[id]," : "parent2[id],");
1818 if (sz!=4)
1819 {
1820 WPS_DEBUG_MSG(("LotusParser::readZone1: the size seems bad for zone %d\n", id));
1821 f << "###";
1822 break;
1823 }
1824 val=int(libwps::readU32(input));
1825 if (val)
1826 {
1827 if (id==3)
1828 m_state->m_actualZoneParentId=val;
1829 f << "Z" << val << ",";
1830 }
1831 if (id==0) m_state->m_actualZoneId=val;
1832 break;
1833 case 4:
1834 f << "stack1[open],";
1835 if (sz!=4)
1836 {
1837 WPS_DEBUG_MSG(("LotusParser::readZone1: the size seems bad for zone 4\n"));
1838 f << "###";
1839 break;
1840 }
1841 // 01[XX][YY]00 where Y is a small even number
1842 m_state->m_zone1Stack.push_back(libwps::readU32(input));
1843 f << m_state->getZone1StackDebugName();
1844 break;
1845 case 5:
1846 f << "stack1[close],";
1847 if (sz!=4)
1848 {
1849 WPS_DEBUG_MSG(("LotusParser::readZone1: the size seems bad for zone 5\n"));
1850 f << "###";
1851 break;
1852 }
1853 else
1854 {
1855 unsigned long lVal=libwps::readU32(input);
1856 if (m_state->m_zone1Stack.empty() || m_state->m_zone1Stack.back()!=lVal)
1857 {
1858 WPS_DEBUG_MSG(("LotusParser::readZone1: the value seems bad for zone 5\n"));
1859 f << "###val=" << std::hex << lVal << ",";
1860 }
1861 if (!m_state->m_zone1Stack.empty()) m_state->m_zone1Stack.pop_back();
1862 f << m_state->getZone1StackDebugName();
1863 }
1864 break;
1865 // level 1=table, 2=col, 3=row
1866 case 0x6: // no data
1867 f << "level[open],";
1868 m_state->m_actualLevels.push_back(Vec2i(0,0));
1869 f << "[" << m_state->getLevelsDebugName() << "],";
1870 break;
1871 case 0x7: // no data
1872 f << "level[close]";
1873 if (m_state->m_actualLevels.empty())
1874 {
1875 WPS_DEBUG_MSG(("LotusParser::readZone1: the level seems bad\n"));
1876 f << "###";
1877 break;
1878 }
1879 else
1880 m_state->m_actualLevels.pop_back();
1881 f << "[" << m_state->getLevelsDebugName() << "],";
1882 break;
1883 case 0x9: // appear one time at the beginning of the file, pos~8
1884 f << "dimension,";
1885 if (sz!=20)
1886 {
1887 WPS_DEBUG_MSG(("LotusParser::readZone1: the size seems bad for zone 9\n"));
1888 f << "###";
1889 break;
1890 }
1891 int dim[4];
1892 for (int &i : dim) i=int(libwps::read32(input));
1893 f << "dim=" << WPSBox2i(Vec2i(dim[0],dim[1]), Vec2i(dim[2],dim[3])) << ",";
1894 for (int i=0; i<2; ++i) // always 1,0
1895 {
1896 val=int(libwps::readU16(input));
1897 if (val!=1-i) f << "f" << i << "=" << val << ",";
1898 }
1899 break;
1900 case 0xa: // appear one time at the beginning of the file, pos=3
1901 {
1902 f << "typea,";
1903 if (sz<24 || (sz%8)!=0)
1904 {
1905 WPS_DEBUG_MSG(("LotusParser::readZone1: the size seems bad for zone a\n"));
1906 f << "###";
1907 break;
1908 }
1909 for (int i=0; i<11; ++i) // f0=cd92|cebd|1cc8, f1=e|f
1910 {
1911 val=int(libwps::readU16(input));
1912 if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1913 }
1914 auto N=int(libwps::readU16(input));
1915 f << "N=" << N << ",";
1916 if (24+N*8!=sz)
1917 {
1918 WPS_DEBUG_MSG(("LotusParser::readZone1: the N value seems bad for zone a\n"));
1919 f << "###";
1920 break;
1921 }
1922 for (int i=0; i<N; ++i)
1923 {
1924 f << "unk" << i << "=[";
1925 for (int j=0; j<4; ++j) // f0=12|18|24|800, f1=15e|2913, f2=4|37|41
1926 {
1927 val=int(libwps::readU16(input));
1928 if (val) f << "f" << j << "=" << std::hex << val << std::dec << ",";
1929 }
1930 f << "],";
1931 }
1932 break;
1933 }
1934 case 0xc: // one by file, after a level[close], before a level[open]
1935 f << "typec,";
1936 if (sz!=4)
1937 {
1938 WPS_DEBUG_MSG(("LotusParser::readZone1: the size seems bad for zone c\n"));
1939 f << "###";
1940 break;
1941 }
1942 for (int i=0; i<2; ++i) // always 0
1943 {
1944 val=int(libwps::readU16(input));
1945 if (val) f << "f" << i << "=" << val << ",";
1946 }
1947 break;
1948 case 0xd: // after a line: the coordinate, after a texbox the text
1949 isParsed=m_graphParser->readGraphDataZone(stream,endPos);
1950 break;
1951
1952 case 0xe: // no data, one by file
1953 f << "styles[def]=begin,";
1954 break;
1955 case 0xf: // no data, one by file
1956 f << "styles[def]=end,";
1957 break;
1958 default:
1959 f << "type=" << std::hex << id << std::dec << ",";
1960 break;
1961 }
1962 if (!isParsed)
1963 {
1964 ascFile.addPos(pos);
1965 ascFile.addNote(f.str().c_str());
1966 }
1967 if (input->tell()!=endPos && input->tell()!=pos)
1968 ascFile.addDelimiter(input->tell(),'|');
1969 input->seek(endPos, librevenge::RVNG_SEEK_SET);
1970 return true;
1971 }
1972
readSheetZone(std::shared_ptr<WPSStream> stream)1973 bool LotusParser::readSheetZone(std::shared_ptr<WPSStream> stream)
1974 {
1975 if (!stream) return false;
1976 RVNGInputStreamPtr &input = stream->m_input;
1977 libwps::DebugFile &ascFile=stream->m_ascii;
1978 libwps::DebugStream f;
1979 long pos = input->tell();
1980 auto id = int(libwps::readU8(input));
1981 if (libwps::readU8(input) != 2)
1982 {
1983 input->seek(pos, librevenge::RVNG_SEEK_SET);
1984 return false;
1985 }
1986 auto sz = long(libwps::readU16(input));
1987 long endPos=pos+4+sz;
1988 if (sz<0 || !stream->checkFilePosition(endPos))
1989 {
1990 input->seek(pos, librevenge::RVNG_SEEK_SET);
1991 return false;
1992 }
1993 f << "Entries(SheetZone):";
1994 int val;
1995 switch (id)
1996 {
1997 case 0: // appear at the end of the file: pos~end-3
1998 f << "rList,";
1999 if (sz!=10)
2000 {
2001 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 200\n"));
2002 f << "###";
2003 break;
2004 }
2005 m_state->m_actualZoneParentId=0;
2006 f << "sheet[root]=Z" << int(libwps::readU32(input)) << ",";
2007 for (int i=0; i<3; ++i) // f1=57
2008 {
2009 val=int(libwps::readU16(input));
2010 if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
2011 }
2012 break;
2013 case 1: // root of all sheet node
2014 f << "root,";
2015 if (sz!=78)
2016 {
2017 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 201\n"));
2018 f << "###";
2019 break;
2020 }
2021 for (int i=0; i<10; ++i) // f0=5|7, f6:big number, f8:big number
2022 {
2023 val=int(libwps::readU16(input));
2024 if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
2025 }
2026 for (int i=0; i<24; ++i) // 0
2027 {
2028 val=int(libwps::readU16(input));
2029 if (val) f << "g" << i << "=" << val << ",";
2030 }
2031 for (int i=0; i<5; ++i)
2032 {
2033 val=int(libwps::readU16(input));
2034 int const expected[]= {0x4001,0x2003,0x100,0x64,0};
2035 if (val!=expected[i]) f << "h" << i << "=" << std::hex << val << std::dec << ",";
2036 }
2037 break;
2038 case 2: // appear after zone 0: pos~end-2
2039 {
2040 f << "list,";
2041 if (sz<16 || (sz%4)!=0)
2042 {
2043 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 202\n"));
2044 f << "###";
2045 break;
2046 }
2047 auto N=int(libwps::readU16(input));
2048 f << "N=" << N << ",";
2049 if (16+4*N!=sz)
2050 {
2051 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the N value seems bad for zone 202\n"));
2052 f << "###";
2053 break;
2054 }
2055 if (!m_state->m_sheetZoneIdList.empty())
2056 {
2057 WPS_DEBUG_MSG(("LotusParser::readSheetZone: oops the sheet list is not empty\n"));
2058 }
2059 f << "zones=[";
2060 for (int i=0; i<N; ++i)
2061 {
2062 m_state->m_sheetZoneIdList.push_back(int(libwps::readU32(input)));
2063 f << "Z" << m_state->m_sheetZoneIdList.back() << ",";
2064 }
2065 f << "],";
2066 for (int i=0; i<7; ++i) // f3=1-8
2067 {
2068 val=int(libwps::readU16(input));
2069 if (val) f << "f" << i << "=" << val << ",";
2070 }
2071 break;
2072 }
2073 case 4:
2074 {
2075 f << "name,";
2076 if (sz<14)
2077 {
2078 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 204\n"));
2079 f << "###";
2080 break;
2081 }
2082 for (int i=0; i<4; ++i) // f2=0|1
2083 {
2084 val=int(libwps::readU16(input));
2085 int const expected[]= {0x200,0x11a,0,0};
2086 if (val!=expected[i]) f << "f" << i << "=" << std::hex << val << std::dec << ",";
2087 }
2088 auto N=int(libwps::readU16(input));
2089 f << "N=" << N << ",";
2090 if (14+N!=sz)
2091 {
2092 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the N value seems bad for zone 204\n"));
2093 f << "###";
2094 break;
2095 }
2096 std::string text;
2097 for (int i=0; i<N; ++i) text+=char(libwps::readU8(input));
2098 f << text << ",";
2099 for (int i=0; i<2; ++i) // g0=5f|76
2100 {
2101 val=int(libwps::readU16(input));
2102 if (val) f << "g" << i << "=" << val << ",";
2103 }
2104 break;
2105 }
2106 case 5: // defined a child of a sheetNames
2107 f << "Data0,";
2108 if (sz!=4)
2109 {
2110 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 205\n"));
2111 f << "###";
2112 break;
2113 }
2114 val=int(libwps::readU32(input));
2115 if (!val) break;
2116 if (m_state->m_dataZoneIdToSheetZoneIdMap.find(val) !=
2117 m_state->m_dataZoneIdToSheetZoneIdMap.end())
2118 {
2119 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the zone %d has already a parent\n", m_state->m_actualZoneId));
2120 }
2121 else
2122 m_state->m_dataZoneIdToSheetZoneIdMap[val]=m_state->m_actualZoneId;
2123 f << "Z" << val << ",";
2124 break;
2125
2126 // two by files: first one a sheet sub zone, second close id
2127 case 0x82:
2128 case 0x83:
2129 case 0x84:
2130 case 0x93:
2131 case 0x94:
2132 case 0x95: // column definition
2133 case 0x96: // row definition
2134 {
2135 auto subZId=size_t(id&0x1F);
2136 f << "sheetC" << std::hex << subZId << std::dec << "[" << (m_state->m_sheetSubZoneOpened[subZId] ? "close" : "open") << "],";
2137 m_state->m_sheetSubZoneOpened[subZId]=!m_state->m_sheetSubZoneOpened[subZId];
2138 break;
2139 }
2140 case 0x80: // one by sheet, between 288 and 287
2141 f << "sheetB0,";
2142 if (sz!=8)
2143 {
2144 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 280\n"));
2145 f << "###";
2146 break;
2147 }
2148 for (int i=0; i<4; ++i) // 0|1
2149 {
2150 val=int(libwps::readU16(input));
2151 if (val) f << "f" << i << "=" << val << ",";
2152 }
2153 break;
2154 case 0x81: // one by sheet, in sheetData0
2155 f << "sheetB1,";
2156 if (sz!=44)
2157 {
2158 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 281\n"));
2159 f << "###";
2160 break;
2161 }
2162 val=int(libwps::readU16(input));
2163 if (val!=1) f << "f0=" << val << ",";
2164 f << "unkn=[";
2165 for (int i=0; i<4; ++i) // maybe some some id
2166 {
2167 val=int(libwps::readU16(input));
2168 if (val)
2169 f << val << ",";
2170 else
2171 f << "_,";
2172 }
2173 f << "],";
2174 for (int i=0; i<17; ++i) // g4=ff, g6=1fff|ffff, g8=1, g9=0|small number
2175 {
2176 val=int(libwps::read16(input));
2177 if (val) f << "g" << i << "=" << std::hex << val << std::dec << ",";
2178 }
2179 break;
2180 case 0x85: // in general, I found them in sheetData0+1 and sheetData+2 (with the same value)
2181 f << "data1,";
2182 if (sz!=4)
2183 {
2184 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 285\n"));
2185 f << "###";
2186 break;
2187 }
2188 f << "id=Z" << int(libwps::readU32(input)) << ",";
2189 break;
2190 case 0x86: // one by file in general after sheetC16
2191 f << "sheetB6,";
2192 if (sz!=8 && sz!=10)
2193 {
2194 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 286\n"));
2195 f << "###";
2196 break;
2197 }
2198 for (long i=0; i<sz/2; ++i) // 100,0,100,2000 in 123:v4.0 , 100,0,100,0,100:v4.5
2199 {
2200 val=int(libwps::readU16(input));
2201 if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
2202 }
2203 break;
2204 case 0x87: // one by sheet, after 280
2205 f << "sheetB7,";
2206 if (sz!=6)
2207 {
2208 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 287\n"));
2209 f << "###";
2210 break;
2211 }
2212 for (int i=0; i<3; ++i) // 1,1,0
2213 {
2214 val=int(libwps::readU16(input));
2215 if (val) f << "f" << i << "=" << val << ",";
2216 }
2217 break;
2218 case 0x88: // one by sheet, between 281 and 280
2219 f << "sheetB8,";
2220 if (sz!=4)
2221 {
2222 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 288\n"));
2223 f << "###";
2224 break;
2225 }
2226 val=int(libwps::readU32(input)); // always 1, maybe and id
2227 if (val!=1) f << "f0=" << val << ",";
2228 break;
2229 case 0x92: // in sheetC2. Checkme, zone with variable size
2230 f << "sheetB12,";
2231 if (sz<28)
2232 {
2233 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone 288\n"));
2234 f << "###";
2235 break;
2236 }
2237 for (int i=0; i<14; ++i) // f0=3600|438,f2=f0|c000|e040, f4=500[256]
2238 {
2239 val=int(libwps::readU16(input));
2240 int const expected[]= {0,0,0,0,0, 0x35d4,0,0x1003,0x2000,0, 0x60,0,0x60,0};
2241 if (val!=expected[i]) f << "f" << i << "=" << std::hex << val << std::dec << ",";
2242 }
2243 // after some 0 or 3ff0
2244 break;
2245 case 0x99: // in sheetC15, sheetC16
2246 case 0x9a: // in sheetC4, sheetC13, sheetC14
2247 f << "sheetB" << std::hex << (id-0x90) << std::dec << ",";
2248 if (sz!=10)
2249 {
2250 WPS_DEBUG_MSG(("LotusParser::readSheetZone: the size seems bad for zone %d\n", id));
2251 f << "###";
2252 break;
2253 }
2254 for (int i=0; i<5; ++i) // always 0
2255 {
2256 val=int(libwps::readU16(input));
2257 if (val) f << "f" << i << "=" << val << ",";
2258 }
2259 break;
2260 default:
2261 f << "type=" << std::hex << id << std::dec << ",";
2262 break;
2263 }
2264 ascFile.addPos(pos);
2265 ascFile.addNote(f.str().c_str());
2266 if (input->tell()!=endPos && input->tell()!=pos)
2267 ascFile.addDelimiter(input->tell(),'|');
2268 input->seek(endPos, librevenge::RVNG_SEEK_SET);
2269 return true;
2270 }
2271
readZone4(std::shared_ptr<WPSStream> stream)2272 bool LotusParser::readZone4(std::shared_ptr<WPSStream> stream)
2273 {
2274 if (!stream) return false;
2275 RVNGInputStreamPtr &input = stream->m_input;
2276 libwps::DebugFile &ascFile=stream->m_ascii;
2277 libwps::DebugStream f;
2278 long pos = input->tell();
2279 auto id = int(libwps::readU8(input));
2280 if (libwps::readU8(input)!=4)
2281 {
2282 input->seek(pos, librevenge::RVNG_SEEK_SET);
2283 return false;
2284 }
2285 auto sz = long(libwps::readU16(input));
2286 long endPos=pos+4+sz;
2287 if (sz<0 || !stream->checkFilePosition(endPos))
2288 {
2289 input->seek(pos, librevenge::RVNG_SEEK_SET);
2290 return false;
2291 }
2292 f << "Entries(Zone4):";
2293 int val;
2294 switch (id)
2295 {
2296 case 0: // one by sheet, page definition ?
2297 {
2298 f << "sheet[page,def],";
2299 if (sz<90)
2300 {
2301 WPS_DEBUG_MSG(("LotusParser::readZone4: the size seems bad for zone 0\n"));
2302 f << "###";
2303 break;
2304 }
2305 f << "dims=["; // margins + page dim?
2306 for (int i=0; i<6; ++i)
2307 {
2308 val=int(libwps::read32(input));
2309 if (val)
2310 f << val << ",";
2311 else
2312 f << "_,";
2313 }
2314 f << "],";
2315 f << "unkn=[";
2316 for (int i=0; i<9; ++i) // list of 0|2|5
2317 {
2318 val=int(libwps::read16(input));
2319 if (val)
2320 f << val << ",";
2321 else
2322 f << "_,";
2323 }
2324 f << "],";
2325 for (int i=0; i<3; ++i) // some zone
2326 {
2327 val=int(libwps::read32(input));
2328 if (val)
2329 f << "zone" << i << "=Z" << val << ",";
2330 }
2331 for (int i=0; i<12; ++i) // f2=0|8, f4=119|131|137, f5=0|7, f11=0|9
2332 {
2333 val=int(libwps::read16(input));
2334 int const expected[]= {0,0,0,0,0x131, 0,1,1,0x270f,1, 0x64,1};
2335 if (val!=expected[i])
2336 f << "f" << i << "=" << val << ",";
2337 }
2338 f << "fl=[";
2339 for (int i=0; i<10; ++i) // list of 0|1|3, often 1
2340 {
2341 val=int(libwps::readU8(input));
2342 if (val)
2343 f << val << ",";
2344 else
2345 f << "_,";
2346 }
2347 f << "],";
2348 if (sz<92) break;
2349 // unsure from here, often the main style name?
2350 std::string name;
2351 while (input->tell()<endPos)
2352 {
2353 auto c=char(libwps::readU8(input));
2354 if (!c) break;
2355 name+=c;
2356 }
2357 if (!name.empty()) f << name << ",";
2358 break;
2359 }
2360 case 1: // after zoneA0, in general 6 item f0=0-5, tab? or item style?
2361 f << "zoneA1,";
2362 if (sz!=7)
2363 {
2364 WPS_DEBUG_MSG(("LotusParser::readZone4: the size seems bad for zone 1\n"));
2365 f << "###";
2366 break;
2367 }
2368 f << "id=" << int(libwps::readU8(input)) << ",";
2369 for (int i=0; i<3; ++i) // 0
2370 {
2371 val=int(libwps::readU16(input));
2372 if (val) f << "f" << i << "=" << val << ",";
2373 }
2374 break;
2375 case 3: // in sheet definition, after the style
2376 {
2377 f << "footerDef,";
2378 if (sz<31)
2379 {
2380 WPS_DEBUG_MSG(("LotusParser::readZone4: the size seems bad for zone 3\n"));
2381 f << "###";
2382 break;
2383 }
2384 for (int i=0; i<13; ++i) // f4==5,f6=4,f7=76,f8=2,f9=2,f10=66,f11=1
2385 {
2386 val=int(libwps::readU16(input));
2387 if (val) f << "f" << i << "=" << val << ",";
2388 }
2389 val=int(libwps::readU8(input)); // 0
2390 if (val) f << "f13=" << val << ",";
2391 for (int s=0; s<2; ++s)
2392 {
2393 auto sSz=int(libwps::readU16(input));
2394 if (input->tell()+sSz+(s==0 ? 2 : 0)>endPos)
2395 {
2396 WPS_DEBUG_MSG(("LotusParser::readZone4: the size seems bad for zone 3\n"));
2397 f << "###";
2398 break;
2399 }
2400 std::string name;
2401 for (int i=0; i<sSz; ++i)
2402 {
2403 auto c=char(libwps::readU8(input));
2404 if (c) name+=c;
2405 else if (i+1!=sSz)
2406 {
2407 WPS_DEBUG_MSG(("LotusParser::readZone4: find odd char in zone 3\n"));
2408 f << "###";
2409 }
2410 }
2411 if (!name.empty()) f << "string" << s << "=" << name << ",";
2412 }
2413 break;
2414 }
2415 case 0x80: // rare, present in sheet definition
2416 f << "chartSheet,";
2417 if (sz!=4)
2418 {
2419 WPS_DEBUG_MSG(("LotusParser::readZone4: the size seems bad for zone 80\n"));
2420 f << "###";
2421 break;
2422 }
2423 f << "id=Z" << int(libwps::readU32(input)) << ",";
2424 break;
2425 case 0x81: // after 480. checkme chart series
2426 f << "chartSeries,";
2427 if (sz!=12)
2428 {
2429 WPS_DEBUG_MSG(("LotusParser::readZone4: the size seems bad for zone 81\n"));
2430 f << "###";
2431 break;
2432 }
2433 f << "unkn=[";
2434 for (int i=0; i<3; ++i)
2435 {
2436 val=int(libwps::readU32(input));
2437 if (val)
2438 f << val << ",";
2439 else
2440 f << "_,";
2441 }
2442 f << "],";
2443 break;
2444 default:
2445 f << "type=" << std::hex << id << std::dec << ",";
2446 break;
2447 }
2448 ascFile.addPos(pos);
2449 ascFile.addNote(f.str().c_str());
2450 if (input->tell()!=endPos && input->tell()!=pos)
2451 ascFile.addDelimiter(input->tell(),'|');
2452 input->seek(endPos, librevenge::RVNG_SEEK_SET);
2453 return true;
2454 }
2455
readChartZone(std::shared_ptr<WPSStream> stream)2456 bool LotusParser::readChartZone(std::shared_ptr<WPSStream> stream)
2457 {
2458 if (!stream) return false;
2459 RVNGInputStreamPtr &input = stream->m_input;
2460 libwps::DebugFile &ascFile=stream->m_ascii;
2461 libwps::DebugStream f;
2462 long pos = input->tell();
2463 auto id = int(libwps::readU8(input));
2464 if (libwps::readU8(input)!=5)
2465 {
2466 input->seek(pos, librevenge::RVNG_SEEK_SET);
2467 return false;
2468 }
2469 auto sz = long(libwps::readU16(input));
2470 long endPos=pos+4+sz;
2471 if (sz<0 || !stream->checkFilePosition(endPos))
2472 {
2473 input->seek(pos, librevenge::RVNG_SEEK_SET);
2474 return false;
2475 }
2476 f << "Entries(ChartZone):";
2477 int val;
2478 switch (id)
2479 {
2480 case 0:
2481 {
2482 f << "name,";
2483 if (sz<6)
2484 {
2485 WPS_DEBUG_MSG(("LotusParser::readChartZone: the size seems bad for zone 0\n"));
2486 f << "###";
2487 break;
2488 }
2489 for (int i=0; i<2; ++i) // always 0
2490 {
2491 val=int(libwps::readU16(input));
2492 if (val)
2493 f << "f" << i << "=" << val << ",";
2494 }
2495 auto sSz=int(libwps::readU16(input));
2496 if (6+sSz>sz)
2497 {
2498 WPS_DEBUG_MSG(("LotusParser::readChartZone: the size seems bad for zone 0\n"));
2499 f << "###";
2500 break;
2501 }
2502 std::string name;
2503 for (int i=0; i<sSz; ++i)
2504 {
2505 auto c=char(libwps::readU8(input));
2506 if (c) name+=c;
2507 else if (i+1!=sSz)
2508 {
2509 WPS_DEBUG_MSG(("LotusParser::readChartZone: find odd char in zone 0\n"));
2510 f << "###";
2511 }
2512 }
2513 if (!name.empty()) f << name << ",";
2514 break;
2515 }
2516 case 2:
2517 f << "series,";
2518 if (sz!=12)
2519 {
2520 WPS_DEBUG_MSG(("LotusParser::readChartZone: the size seems bad for zone 2\n"));
2521 f << "###";
2522 break;
2523 }
2524 f << "unkn=[";
2525 for (int i=0; i<3; ++i)
2526 {
2527 val=int(libwps::readU32(input));
2528 if (val)
2529 f << val << ",";
2530 else
2531 f << "_,";
2532 }
2533 f << "],";
2534 break;
2535 case 3: // last zone
2536 f << "end,";
2537 if (sz!=0)
2538 {
2539 WPS_DEBUG_MSG(("LotusParser::readChartZone: the size seems bad for zone 3\n"));
2540 f << "###";
2541 break;
2542 }
2543 break;
2544 default:
2545 f << "type=" << std::hex << id << std::dec << ",";
2546 break;
2547 }
2548 ascFile.addPos(pos);
2549 ascFile.addNote(f.str().c_str());
2550 if (input->tell()!=endPos && input->tell()!=pos)
2551 ascFile.addDelimiter(input->tell(),'|');
2552 input->seek(endPos, librevenge::RVNG_SEEK_SET);
2553 return true;
2554 }
2555
readRefZone(std::shared_ptr<WPSStream> stream)2556 bool LotusParser::readRefZone(std::shared_ptr<WPSStream> stream)
2557 {
2558 if (!stream) return false;
2559 RVNGInputStreamPtr &input = stream->m_input;
2560 libwps::DebugFile &ascFile=stream->m_ascii;
2561 libwps::DebugStream f;
2562 long pos = input->tell();
2563 auto id = int(libwps::readU8(input));
2564 if (libwps::readU8(input)!=6)
2565 {
2566 input->seek(pos, librevenge::RVNG_SEEK_SET);
2567 return false;
2568 }
2569 auto sz = long(libwps::readU16(input));
2570 long endPos=pos+4+sz;
2571 if (sz<0 || !stream->checkFilePosition(endPos))
2572 {
2573 input->seek(pos, librevenge::RVNG_SEEK_SET);
2574 return false;
2575 }
2576 f << "Entries(RefZone):";
2577 int val;
2578 switch (id)
2579 {
2580 case 0x40: // after 642
2581 f << "cells,";
2582 if (sz!=12)
2583 {
2584 WPS_DEBUG_MSG(("LotusParser::readRefZone: the size seems bad for zone 640\n"));
2585 f << "###";
2586 break;
2587 }
2588 for (int i=0; i<6; ++i) // C?R?S? <-> C?R?S?: checkme maybe this stores also the range
2589 {
2590 f << int(libwps::readU16(input));
2591 if (i==2) f << "<->";
2592 else if (i==5) f << ",";
2593 else f << ":";
2594 }
2595 break;
2596 case 0x42: // after 407
2597 f << "begin,";
2598 if (sz!=4)
2599 {
2600 WPS_DEBUG_MSG(("LotusParser::readRefZone: the size seems bad for zone 642\n"));
2601 f << "###";
2602 break;
2603 }
2604 val=int(libwps::readU32(input)); // always 3
2605 if (val!=3) f << "f0=" << val << ",";
2606 break;
2607 case 0x43: // find something similar to A:E7 for a cell or to B:H3..B:H80 for a cell list
2608 {
2609 f << "name,";
2610 if (sz<=0)
2611 {
2612 WPS_DEBUG_MSG(("LotusParser::readRefZone: the size seems bad for zone 643\n"));
2613 f << "###";
2614 break;
2615 }
2616 std::string name;
2617 for (long i=0; i<sz; ++i)
2618 {
2619 auto c=char(libwps::readU8(input));
2620 if (c) name+=c;
2621 else if (i+1!=sz)
2622 {
2623 WPS_DEBUG_MSG(("LotusParser::readRefZone: find odd char in zone 643\n"));
2624 f << "###";
2625 }
2626 }
2627 if (!name.empty()) f << name << ",";
2628 break;
2629 }
2630 default:
2631 f << "type=" << std::hex << id << std::dec << ",";
2632 break;
2633 }
2634 ascFile.addPos(pos);
2635 ascFile.addNote(f.str().c_str());
2636 if (input->tell()!=endPos && input->tell()!=pos)
2637 ascFile.addDelimiter(input->tell(),'|');
2638 input->seek(endPos, librevenge::RVNG_SEEK_SET);
2639 return true;
2640 }
2641
readZone7(std::shared_ptr<WPSStream> stream)2642 bool LotusParser::readZone7(std::shared_ptr<WPSStream> stream)
2643 {
2644 if (!stream) return false;
2645 RVNGInputStreamPtr &input = stream->m_input;
2646 libwps::DebugFile &ascFile=stream->m_ascii;
2647 libwps::DebugStream f;
2648 long pos = input->tell();
2649 auto id = int(libwps::readU8(input));
2650 if (libwps::readU8(input)!=7)
2651 {
2652 input->seek(pos, librevenge::RVNG_SEEK_SET);
2653 return false;
2654 }
2655 auto sz = long(libwps::readU16(input));
2656 long endPos=pos+4+sz;
2657 if (sz<0 || !stream->checkFilePosition(endPos))
2658 {
2659 input->seek(pos, librevenge::RVNG_SEEK_SET);
2660 return false;
2661 }
2662 f << "Entries(Zone7)[" << std::hex << id << std::dec << "]:";
2663
2664 // normally, 780, ..., 701, 702, ..., 703, ..., 704, ...
2665 // in 704: the cell style
2666 int val;
2667 switch (id)
2668 {
2669 case 1:
2670 // empty zone (or container of 702)
2671 if (sz!=28)
2672 {
2673 WPS_DEBUG_MSG(("LotusParser::readZone7: the size seems bad for zone 1\n"));
2674 f << "###";
2675 break;
2676 }
2677 for (int i=0; i<4; ++i)
2678 {
2679 val=int(libwps::readU16(input));
2680 if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
2681 }
2682 f << "mat=[";
2683 for (int i=0; i<4; ++i)
2684 {
2685 double res;
2686 bool isNan;
2687 long actPos=pos;
2688 if (libwps::readDouble4(input, res, isNan))
2689 f << res << ",";
2690 else
2691 {
2692 f << "###";
2693 input->seek(actPos+2, librevenge::RVNG_SEEK_SET);
2694 }
2695 }
2696 f << "],";
2697 for (int i=0; i<2; ++i)
2698 {
2699 val=int(libwps::readU16(input));
2700 if (val) f << "g" << i << "=" << std::hex << val << std::dec << ",";
2701 }
2702 break;
2703 case 2:
2704 // precedes the LotusbE
2705 if (sz!=8)
2706 {
2707 WPS_DEBUG_MSG(("LotusParser::readZone7: the size seems bad for zone 2\n"));
2708 f << "###";
2709 break;
2710 }
2711 for (int i=0; i<4; ++i) // always 0
2712 {
2713 val=int(libwps::readU16(input));
2714 if (val) f << "f" << i << "=" << val << ",";
2715 }
2716 break;
2717 case 3:
2718 // precedes LotuscE, Lotus1cE, the col size, the link, the sheet name, the cells content
2719 f << "content,";
2720 if (sz!=6)
2721 {
2722 WPS_DEBUG_MSG(("LotusParser::readZone7: the size seems bad for zone 3\n"));
2723 f << "###";
2724 break;
2725 }
2726 for (int i=0; i<3; ++i) // list of 0|1
2727 {
2728 val=int(libwps::readU16(input));
2729 if (val) f << "f" << i << "=" << val << ",";
2730 }
2731 break;
2732 case 4:
2733 // precedes the cell styles
2734 f << "styles,";
2735 if (sz!=0)
2736 {
2737 WPS_DEBUG_MSG(("LotusParser::readZone7: the size seems bad for zone 4\n"));
2738 f << "###";
2739 break;
2740 }
2741 break;
2742 case 0x80: // first zone, precedes Data105N,Data104N,Data100N,Lotus3E
2743 f << "first,";
2744 if (sz!=12)
2745 {
2746 WPS_DEBUG_MSG(("LotusParser::readZone7: the size seems bad for zone 80\n"));
2747 f << "###";
2748 break;
2749 }
2750 for (int i=0; i<6; ++i) // f0=6f|ef
2751 {
2752 val=int(libwps::readU16(input));
2753 int const expected[]= {0xef, 0, 7, 0, 0x5f, 0x57};
2754 if (val!=expected[i]) f << "f" << i << "=" << std::hex << val << std::dec << ",";
2755 }
2756 break;
2757 default:
2758 break;
2759 }
2760 ascFile.addPos(pos);
2761 ascFile.addNote(f.str().c_str());
2762 if (input->tell()!=endPos && input->tell()!=pos)
2763 ascFile.addDelimiter(input->tell(),'|');
2764 input->seek(endPos, librevenge::RVNG_SEEK_SET);
2765 return true;
2766 }
2767
readZone8(std::shared_ptr<WPSStream> stream)2768 bool LotusParser::readZone8(std::shared_ptr<WPSStream> stream)
2769 {
2770 if (!stream) return false;
2771 RVNGInputStreamPtr &input = stream->m_input;
2772 libwps::DebugFile &ascFile=stream->m_ascii;
2773 libwps::DebugStream f;
2774 long pos = input->tell();
2775 auto id = int(libwps::readU8(input));
2776 if (id==1)
2777 {
2778 input->seek(pos, librevenge::RVNG_SEEK_SET);
2779 WPSVec3i minC, maxC;
2780 m_state->getLevels(minC, maxC);
2781 return m_spreadsheetParser->readCellsFormat801
2782 (stream, minC, maxC, m_state->m_sheetSubZoneOpened[0x15] ? 0 :
2783 m_state->m_sheetSubZoneOpened[0x16] ? 1 : -1);
2784 }
2785 if (libwps::readU8(input)!=8)
2786 {
2787 input->seek(pos, librevenge::RVNG_SEEK_SET);
2788 return false;
2789 }
2790 auto sz = long(libwps::readU16(input));
2791 long endPos=pos+4+sz;
2792 if (sz<0 || !stream->checkFilePosition(endPos))
2793 {
2794 input->seek(pos, librevenge::RVNG_SEEK_SET);
2795 return false;
2796 }
2797 int const vers=version();
2798 f << "Entries(Zone8):";
2799 int val;
2800 switch (id)
2801 {
2802 case 0: // v4: sz2, v4.5: sz4
2803 {
2804 f << "level[select],";
2805 int const expectedSz=vers<=4 ? 2 : 4; // checkme: unsure about vers==5
2806 if (sz!=expectedSz)
2807 {
2808 WPS_DEBUG_MSG(("LotusParser::readZone8: the level size seems bad\n"));
2809 f << "###";
2810 break;
2811 }
2812 if (m_state->m_actualLevels.empty())
2813 {
2814 WPS_DEBUG_MSG(("LotusParser::readZone8: the level seems bad\n"));
2815 f << "###";
2816 break;
2817 }
2818 long count=int(sz>=4 ? libwps::readU32(input) : libwps::readU16(input));
2819 Vec2i &zone=m_state->m_actualLevels.back();
2820 if (int(zone[1]+count)<0)
2821 {
2822 WPS_DEBUG_MSG(("LotusParser::readZone8: arg the delta bad\n"));
2823 f << "###delta=" << count << ",";
2824 count=0;
2825 }
2826 zone[0] = zone[1];
2827 zone[1] += int(count);
2828 f << "pos=[" << m_state->getLevelsDebugName() << "],";
2829 break;
2830 }
2831 // 1 already done
2832 case 2: // very often 802 and 803 are close to each other (in the sheet's zone)
2833 case 3:
2834 if (id==2)
2835 f << "column[def],";
2836 else
2837 f << "zoneA" << id << ",";
2838 if (sz!=2)
2839 {
2840 WPS_DEBUG_MSG(("LotusParser::readZone8: the size seems bad for id=%d\n", id));
2841 f << "###";
2842 break;
2843 }
2844 val=int(libwps::readU16(input)); // 1|2
2845 if (val!=1) f << "f0=" << val << ",";
2846 break;
2847 case 4:
2848 {
2849 f << "zoneA4,";
2850 if (m_state->m_sheetSubZoneOpened[0x15]) f << "cols,";
2851 else if (m_state->m_sheetSubZoneOpened[0x16]) f << "rows,";
2852 if (sz<4)
2853 {
2854 WPS_DEBUG_MSG(("LotusParser::readZone8: the size seems bad for 804\n"));
2855 f << "###";
2856 break;
2857 }
2858 val=int(libwps::readU16(input));
2859 if (val!=3) f << "f0=" << val << ",";
2860 auto N=int(libwps::readU16(input));
2861 f << "N=" << N << ",";
2862 int const expectedSz=vers<=4 ? 2 : 4; // checkme: unsure about vers==5
2863 if (sz!=4+N*expectedSz)
2864 {
2865 WPS_DEBUG_MSG(("LotusParser::readZone8: the N value seems bad for 804\n"));
2866 f << "###";
2867 break;
2868 }
2869 f << "unk=[";
2870 for (int i=0; i<N; ++i)
2871 {
2872 f << int(libwps::readU8(input));
2873 f << ":" << int(libwps::readU8(input));
2874 if (expectedSz==4)
2875 {
2876 f << "<->" << int(libwps::readU8(input));
2877 f << ":" << int(libwps::readU8(input));
2878 }
2879 f << ",";
2880 }
2881 f << "],";
2882 break;
2883 }
2884 case 0x83: // often the last 80X's zone
2885 f << "zoneB3,";
2886 if (sz!=5)
2887 {
2888 WPS_DEBUG_MSG(("LotusParser::readZone8: the size seems bad for 883\n"));
2889 f << "###";
2890 break;
2891 }
2892 for (int i=0; i<5; ++i) // always 0
2893 {
2894 val=int(libwps::readU8(input));
2895 if (val) f << "f" << i << "=" << val << ",";
2896 }
2897 break;
2898 default:
2899 f << "type=" << std::hex << id << std::dec << ",";
2900 break;
2901 }
2902 ascFile.addPos(pos);
2903 ascFile.addNote(f.str().c_str());
2904 if (input->tell()!=endPos && input->tell()!=pos)
2905 ascFile.addDelimiter(input->tell(),'|');
2906 input->seek(endPos, librevenge::RVNG_SEEK_SET);
2907 return true;
2908 }
2909
readVersionZone(std::shared_ptr<WPSStream> stream)2910 bool LotusParser::readVersionZone(std::shared_ptr<WPSStream> stream)
2911 {
2912 if (!stream) return false;
2913 RVNGInputStreamPtr &input = stream->m_input;
2914 libwps::DebugFile &ascFile=stream->m_ascii;
2915 libwps::DebugStream f;
2916 long pos = input->tell();
2917 auto id = int(libwps::readU8(input));
2918 if (libwps::readU8(input)!=0xa)
2919 {
2920 input->seek(pos, librevenge::RVNG_SEEK_SET);
2921 return false;
2922 }
2923 auto sz = long(libwps::readU16(input));
2924 long endPos=pos+4+sz;
2925 if (sz<0 || !stream->checkFilePosition(endPos))
2926 {
2927 input->seek(pos, librevenge::RVNG_SEEK_SET);
2928 return false;
2929 }
2930 f << "Entries(VersionZone):";
2931 // TODO
2932 switch (id)
2933 {
2934 default:
2935 f << "type=" << std::hex << id << std::dec << ",";
2936 break;
2937 }
2938 ascFile.addPos(pos);
2939 ascFile.addNote(f.str().c_str());
2940 if (input->tell()!=endPos && input->tell()!=pos)
2941 ascFile.addDelimiter(input->tell(),'|');
2942 input->seek(endPos, librevenge::RVNG_SEEK_SET);
2943 return true;
2944 }
2945 ////////////////////////////////////////////////////////////
2946 // generic
2947 ////////////////////////////////////////////////////////////
readMacFontName(std::shared_ptr<WPSStream> stream,long endPos)2948 bool LotusParser::readMacFontName(std::shared_ptr<WPSStream> stream, long endPos)
2949 {
2950 if (!stream) return false;
2951 RVNGInputStreamPtr &input=stream->m_input;
2952 libwps::DebugFile &ascFile=stream->m_ascii;
2953 libwps::DebugStream f;
2954
2955 const int vers=version();
2956 long pos = input->tell();
2957 long sz=endPos-pos;
2958 f << "Entries(MacFontName):";
2959 if ((vers<=1 && sz<7) || (vers>1 && sz!=42))
2960 {
2961 WPS_DEBUG_MSG(("LotusParser::readMacFontName: the zone size seems bad\n"));
2962 f << "###";
2963 ascFile.addPos(pos-6);
2964 ascFile.addNote(f.str().c_str());
2965 return true;
2966 }
2967 if (vers<=1)
2968 {
2969 // seems only to exist in a lotus mac file, so revert the default encoding to MacRoman if undef
2970 if (m_state->m_fontType==libwps_tools_win::Font::UNKNOWN)
2971 m_state->m_fontType=libwps_tools_win::Font::MAC_ROMAN;
2972 m_state->m_isMacFile=true;
2973 auto id=int(libwps::readU16(input));
2974 f << "FN" << id << ",";
2975 auto val=int(libwps::readU16(input)); // always 0?
2976 if (val)
2977 f << "f0=" << val << ",";
2978 val=int(libwps::read16(input)); // find -1, 30 (Geneva), 60 (Helvetica)
2979 if (val)
2980 f << "f1=" << val << ",";
2981 librevenge::RVNGString name("");
2982 bool nameOk=true;
2983 for (long i=0; i<sz-6; ++i)
2984 {
2985 auto c=char(libwps::readU8(input));
2986 if (!c) break;
2987 if (nameOk && !(c==' ' || (c>='0'&&c<='9') || (c>='a'&&c<='z') || (c>='A'&&c<='Z')))
2988 {
2989 nameOk=false;
2990 WPS_DEBUG_MSG(("LotusParser::readMacFontName: find odd character in name\n"));
2991 f << "#";
2992 }
2993 name.append(c);
2994 }
2995 f << name.cstr() << ",";
2996 if (m_state->m_fontsMap.find(id)!=m_state->m_fontsMap.end())
2997 {
2998 WPS_DEBUG_MSG(("LotusParser::readMacFontName: a font with id=%d already exists\n", id));
2999 f << "###id,";
3000 }
3001 else if (nameOk && !name.empty())
3002 {
3003 auto encoding=name!="Symbol" ? libwps_tools_win::Font::MAC_ROMAN : libwps_tools_win::Font::MAC_SYMBOL;
3004 LotusParserInternal::Font font(encoding);
3005 font.m_name=name;
3006 m_state->m_fontsMap.insert(std::map<int, LotusParserInternal::Font>::value_type(id,font));
3007 }
3008 ascFile.addPos(pos-6);
3009 ascFile.addNote(f.str().c_str());
3010 return true;
3011 }
3012
3013 int id=0;
3014 for (int i=0; i<4; ++i)
3015 {
3016 auto val=int(libwps::readU8(input)); // 0|1
3017 if (i==1)
3018 {
3019 id=val;
3020 f << "FN" << id << ",";
3021 }
3022 else if (val)
3023 f << "fl" << i << "=" << val << ",";
3024 }
3025 for (int i=0; i<2; ++i) // f1=0|1288
3026 {
3027 auto val=int(libwps::read16(input));
3028 if (val)
3029 f << "f" << i << "=" << val << ",";
3030 }
3031 librevenge::RVNGString name("");
3032 bool nameOk=true;
3033 for (int i=0; i<8; ++i)
3034 {
3035 auto c=char(libwps::read8(input));
3036 if (!c) break;
3037 if (nameOk && !(c==' ' || (c>='0'&&c<='9') || (c>='a'&&c<='z') || (c>='A'&&c<='Z')))
3038 {
3039 nameOk=false;
3040 WPS_DEBUG_MSG(("LotusParser::readMacFontName: find odd character in name\n"));
3041 f << "#";
3042 }
3043 name.append(c);
3044 }
3045 f << name.cstr() << ",";
3046 if (m_state->m_fontsMap.find(id)!=m_state->m_fontsMap.end())
3047 {
3048 WPS_DEBUG_MSG(("LotusParser::readMacFontName: a font with id=%d already exists\n", id));
3049 f << "###id,";
3050 }
3051 else if (nameOk && !name.empty())
3052 {
3053 LotusParserInternal::Font font(getDefaultFontType());
3054 font.m_name=name;
3055 m_state->m_fontsMap.insert(std::map<int, LotusParserInternal::Font>::value_type(id,font));
3056 }
3057 input->seek(pos+16, librevenge::RVNG_SEEK_SET);
3058 if (input->tell()!=endPos)
3059 {
3060 ascFile.addDelimiter(input->tell(),'|');
3061 input->seek(endPos, librevenge::RVNG_SEEK_SET);
3062 }
3063 ascFile.addPos(pos-6);
3064 ascFile.addNote(f.str().c_str());
3065 return true;
3066 }
3067
readFMTStyleName(std::shared_ptr<WPSStream> stream)3068 bool LotusParser::readFMTStyleName(std::shared_ptr<WPSStream> stream)
3069 {
3070 RVNGInputStreamPtr &input = stream->m_input;
3071 libwps::DebugFile &ascFile=stream->m_ascii;
3072 libwps::DebugStream f;
3073
3074 long pos = input->tell();
3075 auto type = int(libwps::read16(input));
3076 if (type!=0xb6)
3077 {
3078 WPS_DEBUG_MSG(("LotusParser::readFMTStyleName: not a font name definition\n"));
3079 return false;
3080 }
3081 auto sz = long(libwps::readU16(input));
3082 long endPos=pos+4+sz;
3083 if (sz<8)
3084 {
3085 WPS_DEBUG_MSG(("LotusParser::readFMTStyleName: the zone size seems bad\n"));
3086 ascFile.addPos(pos);
3087 ascFile.addNote("Entries(FMTStyleName):###");
3088 return true;
3089 }
3090 f << "Entries(FMTStyleName):";
3091 f << "id=" << libwps::readU16(input) << ",";
3092 std::string name;
3093 for (int i=0; i<6; ++i)
3094 {
3095 auto c=char(libwps::readU8(input));
3096 if (c==0) break;
3097 name+= c;
3098 }
3099 f << "title=" << name << ",";
3100 input->seek(pos+12, librevenge::RVNG_SEEK_SET);
3101 name.clear();
3102 for (long i=0; i<sz-8; ++i) name+= char(libwps::readU8(input));
3103 f << name << ",";
3104 if (input->tell()!=endPos)
3105 {
3106 WPS_DEBUG_MSG(("LotusParser::readFMTStyleName: find extra data\n"));
3107 f << "###extra";
3108 input->seek(endPos, librevenge::RVNG_SEEK_SET);
3109 }
3110 ascFile.addPos(pos);
3111 ascFile.addNote(f.str().c_str());
3112 return true;
3113 }
3114
readLinkZone(std::shared_ptr<WPSStream> stream)3115 bool LotusParser::readLinkZone(std::shared_ptr<WPSStream> stream)
3116 {
3117 RVNGInputStreamPtr &input=stream->m_input;
3118 libwps::DebugFile &ascFile=stream->m_ascii;
3119 libwps::DebugStream f;
3120
3121 long pos = input->tell();
3122 auto type = int(libwps::read16(input));
3123 if (type!=0xa)
3124 {
3125 WPS_DEBUG_MSG(("LotusParser::readLinkZone: not a link definition\n"));
3126 return false;
3127 }
3128 auto sz = long(libwps::readU16(input));
3129 f << "Entries(Link):";
3130 if (sz < 19)
3131 {
3132 WPS_DEBUG_MSG(("LotusParser::readLinkZone: the zone is too short\n"));
3133 f << "###";
3134 ascFile.addPos(pos);
3135 ascFile.addNote(f.str().c_str());
3136 return true;
3137 }
3138 type=int(libwps::read16(input));
3139 if (type==0) // fixme: find if this is a note, so that we can retrieve it
3140 f << "chart/note/...,";
3141 else if (type==1)
3142 f << "file,";
3143 else
3144 {
3145 WPS_DEBUG_MSG(("LotusParser::readLinkZone: find unknown type\n"));
3146 f << "##type=" << type << ",";
3147 ascFile.addPos(pos);
3148 ascFile.addNote(f.str().c_str());
3149 return true;
3150 }
3151 f << "ID=" << int(libwps::readU8(input)) << ","; // 0,19,42,53,ff
3152 auto id = int(libwps::readU8(input));
3153 f << "id=" << id << ",";
3154
3155 Link link;
3156 // C0: current selection
3157 // ----- chart ----:
3158 // G[23-28] color series 0->5
3159 // G[2a-2f] hatch series 0->5
3160 // G[39-3e]: data series 0, 1, ...
3161 // G[3f]: chart axis 0
3162 // G[40-45]: legend serie 0->5
3163 // G[47][22,27,2c,31,36,3b,40,45,4a,4f,54,59,5e]: data serie 6-18 (+1 label)
3164 // G[48][23,28,2d,32]: serie 19-22 (+1 label)
3165
3166 // G[4c-4e]: unit axis x,y,ysecond
3167 // G[4f-51]: label axis x,y,ysecond
3168 // G[52-55]: title, subtile, note1, note2
3169 // ----- unknown -----:
3170 // P3: can contains often a basic name or cells zone
3171 // Q[0-2]: contains often <<XXX>>YYY: link to another sheetname?
3172 for (int i=0; i<14; ++i)
3173 {
3174 auto c=char(libwps::readU8(input));
3175 if (!c) break;
3176 link.m_name += c;
3177 }
3178 f << "\"" << link.m_name << "\",";
3179 input->seek(pos+4+18, librevenge::RVNG_SEEK_SET);
3180 bool ok=true;
3181 switch (type)
3182 {
3183 case 0:
3184 if (sz<26)
3185 {
3186 WPS_DEBUG_MSG(("LotusParser::readLinkZone: the chart zone seems too short\n"));
3187 f << "###";
3188 break;
3189 }
3190 for (int i=0; i<2; ++i)
3191 {
3192 auto row=int(libwps::readU16(input));
3193 auto table=int(libwps::readU8(input));
3194 auto col=int(libwps::readU8(input));
3195 link.m_cells[i]=WPSVec3i(col,row,table);
3196 f << "C" << col << "-" << row;
3197 if (table) f << "[" << table << "]";
3198 if (i==0)
3199 f << "<->";
3200 else
3201 f << ",";
3202 }
3203 break;
3204 case 1:
3205 {
3206 if (sz>18)
3207 link.m_linkName=libwps_tools_win::Font::unicodeString(input.get(), static_cast<unsigned long>(sz-18), getDefaultFontType());
3208 f << "link=" << link.m_linkName.cstr() << ",";
3209 break;
3210 }
3211 default:
3212 ok=false;
3213 WPS_DEBUG_MSG(("LotusParser::readLinkZone: find unknown type\n"));
3214 f << "###";
3215 break;
3216 }
3217 if (ok) m_state->m_linkIdToLinkMap.insert(std::multimap<int,Link>::value_type(id, link));
3218 if (input->tell()!=pos+4+sz && input->tell()+1!=pos+4+sz)
3219 {
3220 WPS_DEBUG_MSG(("LotusParser::readLinkZone: the zone seems too short\n"));
3221 f << "##";
3222 ascFile.addDelimiter(input->tell(), '|');
3223 }
3224 ascFile.addPos(pos);
3225 ascFile.addNote(f.str().c_str());
3226 return true;
3227 }
3228
3229 // ----------------------------------------------------------------------
3230 // Header/Footer/PageDim
3231 // ----------------------------------------------------------------------
readDocumentInfoMac(std::shared_ptr<WPSStream> stream,long endPos)3232 bool LotusParser::readDocumentInfoMac(std::shared_ptr<WPSStream> stream, long endPos)
3233 {
3234 RVNGInputStreamPtr &input=stream->m_input;
3235 libwps::DebugFile &ascFile=stream->m_ascii;
3236 libwps::DebugStream f;
3237
3238 long pos = input->tell();
3239 f << "Entries(DocMacInfo):";
3240 if (endPos-pos!=51)
3241 {
3242 WPS_DEBUG_MSG(("LotusParser::readDocumentInfoMac: the zone size seems bad\n"));
3243 f << "###";
3244 ascFile.addPos(pos-6);
3245 ascFile.addNote(f.str().c_str());
3246 return true;
3247 }
3248 int dim[7];
3249 for (int i=0; i<7; ++i)
3250 {
3251 auto val=int(libwps::read8(input));
3252 if (i==0)
3253 f << "dim[unkn]=";
3254 else if (i==1)
3255 f << "margins=[";
3256 else if (i==5)
3257 f << "pagesize=[";
3258 dim[i]=int(libwps::read16(input));
3259 f << dim[i];
3260 if (val) f << "[" << val << "]";
3261 val=int(libwps::read8(input)); // always 0
3262 if (val) f << "[" << val << "]";
3263 f << ",";
3264 if (i==4 || i==6) f << "],";
3265 }
3266 // check order
3267 if (dim[5]>dim[1]+dim[3] && dim[6]>dim[2]+dim[4])
3268 {
3269 m_state->m_pageSpan.setFormWidth(dim[5]);
3270 m_state->m_pageSpan.setFormLength(dim[6]);
3271 m_state->m_pageSpan.setMarginLeft(dim[1]);
3272 m_state->m_pageSpan.setMarginTop(dim[2]);
3273 m_state->m_pageSpan.setMarginRight(dim[3]);
3274 m_state->m_pageSpan.setMarginBottom(dim[4]);
3275 }
3276 else
3277 f << "###";
3278 f << "unkn=[";
3279 for (int i=0; i<5; ++i) // 1,1,1,100|inf,1
3280 {
3281 auto val=int(libwps::read16(input));
3282 if (val==9999)
3283 f << "inf,";
3284 else if (val)
3285 f << val << ",";
3286 else
3287 f << "_,";
3288 }
3289 f << "],";
3290 for (int i=0; i<13; ++i) // always 0?
3291 {
3292 auto val=int(libwps::read8(input));
3293 if (val)
3294 f << "g" << i << "=" << val << ",";
3295 }
3296 ascFile.addPos(pos-6);
3297 ascFile.addNote(f.str().c_str());
3298 return true;
3299 }
3300
3301 ////////////////////////////////////////////////////////////
3302 // decode
3303 ////////////////////////////////////////////////////////////
decodeStream(RVNGInputStreamPtr input,long endPos,std::vector<uint8_t> const & key)3304 RVNGInputStreamPtr LotusParser::decodeStream(RVNGInputStreamPtr input, long endPos, std::vector<uint8_t> const &key)
3305 {
3306 if (!input || key.size()!=16)
3307 {
3308 WPS_DEBUG_MSG(("LotusParser::decodeStream: the arguments seems bad\n"));
3309 return RVNGInputStreamPtr();
3310 }
3311 long actPos=input->tell();
3312 input->seek(0,librevenge::RVNG_SEEK_SET);
3313 librevenge::RVNGBinaryData data;
3314 if (!libwps::readData(input, static_cast<unsigned long>(endPos), data) || long(data.size())!=endPos || !data.getDataBuffer())
3315 {
3316 WPS_DEBUG_MSG(("LotusParser::decodeStream: can not read the original input\n"));
3317 return RVNGInputStreamPtr();
3318 }
3319 auto *buf=const_cast<unsigned char *>(data.getDataBuffer());
3320 input->seek(actPos,librevenge::RVNG_SEEK_SET);
3321 uint8_t d7=0;
3322 bool transform=true;
3323 while (!input->isEnd())
3324 {
3325 long pos=input->tell();
3326 if (pos+4>endPos) break;
3327 auto type=int(libwps::readU16(input));
3328 auto sSz=int(libwps::readU16(input));
3329 if (pos+4+sSz>endPos)
3330 {
3331 input->seek(pos,librevenge::RVNG_SEEK_SET);
3332 break;
3333 }
3334 // Special case :
3335 // 123 files:
3336 // - the style zone (between 0x10e and 0x10f) is not transformed
3337 // - the stack1[open|close] field are not transformed
3338 if (type==0x10e)
3339 transform=false;
3340 else if (type==0x10f)
3341 transform=true;
3342 if (type==0x104 || type==0x105 || !transform)
3343 {
3344 input->seek(pos+4+sSz,librevenge::RVNG_SEEK_SET);
3345 continue;
3346 }
3347 auto d4=uint8_t(sSz);
3348 uint8_t d5=key[13];
3349 for (int i=0; i<sSz; ++i)
3350 {
3351 auto c=uint8_t(libwps::readU8(input));
3352 buf[pos+4+i]=(c^key[d7&0xf]);
3353 d7=uint8_t(c+d4);
3354 d4=uint8_t(d4+d5++);
3355 }
3356 }
3357 if (input->tell()!=endPos)
3358 {
3359 WPS_DEBUG_MSG(("LotusParser::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)));
3360 }
3361 RVNGInputStreamPtr res(new WPSStringStream(data.getDataBuffer(), static_cast<unsigned int>(endPos)));
3362 res->seek(actPos, librevenge::RVNG_SEEK_SET);
3363 return res;
3364 }
3365
retrievePasswordKeys(std::vector<uint8_t> const & fileKeys)3366 std::vector<uint8_t> LotusParser::retrievePasswordKeys(std::vector<uint8_t> const &fileKeys)
3367 {
3368 /* let try to detect short password (|password|<=14) by using the
3369 fact that fileKeys differ from the keys in two positions.
3370
3371 If the password length is less or equal to 12:
3372 Using fileKeys[12] and fileKeys[14], we can "retrieve"
3373 the password length. Then knowing this length, fileKeys[14]
3374 and fileKeys[15] give us the key. Finally, we can retrieve the
3375 password and check if it gives us again fileKeys.
3376
3377 We can also test password with length 13 or 14 similarly.
3378
3379 Note: if |password|>14, we can detect it by testing 256*256 posibilities, but :-~
3380 */
3381 std::vector<uint8_t> res;
3382 if (fileKeys.size()!=16)
3383 {
3384 WPS_DEBUG_MSG(("LotusParser::retrievePasswordKeys: the file keys seems bad\n"));
3385 return res;
3386 }
3387 static uint8_t const defValues[]=
3388 {
3389 0xb9,0x5f, 0xd7,0x31, 0xdb,0x75, 9,0x72,
3390 0x5d,0x85, 0x32,0x11, 0x5,0x11, 0x58,0
3391 };
3392 std::map<uint8_t,size_t> diffToPosMap;
3393 for (size_t i=0; i<14; ++i)
3394 diffToPosMap[defValues[i+2]^defValues[i]]=i;
3395 uint8_t diff12=fileKeys[12]^fileKeys[14];
3396 std::vector<size_t> posToTest;
3397 if (diffToPosMap.find(diff12)!=diffToPosMap.end() && diffToPosMap.find(diff12)->second+2<14)
3398 {
3399 posToTest.push_back(diffToPosMap.find(diff12)->second+2);
3400 // defValues[0]^defValues[2]=defValues[1]^defValues[3]=0x6e => we must add by hand this position
3401 if (diff12==0x6e)
3402 posToTest.push_back(2);
3403 }
3404 // check also password with length 13 or 14
3405 posToTest.push_back(0);
3406 posToTest.push_back(1);
3407 for (size_t actPos : posToTest)
3408 {
3409 auto key=uint16_t(((fileKeys[14]^defValues[actPos])<<8)|(fileKeys[15]^defValues[actPos+1]));
3410 res=fileKeys;
3411 res[7]=uint8_t(res[7]^key);
3412 res[13]=uint8_t(res[13]^(key>>8));
3413 // now build the password
3414 std::string password;
3415 for (size_t i=0; i<size_t(16-actPos-2); ++i)
3416 password+=char(res[i]^(key>>((i%2)==0 ? 8 : 0)));
3417 // check if the password is correct
3418 uint16_t resKey;
3419 std::vector<uint8_t> resKeys;
3420 if (libwps::encodeLotusPassword(password.c_str(), resKey, resKeys, defValues) && key==resKey && res==resKeys)
3421 {
3422 WPS_DEBUG_MSG(("LotusParser::retrievePasswordKeys: Find password %s\n", password.c_str()));
3423 return res;
3424 }
3425 }
3426 return std::vector<uint8_t>();
3427 }
3428
3429 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
3430