1 /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2 
3 /* libstaroffice
4 * Version: MPL 2.0 / LGPLv2+
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 2.0 (the "License"); you may not use this file except in compliance with
8 * the License or as specified alternatively below. You may obtain a copy of
9 * the License at http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Major Contributor(s):
17 * Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18 * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20 * Copyright (C) 2006, 2007 Andrew Ziem
21 * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22 *
23 *
24 * All Rights Reserved.
25 *
26 * For minor contributions see the git repository.
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30 * in which case the provisions of the LGPLv2+ are applicable
31 * instead of those above.
32 */
33 
34 #include <string.h>
35 
36 #include <limits>
37 #include <cmath>
38 #include <cstring>
39 
40 #include <librevenge-stream/librevenge-stream.h>
41 #include <librevenge/librevenge.h>
42 
43 #include "STOFFDebug.hxx"
44 
45 #include "STOFFInputStream.hxx"
46 
STOFFInputStream(std::shared_ptr<librevenge::RVNGInputStream> inp,bool inverted)47 STOFFInputStream::STOFFInputStream(std::shared_ptr<librevenge::RVNGInputStream> inp, bool inverted)
48   : m_stream(inp)
49   , m_streamSize(0)
50   , m_inverseRead(inverted)
51 {
52   updateStreamSize();
53 }
54 
STOFFInputStream(librevenge::RVNGInputStream * inp,bool inverted)55 STOFFInputStream::STOFFInputStream(librevenge::RVNGInputStream *inp, bool inverted)
56   : m_stream()
57   , m_streamSize(0)
58   , m_inverseRead(inverted)
59 {
60   if (!inp) return;
61 
62   m_stream = std::shared_ptr<librevenge::RVNGInputStream>(inp, STOFF_shared_ptr_noop_deleter<librevenge::RVNGInputStream>());
63   updateStreamSize();
64   if (m_stream)
65     seek(0, librevenge::RVNG_SEEK_SET);
66 }
67 
~STOFFInputStream()68 STOFFInputStream::~STOFFInputStream()
69 {
70 }
71 
get(librevenge::RVNGBinaryData const & data,bool inverted)72 std::shared_ptr<STOFFInputStream> STOFFInputStream::get(librevenge::RVNGBinaryData const &data, bool inverted)
73 {
74   std::shared_ptr<STOFFInputStream> res;
75   if (!data.size())
76     return res;
77   auto *dataStream = const_cast<librevenge::RVNGInputStream *>(data.getDataStream());
78   if (!dataStream) {
79     STOFF_DEBUG_MSG(("STOFFInputStream::get: can not retrieve a librevenge::RVNGInputStream\n"));
80     return res;
81   }
82   res.reset(new STOFFInputStream(dataStream, inverted));
83   if (res && res->size()>=long(data.size())) {
84     res->seek(0, librevenge::RVNG_SEEK_SET);
85     return res;
86   }
87   STOFF_DEBUG_MSG(("STOFFInputStream::get: the final stream seems bad\n"));
88   res.reset();
89   return res;
90 }
91 
updateStreamSize()92 void STOFFInputStream::updateStreamSize()
93 {
94   if (!m_stream)
95     m_streamSize=0;
96   else {
97     long actPos = tell();
98     m_stream->seek(0, librevenge::RVNG_SEEK_END);
99     m_streamSize=tell();
100     m_stream->seek(actPos, librevenge::RVNG_SEEK_SET);
101   }
102 }
103 
read(size_t numBytes,unsigned long & numBytesRead)104 const uint8_t *STOFFInputStream::read(size_t numBytes, unsigned long &numBytesRead)
105 {
106   if (!hasDataFork())
107     throw libstoff::FileException();
108   return m_stream->read(numBytes,numBytesRead);
109 }
110 
tell()111 long STOFFInputStream::tell()
112 {
113   if (!hasDataFork())
114     return 0;
115   return m_stream->tell();
116 }
117 
seek(long offset,librevenge::RVNG_SEEK_TYPE seekType)118 int STOFFInputStream::seek(long offset, librevenge::RVNG_SEEK_TYPE seekType)
119 {
120   if (!hasDataFork()) {
121     if (offset == 0)
122       return 0;
123     throw libstoff::FileException();
124   }
125 
126   if (seekType == librevenge::RVNG_SEEK_CUR)
127     offset += tell();
128   else if (seekType==librevenge::RVNG_SEEK_END)
129     offset += m_streamSize;
130 
131   if (offset < 0)
132     offset = 0;
133   if (offset > size())
134     offset = size();
135 
136   return m_stream->seek(offset, librevenge::RVNG_SEEK_SET);
137 }
138 
isEnd()139 bool STOFFInputStream::isEnd()
140 {
141   if (!hasDataFork())
142     return true;
143   long pos = m_stream->tell();
144   if (pos >= size()) return true;
145 
146   return m_stream->isEnd();
147 }
148 
readULong(librevenge::RVNGInputStream * stream,int num,unsigned long a,bool inverseRead)149 unsigned long STOFFInputStream::readULong(librevenge::RVNGInputStream *stream, int num, unsigned long a, bool inverseRead)
150 {
151   if (!stream || num == 0 || stream->isEnd()) return a;
152   if (inverseRead) {
153     unsigned long val = readU8(stream);
154     return val + (readULong(stream, num-1,0, inverseRead) << 8);
155   }
156   switch (num) {
157   case 4:
158   case 2:
159   case 1: {
160     unsigned long numBytesRead;
161     uint8_t const *p = stream->read(static_cast<unsigned long>(num), numBytesRead);
162     if (!p || int(numBytesRead) != num)
163       return 0;
164     switch (num) {
165     case 4:
166       return static_cast<unsigned long>(p[3])|(static_cast<unsigned long>(p[2])<<8)|
167              (static_cast<unsigned long>(p[1])<<16)|(static_cast<unsigned long>(p[0])<<24)|((a<<16)<<16);
168     case 2:
169       return (static_cast<unsigned long>(p[1]))|(static_cast<unsigned long>(p[0])<<8)|(a<<16);
170     case 1:
171       return (static_cast<unsigned long>(p[0]))|(a<<8);
172     default:
173       break;
174     }
175     break;
176   }
177   default:
178     return readULong(stream, num-1,(a<<8) + static_cast<unsigned long>(readU8(stream)), inverseRead);
179   }
180   return 0;
181 }
182 
readLong(int num)183 long STOFFInputStream::readLong(int num)
184 {
185   auto v = long(readULong(num));
186   switch (num) {
187   case 4:
188     return static_cast<int32_t>(v);
189   case 2:
190     return static_cast<int16_t>(v);
191   case 1:
192     return static_cast<int8_t>(v);
193   default:
194     break;
195   }
196 
197   if ((v & long(0x1 << (num*8-1))) == 0) return v;
198   return v | long(0xFFFFFFFF << 8*num);
199 }
200 
readU8(librevenge::RVNGInputStream * stream)201 uint8_t STOFFInputStream::readU8(librevenge::RVNGInputStream *stream)
202 {
203   if (!stream)
204     return 0;
205   unsigned long numBytesRead;
206   uint8_t const *p = stream->read(sizeof(uint8_t), numBytesRead);
207 
208   if (!p || numBytesRead != sizeof(uint8_t))
209     return 0;
210 
211   return *reinterpret_cast<uint8_t const *>(p);
212 }
213 
peek()214 int STOFFInputStream::peek()
215 {
216   if (isEnd()) return -1;
217   auto res=int(readULong(1));
218   seek(-1, librevenge::RVNG_SEEK_CUR);
219   return res;
220 }
221 
readColor(STOFFColor & color)222 bool STOFFInputStream::readColor(STOFFColor &color)
223 {
224   if (!m_stream || !checkPosition(tell()+2)) return false;
225   auto colId=int(readULong(2));
226   if (colId & 0x8000) {
227     if (!checkPosition(tell()+6)) return false;
228     unsigned char col[3];
229     for (unsigned char &i : col) i=static_cast<unsigned char>(readULong(2)>>8);
230     color=STOFFColor(col[0],col[1],col[2]);
231     return true;
232   }
233   static uint32_t const listColors[] = {
234     0,                          // COL_BLACK
235     0x000080,                           // COL_BLUE
236     0x008000,                          // COL_GREEN
237     0x008080,                           // COL_CYAN
238     0x800000,                            // COL_RED
239     0x800080,                        // COL_MAGENTA
240     0x808000,                          // COL_BROWN
241     0x808080,                           // COL_GRAY
242     0xc0c0c0,                      // COL_LIGHTGRAY
243     0x0000ff,                      // COL_LIGHTBLUE
244     0x00ff00,                     // COL_LIGHTGREEN
245     0x00ffff,                      // COL_LIGHTCYAN
246     0xff0000,                       // COL_LIGHTRED
247     0xff00ff,                   // COL_LIGHTMAGENTA
248     0xffff00,                         // COL_YELLOW
249     0xffffff,                          // COL_WHITE
250     0xffffff,                          // COL_MENUBAR
251     0,                          // COL_MENUBARTEXT
252     0xffffff,                          // COL_POPUPMENU
253     0,                          // COL_POPUPMENUTEXT
254     0,                          // COL_WINDOWTEXT
255     0xffffff,                          // COL_WINDOWWORKSPACE
256     0,                          // COL_HIGHLIGHT
257     0xffffff,                          // COL_HIGHLIGHTTEXT
258     0,                          // COL_3DTEXT
259     0xc0c0c0,                      // COL_3DFACE
260     0xffffff,                          // COL_3DLIGHT
261     0x808080,                           // COL_3DSHADOW
262     0xc0c0c0,                      // COL_SCROLLBAR
263     0xffffff,                          // COL_FIELD
264     0                           // COL_FIELDTEXT
265   };
266   if (colId<0 || colId>=int(STOFF_N_ELEMENTS(listColors))) {
267     STOFF_DEBUG_MSG(("STOFFInputStream::readColor: can not find color %d\n", colId));
268     return false;
269   }
270   color=STOFFColor(listColors[colId]);
271   return true;
272 }
273 
readCompressedULong(unsigned long & res)274 bool STOFFInputStream::readCompressedULong(unsigned long &res)
275 {
276   // sw_sw3imp.cxx Sw3IoImp::InULong
277   if (!m_stream)
278     return false;
279 
280   unsigned long numBytesRead;
281   uint8_t const *p = m_stream->read(sizeof(uint8_t), numBytesRead);
282 
283   if (!p || numBytesRead != sizeof(uint8_t))
284     return false;
285   if ((p[0]&0x80)==0) {
286     res=p[0]&0x7f;
287     return true;
288   }
289   if ((p[0]&0xC0)==0x80) {
290     res=(p[0]&0x3f);
291     p = m_stream->read(sizeof(uint8_t), numBytesRead);
292     if (!p || numBytesRead != sizeof(uint8_t))
293       return false;
294     res=(res<<8)|p[0];
295     return true;
296   }
297   if ((p[0]&0xe0)==0xc0) {
298     res=p[0]&0x1f;
299     p = m_stream->read(2*sizeof(uint8_t), numBytesRead);
300 
301     if (!p || numBytesRead != 2*sizeof(uint8_t))
302       return false;
303     res= (res<<16)|static_cast<unsigned long>(p[1]<<8)|p[0];//checkme
304     return true;
305   }
306   if ((p[0]&0xf0)==0xe0) {
307     res=p[0]&0xf;
308     p = m_stream->read(3*sizeof(uint8_t), numBytesRead);
309 
310     if (!p || numBytesRead != 3*sizeof(uint8_t))
311       return false;
312     res=(res<<24)|static_cast<unsigned long>(p[0]<<16)|static_cast<unsigned long>(p[2]<<8)|p[1];//checkme
313     return true;
314   }
315   if ((p[0]&0xf8)==0xf0) {
316     res=readULong(4);
317     return true;
318   }
319   return false;
320 }
321 
readCompressedLong(long & res)322 bool STOFFInputStream::readCompressedLong(long &res)
323 {
324   if (!m_stream)
325     return false;
326 
327   unsigned long numBytesRead;
328   uint8_t const *p = m_stream->read(sizeof(uint8_t), numBytesRead);
329 
330   if (!p || numBytesRead != sizeof(uint8_t))
331     return false;
332   if (p[0]&0x80) {
333     res=p[0]&0x7f;
334     return true;
335   }
336   if (p[0]&0x40) {
337     res=p[0]&0x3f;
338     p = m_stream->read(sizeof(uint8_t), numBytesRead);
339 
340     if (!p || numBytesRead != sizeof(uint8_t))
341       return false;
342     res=(res<<8)|p[0];
343     return true;
344   }
345   else if (p[0]&0x20) {
346     res=p[0]&0x1f;
347     p = m_stream->read(3*sizeof(uint8_t), numBytesRead);
348 
349     if (!p || numBytesRead != 3*sizeof(uint8_t))
350       return false;
351     res=(res<<24)|(p[0]<<16)|(p[2]<<8)|p[1];//checkme
352     return true;
353   }
354   else if (p[0]==0x10) {
355     long pos=tell();
356     if (pos+4 > m_streamSize) return false;
357     res=long(readULong(4));
358     return true;
359   }
360   return false;
361 }
362 
readDouble8(double & res,bool & isNotANumber)363 bool STOFFInputStream::readDouble8(double &res, bool &isNotANumber)
364 {
365   if (!m_stream) return false;
366   long pos=tell();
367   if (pos+8 > m_streamSize) return false;
368 
369   isNotANumber=false;
370   res=0;
371   auto mantExp=int(readULong(1));
372   auto val=int(readULong(1));
373   int exp=(mantExp<<4)+(val>>4);
374   double mantisse=double(val&0xF)/16.;
375   double factor=1./16/256.;
376   for (int j = 0; j < 6; ++j, factor/=256)
377     mantisse+=double(readULong(1))*factor;
378   int sign = 1;
379   if (exp & 0x800) {
380     exp &= 0x7ff;
381     sign = -1;
382   }
383   if (exp == 0) {
384     if (mantisse <= 1.e-5 || mantisse >= 1-1.e-5)
385       return true;
386     // a Nan representation ?
387     return false;
388   }
389   if (exp == 0x7FF) {
390     if (mantisse >= 1.-1e-5) {
391       isNotANumber=true;
392       res=std::numeric_limits<double>::quiet_NaN();
393       return true; // ok 0x7FF and 0xFFF are nan
394     }
395     return false;
396   }
397   exp -= 0x3ff;
398   res = std::ldexp(1.+mantisse, exp);
399   if (sign == -1)
400     res *= -1.;
401   return true;
402 }
403 
readDouble10(double & res,bool & isNotANumber)404 bool STOFFInputStream::readDouble10(double &res, bool &isNotANumber)
405 {
406   if (!m_stream) return false;
407   long pos=tell();
408   if (pos+10 > m_streamSize) return false;
409 
410   auto exp = int(readULong(2));
411   int sign = 1;
412   if (exp & 0x8000) {
413     exp &= 0x7fff;
414     sign = -1;
415   }
416   exp -= 0x3fff;
417 
418   isNotANumber=false;
419   auto mantisse = static_cast<unsigned long>(readULong(4));
420   if ((mantisse & 0x80000001) == 0) {
421     // unormalized number are not frequent, but can appear at least for date, ...
422     if (readULong(4) != 0)
423       seek(-4, librevenge::RVNG_SEEK_CUR);
424     else {
425       if (exp == -0x3fff && mantisse == 0) {
426         res=0;
427         return true; // ok zero
428       }
429       if (exp == 0x4000 && (mantisse & 0xFFFFFFL)==0) { // ok Nan
430         isNotANumber = true;
431         res=std::numeric_limits<double>::quiet_NaN();
432         return true;
433       }
434       return false;
435     }
436   }
437   // or std::ldexp((total value)/double(0x80000000), exp);
438   res=std::ldexp(double(readULong(4)),exp-63)+std::ldexp(double(mantisse),exp-31);
439   if (sign == -1)
440     res *= -1.;
441   return true;
442 }
443 
readDoubleReverted8(double & res,bool & isNotANumber)444 bool STOFFInputStream::readDoubleReverted8(double &res, bool &isNotANumber)
445 {
446   if (!m_stream) return false;
447   long pos=tell();
448   if (pos+8 > m_streamSize) return false;
449 
450   isNotANumber=false;
451   res=0;
452   int bytes[6];
453   for (int &byte : bytes) byte=int(readULong(1));
454 
455   auto val=int(readULong(1));
456   auto mantExp=int(readULong(1));
457   int exp=(mantExp<<4)+(val>>4);
458   double mantisse=double(val&0xF)/16.;
459   double factor=1./16./256.;
460   for (int j = 0; j < 6; ++j, factor/=256)
461     mantisse+=double(bytes[5-j])*factor;
462   int sign = 1;
463   if (exp & 0x800) {
464     exp &= 0x7ff;
465     sign = -1;
466   }
467   if (exp == 0) {
468     if (mantisse <= 1.e-5 || mantisse >= 1-1.e-5)
469       return true;
470     // a Nan representation ?
471     return false;
472   }
473   if (exp == 0x7FF) {
474     if (mantisse >= 1.-1e-5) {
475       isNotANumber=true;
476       res=std::numeric_limits<double>::quiet_NaN();
477       return true; // ok 0x7FF and 0xFFF are nan
478     }
479     return false;
480   }
481   exp -= 0x3ff;
482   res = std::ldexp(1.+mantisse, exp);
483   if (sign == -1)
484     res *= -1.;
485   return true;
486 }
487 
488 ////////////////////////////////////////////////////////////
489 //
490 // OLE part
491 //
492 ////////////////////////////////////////////////////////////
493 
isStructured()494 bool STOFFInputStream::isStructured()
495 {
496   if (!m_stream) return false;
497   long pos=m_stream->tell();
498   bool ok=m_stream->isStructured();
499   m_stream->seek(pos, librevenge::RVNG_SEEK_SET);
500   return ok;
501 }
502 
subStreamCount()503 unsigned STOFFInputStream::subStreamCount()
504 {
505   if (!m_stream || !m_stream->isStructured()) {
506     STOFF_DEBUG_MSG(("STOFFInputStream::subStreamCount: called on unstructured file\n"));
507     return 0;
508   }
509   return m_stream->subStreamCount();
510 }
511 
subStreamName(unsigned id)512 std::string STOFFInputStream::subStreamName(unsigned id)
513 {
514   if (!m_stream || !m_stream->isStructured()) {
515     STOFF_DEBUG_MSG(("STOFFInputStream::subStreamName: called on unstructured file\n"));
516     return std::string("");
517   }
518   auto const *nm=m_stream->subStreamName(id);
519   if (!nm) {
520     STOFF_DEBUG_MSG(("STOFFInputStream::subStreamName: can not find stream %d\n", int(id)));
521     return std::string("");
522   }
523   return std::string(nm);
524 }
525 
getSubStreamByName(std::string const & name)526 std::shared_ptr<STOFFInputStream> STOFFInputStream::getSubStreamByName(std::string const &name)
527 {
528   std::shared_ptr<STOFFInputStream> empty;
529   if (!m_stream || !m_stream->isStructured() || name.empty()) {
530     STOFF_DEBUG_MSG(("STOFFInputStream::getSubStreamByName: called on unstructured file\n"));
531     return empty;
532   }
533 
534   long actPos = tell();
535   seek(0, librevenge::RVNG_SEEK_SET);
536   std::shared_ptr<librevenge::RVNGInputStream> res(m_stream->getSubStreamByName(name.c_str()));
537   seek(actPos, librevenge::RVNG_SEEK_SET);
538 
539   if (!res)
540     return empty;
541   std::shared_ptr<STOFFInputStream> inp(new STOFFInputStream(res,m_inverseRead));
542   inp->seek(0, librevenge::RVNG_SEEK_SET);
543   return inp;
544 }
545 
getSubStreamById(unsigned id)546 std::shared_ptr<STOFFInputStream> STOFFInputStream::getSubStreamById(unsigned id)
547 {
548   std::shared_ptr<STOFFInputStream> empty;
549   if (!m_stream || !m_stream->isStructured()) {
550     STOFF_DEBUG_MSG(("STOFFInputStream::getSubStreamById: called on unstructured file\n"));
551     return empty;
552   }
553 
554   long actPos = tell();
555   seek(0, librevenge::RVNG_SEEK_SET);
556   std::shared_ptr<librevenge::RVNGInputStream> res(m_stream->getSubStreamById(id));
557   seek(actPos, librevenge::RVNG_SEEK_SET);
558 
559   if (!res)
560     return empty;
561   std::shared_ptr<STOFFInputStream> inp(new STOFFInputStream(res,m_inverseRead));
562   inp->seek(0, librevenge::RVNG_SEEK_SET);
563   return inp;
564 }
565 
566 ////////////////////////////////////////////////////////////
567 //
568 //  a function to read a data block
569 //
570 ////////////////////////////////////////////////////////////
571 
readDataBlock(long sz,librevenge::RVNGBinaryData & data)572 bool STOFFInputStream::readDataBlock(long sz, librevenge::RVNGBinaryData &data)
573 {
574   if (!hasDataFork()) return false;
575 
576   data.clear();
577   if (sz < 0) return false;
578   if (sz == 0) return true;
579   long endPos=tell()+sz;
580   if (endPos > size()) return false;
581 
582   const unsigned char *readData;
583   unsigned long sizeRead;
584   if ((readData=m_stream->read(static_cast<unsigned long>(sz), sizeRead)) == nullptr || long(sizeRead)!=sz)
585     return false;
586   data.append(readData, sizeRead);
587   return true;
588 }
589 
readEndDataBlock(librevenge::RVNGBinaryData & data)590 bool STOFFInputStream::readEndDataBlock(librevenge::RVNGBinaryData &data)
591 {
592   data.clear();
593   if (!hasDataFork()) return false;
594 
595   return readDataBlock(size()-tell(), data);
596 }
597 
598 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
599