blob: 3ea2a57006154136524a53dd4348a108a6db2b38 [file] [log] [blame]
rjw6c1fd8f2022-11-30 14:33:01 +08001# OLE::Storage_Lite
2# by Kawai, Takanori (Hippo2000) 2000.11.4, 8, 14
3# This Program is Still ALPHA version.
4#//////////////////////////////////////////////////////////////////////////////
5# OLE::Storage_Lite::PPS Object
6#//////////////////////////////////////////////////////////////////////////////
7#==============================================================================
8# OLE::Storage_Lite::PPS
9#==============================================================================
10package OLE::Storage_Lite::PPS;
11require Exporter;
12use strict;
13use vars qw($VERSION @ISA);
14@ISA = qw(Exporter);
15$VERSION = '0.19';
16
17#------------------------------------------------------------------------------
18# new (OLE::Storage_Lite::PPS)
19#------------------------------------------------------------------------------
20sub new ($$$$$$$$$$;$$) {
21#1. Constructor for General Usage
22 my($sClass, $iNo, $sNm, $iType, $iPrev, $iNext, $iDir,
23 $raTime1st, $raTime2nd, $iStart, $iSize, $sData, $raChild) = @_;
24
25 if($iType == OLE::Storage_Lite::PpsType_File()) { #FILE
26 return OLE::Storage_Lite::PPS::File->_new
27 ($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd,
28 $iStart, $iSize, $sData, $raChild);
29 }
30 elsif($iType == OLE::Storage_Lite::PpsType_Dir()) { #DIRECTRY
31 return OLE::Storage_Lite::PPS::Dir->_new
32 ($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd,
33 $iStart, $iSize, $sData, $raChild);
34 }
35 elsif($iType == OLE::Storage_Lite::PpsType_Root()) { #ROOT
36 return OLE::Storage_Lite::PPS::Root->_new
37 ($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd,
38 $iStart, $iSize, $sData, $raChild);
39 }
40 else {
41 die "Error PPS:$iType $sNm\n";
42 }
43}
44#------------------------------------------------------------------------------
45# _new (OLE::Storage_Lite::PPS)
46# for OLE::Storage_Lite
47#------------------------------------------------------------------------------
48sub _new ($$$$$$$$$$$;$$) {
49 my($sClass, $iNo, $sNm, $iType, $iPrev, $iNext, $iDir,
50 $raTime1st, $raTime2nd, $iStart, $iSize, $sData, $raChild) = @_;
51#1. Constructor for OLE::Storage_Lite
52 my $oThis = {
53 No => $iNo,
54 Name => $sNm,
55 Type => $iType,
56 PrevPps => $iPrev,
57 NextPps => $iNext,
58 DirPps => $iDir,
59 Time1st => $raTime1st,
60 Time2nd => $raTime2nd,
61 StartBlock => $iStart,
62 Size => $iSize,
63 Data => $sData,
64 Child => $raChild,
65 };
66 bless $oThis, $sClass;
67 return $oThis;
68}
69#------------------------------------------------------------------------------
70# _DataLen (OLE::Storage_Lite::PPS)
71# Check for update
72#------------------------------------------------------------------------------
73sub _DataLen($) {
74 my($oSelf) =@_;
75 return 0 unless(defined($oSelf->{Data}));
76 return ($oSelf->{_PPS_FILE})?
77 ($oSelf->{_PPS_FILE}->stat())[7] : length($oSelf->{Data});
78}
79#------------------------------------------------------------------------------
80# _makeSmallData (OLE::Storage_Lite::PPS)
81#------------------------------------------------------------------------------
82sub _makeSmallData($$$) {
83 my($oThis, $aList, $rhInfo) = @_;
84 my ($sRes);
85 my $FILE = $rhInfo->{_FILEH_};
86 my $iSmBlk = 0;
87
88 foreach my $oPps (@$aList) {
89#1. Make SBD, small data string
90 if($oPps->{Type}==OLE::Storage_Lite::PpsType_File()) {
91 next if($oPps->{Size}<=0);
92 if($oPps->{Size} < $rhInfo->{_SMALL_SIZE}) {
93 my $iSmbCnt = int($oPps->{Size} / $rhInfo->{_SMALL_BLOCK_SIZE})
94 + (($oPps->{Size} % $rhInfo->{_SMALL_BLOCK_SIZE})? 1: 0);
95 #1.1 Add to SBD
96 for (my $i = 0; $i<($iSmbCnt-1); $i++) {
97 print {$FILE} (pack("V", $i+$iSmBlk+1));
98 }
99 print {$FILE} (pack("V", -2));
100
101 #1.2 Add to Data String(this will be written for RootEntry)
102 #Check for update
103 if($oPps->{_PPS_FILE}) {
104 my $sBuff;
105 $oPps->{_PPS_FILE}->seek(0, 0); #To The Top
106 while($oPps->{_PPS_FILE}->read($sBuff, 4096)) {
107 $sRes .= $sBuff;
108 }
109 }
110 else {
111 $sRes .= $oPps->{Data};
112 }
113 $sRes .= ("\x00" x
114 ($rhInfo->{_SMALL_BLOCK_SIZE} - ($oPps->{Size}% $rhInfo->{_SMALL_BLOCK_SIZE})))
115 if($oPps->{Size}% $rhInfo->{_SMALL_BLOCK_SIZE});
116 #1.3 Set for PPS
117 $oPps->{StartBlock} = $iSmBlk;
118 $iSmBlk += $iSmbCnt;
119 }
120 }
121 }
122 my $iSbCnt = int($rhInfo->{_BIG_BLOCK_SIZE}/ OLE::Storage_Lite::LongIntSize());
123 print {$FILE} (pack("V", -1) x ($iSbCnt - ($iSmBlk % $iSbCnt)))
124 if($iSmBlk % $iSbCnt);
125#2. Write SBD with adjusting length for block
126 return $sRes;
127}
128#------------------------------------------------------------------------------
129# _savePpsWk (OLE::Storage_Lite::PPS)
130#------------------------------------------------------------------------------
131sub _savePpsWk($$)
132{
133 my($oThis, $rhInfo) = @_;
134#1. Write PPS
135 my $FILE = $rhInfo->{_FILEH_};
136 print {$FILE} (
137 $oThis->{Name}
138 . ("\x00" x (64 - length($oThis->{Name}))) #64
139 , pack("v", length($oThis->{Name}) + 2) #66
140 , pack("c", $oThis->{Type}) #67
141 , pack("c", 0x00) #UK #68
142 , pack("V", $oThis->{PrevPps}) #Prev #72
143 , pack("V", $oThis->{NextPps}) #Next #76
144 , pack("V", $oThis->{DirPps}) #Dir #80
145 , "\x00\x09\x02\x00" #84
146 , "\x00\x00\x00\x00" #88
147 , "\xc0\x00\x00\x00" #92
148 , "\x00\x00\x00\x46" #96
149 , "\x00\x00\x00\x00" #100
150 , OLE::Storage_Lite::LocalDate2OLE($oThis->{Time1st}) #108
151 , OLE::Storage_Lite::LocalDate2OLE($oThis->{Time2nd}) #116
152 , pack("V", defined($oThis->{StartBlock})?
153 $oThis->{StartBlock}:0) #116
154 , pack("V", defined($oThis->{Size})?
155 $oThis->{Size} : 0) #124
156 , pack("V", 0), #128
157 );
158}
159
160#//////////////////////////////////////////////////////////////////////////////
161# OLE::Storage_Lite::PPS::Root Object
162#//////////////////////////////////////////////////////////////////////////////
163#==============================================================================
164# OLE::Storage_Lite::PPS::Root
165#==============================================================================
166package OLE::Storage_Lite::PPS::Root;
167require Exporter;
168use strict;
169use IO::File;
170use IO::Handle;
171use Fcntl;
172use vars qw($VERSION @ISA);
173@ISA = qw(OLE::Storage_Lite::PPS Exporter);
174$VERSION = '0.19';
175sub _savePpsSetPnt($$$);
176sub _savePpsSetPnt2($$$);
177#------------------------------------------------------------------------------
178# new (OLE::Storage_Lite::PPS::Root)
179#------------------------------------------------------------------------------
180sub new ($;$$$) {
181 my($sClass, $raTime1st, $raTime2nd, $raChild) = @_;
182 OLE::Storage_Lite::PPS::_new(
183 $sClass,
184 undef,
185 OLE::Storage_Lite::Asc2Ucs('Root Entry'),
186 5,
187 undef,
188 undef,
189 undef,
190 $raTime1st,
191 $raTime2nd,
192 undef,
193 undef,
194 undef,
195 $raChild);
196}
197#------------------------------------------------------------------------------
198# save (OLE::Storage_Lite::PPS::Root)
199#------------------------------------------------------------------------------
200sub save($$;$$) {
201 my($oThis, $sFile, $bNoAs, $rhInfo) = @_;
202 #0.Initial Setting for saving
203 $rhInfo = {} unless($rhInfo);
204 $rhInfo->{_BIG_BLOCK_SIZE} = 2**
205 (($rhInfo->{_BIG_BLOCK_SIZE})?
206 _adjust2($rhInfo->{_BIG_BLOCK_SIZE}) : 9);
207 $rhInfo->{_SMALL_BLOCK_SIZE}= 2 **
208 (($rhInfo->{_SMALL_BLOCK_SIZE})?
209 _adjust2($rhInfo->{_SMALL_BLOCK_SIZE}): 6);
210 $rhInfo->{_SMALL_SIZE} = 0x1000;
211 $rhInfo->{_PPS_SIZE} = 0x80;
212
213 my $closeFile = 1;
214
215 #1.Open File
216 #1.1 $sFile is Ref of scalar
217 if(ref($sFile) eq 'SCALAR') {
218 require IO::Scalar;
219 my $oIo = new IO::Scalar $sFile, O_WRONLY;
220 $rhInfo->{_FILEH_} = $oIo;
221 }
222 #1.1.1 $sFile is a IO::Scalar object
223 # Now handled as a filehandle ref below.
224
225 #1.2 $sFile is a IO::Handle object
226 elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) {
227 # Not all filehandles support binmode() so try it in an eval.
228 eval{ binmode $sFile };
229 $rhInfo->{_FILEH_} = $sFile;
230 }
231 #1.3 $sFile is a simple filename string
232 elsif(!ref($sFile)) {
233 if($sFile ne '-') {
234 my $oIo = new IO::File;
235 $oIo->open(">$sFile") || return undef;
236 binmode($oIo);
237 $rhInfo->{_FILEH_} = $oIo;
238 }
239 else {
240 my $oIo = new IO::Handle;
241 $oIo->fdopen(fileno(STDOUT),"w") || return undef;
242 binmode($oIo);
243 $rhInfo->{_FILEH_} = $oIo;
244 }
245 }
246 #1.4 Assume that if $sFile is a ref then it is a valid filehandle
247 else {
248 # Not all filehandles support binmode() so try it in an eval.
249 eval{ binmode $sFile };
250 $rhInfo->{_FILEH_} = $sFile;
251 # Caller controls filehandle closing
252 $closeFile = 0;
253 }
254
255 my $iBlk = 0;
256 #1. Make an array of PPS (for Save)
257 my @aList=();
258 if($bNoAs) {
259 _savePpsSetPnt2([$oThis], \@aList, $rhInfo);
260 }
261 else {
262 _savePpsSetPnt([$oThis], \@aList, $rhInfo);
263 }
264 my ($iSBDcnt, $iBBcnt, $iPPScnt) = $oThis->_calcSize(\@aList, $rhInfo);
265
266 #2.Save Header
267 $oThis->_saveHeader($rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt);
268
269 #3.Make Small Data string (write SBD)
270 my $sSmWk = $oThis->_makeSmallData(\@aList, $rhInfo);
271 $oThis->{Data} = $sSmWk; #Small Datas become RootEntry Data
272
273 #4. Write BB
274 my $iBBlk = $iSBDcnt;
275 $oThis->_saveBigData(\$iBBlk, \@aList, $rhInfo);
276
277 #5. Write PPS
278 $oThis->_savePps(\@aList, $rhInfo);
279
280 #6. Write BD and BDList and Adding Header informations
281 $oThis->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt, $rhInfo);
282
283 #7.Close File
284 return $rhInfo->{_FILEH_}->close if $closeFile;
285}
286#------------------------------------------------------------------------------
287# _calcSize (OLE::Storage_Lite::PPS)
288#------------------------------------------------------------------------------
289sub _calcSize($$)
290{
291 my($oThis, $raList, $rhInfo) = @_;
292
293#0. Calculate Basic Setting
294 my ($iSBDcnt, $iBBcnt, $iPPScnt) = (0,0,0);
295 my $iSmallLen = 0;
296 my $iSBcnt = 0;
297 foreach my $oPps (@$raList) {
298 if($oPps->{Type}==OLE::Storage_Lite::PpsType_File()) {
299 $oPps->{Size} = $oPps->_DataLen(); #Mod
300 if($oPps->{Size} < $rhInfo->{_SMALL_SIZE}) {
301 $iSBcnt += int($oPps->{Size} / $rhInfo->{_SMALL_BLOCK_SIZE})
302 + (($oPps->{Size} % $rhInfo->{_SMALL_BLOCK_SIZE})? 1: 0);
303 }
304 else {
305 $iBBcnt +=
306 (int($oPps->{Size}/ $rhInfo->{_BIG_BLOCK_SIZE}) +
307 (($oPps->{Size}% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0));
308 }
309 }
310 }
311 $iSmallLen = $iSBcnt * $rhInfo->{_SMALL_BLOCK_SIZE};
312 my $iSlCnt = int($rhInfo->{_BIG_BLOCK_SIZE}/ OLE::Storage_Lite::LongIntSize());
313 $iSBDcnt = int($iSBcnt / $iSlCnt)+ (($iSBcnt % $iSlCnt)? 1:0);
314 $iBBcnt += (int($iSmallLen/ $rhInfo->{_BIG_BLOCK_SIZE}) +
315 (( $iSmallLen% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0));
316 my $iCnt = scalar(@$raList);
317 my $iBdCnt = $rhInfo->{_BIG_BLOCK_SIZE}/OLE::Storage_Lite::PpsSize();
318 $iPPScnt = (int($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0));
319 return ($iSBDcnt, $iBBcnt, $iPPScnt);
320}
321#------------------------------------------------------------------------------
322# _adjust2 (OLE::Storage_Lite::PPS::Root)
323#------------------------------------------------------------------------------
324sub _adjust2($) {
325 my($i2) = @_;
326 my $iWk;
327 $iWk = log($i2)/log(2);
328 return ($iWk > int($iWk))? int($iWk)+1:$iWk;
329}
330#------------------------------------------------------------------------------
331# _saveHeader (OLE::Storage_Lite::PPS::Root)
332#------------------------------------------------------------------------------
333sub _saveHeader($$$$$) {
334 my($oThis, $rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt) = @_;
335 my $FILE = $rhInfo->{_FILEH_};
336
337#0. Calculate Basic Setting
338 my $iBlCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize();
339 my $i1stBdL = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize());
340 my $i1stBdMax = $i1stBdL * $iBlCnt - $i1stBdL;
341 my $iBdExL = 0;
342 my $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
343 my $iAllW = $iAll;
344 my $iBdCntW = int($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
345 my $iBdCnt = int(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
346 my $i;
347
348 if ($iBdCnt > $i1stBdL) {
349 #0.1 Calculate BD count
350 $iBlCnt--; #the BlCnt is reduced in the count of the last sect is used for a pointer the next Bl
351 my $iBBleftover = $iAll - $i1stBdMax;
352
353 if ($iAll >$i1stBdMax) {
354 while(1) {
355 $iBdCnt = int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0);
356 $iBdExL = int(($iBdCnt) / $iBlCnt) + ((($iBdCnt) % $iBlCnt)? 1: 0);
357 $iBBleftover = $iBBleftover + $iBdExL;
358 last if($iBdCnt == (int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0)));
359 }
360 }
361 $iBdCnt += $i1stBdL;
362 #print "iBdCnt = $iBdCnt \n";
363 }
364#1.Save Header
365 print {$FILE} (
366 "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
367 , "\x00\x00\x00\x00" x 4
368 , pack("v", 0x3b)
369 , pack("v", 0x03)
370 , pack("v", -2)
371 , pack("v", 9)
372 , pack("v", 6)
373 , pack("v", 0)
374 , "\x00\x00\x00\x00" x 2
375 , pack("V", $iBdCnt),
376 , pack("V", $iBBcnt+$iSBDcnt), #ROOT START
377 , pack("V", 0)
378 , pack("V", 0x1000)
379 , pack("V", $iSBDcnt ? 0 : -2) #Small Block Depot
380 , pack("V", $iSBDcnt)
381 );
382#2. Extra BDList Start, Count
383 if($iAll <= $i1stBdMax) {
384 print {$FILE} (
385 pack("V", -2), #Extra BDList Start
386 pack("V", 0), #Extra BDList Count
387 );
388 }
389 else {
390 print {$FILE} (
391 pack("V", $iAll+$iBdCnt),
392 pack("V", $iBdExL),
393 );
394 }
395
396#3. BDList
397 for($i=0; $i<$i1stBdL and $i < $iBdCnt; $i++) {
398 print {$FILE} (pack("V", $iAll+$i));
399 }
400 print {$FILE} ((pack("V", -1)) x($i1stBdL-$i)) if($i<$i1stBdL);
401}
402#------------------------------------------------------------------------------
403# _saveBigData (OLE::Storage_Lite::PPS)
404#------------------------------------------------------------------------------
405sub _saveBigData($$$$) {
406 my($oThis, $iStBlk, $raList, $rhInfo) = @_;
407 my $iRes = 0;
408 my $FILE = $rhInfo->{_FILEH_};
409
410#1.Write Big (ge 0x1000) Data into Block
411 foreach my $oPps (@$raList) {
412 if($oPps->{Type}!=OLE::Storage_Lite::PpsType_Dir()) {
413#print "PPS: $oPps DEF:", defined($oPps->{Data}), "\n";
414 $oPps->{Size} = $oPps->_DataLen(); #Mod
415 if(($oPps->{Size} >= $rhInfo->{_SMALL_SIZE}) ||
416 (($oPps->{Type} == OLE::Storage_Lite::PpsType_Root()) && defined($oPps->{Data}))) {
417 #1.1 Write Data
418 #Check for update
419 if($oPps->{_PPS_FILE}) {
420 my $sBuff;
421 my $iLen = 0;
422 $oPps->{_PPS_FILE}->seek(0, 0); #To The Top
423 while($oPps->{_PPS_FILE}->read($sBuff, 4096)) {
424 $iLen += length($sBuff);
425 print {$FILE} ($sBuff); #Check for update
426 }
427 }
428 else {
429 print {$FILE} ($oPps->{Data});
430 }
431 print {$FILE} (
432 "\x00" x
433 ($rhInfo->{_BIG_BLOCK_SIZE} -
434 ($oPps->{Size} % $rhInfo->{_BIG_BLOCK_SIZE}))
435 ) if ($oPps->{Size} % $rhInfo->{_BIG_BLOCK_SIZE});
436 #1.2 Set For PPS
437 $oPps->{StartBlock} = $$iStBlk;
438 $$iStBlk +=
439 (int($oPps->{Size}/ $rhInfo->{_BIG_BLOCK_SIZE}) +
440 (($oPps->{Size}% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0));
441 }
442 }
443 }
444}
445#------------------------------------------------------------------------------
446# _savePps (OLE::Storage_Lite::PPS::Root)
447#------------------------------------------------------------------------------
448sub _savePps($$$)
449{
450 my($oThis, $raList, $rhInfo) = @_;
451#0. Initial
452 my $FILE = $rhInfo->{_FILEH_};
453#2. Save PPS
454 foreach my $oItem (@$raList) {
455 $oItem->_savePpsWk($rhInfo);
456 }
457#3. Adjust for Block
458 my $iCnt = scalar(@$raList);
459 my $iBCnt = $rhInfo->{_BIG_BLOCK_SIZE} / $rhInfo->{_PPS_SIZE};
460 print {$FILE} ("\x00" x (($iBCnt - ($iCnt % $iBCnt)) * $rhInfo->{_PPS_SIZE}))
461 if($iCnt % $iBCnt);
462 return int($iCnt / $iBCnt) + (($iCnt % $iBCnt)? 1: 0);
463}
464#------------------------------------------------------------------------------
465# _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root)
466# For Test
467#------------------------------------------------------------------------------
468sub _savePpsSetPnt2($$$)
469{
470 my($aThis, $raList, $rhInfo) = @_;
471#1. make Array as Children-Relations
472#1.1 if No Children
473 if($#$aThis < 0) {
474 return 0xFFFFFFFF;
475 }
476 elsif($#$aThis == 0) {
477#1.2 Just Only one
478 push @$raList, $aThis->[0];
479 $aThis->[0]->{No} = $#$raList;
480 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
481 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
482 $aThis->[0]->{DirPps} = _savePpsSetPnt2($aThis->[0]->{Child}, $raList, $rhInfo);
483 return $aThis->[0]->{No};
484 }
485 else {
486#1.3 Array
487 my $iCnt = $#$aThis + 1;
488#1.3.1 Define Center
489 my $iPos = 0; #int($iCnt/ 2); #$iCnt
490
491 my @aWk = @$aThis;
492 my @aPrev = ($#$aThis > 1)? splice(@aWk, 1, 1) : (); #$iPos);
493 my @aNext = splice(@aWk, 1); #, $iCnt - $iPos -1);
494 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt2(
495 \@aPrev, $raList, $rhInfo);
496 push @$raList, $aThis->[$iPos];
497 $aThis->[$iPos]->{No} = $#$raList;
498
499#1.3.2 Devide a array into Previous,Next
500 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt2(
501 \@aNext, $raList, $rhInfo);
502 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt2($aThis->[$iPos]->{Child}, $raList, $rhInfo);
503 return $aThis->[$iPos]->{No};
504 }
505}
506#------------------------------------------------------------------------------
507# _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root)
508# For Test
509#------------------------------------------------------------------------------
510sub _savePpsSetPnt2s($$$)
511{
512 my($aThis, $raList, $rhInfo) = @_;
513#1. make Array as Children-Relations
514#1.1 if No Children
515 if($#$aThis < 0) {
516 return 0xFFFFFFFF;
517 }
518 elsif($#$aThis == 0) {
519#1.2 Just Only one
520 push @$raList, $aThis->[0];
521 $aThis->[0]->{No} = $#$raList;
522 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
523 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
524 $aThis->[0]->{DirPps} = _savePpsSetPnt2($aThis->[0]->{Child}, $raList, $rhInfo);
525 return $aThis->[0]->{No};
526 }
527 else {
528#1.3 Array
529 my $iCnt = $#$aThis + 1;
530#1.3.1 Define Center
531 my $iPos = 0; #int($iCnt/ 2); #$iCnt
532 push @$raList, $aThis->[$iPos];
533 $aThis->[$iPos]->{No} = $#$raList;
534 my @aWk = @$aThis;
535#1.3.2 Devide a array into Previous,Next
536 my @aPrev = splice(@aWk, 0, $iPos);
537 my @aNext = splice(@aWk, 1, $iCnt - $iPos -1);
538 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt2(
539 \@aPrev, $raList, $rhInfo);
540 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt2(
541 \@aNext, $raList, $rhInfo);
542 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt2($aThis->[$iPos]->{Child}, $raList, $rhInfo);
543 return $aThis->[$iPos]->{No};
544 }
545}
546#------------------------------------------------------------------------------
547# _savePpsSetPnt (OLE::Storage_Lite::PPS::Root)
548#------------------------------------------------------------------------------
549sub _savePpsSetPnt($$$)
550{
551 my($aThis, $raList, $rhInfo) = @_;
552#1. make Array as Children-Relations
553#1.1 if No Children
554 if($#$aThis < 0) {
555 return 0xFFFFFFFF;
556 }
557 elsif($#$aThis == 0) {
558#1.2 Just Only one
559 push @$raList, $aThis->[0];
560 $aThis->[0]->{No} = $#$raList;
561 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
562 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
563 $aThis->[0]->{DirPps} = _savePpsSetPnt($aThis->[0]->{Child}, $raList, $rhInfo);
564 return $aThis->[0]->{No};
565 }
566 else {
567#1.3 Array
568 my $iCnt = $#$aThis + 1;
569#1.3.1 Define Center
570 my $iPos = int($iCnt/ 2); #$iCnt
571 push @$raList, $aThis->[$iPos];
572 $aThis->[$iPos]->{No} = $#$raList;
573 my @aWk = @$aThis;
574#1.3.2 Devide a array into Previous,Next
575 my @aPrev = splice(@aWk, 0, $iPos);
576 my @aNext = splice(@aWk, 1, $iCnt - $iPos -1);
577 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt(
578 \@aPrev, $raList, $rhInfo);
579 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt(
580 \@aNext, $raList, $rhInfo);
581 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt($aThis->[$iPos]->{Child}, $raList, $rhInfo);
582 return $aThis->[$iPos]->{No};
583 }
584}
585#------------------------------------------------------------------------------
586# _savePpsSetPnt (OLE::Storage_Lite::PPS::Root)
587#------------------------------------------------------------------------------
588sub _savePpsSetPnt1($$$)
589{
590 my($aThis, $raList, $rhInfo) = @_;
591#1. make Array as Children-Relations
592#1.1 if No Children
593 if($#$aThis < 0) {
594 return 0xFFFFFFFF;
595 }
596 elsif($#$aThis == 0) {
597#1.2 Just Only one
598 push @$raList, $aThis->[0];
599 $aThis->[0]->{No} = $#$raList;
600 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
601 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
602 $aThis->[0]->{DirPps} = _savePpsSetPnt($aThis->[0]->{Child}, $raList, $rhInfo);
603 return $aThis->[0]->{No};
604 }
605 else {
606#1.3 Array
607 my $iCnt = $#$aThis + 1;
608#1.3.1 Define Center
609 my $iPos = int($iCnt/ 2); #$iCnt
610 push @$raList, $aThis->[$iPos];
611 $aThis->[$iPos]->{No} = $#$raList;
612 my @aWk = @$aThis;
613#1.3.2 Devide a array into Previous,Next
614 my @aPrev = splice(@aWk, 0, $iPos);
615 my @aNext = splice(@aWk, 1, $iCnt - $iPos -1);
616 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt(
617 \@aPrev, $raList, $rhInfo);
618 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt(
619 \@aNext, $raList, $rhInfo);
620 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt($aThis->[$iPos]->{Child}, $raList, $rhInfo);
621 return $aThis->[$iPos]->{No};
622 }
623}
624#------------------------------------------------------------------------------
625# _saveBbd (OLE::Storage_Lite)
626#------------------------------------------------------------------------------
627sub _saveBbd($$$$)
628{
629 my($oThis, $iSbdSize, $iBsize, $iPpsCnt, $rhInfo) = @_;
630 my $FILE = $rhInfo->{_FILEH_};
631#0. Calculate Basic Setting
632 my $iBbCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize();
633 my $iBlCnt = $iBbCnt - 1;
634 my $i1stBdL = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize());
635 my $i1stBdMax = $i1stBdL * $iBbCnt - $i1stBdL;
636 my $iBdExL = 0;
637 my $iAll = $iBsize + $iPpsCnt + $iSbdSize;
638 my $iAllW = $iAll;
639 my $iBdCntW = int($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
640 my $iBdCnt = 0;
641 my $i;
642#0.1 Calculate BD count
643 my $iBBleftover = $iAll - $i1stBdMax;
644 if ($iAll >$i1stBdMax) {
645
646 while(1) {
647 $iBdCnt = int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0);
648 $iBdExL = int(($iBdCnt) / $iBlCnt) + ((($iBdCnt) % $iBlCnt)? 1: 0);
649 $iBBleftover = $iBBleftover + $iBdExL;
650 last if($iBdCnt == (int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0)));
651 }
652 }
653 $iAllW += $iBdExL;
654 $iBdCnt += $i1stBdL;
655 #print "iBdCnt = $iBdCnt \n";
656
657#1. Making BD
658#1.1 Set for SBD
659 if($iSbdSize > 0) {
660 for ($i = 0; $i<($iSbdSize-1); $i++) {
661 print {$FILE} (pack("V", $i+1));
662 }
663 print {$FILE} (pack("V", -2));
664 }
665#1.2 Set for B
666 for ($i = 0; $i<($iBsize-1); $i++) {
667 print {$FILE} (pack("V", $i+$iSbdSize+1));
668 }
669 print {$FILE} (pack("V", -2));
670
671#1.3 Set for PPS
672 for ($i = 0; $i<($iPpsCnt-1); $i++) {
673 print {$FILE} (pack("V", $i+$iSbdSize+$iBsize+1));
674 }
675 print {$FILE} (pack("V", -2));
676#1.4 Set for BBD itself ( 0xFFFFFFFD : BBD)
677 for($i=0; $i<$iBdCnt;$i++) {
678 print {$FILE} (pack("V", 0xFFFFFFFD));
679 }
680#1.5 Set for ExtraBDList
681 for($i=0; $i<$iBdExL;$i++) {
682 print {$FILE} (pack("V", 0xFFFFFFFC));
683 }
684#1.6 Adjust for Block
685 print {$FILE} (pack("V", -1) x ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)))
686 if(($iAllW + $iBdCnt) % $iBbCnt);
687#2.Extra BDList
688 if($iBdCnt > $i1stBdL) {
689 my $iN=0;
690 my $iNb=0;
691 for($i=$i1stBdL;$i<$iBdCnt; $i++, $iN++) {
692 if($iN>=($iBbCnt-1)) {
693 $iN = 0;
694 $iNb++;
695 print {$FILE} (pack("V", $iAll+$iBdCnt+$iNb));
696 }
697 print {$FILE} (pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i));
698 }
699 print {$FILE} (pack("V", -1) x (($iBbCnt-1) - (($iBdCnt-$i1stBdL) % ($iBbCnt-1))))
700 if(($iBdCnt-$i1stBdL) % ($iBbCnt-1));
701 print {$FILE} (pack("V", -2));
702 }
703}
704
705#//////////////////////////////////////////////////////////////////////////////
706# OLE::Storage_Lite::PPS::File Object
707#//////////////////////////////////////////////////////////////////////////////
708#==============================================================================
709# OLE::Storage_Lite::PPS::File
710#==============================================================================
711package OLE::Storage_Lite::PPS::File;
712require Exporter;
713use strict;
714use vars qw($VERSION @ISA);
715@ISA = qw(OLE::Storage_Lite::PPS Exporter);
716$VERSION = '0.19';
717#------------------------------------------------------------------------------
718# new (OLE::Storage_Lite::PPS::File)
719#------------------------------------------------------------------------------
720sub new ($$$) {
721 my($sClass, $sNm, $sData) = @_;
722 OLE::Storage_Lite::PPS::_new(
723 $sClass,
724 undef,
725 $sNm,
726 2,
727 undef,
728 undef,
729 undef,
730 undef,
731 undef,
732 undef,
733 undef,
734 $sData,
735 undef);
736}
737#------------------------------------------------------------------------------
738# newFile (OLE::Storage_Lite::PPS::File)
739#------------------------------------------------------------------------------
740sub newFile ($$;$) {
741 my($sClass, $sNm, $sFile) = @_;
742 my $oSelf =
743 OLE::Storage_Lite::PPS::_new(
744 $sClass,
745 undef,
746 $sNm,
747 2,
748 undef,
749 undef,
750 undef,
751 undef,
752 undef,
753 undef,
754 undef,
755 '',
756 undef);
757#
758 if((!defined($sFile)) or ($sFile eq '')) {
759 $oSelf->{_PPS_FILE} = IO::File->new_tmpfile();
760 }
761 elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) {
762 $oSelf->{_PPS_FILE} = $sFile;
763 }
764 elsif(!ref($sFile)) {
765 #File Name
766 $oSelf->{_PPS_FILE} = new IO::File;
767 return undef unless($oSelf->{_PPS_FILE});
768 $oSelf->{_PPS_FILE}->open("$sFile", "r+") || return undef;
769 }
770 else {
771 return undef;
772 }
773 if($oSelf->{_PPS_FILE}) {
774 $oSelf->{_PPS_FILE}->seek(0, 2);
775 binmode($oSelf->{_PPS_FILE});
776 $oSelf->{_PPS_FILE}->autoflush(1);
777 }
778 return $oSelf;
779}
780#------------------------------------------------------------------------------
781# append (OLE::Storage_Lite::PPS::File)
782#------------------------------------------------------------------------------
783sub append ($$) {
784 my($oSelf, $sData) = @_;
785 if($oSelf->{_PPS_FILE}) {
786 print {$oSelf->{_PPS_FILE}} $sData;
787 }
788 else {
789 $oSelf->{Data} .= $sData;
790 }
791}
792
793#//////////////////////////////////////////////////////////////////////////////
794# OLE::Storage_Lite::PPS::Dir Object
795#//////////////////////////////////////////////////////////////////////////////
796#------------------------------------------------------------------------------
797# new (OLE::Storage_Lite::PPS::Dir)
798#------------------------------------------------------------------------------
799package OLE::Storage_Lite::PPS::Dir;
800require Exporter;
801use strict;
802use vars qw($VERSION @ISA);
803@ISA = qw(OLE::Storage_Lite::PPS Exporter);
804$VERSION = '0.19';
805sub new ($$;$$$) {
806 my($sClass, $sName, $raTime1st, $raTime2nd, $raChild) = @_;
807 OLE::Storage_Lite::PPS::_new(
808 $sClass,
809 undef,
810 $sName,
811 1,
812 undef,
813 undef,
814 undef,
815 $raTime1st,
816 $raTime2nd,
817 undef,
818 undef,
819 undef,
820 $raChild);
821}
822#==============================================================================
823# OLE::Storage_Lite
824#==============================================================================
825package OLE::Storage_Lite;
826require Exporter;
827
828use strict;
829use IO::File;
830use Time::Local 'timegm';
831
832use vars qw($VERSION @ISA @EXPORT);
833@ISA = qw(Exporter);
834$VERSION = '0.19';
835sub _getPpsSearch($$$$$;$);
836sub _getPpsTree($$$;$);
837#------------------------------------------------------------------------------
838# Const for OLE::Storage_Lite
839#------------------------------------------------------------------------------
840#0. Constants
841sub PpsType_Root {5};
842sub PpsType_Dir {1};
843sub PpsType_File {2};
844sub DataSizeSmall{0x1000};
845sub LongIntSize {4};
846sub PpsSize {0x80};
847#------------------------------------------------------------------------------
848# new OLE::Storage_Lite
849#------------------------------------------------------------------------------
850sub new($$) {
851 my($sClass, $sFile) = @_;
852 my $oThis = {
853 _FILE => $sFile,
854 };
855 bless $oThis;
856 return $oThis;
857}
858#------------------------------------------------------------------------------
859# getPpsTree: OLE::Storage_Lite
860#------------------------------------------------------------------------------
861sub getPpsTree($;$)
862{
863 my($oThis, $bData) = @_;
864#0.Init
865 my $rhInfo = _initParse($oThis->{_FILE});
866 return undef unless($rhInfo);
867#1. Get Data
868 my ($oPps) = _getPpsTree(0, $rhInfo, $bData);
869 close(IN);
870 return $oPps;
871}
872#------------------------------------------------------------------------------
873# getSearch: OLE::Storage_Lite
874#------------------------------------------------------------------------------
875sub getPpsSearch($$;$$)
876{
877 my($oThis, $raName, $bData, $iCase) = @_;
878#0.Init
879 my $rhInfo = _initParse($oThis->{_FILE});
880 return undef unless($rhInfo);
881#1. Get Data
882 my @aList = _getPpsSearch(0, $rhInfo, $raName, $bData, $iCase);
883 close(IN);
884 return @aList;
885}
886#------------------------------------------------------------------------------
887# getNthPps: OLE::Storage_Lite
888#------------------------------------------------------------------------------
889sub getNthPps($$;$)
890{
891 my($oThis, $iNo, $bData) = @_;
892#0.Init
893 my $rhInfo = _initParse($oThis->{_FILE});
894 return undef unless($rhInfo);
895#1. Get Data
896 my $oPps = _getNthPps($iNo, $rhInfo, $bData);
897 close IN;
898 return $oPps;
899}
900#------------------------------------------------------------------------------
901# _initParse: OLE::Storage_Lite
902#------------------------------------------------------------------------------
903sub _initParse($) {
904 my($sFile)=@_;
905 my $oIo;
906 #1. $sFile is Ref of scalar
907 if(ref($sFile) eq 'SCALAR') {
908 require IO::Scalar;
909 $oIo = new IO::Scalar;
910 $oIo->open($sFile);
911 }
912 #2. $sFile is a IO::Handle object
913 elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) {
914 $oIo = $sFile;
915 binmode($oIo);
916 }
917 #3. $sFile is a simple filename string
918 elsif(!ref($sFile)) {
919 $oIo = new IO::File;
920 $oIo->open("<$sFile") || return undef;
921 binmode($oIo);
922 }
923 #4 Assume that if $sFile is a ref then it is a valid filehandle
924 else {
925 $oIo = $sFile;
926 # Not all filehandles support binmode() so try it in an eval.
927 eval{ binmode $oIo };
928 }
929 return _getHeaderInfo($oIo);
930}
931#------------------------------------------------------------------------------
932# _getPpsTree: OLE::Storage_Lite
933#------------------------------------------------------------------------------
934sub _getPpsTree($$$;$) {
935 my($iNo, $rhInfo, $bData, $raDone) = @_;
936 if(defined($raDone)) {
937 return () if(grep {$_ ==$iNo} @$raDone);
938 }
939 else {
940 $raDone=[];
941 }
942 push @$raDone, $iNo;
943
944 my $iRootBlock = $rhInfo->{_ROOT_START} ;
945#1. Get Information about itself
946 my $oPps = _getNthPps($iNo, $rhInfo, $bData);
947#2. Child
948 if($oPps->{DirPps} != 0xFFFFFFFF) {
949 my @aChildL = _getPpsTree($oPps->{DirPps}, $rhInfo, $bData, $raDone);
950 $oPps->{Child} = \@aChildL;
951 }
952 else {
953 $oPps->{Child} = undef;
954 }
955#3. Previous,Next PPSs
956 my @aList = ();
957 push @aList, _getPpsTree($oPps->{PrevPps}, $rhInfo, $bData, $raDone)
958 if($oPps->{PrevPps} != 0xFFFFFFFF);
959 push @aList, $oPps;
960 push @aList, _getPpsTree($oPps->{NextPps}, $rhInfo, $bData, $raDone)
961 if($oPps->{NextPps} != 0xFFFFFFFF);
962 return @aList;
963}
964#------------------------------------------------------------------------------
965# _getPpsSearch: OLE::Storage_Lite
966#------------------------------------------------------------------------------
967sub _getPpsSearch($$$$$;$) {
968 my($iNo, $rhInfo, $raName, $bData, $iCase, $raDone) = @_;
969 my $iRootBlock = $rhInfo->{_ROOT_START} ;
970 my @aRes;
971#1. Check it self
972 if(defined($raDone)) {
973 return () if(grep {$_==$iNo} @$raDone);
974 }
975 else {
976 $raDone=[];
977 }
978 push @$raDone, $iNo;
979 my $oPps = _getNthPps($iNo, $rhInfo, undef);
980# if(grep($_ eq $oPps->{Name}, @$raName)) {
981 if(($iCase && (grep(/^\Q$oPps->{Name}\E$/i, @$raName))) ||
982 (grep($_ eq $oPps->{Name}, @$raName))) {
983 $oPps = _getNthPps($iNo, $rhInfo, $bData) if ($bData);
984 @aRes = ($oPps);
985 }
986 else {
987 @aRes = ();
988 }
989#2. Check Child, Previous, Next PPSs
990 push @aRes, _getPpsSearch($oPps->{DirPps}, $rhInfo, $raName, $bData, $iCase, $raDone)
991 if($oPps->{DirPps} != 0xFFFFFFFF) ;
992 push @aRes, _getPpsSearch($oPps->{PrevPps}, $rhInfo, $raName, $bData, $iCase, $raDone)
993 if($oPps->{PrevPps} != 0xFFFFFFFF );
994 push @aRes, _getPpsSearch($oPps->{NextPps}, $rhInfo, $raName, $bData, $iCase, $raDone)
995 if($oPps->{NextPps} != 0xFFFFFFFF);
996 return @aRes;
997}
998#===================================================================
999# Get Header Info (BASE Informain about that file)
1000#===================================================================
1001sub _getHeaderInfo($){
1002 my($FILE) = @_;
1003 my($iWk);
1004 my $rhInfo = {};
1005 $rhInfo->{_FILEH_} = $FILE;
1006 my $sWk;
1007#0. Check ID
1008 $rhInfo->{_FILEH_}->seek(0, 0);
1009 $rhInfo->{_FILEH_}->read($sWk, 8);
1010 return undef unless($sWk eq "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1");
1011#BIG BLOCK SIZE
1012 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x1E, 2, "v");
1013 return undef unless(defined($iWk));
1014 $rhInfo->{_BIG_BLOCK_SIZE} = 2 ** $iWk;
1015#SMALL BLOCK SIZE
1016 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x20, 2, "v");
1017 return undef unless(defined($iWk));
1018 $rhInfo->{_SMALL_BLOCK_SIZE} = 2 ** $iWk;
1019#BDB Count
1020 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x2C, 4, "V");
1021 return undef unless(defined($iWk));
1022 $rhInfo->{_BDB_COUNT} = $iWk;
1023#START BLOCK
1024 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x30, 4, "V");
1025 return undef unless(defined($iWk));
1026 $rhInfo->{_ROOT_START} = $iWk;
1027#MIN SIZE OF BB
1028# $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x38, 4, "V");
1029# return undef unless(defined($iWk));
1030# $rhInfo->{_MIN_SIZE_BB} = $iWk;
1031#SMALL BD START
1032 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x3C, 4, "V");
1033 return undef unless(defined($iWk));
1034 $rhInfo->{_SBD_START} = $iWk;
1035#SMALL BD COUNT
1036 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x40, 4, "V");
1037 return undef unless(defined($iWk));
1038 $rhInfo->{_SBD_COUNT} = $iWk;
1039#EXTRA BBD START
1040 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x44, 4, "V");
1041 return undef unless(defined($iWk));
1042 $rhInfo->{_EXTRA_BBD_START} = $iWk;
1043#EXTRA BD COUNT
1044 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x48, 4, "V");
1045 return undef unless(defined($iWk));
1046 $rhInfo->{_EXTRA_BBD_COUNT} = $iWk;
1047#GET BBD INFO
1048 $rhInfo->{_BBD_INFO}= _getBbdInfo($rhInfo);
1049#GET ROOT PPS
1050 my $oRoot = _getNthPps(0, $rhInfo, undef);
1051 $rhInfo->{_SB_START} = $oRoot->{StartBlock};
1052 $rhInfo->{_SB_SIZE} = $oRoot->{Size};
1053 return $rhInfo;
1054}
1055#------------------------------------------------------------------------------
1056# _getInfoFromFile
1057#------------------------------------------------------------------------------
1058sub _getInfoFromFile($$$$) {
1059 my($FILE, $iPos, $iLen, $sFmt) =@_;
1060 my($sWk);
1061 return undef unless($FILE);
1062 return undef if($FILE->seek($iPos, 0)==0);
1063 return undef if($FILE->read($sWk, $iLen)!=$iLen);
1064 return unpack($sFmt, $sWk);
1065}
1066#------------------------------------------------------------------------------
1067# _getBbdInfo
1068#------------------------------------------------------------------------------
1069sub _getBbdInfo($) {
1070 my($rhInfo) =@_;
1071 my @aBdList = ();
1072 my $iBdbCnt = $rhInfo->{_BDB_COUNT};
1073 my $iGetCnt;
1074 my $sWk;
1075 my $i1stCnt = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize());
1076 my $iBdlCnt = int($rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize()) - 1;
1077
1078#1. 1st BDlist
1079 $rhInfo->{_FILEH_}->seek(0x4C, 0);
1080 $iGetCnt = ($iBdbCnt < $i1stCnt)? $iBdbCnt: $i1stCnt;
1081 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()*$iGetCnt);
1082 push @aBdList, unpack("V$iGetCnt", $sWk);
1083 $iBdbCnt -= $iGetCnt;
1084#2. Extra BDList
1085 my $iBlock = $rhInfo->{_EXTRA_BBD_START};
1086 while(($iBdbCnt> 0) && _isNormalBlock($iBlock)){
1087 _setFilePos($iBlock, 0, $rhInfo);
1088 $iGetCnt= ($iBdbCnt < $iBdlCnt)? $iBdbCnt: $iBdlCnt;
1089 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()*$iGetCnt);
1090 push @aBdList, unpack("V$iGetCnt", $sWk);
1091 $iBdbCnt -= $iGetCnt;
1092 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize());
1093 $iBlock = unpack("V", $sWk);
1094 }
1095#3.Get BDs
1096 my @aWk;
1097 my %hBd;
1098 my $iBlkNo = 0;
1099 my $iBdL;
1100 my $i;
1101 my $iBdCnt = int($rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize());
1102 foreach $iBdL (@aBdList) {
1103 _setFilePos($iBdL, 0, $rhInfo);
1104 $rhInfo->{_FILEH_}->read($sWk, $rhInfo->{_BIG_BLOCK_SIZE});
1105 @aWk = unpack("V$iBdCnt", $sWk);
1106 for($i=0;$i<$iBdCnt;$i++, $iBlkNo++) {
1107 if($aWk[$i] != ($iBlkNo+1)){
1108 $hBd{$iBlkNo} = $aWk[$i];
1109 }
1110 }
1111 }
1112 return \%hBd;
1113}
1114#------------------------------------------------------------------------------
1115# getNthPps (OLE::Storage_Lite)
1116#------------------------------------------------------------------------------
1117sub _getNthPps($$$){
1118 my($iPos, $rhInfo, $bData) = @_;
1119 my($iPpsStart) = ($rhInfo->{_ROOT_START});
1120 my($iPpsBlock, $iPpsPos);
1121 my $sWk;
1122 my $iBlock;
1123
1124 my $iBaseCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::PpsSize();
1125 $iPpsBlock = int($iPos / $iBaseCnt);
1126 $iPpsPos = $iPos % $iBaseCnt;
1127
1128 $iBlock = _getNthBlockNo($iPpsStart, $iPpsBlock, $rhInfo);
1129 return undef unless(defined($iBlock));
1130
1131 _setFilePos($iBlock, OLE::Storage_Lite::PpsSize()*$iPpsPos, $rhInfo);
1132 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::PpsSize());
1133 return undef unless($sWk);
1134 my $iNmSize = unpack("v", substr($sWk, 0x40, 2));
1135 $iNmSize = ($iNmSize > 2)? $iNmSize - 2 : $iNmSize;
1136 my $sNm= substr($sWk, 0, $iNmSize);
1137 my $iType = unpack("C", substr($sWk, 0x42, 2));
1138 my $lPpsPrev = unpack("V", substr($sWk, 0x44, OLE::Storage_Lite::LongIntSize()));
1139 my $lPpsNext = unpack("V", substr($sWk, 0x48, OLE::Storage_Lite::LongIntSize()));
1140 my $lDirPps = unpack("V", substr($sWk, 0x4C, OLE::Storage_Lite::LongIntSize()));
1141 my @raTime1st =
1142 (($iType == OLE::Storage_Lite::PpsType_Root()) or ($iType == OLE::Storage_Lite::PpsType_Dir()))?
1143 OLEDate2Local(substr($sWk, 0x64, 8)) : undef ,
1144 my @raTime2nd =
1145 (($iType == OLE::Storage_Lite::PpsType_Root()) or ($iType == OLE::Storage_Lite::PpsType_Dir()))?
1146 OLEDate2Local(substr($sWk, 0x6C, 8)) : undef,
1147 my($iStart, $iSize) = unpack("VV", substr($sWk, 0x74, 8));
1148 if($bData) {
1149 my $sData = _getData($iType, $iStart, $iSize, $rhInfo);
1150 return OLE::Storage_Lite::PPS->new(
1151 $iPos, $sNm, $iType, $lPpsPrev, $lPpsNext, $lDirPps,
1152 \@raTime1st, \@raTime2nd, $iStart, $iSize, $sData, undef);
1153 }
1154 else {
1155 return OLE::Storage_Lite::PPS->new(
1156 $iPos, $sNm, $iType, $lPpsPrev, $lPpsNext, $lDirPps,
1157 \@raTime1st, \@raTime2nd, $iStart, $iSize, undef, undef);
1158 }
1159}
1160#------------------------------------------------------------------------------
1161# _setFilePos (OLE::Storage_Lite)
1162#------------------------------------------------------------------------------
1163sub _setFilePos($$$){
1164 my($iBlock, $iPos, $rhInfo) = @_;
1165 $rhInfo->{_FILEH_}->seek(($iBlock+1)*$rhInfo->{_BIG_BLOCK_SIZE}+$iPos, 0);
1166}
1167#------------------------------------------------------------------------------
1168# _getNthBlockNo (OLE::Storage_Lite)
1169#------------------------------------------------------------------------------
1170sub _getNthBlockNo($$$){
1171 my($iStBlock, $iNth, $rhInfo) = @_;
1172 my $iSv;
1173 my $iNext = $iStBlock;
1174 for(my $i =0; $i<$iNth; $i++) {
1175 $iSv = $iNext;
1176 $iNext = _getNextBlockNo($iSv, $rhInfo);
1177 return undef unless _isNormalBlock($iNext);
1178 }
1179 return $iNext;
1180}
1181#------------------------------------------------------------------------------
1182# _getData (OLE::Storage_Lite)
1183#------------------------------------------------------------------------------
1184sub _getData($$$$)
1185{
1186 my($iType, $iBlock, $iSize, $rhInfo) = @_;
1187 if ($iType == OLE::Storage_Lite::PpsType_File()) {
1188 if($iSize < OLE::Storage_Lite::DataSizeSmall()) {
1189 return _getSmallData($iBlock, $iSize, $rhInfo);
1190 }
1191 else {
1192 return _getBigData($iBlock, $iSize, $rhInfo);
1193 }
1194 }
1195 elsif($iType == OLE::Storage_Lite::PpsType_Root()) { #Root
1196 return _getBigData($iBlock, $iSize, $rhInfo);
1197 }
1198 elsif($iType == OLE::Storage_Lite::PpsType_Dir()) { # Directory
1199 return undef;
1200 }
1201}
1202#------------------------------------------------------------------------------
1203# _getBigData (OLE::Storage_Lite)
1204#------------------------------------------------------------------------------
1205sub _getBigData($$$)
1206{
1207 my($iBlock, $iSize, $rhInfo) = @_;
1208 my($iRest, $sWk, $sRes);
1209
1210 return '' unless(_isNormalBlock($iBlock));
1211 $iRest = $iSize;
1212 my($i, $iGetSize, $iNext);
1213 $sRes = '';
1214 my @aKeys= sort({$a<=>$b} keys(%{$rhInfo->{_BBD_INFO}}));
1215
1216 while ($iRest > 0) {
1217 my @aRes = grep($_ >= $iBlock, @aKeys);
1218 my $iNKey = $aRes[0];
1219 $i = $iNKey - $iBlock;
1220 $iNext = $rhInfo->{_BBD_INFO}{$iNKey};
1221 _setFilePos($iBlock, 0, $rhInfo);
1222 my $iGetSize = ($rhInfo->{_BIG_BLOCK_SIZE} * ($i+1));
1223 $iGetSize = $iRest if($iRest < $iGetSize);
1224 $rhInfo->{_FILEH_}->read( $sWk, $iGetSize);
1225 $sRes .= $sWk;
1226 $iRest -= $iGetSize;
1227 $iBlock= $iNext;
1228 }
1229 return $sRes;
1230}
1231#------------------------------------------------------------------------------
1232# _getNextBlockNo (OLE::Storage_Lite)
1233#------------------------------------------------------------------------------
1234sub _getNextBlockNo($$){
1235 my($iBlockNo, $rhInfo) = @_;
1236 my $iRes = $rhInfo->{_BBD_INFO}->{$iBlockNo};
1237 return defined($iRes)? $iRes: $iBlockNo+1;
1238}
1239#------------------------------------------------------------------------------
1240# _isNormalBlock (OLE::Storage_Lite)
1241# 0xFFFFFFFC : BDList, 0xFFFFFFFD : BBD,
1242# 0xFFFFFFFE: End of Chain 0xFFFFFFFF : unused
1243#------------------------------------------------------------------------------
1244sub _isNormalBlock($){
1245 my($iBlock) = @_;
1246 return ($iBlock < 0xFFFFFFFC)? 1: undef;
1247}
1248#------------------------------------------------------------------------------
1249# _getSmallData (OLE::Storage_Lite)
1250#------------------------------------------------------------------------------
1251sub _getSmallData($$$)
1252{
1253 my($iSmBlock, $iSize, $rhInfo) = @_;
1254 my($sRes, $sWk);
1255 my $iRest = $iSize;
1256 $sRes = '';
1257 while ($iRest > 0) {
1258 _setFilePosSmall($iSmBlock, $rhInfo);
1259 $rhInfo->{_FILEH_}->read($sWk,
1260 ($iRest >= $rhInfo->{_SMALL_BLOCK_SIZE})?
1261 $rhInfo->{_SMALL_BLOCK_SIZE}: $iRest);
1262 $sRes .= $sWk;
1263 $iRest -= $rhInfo->{_SMALL_BLOCK_SIZE};
1264 $iSmBlock= _getNextSmallBlockNo($iSmBlock, $rhInfo);
1265 }
1266 return $sRes;
1267}
1268#------------------------------------------------------------------------------
1269# _setFilePosSmall(OLE::Storage_Lite)
1270#------------------------------------------------------------------------------
1271sub _setFilePosSmall($$)
1272{
1273 my($iSmBlock, $rhInfo) = @_;
1274 my $iSmStart = $rhInfo->{_SB_START};
1275 my $iBaseCnt = $rhInfo->{_BIG_BLOCK_SIZE} / $rhInfo->{_SMALL_BLOCK_SIZE};
1276 my $iNth = int($iSmBlock/$iBaseCnt);
1277 my $iPos = $iSmBlock % $iBaseCnt;
1278
1279 my $iBlk = _getNthBlockNo($iSmStart, $iNth, $rhInfo);
1280 _setFilePos($iBlk, $iPos * $rhInfo->{_SMALL_BLOCK_SIZE}, $rhInfo);
1281}
1282#------------------------------------------------------------------------------
1283# _getNextSmallBlockNo (OLE::Storage_Lite)
1284#------------------------------------------------------------------------------
1285sub _getNextSmallBlockNo($$)
1286{
1287 my($iSmBlock, $rhInfo) = @_;
1288 my($sWk);
1289
1290 my $iBaseCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize();
1291 my $iNth = int($iSmBlock/$iBaseCnt);
1292 my $iPos = $iSmBlock % $iBaseCnt;
1293 my $iBlk = _getNthBlockNo($rhInfo->{_SBD_START}, $iNth, $rhInfo);
1294 _setFilePos($iBlk, $iPos * OLE::Storage_Lite::LongIntSize(), $rhInfo);
1295 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize());
1296 return unpack("V", $sWk);
1297
1298}
1299#------------------------------------------------------------------------------
1300# Asc2Ucs: OLE::Storage_Lite
1301#------------------------------------------------------------------------------
1302sub Asc2Ucs($)
1303{
1304 my($sAsc) = @_;
1305 return join("\x00", split //, $sAsc) . "\x00";
1306}
1307#------------------------------------------------------------------------------
1308# Ucs2Asc: OLE::Storage_Lite
1309#------------------------------------------------------------------------------
1310sub Ucs2Asc($)
1311{
1312 my($sUcs) = @_;
1313 return join('', map(pack('c', $_), unpack('v*', $sUcs)));
1314}
1315
1316#------------------------------------------------------------------------------
1317# OLEDate2Local()
1318#
1319# Convert from a Window FILETIME structure to a localtime array. FILETIME is
1320# a 64-bit value representing the number of 100-nanosecond intervals since
1321# January 1 1601.
1322#
1323# We first convert the FILETIME to seconds and then subtract the difference
1324# between the 1601 epoch and the 1970 Unix epoch.
1325#
1326sub OLEDate2Local {
1327
1328 my $oletime = shift;
1329
1330 # Unpack the FILETIME into high and low longs.
1331 my ( $lo, $hi ) = unpack 'V2', $oletime;
1332
1333 # Convert the longs to a double.
1334 my $nanoseconds = $hi * 2**32 + $lo;
1335
1336 # Convert the 100 nanosecond units into seconds.
1337 my $time = $nanoseconds / 1e7;
1338
1339 # Subtract the number of seconds between the 1601 and 1970 epochs.
1340 $time -= 11644473600;
1341
1342 # Convert to a localtime (actually gmtime) structure.
1343 my @localtime = gmtime($time);
1344
1345 return @localtime;
1346}
1347
1348#------------------------------------------------------------------------------
1349# LocalDate2OLE()
1350#
1351# Convert from a a localtime array to a Window FILETIME structure. FILETIME is
1352# a 64-bit value representing the number of 100-nanosecond intervals since
1353# January 1 1601.
1354#
1355# We first convert the localtime (actually gmtime) to seconds and then add the
1356# difference between the 1601 epoch and the 1970 Unix epoch. We convert that to
1357# 100 nanosecond units, divide it into high and low longs and return it as a
1358# packed 64bit structure.
1359#
1360sub LocalDate2OLE {
1361
1362 my $localtime = shift;
1363
1364 return "\x00" x 8 unless $localtime;
1365
1366 # Convert from localtime (actually gmtime) to seconds.
1367 my $time = timegm( @{$localtime} );
1368
1369 # Add the number of seconds between the 1601 and 1970 epochs.
1370 $time += 11644473600;
1371
1372 # The FILETIME seconds are in units of 100 nanoseconds.
1373 my $nanoseconds = $time * 1E7;
1374
1375use POSIX 'fmod';
1376
1377 # Pack the total nanoseconds into 64 bits...
1378 my $hi = int( $nanoseconds / 2**32 );
1379 my $lo = fmod($nanoseconds, 2**32);
1380
1381 my $oletime = pack "VV", $lo, $hi;
1382
1383 return $oletime;
1384}
1385
13861;
1387__END__
1388
1389
1390=head1 NAME
1391
1392OLE::Storage_Lite - Simple Class for OLE document interface.
1393
1394=head1 SYNOPSIS
1395
1396 use OLE::Storage_Lite;
1397
1398 # Initialize.
1399
1400 # From a file
1401 my $oOl = OLE::Storage_Lite->new("some.xls");
1402
1403 # From a filehandle object
1404 use IO::File;
1405 my $oIo = new IO::File;
1406 $oIo->open("<iofile.xls");
1407 binmode($oIo);
1408 my $oOl = OLE::Storage_Lite->new($oFile);
1409
1410 # Read data
1411 my $oPps = $oOl->getPpsTree(1);
1412
1413 # Save Data
1414 # To a File
1415 $oPps->save("kaba.xls"); #kaba.xls
1416 $oPps->save('-'); #STDOUT
1417
1418 # To a filehandle object
1419 my $oIo = new IO::File;
1420 $oIo->open(">iofile.xls");
1421 bimode($oIo);
1422 $oPps->save($oIo);
1423
1424
1425=head1 DESCRIPTION
1426
1427OLE::Storage_Lite allows you to read and write an OLE structured file.
1428
1429OLE::Storage_Lite::PPS is a class representing PPS. OLE::Storage_Lite::PPS::Root, OLE::Storage_Lite::PPS::File and OLE::Storage_Lite::PPS::Dir
1430are subclasses of OLE::Storage_Lite::PPS.
1431
1432
1433=head2 new()
1434
1435Constructor.
1436
1437 $oOle = OLE::Storage_Lite->new($sFile);
1438
1439Creates a OLE::Storage_Lite object for C<$sFile>. C<$sFile> must be a correct file name.
1440
1441The C<new()> constructor also accepts a valid filehandle. Remember to C<binmode()> the filehandle first.
1442
1443
1444=head2 getPpsTree()
1445
1446 $oPpsRoot = $oOle->getPpsTree([$bData]);
1447
1448Returns PPS as an OLE::Storage_Lite::PPS::Root object.
1449Other PPS objects will be included as its children.
1450
1451If C<$bData> is true, the objects will have data in the file.
1452
1453
1454=head2 getPpsSearch()
1455
1456 $oPpsRoot = $oOle->getPpsTree($raName [, $bData][, $iCase] );
1457
1458Returns PPSs as OLE::Storage_Lite::PPS objects that has the name specified in C<$raName> array.
1459
1460If C<$bData> is true, the objects will have data in the file.
1461If C<$iCase> is true, search is case insensitive.
1462
1463
1464=head2 getNthPps()
1465
1466 $oPpsRoot = $oOle->getNthPps($iNth [, $bData]);
1467
1468Returns PPS as C<OLE::Storage_Lite::PPS> object specified number C<$iNth>.
1469
1470If C<$bData> is true, the objects will have data in the file.
1471
1472
1473=head2 Asc2Ucs()
1474
1475 $sUcs2 = OLE::Storage_Lite::Asc2Ucs($sAsc>);
1476
1477Utility function. Just adds 0x00 after every characters in C<$sAsc>.
1478
1479
1480=head2 Ucs2Asc()
1481
1482 $sAsc = OLE::Storage_Lite::Ucs2Asc($sUcs2);
1483
1484Utility function. Just deletes 0x00 after words in C<$sUcs>.
1485
1486
1487=head1 OLE::Storage_Lite::PPS
1488
1489OLE::Storage_Lite::PPS has these properties:
1490
1491=over 4
1492
1493=item No
1494
1495Order number in saving.
1496
1497=item Name
1498
1499Its name in UCS2 (a.k.a Unicode).
1500
1501=item Type
1502
1503Its type (1:Dir, 2:File (Data), 5: Root)
1504
1505=item PrevPps
1506
1507Previous pps (as No)
1508
1509=item NextPps
1510
1511Next pps (as No)
1512
1513=item DirPps
1514
1515Dir pps (as No).
1516
1517=item Time1st
1518
1519Timestamp 1st in array ref as similar fomat of localtime.
1520
1521=item Time2nd
1522
1523Timestamp 2nd in array ref as similar fomat of localtime.
1524
1525=item StartBlock
1526
1527Start block number
1528
1529=item Size
1530
1531Size of the pps
1532
1533=item Data
1534
1535Its data
1536
1537=item Child
1538
1539Its child PPSs in array ref
1540
1541=back
1542
1543
1544=head1 OLE::Storage_Lite::PPS::Root
1545
1546OLE::Storage_Lite::PPS::Root has 2 methods.
1547
1548=head2 new()
1549
1550 $oRoot = OLE::Storage_Lite::PPS::Root->new(
1551 $raTime1st,
1552 $raTime2nd,
1553 $raChild);
1554
1555
1556Constructor.
1557
1558C<$raTime1st>, C<$raTime2nd> are array refs with ($iSec, $iMin, $iHour, $iDay, $iMon, $iYear).
1559$iSec means seconds, $iMin means minutes. $iHour means hours.
1560$iDay means day. $iMon is month -1. $iYear is year - 1900.
1561
1562C<$raChild> is a array ref of children PPSs.
1563
1564
1565=head2 save()
1566
1567 $oRoot = $oRoot>->save(
1568 $sFile,
1569 $bNoAs);
1570
1571
1572Saves information into C<$sFile>. If C<$sFile> is '-', this will use STDOUT.
1573
1574The C<new()> constructor also accepts a valid filehandle. Remember to C<binmode()> the filehandle first.
1575
1576If C<$bNoAs> is defined, this function will use the No of PPSs for saving order.
1577If C<$bNoAs> is undefined, this will calculate PPS saving order.
1578
1579
1580=head1 OLE::Storage_Lite::PPS::Dir
1581
1582OLE::Storage_Lite::PPS::Dir has 1 method.
1583
1584=head2 new()
1585
1586 $oRoot = OLE::Storage_Lite::PPS::Dir->new(
1587 $sName,
1588 [, $raTime1st]
1589 [, $raTime2nd]
1590 [, $raChild>]);
1591
1592
1593Constructor.
1594
1595C<$sName> is a name of the PPS.
1596
1597C<$raTime1st>, C<$raTime2nd> is a array ref as
1598($iSec, $iMin, $iHour, $iDay, $iMon, $iYear).
1599$iSec means seconds, $iMin means minutes. $iHour means hours.
1600$iDay means day. $iMon is month -1. $iYear is year - 1900.
1601
1602C<$raChild> is a array ref of children PPSs.
1603
1604
1605=head1 OLE::Storage_Lite::PPS::File
1606
1607OLE::Storage_Lite::PPS::File has 3 method.
1608
1609=head2 new
1610
1611 $oRoot = OLE::Storage_Lite::PPS::File->new($sName, $sData);
1612
1613C<$sName> is name of the PPS.
1614
1615C<$sData> is data of the PPS.
1616
1617
1618=head2 newFile()
1619
1620 $oRoot = OLE::Storage_Lite::PPS::File->newFile($sName, $sFile);
1621
1622This function makes to use file handle for geting and storing data.
1623
1624C<$sName> is name of the PPS.
1625
1626If C<$sFile> is scalar, it assumes that is a filename.
1627If C<$sFile> is an IO::Handle object, it uses that specified handle.
1628If C<$sFile> is undef or '', it uses temporary file.
1629
1630CAUTION: Take care C<$sFile> will be updated by C<append> method.
1631So if you want to use IO::Handle and append a data to it,
1632you should open the handle with "r+".
1633
1634
1635=head2 append()
1636
1637 $oRoot = $oPps->append($sData);
1638
1639appends specified data to that PPS.
1640
1641C<$sData> is appending data for that PPS.
1642
1643
1644=head1 CAUTION
1645
1646A saved file with VBA (a.k.a Macros) by this module will not work correctly.
1647However modules can get the same information from the file,
1648the file occurs a error in application(Word, Excel ...).
1649
1650
1651=head1 DEPRECATED FEATURES
1652
1653Older version of C<OLE::Storage_Lite> autovivified a scalar ref in the C<new()> constructors into a scalar filehandle. This functionality is still there for backwards compatibility but it is highly recommended that you do not use it. Instead create a filehandle (scalar or otherwise) and pass that in.
1654
1655
1656=head1 COPYRIGHT
1657
1658The OLE::Storage_Lite module is Copyright (c) 2000,2001 Kawai Takanori. Japan.
1659All rights reserved.
1660
1661You may distribute under the terms of either the GNU General Public
1662License or the Artistic License, as specified in the Perl README file.
1663
1664
1665=head1 ACKNOWLEDGEMENTS
1666
1667First of all, I would like to acknowledge to Martin Schwartz and his module OLE::Storage.
1668
1669
1670=head1 AUTHOR
1671
1672Kawai Takanori kwitknr@cpan.org
1673
1674This module is currently maintained by John McNamara jmcnamara@cpan.org
1675
1676
1677=head1 SEE ALSO
1678
1679OLE::Storage
1680
1681Documentation for the OLE Compound document has been released by Microsoft under the I<Open Specification Promise>. See http://www.microsoft.com/interop/docs/supportingtechnologies.mspx
1682
1683The Digital Imaging Group have also detailed the OLE format in the JPEG2000 specification: see Appendix A of http://www.i3a.org/pdf/wg1n1017.pdf
1684
1685
1686=cut