Code

Cleaned up virtual destructor problem and compile warnings.
[inkscape.git] / src / extension / internal / libwpg / WPGOLEStream.cpp
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];
55     
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
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
124   public:
125     Storage* storage;         // owner
126     std::stringstream buf;
127     int result;               // result of operation
128     unsigned long bufsize;    // size of the buffer
129     
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
134     
135     std::vector<unsigned long> sb_blocks; // blocks for "small" files
136        
137     std::list<Stream*> streams;
139     StorageIO( Storage* storage, const std::stringstream &memorystream );
140     ~StorageIO();
141     
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 );
152     
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
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 )
203   return ptr[0]+(ptr[1]<<8);
206 static inline unsigned long readU32( const unsigned char* ptr )
208   return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
211 static const unsigned char wpgole_magic[] = 
212  { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
214 // =========== Header ==========
216 Header::Header()
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;
234 bool Header::valid()
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;
243   
244   return true;
247 void Header::load( const unsigned char* buffer )
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 );
258   
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 );
264  
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()
274   blockSize = 4096;
275   // initial size
276   resize( 128 );
279 unsigned long AllocTable::count()
281   return data.size();
284 void AllocTable::resize( unsigned long newsize )
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;
293 // make sure there're still free blocks
294 void AllocTable::preserve( unsigned long n )
296   std::vector<unsigned long> pre;
297   for( unsigned i=0; i < n; i++ )
298     pre.push_back( unused() );
301 unsigned long AllocTable::operator[]( unsigned long index )
303   unsigned long result;
304   result = data[index];
305   return result;
308 void AllocTable::set( unsigned long index, unsigned long value )
310   if( index >= count() ) resize( index + 1);
311   data[ index ] = value;
314 void AllocTable::setChain( std::vector<unsigned long> chain )
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   }
324 // follow 
325 std::vector<unsigned long> AllocTable::follow( unsigned long start )
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;
346 unsigned AllocTable::unused()
348   // find first available block
349   for( unsigned i = 0; i < data.size(); i++ )
350     if( data[i] == Avail )
351       return i;
352   
353   // completely full, so enlarge the table
354   unsigned block = data.size();
355   resize( data.size()+10 );
356   return block;      
359 void AllocTable::load( const unsigned char* buffer, unsigned len )
361   resize( len / 4 );
362   for( unsigned i = 0; i < count(); i++ )
363     set( i, readU32( buffer + i*4 ) );
366 // =========== DirTree ==========
368 const unsigned DirTree::End = 0xffffffff;
370 DirTree::DirTree()
372   clear();
375 void DirTree::clear()
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;
389 unsigned DirTree::entryCount()
391   return entries.size();
394 DirEntry* DirTree::entry( unsigned index )
396   if( index >= entryCount() ) return (DirEntry*) 0;
397   return &entries[ index ];
400 int DirTree::parent( unsigned index )
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   }
411         
412   return -1;
415 std::string DirTree::fullName( unsigned index )
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;
439 // given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
440 DirEntry* DirTree::entry( const std::string& name )
443    if( !name.length() ) return (DirEntry*)0;
444  
445    // quick check for "/" (that's root)
446    if( name == "/" ) return entry( 0 );
447    
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    }
460   
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      }
480      
481      // traverse to the child
482      if( child > 0 ) index = child;
483      else return (DirEntry*)0;
484    }
486    return entry( index );
489 // helper function: recursively find siblings of index
490 void dirtree_find_siblings( DirTree* dirtree, std::vector<unsigned>& result, 
491   unsigned index )
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 );
503   
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   }
512     
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   }
523 std::vector<unsigned> DirTree::children( unsigned index )
525   std::vector<unsigned> result;
526   
527   DirEntry* e = entry( index );
528   if( e ) if( e->valid && e->child < entryCount() )
529     dirtree_find_siblings( this, result, e->child );
530     
531   return result;
534 void DirTree::load( unsigned char* buffer, unsigned size )
536   entries.clear();
537   
538   for( unsigned i = 0; i < size/128; i++ )
539   {
540     unsigned p = i * 128;
541     
542     // would be < 32 if first char in the name isn't printable
543     unsigned prefix = 32;
544     
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] );
551       
552     // first char isn't printable ? remove it...
553     if( buffer[p] < 32 )
554     { 
555       prefix = buffer[0]; 
556       name.erase( 0,1 ); 
557     }
558     
559     // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
560     unsigned type = buffer[ 0x42 + p];
561     
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 );
571     
572     // sanity checks
573     if( (type != 2) && (type != 1 ) && (type != 5 ) ) e.valid = false;
574     if( name_len < 1 ) e.valid = false;
575     
576     entries.push_back( e );
577   }  
580 // =========== StorageIO ==========
582 StorageIO::StorageIO( Storage* st, const std::stringstream &memorystream ) :
583         buf( memorystream.str(), std::ios::binary | std::ios::in )
585   storage = st;
586   result = Storage::Ok;
587   
588   header = new Header();
589   dirtree = new DirTree();
590   bbat = new AllocTable();
591   sbat = new AllocTable();
592   
593   bufsize = 0;
594   bbat->blockSize = 1 << header->b_shift;
595   sbat->blockSize = 1 << header->s_shift;
598 StorageIO::~StorageIO()
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;
610 bool StorageIO::isOle()
612   load();
613   return (result == Storage::Ok);
616 void StorageIO::load()
618   unsigned char* buffer = 0;
619   unsigned long buflen = 0;
620   std::vector<unsigned long> blocks;
621   
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;
638  
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;
647   
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   }  
692   
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;
702   
703   // fetch block chain as data for small-files
704   sb_blocks = bbat->follow( sb_start ); // small files
705   
706   // so far so good
707   result = Storage::Ok;
710 StreamIO* StorageIO::streamIO( const std::string& name )
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;
724   
725   return result;
728 unsigned long StorageIO::loadBigBlocks( std::vector<unsigned long> blocks,
729   unsigned char* data, unsigned long maxlen )
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;
752 unsigned long StorageIO::loadBigBlock( unsigned long block,
753   unsigned char* data, unsigned long maxlen )
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;
762   
763   return loadBigBlocks( blocks, data, maxlen );
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 )
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   }
798   
799   delete[] buf;
801   return bytes;
804 unsigned long StorageIO::loadSmallBlock( unsigned long block,
805   unsigned char* data, unsigned long maxlen )
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 );
818 // =========== StreamIO ==========
820 StreamIO::StreamIO( StorageIO* s, DirEntry* e)
822   io = s;
823   entry = e;
824   eof = false;
825   fail = false;
826   
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();
841 // FIXME tell parent we're gone
842 StreamIO::~StreamIO()
844   delete[] cache_data;  
847 unsigned long StreamIO::tell()
849   return m_pos;
852 int StreamIO::getch()
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;
871 unsigned long StreamIO::read( unsigned long pos, unsigned char* data, unsigned long maxlen )
873   // sanity checks
874   if( !data ) return 0;
875   if( maxlen == 0 ) return 0;
877   unsigned long totalbytes = 0;
878   
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;
906     
907     if( index >= blocks.size() ) return 0;
908     
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;
929 unsigned long StreamIO::read( unsigned char* data, unsigned long maxlen )
931   unsigned long bytes = read( tell(), data, maxlen );
932   m_pos += bytes;
933   return bytes;
936 void StreamIO::updateCache()
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 );
948 // =========== Storage ==========
950 Storage::Storage( const std::stringstream &memorystream )
952   io = new StorageIO( this, memorystream );
955 Storage::~Storage()
957   delete io;
960 int Storage::result()
962   return io->result;
965 bool Storage::isOle()
967   return io->isOle();
970 // =========== Stream ==========
972 Stream::Stream( Storage* storage, const std::string& name )
974   io = storage->io->streamIO( name );
977 // FIXME tell parent we're gone
978 Stream::~Stream()
980   delete io;
983 unsigned long Stream::size()
985   return io ? io->entry->size : 0;
988 unsigned long Stream::read( unsigned char* data, unsigned long maxlen )
990   return io ? io->read( data, maxlen ) : 0;