90365afb36bd3340d735e0d7ac1d7186731e74c1
[pear] / OLE / PPS / Root.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4                                                        |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2002 The PHP Group                                |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license,      |
9 // | that is bundled with this package in the file LICENSE, and is        |
10 // | available at through the world-wide-web at                           |
11 // | http://www.php.net/license/2_02.txt.                                 |
12 // | If you did not receive a copy of the PHP license and are unable to   |
13 // | obtain it through the world-wide-web, please send a note to          |
14 // | license@php.net so we can mail you a copy immediately.               |
15 // +----------------------------------------------------------------------+
16 // | Author: Xavier Noguer <xnoguer@php.net>                              |
17 // | Based on OLE::Storage_Lite by Kawai, Takanori                        |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id: Root.php 322720 2012-01-25 12:56:57Z clockwerx $
21
22
23 require_once 'OLE/PPS.php';
24 require_once 'System.php';
25
26 /**
27 * Class for creating Root PPS's for OLE containers
28 *
29 * @author   Xavier Noguer <xnoguer@php.net>
30 * @category Structures
31 * @package  OLE
32 */
33 class OLE_PPS_Root extends OLE_PPS
34 {
35     /**
36     * Flag to enable new logic
37     * @var bool
38     */
39     var $new_func = true;
40
41     /**
42     * The temporary dir for storing the OLE file
43     * @var string
44     */
45     var $_tmp_dir;
46     
47     
48     var $_BIG_BLOCK_SIZE;
49     var $_SMALL_BLOCK_SIZE;
50     var $_FILEH_;
51     /**
52     * Constructor
53     *
54     * @access public
55     * @param integer $time_1st A timestamp
56     * @param integer $time_2nd A timestamp
57     */
58     function __construct($time_1st, $time_2nd, $raChild)
59     {
60         $this->_tmp_dir = System::tmpdir();
61         parent::__construct(
62            null, 
63            OLE::Asc2Ucs('Root Entry'),
64            OLE_PPS_TYPE_ROOT,
65            null,
66            null,
67            null,
68            $time_1st,
69            $time_2nd,
70            null,
71            $raChild);
72     }
73
74     /**
75     * Sets the temp dir used for storing the OLE file
76     *
77     * @access public
78     * @param string $dir The dir to be used as temp dir
79     * @return true if given dir is valid, false otherwise
80     */
81     function setTempDir($dir)
82     {
83         if (is_dir($dir)) {
84             $this->_tmp_dir = $dir;
85             return true;
86         }
87         return false;
88     }
89
90     /**
91     * Method for saving the whole OLE container (including files).
92     * In fact, if called with an empty argument (or '-'), it saves to a
93     * temporary file and then outputs it's contents to stdout.
94     *
95     * @param string $filename The name of the file where to save the OLE container
96     * @access public
97     * @return mixed true on success, PEAR_Error on failure
98     */
99     function save($filename)
100     {
101         // Initial Setting for saving
102         $this->_BIG_BLOCK_SIZE  = pow(2,
103                       ((isset($this->_BIG_BLOCK_SIZE))? $this->_adjust2($this->_BIG_BLOCK_SIZE)  : 9));
104         $this->_SMALL_BLOCK_SIZE= pow(2, 
105                       ((isset($this->_SMALL_BLOCK_SIZE))?  $this->_adjust2($this->_SMALL_BLOCK_SIZE): 6));
106  
107         // Open temp file if we are sending output to stdout
108         if (($filename == '-') || ($filename == '')) {
109             $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root");
110             $this->_FILEH_ = @fopen($this->_tmp_filename,"w+b");
111             if ($this->_FILEH_ == false) {
112                 return $this->raiseError("Can't create temporary file.");
113             }
114         } else {
115             $this->_FILEH_ = @fopen($filename, "wb");
116             if ($this->_FILEH_ == false) {
117                 return $this->raiseError("Can't open $filename. It may be in use or protected.");
118             }
119         }
120         // Make an array of PPS's (for Save)
121         $aList = array();
122         OLE_PPS_Root::_savePpsSetPnt($aList, array($this));
123         // calculate values for header
124         list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo);
125         // Save Header
126         $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
127   
128         // Make Small Data string (write SBD)
129         $this->_data = $this->_makeSmallData($aList);
130   
131         // Write BB
132         $this->_saveBigData($iSBDcnt, $aList);
133         // Write PPS
134         $this->_savePps($aList);
135         // Write Big Block Depot and BDList and Adding Header informations
136         $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt);
137         // Close File, send it to stdout if necessary
138         if (($filename == '-') || ($filename == '')) {
139             fseek($this->_FILEH_, 0);
140             fpassthru($this->_FILEH_);
141             @fclose($this->_FILEH_);
142             // Delete the temporary file.
143             @unlink($this->_tmp_filename);
144         } else {
145             @fclose($this->_FILEH_);
146         }
147
148         return true;
149     }
150
151     /**
152     * Calculate some numbers
153     *
154     * @access private
155     * @param array $raList Reference to an array of PPS's
156     * @return array The array of numbers
157     */
158     function _calcSize(&$raList) 
159     {
160         // Calculate Basic Setting
161         list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0);
162         $iSmallLen = 0;
163         $iSBcnt = 0;
164         for ($i = 0; $i < count($raList); $i++) {
165             if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) {
166                 $raList[$i]->Size = $raList[$i]->_DataLen();
167                 if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) {
168                     $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE)
169                                   + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0);
170                 } else {
171                     $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) +
172                         (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0));
173                 }
174             }
175         }
176         $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE;
177         $iSlCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE);
178         $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0);
179         $iBBcnt +=  (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) +
180                       (( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0));
181         $iCnt = count($raList);
182         $iBdCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE;
183         $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0));
184    
185         return array($iSBDcnt, $iBBcnt, $iPPScnt);
186     }
187
188     /**
189     * Helper function for caculating a magic value for block sizes
190     *
191     * @access private
192     * @param integer $i2 The argument
193     * @see save()
194     * @return integer
195     */
196     function _adjust2($i2)
197     {
198         $iWk = log($i2)/log(2);
199         return ($iWk > floor($iWk))? floor($iWk)+1:$iWk;
200     }
201
202     /**
203     * Save OLE header
204     *
205     * @access private
206     * @param integer $iSBDcnt
207     * @param integer $iBBcnt
208     * @param integer $iPPScnt
209     */
210     function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt)
211     {
212         $FILE = $this->_FILEH_;
213   
214         if($this->new_func)
215           return $this->_create_header($iSBDcnt, $iBBcnt, $iPPScnt);
216
217         // Calculate Basic Setting
218         $iBlCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE;
219         $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE;
220   
221         $iBdExL = 0;
222         $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
223         $iAllW = $iAll;
224         $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
225         $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
226   
227         // Calculate BD count
228         if ($iBdCnt > $i1stBdL) {
229             while (1) {
230                 $iBdExL++;
231                 $iAllW++;
232                 $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
233                 $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
234                 if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) {
235                     break;
236                 }
237             }
238         }
239   
240         // Save Header
241         fwrite($FILE,
242                   "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
243                   . "\x00\x00\x00\x00"
244                   . "\x00\x00\x00\x00"
245                   . "\x00\x00\x00\x00"
246                   . "\x00\x00\x00\x00"
247                   . pack("v", 0x3b)
248                   . pack("v", 0x03)
249                   . pack("v", -2)
250                   . pack("v", 9)
251                   . pack("v", 6)
252                   . pack("v", 0)
253                   . "\x00\x00\x00\x00"
254                   . "\x00\x00\x00\x00"
255                   . pack("V", $iBdCnt) 
256                   . pack("V", $iBBcnt+$iSBDcnt) //ROOT START
257                   . pack("V", 0)
258                   . pack("V", 0x1000)
259                   . pack("V", $iSBDcnt ? 0 : -2)                  //Small Block Depot
260                   . pack("V", $iSBDcnt)
261           );
262         // Extra BDList Start, Count
263         if ($iBdCnt < $i1stBdL) {
264             fwrite($FILE,
265                       pack("V", -2).      // Extra BDList Start
266                       pack("V", 0)        // Extra BDList Count
267                   );
268         } else {
269             fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL));
270         }
271
272         // BDList
273         for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; $i++) {
274             fwrite($FILE, pack("V", $iAll+$i));
275         }
276         if ($i < $i1stBdL) {
277             for ($j = 0; $j < ($i1stBdL-$i); $j++) {
278                 fwrite($FILE, (pack("V", -1)));
279             }
280         }
281     }
282
283     /**
284     * Saving big data (PPS's with data bigger than OLE_DATA_SIZE_SMALL)
285     *
286     * @access private
287     * @param integer $iStBlk
288     * @param array &$raList Reference to array of PPS's
289     */
290     function _saveBigData($iStBlk, &$raList)
291     {
292         $FILE = $this->_FILEH_;
293    
294         // cycle through PPS's
295         for ($i = 0; $i < count($raList); $i++) {
296             if ($raList[$i]->Type != OLE_PPS_TYPE_DIR) {
297                 $raList[$i]->Size = $raList[$i]->_DataLen();
298                 if (($raList[$i]->Size >= OLE_DATA_SIZE_SMALL) ||
299                     (($raList[$i]->Type == OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data)))
300                 {
301                     // Write Data
302                     if (isset($raList[$i]->_PPS_FILE)) {
303                         $iLen = 0;
304                         fseek($raList[$i]->_PPS_FILE, 0); // To The Top
305                         while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) {
306                             $iLen += strlen($sBuff);
307                             fwrite($FILE, $sBuff);
308                         }
309                     } else {
310                         fwrite($FILE, $raList[$i]->_data);
311                     }
312            
313                     if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) {
314                         for ($j = 0; $j < ($this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)); $j++) {
315                             fwrite($FILE, "\x00");
316                         }
317                     }
318                     // Set For PPS
319                     $raList[$i]->_StartBlock = $iStBlk;
320                     $iStBlk += 
321                             (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) +
322                                 (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0));
323                 }
324                 // Close file for each PPS, and unlink it
325                 if (isset($raList[$i]->_PPS_FILE)) {
326                     @fclose($raList[$i]->_PPS_FILE);
327                     $raList[$i]->_PPS_FILE = null;
328                     @unlink($raList[$i]->_tmp_filename);
329                 }
330             }
331         }
332     }
333
334     /**
335     * get small data (PPS's with data smaller than OLE_DATA_SIZE_SMALL)
336     *
337     * @access private
338     * @param array &$raList Reference to array of PPS's
339     */
340     function _makeSmallData(&$raList)
341     {
342         $sRes = '';
343         $FILE = $this->_FILEH_;
344         $iSmBlk = 0;
345    
346         for ($i = 0; $i < count($raList); $i++) {
347             // Make SBD, small data string
348             if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) {
349                 if ($raList[$i]->Size <= 0) {
350                     continue;
351                 }
352                 if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) {
353                     $iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE)
354                                   + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0);
355                     // Add to SBD
356                     for ($j = 0; $j < ($iSmbCnt-1); $j++) {
357                         fwrite($FILE, pack("V", $j+$iSmBlk+1));
358                     }
359                     fwrite($FILE, pack("V", -2));
360                    
361                     // Add to Data String(this will be written for RootEntry)
362                     if ($raList[$i]->_PPS_FILE) {
363                         fseek($raList[$i]->_PPS_FILE, 0); // To The Top
364                         while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) {
365                             $sRes .= $sBuff;
366                         }
367                     } else {
368                         $sRes .= $raList[$i]->_data;
369                     }
370                     if ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) {
371                         for ($j = 0; $j < ($this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)); $j++) {
372                             $sRes .= "\x00";
373                         }
374                     }
375                     // Set for PPS
376                     $raList[$i]->_StartBlock = $iSmBlk;
377                     $iSmBlk += $iSmbCnt;
378                 }
379             }
380         }
381         $iSbCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE);
382         if ($iSmBlk % $iSbCnt) {
383             for ($i = 0; $i < ($iSbCnt - ($iSmBlk % $iSbCnt)); $i++) {
384                 fwrite($FILE, pack("V", -1));
385             }
386         }
387         return $sRes;
388     }
389
390     /**
391     * Saves all the PPS's WKs
392     *
393     * @access private
394     * @param array $raList Reference to an array with all PPS's
395     */
396     function _savePps(&$raList) 
397     {
398         // Save each PPS WK
399         for ($i = 0; $i < count($raList); $i++) {
400             fwrite($this->_FILEH_, $raList[$i]->_getPpsWk());
401         }
402         // Adjust for Block
403         $iCnt = count($raList);
404         $iBCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE;
405         if ($iCnt % $iBCnt) {
406             for ($i = 0; $i < (($iBCnt - ($iCnt % $iBCnt)) * OLE_PPS_SIZE); $i++) {
407                 fwrite($this->_FILEH_, "\x00");
408             }
409         }
410     }
411
412     /**
413     * Saving Big Block Depot
414     *
415     * @access private
416     * @param integer $iSbdSize
417     * @param integer $iBsize
418     * @param integer $iPpsCnt
419     */
420     function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) 
421     {
422       if($this->new_func)
423         return $this->_create_big_block_chain($iSbdSize, $iBsize, $iPpsCnt);
424
425         $FILE = $this->_FILEH_;
426         // Calculate Basic Setting
427         $iBbCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE;
428         $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE;
429       
430         $iBdExL = 0;
431         $iAll = $iBsize + $iPpsCnt + $iSbdSize;
432         $iAllW = $iAll;
433         $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
434         $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0);
435         // Calculate BD count
436         if ($iBdCnt >$i1stBdL) {
437             while (1) {
438                 $iBdExL++;
439                 $iAllW++;
440                 $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
441                 $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0);
442                 if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) {
443                     break;
444                 }
445             }
446         }
447       
448         // Making BD
449         // Set for SBD
450         if ($iSbdSize > 0) {
451             for ($i = 0; $i < ($iSbdSize - 1); $i++) {
452                 fwrite($FILE, pack("V", $i+1));
453             }
454             fwrite($FILE, pack("V", -2));
455         }
456         // Set for B
457         for ($i = 0; $i < ($iBsize - 1); $i++) {
458             fwrite($FILE, pack("V", $i+$iSbdSize+1));
459         }
460         fwrite($FILE, pack("V", -2));
461       
462         // Set for PPS
463         for ($i = 0; $i < ($iPpsCnt - 1); $i++) {
464             fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1));
465         }
466         fwrite($FILE, pack("V", -2));
467         // Set for BBD itself ( 0xFFFFFFFD : BBD)
468         for ($i = 0; $i < $iBdCnt; $i++) {
469             fwrite($FILE, pack("V", 0xFFFFFFFD));
470         }
471         // Set for ExtraBDList
472         for ($i = 0; $i < $iBdExL; $i++) {
473             fwrite($FILE, pack("V", 0xFFFFFFFC));
474         }
475         // Adjust for Block
476         if (($iAllW + $iBdCnt) % $iBbCnt) {
477             for ($i = 0; $i < ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); $i++) {
478                 fwrite($FILE, pack("V", -1));
479             }
480         }
481         // Extra BDList
482         if ($iBdCnt > $i1stBdL) {
483             $iN=0;
484             $iNb=0;
485             for ($i = $i1stBdL;$i < $iBdCnt; $i++, $iN++) {
486                 if ($iN >= ($iBbCnt - 1)) {
487                     $iN = 0;
488                     $iNb++;
489                     fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb));
490                 }
491                 fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i));
492             }
493             if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) {
494                 for ($i = 0; $i < (($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1))); $i++) {
495                     fwrite($FILE, pack("V", -1)); 
496                 }
497             }
498             fwrite($FILE, pack("V", -2));
499         }
500     }
501
502
503
504     /**
505      * New method to store Bigblock chain
506      *
507      * @access private
508      * @param integer $num_sb_blocks - number of Smallblock depot blocks
509      * @param integer $num_bb_blocks - number of Bigblock depot blocks
510      * @param integer $num_pps_blocks - number of PropertySetStorage blocks
511      */
512     function _create_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks) 
513     {
514       $FILE = $this->_FILEH_;
515
516       $bbd_info = $this->_calculate_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks);
517           
518       $data = "";
519
520       if($num_sb_blocks > 0)
521         {
522           for($i = 0; $i<($num_sb_blocks-1); $i++)
523             $data .= pack("V", $i+1);
524           $data .= pack("V", -2);
525         }
526
527       for($i = 0; $i<($num_bb_blocks-1); $i++)
528         $data .= pack("V", $i + $num_sb_blocks + 1);
529       $data .= pack("V", -2);
530
531       for($i = 0; $i<($num_pps_blocks-1); $i++)
532         $data .= pack("V", $i + $num_sb_blocks + $num_bb_blocks + 1);
533       $data .= pack("V", -2);
534
535       for($i = 0; $i < $bbd_info["0xFFFFFFFD_blockchain_entries"]; $i++)
536         $data .= pack("V", 0xFFFFFFFD);
537
538       for($i = 0; $i < $bbd_info["0xFFFFFFFC_blockchain_entries"]; $i++)
539         $data .= pack("V", 0xFFFFFFFC);
540
541       // Adjust for Block
542       $all_entries = $num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"];
543       if($all_entries % $bbd_info["entries_per_block"])
544         {
545           $rest = $bbd_info["entries_per_block"] - ($all_entries % $bbd_info["entries_per_block"]);
546           for($i = 0; $i < $rest; $i++)
547             $data .= pack("V", -1);
548         }
549
550       // Extra BDList
551       if($bbd_info["blockchain_list_entries"] > $bbd_info["header_blockchain_list_entries"])
552         {
553           $iN=0;
554           $iNb=0;
555           for($i = $bbd_info["header_blockchain_list_entries"]; $i < $bbd_info["blockchain_list_entries"]; $i++, $iN++)
556             {
557               if($iN >= ($bbd_info["entries_per_block"]-1))
558                 {
559                   $iN = 0;
560                   $iNb++;
561                   $data .= pack("V", $num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $iNb);
562                 }
563
564               $data .= pack("V", $num_bb_blocks + $num_sb_blocks + $num_pps_blocks + $i);
565             }
566
567           $all_entries = $bbd_info["blockchain_list_entries"] - $bbd_info["header_blockchain_list_entries"];
568           if(($all_entries % ($bbd_info["entries_per_block"] - 1)))
569             {
570               $rest = ($bbd_info["entries_per_block"] - 1) - ($all_entries % ($bbd_info["entries_per_block"] - 1));
571               for($i = 0; $i < $rest; $i++)
572                 $data .= pack("V", -1);
573             }
574
575           $data .= pack("V", -2);
576         }
577
578       /*
579         $this->dump($data, 0, strlen($data));
580         die;
581       */
582
583       fwrite($FILE, $data);
584     }
585
586     /**
587      * New method to store Header
588      *
589      * @access private
590      * @param integer $num_sb_blocks - number of Smallblock depot blocks
591      * @param integer $num_bb_blocks - number of Bigblock depot blocks
592      * @param integer $num_pps_blocks - number of PropertySetStorage blocks
593      */
594     function _create_header($num_sb_blocks, $num_bb_blocks, $num_pps_blocks) 
595     {
596       $FILE = $this->_FILEH_;
597
598       $bbd_info = $this->_calculate_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks);
599   
600       // Save Header
601       fwrite($FILE,
602              "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
603              . "\x00\x00\x00\x00"
604              . "\x00\x00\x00\x00"
605              . "\x00\x00\x00\x00"
606              . "\x00\x00\x00\x00"
607              . pack("v", 0x3b)
608              . pack("v", 0x03)
609              . pack("v", -2)
610              . pack("v", 9)
611              . pack("v", 6)
612              . pack("v", 0)
613              . "\x00\x00\x00\x00"
614              . "\x00\x00\x00\x00"
615              . pack("V", $bbd_info["blockchain_list_entries"]) 
616              . pack("V", $num_sb_blocks + $num_bb_blocks) //ROOT START
617              . pack("V", 0)
618              . pack("V", 0x1000)
619              );
620
621       //Small Block Depot
622       if($num_sb_blocks > 0)
623         fwrite($FILE, pack("V", 0));
624       else
625         fwrite($FILE, pack("V", -2));
626
627       fwrite($FILE, pack("V", $num_sb_blocks));
628
629       // Extra BDList Start, Count
630       if($bbd_info["blockchain_list_entries"] < $bbd_info["header_blockchain_list_entries"])
631         {
632           fwrite($FILE,
633                  pack("V", -2).      // Extra BDList Start
634                  pack("V", 0)        // Extra BDList Count
635                  );
636         }
637       else
638         {
639           fwrite($FILE, pack("V", $num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"]) . pack("V", $bbd_info["0xFFFFFFFC_blockchain_entries"]));
640         }
641
642       // BDList
643       for ($i=0; $i < $bbd_info["header_blockchain_list_entries"] and $i < $bbd_info["blockchain_list_entries"]; $i++) 
644         {
645           fwrite($FILE, pack("V", $num_bb_blocks + $num_sb_blocks + $num_pps_blocks + $i));
646         }
647
648       if($i < $bbd_info["header_blockchain_list_entries"])
649         {
650           for($j = 0; $j < ($bbd_info["header_blockchain_list_entries"]-$i); $j++) 
651             {
652               fwrite($FILE, (pack("V", -1)));
653             }
654         }
655     }
656
657     /**
658      * New method to calculate Bigblock chain
659      *
660      * @access private
661      * @param integer $num_sb_blocks - number of Smallblock depot blocks
662      * @param integer $num_bb_blocks - number of Bigblock depot blocks
663      * @param integer $num_pps_blocks - number of PropertySetStorage blocks
664      */
665     function _calculate_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks)
666     {
667       $bbd_info["entries_per_block"] = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE;
668       $bbd_info["header_blockchain_list_entries"] = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE;
669       $bbd_info["blockchain_entries"] = $num_sb_blocks + $num_bb_blocks + $num_pps_blocks;
670       $bbd_info["0xFFFFFFFD_blockchain_entries"] = $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"]);
671       $bbd_info["blockchain_list_entries"] = $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"] + $bbd_info["0xFFFFFFFD_blockchain_entries"]);
672
673       // do some magic
674       $bbd_info["ext_blockchain_list_entries"] = 0;
675       $bbd_info["0xFFFFFFFC_blockchain_entries"] = 0;
676       if($bbd_info["blockchain_list_entries"] > $bbd_info["header_blockchain_list_entries"])
677         {
678           do
679             {
680               $bbd_info["blockchain_list_entries"] = $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"] + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"]);
681               $bbd_info["ext_blockchain_list_entries"] = $bbd_info["blockchain_list_entries"] - $bbd_info["header_blockchain_list_entries"];
682               $bbd_info["0xFFFFFFFC_blockchain_entries"] = $this->get_number_of_pointer_blocks($bbd_info["ext_blockchain_list_entries"]);
683               $bbd_info["0xFFFFFFFD_blockchain_entries"] = $this->get_number_of_pointer_blocks($num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"]);
684             }
685           while($bbd_info["blockchain_list_entries"] < $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"] + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"]));
686         }
687
688       return $bbd_info;
689     }
690
691     /**
692      * Calculates number of pointer blocks
693      *
694      * @access public
695      * @param integer $num_pointers - number of pointers
696      */
697     function get_number_of_pointer_blocks($num_pointers)
698     {
699       $pointers_per_block = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE;
700       
701       return floor($num_pointers / $pointers_per_block) + (($num_pointers % $pointers_per_block)? 1: 0);
702     }
703
704     /**
705      * Support method for some hexdumping
706      *
707      * @access public
708      * @param string $data - Binary data
709      * @param integer $from - Start offset of data to dump
710      * @param integer $to - Target offset of data to dump
711      */
712     function dump($data, $from, $to)
713     {
714       $chars = array();
715       $i = 0;
716       for($i = $from; $i < $to; $i++)
717         {
718           if(sizeof($chars) == 16)
719             {
720               printf("%08X (% 12d) |", $i-16, $i-16);
721               foreach($chars as $char)
722                 printf(" %02X", $char);
723               print " |\n";
724
725               $chars = array();
726             }
727
728           $chars[] = ord($data[$i]);
729         }
730
731       if(sizeof($chars))
732         {
733           printf("%08X (% 12d) |", $i-sizeof($chars), $i-sizeof($chars));
734           foreach($chars as $char)
735             printf(" %02X", $char);
736           print " |\n";
737
738           $chars = array();
739         }
740     }
741 }
742 ?>