1 /* POLE - Portable C++ library to access OLE Storage
2 Copyright (C) 2002-2005 Ariya Hidayat <ariya@kde.org>
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions
6 are met:
7 * Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
12 * Neither the name of the authors nor the names of its contributors may be
13 used to endorse or promote products derived from this software without
14 specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 This file taken from libwpg WPGOLEStream.cpp 1.5 Thu Aug 17 21:21:30 2006
31 */
32
33 #include "WPGOLEStream.h"
34
35 #include <sstream>
36 #include <iostream>
37 #include <list>
38 #include <string>
39 #include <vector>
40
41 #include <string.h>
42
43 namespace libwpg
44 {
45
46 class Header
47 {
48 public:
49 unsigned char id[8]; // signature, or magic identifier
50 unsigned b_shift; // bbat->blockSize = 1 << b_shift
51 unsigned s_shift; // sbat->blockSize = 1 << s_shift
52 unsigned num_bat; // blocks allocated for big bat
53 unsigned dirent_start; // starting block for directory info
54 unsigned threshold; // switch from small to big file (usually 4K)
55 unsigned sbat_start; // starting block index to store small bat
56 unsigned num_sbat; // blocks allocated for small bat
57 unsigned mbat_start; // starting block to store meta bat
58 unsigned num_mbat; // blocks allocated for meta bat
59 unsigned long bb_blocks[109];
60
61 Header();
62 bool valid();
63 void load( const unsigned char* buffer );
64 void save( unsigned char* buffer );
65 void debug();
66 };
67
68 class AllocTable
69 {
70 public:
71 static const unsigned Eof;
72 static const unsigned Avail;
73 static const unsigned Bat;
74 static const unsigned MetaBat;
75 unsigned blockSize;
76 AllocTable();
77 void clear();
78 unsigned long count();
79 void resize( unsigned long newsize );
80 void preserve( unsigned long n );
81 void set( unsigned long index, unsigned long val );
82 unsigned unused();
83 void setChain( std::vector<unsigned long> );
84 std::vector<unsigned long> follow( unsigned long start );
85 unsigned long operator[](unsigned long index );
86 void load( const unsigned char* buffer, unsigned len );
87 void save( unsigned char* buffer );
88 private:
89 std::vector<unsigned long> data;
90 AllocTable( const AllocTable& );
91 AllocTable& operator=( const AllocTable& );
92 };
93
94 class DirEntry
95 {
96 public:
DirEntry()97 DirEntry() : valid(false), name(), dir(false), size(0), start(0),
98 prev(0), next(0), child(0) {};
99 bool valid; // false if invalid (should be skipped)
100 std::string name; // the name, not in unicode anymore
101 bool dir; // true if directory
102 unsigned long size; // size (not valid if directory)
103 unsigned long start; // starting block
104 unsigned prev; // previous sibling
105 unsigned next; // next sibling
106 unsigned child; // first child
107 };
108
109 class DirTree
110 {
111 public:
112 static const unsigned End;
113 DirTree();
114 void clear();
115 unsigned entryCount();
116 DirEntry* entry( unsigned index );
117 DirEntry* entry( const std::string& name );
118 int parent( unsigned index );
119 std::string fullName( unsigned index );
120 std::vector<unsigned> children( unsigned index );
121 void load( unsigned char* buffer, unsigned len );
122 void save( unsigned char* buffer );
123 private:
124 std::vector<DirEntry> entries;
125 DirTree( const DirTree& );
126 DirTree& operator=( const DirTree& );
127 };
128
129 class StorageIO
130 {
131 public:
132 Storage* storage; // owner
133 std::stringstream buf;
134 int result; // result of operation
135 unsigned long bufsize; // size of the buffer
136
137 Header* header; // storage header
138 DirTree* dirtree; // directory tree
139 AllocTable* bbat; // allocation table for big blocks
140 AllocTable* sbat; // allocation table for small blocks
141
142 std::vector<unsigned long> sb_blocks; // blocks for "small" files
143
144 std::list<Stream*> streams;
145
146 StorageIO( Storage* storage, const std::stringstream &memorystream );
147 ~StorageIO();
148
149 bool isOLEStream();
150 void load();
151
152 unsigned long loadBigBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
153
154 unsigned long loadBigBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
155
156 unsigned long loadSmallBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
157
158 unsigned long loadSmallBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
159
160 StreamIO* streamIO( const std::string& name );
161
162 private:
163 // no copy or assign
164 StorageIO( const StorageIO& );
165 StorageIO& operator=( const StorageIO& );
166
167 };
168
169 class StreamIO
170 {
171 public:
172 StorageIO* io;
173 DirEntry* entry;
174 std::string fullName;
175 bool eof;
176 bool fail;
177
178 StreamIO( StorageIO* io, DirEntry* entry );
179 ~StreamIO();
180 unsigned long size();
181 unsigned long tell();
182 int getch();
183 unsigned long read( unsigned char* data, unsigned long maxlen );
184 unsigned long read( unsigned long pos, unsigned char* data, unsigned long maxlen );
185
186
187 private:
188 std::vector<unsigned long> blocks;
189
190 // no copy or assign
191 StreamIO( const StreamIO& );
192 StreamIO& operator=( const StreamIO& );
193
194 // pointer for read
195 unsigned long m_pos;
196
197 // simple cache system to speed-up getch()
198 unsigned char* cache_data;
199 unsigned long cache_size;
200 unsigned long cache_pos;
201 void updateCache();
202 };
203
204 } // namespace libwpg
205
readU16(const unsigned char * ptr)206 static inline unsigned long readU16( const unsigned char* ptr )
207 {
208 return ptr[0]+(ptr[1]<<8);
209 }
210
readU32(const unsigned char * ptr)211 static inline unsigned long readU32( const unsigned char* ptr )
212 {
213 return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
214 }
215
216 static const unsigned char wpsole_magic[] =
217 { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
218
219
220 // =========== Header ==========
221
Header()222 libwpg::Header::Header() :
223 b_shift(9),
224 s_shift(6),
225 num_bat(0),
226 dirent_start(0),
227 threshold(4096),
228 sbat_start(0),
229 num_sbat(0),
230 mbat_start(0),
231 num_mbat(0)
232 {
233 for( unsigned i = 0; i < 8; i++ )
234 id[i] = wpsole_magic[i];
235 for( unsigned j=0; j<109; j++ )
236 bb_blocks[j] = libwpg::AllocTable::Avail;
237 }
238
valid()239 bool libwpg::Header::valid()
240 {
241 if( threshold != 4096 ) return false;
242 if( num_bat == 0 ) return false;
243 if( (num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false;
244 if( (num_bat < 109) && (num_mbat != 0) ) return false;
245 if( s_shift > b_shift ) return false;
246 if( b_shift <= 6 ) return false;
247 if( b_shift >=31 ) return false;
248
249 return true;
250 }
251
load(const unsigned char * buffer)252 void libwpg::Header::load( const unsigned char* buffer )
253 {
254 b_shift = readU16( buffer + 0x1e );
255 s_shift = readU16( buffer + 0x20 );
256 num_bat = readU32( buffer + 0x2c );
257 dirent_start = readU32( buffer + 0x30 );
258 threshold = readU32( buffer + 0x38 );
259 sbat_start = readU32( buffer + 0x3c );
260 num_sbat = readU32( buffer + 0x40 );
261 mbat_start = readU32( buffer + 0x44 );
262 num_mbat = readU32( buffer + 0x48 );
263
264 for( unsigned i = 0; i < 8; i++ )
265 id[i] = buffer[i];
266 for( unsigned j=0; j<109; j++ )
267 bb_blocks[j] = readU32( buffer + 0x4C+j*4 );
268 }
269
270
271
272 // =========== AllocTable ==========
273
274 const unsigned libwpg::AllocTable::Avail = 0xffffffff;
275 const unsigned libwpg::AllocTable::Eof = 0xfffffffe;
276 const unsigned libwpg::AllocTable::Bat = 0xfffffffd;
277 const unsigned libwpg::AllocTable::MetaBat = 0xfffffffc;
278
AllocTable()279 libwpg::AllocTable::AllocTable() :
280 blockSize(4096),
281 data()
282 {
283 // initial size
284 resize( 128 );
285 }
286
count()287 unsigned long libwpg::AllocTable::count()
288 {
289 return data.size();
290 }
291
resize(unsigned long newsize)292 void libwpg::AllocTable::resize( unsigned long newsize )
293 {
294 unsigned oldsize = data.size();
295 data.resize( newsize );
296 if( newsize > oldsize )
297 for( unsigned i = oldsize; i<newsize; i++ )
298 data[i] = Avail;
299 }
300
301 // make sure there're still free blocks
preserve(unsigned long n)302 void libwpg::AllocTable::preserve( unsigned long n )
303 {
304 std::vector<unsigned long> pre;
305 for( unsigned i=0; i < n; i++ )
306 pre.push_back( unused() );
307 }
308
operator [](unsigned long index)309 unsigned long libwpg::AllocTable::operator[]( unsigned long index )
310 {
311 unsigned long result;
312 result = data[index];
313 return result;
314 }
315
set(unsigned long index,unsigned long value)316 void libwpg::AllocTable::set( unsigned long index, unsigned long value )
317 {
318 if( index >= count() ) resize( index + 1);
319 data[ index ] = value;
320 }
321
setChain(std::vector<unsigned long> chain)322 void libwpg::AllocTable::setChain( std::vector<unsigned long> chain )
323 {
324 if( chain.size() )
325 {
326 for( unsigned i=0; i<chain.size()-1; i++ )
327 set( chain[i], chain[i+1] );
328 set( chain[ chain.size()-1 ], AllocTable::Eof );
329 }
330 }
331
332 // TODO: optimize this with better search
already_exist(const std::vector<unsigned long> & chain,unsigned long item)333 static bool already_exist(const std::vector<unsigned long>& chain,
334 unsigned long item)
335 {
336 for(unsigned i = 0; i < chain.size(); i++)
337 if(chain[i] == item) return true;
338
339 return false;
340 }
341
342 // follow
follow(unsigned long start)343 std::vector<unsigned long> libwpg::AllocTable::follow( unsigned long start )
344 {
345 std::vector<unsigned long> chain;
346
347 if( start >= count() ) return chain;
348
349 unsigned long p = start;
350 while( p < count() )
351 {
352 if( p == (unsigned long)Eof ) break;
353 if( p == (unsigned long)Bat ) break;
354 if( p == (unsigned long)MetaBat ) break;
355 if( already_exist(chain, p) ) break;
356 chain.push_back( p );
357 if( data[p] >= count() ) break;
358 p = data[ p ];
359 }
360
361 return chain;
362 }
363
unused()364 unsigned libwpg::AllocTable::unused()
365 {
366 // find first available block
367 for( unsigned i = 0; i < data.size(); i++ )
368 if( data[i] == Avail )
369 return i;
370
371 // completely full, so enlarge the table
372 unsigned block = data.size();
373 resize( data.size()+10 );
374 return block;
375 }
376
load(const unsigned char * buffer,unsigned len)377 void libwpg::AllocTable::load( const unsigned char* buffer, unsigned len )
378 {
379 resize( len / 4 );
380 for( unsigned i = 0; i < count(); i++ )
381 set( i, readU32( buffer + i*4 ) );
382 }
383
384 // =========== DirTree ==========
385
386 const unsigned libwpg::DirTree::End = 0xffffffff;
387
DirTree()388 libwpg::DirTree::DirTree() :
389 entries()
390 {
391 clear();
392 }
393
clear()394 void libwpg::DirTree::clear()
395 {
396 // leave only root entry
397 entries.resize( 1 );
398 entries[0].valid = true;
399 entries[0].name = "Root Entry";
400 entries[0].dir = true;
401 entries[0].size = 0;
402 entries[0].start = End;
403 entries[0].prev = End;
404 entries[0].next = End;
405 entries[0].child = End;
406 }
407
entryCount()408 unsigned libwpg::DirTree::entryCount()
409 {
410 return entries.size();
411 }
412
entry(unsigned index)413 libwpg::DirEntry* libwpg::DirTree::entry( unsigned index )
414 {
415 if( index >= entryCount() ) return (libwpg::DirEntry*) 0;
416 return &entries[ index ];
417 }
418
parent(unsigned index)419 int libwpg::DirTree::parent( unsigned index )
420 {
421 // brute-force, basically we iterate for each entries, find its children
422 // and check if one of the children is 'index'
423 for( unsigned j=0; j<entryCount(); j++ )
424 {
425 std::vector<unsigned> chi = children( j );
426 for( unsigned i=0; i<chi.size();i++ )
427 if( chi[i] == index )
428 return j;
429 }
430
431 return -1;
432 }
433
fullName(unsigned index)434 std::string libwpg::DirTree::fullName( unsigned index )
435 {
436 // don't use root name ("Root Entry"), just give "/"
437 if( index == 0 ) return "/";
438
439 std::string result = entry( index )->name;
440 result.insert( 0, "/" );
441 int p = parent( index );
442 DirEntry * _entry = 0;
443 while( p > 0 )
444 {
445 _entry = entry( p );
446 if (_entry->dir && _entry->valid)
447 {
448 result.insert( 0, _entry->name);
449 result.insert( 0, "/" );
450 }
451 --p;
452 index = p;
453 if( index <= 0 ) break;
454 }
455 return result;
456 }
457
458 // given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
entry(const std::string & name)459 libwpg::DirEntry* libwpg::DirTree::entry( const std::string& name )
460 {
461
462 if( !name.length() ) return (libwpg::DirEntry*)0;
463
464 // quick check for "/" (that's root)
465 if( name == "/" ) return entry( 0 );
466
467 // split the names, e.g "/ObjectPool/_1020961869" will become:
468 // "ObjectPool" and "_1020961869"
469 std::list<std::string> names;
470 std::string::size_type start = 0, end = 0;
471 if( name[0] == '/' ) start++;
472 while( start < name.length() )
473 {
474 end = name.find_first_of( '/', start );
475 if( end == std::string::npos ) end = name.length();
476 names.push_back( name.substr( start, end-start ) );
477 start = end+1;
478 }
479
480 // start from root
481 int index = 0 ;
482
483 // trace one by one
484 std::list<std::string>::iterator it;
485
486 for( it = names.begin(); it != names.end(); ++it )
487 {
488 // find among the children of index
489 std::vector<unsigned> chi = children( index );
490 unsigned child = 0;
491 for( unsigned i = 0; i < chi.size(); i++ )
492 {
493 libwpg::DirEntry* ce = entry( chi[i] );
494 if( ce )
495 if( ce->valid && ( ce->name.length()>1 ) )
496 if( ce->name == *it )
497 child = chi[i];
498 }
499
500 // traverse to the child
501 if( child > 0 ) index = child;
502 else return (libwpg::DirEntry*)0;
503 }
504
505 return entry( index );
506 }
507
508 // helper function: recursively find siblings of index
dirtree_find_siblings(libwpg::DirTree * dirtree,std::vector<unsigned> & result,unsigned index)509 void dirtree_find_siblings( libwpg::DirTree* dirtree, std::vector<unsigned>& result,
510 unsigned index )
511 {
512 libwpg::DirEntry* e = dirtree->entry( index );
513 if( !e ) return;
514 if( !e->valid ) return;
515
516 // prevent infinite loop
517 for( unsigned i = 0; i < result.size(); i++ )
518 if( result[i] == index ) return;
519
520 // add myself
521 result.push_back( index );
522
523 // visit previous sibling, don't go infinitely
524 unsigned prev = e->prev;
525 if( ( prev > 0 ) && ( prev < dirtree->entryCount() ) )
526 {
527 for( unsigned i = 0; i < result.size(); i++ )
528 if( result[i] == prev ) prev = 0;
529 if( prev ) dirtree_find_siblings( dirtree, result, prev );
530 }
531
532 // visit next sibling, don't go infinitely
533 unsigned next = e->next;
534 if( ( next > 0 ) && ( next < dirtree->entryCount() ) )
535 {
536 for( unsigned i = 0; i < result.size(); i++ )
537 if( result[i] == next ) next = 0;
538 if( next ) dirtree_find_siblings( dirtree, result, next );
539 }
540 }
541
children(unsigned index)542 std::vector<unsigned> libwpg::DirTree::children( unsigned index )
543 {
544 std::vector<unsigned> result;
545
546 DirEntry* e = entry( index );
547 if( e ) if( e->valid && e->child < entryCount() )
548 dirtree_find_siblings( this, result, e->child );
549
550 return result;
551 }
552
load(unsigned char * buffer,unsigned size)553 void libwpg::DirTree::load( unsigned char* buffer, unsigned size )
554 {
555 entries.clear();
556
557 for( unsigned i = 0; i < size/128; i++ )
558 {
559 unsigned p = i * 128;
560
561 // would be < 32 if first char in the name isn't printable
562 // unsigned prefix = 32;
563
564 // parse name of this entry, which stored as Unicode 16-bit
565 std::string name;
566 int name_len = readU16( buffer + 0x40+p );
567 if( name_len > 64 ) name_len = 64;
568 for( int j=0; ( buffer[j+p]) && (j<name_len); j+= 2 )
569 name.append( 1, buffer[j+p] );
570
571 // first char isn't printable ? remove it...
572 if( buffer[p] < 32 )
573 {
574 // prefix = buffer[0];
575 name.erase( 0,1 );
576 }
577
578 // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
579 unsigned type = buffer[ 0x42 + p];
580
581 libwpg::DirEntry e;
582 e.valid = true;
583 e.name = name;
584 e.start = readU32( buffer + 0x74+p );
585 e.size = readU32( buffer + 0x78+p );
586 e.prev = readU32( buffer + 0x44+p );
587 e.next = readU32( buffer + 0x48+p );
588 e.child = readU32( buffer + 0x4C+p );
589 e.dir = ( type!=2 );
590
591 // sanity checks
592 if( (type != 2) && (type != 1 ) && (type != 5 ) ) e.valid = false;
593 if( name_len < 1 ) e.valid = false;
594
595 entries.push_back( e );
596 }
597 }
598
599 // =========== StorageIO ==========
600
StorageIO(libwpg::Storage * st,const std::stringstream & memorystream)601 libwpg::StorageIO::StorageIO( libwpg::Storage* st, const std::stringstream &memorystream ) :
602 storage(st),
603 buf( memorystream.str(), std::ios::binary | std::ios::in ),
604 result(libwpg::Storage::Ok),
605 bufsize(0),
606 header(new libwpg::Header()),
607 dirtree(new libwpg::DirTree()),
608 bbat(new libwpg::AllocTable()),
609 sbat(new libwpg::AllocTable()),
610 sb_blocks(),
611 streams()
612 {
613 bbat->blockSize = 1 << header->b_shift;
614 sbat->blockSize = 1 << header->s_shift;
615 }
616
~StorageIO()617 libwpg::StorageIO::~StorageIO()
618 {
619 delete sbat;
620 delete bbat;
621 delete dirtree;
622 delete header;
623
624 std::list<libwpg::Stream*>::iterator it;
625 for( it = streams.begin(); it != streams.end(); ++it )
626 delete *it;
627 }
628
isOLEStream()629 bool libwpg::StorageIO::isOLEStream()
630 {
631 load();
632 return (result == libwpg::Storage::Ok);
633 }
634
load()635 void libwpg::StorageIO::load()
636 {
637 unsigned char* buffer = 0;
638 unsigned long buflen = 0;
639 std::vector<unsigned long> blocks;
640
641 // find size of input file
642 buf.seekg( 0, std::ios::end );
643 bufsize = buf.tellg();
644
645 // load header
646 buffer = new unsigned char[512];
647 buf.seekg( 0 );
648 buf.read( (char*)buffer, 512 );
649 header->load( buffer );
650 delete[] buffer;
651
652 // check OLE magic id
653 result = libwpg::Storage::NotOLE;
654 for( unsigned i=0; i<8; i++ )
655 if( header->id[i] != wpsole_magic[i] )
656 return;
657
658 // sanity checks
659 result = libwpg::Storage::BadOLE;
660 if( !header->valid() ) return;
661 if( header->threshold != 4096 ) return;
662
663 // important block size
664 bbat->blockSize = 1 << header->b_shift;
665 sbat->blockSize = 1 << header->s_shift;
666
667 // find blocks allocated to store big bat
668 // the first 109 blocks are in header, the rest in meta bat
669 blocks.clear();
670 blocks.resize( header->num_bat );
671 for( unsigned j = 0; j < 109; j++ )
672 if( j >= header->num_bat ) break;
673 else blocks[j] = header->bb_blocks[j];
674 if( (header->num_bat > 109) && (header->num_mbat > 0) )
675 {
676 unsigned char* buffer2 = new unsigned char[ bbat->blockSize ];
677 unsigned k = 109;
678 for( unsigned r = 0; r < header->num_mbat; r++ )
679 {
680 loadBigBlock( header->mbat_start+r, buffer2, bbat->blockSize );
681 for( unsigned s=0; s < bbat->blockSize; s+=4 )
682 {
683 if( k >= header->num_bat ) break;
684 else blocks[k++] = readU32( buffer2 + s );
685 }
686 }
687 delete[] buffer2;
688 }
689
690 // load big bat
691 buflen = blocks.size()*bbat->blockSize;
692 if( buflen > 0 )
693 {
694 buffer = new unsigned char[ buflen ];
695 loadBigBlocks( blocks, buffer, buflen );
696 bbat->load( buffer, buflen );
697 delete[] buffer;
698 }
699
700 // load small bat
701 blocks.clear();
702 blocks = bbat->follow( header->sbat_start );
703 buflen = blocks.size()*bbat->blockSize;
704 if( buflen > 0 )
705 {
706 buffer = new unsigned char[ buflen ];
707 loadBigBlocks( blocks, buffer, buflen );
708 sbat->load( buffer, buflen );
709 delete[] buffer;
710 }
711
712 // load directory tree
713 blocks.clear();
714 blocks = bbat->follow( header->dirent_start );
715 buflen = blocks.size()*bbat->blockSize;
716 buffer = new unsigned char[ buflen ];
717 loadBigBlocks( blocks, buffer, buflen );
718 dirtree->load( buffer, buflen );
719 unsigned sb_start = readU32( buffer + 0x74 );
720 delete[] buffer;
721
722 // fetch block chain as data for small-files
723 sb_blocks = bbat->follow( sb_start ); // small files
724
725 // so far so good
726 result = libwpg::Storage::Ok;
727 }
728
streamIO(const std::string & name)729 libwpg::StreamIO* libwpg::StorageIO::streamIO( const std::string& name )
730 {
731 load();
732
733 // sanity check
734 if( !name.length() ) return (libwpg::StreamIO*)0;
735
736 // search in the entries
737 libwpg::DirEntry* entry = dirtree->entry( name );
738 if( !entry ) return (libwpg::StreamIO*)0;
739 if( entry->dir ) return (libwpg::StreamIO*)0;
740
741 libwpg::StreamIO* result = new libwpg::StreamIO( this, entry );
742 result->fullName = name;
743
744 return result;
745 }
746
loadBigBlocks(std::vector<unsigned long> blocks,unsigned char * data,unsigned long maxlen)747 unsigned long libwpg::StorageIO::loadBigBlocks( std::vector<unsigned long> blocks,
748 unsigned char* data, unsigned long maxlen )
749 {
750 // sentinel
751 if( !data ) return 0;
752 if( blocks.size() < 1 ) return 0;
753 if( maxlen == 0 ) return 0;
754
755 // read block one by one, seems fast enough
756 unsigned long bytes = 0;
757 for( unsigned long i=0; (i < blocks.size() ) & ( bytes<maxlen ); i++ )
758 {
759 unsigned long block = blocks[i];
760 unsigned long pos = bbat->blockSize * ( block+1 );
761 unsigned long p = (bbat->blockSize < maxlen-bytes) ? bbat->blockSize : maxlen-bytes;
762 if( pos + p > bufsize ) p = bufsize - pos;
763 buf.seekg( pos );
764 buf.read( (char*)data + bytes, p );
765 bytes += p;
766 }
767
768 return bytes;
769 }
770
loadBigBlock(unsigned long block,unsigned char * data,unsigned long maxlen)771 unsigned long libwpg::StorageIO::loadBigBlock( unsigned long block,
772 unsigned char* data, unsigned long maxlen )
773 {
774 // sentinel
775 if( !data ) return 0;
776
777 // wraps call for loadBigBlocks
778 std::vector<unsigned long> blocks;
779 blocks.resize( 1 );
780 blocks[ 0 ] = block;
781
782 return loadBigBlocks( blocks, data, maxlen );
783 }
784
785 // return number of bytes which has been read
loadSmallBlocks(std::vector<unsigned long> blocks,unsigned char * data,unsigned long maxlen)786 unsigned long libwpg::StorageIO::loadSmallBlocks( std::vector<unsigned long> blocks,
787 unsigned char* data, unsigned long maxlen )
788 {
789 // sentinel
790 if( !data ) return 0;
791 if( blocks.size() < 1 ) return 0;
792 if( maxlen == 0 ) return 0;
793
794 // our own local buffer
795 unsigned char* buf = new unsigned char[ bbat->blockSize ];
796
797 // read small block one by one
798 unsigned long bytes = 0;
799 for( unsigned long i=0; ( i<blocks.size() ) & ( bytes<maxlen ); i++ )
800 {
801 unsigned long block = blocks[i];
802
803 // find where the small-block exactly is
804 unsigned long pos = block * sbat->blockSize;
805 unsigned long bbindex = pos / bbat->blockSize;
806 if( bbindex >= sb_blocks.size() ) break;
807
808 loadBigBlock( sb_blocks[ bbindex ], buf, bbat->blockSize );
809
810 // copy the data
811 unsigned offset = pos % bbat->blockSize;
812 unsigned long p = (maxlen-bytes < bbat->blockSize-offset ) ? maxlen-bytes : bbat->blockSize-offset;
813 p = (sbat->blockSize<p ) ? sbat->blockSize : p;
814 memcpy( data + bytes, buf + offset, p );
815 bytes += p;
816 }
817
818 delete[] buf;
819
820 return bytes;
821 }
822
loadSmallBlock(unsigned long block,unsigned char * data,unsigned long maxlen)823 unsigned long libwpg::StorageIO::loadSmallBlock( unsigned long block,
824 unsigned char* data, unsigned long maxlen )
825 {
826 // sentinel
827 if( !data ) return 0;
828
829 // wraps call for loadSmallBlocks
830 std::vector<unsigned long> blocks;
831 blocks.resize( 1 );
832 blocks.assign( 1, block );
833
834 return loadSmallBlocks( blocks, data, maxlen );
835 }
836
837 // =========== StreamIO ==========
838
StreamIO(libwpg::StorageIO * s,libwpg::DirEntry * e)839 libwpg::StreamIO::StreamIO( libwpg::StorageIO* s, libwpg::DirEntry* e) :
840 io(s),
841 entry(e),
842 fullName(),
843 eof(false),
844 fail(false),
845 blocks(),
846 m_pos(0),
847 cache_data(nullptr),
848 cache_size(4096),
849 cache_pos(0)
850 {
851 if( entry->size >= io->header->threshold )
852 blocks = io->bbat->follow( entry->start );
853 else
854 blocks = io->sbat->follow( entry->start );
855
856 // prepare cache
857 cache_data = new unsigned char[cache_size];
858 updateCache();
859 }
860
861 // FIXME tell parent we're gone
~StreamIO()862 libwpg::StreamIO::~StreamIO()
863 {
864 delete[] cache_data;
865 }
866
tell()867 unsigned long libwpg::StreamIO::tell()
868 {
869 return m_pos;
870 }
871
getch()872 int libwpg::StreamIO::getch()
873 {
874 // past end-of-file ?
875 if( m_pos > entry->size ) return -1;
876
877 // need to update cache ?
878 if( !cache_size || ( m_pos < cache_pos ) ||
879 ( m_pos >= cache_pos + cache_size ) )
880 updateCache();
881
882 // something bad if we don't get good cache
883 if( !cache_size ) return -1;
884
885 int data = cache_data[m_pos - cache_pos];
886 m_pos++;
887
888 return data;
889 }
890
read(unsigned long pos,unsigned char * data,unsigned long maxlen)891 unsigned long libwpg::StreamIO::read( unsigned long pos, unsigned char* data, unsigned long maxlen )
892 {
893 // sanity checks
894 if( !data ) return 0;
895 if( maxlen == 0 ) return 0;
896
897 unsigned long totalbytes = 0;
898
899 if ( entry->size < io->header->threshold )
900 {
901 // small file
902 unsigned long index = pos / io->sbat->blockSize;
903
904 if( index >= blocks.size() ) return 0;
905
906 unsigned char* buf = new unsigned char[ io->sbat->blockSize ];
907 unsigned long offset = pos % io->sbat->blockSize;
908 while( totalbytes < maxlen )
909 {
910 if( index >= blocks.size() ) break;
911 io->loadSmallBlock( blocks[index], buf, io->bbat->blockSize );
912 unsigned long count = io->sbat->blockSize - offset;
913 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
914 memcpy( data+totalbytes, buf + offset, count );
915 totalbytes += count;
916 offset = 0;
917 index++;
918 }
919 delete[] buf;
920
921 }
922 else
923 {
924 // big file
925 unsigned long index = pos / io->bbat->blockSize;
926
927 if( index >= blocks.size() ) return 0;
928
929 unsigned char* buf = new unsigned char[ io->bbat->blockSize ];
930 unsigned long offset = pos % io->bbat->blockSize;
931 while( totalbytes < maxlen )
932 {
933 if( index >= blocks.size() ) break;
934 io->loadBigBlock( blocks[index], buf, io->bbat->blockSize );
935 unsigned long count = io->bbat->blockSize - offset;
936 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
937 memcpy( data+totalbytes, buf + offset, count );
938 totalbytes += count;
939 index++;
940 offset = 0;
941 }
942 delete [] buf;
943
944 }
945
946 return totalbytes;
947 }
948
read(unsigned char * data,unsigned long maxlen)949 unsigned long libwpg::StreamIO::read( unsigned char* data, unsigned long maxlen )
950 {
951 unsigned long bytes = read( tell(), data, maxlen );
952 m_pos += bytes;
953 return bytes;
954 }
955
updateCache()956 void libwpg::StreamIO::updateCache()
957 {
958 // sanity check
959 if( !cache_data ) return;
960
961 cache_pos = m_pos - ( m_pos % cache_size );
962 unsigned long bytes = cache_size;
963 if( cache_pos + bytes > entry->size ) bytes = entry->size - cache_pos;
964 cache_size = read( cache_pos, cache_data, bytes );
965 }
966
967
968 // =========== Storage ==========
969
Storage(const std::stringstream & memorystream)970 libwpg::Storage::Storage( const std::stringstream &memorystream ) :
971 io(nullptr)
972 {
973 io = new StorageIO( this, memorystream );
974 }
975
~Storage()976 libwpg::Storage::~Storage()
977 {
978 delete io;
979 }
980
result()981 int libwpg::Storage::result()
982 {
983 return io->result;
984 }
985
isOLEStream()986 bool libwpg::Storage::isOLEStream()
987 {
988 return io->isOLEStream();
989 }
990
991 // =========== Stream ==========
992
Stream(libwpg::Storage * storage,const std::string & name)993 libwpg::Stream::Stream( libwpg::Storage* storage, const std::string& name ) :
994 io(storage->io->streamIO( name ))
995 {
996 }
997
998 // FIXME tell parent we're gone
~Stream()999 libwpg::Stream::~Stream()
1000 {
1001 delete io;
1002 }
1003
size()1004 unsigned long libwpg::Stream::size()
1005 {
1006 return io ? io->entry->size : 0;
1007 }
1008
read(unsigned char * data,unsigned long maxlen)1009 unsigned long libwpg::Stream::read( unsigned char* data, unsigned long maxlen )
1010 {
1011 return io ? io->read( data, maxlen ) : 0;
1012 }
1013