kpresenter/powerpoint/libppt/pole.cpp
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#include <fstream>
30#include <iostream>
31#include <list>
32#include <string>
33#include <vector>
34
35#include "pole.h"
36
37// enable to activate debugging output
38// #define POLE_DEBUG
39
40namespace POLE
41{
42
43class Header
44{
45 public:
46 unsigned char id[8]; // signature, or magic identifier
47 unsigned b_shift; // bbat->blockSize = 1 << b_shift
48 unsigned s_shift; // sbat->blockSize = 1 << s_shift
49 unsigned num_bat; // blocks allocated for big bat
50 unsigned dirent_start; // starting block for directory info
51 unsigned threshold; // switch from small to big file (usually 4K)
52 unsigned sbat_start; // starting block index to store small bat
53 unsigned num_sbat; // blocks allocated for small bat
54 unsigned mbat_start; // starting block to store meta bat
55 unsigned num_mbat; // blocks allocated for meta bat
56 unsigned long bb_blocks[109];
57
58 Header();
59 bool valid();
60 void load( const unsigned char* buffer );
61 void save( unsigned char* buffer );
62 void debug();
63};
64
65class AllocTable
66{
67 public:
68 static const unsigned Eof;
69 static const unsigned Avail;
70 static const unsigned Bat;
71 static const unsigned MetaBat;
72 unsigned blockSize;
73 AllocTable();
74 void clear();
75 unsigned long count();
76 void resize( unsigned long newsize );
77 void preserve( unsigned long n );
78 void set( unsigned long index, unsigned long val );
79 unsigned unused();
80 void setChain( std::vector<unsigned long> );
81 std::vector<unsigned long> follow( unsigned long start );
82 unsigned long operator[](unsigned long index );
83 void load( const unsigned char* buffer, unsigned len );
84 void save( unsigned char* buffer );
85 unsigned size();
86 void debug();
87 private:
88 std::vector<unsigned long> data;
89 AllocTable( const AllocTable& );
90 AllocTable& operator=( const AllocTable& );
91};
92
93class DirEntry
94{
95 public:
96 bool valid; // false if invalid (should be skipped)
97 std::string name; // the name, not in unicode anymore
98 bool dir; // true if directory
99 unsigned long size; // size (not valid if directory)
100 unsigned long start; // starting block
101 unsigned prev; // previous sibling
102 unsigned next; // next sibling
103 unsigned child; // first child
104};
105
106class DirTree
107{
108 public:
109 static const unsigned End;
110 DirTree();
111 void clear();
112 unsigned entryCount();
113 DirEntry* entry( unsigned index );
114 DirEntry* entry( const std::string& name, bool create=false );
115 int indexOf( DirEntry* e );
116 int parent( unsigned index );
117 std::string fullName( unsigned index );
118 std::vector<unsigned> children( unsigned index );
119 void load( unsigned char* buffer, unsigned len );
120 void save( unsigned char* buffer );
121 unsigned size();
122 void debug();
123 private:
124 std::vector<DirEntry> entries;
125 DirTree( const DirTree& );
126 DirTree& operator=( const DirTree& );
127};
128
129class StorageIO
130{
131 public:
132 Storage* storage; // owner
133 std::string filename; // filename
134 std::fstream file; // associated with above name
135 int result; // result of operation
136 bool opened; // true if file is opened
137 unsigned long filesize; // size of the file
138
139 Header* header; // storage header
140 DirTree* dirtree; // directory tree
141 AllocTable* bbat; // allocation table for big blocks
142 AllocTable* sbat; // allocation table for small blocks
143
144 std::vector<unsigned long> sb_blocks; // blocks for "small" files
145
146 std::list<Stream*> streams;
147
148 StorageIO( Storage* storage, const char* filename );
149 ~StorageIO();
150
151 bool open();
152 void close();
153 void flush();
154 void load();
155 void create();
156
157 unsigned long loadBigBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
158
159 unsigned long loadBigBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
160
161 unsigned long loadSmallBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
162
163 unsigned long loadSmallBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
164
165 StreamIO* streamIO( const std::string& name );
166
167 private:
168 // no copy or assign
169 StorageIO( const StorageIO& );
170 StorageIO& operator=( const StorageIO& );
171
172};
173
174class StreamIO
175{
176 public:
177 StorageIO* io;
178 DirEntry* entry;
179 std::string fullName;
180 bool eof;
181 bool fail;
182
183 StreamIO( StorageIO* io, DirEntry* entry );
184 ~StreamIO();
185 unsigned long size();
186 void seek( unsigned long pos );
187 unsigned long tell();
188 int getch();
189 unsigned long read( unsigned char* data, unsigned long maxlen );
190 unsigned long read( unsigned long pos, unsigned char* data, unsigned long maxlen );
191
192
193 private:
194 std::vector<unsigned long> blocks;
195
196 // no copy or assign
197 StreamIO( const StreamIO& );
198 StreamIO& operator=( const StreamIO& );
199
200 // pointer for read
201 unsigned long m_pos;
202
203 // simple cache system to speed-up getch()
204 unsigned char* cache_data;
205 unsigned long cache_size;
206 unsigned long cache_pos;
207 void updateCache();
208};
209
210} // namespace POLE
211
212using namespace POLE;
213
214static inline unsigned long readU16( const unsigned char* ptr )
215{
216 return ptr[0]+(ptr[1]<<8);
217}
218
219static inline unsigned long readU32( const unsigned char* ptr )
220{
221 return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
222}
223
224static inline void writeU16( unsigned char* ptr, unsigned long data )
225{
226 ptr[0] = (unsigned char)(data & 0xff);
227 ptr[1] = (unsigned char)((data >> 8) & 0xff);
228}
229
230static inline void writeU32( unsigned char* ptr, unsigned long data )
231{
232 ptr[0] = (unsigned char)(data & 0xff);
233 ptr[1] = (unsigned char)((data >> 8) & 0xff);
234 ptr[2] = (unsigned char)((data >> 16) & 0xff);
235 ptr[3] = (unsigned char)((data >> 24) & 0xff);
236}
237
238static const unsigned char pole_magic[] =
239 { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
240
241// =========== Header ==========
242
243Header::Header()
244{
245 b_shift = 9;
246 s_shift = 6;
247 num_bat = 0;
248 dirent_start = 0;
249 threshold = 4096;
250 sbat_start = 0;
251 num_sbat = 0;
252 mbat_start = 0;
253 num_mbat = 0;
254
255 for( unsigned i = 0; i < 8; i++ )
256 id[i] = pole_magic[i];
257 for( unsigned i=0; i<109; i++ )
258 bb_blocks[i] = AllocTable::Avail;
259}
260
261bool Header::valid()
262{
263 if( threshold != 4096 ) return false;
264 if( num_bat == 0 ) return false;
265 if( (num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false;
266 if( (num_bat < 109) && (num_mbat != 0) ) return false;
267 if( s_shift > b_shift ) return false;
268 if( b_shift <= 6 ) return false;
269 if( b_shift >=31 ) return false;
270
271 return true;
272}
273
274void Header::load( const unsigned char* buffer )
275{
276 b_shift = readU16( buffer + 0x1e );
277 s_shift = readU16( buffer + 0x20 );
278 num_bat = readU32( buffer + 0x2c );
279 dirent_start = readU32( buffer + 0x30 );
280 threshold = readU32( buffer + 0x38 );
281 sbat_start = readU32( buffer + 0x3c );
282 num_sbat = readU32( buffer + 0x40 );
283 mbat_start = readU32( buffer + 0x44 );
284 num_mbat = readU32( buffer + 0x48 );
285
286 for( unsigned i = 0; i < 8; i++ )
287 id[i] = buffer[i];
288 for( unsigned i=0; i<109; i++ )
289 bb_blocks[i] = readU32( buffer + 0x4C+i*4 );
290}
291
292void Header::save( unsigned char* buffer )
293{
294 memset( buffer, 0, 0x4c );
295 memcpy( buffer, pole_magic, 8 ); // ole signature
296 writeU32( buffer + 8, 0 ); // unknown
297 writeU32( buffer + 12, 0 ); // unknown
298 writeU32( buffer + 16, 0 ); // unknown
299 writeU16( buffer + 24, 0x003e ); // revision ?
300 writeU16( buffer + 26, 3 ); // version ?
301 writeU16( buffer + 28, 0xfffe ); // unknown
302 writeU16( buffer + 0x1e, b_shift );
303 writeU16( buffer + 0x20, s_shift );
304 writeU32( buffer + 0x2c, num_bat );
305 writeU32( buffer + 0x30, dirent_start );
306 writeU32( buffer + 0x38, threshold );
307 writeU32( buffer + 0x3c, sbat_start );
308 writeU32( buffer + 0x40, num_sbat );
309 writeU32( buffer + 0x44, mbat_start );
310 writeU32( buffer + 0x48, num_mbat );
311
312 for( unsigned i=0; i<109; i++ )
313 writeU32( buffer + 0x4C+i*4, bb_blocks[i] );
314}
315
316void Header::debug()
317{
318 std::cout << std::endl;
319 std::cout << "b_shift " << b_shift << std::endl;
320 std::cout << "s_shift " << s_shift << std::endl;
321 std::cout << "num_bat " << num_bat << std::endl;
322 std::cout << "dirent_start " << dirent_start << std::endl;
323 std::cout << "threshold " << threshold << std::endl;
324 std::cout << "sbat_start " << sbat_start << std::endl;
325 std::cout << "num_sbat " << num_sbat << std::endl;
326 std::cout << "mbat_start " << mbat_start << std::endl;
327 std::cout << "num_mbat " << num_mbat << std::endl;
328
329 unsigned s = (num_bat<=109) ? num_bat : 109;
330 std::cout << "bat blocks: ";
331 for( unsigned i = 0; i < s; i++ )
332 std::cout << bb_blocks[i] << " ";
333 std::cout << std::endl;
334}
335
336// =========== AllocTable ==========
337
338const unsigned AllocTable::Avail = 0xffffffff;
339const unsigned AllocTable::Eof = 0xfffffffe;
340const unsigned AllocTable::Bat = 0xfffffffd;
341const unsigned AllocTable::MetaBat = 0xfffffffc;
342
343AllocTable::AllocTable()
344{
345 blockSize = 4096;
346 // initial size
347 resize( 128 );
348}
349
350unsigned long AllocTable::count()
351{
352 return data.size();
353}
354
355void AllocTable::resize( unsigned long newsize )
356{
357 unsigned oldsize = data.size();
358 data.resize( newsize );
359 if( newsize > oldsize )
360 for( unsigned i = oldsize; i<newsize; i++ )
361 data[i] = Avail;
362}
363
364// make sure there're still free blocks
365void AllocTable::preserve( unsigned long n )
366{
367 std::vector<unsigned long> pre;
368 for( unsigned i=0; i < n; i++ )
369 pre.push_back( unused() );
370}
371
372unsigned long AllocTable::operator[]( unsigned long index )
373{
374 unsigned long result;
375 result = data[index];
376 return result;
377}
378
379void AllocTable::set( unsigned long index, unsigned long value )
380{
381 if( index >= count() ) resize( index + 1);
382 data[ index ] = value;
383}
384
385void AllocTable::setChain( std::vector<unsigned long> chain )
386{
387 if( chain.size() )
388 {
389 for( unsigned i=0; i<chain.size()-1; i++ )
390 set( chain[i], chain[i+1] );
391 set( chain[ chain.size()-1 ], AllocTable::Eof );
392 }
393}
394
395// follow
396std::vector<unsigned long> AllocTable::follow( unsigned long start )
397{
398 std::vector<unsigned long> chain;
399
400 if( start >= count() ) return chain;
401
402 unsigned long p = start;
403 while( p < count() )
404 {
405 if( p == (unsigned long)Eof ) break;
406 if( p == (unsigned long)Bat ) break;
407 if( p == (unsigned long)MetaBat ) break;
408 if( p >= count() ) break;
409 chain.push_back( p );
410 if( data[p] >= count() ) break;
411 p = data[ p ];
412 }
413
414 return chain;
415}
416
417unsigned AllocTable::unused()
418{
419 // find first available block
420 for( unsigned i = 0; i < data.size(); i++ )
421 if( data[i] == Avail )
422 return i;
423
424 // completely full, so enlarge the table
425 unsigned block = data.size();
426 resize( data.size()+10 );
427 return block;
428}
429
430void AllocTable::load( const unsigned char* buffer, unsigned len )
431{
432 resize( len / 4 );
433 for( unsigned i = 0; i < count(); i++ )
434 set( i, readU32( buffer + i*4 ) );
435}
436
437// return space required to save this dirtree
438unsigned AllocTable::size()
439{
440 return count() * 4;
441}
442
443void AllocTable::save( unsigned char* buffer )
444{
445 for( unsigned i = 0; i < count(); i++ )
446 writeU32( buffer + i*4, data[i] );
447}
448
449void AllocTable::debug()
450{
451 std::cout << "block size " << data.size() << std::endl;
452 for( unsigned i=0; i< data.size(); i++ )
453 {
454 if( data[i] == Avail ) continue;
455 std::cout << i << ": ";
456 if( data[i] == Eof ) std::cout << "[eof]";
457 else if( data[i] == Bat ) std::cout << "[bat]";
458 else if( data[i] == MetaBat ) std::cout << "[metabat]";
459 else std::cout << data[i];
460 std::cout << std::endl;
461 }
462}
463
464// =========== DirTree ==========
465
466const unsigned DirTree::End = 0xffffffff;
467
468DirTree::DirTree()
469{
470 clear();
471}
472
473void DirTree::clear()
474{
475 // leave only root entry
476 entries.resize( 1 );
477 entries[0].valid = true;
478 entries[0].name = "Root Entry";
479 entries[0].dir = true;
480 entries[0].size = 0;
481 entries[0].start = End;
482 entries[0].prev = End;
483 entries[0].next = End;
484 entries[0].child = End;
485}
486
487unsigned DirTree::entryCount()
488{
489 return entries.size();
490}
491
492DirEntry* DirTree::entry( unsigned index )
493{
494 if( index >= entryCount() ) return (DirEntry*) 0;
495 return &entries[ index ];
496}
497
498int DirTree::indexOf( DirEntry* e )
499{
500 for( unsigned i = 0; i < entryCount(); i++ )
501 if( entry( i ) == e ) return i;
502
503 return -1;
504}
505
506int DirTree::parent( unsigned index )
507{
508 // brute-force, basically we iterate for each entries, find its children
509 // and check if one of the children is 'index'
510 for( unsigned j=0; j<entryCount(); j++ )
511 {
512 std::vector<unsigned> chi = children( j );
513 for( unsigned i=0; i<chi.size();i++ )
514 if( chi[i] == index )
515 return j;
516 }
517
518 return -1;
519}
520
521std::string DirTree::fullName( unsigned index )
522{
523 // don't use root name ("Root Entry"), just give "/"
524 if( index == 0 ) return "/";
525
526 std::string result = entry( index )->name;
527 result.insert( 0, "/" );
528 int p = parent( index );
529 DirEntry * _entry = 0;
530 while( p > 0 )
531 {
532 _entry = entry( p );
533 if (_entry->dir && _entry->valid)
534 {
535 result.insert( 0, _entry->name);
536 result.insert( 0, "/" );
537 }
538 --p;
539 index = p;
540 if( index <= 0 ) break;
541 }
542 return result;
543}
544
545// given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
546// if not found and create is false, return 0
547// if create is true, a new entry is returned
548DirEntry* DirTree::entry( const std::string& name, bool create )
549{
550 if( !name.length() ) return (DirEntry*)0;
551
552 // quick check for "/" (that's root)
553 if( name == "/" ) return entry( 0 );
554
555 // split the names, e.g "/ObjectPool/_1020961869" will become:
556 // "ObjectPool" and "_1020961869"
557 std::list<std::string> names;
558 std::string::size_type start = 0, end = 0;
559 if( name[0] == '/' ) start++;
560 while( start < name.length() )
561 {
562 end = name.find_first_of( '/', start );
563 if( end == std::string::npos ) end = name.length();
564 names.push_back( name.substr( start, end-start ) );
565 start = end+1;
566 }
567
568 // start from root
569 int index = 0 ;
570
571 // trace one by one
572 std::list<std::string>::iterator it;
573
574 for( it = names.begin(); it != names.end(); ++it )
575 {
576 // find among the children of index
577 std::vector<unsigned> chi = children( index );
578 unsigned child = 0;
579 for( unsigned i = 0; i < chi.size(); i++ )
580 {
581 DirEntry* ce = entry( chi[i] );
582 if( ce )
583 if( ce->valid && ( ce->name.length()>1 ) )
584 if( ce->name == *it )
585 child = chi[i];
586 }
587
588 // traverse to the child
589 if( child > 0 ) index = child;
590 else
591 {
592 // not found among children
593 if( !create ) return (DirEntry*)0;
594
595 // create a new entry
596 unsigned parent = index;
597 entries.push_back( DirEntry() );
598 index = entryCount()-1;
599 DirEntry* e = entry( index );
600 e->valid = true;
601 e->name = *it;
602 e->dir = false;
603 e->size = 0;
604 e->start = 0;
605 e->child = End;
606 e->prev = End;
607 e->next = entry(parent)->child;
608 entry(parent)->child = index;
609 }
610 }
611
612 return entry( index );
613}
614
615// helper function: recursively find siblings of index
616void dirtree_find_siblings( DirTree* dirtree, std::vector<unsigned>& result,
617 unsigned index )
618{
619 DirEntry* e = dirtree->entry( index );
620 if( !e ) return;
621 if( !e->valid ) return;
622
623 // prevent infinite loop
624 for( unsigned i = 0; i < result.size(); i++ )
625 if( result[i] == index ) return;
626
627 // add myself
628 result.push_back( index );
629
630 // visit previous sibling, don't go infinitely
631 unsigned prev = e->prev;
632 if( ( prev > 0 ) && ( prev < dirtree->entryCount() ) )
633 {
634 for( unsigned i = 0; i < result.size(); i++ )
635 if( result[i] == prev ) prev = 0;
636 if( prev ) dirtree_find_siblings( dirtree, result, prev );
637 }
638
639 // visit next sibling, don't go infinitely
640 unsigned next = e->next;
641 if( ( next > 0 ) && ( next < dirtree->entryCount() ) )
642 {
643 for( unsigned i = 0; i < result.size(); i++ )
644 if( result[i] == next ) next = 0;
645 if( next ) dirtree_find_siblings( dirtree, result, next );
646 }
647}
648
649std::vector<unsigned> DirTree::children( unsigned index )
650{
651 std::vector<unsigned> result;
652
653 DirEntry* e = entry( index );
654 if( e ) if( e->valid && e->child < entryCount() )
655 dirtree_find_siblings( this, result, e->child );
656
657 return result;
658}
659
660void DirTree::load( unsigned char* buffer, unsigned size )
661{
662 entries.clear();
663
664 for( unsigned i = 0; i < size/128; i++ )
665 {
666 unsigned p = i * 128;
667
668 // would be < 32 if first char in the name isn't printable
669 unsigned prefix = 32;
670
671 // parse name of this entry, which stored as Unicode 16-bit
672 std::string name;
673 int name_len = readU16( buffer + 0x40+p );
674 if( name_len > 64 ) name_len = 64;
675 for( int j=0; ( buffer[j+p]) && (j<name_len); j+= 2 )
676 name.append( 1, buffer[j+p] );
677
678 // first char isn't printable ? remove it...
679 if( buffer[p] < 32 )
680 {
681 prefix = buffer[0];
682 name.erase( 0,1 );
683 }
684
685 // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
686 unsigned type = buffer[ 0x42 + p];
687
688 DirEntry e;
689 e.valid = true;
690 e.name = name;
691 e.start = readU32( buffer + 0x74+p );
692 e.size = readU32( buffer + 0x78+p );
693 e.prev = readU32( buffer + 0x44+p );
694 e.next = readU32( buffer + 0x48+p );
695 e.child = readU32( buffer + 0x4C+p );
696 e.dir = ( type!=2 );
697
698 // sanity checks
699 if( (type != 2) && (type != 1 ) && (type != 5 ) ) e.valid = false;
700 if( name_len < 1 ) e.valid = false;
701
702 entries.push_back( e );
703 }
704}
705
706// return space required to save this dirtree
707unsigned DirTree::size()
708{
709 return entryCount() * 128;
710}
711
712void DirTree::save( unsigned char* buffer )
713{
714 memset( buffer, 0, size() );
715
716 // root is fixed as "Root Entry"
717 DirEntry* root = entry( 0 );
718 std::string name = "Root Entry";
719 for( unsigned j = 0; j < name.length(); j++ )
720 buffer[ j*2 ] = name[j];
721 writeU16( buffer + 0x40, name.length()*2 + 2 );
722 writeU32( buffer + 0x74, 0xffffffff );
723 writeU32( buffer + 0x78, 0 );
724 writeU32( buffer + 0x44, 0xffffffff );
725 writeU32( buffer + 0x48, 0xffffffff );
726 writeU32( buffer + 0x4c, root->child );
727 buffer[ 0x42 ] = 5;
728 buffer[ 0x43 ] = 1;
729
730 for( unsigned i = 1; i < entryCount(); i++ )
731 {
732 DirEntry* e = entry( i );
733 if( !e ) continue;
734 if( e->dir )
735 {
736 e->start = 0xffffffff;
737 e->size = 0;
738 }
739
740 // max length for name is 32 chars
741 std::string name = e->name;
742 if( name.length() > 32 )
743 name.erase( 32, name.length() );
744
745 // write name as Unicode 16-bit
746 for( unsigned j = 0; j < name.length(); j++ )
747 buffer[ i*128 + j*2 ] = name[j];
748
749 writeU16( buffer + i*128 + 0x40, name.length()*2 + 2 );
750 writeU32( buffer + i*128 + 0x74, e->start );
751 writeU32( buffer + i*128 + 0x78, e->size );
752 writeU32( buffer + i*128 + 0x44, e->prev );
753 writeU32( buffer + i*128 + 0x48, e->next );
754 writeU32( buffer + i*128 + 0x4c, e->child );
755 buffer[ i*128 + 0x42 ] = e->dir ? 1 : 2;
756 buffer[ i*128 + 0x43 ] = 1; // always black
757 }
758}
759
760void DirTree::debug()
761{
762 for( unsigned i = 0; i < entryCount(); i++ )
763 {
764 DirEntry* e = entry( i );
765 if( !e ) continue;
766 std::cout << i << ": ";
767 if( !e->valid ) std::cout << "INVALID ";
768 std::cout << e->name << " ";
769 if( e->dir ) std::cout << "(Dir) ";
770 else std::cout << "(File) ";
771 std::cout << e->size << " ";
772 std::cout << "s:" << e->start << " ";
773 std::cout << "(";
774 if( e->child == End ) std::cout << "-"; else std::cout << e->child;
775 std::cout << " ";
776 if( e->prev == End ) std::cout << "-"; else std::cout << e->prev;
777 std::cout << ":";
778 if( e->next == End ) std::cout << "-"; else std::cout << e->next;
779 std::cout << ")";
780 std::cout << std::endl;
781 }
782}
783
784// =========== StorageIO ==========
785
786StorageIO::StorageIO( Storage* st, const char* fname )
787{
788 storage = st;
789 filename = fname;
790 result = Storage::Ok;
791 opened = false;
792
793 header = new Header();
794 dirtree = new DirTree();
795 bbat = new AllocTable();
796 sbat = new AllocTable();
797
798 filesize = 0;
799 bbat->blockSize = 1 << header->b_shift;
800 sbat->blockSize = 1 << header->s_shift;
801}
802
803StorageIO::~StorageIO()
804{
805 if( opened ) close();
806 delete sbat;
807 delete bbat;
808 delete dirtree;
809 delete header;
810}
811
812bool StorageIO::open()
813{
814 // already opened ? close first
815 if( opened ) close();
816
817 load();
818
819 return result == Storage::Ok;
820}
821
822void StorageIO::load()
823{
824 unsigned char* buffer = 0;
825 unsigned long buflen = 0;
826 std::vector<unsigned long> blocks;
827
828 // open the file, check for error
829 result = Storage::OpenFailed;
830 file.open( filename.c_str(), std::ios::binary | std::ios::in );
831 if( !file.good() ) return;
832
833 // find size of input file
834 file.seekg( 0, std::ios::end );
835 filesize = file.tellg();
836
837 // load header
838 buffer = new unsigned char[512];
839 file.seekg( 0 );
840 file.read( (char*)buffer, 512 );
841 header->load( buffer );
842 delete[] buffer;
843
844 // check OLE magic id
845 result = Storage::NotOLE;
846 for( unsigned i=0; i<8; i++ )
847 if( header->id[i] != pole_magic[i] )
848 return;
849
850 // sanity checks
851 result = Storage::BadOLE;
852 if( !header->valid() ) return;
853 if( header->threshold != 4096 ) return;
854
855 // important block size
856 bbat->blockSize = 1 << header->b_shift;
857 sbat->blockSize = 1 << header->s_shift;
858
859 // find blocks allocated to store big bat
860 // the first 109 blocks are in header, the rest in meta bat
861 blocks.clear();
862 blocks.resize( header->num_bat );
863 for( unsigned i = 0; i < 109; i++ )
864 if( i >= header->num_bat ) break;
865 else blocks[i] = header->bb_blocks[i];
866 if( (header->num_bat > 109) && (header->num_mbat > 0) )
867 {
868 unsigned char* buffer2 = new unsigned char[ bbat->blockSize ];
869 unsigned k = 109;
870 unsigned mblock = header->mbat_start;
871 for( unsigned r = 0; r < header->num_mbat; r++ )
872 {
873 loadBigBlock( mblock, buffer2, bbat->blockSize );
874 for( unsigned s=0; s < bbat->blockSize-4; s+=4 )
875 {
876 if( k >= header->num_bat ) break;
877 else blocks[k++] = readU32( buffer2 + s );
878 }
879 mblock = readU32( buffer2 + bbat->blockSize-4 );
880 }
881 delete[] buffer2;
882 }
883
884 // load big bat
885 buflen = blocks.size()*bbat->blockSize;
886 if( buflen > 0 )
887 {
888 buffer = new unsigned char[ buflen ];
889 loadBigBlocks( blocks, buffer, buflen );
890 bbat->load( buffer, buflen );
891 delete[] buffer;
892 }
893
894 // load small bat
895 blocks.clear();
896 blocks = bbat->follow( header->sbat_start );
897 buflen = blocks.size()*bbat->blockSize;
898 if( buflen > 0 )
899 {
900 buffer = new unsigned char[ buflen ];
901 loadBigBlocks( blocks, buffer, buflen );
902 sbat->load( buffer, buflen );
903 delete[] buffer;
904 }
905
906 // load directory tree
907 blocks.clear();
908 blocks = bbat->follow( header->dirent_start );
909 buflen = blocks.size()*bbat->blockSize;
910 buffer = new unsigned char[ buflen ];
911 loadBigBlocks( blocks, buffer, buflen );
912 dirtree->load( buffer, buflen );
913 unsigned sb_start = readU32( buffer + 0x74 );
914 delete[] buffer;
915
916 // fetch block chain as data for small-files
917 sb_blocks = bbat->follow( sb_start ); // small files
918
919 // for troubleshooting, just enable this block
920#if 0
921 header->debug();
922 sbat->debug();
923 bbat->debug();
924 dirtree->debug();
925#endif
926
927 // so far so good
928 result = Storage::Ok;
929 opened = true;
930}
931
932void StorageIO::create()
933{
934 // std::cout << "Creating " << filename << std::endl;
935
936 file.open( filename.c_str(), std::ios::out|std::ios::binary );
937 if( !file.good() )
938 {
939 std::cerr << "Can't create " << filename << std::endl;
940 result = Storage::OpenFailed;
941 return;
942 }
943
944 // so far so good
945 opened = true;
946 result = Storage::Ok;
947}
948
949void StorageIO::flush()
950{
951 /* Note on Microsoft implementation:
952 - directory entries are stored in the last block(s)
953 - BATs are as second to the last
954 - Meta BATs are third to the last
955 */
956}
957
958void StorageIO::close()
959{
960 if( !opened ) return;
961
962 file.close();
963 opened = false;
964
965 std::list<Stream*>::iterator it;
966 for( it = streams.begin(); it != streams.end(); ++it )
967 delete *it;
968}
969
970StreamIO* StorageIO::streamIO( const std::string& name )
971{
972 // sanity check
973 if( !name.length() ) return (StreamIO*)0;
974
975 // search in the entries
976 DirEntry* entry = dirtree->entry( name );
977 //if( entry) std::cout << "FOUND\n";
978 if( !entry ) return (StreamIO*)0;
979 //if( !entry->dir ) std::cout << " NOT DIR\n";
980 if( entry->dir ) return (StreamIO*)0;
981
982 StreamIO* result = new StreamIO( this, entry );
983 result->fullName = name;
984
985 return result;
986}
987
988unsigned long StorageIO::loadBigBlocks( std::vector<unsigned long> blocks,
989 unsigned char* data, unsigned long maxlen )
990{
991 // sentinel
992 if( !data ) return 0;
993 if( !file.good() ) return 0;
994 if( blocks.size() < 1 ) return 0;
995 if( maxlen == 0 ) return 0;
996
997 // read block one by one, seems fast enough
998 unsigned long bytes = 0;
999 for( unsigned long i=0; (i < blocks.size() ) & ( bytes<maxlen ); i++ )
1000 {
1001 unsigned long block = blocks[i];
1002 unsigned long pos = bbat->blockSize * ( block+1 );
1003 unsigned long p = (bbat->blockSize < maxlen-bytes) ? bbat->blockSize : maxlen-bytes;
1004 if( pos + p > filesize ) p = filesize - pos;
1005 file.seekg( pos );
1006 file.read( (char*)data + bytes, p );
1007 bytes += p;
1008 }
1009
1010 return bytes;
1011}
1012
1013unsigned long StorageIO::loadBigBlock( unsigned long block,
1014 unsigned char* data, unsigned long maxlen )
1015{
1016 // sentinel
1017 if( !data ) return 0;
1018 if( !file.good() ) return 0;
1019
1020 // wraps call for loadBigBlocks
1021 std::vector<unsigned long> blocks;
1022 blocks.resize( 1 );
1023 blocks[ 0 ] = block;
1024
1025 return loadBigBlocks( blocks, data, maxlen );
1026}
1027
1028// return number of bytes which has been read
1029unsigned long StorageIO::loadSmallBlocks( std::vector<unsigned long> blocks,
1030 unsigned char* data, unsigned long maxlen )
1031{
1032 // sentinel
1033 if( !data ) return 0;
1034 if( !file.good() ) return 0;
1035 if( blocks.size() < 1 ) return 0;
1036 if( maxlen == 0 ) return 0;
1037
1038 // our own local buffer
1039 unsigned char* buf = new unsigned char[ bbat->blockSize ];
1040
1041 // read small block one by one
1042 unsigned long bytes = 0;
1043 for( unsigned long i=0; ( i<blocks.size() ) & ( bytes<maxlen ); i++ )
1044 {
1045 unsigned long block = blocks[i];
1046
1047 // find where the small-block exactly is
1048 unsigned long pos = block * sbat->blockSize;
1049 unsigned long bbindex = pos / bbat->blockSize;
1050 if( bbindex >= sb_blocks.size() ) break;
1051
1052 loadBigBlock( sb_blocks[ bbindex ], buf, bbat->blockSize );
1053
1054 // copy the data
1055 unsigned offset = pos % bbat->blockSize;
1056 unsigned long p = (maxlen-bytes < bbat->blockSize-offset ) ? maxlen-bytes : bbat->blockSize-offset;
1057 p = (sbat->blockSize<p ) ? sbat->blockSize : p;
1058 memcpy( data + bytes, buf + offset, p );
1059 bytes += p;
1060 }
1061
1062 delete[] buf;
1063
1064 return bytes;
1065}
1066
1067unsigned long StorageIO::loadSmallBlock( unsigned long block,
1068 unsigned char* data, unsigned long maxlen )
1069{
1070 // sentinel
1071 if( !data ) return 0;
1072 if( !file.good() ) return 0;
1073
1074 // wraps call for loadSmallBlocks
1075 std::vector<unsigned long> blocks;
1076 blocks.resize( 1 );
1077 blocks.assign( 1, block );
1078
1079 return loadSmallBlocks( blocks, data, maxlen );
1080}
1081
1082// =========== StreamIO ==========
1083
1084StreamIO::StreamIO( StorageIO* s, DirEntry* e)
1085{
1086 io = s;
1087 entry = e;
1088 eof = false;
1089 fail = false;
1090
1091 m_pos = 0;
1092
1093 if( entry->size >= io->header->threshold )
1094 blocks = io->bbat->follow( entry->start );
1095 else
1096 blocks = io->sbat->follow( entry->start );
1097
1098 // prepare cache
1099 cache_pos = 0;
1100 cache_size = 4096; // optimal ?
1101 cache_data = new unsigned char[cache_size];
1102 updateCache();
1103}
1104
1105// FIXME tell parent we're gone
1106StreamIO::~StreamIO()
1107{
1108 delete[] cache_data;
1109}
1110
1111void StreamIO::seek( unsigned long pos )
1112{
1113 m_pos = pos;
1114}
1115
1116unsigned long StreamIO::tell()
1117{
1118 return m_pos;
1119}
1120
1121int StreamIO::getch()
1122{
1123 // past end-of-file ?
1124 if( m_pos > entry->size ) return -1;
1125
1126 // need to update cache ?
1127 if( !cache_size || ( m_pos < cache_pos ) ||
1128 ( m_pos >= cache_pos + cache_size ) )
1129 updateCache();
1130
1131 // something bad if we don't get good cache
1132 if( !cache_size ) return -1;
1133
1134 int data = cache_data[m_pos - cache_pos];
1135 m_pos++;
1136
1137 return data;
1138}
1139
1140unsigned long StreamIO::read( unsigned long pos, unsigned char* data, unsigned long maxlen )
1141{
1142 // sanity checks
1143 if( !data ) return 0;
1144 if( maxlen == 0 ) return 0;
1145
1146 unsigned long totalbytes = 0;
1147
1148 if ( entry->size < io->header->threshold )
1149 {
1150 // small file
1151 unsigned long index = pos / io->sbat->blockSize;
1152
1153 if( index >= blocks.size() ) return 0;
1154
1155 unsigned char* buf = new unsigned char[ io->sbat->blockSize ];
1156 unsigned long offset = pos % io->sbat->blockSize;
1157 while( totalbytes < maxlen )
1158 {
1159 if( index >= blocks.size() ) break;
1160 io->loadSmallBlock( blocks[index], buf, io->bbat->blockSize );
1161 unsigned long count = io->sbat->blockSize - offset;
1162 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
1163 memcpy( data+totalbytes, buf + offset, count );
1164 totalbytes += count;
1165 offset = 0;
1166 index++;
1167 }
1168 delete[] buf;
1169
1170 }
1171 else
1172 {
1173 // big file
1174 unsigned long index = pos / io->bbat->blockSize;
1175
1176 if( index >= blocks.size() ) return 0;
1177
1178 unsigned char* buf = new unsigned char[ io->bbat->blockSize ];
1179 unsigned long offset = pos % io->bbat->blockSize;
1180 while( totalbytes < maxlen )
1181 {
1182 if( index >= blocks.size() ) break;
1183 io->loadBigBlock( blocks[index], buf, io->bbat->blockSize );
1184 unsigned long count = io->bbat->blockSize - offset;
1185 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
1186 memcpy( data+totalbytes, buf + offset, count );
1187 totalbytes += count;
1188 index++;
1189 offset = 0;
1190 }
1191 delete [] buf;
1192
1193 }
1194
1195 return totalbytes;
1196}
1197
1198unsigned long StreamIO::read( unsigned char* data, unsigned long maxlen )
1199{
1200 unsigned long bytes = read( tell(), data, maxlen );
1201 m_pos += bytes;
1202 return bytes;
1203}
1204
1205void StreamIO::updateCache()
1206{
1207 // sanity check
1208 if( !cache_data ) return;
1209
1210 cache_pos = m_pos - ( m_pos % cache_size );
1211 unsigned long bytes = cache_size;
1212 if( cache_pos + bytes > entry->size ) bytes = entry->size - cache_pos;
1213 cache_size = read( cache_pos, cache_data, bytes );
1214}
1215
1216
1217// =========== Storage ==========
1218
1219Storage::Storage( const char* filename )
1220{
1221 io = new StorageIO( this, filename );
1222}
1223
1224Storage::~Storage()
1225{
1226 delete io;
1227}
1228
1229int Storage::result()
1230{
1231 return io->result;
1232}
1233
1234bool Storage::open()
1235{
1236 return io->open();
1237}
1238
1239void Storage::close()
1240{
1241 io->close();
1242}
1243
1244std::list<std::string> Storage::entries( const std::string& path )
1245{
1246 std::list<std::string> result;
1247 DirTree* dt = io->dirtree;
1248 DirEntry* e = dt->entry( path, false );
1249 if( e && e->dir )
1250 {
1251 unsigned parent = dt->indexOf( e );
1252 std::vector<unsigned> children = dt->children( parent );
1253 for( unsigned i = 0; i < children.size(); i++ )
1254 result.push_back( dt->entry( children[i] )->name );
1255 }
1256
1257 return result;
1258}
1259
1260bool Storage::isDirectory( const std::string& name )
1261{
1262 DirEntry* e = io->dirtree->entry( name, false );
1263 return e ? e->dir : false;
1264}
1265
1266// =========== Stream ==========
1267
1268Stream::Stream( Storage* storage, const std::string& name )
1269{
1270 io = storage->io->streamIO( name );
1271}
1272
1273// FIXME tell parent we're gone
1274Stream::~Stream()
1275{
1276 delete io;
1277}
1278
1279std::string Stream::fullName()
1280{
1281 return io ? io->fullName : std::string();
1282}
1283
1284unsigned long Stream::tell()
1285{
1286 return io ? io->tell() : 0;
1287}
1288
1289void Stream::seek( unsigned long newpos )
1290{
1291 if( io ) io->seek( newpos );
1292}
1293
1294unsigned long Stream::size()
1295{
1296 return io ? io->entry->size : 0;
1297}
1298
1299int Stream::getch()
1300{
1301 return io ? io->getch() : 0;
1302}
1303
1304unsigned long Stream::read( unsigned char* data, unsigned long maxlen )
1305{
1306 return io ? io->read( data, maxlen ) : 0;
1307}
1308
1309bool Stream::eof()
1310{
1311 return io ? io->eof : false;
1312}
1313
1314bool Stream::fail()
1315{
1316 return io ? io->fail : true;
1317}