kword/hancomword/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 <string.h>
36
37#include "pole.h"
38
39// enable to activate debugging output
40// #define POLE_DEBUG
41
42namespace POLE
43{
44
45class Header
46{
47 public:
48 unsigned char id[8]; // signature, or magic identifier
49 unsigned b_shift; // bbat->blockSize = 1 << b_shift
50 unsigned s_shift; // sbat->blockSize = 1 << s_shift
51 unsigned num_bat; // blocks allocated for big bat
52 unsigned dirent_start; // starting block for directory info
53 unsigned threshold; // switch from small to big file (usually 4K)
54 unsigned sbat_start; // starting block index to store small bat
55 unsigned num_sbat; // blocks allocated for small bat
56 unsigned mbat_start; // starting block to store meta bat
57 unsigned num_mbat; // blocks allocated for meta bat
58 unsigned long bb_blocks[109];
59
60 Header();
61 bool valid();
62 void load( const unsigned char* buffer );
63 void save( unsigned char* buffer );
64 void debug();
65};
66
67class AllocTable
68{
69 public:
70 static const unsigned Eof;
71 static const unsigned Avail;
72 static const unsigned Bat;
73 static const unsigned MetaBat;
74 unsigned blockSize;
75 AllocTable();
76 void clear();
77 unsigned long count();
78 void resize( unsigned long newsize );
79 void preserve( unsigned long n );
80 void set( unsigned long index, unsigned long val );
81 unsigned unused();
82 void setChain( std::vector<unsigned long> );
83 std::vector<unsigned long> follow( unsigned long start );
84 unsigned long operator[](unsigned long index );
85 void load( const unsigned char* buffer, unsigned len );
86 void save( unsigned char* buffer );
87 unsigned size();
88 void debug();
89 private:
90 std::vector<unsigned long> data;
91 AllocTable( const AllocTable& );
92 AllocTable& operator=( const AllocTable& );
93};
94
95class DirEntry
96{
97 public:
98 bool valid; // false if invalid (should be skipped)
99 std::string name; // the name, not in unicode anymore
100 bool dir; // true if directory
101 unsigned long size; // size (not valid if directory)
102 unsigned long start; // starting block
103 unsigned prev; // previous sibling
104 unsigned next; // next sibling
105 unsigned child; // first child
106};
107
108class DirTree
109{
110 public:
111 static const unsigned End;
112 DirTree();
113 void clear();
114 unsigned entryCount();
115 DirEntry* entry( unsigned index );
116 DirEntry* entry( const std::string& name, bool create=false );
117 int indexOf( DirEntry* e );
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 unsigned size();
124 void debug();
125 private:
126 std::vector<DirEntry> entries;
127 DirTree( const DirTree& );
128 DirTree& operator=( const DirTree& );
129};
130
131class StorageIO
132{
133 public:
134 Storage* storage; // owner
135 std::string filename; // filename
136 std::fstream file; // associated with above name
137 int result; // result of operation
138 bool opened; // true if file is opened
139 unsigned long filesize; // size of the file
140
141 Header* header; // storage header
142 DirTree* dirtree; // directory tree
143 AllocTable* bbat; // allocation table for big blocks
144 AllocTable* sbat; // allocation table for small blocks
145
146 std::vector<unsigned long> sb_blocks; // blocks for "small" files
147
148 std::list<Stream*> streams;
149
150 StorageIO( Storage* storage, const char* filename );
151 ~StorageIO();
152
153 bool open();
154 void close();
155 void flush();
156 void load();
157 void create();
158
159 unsigned long loadBigBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
160
161 unsigned long loadBigBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
162
163 unsigned long loadSmallBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
164
165 unsigned long loadSmallBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
166
167 StreamIO* streamIO( const std::string& name );
168
169 private:
170 // no copy or assign
171 StorageIO( const StorageIO& );
172 StorageIO& operator=( const StorageIO& );
173
174};
175
176class StreamIO
177{
178 public:
179 StorageIO* io;
180 DirEntry* entry;
181 std::string fullName;
182 bool eof;
183 bool fail;
184
185 StreamIO( StorageIO* io, DirEntry* entry );
186 ~StreamIO();
187 unsigned long size();
188 void seek( unsigned long pos );
189 unsigned long tell();
190 int getch();
191 unsigned long read( unsigned char* data, unsigned long maxlen );
192 unsigned long read( unsigned long pos, unsigned char* data, unsigned long maxlen );
193
194
195 private:
196 std::vector<unsigned long> blocks;
197
198 // no copy or assign
199 StreamIO( const StreamIO& );
200 StreamIO& operator=( const StreamIO& );
201
202 // pointer for read
203 unsigned long m_pos;
204
205 // simple cache system to speed-up getch()
206 unsigned char* cache_data;
207 unsigned long cache_size;
208 unsigned long cache_pos;
209 void updateCache();
210};
211
212} // namespace POLE
213
214using namespace POLE;
215
216static inline unsigned long readU16( const unsigned char* ptr )
217{
218 return ptr[0]+(ptr[1]<<8);
219}
220
221static inline unsigned long readU32( const unsigned char* ptr )
222{
223 return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
224}
225
226static inline void writeU16( unsigned char* ptr, unsigned long data )
227{
228 ptr[0] = (unsigned char)(data & 0xff);
229 ptr[1] = (unsigned char)((data >> 8) & 0xff);
230}
231
232static inline void writeU32( unsigned char* ptr, unsigned long data )
233{
234 ptr[0] = (unsigned char)(data & 0xff);
235 ptr[1] = (unsigned char)((data >> 8) & 0xff);
236 ptr[2] = (unsigned char)((data >> 16) & 0xff);
237 ptr[3] = (unsigned char)((data >> 24) & 0xff);
238}
239
240static const unsigned char pole_magic[] =
241 { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
242
243// =========== Header ==========
244
245Header::Header()
246{
247 b_shift = 9;
248 s_shift = 6;
249 num_bat = 0;
250 dirent_start = 0;
251 threshold = 4096;
252 sbat_start = 0;
253 num_sbat = 0;
254 mbat_start = 0;
255 num_mbat = 0;
256
257 for( unsigned i = 0; i < 8; i++ )
258 id[i] = pole_magic[i];
259 for( unsigned i=0; i<109; i++ )
260 bb_blocks[i] = AllocTable::Avail;
261}
262
263bool Header::valid()
264{
265 if( threshold != 4096 ) return false;
266 if( num_bat == 0 ) return false;
267 if( (num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false;
268 if( (num_bat < 109) && (num_mbat != 0) ) return false;
269 if( s_shift > b_shift ) return false;
270 if( b_shift <= 6 ) return false;
271 if( b_shift >=31 ) return false;
272
273 return true;
274}
275
276void Header::load( const unsigned char* buffer )
277{
278 b_shift = readU16( buffer + 0x1e );
279 s_shift = readU16( buffer + 0x20 );
280 num_bat = readU32( buffer + 0x2c );
281 dirent_start = readU32( buffer + 0x30 );
282 threshold = readU32( buffer + 0x38 );
283 sbat_start = readU32( buffer + 0x3c );
284 num_sbat = readU32( buffer + 0x40 );
285 mbat_start = readU32( buffer + 0x44 );
286 num_mbat = readU32( buffer + 0x48 );
287
288 for( unsigned i = 0; i < 8; i++ )
289 id[i] = buffer[i];
290 for( unsigned i=0; i<109; i++ )
291 bb_blocks[i] = readU32( buffer + 0x4C+i*4 );
292}
293
294void Header::save( unsigned char* buffer )
295{
296 memset( buffer, 0, 0x4c );
297 memcpy( buffer, pole_magic, 8 ); // ole signature
298 writeU32( buffer + 8, 0 ); // unknown
299 writeU32( buffer + 12, 0 ); // unknown
300 writeU32( buffer + 16, 0 ); // unknown
301 writeU16( buffer + 24, 0x003e ); // revision ?
302 writeU16( buffer + 26, 3 ); // version ?
303 writeU16( buffer + 28, 0xfffe ); // unknown
304 writeU16( buffer + 0x1e, b_shift );
305 writeU16( buffer + 0x20, s_shift );
306 writeU32( buffer + 0x2c, num_bat );
307 writeU32( buffer + 0x30, dirent_start );
308 writeU32( buffer + 0x38, threshold );
309 writeU32( buffer + 0x3c, sbat_start );
310 writeU32( buffer + 0x40, num_sbat );
311 writeU32( buffer + 0x44, mbat_start );
312 writeU32( buffer + 0x48, num_mbat );
313
314 for( unsigned i=0; i<109; i++ )
315 writeU32( buffer + 0x4C+i*4, bb_blocks[i] );
316}
317
318void Header::debug()
319{
320 std::cout << std::endl;
321 std::cout << "b_shift " << b_shift << std::endl;
322 std::cout << "s_shift " << s_shift << std::endl;
323 std::cout << "num_bat " << num_bat << std::endl;
324 std::cout << "dirent_start " << dirent_start << std::endl;
325 std::cout << "threshold " << threshold << std::endl;
326 std::cout << "sbat_start " << sbat_start << std::endl;
327 std::cout << "num_sbat " << num_sbat << std::endl;
328 std::cout << "mbat_start " << mbat_start << std::endl;
329 std::cout << "num_mbat " << num_mbat << std::endl;
330
331 unsigned s = (num_bat<=109) ? num_bat : 109;
332 std::cout << "bat blocks: ";
333 for( unsigned i = 0; i < s; i++ )
334 std::cout << bb_blocks[i] << " ";
335 std::cout << std::endl;
336}
337
338// =========== AllocTable ==========
339
340const unsigned AllocTable::Avail = 0xffffffff;
341const unsigned AllocTable::Eof = 0xfffffffe;
342const unsigned AllocTable::Bat = 0xfffffffd;
343const unsigned AllocTable::MetaBat = 0xfffffffc;
344
345AllocTable::AllocTable()
346{
347 blockSize = 4096;
348 // initial size
349 resize( 128 );
350}
351
352unsigned long AllocTable::count()
353{
354 return data.size();
355}
356
357void AllocTable::resize( unsigned long newsize )
358{
359 unsigned oldsize = data.size();
360 data.resize( newsize );
361 if( newsize > oldsize )
362 for( unsigned i = oldsize; i<newsize; i++ )
363 data[i] = Avail;
364}
365
366// make sure there're still free blocks
367void AllocTable::preserve( unsigned long n )
368{
369 std::vector<unsigned long> pre;
370 for( unsigned i=0; i < n; i++ )
371 pre.push_back( unused() );
372}
373
374unsigned long AllocTable::operator[]( unsigned long index )
375{
376 unsigned long result;
377 result = data[index];
378 return result;
379}
380
381void AllocTable::set( unsigned long index, unsigned long value )
382{
383 if( index >= count() ) resize( index + 1);
384 data[ index ] = value;
385}
386
387void AllocTable::setChain( std::vector<unsigned long> chain )
388{
389 if( chain.size() )
390 {
391 for( unsigned i=0; i<chain.size()-1; i++ )
392 set( chain[i], chain[i+1] );
393 set( chain[ chain.size()-1 ], AllocTable::Eof );
394 }
395}
396
397// follow
398std::vector<unsigned long> AllocTable::follow( unsigned long start )
399{
400 std::vector<unsigned long> chain;
401
402 if( start >= count() ) return chain;
403
404 unsigned long p = start;
405 while( p < count() )
406 {
407 if( p == (unsigned long)Eof ) break;
408 if( p == (unsigned long)Bat ) break;
409 if( p == (unsigned long)MetaBat ) break;
410 if( p >= count() ) break;
411 chain.push_back( p );
412 if( data[p] >= count() ) break;
413 p = data[ p ];
414 }
415
416 return chain;
417}
418
419unsigned AllocTable::unused()
420{
421 // find first available block
422 for( unsigned i = 0; i < data.size(); i++ )
423 if( data[i] == Avail )
424 return i;
425
426 // completely full, so enlarge the table
427 unsigned block = data.size();
428 resize( data.size()+10 );
429 return block;
430}
431
432void AllocTable::load( const unsigned char* buffer, unsigned len )
433{
434 resize( len / 4 );
435 for( unsigned i = 0; i < count(); i++ )
436 set( i, readU32( buffer + i*4 ) );
437}
438
439// return space required to save this dirtree
440unsigned AllocTable::size()
441{
442 return count() * 4;
443}
444
445void AllocTable::save( unsigned char* buffer )
446{
447 for( unsigned i = 0; i < count(); i++ )
448 writeU32( buffer + i*4, data[i] );
449}
450
451void AllocTable::debug()
452{
453 std::cout << "block size " << data.size() << std::endl;
454 for( unsigned i=0; i< data.size(); i++ )
455 {
456 if( data[i] == Avail ) continue;
457 std::cout << i << ": ";
458 if( data[i] == Eof ) std::cout << "[eof]";
459 else if( data[i] == Bat ) std::cout << "[bat]";
460 else if( data[i] == MetaBat ) std::cout << "[metabat]";
461 else std::cout << data[i];
462 std::cout << std::endl;
463 }
464}
465
466// =========== DirTree ==========
467
468const unsigned DirTree::End = 0xffffffff;
469
470DirTree::DirTree()
471{
472 clear();
473}
474
475void DirTree::clear()
476{
477 // leave only root entry
478 entries.resize( 1 );
479 entries[0].valid = true;
480 entries[0].name = "Root Entry";
481 entries[0].dir = true;
482 entries[0].size = 0;
483 entries[0].start = End;
484 entries[0].prev = End;
485 entries[0].next = End;
486 entries[0].child = End;
487}
488
489unsigned DirTree::entryCount()
490{
491 return entries.size();
492}
493
494DirEntry* DirTree::entry( unsigned index )
495{
496 if( index >= entryCount() ) return (DirEntry*) 0;
497 return &entries[ index ];
498}
499
500int DirTree::indexOf( DirEntry* e )
501{
502 for( unsigned i = 0; i < entryCount(); i++ )
503 if( entry( i ) == e ) return i;
504
505 return -1;
506}
507
508int DirTree::parent( unsigned index )
509{
510 // brute-force, basically we iterate for each entries, find its children
511 // and check if one of the children is 'index'
512 for( unsigned j=0; j<entryCount(); j++ )
513 {
514 std::vector<unsigned> chi = children( j );
515 for( unsigned i=0; i<chi.size();i++ )
516 if( chi[i] == index )
517 return j;
518 }
519
520 return -1;
521}
522
523std::string DirTree::fullName( unsigned index )
524{
525 // don't use root name ("Root Entry"), just give "/"
526 if( index == 0 ) return "/";
527
528 std::string result = entry( index )->name;
529 result.insert( 0, "/" );
530 int p = parent( index );
531 DirEntry * _entry = 0;
532 while( p > 0 )
533 {
534 _entry = entry( p );
535 if (_entry->dir && _entry->valid)
536 {
537 result.insert( 0, _entry->name);
538 result.insert( 0, "/" );
539 }
540 --p;
541 index = p;
542 if( index <= 0 ) break;
543 }
544 return result;
545}
546
547// given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
548// if not found and create is false, return 0
549// if create is true, a new entry is returned
550DirEntry* DirTree::entry( const std::string& name, bool create )
551{
552 if( !name.length() ) return (DirEntry*)0;
553
554 // quick check for "/" (that's root)
555 if( name == "/" ) return entry( 0 );
556
557 // split the names, e.g "/ObjectPool/_1020961869" will become:
558 // "ObjectPool" and "_1020961869"
559 std::list<std::string> names;
560 std::string::size_type start = 0, end = 0;
561 if( name[0] == '/' ) start++;
562 while( start < name.length() )
563 {
564 end = name.find_first_of( '/', start );
565 if( end == std::string::npos ) end = name.length();
566 names.push_back( name.substr( start, end-start ) );
567 start = end+1;
568 }
569
570 // start from root
571 int index = 0 ;
572
573 // trace one by one
574 std::list<std::string>::iterator it;
575
576 for( it = names.begin(); it != names.end(); ++it )
577 {
578 // find among the children of index
579 std::vector<unsigned> chi = children( index );
580 unsigned child = 0;
581 for( unsigned i = 0; i < chi.size(); i++ )
582 {
583 DirEntry* ce = entry( chi[i] );
584 if( ce )
585 if( ce->valid && ( ce->name.length()>1 ) )
586 if( ce->name == *it )
587 child = chi[i];
588 }
589
590 // traverse to the child
591 if( child > 0 ) index = child;
592 else
593 {
594 // not found among children
595 if( !create ) return (DirEntry*)0;
596
597 // create a new entry
598 unsigned parent = index;
599 entries.push_back( DirEntry() );
600 index = entryCount()-1;
601 DirEntry* e = entry( index );
602 e->valid = true;
603 e->name = *it;
604 e->dir = false;
605 e->size = 0;
606 e->start = 0;
607 e->child = End;
608 e->prev = End;
609 e->next = entry(parent)->child;
610 entry(parent)->child = index;
611 }
612 }
613
614 return entry( index );
615}
616
617// helper function: recursively find siblings of index
618void dirtree_find_siblings( DirTree* dirtree, std::vector<unsigned>& result,
619 unsigned index )
620{
621 DirEntry* e = dirtree->entry( index );
622 if( !e ) return;
623 if( !e->valid ) return;
624
625 // prevent infinite loop
626 for( unsigned i = 0; i < result.size(); i++ )
627 if( result[i] == index ) return;
628
629 // add myself
630 result.push_back( index );
631
632 // visit previous sibling, don't go infinitely
633 unsigned prev = e->prev;
634 if( ( prev > 0 ) && ( prev < dirtree->entryCount() ) )
635 {
636 for( unsigned i = 0; i < result.size(); i++ )
637 if( result[i] == prev ) prev = 0;
638 if( prev ) dirtree_find_siblings( dirtree, result, prev );
639 }
640
641 // visit next sibling, don't go infinitely
642 unsigned next = e->next;
643 if( ( next > 0 ) && ( next < dirtree->entryCount() ) )
644 {
645 for( unsigned i = 0; i < result.size(); i++ )
646 if( result[i] == next ) next = 0;
647 if( next ) dirtree_find_siblings( dirtree, result, next );
648 }
649}
650
651std::vector<unsigned> DirTree::children( unsigned index )
652{
653 std::vector<unsigned> result;
654
655 DirEntry* e = entry( index );
656 if( e ) if( e->valid && e->child < entryCount() )
657 dirtree_find_siblings( this, result, e->child );
658
659 return result;
660}
661
662void DirTree::load( unsigned char* buffer, unsigned size )
663{
664 entries.clear();
665
666 for( unsigned i = 0; i < size/128; i++ )
667 {
668 unsigned p = i * 128;
669
670 // would be < 32 if first char in the name isn't printable
671 unsigned prefix = 32;
672
673 // parse name of this entry, which stored as Unicode 16-bit
674 std::string name;
675 int name_len = readU16( buffer + 0x40+p );
676 if( name_len > 64 ) name_len = 64;
677 for( int j=0; ( buffer[j+p]) && (j<name_len); j+= 2 )
678 name.append( 1, buffer[j+p] );
679
680 // first char isn't printable ? remove it...
681 if( buffer[p] < 32 )
682 {
683 prefix = buffer[0];
684 name.erase( 0,1 );
685 }
686
687 // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
688 unsigned type = buffer[ 0x42 + p];
689
690 DirEntry e;
691 e.valid = true;
692 e.name = name;
693 e.start = readU32( buffer + 0x74+p );
694 e.size = readU32( buffer + 0x78+p );
695 e.prev = readU32( buffer + 0x44+p );
696 e.next = readU32( buffer + 0x48+p );
697 e.child = readU32( buffer + 0x4C+p );
698 e.dir = ( type!=2 );
699
700 // sanity checks
701 if( (type != 2) && (type != 1 ) && (type != 5 ) ) e.valid = false;
702 if( name_len < 1 ) e.valid = false;
703
704 entries.push_back( e );
705 }
706}
707
708// return space required to save this dirtree
709unsigned DirTree::size()
710{
711 return entryCount() * 128;
712}
713
714void DirTree::save( unsigned char* buffer )
715{
716 memset( buffer, 0, size() );
717
718 // root is fixed as "Root Entry"
719 DirEntry* root = entry( 0 );
720 std::string name = "Root Entry";
721 for( unsigned j = 0; j < name.length(); j++ )
722 buffer[ j*2 ] = name[j];
723 writeU16( buffer + 0x40, name.length()*2 + 2 );
724 writeU32( buffer + 0x74, 0xffffffff );
725 writeU32( buffer + 0x78, 0 );
726 writeU32( buffer + 0x44, 0xffffffff );
727 writeU32( buffer + 0x48, 0xffffffff );
728 writeU32( buffer + 0x4c, root->child );
729 buffer[ 0x42 ] = 5;
730 buffer[ 0x43 ] = 1;
731
732 for( unsigned i = 1; i < entryCount(); i++ )
733 {
734 DirEntry* e = entry( i );
735 if( !e ) continue;
736 if( e->dir )
737 {
738 e->start = 0xffffffff;
739 e->size = 0;
740 }
741
742 // max length for name is 32 chars
743 std::string name = e->name;
744 if( name.length() > 32 )
745 name.erase( 32, name.length() );
746
747 // write name as Unicode 16-bit
748 for( unsigned j = 0; j < name.length(); j++ )
749 buffer[ i*128 + j*2 ] = name[j];
750
751 writeU16( buffer + i*128 + 0x40, name.length()*2 + 2 );
752 writeU32( buffer + i*128 + 0x74, e->start );
753 writeU32( buffer + i*128 + 0x78, e->size );
754 writeU32( buffer + i*128 + 0x44, e->prev );
755 writeU32( buffer + i*128 + 0x48, e->next );
756 writeU32( buffer + i*128 + 0x4c, e->child );
757 buffer[ i*128 + 0x42 ] = e->dir ? 1 : 2;
758 buffer[ i*128 + 0x43 ] = 1; // always black
759 }
760}
761
762void DirTree::debug()
763{
764 for( unsigned i = 0; i < entryCount(); i++ )
765 {
766 DirEntry* e = entry( i );
767 if( !e ) continue;
768 std::cout << i << ": ";
769 if( !e->valid ) std::cout << "INVALID ";
770 std::cout << e->name << " ";
771 if( e->dir ) std::cout << "(Dir) ";
772 else std::cout << "(File) ";
773 std::cout << e->size << " ";
774 std::cout << "s:" << e->start << " ";
775 std::cout << "(";
776 if( e->child == End ) std::cout << "-"; else std::cout << e->child;
777 std::cout << " ";
778 if( e->prev == End ) std::cout << "-"; else std::cout << e->prev;
779 std::cout << ":";
780 if( e->next == End ) std::cout << "-"; else std::cout << e->next;
781 std::cout << ")";
782 std::cout << std::endl;
783 }
784}
785
786// =========== StorageIO ==========
787
788StorageIO::StorageIO( Storage* st, const char* fname )
789{
790 storage = st;
791 filename = fname;
792 result = Storage::Ok;
793 opened = false;
794
795 header = new Header();
796 dirtree = new DirTree();
797 bbat = new AllocTable();
798 sbat = new AllocTable();
799
800 filesize = 0;
801 bbat->blockSize = 1 << header->b_shift;
802 sbat->blockSize = 1 << header->s_shift;
803}
804
805StorageIO::~StorageIO()
806{
807 if( opened ) close();
808 delete sbat;
809 delete bbat;
810 delete dirtree;
811 delete header;
812}
813
814bool StorageIO::open()
815{
816 // already opened ? close first
817 if( opened ) close();
818
819 load();
820
821 return result == Storage::Ok;
822}
823
824void StorageIO::load()
825{
826 unsigned char* buffer = 0;
827 unsigned long buflen = 0;
828 std::vector<unsigned long> blocks;
829
830 // open the file, check for error
831 result = Storage::OpenFailed;
832 file.open( filename.c_str(), std::ios::binary | std::ios::in );
833 if( !file.good() ) return;
834
835 // find size of input file
836 file.seekg( 0, std::ios::end );
837 filesize = file.tellg();
838
839 // load header
840 buffer = new unsigned char[512];
841 file.seekg( 0 );
842 file.read( (char*)buffer, 512 );
843 header->load( buffer );
844 delete[] buffer;
845
846 // check OLE magic id
847 result = Storage::NotOLE;
848 for( unsigned i=0; i<8; i++ )
849 if( header->id[i] != pole_magic[i] )
850 return;
851
852 // sanity checks
853 result = Storage::BadOLE;
854 if( !header->valid() ) return;
855 if( header->threshold != 4096 ) return;
856
857 // important block size
858 bbat->blockSize = 1 << header->b_shift;
859 sbat->blockSize = 1 << header->s_shift;
860
861 // find blocks allocated to store big bat
862 // the first 109 blocks are in header, the rest in meta bat
863 blocks.clear();
864 blocks.resize( header->num_bat );
865 for( unsigned i = 0; i < 109; i++ )
866 if( i >= header->num_bat ) break;
867 else blocks[i] = header->bb_blocks[i];
868 if( (header->num_bat > 109) && (header->num_mbat > 0) )
869 {
870 unsigned char* buffer2 = new unsigned char[ bbat->blockSize ];
871 unsigned k = 109;
872 for( unsigned r = 0; r < header->num_mbat; r++ )
873 {
874 loadBigBlock( header->mbat_start+r, buffer2, bbat->blockSize );
875 for( unsigned s=0; s < bbat->blockSize; s+=4 )
876 {
877 if( k >= header->num_bat ) break;
878 else blocks[k++] = readU32( buffer2 + s );
879 }
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}