1 /* POLE - Portable C++ library to access OLE Storage
2 Copyright (C) 2002-2005 Ariya Hidayat <ariya@kde.org>
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.
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 */
29 #include <sstream>
30 #include <iostream>
31 #include <list>
32 #include <string>
33 #include <vector>
35 #include "WPGOLEStream.h"
36 #include "libwpg_utils.h"
38 namespace libwpg
39 {
41 class Header
42 {
43 public:
44 unsigned char id[8]; // signature, or magic identifier
45 unsigned b_shift; // bbat->blockSize = 1 << b_shift
46 unsigned s_shift; // sbat->blockSize = 1 << s_shift
47 unsigned num_bat; // blocks allocated for big bat
48 unsigned dirent_start; // starting block for directory info
49 unsigned threshold; // switch from small to big file (usually 4K)
50 unsigned sbat_start; // starting block index to store small bat
51 unsigned num_sbat; // blocks allocated for small bat
52 unsigned mbat_start; // starting block to store meta bat
53 unsigned num_mbat; // blocks allocated for meta bat
54 unsigned long bb_blocks[109];
56 Header();
57 bool valid();
58 void load( const unsigned char* buffer );
59 void save( unsigned char* buffer );
60 void debug();
61 };
63 class AllocTable
64 {
65 public:
66 static const unsigned Eof;
67 static const unsigned Avail;
68 static const unsigned Bat;
69 static const unsigned MetaBat;
70 unsigned blockSize;
71 AllocTable();
72 void clear();
73 unsigned long count();
74 void resize( unsigned long newsize );
75 void preserve( unsigned long n );
76 void set( unsigned long index, unsigned long val );
77 unsigned unused();
78 void setChain( std::vector<unsigned long> );
79 std::vector<unsigned long> follow( unsigned long start );
80 unsigned long operator[](unsigned long index );
81 void load( const unsigned char* buffer, unsigned len );
82 void save( unsigned char* buffer );
83 private:
84 std::vector<unsigned long> data;
85 AllocTable( const AllocTable& );
86 AllocTable& operator=( const AllocTable& );
87 };
89 class DirEntry
90 {
91 public:
92 bool valid; // false if invalid (should be skipped)
93 std::string name; // the name, not in unicode anymore
94 bool dir; // true if directory
95 unsigned long size; // size (not valid if directory)
96 unsigned long start; // starting block
97 unsigned prev; // previous sibling
98 unsigned next; // next sibling
99 unsigned child; // first child
100 };
102 class DirTree
103 {
104 public:
105 static const unsigned End;
106 DirTree();
107 void clear();
108 unsigned entryCount();
109 DirEntry* entry( unsigned index );
110 DirEntry* entry( const std::string& name );
111 int parent( unsigned index );
112 std::string fullName( unsigned index );
113 std::vector<unsigned> children( unsigned index );
114 void load( unsigned char* buffer, unsigned len );
115 void save( unsigned char* buffer );
116 private:
117 std::vector<DirEntry> entries;
118 DirTree( const DirTree& );
119 DirTree& operator=( const DirTree& );
120 };
122 class StorageIO
123 {
124 public:
125 Storage* storage; // owner
126 std::stringstream buf;
127 int result; // result of operation
128 unsigned long bufsize; // size of the buffer
130 Header* header; // storage header
131 DirTree* dirtree; // directory tree
132 AllocTable* bbat; // allocation table for big blocks
133 AllocTable* sbat; // allocation table for small blocks
135 std::vector<unsigned long> sb_blocks; // blocks for "small" files
137 std::list<Stream*> streams;
139 StorageIO( Storage* storage, const std::stringstream &memorystream );
140 ~StorageIO();
142 bool isOle();
143 void load();
145 unsigned long loadBigBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
147 unsigned long loadBigBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
149 unsigned long loadSmallBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
151 unsigned long loadSmallBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
153 StreamIO* streamIO( const std::string& name );
155 private:
156 // no copy or assign
157 StorageIO( const StorageIO& );
158 StorageIO& operator=( const StorageIO& );
160 };
162 class StreamIO
163 {
164 public:
165 StorageIO* io;
166 DirEntry* entry;
167 std::string fullName;
168 bool eof;
169 bool fail;
171 StreamIO( StorageIO* io, DirEntry* entry );
172 ~StreamIO();
173 unsigned long size();
174 unsigned long tell();
175 int getch();
176 unsigned long read( unsigned char* data, unsigned long maxlen );
177 unsigned long read( unsigned long pos, unsigned char* data, unsigned long maxlen );
180 private:
181 std::vector<unsigned long> blocks;
183 // no copy or assign
184 StreamIO( const StreamIO& );
185 StreamIO& operator=( const StreamIO& );
187 // pointer for read
188 unsigned long m_pos;
190 // simple cache system to speed-up getch()
191 unsigned char* cache_data;
192 unsigned long cache_size;
193 unsigned long cache_pos;
194 void updateCache();
195 };
197 }; // namespace libwpg
199 using namespace libwpg;
201 static inline unsigned long readU16( const unsigned char* ptr )
202 {
203 return ptr[0]+(ptr[1]<<8);
204 }
206 static inline unsigned long readU32( const unsigned char* ptr )
207 {
208 return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
209 }
211 static const unsigned char wpgole_magic[] =
212 { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
214 // =========== Header ==========
216 Header::Header()
217 {
218 b_shift = 9;
219 s_shift = 6;
220 num_bat = 0;
221 dirent_start = 0;
222 threshold = 4096;
223 sbat_start = 0;
224 num_sbat = 0;
225 mbat_start = 0;
226 num_mbat = 0;
228 for( unsigned i = 0; i < 8; i++ )
229 id[i] = wpgole_magic[i];
230 for( unsigned j=0; j<109; j++ )
231 bb_blocks[j] = AllocTable::Avail;
232 }
234 bool Header::valid()
235 {
236 if( threshold != 4096 ) return false;
237 if( num_bat == 0 ) return false;
238 if( (num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false;
239 if( (num_bat < 109) && (num_mbat != 0) ) return false;
240 if( s_shift > b_shift ) return false;
241 if( b_shift <= 6 ) return false;
242 if( b_shift >=31 ) return false;
244 return true;
245 }
247 void Header::load( const unsigned char* buffer )
248 {
249 b_shift = readU16( buffer + 0x1e );
250 s_shift = readU16( buffer + 0x20 );
251 num_bat = readU32( buffer + 0x2c );
252 dirent_start = readU32( buffer + 0x30 );
253 threshold = readU32( buffer + 0x38 );
254 sbat_start = readU32( buffer + 0x3c );
255 num_sbat = readU32( buffer + 0x40 );
256 mbat_start = readU32( buffer + 0x44 );
257 num_mbat = readU32( buffer + 0x48 );
259 for( unsigned i = 0; i < 8; i++ )
260 id[i] = buffer[i];
261 for( unsigned j=0; j<109; j++ )
262 bb_blocks[j] = readU32( buffer + 0x4C+j*4 );
263 }
265 // =========== AllocTable ==========
267 const unsigned AllocTable::Avail = 0xffffffff;
268 const unsigned AllocTable::Eof = 0xfffffffe;
269 const unsigned AllocTable::Bat = 0xfffffffd;
270 const unsigned AllocTable::MetaBat = 0xfffffffc;
272 AllocTable::AllocTable()
273 {
274 blockSize = 4096;
275 // initial size
276 resize( 128 );
277 }
279 unsigned long AllocTable::count()
280 {
281 return data.size();
282 }
284 void AllocTable::resize( unsigned long newsize )
285 {
286 unsigned oldsize = data.size();
287 data.resize( newsize );
288 if( newsize > oldsize )
289 for( unsigned i = oldsize; i<newsize; i++ )
290 data[i] = Avail;
291 }
293 // make sure there're still free blocks
294 void AllocTable::preserve( unsigned long n )
295 {
296 std::vector<unsigned long> pre;
297 for( unsigned i=0; i < n; i++ )
298 pre.push_back( unused() );
299 }
301 unsigned long AllocTable::operator[]( unsigned long index )
302 {
303 unsigned long result;
304 result = data[index];
305 return result;
306 }
308 void AllocTable::set( unsigned long index, unsigned long value )
309 {
310 if( index >= count() ) resize( index + 1);
311 data[ index ] = value;
312 }
314 void AllocTable::setChain( std::vector<unsigned long> chain )
315 {
316 if( chain.size() )
317 {
318 for( unsigned i=0; i<chain.size()-1; i++ )
319 set( chain[i], chain[i+1] );
320 set( chain[ chain.size()-1 ], AllocTable::Eof );
321 }
322 }
324 // follow
325 std::vector<unsigned long> AllocTable::follow( unsigned long start )
326 {
327 std::vector<unsigned long> chain;
329 if( start >= count() ) return chain;
331 unsigned long p = start;
332 while( p < count() )
333 {
334 if( p == (unsigned long)Eof ) break;
335 if( p == (unsigned long)Bat ) break;
336 if( p == (unsigned long)MetaBat ) break;
337 if( p >= count() ) break;
338 chain.push_back( p );
339 if( data[p] >= count() ) break;
340 p = data[ p ];
341 }
343 return chain;
344 }
346 unsigned AllocTable::unused()
347 {
348 // find first available block
349 for( unsigned i = 0; i < data.size(); i++ )
350 if( data[i] == Avail )
351 return i;
353 // completely full, so enlarge the table
354 unsigned block = data.size();
355 resize( data.size()+10 );
356 return block;
357 }
359 void AllocTable::load( const unsigned char* buffer, unsigned len )
360 {
361 resize( len / 4 );
362 for( unsigned i = 0; i < count(); i++ )
363 set( i, readU32( buffer + i*4 ) );
364 }
366 // =========== DirTree ==========
368 const unsigned DirTree::End = 0xffffffff;
370 DirTree::DirTree()
371 {
372 clear();
373 }
375 void DirTree::clear()
376 {
377 // leave only root entry
378 entries.resize( 1 );
379 entries[0].valid = true;
380 entries[0].name = "Root Entry";
381 entries[0].dir = true;
382 entries[0].size = 0;
383 entries[0].start = End;
384 entries[0].prev = End;
385 entries[0].next = End;
386 entries[0].child = End;
387 }
389 unsigned DirTree::entryCount()
390 {
391 return entries.size();
392 }
394 DirEntry* DirTree::entry( unsigned index )
395 {
396 if( index >= entryCount() ) return (DirEntry*) 0;
397 return &entries[ index ];
398 }
400 int DirTree::parent( unsigned index )
401 {
402 // brute-force, basically we iterate for each entries, find its children
403 // and check if one of the children is 'index'
404 for( unsigned j=0; j<entryCount(); j++ )
405 {
406 std::vector<unsigned> chi = children( j );
407 for( unsigned i=0; i<chi.size();i++ )
408 if( chi[i] == index )
409 return j;
410 }
412 return -1;
413 }
415 std::string DirTree::fullName( unsigned index )
416 {
417 // don't use root name ("Root Entry"), just give "/"
418 if( index == 0 ) return "/";
420 std::string result = entry( index )->name;
421 result.insert( 0, "/" );
422 int p = parent( index );
423 DirEntry * _entry = 0;
424 while( p > 0 )
425 {
426 _entry = entry( p );
427 if (_entry->dir && _entry->valid)
428 {
429 result.insert( 0, _entry->name);
430 result.insert( 0, "/" );
431 }
432 --p;
433 index = p;
434 if( index <= 0 ) break;
435 }
436 return result;
437 }
439 // given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
440 DirEntry* DirTree::entry( const std::string& name )
441 {
443 if( !name.length() ) return (DirEntry*)0;
445 // quick check for "/" (that's root)
446 if( name == "/" ) return entry( 0 );
448 // split the names, e.g "/ObjectPool/_1020961869" will become:
449 // "ObjectPool" and "_1020961869"
450 std::list<std::string> names;
451 std::string::size_type start = 0, end = 0;
452 if( name[0] == '/' ) start++;
453 while( start < name.length() )
454 {
455 end = name.find_first_of( '/', start );
456 if( end == std::string::npos ) end = name.length();
457 names.push_back( name.substr( start, end-start ) );
458 start = end+1;
459 }
461 // start from root
462 int index = 0 ;
464 // trace one by one
465 std::list<std::string>::iterator it;
467 for( it = names.begin(); it != names.end(); ++it )
468 {
469 // find among the children of index
470 std::vector<unsigned> chi = children( index );
471 unsigned child = 0;
472 for( unsigned i = 0; i < chi.size(); i++ )
473 {
474 DirEntry* ce = entry( chi[i] );
475 if( ce )
476 if( ce->valid && ( ce->name.length()>1 ) )
477 if( ce->name == *it )
478 child = chi[i];
479 }
481 // traverse to the child
482 if( child > 0 ) index = child;
483 else return (DirEntry*)0;
484 }
486 return entry( index );
487 }
489 // helper function: recursively find siblings of index
490 void dirtree_find_siblings( DirTree* dirtree, std::vector<unsigned>& result,
491 unsigned index )
492 {
493 DirEntry* e = dirtree->entry( index );
494 if( !e ) return;
495 if( !e->valid ) return;
497 // prevent infinite loop
498 for( unsigned i = 0; i < result.size(); i++ )
499 if( result[i] == index ) return;
501 // add myself
502 result.push_back( index );
504 // visit previous sibling, don't go infinitely
505 unsigned prev = e->prev;
506 if( ( prev > 0 ) && ( prev < dirtree->entryCount() ) )
507 {
508 for( unsigned i = 0; i < result.size(); i++ )
509 if( result[i] == prev ) prev = 0;
510 if( prev ) dirtree_find_siblings( dirtree, result, prev );
511 }
513 // visit next sibling, don't go infinitely
514 unsigned next = e->next;
515 if( ( next > 0 ) && ( next < dirtree->entryCount() ) )
516 {
517 for( unsigned i = 0; i < result.size(); i++ )
518 if( result[i] == next ) next = 0;
519 if( next ) dirtree_find_siblings( dirtree, result, next );
520 }
521 }
523 std::vector<unsigned> DirTree::children( unsigned index )
524 {
525 std::vector<unsigned> result;
527 DirEntry* e = entry( index );
528 if( e ) if( e->valid && e->child < entryCount() )
529 dirtree_find_siblings( this, result, e->child );
531 return result;
532 }
534 void DirTree::load( unsigned char* buffer, unsigned size )
535 {
536 entries.clear();
538 for( unsigned i = 0; i < size/128; i++ )
539 {
540 unsigned p = i * 128;
542 // would be < 32 if first char in the name isn't printable
543 unsigned prefix = 32;
545 // parse name of this entry, which stored as Unicode 16-bit
546 std::string name;
547 int name_len = readU16( buffer + 0x40+p );
548 if( name_len > 64 ) name_len = 64;
549 for( int j=0; ( buffer[j+p]) && (j<name_len); j+= 2 )
550 name.append( 1, buffer[j+p] );
552 // first char isn't printable ? remove it...
553 if( buffer[p] < 32 )
554 {
555 prefix = buffer[0];
556 name.erase( 0,1 );
557 }
559 // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
560 unsigned type = buffer[ 0x42 + p];
562 DirEntry e;
563 e.valid = true;
564 e.name = name;
565 e.start = readU32( buffer + 0x74+p );
566 e.size = readU32( buffer + 0x78+p );
567 e.prev = readU32( buffer + 0x44+p );
568 e.next = readU32( buffer + 0x48+p );
569 e.child = readU32( buffer + 0x4C+p );
570 e.dir = ( type!=2 );
572 // sanity checks
573 if( (type != 2) && (type != 1 ) && (type != 5 ) ) e.valid = false;
574 if( name_len < 1 ) e.valid = false;
576 entries.push_back( e );
577 }
578 }
580 // =========== StorageIO ==========
582 StorageIO::StorageIO( Storage* st, const std::stringstream &memorystream ) :
583 buf( memorystream.str(), std::ios::binary | std::ios::in )
584 {
585 storage = st;
586 result = Storage::Ok;
588 header = new Header();
589 dirtree = new DirTree();
590 bbat = new AllocTable();
591 sbat = new AllocTable();
593 bufsize = 0;
594 bbat->blockSize = 1 << header->b_shift;
595 sbat->blockSize = 1 << header->s_shift;
596 }
598 StorageIO::~StorageIO()
599 {
600 delete sbat;
601 delete bbat;
602 delete dirtree;
603 delete header;
605 std::list<Stream*>::iterator it;
606 for( it = streams.begin(); it != streams.end(); ++it )
607 delete *it;
608 }
610 bool StorageIO::isOle()
611 {
612 load();
613 return (result == Storage::Ok);
614 }
616 void StorageIO::load()
617 {
618 unsigned char* buffer = 0;
619 unsigned long buflen = 0;
620 std::vector<unsigned long> blocks;
622 // find size of input file
623 buf.seekg( 0, std::ios::end );
624 bufsize = buf.tellg();
626 // load header
627 buffer = new unsigned char[512];
628 buf.seekg( 0 );
629 buf.read( (char*)buffer, 512 );
630 header->load( buffer );
631 delete[] buffer;
633 // check OLE magic id
634 result = Storage::NotOLE;
635 for( unsigned i=0; i<8; i++ )
636 if( header->id[i] != wpgole_magic[i] )
637 return;
639 // sanity checks
640 result = Storage::BadOLE;
641 if( !header->valid() ) return;
642 if( header->threshold != 4096 ) return;
644 // important block size
645 bbat->blockSize = 1 << header->b_shift;
646 sbat->blockSize = 1 << header->s_shift;
648 // find blocks allocated to store big bat
649 // the first 109 blocks are in header, the rest in meta bat
650 blocks.clear();
651 blocks.resize( header->num_bat );
652 for( unsigned j = 0; j < 109; j++ )
653 if( j >= header->num_bat ) break;
654 else blocks[j] = header->bb_blocks[j];
655 if( (header->num_bat > 109) && (header->num_mbat > 0) )
656 {
657 unsigned char* buffer2 = new unsigned char[ bbat->blockSize ];
658 unsigned k = 109;
659 for( unsigned r = 0; r < header->num_mbat; r++ )
660 {
661 loadBigBlock( header->mbat_start+r, buffer2, bbat->blockSize );
662 for( unsigned s=0; s < bbat->blockSize; s+=4 )
663 {
664 if( k >= header->num_bat ) break;
665 else blocks[k++] = readU32( buffer2 + s );
666 }
667 }
668 delete[] buffer2;
669 }
671 // load big bat
672 buflen = blocks.size()*bbat->blockSize;
673 if( buflen > 0 )
674 {
675 buffer = new unsigned char[ buflen ];
676 loadBigBlocks( blocks, buffer, buflen );
677 bbat->load( buffer, buflen );
678 delete[] buffer;
679 }
681 // load small bat
682 blocks.clear();
683 blocks = bbat->follow( header->sbat_start );
684 buflen = blocks.size()*bbat->blockSize;
685 if( buflen > 0 )
686 {
687 buffer = new unsigned char[ buflen ];
688 loadBigBlocks( blocks, buffer, buflen );
689 sbat->load( buffer, buflen );
690 delete[] buffer;
691 }
693 // load directory tree
694 blocks.clear();
695 blocks = bbat->follow( header->dirent_start );
696 buflen = blocks.size()*bbat->blockSize;
697 buffer = new unsigned char[ buflen ];
698 loadBigBlocks( blocks, buffer, buflen );
699 dirtree->load( buffer, buflen );
700 unsigned sb_start = readU32( buffer + 0x74 );
701 delete[] buffer;
703 // fetch block chain as data for small-files
704 sb_blocks = bbat->follow( sb_start ); // small files
706 // so far so good
707 result = Storage::Ok;
708 }
710 StreamIO* StorageIO::streamIO( const std::string& name )
711 {
712 load();
714 // sanity check
715 if( !name.length() ) return (StreamIO*)0;
717 // search in the entries
718 DirEntry* entry = dirtree->entry( name );
719 if( !entry ) return (StreamIO*)0;
720 if( entry->dir ) return (StreamIO*)0;
722 StreamIO* result = new StreamIO( this, entry );
723 result->fullName = name;
725 return result;
726 }
728 unsigned long StorageIO::loadBigBlocks( std::vector<unsigned long> blocks,
729 unsigned char* data, unsigned long maxlen )
730 {
731 // sentinel
732 if( !data ) return 0;
733 if( blocks.size() < 1 ) return 0;
734 if( maxlen == 0 ) return 0;
736 // read block one by one, seems fast enough
737 unsigned long bytes = 0;
738 for( unsigned long i=0; (i < blocks.size() ) & ( bytes<maxlen ); i++ )
739 {
740 unsigned long block = blocks[i];
741 unsigned long pos = bbat->blockSize * ( block+1 );
742 unsigned long p = (bbat->blockSize < maxlen-bytes) ? bbat->blockSize : maxlen-bytes;
743 if( pos + p > bufsize ) p = bufsize - pos;
744 buf.seekg( pos );
745 buf.read( (char*)data + bytes, p );
746 bytes += p;
747 }
749 return bytes;
750 }
752 unsigned long StorageIO::loadBigBlock( unsigned long block,
753 unsigned char* data, unsigned long maxlen )
754 {
755 // sentinel
756 if( !data ) return 0;
758 // wraps call for loadBigBlocks
759 std::vector<unsigned long> blocks;
760 blocks.resize( 1 );
761 blocks[ 0 ] = block;
763 return loadBigBlocks( blocks, data, maxlen );
764 }
766 // return number of bytes which has been read
767 unsigned long StorageIO::loadSmallBlocks( std::vector<unsigned long> blocks,
768 unsigned char* data, unsigned long maxlen )
769 {
770 // sentinel
771 if( !data ) return 0;
772 if( blocks.size() < 1 ) return 0;
773 if( maxlen == 0 ) return 0;
775 // our own local buffer
776 unsigned char* buf = new unsigned char[ bbat->blockSize ];
778 // read small block one by one
779 unsigned long bytes = 0;
780 for( unsigned long i=0; ( i<blocks.size() ) & ( bytes<maxlen ); i++ )
781 {
782 unsigned long block = blocks[i];
784 // find where the small-block exactly is
785 unsigned long pos = block * sbat->blockSize;
786 unsigned long bbindex = pos / bbat->blockSize;
787 if( bbindex >= sb_blocks.size() ) break;
789 loadBigBlock( sb_blocks[ bbindex ], buf, bbat->blockSize );
791 // copy the data
792 unsigned offset = pos % bbat->blockSize;
793 unsigned long p = (maxlen-bytes < bbat->blockSize-offset ) ? maxlen-bytes : bbat->blockSize-offset;
794 p = (sbat->blockSize<p ) ? sbat->blockSize : p;
795 memcpy( data + bytes, buf + offset, p );
796 bytes += p;
797 }
799 delete[] buf;
801 return bytes;
802 }
804 unsigned long StorageIO::loadSmallBlock( unsigned long block,
805 unsigned char* data, unsigned long maxlen )
806 {
807 // sentinel
808 if( !data ) return 0;
810 // wraps call for loadSmallBlocks
811 std::vector<unsigned long> blocks;
812 blocks.resize( 1 );
813 blocks.assign( 1, block );
815 return loadSmallBlocks( blocks, data, maxlen );
816 }
818 // =========== StreamIO ==========
820 StreamIO::StreamIO( StorageIO* s, DirEntry* e)
821 {
822 io = s;
823 entry = e;
824 eof = false;
825 fail = false;
827 m_pos = 0;
829 if( entry->size >= io->header->threshold )
830 blocks = io->bbat->follow( entry->start );
831 else
832 blocks = io->sbat->follow( entry->start );
834 // prepare cache
835 cache_pos = 0;
836 cache_size = 4096; // optimal ?
837 cache_data = new unsigned char[cache_size];
838 updateCache();
839 }
841 // FIXME tell parent we're gone
842 StreamIO::~StreamIO()
843 {
844 delete[] cache_data;
845 }
847 unsigned long StreamIO::tell()
848 {
849 return m_pos;
850 }
852 int StreamIO::getch()
853 {
854 // past end-of-file ?
855 if( m_pos > entry->size ) return -1;
857 // need to update cache ?
858 if( !cache_size || ( m_pos < cache_pos ) ||
859 ( m_pos >= cache_pos + cache_size ) )
860 updateCache();
862 // something bad if we don't get good cache
863 if( !cache_size ) return -1;
865 int data = cache_data[m_pos - cache_pos];
866 m_pos++;
868 return data;
869 }
871 unsigned long StreamIO::read( unsigned long pos, unsigned char* data, unsigned long maxlen )
872 {
873 // sanity checks
874 if( !data ) return 0;
875 if( maxlen == 0 ) return 0;
877 unsigned long totalbytes = 0;
879 if ( entry->size < io->header->threshold )
880 {
881 // small file
882 unsigned long index = pos / io->sbat->blockSize;
884 if( index >= blocks.size() ) return 0;
886 unsigned char* buf = new unsigned char[ io->sbat->blockSize ];
887 unsigned long offset = pos % io->sbat->blockSize;
888 while( totalbytes < maxlen )
889 {
890 if( index >= blocks.size() ) break;
891 io->loadSmallBlock( blocks[index], buf, io->bbat->blockSize );
892 unsigned long count = io->sbat->blockSize - offset;
893 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
894 memcpy( data+totalbytes, buf + offset, count );
895 totalbytes += count;
896 offset = 0;
897 index++;
898 }
899 delete[] buf;
901 }
902 else
903 {
904 // big file
905 unsigned long index = pos / io->bbat->blockSize;
907 if( index >= blocks.size() ) return 0;
909 unsigned char* buf = new unsigned char[ io->bbat->blockSize ];
910 unsigned long offset = pos % io->bbat->blockSize;
911 while( totalbytes < maxlen )
912 {
913 if( index >= blocks.size() ) break;
914 io->loadBigBlock( blocks[index], buf, io->bbat->blockSize );
915 unsigned long count = io->bbat->blockSize - offset;
916 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
917 memcpy( data+totalbytes, buf + offset, count );
918 totalbytes += count;
919 index++;
920 offset = 0;
921 }
922 delete [] buf;
924 }
926 return totalbytes;
927 }
929 unsigned long StreamIO::read( unsigned char* data, unsigned long maxlen )
930 {
931 unsigned long bytes = read( tell(), data, maxlen );
932 m_pos += bytes;
933 return bytes;
934 }
936 void StreamIO::updateCache()
937 {
938 // sanity check
939 if( !cache_data ) return;
941 cache_pos = m_pos - ( m_pos % cache_size );
942 unsigned long bytes = cache_size;
943 if( cache_pos + bytes > entry->size ) bytes = entry->size - cache_pos;
944 cache_size = read( cache_pos, cache_data, bytes );
945 }
948 // =========== Storage ==========
950 Storage::Storage( const std::stringstream &memorystream )
951 {
952 io = new StorageIO( this, memorystream );
953 }
955 Storage::~Storage()
956 {
957 delete io;
958 }
960 int Storage::result()
961 {
962 return io->result;
963 }
965 bool Storage::isOle()
966 {
967 return io->isOle();
968 }
970 // =========== Stream ==========
972 Stream::Stream( Storage* storage, const std::string& name )
973 {
974 io = storage->io->streamIO( name );
975 }
977 // FIXME tell parent we're gone
978 Stream::~Stream()
979 {
980 delete io;
981 }
983 unsigned long Stream::size()
984 {
985 return io ? io->entry->size : 0;
986 }
988 unsigned long Stream::read( unsigned char* data, unsigned long maxlen )
989 {
990 return io ? io->read( data, maxlen ) : 0;
991 }