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