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