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