# OLE::Storage_Lite | |
# by Kawai, Takanori (Hippo2000) 2000.11.4, 8, 14 | |
# This Program is Still ALPHA version. | |
#////////////////////////////////////////////////////////////////////////////// | |
# OLE::Storage_Lite::PPS Object | |
#////////////////////////////////////////////////////////////////////////////// | |
#============================================================================== | |
# OLE::Storage_Lite::PPS | |
#============================================================================== | |
package OLE::Storage_Lite::PPS; | |
require Exporter; | |
use strict; | |
use vars qw($VERSION @ISA); | |
@ISA = qw(Exporter); | |
$VERSION = '0.19'; | |
#------------------------------------------------------------------------------ | |
# new (OLE::Storage_Lite::PPS) | |
#------------------------------------------------------------------------------ | |
sub new ($$$$$$$$$$;$$) { | |
#1. Constructor for General Usage | |
my($sClass, $iNo, $sNm, $iType, $iPrev, $iNext, $iDir, | |
$raTime1st, $raTime2nd, $iStart, $iSize, $sData, $raChild) = @_; | |
if($iType == OLE::Storage_Lite::PpsType_File()) { #FILE | |
return OLE::Storage_Lite::PPS::File->_new | |
($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd, | |
$iStart, $iSize, $sData, $raChild); | |
} | |
elsif($iType == OLE::Storage_Lite::PpsType_Dir()) { #DIRECTRY | |
return OLE::Storage_Lite::PPS::Dir->_new | |
($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd, | |
$iStart, $iSize, $sData, $raChild); | |
} | |
elsif($iType == OLE::Storage_Lite::PpsType_Root()) { #ROOT | |
return OLE::Storage_Lite::PPS::Root->_new | |
($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd, | |
$iStart, $iSize, $sData, $raChild); | |
} | |
else { | |
die "Error PPS:$iType $sNm\n"; | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _new (OLE::Storage_Lite::PPS) | |
# for OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub _new ($$$$$$$$$$$;$$) { | |
my($sClass, $iNo, $sNm, $iType, $iPrev, $iNext, $iDir, | |
$raTime1st, $raTime2nd, $iStart, $iSize, $sData, $raChild) = @_; | |
#1. Constructor for OLE::Storage_Lite | |
my $oThis = { | |
No => $iNo, | |
Name => $sNm, | |
Type => $iType, | |
PrevPps => $iPrev, | |
NextPps => $iNext, | |
DirPps => $iDir, | |
Time1st => $raTime1st, | |
Time2nd => $raTime2nd, | |
StartBlock => $iStart, | |
Size => $iSize, | |
Data => $sData, | |
Child => $raChild, | |
}; | |
bless $oThis, $sClass; | |
return $oThis; | |
} | |
#------------------------------------------------------------------------------ | |
# _DataLen (OLE::Storage_Lite::PPS) | |
# Check for update | |
#------------------------------------------------------------------------------ | |
sub _DataLen($) { | |
my($oSelf) =@_; | |
return 0 unless(defined($oSelf->{Data})); | |
return ($oSelf->{_PPS_FILE})? | |
($oSelf->{_PPS_FILE}->stat())[7] : length($oSelf->{Data}); | |
} | |
#------------------------------------------------------------------------------ | |
# _makeSmallData (OLE::Storage_Lite::PPS) | |
#------------------------------------------------------------------------------ | |
sub _makeSmallData($$$) { | |
my($oThis, $aList, $rhInfo) = @_; | |
my ($sRes); | |
my $FILE = $rhInfo->{_FILEH_}; | |
my $iSmBlk = 0; | |
foreach my $oPps (@$aList) { | |
#1. Make SBD, small data string | |
if($oPps->{Type}==OLE::Storage_Lite::PpsType_File()) { | |
next if($oPps->{Size}<=0); | |
if($oPps->{Size} < $rhInfo->{_SMALL_SIZE}) { | |
my $iSmbCnt = int($oPps->{Size} / $rhInfo->{_SMALL_BLOCK_SIZE}) | |
+ (($oPps->{Size} % $rhInfo->{_SMALL_BLOCK_SIZE})? 1: 0); | |
#1.1 Add to SBD | |
for (my $i = 0; $i<($iSmbCnt-1); $i++) { | |
print {$FILE} (pack("V", $i+$iSmBlk+1)); | |
} | |
print {$FILE} (pack("V", -2)); | |
#1.2 Add to Data String(this will be written for RootEntry) | |
#Check for update | |
if($oPps->{_PPS_FILE}) { | |
my $sBuff; | |
$oPps->{_PPS_FILE}->seek(0, 0); #To The Top | |
while($oPps->{_PPS_FILE}->read($sBuff, 4096)) { | |
$sRes .= $sBuff; | |
} | |
} | |
else { | |
$sRes .= $oPps->{Data}; | |
} | |
$sRes .= ("\x00" x | |
($rhInfo->{_SMALL_BLOCK_SIZE} - ($oPps->{Size}% $rhInfo->{_SMALL_BLOCK_SIZE}))) | |
if($oPps->{Size}% $rhInfo->{_SMALL_BLOCK_SIZE}); | |
#1.3 Set for PPS | |
$oPps->{StartBlock} = $iSmBlk; | |
$iSmBlk += $iSmbCnt; | |
} | |
} | |
} | |
my $iSbCnt = int($rhInfo->{_BIG_BLOCK_SIZE}/ OLE::Storage_Lite::LongIntSize()); | |
print {$FILE} (pack("V", -1) x ($iSbCnt - ($iSmBlk % $iSbCnt))) | |
if($iSmBlk % $iSbCnt); | |
#2. Write SBD with adjusting length for block | |
return $sRes; | |
} | |
#------------------------------------------------------------------------------ | |
# _savePpsWk (OLE::Storage_Lite::PPS) | |
#------------------------------------------------------------------------------ | |
sub _savePpsWk($$) | |
{ | |
my($oThis, $rhInfo) = @_; | |
#1. Write PPS | |
my $FILE = $rhInfo->{_FILEH_}; | |
print {$FILE} ( | |
$oThis->{Name} | |
. ("\x00" x (64 - length($oThis->{Name}))) #64 | |
, pack("v", length($oThis->{Name}) + 2) #66 | |
, pack("c", $oThis->{Type}) #67 | |
, pack("c", 0x00) #UK #68 | |
, pack("V", $oThis->{PrevPps}) #Prev #72 | |
, pack("V", $oThis->{NextPps}) #Next #76 | |
, pack("V", $oThis->{DirPps}) #Dir #80 | |
, "\x00\x09\x02\x00" #84 | |
, "\x00\x00\x00\x00" #88 | |
, "\xc0\x00\x00\x00" #92 | |
, "\x00\x00\x00\x46" #96 | |
, "\x00\x00\x00\x00" #100 | |
, OLE::Storage_Lite::LocalDate2OLE($oThis->{Time1st}) #108 | |
, OLE::Storage_Lite::LocalDate2OLE($oThis->{Time2nd}) #116 | |
, pack("V", defined($oThis->{StartBlock})? | |
$oThis->{StartBlock}:0) #116 | |
, pack("V", defined($oThis->{Size})? | |
$oThis->{Size} : 0) #124 | |
, pack("V", 0), #128 | |
); | |
} | |
#////////////////////////////////////////////////////////////////////////////// | |
# OLE::Storage_Lite::PPS::Root Object | |
#////////////////////////////////////////////////////////////////////////////// | |
#============================================================================== | |
# OLE::Storage_Lite::PPS::Root | |
#============================================================================== | |
package OLE::Storage_Lite::PPS::Root; | |
require Exporter; | |
use strict; | |
use IO::File; | |
use IO::Handle; | |
use Fcntl; | |
use vars qw($VERSION @ISA); | |
@ISA = qw(OLE::Storage_Lite::PPS Exporter); | |
$VERSION = '0.19'; | |
sub _savePpsSetPnt($$$); | |
sub _savePpsSetPnt2($$$); | |
#------------------------------------------------------------------------------ | |
# new (OLE::Storage_Lite::PPS::Root) | |
#------------------------------------------------------------------------------ | |
sub new ($;$$$) { | |
my($sClass, $raTime1st, $raTime2nd, $raChild) = @_; | |
OLE::Storage_Lite::PPS::_new( | |
$sClass, | |
undef, | |
OLE::Storage_Lite::Asc2Ucs('Root Entry'), | |
5, | |
undef, | |
undef, | |
undef, | |
$raTime1st, | |
$raTime2nd, | |
undef, | |
undef, | |
undef, | |
$raChild); | |
} | |
#------------------------------------------------------------------------------ | |
# save (OLE::Storage_Lite::PPS::Root) | |
#------------------------------------------------------------------------------ | |
sub save($$;$$) { | |
my($oThis, $sFile, $bNoAs, $rhInfo) = @_; | |
#0.Initial Setting for saving | |
$rhInfo = {} unless($rhInfo); | |
$rhInfo->{_BIG_BLOCK_SIZE} = 2** | |
(($rhInfo->{_BIG_BLOCK_SIZE})? | |
_adjust2($rhInfo->{_BIG_BLOCK_SIZE}) : 9); | |
$rhInfo->{_SMALL_BLOCK_SIZE}= 2 ** | |
(($rhInfo->{_SMALL_BLOCK_SIZE})? | |
_adjust2($rhInfo->{_SMALL_BLOCK_SIZE}): 6); | |
$rhInfo->{_SMALL_SIZE} = 0x1000; | |
$rhInfo->{_PPS_SIZE} = 0x80; | |
my $closeFile = 1; | |
#1.Open File | |
#1.1 $sFile is Ref of scalar | |
if(ref($sFile) eq 'SCALAR') { | |
require IO::Scalar; | |
my $oIo = new IO::Scalar $sFile, O_WRONLY; | |
$rhInfo->{_FILEH_} = $oIo; | |
} | |
#1.1.1 $sFile is a IO::Scalar object | |
# Now handled as a filehandle ref below. | |
#1.2 $sFile is a IO::Handle object | |
elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) { | |
# Not all filehandles support binmode() so try it in an eval. | |
eval{ binmode $sFile }; | |
$rhInfo->{_FILEH_} = $sFile; | |
} | |
#1.3 $sFile is a simple filename string | |
elsif(!ref($sFile)) { | |
if($sFile ne '-') { | |
my $oIo = new IO::File; | |
$oIo->open(">$sFile") || return undef; | |
binmode($oIo); | |
$rhInfo->{_FILEH_} = $oIo; | |
} | |
else { | |
my $oIo = new IO::Handle; | |
$oIo->fdopen(fileno(STDOUT),"w") || return undef; | |
binmode($oIo); | |
$rhInfo->{_FILEH_} = $oIo; | |
} | |
} | |
#1.4 Assume that if $sFile is a ref then it is a valid filehandle | |
else { | |
# Not all filehandles support binmode() so try it in an eval. | |
eval{ binmode $sFile }; | |
$rhInfo->{_FILEH_} = $sFile; | |
# Caller controls filehandle closing | |
$closeFile = 0; | |
} | |
my $iBlk = 0; | |
#1. Make an array of PPS (for Save) | |
my @aList=(); | |
if($bNoAs) { | |
_savePpsSetPnt2([$oThis], \@aList, $rhInfo); | |
} | |
else { | |
_savePpsSetPnt([$oThis], \@aList, $rhInfo); | |
} | |
my ($iSBDcnt, $iBBcnt, $iPPScnt) = $oThis->_calcSize(\@aList, $rhInfo); | |
#2.Save Header | |
$oThis->_saveHeader($rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt); | |
#3.Make Small Data string (write SBD) | |
my $sSmWk = $oThis->_makeSmallData(\@aList, $rhInfo); | |
$oThis->{Data} = $sSmWk; #Small Datas become RootEntry Data | |
#4. Write BB | |
my $iBBlk = $iSBDcnt; | |
$oThis->_saveBigData(\$iBBlk, \@aList, $rhInfo); | |
#5. Write PPS | |
$oThis->_savePps(\@aList, $rhInfo); | |
#6. Write BD and BDList and Adding Header informations | |
$oThis->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt, $rhInfo); | |
#7.Close File | |
return $rhInfo->{_FILEH_}->close if $closeFile; | |
} | |
#------------------------------------------------------------------------------ | |
# _calcSize (OLE::Storage_Lite::PPS) | |
#------------------------------------------------------------------------------ | |
sub _calcSize($$) | |
{ | |
my($oThis, $raList, $rhInfo) = @_; | |
#0. Calculate Basic Setting | |
my ($iSBDcnt, $iBBcnt, $iPPScnt) = (0,0,0); | |
my $iSmallLen = 0; | |
my $iSBcnt = 0; | |
foreach my $oPps (@$raList) { | |
if($oPps->{Type}==OLE::Storage_Lite::PpsType_File()) { | |
$oPps->{Size} = $oPps->_DataLen(); #Mod | |
if($oPps->{Size} < $rhInfo->{_SMALL_SIZE}) { | |
$iSBcnt += int($oPps->{Size} / $rhInfo->{_SMALL_BLOCK_SIZE}) | |
+ (($oPps->{Size} % $rhInfo->{_SMALL_BLOCK_SIZE})? 1: 0); | |
} | |
else { | |
$iBBcnt += | |
(int($oPps->{Size}/ $rhInfo->{_BIG_BLOCK_SIZE}) + | |
(($oPps->{Size}% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0)); | |
} | |
} | |
} | |
$iSmallLen = $iSBcnt * $rhInfo->{_SMALL_BLOCK_SIZE}; | |
my $iSlCnt = int($rhInfo->{_BIG_BLOCK_SIZE}/ OLE::Storage_Lite::LongIntSize()); | |
$iSBDcnt = int($iSBcnt / $iSlCnt)+ (($iSBcnt % $iSlCnt)? 1:0); | |
$iBBcnt += (int($iSmallLen/ $rhInfo->{_BIG_BLOCK_SIZE}) + | |
(( $iSmallLen% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0)); | |
my $iCnt = scalar(@$raList); | |
my $iBdCnt = $rhInfo->{_BIG_BLOCK_SIZE}/OLE::Storage_Lite::PpsSize(); | |
$iPPScnt = (int($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0)); | |
return ($iSBDcnt, $iBBcnt, $iPPScnt); | |
} | |
#------------------------------------------------------------------------------ | |
# _adjust2 (OLE::Storage_Lite::PPS::Root) | |
#------------------------------------------------------------------------------ | |
sub _adjust2($) { | |
my($i2) = @_; | |
my $iWk; | |
$iWk = log($i2)/log(2); | |
return ($iWk > int($iWk))? int($iWk)+1:$iWk; | |
} | |
#------------------------------------------------------------------------------ | |
# _saveHeader (OLE::Storage_Lite::PPS::Root) | |
#------------------------------------------------------------------------------ | |
sub _saveHeader($$$$$) { | |
my($oThis, $rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt) = @_; | |
my $FILE = $rhInfo->{_FILEH_}; | |
#0. Calculate Basic Setting | |
my $iBlCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize(); | |
my $i1stBdL = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize()); | |
my $i1stBdMax = $i1stBdL * $iBlCnt - $i1stBdL; | |
my $iBdExL = 0; | |
my $iAll = $iBBcnt + $iPPScnt + $iSBDcnt; | |
my $iAllW = $iAll; | |
my $iBdCntW = int($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); | |
my $iBdCnt = int(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); | |
my $i; | |
if ($iBdCnt > $i1stBdL) { | |
#0.1 Calculate BD count | |
$iBlCnt--; #the BlCnt is reduced in the count of the last sect is used for a pointer the next Bl | |
my $iBBleftover = $iAll - $i1stBdMax; | |
if ($iAll >$i1stBdMax) { | |
while(1) { | |
$iBdCnt = int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0); | |
$iBdExL = int(($iBdCnt) / $iBlCnt) + ((($iBdCnt) % $iBlCnt)? 1: 0); | |
$iBBleftover = $iBBleftover + $iBdExL; | |
last if($iBdCnt == (int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0))); | |
} | |
} | |
$iBdCnt += $i1stBdL; | |
#print "iBdCnt = $iBdCnt \n"; | |
} | |
#1.Save Header | |
print {$FILE} ( | |
"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" | |
, "\x00\x00\x00\x00" x 4 | |
, pack("v", 0x3b) | |
, pack("v", 0x03) | |
, pack("v", -2) | |
, pack("v", 9) | |
, pack("v", 6) | |
, pack("v", 0) | |
, "\x00\x00\x00\x00" x 2 | |
, pack("V", $iBdCnt), | |
, pack("V", $iBBcnt+$iSBDcnt), #ROOT START | |
, pack("V", 0) | |
, pack("V", 0x1000) | |
, pack("V", $iSBDcnt ? 0 : -2) #Small Block Depot | |
, pack("V", $iSBDcnt) | |
); | |
#2. Extra BDList Start, Count | |
if($iAll <= $i1stBdMax) { | |
print {$FILE} ( | |
pack("V", -2), #Extra BDList Start | |
pack("V", 0), #Extra BDList Count | |
); | |
} | |
else { | |
print {$FILE} ( | |
pack("V", $iAll+$iBdCnt), | |
pack("V", $iBdExL), | |
); | |
} | |
#3. BDList | |
for($i=0; $i<$i1stBdL and $i < $iBdCnt; $i++) { | |
print {$FILE} (pack("V", $iAll+$i)); | |
} | |
print {$FILE} ((pack("V", -1)) x($i1stBdL-$i)) if($i<$i1stBdL); | |
} | |
#------------------------------------------------------------------------------ | |
# _saveBigData (OLE::Storage_Lite::PPS) | |
#------------------------------------------------------------------------------ | |
sub _saveBigData($$$$) { | |
my($oThis, $iStBlk, $raList, $rhInfo) = @_; | |
my $iRes = 0; | |
my $FILE = $rhInfo->{_FILEH_}; | |
#1.Write Big (ge 0x1000) Data into Block | |
foreach my $oPps (@$raList) { | |
if($oPps->{Type}!=OLE::Storage_Lite::PpsType_Dir()) { | |
#print "PPS: $oPps DEF:", defined($oPps->{Data}), "\n"; | |
$oPps->{Size} = $oPps->_DataLen(); #Mod | |
if(($oPps->{Size} >= $rhInfo->{_SMALL_SIZE}) || | |
(($oPps->{Type} == OLE::Storage_Lite::PpsType_Root()) && defined($oPps->{Data}))) { | |
#1.1 Write Data | |
#Check for update | |
if($oPps->{_PPS_FILE}) { | |
my $sBuff; | |
my $iLen = 0; | |
$oPps->{_PPS_FILE}->seek(0, 0); #To The Top | |
while($oPps->{_PPS_FILE}->read($sBuff, 4096)) { | |
$iLen += length($sBuff); | |
print {$FILE} ($sBuff); #Check for update | |
} | |
} | |
else { | |
print {$FILE} ($oPps->{Data}); | |
} | |
print {$FILE} ( | |
"\x00" x | |
($rhInfo->{_BIG_BLOCK_SIZE} - | |
($oPps->{Size} % $rhInfo->{_BIG_BLOCK_SIZE})) | |
) if ($oPps->{Size} % $rhInfo->{_BIG_BLOCK_SIZE}); | |
#1.2 Set For PPS | |
$oPps->{StartBlock} = $$iStBlk; | |
$$iStBlk += | |
(int($oPps->{Size}/ $rhInfo->{_BIG_BLOCK_SIZE}) + | |
(($oPps->{Size}% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0)); | |
} | |
} | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _savePps (OLE::Storage_Lite::PPS::Root) | |
#------------------------------------------------------------------------------ | |
sub _savePps($$$) | |
{ | |
my($oThis, $raList, $rhInfo) = @_; | |
#0. Initial | |
my $FILE = $rhInfo->{_FILEH_}; | |
#2. Save PPS | |
foreach my $oItem (@$raList) { | |
$oItem->_savePpsWk($rhInfo); | |
} | |
#3. Adjust for Block | |
my $iCnt = scalar(@$raList); | |
my $iBCnt = $rhInfo->{_BIG_BLOCK_SIZE} / $rhInfo->{_PPS_SIZE}; | |
print {$FILE} ("\x00" x (($iBCnt - ($iCnt % $iBCnt)) * $rhInfo->{_PPS_SIZE})) | |
if($iCnt % $iBCnt); | |
return int($iCnt / $iBCnt) + (($iCnt % $iBCnt)? 1: 0); | |
} | |
#------------------------------------------------------------------------------ | |
# _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root) | |
# For Test | |
#------------------------------------------------------------------------------ | |
sub _savePpsSetPnt2($$$) | |
{ | |
my($aThis, $raList, $rhInfo) = @_; | |
#1. make Array as Children-Relations | |
#1.1 if No Children | |
if($#$aThis < 0) { | |
return 0xFFFFFFFF; | |
} | |
elsif($#$aThis == 0) { | |
#1.2 Just Only one | |
push @$raList, $aThis->[0]; | |
$aThis->[0]->{No} = $#$raList; | |
$aThis->[0]->{PrevPps} = 0xFFFFFFFF; | |
$aThis->[0]->{NextPps} = 0xFFFFFFFF; | |
$aThis->[0]->{DirPps} = _savePpsSetPnt2($aThis->[0]->{Child}, $raList, $rhInfo); | |
return $aThis->[0]->{No}; | |
} | |
else { | |
#1.3 Array | |
my $iCnt = $#$aThis + 1; | |
#1.3.1 Define Center | |
my $iPos = 0; #int($iCnt/ 2); #$iCnt | |
my @aWk = @$aThis; | |
my @aPrev = ($#$aThis > 1)? splice(@aWk, 1, 1) : (); #$iPos); | |
my @aNext = splice(@aWk, 1); #, $iCnt - $iPos -1); | |
$aThis->[$iPos]->{PrevPps} = _savePpsSetPnt2( | |
\@aPrev, $raList, $rhInfo); | |
push @$raList, $aThis->[$iPos]; | |
$aThis->[$iPos]->{No} = $#$raList; | |
#1.3.2 Devide a array into Previous,Next | |
$aThis->[$iPos]->{NextPps} = _savePpsSetPnt2( | |
\@aNext, $raList, $rhInfo); | |
$aThis->[$iPos]->{DirPps} = _savePpsSetPnt2($aThis->[$iPos]->{Child}, $raList, $rhInfo); | |
return $aThis->[$iPos]->{No}; | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root) | |
# For Test | |
#------------------------------------------------------------------------------ | |
sub _savePpsSetPnt2s($$$) | |
{ | |
my($aThis, $raList, $rhInfo) = @_; | |
#1. make Array as Children-Relations | |
#1.1 if No Children | |
if($#$aThis < 0) { | |
return 0xFFFFFFFF; | |
} | |
elsif($#$aThis == 0) { | |
#1.2 Just Only one | |
push @$raList, $aThis->[0]; | |
$aThis->[0]->{No} = $#$raList; | |
$aThis->[0]->{PrevPps} = 0xFFFFFFFF; | |
$aThis->[0]->{NextPps} = 0xFFFFFFFF; | |
$aThis->[0]->{DirPps} = _savePpsSetPnt2($aThis->[0]->{Child}, $raList, $rhInfo); | |
return $aThis->[0]->{No}; | |
} | |
else { | |
#1.3 Array | |
my $iCnt = $#$aThis + 1; | |
#1.3.1 Define Center | |
my $iPos = 0; #int($iCnt/ 2); #$iCnt | |
push @$raList, $aThis->[$iPos]; | |
$aThis->[$iPos]->{No} = $#$raList; | |
my @aWk = @$aThis; | |
#1.3.2 Devide a array into Previous,Next | |
my @aPrev = splice(@aWk, 0, $iPos); | |
my @aNext = splice(@aWk, 1, $iCnt - $iPos -1); | |
$aThis->[$iPos]->{PrevPps} = _savePpsSetPnt2( | |
\@aPrev, $raList, $rhInfo); | |
$aThis->[$iPos]->{NextPps} = _savePpsSetPnt2( | |
\@aNext, $raList, $rhInfo); | |
$aThis->[$iPos]->{DirPps} = _savePpsSetPnt2($aThis->[$iPos]->{Child}, $raList, $rhInfo); | |
return $aThis->[$iPos]->{No}; | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _savePpsSetPnt (OLE::Storage_Lite::PPS::Root) | |
#------------------------------------------------------------------------------ | |
sub _savePpsSetPnt($$$) | |
{ | |
my($aThis, $raList, $rhInfo) = @_; | |
#1. make Array as Children-Relations | |
#1.1 if No Children | |
if($#$aThis < 0) { | |
return 0xFFFFFFFF; | |
} | |
elsif($#$aThis == 0) { | |
#1.2 Just Only one | |
push @$raList, $aThis->[0]; | |
$aThis->[0]->{No} = $#$raList; | |
$aThis->[0]->{PrevPps} = 0xFFFFFFFF; | |
$aThis->[0]->{NextPps} = 0xFFFFFFFF; | |
$aThis->[0]->{DirPps} = _savePpsSetPnt($aThis->[0]->{Child}, $raList, $rhInfo); | |
return $aThis->[0]->{No}; | |
} | |
else { | |
#1.3 Array | |
my $iCnt = $#$aThis + 1; | |
#1.3.1 Define Center | |
my $iPos = int($iCnt/ 2); #$iCnt | |
push @$raList, $aThis->[$iPos]; | |
$aThis->[$iPos]->{No} = $#$raList; | |
my @aWk = @$aThis; | |
#1.3.2 Devide a array into Previous,Next | |
my @aPrev = splice(@aWk, 0, $iPos); | |
my @aNext = splice(@aWk, 1, $iCnt - $iPos -1); | |
$aThis->[$iPos]->{PrevPps} = _savePpsSetPnt( | |
\@aPrev, $raList, $rhInfo); | |
$aThis->[$iPos]->{NextPps} = _savePpsSetPnt( | |
\@aNext, $raList, $rhInfo); | |
$aThis->[$iPos]->{DirPps} = _savePpsSetPnt($aThis->[$iPos]->{Child}, $raList, $rhInfo); | |
return $aThis->[$iPos]->{No}; | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _savePpsSetPnt (OLE::Storage_Lite::PPS::Root) | |
#------------------------------------------------------------------------------ | |
sub _savePpsSetPnt1($$$) | |
{ | |
my($aThis, $raList, $rhInfo) = @_; | |
#1. make Array as Children-Relations | |
#1.1 if No Children | |
if($#$aThis < 0) { | |
return 0xFFFFFFFF; | |
} | |
elsif($#$aThis == 0) { | |
#1.2 Just Only one | |
push @$raList, $aThis->[0]; | |
$aThis->[0]->{No} = $#$raList; | |
$aThis->[0]->{PrevPps} = 0xFFFFFFFF; | |
$aThis->[0]->{NextPps} = 0xFFFFFFFF; | |
$aThis->[0]->{DirPps} = _savePpsSetPnt($aThis->[0]->{Child}, $raList, $rhInfo); | |
return $aThis->[0]->{No}; | |
} | |
else { | |
#1.3 Array | |
my $iCnt = $#$aThis + 1; | |
#1.3.1 Define Center | |
my $iPos = int($iCnt/ 2); #$iCnt | |
push @$raList, $aThis->[$iPos]; | |
$aThis->[$iPos]->{No} = $#$raList; | |
my @aWk = @$aThis; | |
#1.3.2 Devide a array into Previous,Next | |
my @aPrev = splice(@aWk, 0, $iPos); | |
my @aNext = splice(@aWk, 1, $iCnt - $iPos -1); | |
$aThis->[$iPos]->{PrevPps} = _savePpsSetPnt( | |
\@aPrev, $raList, $rhInfo); | |
$aThis->[$iPos]->{NextPps} = _savePpsSetPnt( | |
\@aNext, $raList, $rhInfo); | |
$aThis->[$iPos]->{DirPps} = _savePpsSetPnt($aThis->[$iPos]->{Child}, $raList, $rhInfo); | |
return $aThis->[$iPos]->{No}; | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _saveBbd (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _saveBbd($$$$) | |
{ | |
my($oThis, $iSbdSize, $iBsize, $iPpsCnt, $rhInfo) = @_; | |
my $FILE = $rhInfo->{_FILEH_}; | |
#0. Calculate Basic Setting | |
my $iBbCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize(); | |
my $iBlCnt = $iBbCnt - 1; | |
my $i1stBdL = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize()); | |
my $i1stBdMax = $i1stBdL * $iBbCnt - $i1stBdL; | |
my $iBdExL = 0; | |
my $iAll = $iBsize + $iPpsCnt + $iSbdSize; | |
my $iAllW = $iAll; | |
my $iBdCntW = int($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); | |
my $iBdCnt = 0; | |
my $i; | |
#0.1 Calculate BD count | |
my $iBBleftover = $iAll - $i1stBdMax; | |
if ($iAll >$i1stBdMax) { | |
while(1) { | |
$iBdCnt = int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0); | |
$iBdExL = int(($iBdCnt) / $iBlCnt) + ((($iBdCnt) % $iBlCnt)? 1: 0); | |
$iBBleftover = $iBBleftover + $iBdExL; | |
last if($iBdCnt == (int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0))); | |
} | |
} | |
$iAllW += $iBdExL; | |
$iBdCnt += $i1stBdL; | |
#print "iBdCnt = $iBdCnt \n"; | |
#1. Making BD | |
#1.1 Set for SBD | |
if($iSbdSize > 0) { | |
for ($i = 0; $i<($iSbdSize-1); $i++) { | |
print {$FILE} (pack("V", $i+1)); | |
} | |
print {$FILE} (pack("V", -2)); | |
} | |
#1.2 Set for B | |
for ($i = 0; $i<($iBsize-1); $i++) { | |
print {$FILE} (pack("V", $i+$iSbdSize+1)); | |
} | |
print {$FILE} (pack("V", -2)); | |
#1.3 Set for PPS | |
for ($i = 0; $i<($iPpsCnt-1); $i++) { | |
print {$FILE} (pack("V", $i+$iSbdSize+$iBsize+1)); | |
} | |
print {$FILE} (pack("V", -2)); | |
#1.4 Set for BBD itself ( 0xFFFFFFFD : BBD) | |
for($i=0; $i<$iBdCnt;$i++) { | |
print {$FILE} (pack("V", 0xFFFFFFFD)); | |
} | |
#1.5 Set for ExtraBDList | |
for($i=0; $i<$iBdExL;$i++) { | |
print {$FILE} (pack("V", 0xFFFFFFFC)); | |
} | |
#1.6 Adjust for Block | |
print {$FILE} (pack("V", -1) x ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt))) | |
if(($iAllW + $iBdCnt) % $iBbCnt); | |
#2.Extra BDList | |
if($iBdCnt > $i1stBdL) { | |
my $iN=0; | |
my $iNb=0; | |
for($i=$i1stBdL;$i<$iBdCnt; $i++, $iN++) { | |
if($iN>=($iBbCnt-1)) { | |
$iN = 0; | |
$iNb++; | |
print {$FILE} (pack("V", $iAll+$iBdCnt+$iNb)); | |
} | |
print {$FILE} (pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i)); | |
} | |
print {$FILE} (pack("V", -1) x (($iBbCnt-1) - (($iBdCnt-$i1stBdL) % ($iBbCnt-1)))) | |
if(($iBdCnt-$i1stBdL) % ($iBbCnt-1)); | |
print {$FILE} (pack("V", -2)); | |
} | |
} | |
#////////////////////////////////////////////////////////////////////////////// | |
# OLE::Storage_Lite::PPS::File Object | |
#////////////////////////////////////////////////////////////////////////////// | |
#============================================================================== | |
# OLE::Storage_Lite::PPS::File | |
#============================================================================== | |
package OLE::Storage_Lite::PPS::File; | |
require Exporter; | |
use strict; | |
use vars qw($VERSION @ISA); | |
@ISA = qw(OLE::Storage_Lite::PPS Exporter); | |
$VERSION = '0.19'; | |
#------------------------------------------------------------------------------ | |
# new (OLE::Storage_Lite::PPS::File) | |
#------------------------------------------------------------------------------ | |
sub new ($$$) { | |
my($sClass, $sNm, $sData) = @_; | |
OLE::Storage_Lite::PPS::_new( | |
$sClass, | |
undef, | |
$sNm, | |
2, | |
undef, | |
undef, | |
undef, | |
undef, | |
undef, | |
undef, | |
undef, | |
$sData, | |
undef); | |
} | |
#------------------------------------------------------------------------------ | |
# newFile (OLE::Storage_Lite::PPS::File) | |
#------------------------------------------------------------------------------ | |
sub newFile ($$;$) { | |
my($sClass, $sNm, $sFile) = @_; | |
my $oSelf = | |
OLE::Storage_Lite::PPS::_new( | |
$sClass, | |
undef, | |
$sNm, | |
2, | |
undef, | |
undef, | |
undef, | |
undef, | |
undef, | |
undef, | |
undef, | |
'', | |
undef); | |
# | |
if((!defined($sFile)) or ($sFile eq '')) { | |
$oSelf->{_PPS_FILE} = IO::File->new_tmpfile(); | |
} | |
elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) { | |
$oSelf->{_PPS_FILE} = $sFile; | |
} | |
elsif(!ref($sFile)) { | |
#File Name | |
$oSelf->{_PPS_FILE} = new IO::File; | |
return undef unless($oSelf->{_PPS_FILE}); | |
$oSelf->{_PPS_FILE}->open("$sFile", "r+") || return undef; | |
} | |
else { | |
return undef; | |
} | |
if($oSelf->{_PPS_FILE}) { | |
$oSelf->{_PPS_FILE}->seek(0, 2); | |
binmode($oSelf->{_PPS_FILE}); | |
$oSelf->{_PPS_FILE}->autoflush(1); | |
} | |
return $oSelf; | |
} | |
#------------------------------------------------------------------------------ | |
# append (OLE::Storage_Lite::PPS::File) | |
#------------------------------------------------------------------------------ | |
sub append ($$) { | |
my($oSelf, $sData) = @_; | |
if($oSelf->{_PPS_FILE}) { | |
print {$oSelf->{_PPS_FILE}} $sData; | |
} | |
else { | |
$oSelf->{Data} .= $sData; | |
} | |
} | |
#////////////////////////////////////////////////////////////////////////////// | |
# OLE::Storage_Lite::PPS::Dir Object | |
#////////////////////////////////////////////////////////////////////////////// | |
#------------------------------------------------------------------------------ | |
# new (OLE::Storage_Lite::PPS::Dir) | |
#------------------------------------------------------------------------------ | |
package OLE::Storage_Lite::PPS::Dir; | |
require Exporter; | |
use strict; | |
use vars qw($VERSION @ISA); | |
@ISA = qw(OLE::Storage_Lite::PPS Exporter); | |
$VERSION = '0.19'; | |
sub new ($$;$$$) { | |
my($sClass, $sName, $raTime1st, $raTime2nd, $raChild) = @_; | |
OLE::Storage_Lite::PPS::_new( | |
$sClass, | |
undef, | |
$sName, | |
1, | |
undef, | |
undef, | |
undef, | |
$raTime1st, | |
$raTime2nd, | |
undef, | |
undef, | |
undef, | |
$raChild); | |
} | |
#============================================================================== | |
# OLE::Storage_Lite | |
#============================================================================== | |
package OLE::Storage_Lite; | |
require Exporter; | |
use strict; | |
use IO::File; | |
use Time::Local 'timegm'; | |
use vars qw($VERSION @ISA @EXPORT); | |
@ISA = qw(Exporter); | |
$VERSION = '0.19'; | |
sub _getPpsSearch($$$$$;$); | |
sub _getPpsTree($$$;$); | |
#------------------------------------------------------------------------------ | |
# Const for OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
#0. Constants | |
sub PpsType_Root {5}; | |
sub PpsType_Dir {1}; | |
sub PpsType_File {2}; | |
sub DataSizeSmall{0x1000}; | |
sub LongIntSize {4}; | |
sub PpsSize {0x80}; | |
#------------------------------------------------------------------------------ | |
# new OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub new($$) { | |
my($sClass, $sFile) = @_; | |
my $oThis = { | |
_FILE => $sFile, | |
}; | |
bless $oThis; | |
return $oThis; | |
} | |
#------------------------------------------------------------------------------ | |
# getPpsTree: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub getPpsTree($;$) | |
{ | |
my($oThis, $bData) = @_; | |
#0.Init | |
my $rhInfo = _initParse($oThis->{_FILE}); | |
return undef unless($rhInfo); | |
#1. Get Data | |
my ($oPps) = _getPpsTree(0, $rhInfo, $bData); | |
close(IN); | |
return $oPps; | |
} | |
#------------------------------------------------------------------------------ | |
# getSearch: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub getPpsSearch($$;$$) | |
{ | |
my($oThis, $raName, $bData, $iCase) = @_; | |
#0.Init | |
my $rhInfo = _initParse($oThis->{_FILE}); | |
return undef unless($rhInfo); | |
#1. Get Data | |
my @aList = _getPpsSearch(0, $rhInfo, $raName, $bData, $iCase); | |
close(IN); | |
return @aList; | |
} | |
#------------------------------------------------------------------------------ | |
# getNthPps: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub getNthPps($$;$) | |
{ | |
my($oThis, $iNo, $bData) = @_; | |
#0.Init | |
my $rhInfo = _initParse($oThis->{_FILE}); | |
return undef unless($rhInfo); | |
#1. Get Data | |
my $oPps = _getNthPps($iNo, $rhInfo, $bData); | |
close IN; | |
return $oPps; | |
} | |
#------------------------------------------------------------------------------ | |
# _initParse: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub _initParse($) { | |
my($sFile)=@_; | |
my $oIo; | |
#1. $sFile is Ref of scalar | |
if(ref($sFile) eq 'SCALAR') { | |
require IO::Scalar; | |
$oIo = new IO::Scalar; | |
$oIo->open($sFile); | |
} | |
#2. $sFile is a IO::Handle object | |
elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) { | |
$oIo = $sFile; | |
binmode($oIo); | |
} | |
#3. $sFile is a simple filename string | |
elsif(!ref($sFile)) { | |
$oIo = new IO::File; | |
$oIo->open("<$sFile") || return undef; | |
binmode($oIo); | |
} | |
#4 Assume that if $sFile is a ref then it is a valid filehandle | |
else { | |
$oIo = $sFile; | |
# Not all filehandles support binmode() so try it in an eval. | |
eval{ binmode $oIo }; | |
} | |
return _getHeaderInfo($oIo); | |
} | |
#------------------------------------------------------------------------------ | |
# _getPpsTree: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub _getPpsTree($$$;$) { | |
my($iNo, $rhInfo, $bData, $raDone) = @_; | |
if(defined($raDone)) { | |
return () if(grep {$_ ==$iNo} @$raDone); | |
} | |
else { | |
$raDone=[]; | |
} | |
push @$raDone, $iNo; | |
my $iRootBlock = $rhInfo->{_ROOT_START} ; | |
#1. Get Information about itself | |
my $oPps = _getNthPps($iNo, $rhInfo, $bData); | |
#2. Child | |
if($oPps->{DirPps} != 0xFFFFFFFF) { | |
my @aChildL = _getPpsTree($oPps->{DirPps}, $rhInfo, $bData, $raDone); | |
$oPps->{Child} = \@aChildL; | |
} | |
else { | |
$oPps->{Child} = undef; | |
} | |
#3. Previous,Next PPSs | |
my @aList = (); | |
push @aList, _getPpsTree($oPps->{PrevPps}, $rhInfo, $bData, $raDone) | |
if($oPps->{PrevPps} != 0xFFFFFFFF); | |
push @aList, $oPps; | |
push @aList, _getPpsTree($oPps->{NextPps}, $rhInfo, $bData, $raDone) | |
if($oPps->{NextPps} != 0xFFFFFFFF); | |
return @aList; | |
} | |
#------------------------------------------------------------------------------ | |
# _getPpsSearch: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub _getPpsSearch($$$$$;$) { | |
my($iNo, $rhInfo, $raName, $bData, $iCase, $raDone) = @_; | |
my $iRootBlock = $rhInfo->{_ROOT_START} ; | |
my @aRes; | |
#1. Check it self | |
if(defined($raDone)) { | |
return () if(grep {$_==$iNo} @$raDone); | |
} | |
else { | |
$raDone=[]; | |
} | |
push @$raDone, $iNo; | |
my $oPps = _getNthPps($iNo, $rhInfo, undef); | |
# if(grep($_ eq $oPps->{Name}, @$raName)) { | |
if(($iCase && (grep(/^\Q$oPps->{Name}\E$/i, @$raName))) || | |
(grep($_ eq $oPps->{Name}, @$raName))) { | |
$oPps = _getNthPps($iNo, $rhInfo, $bData) if ($bData); | |
@aRes = ($oPps); | |
} | |
else { | |
@aRes = (); | |
} | |
#2. Check Child, Previous, Next PPSs | |
push @aRes, _getPpsSearch($oPps->{DirPps}, $rhInfo, $raName, $bData, $iCase, $raDone) | |
if($oPps->{DirPps} != 0xFFFFFFFF) ; | |
push @aRes, _getPpsSearch($oPps->{PrevPps}, $rhInfo, $raName, $bData, $iCase, $raDone) | |
if($oPps->{PrevPps} != 0xFFFFFFFF ); | |
push @aRes, _getPpsSearch($oPps->{NextPps}, $rhInfo, $raName, $bData, $iCase, $raDone) | |
if($oPps->{NextPps} != 0xFFFFFFFF); | |
return @aRes; | |
} | |
#=================================================================== | |
# Get Header Info (BASE Informain about that file) | |
#=================================================================== | |
sub _getHeaderInfo($){ | |
my($FILE) = @_; | |
my($iWk); | |
my $rhInfo = {}; | |
$rhInfo->{_FILEH_} = $FILE; | |
my $sWk; | |
#0. Check ID | |
$rhInfo->{_FILEH_}->seek(0, 0); | |
$rhInfo->{_FILEH_}->read($sWk, 8); | |
return undef unless($sWk eq "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"); | |
#BIG BLOCK SIZE | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x1E, 2, "v"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_BIG_BLOCK_SIZE} = 2 ** $iWk; | |
#SMALL BLOCK SIZE | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x20, 2, "v"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_SMALL_BLOCK_SIZE} = 2 ** $iWk; | |
#BDB Count | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x2C, 4, "V"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_BDB_COUNT} = $iWk; | |
#START BLOCK | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x30, 4, "V"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_ROOT_START} = $iWk; | |
#MIN SIZE OF BB | |
# $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x38, 4, "V"); | |
# return undef unless(defined($iWk)); | |
# $rhInfo->{_MIN_SIZE_BB} = $iWk; | |
#SMALL BD START | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x3C, 4, "V"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_SBD_START} = $iWk; | |
#SMALL BD COUNT | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x40, 4, "V"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_SBD_COUNT} = $iWk; | |
#EXTRA BBD START | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x44, 4, "V"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_EXTRA_BBD_START} = $iWk; | |
#EXTRA BD COUNT | |
$iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x48, 4, "V"); | |
return undef unless(defined($iWk)); | |
$rhInfo->{_EXTRA_BBD_COUNT} = $iWk; | |
#GET BBD INFO | |
$rhInfo->{_BBD_INFO}= _getBbdInfo($rhInfo); | |
#GET ROOT PPS | |
my $oRoot = _getNthPps(0, $rhInfo, undef); | |
$rhInfo->{_SB_START} = $oRoot->{StartBlock}; | |
$rhInfo->{_SB_SIZE} = $oRoot->{Size}; | |
return $rhInfo; | |
} | |
#------------------------------------------------------------------------------ | |
# _getInfoFromFile | |
#------------------------------------------------------------------------------ | |
sub _getInfoFromFile($$$$) { | |
my($FILE, $iPos, $iLen, $sFmt) =@_; | |
my($sWk); | |
return undef unless($FILE); | |
return undef if($FILE->seek($iPos, 0)==0); | |
return undef if($FILE->read($sWk, $iLen)!=$iLen); | |
return unpack($sFmt, $sWk); | |
} | |
#------------------------------------------------------------------------------ | |
# _getBbdInfo | |
#------------------------------------------------------------------------------ | |
sub _getBbdInfo($) { | |
my($rhInfo) =@_; | |
my @aBdList = (); | |
my $iBdbCnt = $rhInfo->{_BDB_COUNT}; | |
my $iGetCnt; | |
my $sWk; | |
my $i1stCnt = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize()); | |
my $iBdlCnt = int($rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize()) - 1; | |
#1. 1st BDlist | |
$rhInfo->{_FILEH_}->seek(0x4C, 0); | |
$iGetCnt = ($iBdbCnt < $i1stCnt)? $iBdbCnt: $i1stCnt; | |
$rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()*$iGetCnt); | |
push @aBdList, unpack("V$iGetCnt", $sWk); | |
$iBdbCnt -= $iGetCnt; | |
#2. Extra BDList | |
my $iBlock = $rhInfo->{_EXTRA_BBD_START}; | |
while(($iBdbCnt> 0) && _isNormalBlock($iBlock)){ | |
_setFilePos($iBlock, 0, $rhInfo); | |
$iGetCnt= ($iBdbCnt < $iBdlCnt)? $iBdbCnt: $iBdlCnt; | |
$rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()*$iGetCnt); | |
push @aBdList, unpack("V$iGetCnt", $sWk); | |
$iBdbCnt -= $iGetCnt; | |
$rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()); | |
$iBlock = unpack("V", $sWk); | |
} | |
#3.Get BDs | |
my @aWk; | |
my %hBd; | |
my $iBlkNo = 0; | |
my $iBdL; | |
my $i; | |
my $iBdCnt = int($rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize()); | |
foreach $iBdL (@aBdList) { | |
_setFilePos($iBdL, 0, $rhInfo); | |
$rhInfo->{_FILEH_}->read($sWk, $rhInfo->{_BIG_BLOCK_SIZE}); | |
@aWk = unpack("V$iBdCnt", $sWk); | |
for($i=0;$i<$iBdCnt;$i++, $iBlkNo++) { | |
if($aWk[$i] != ($iBlkNo+1)){ | |
$hBd{$iBlkNo} = $aWk[$i]; | |
} | |
} | |
} | |
return \%hBd; | |
} | |
#------------------------------------------------------------------------------ | |
# getNthPps (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _getNthPps($$$){ | |
my($iPos, $rhInfo, $bData) = @_; | |
my($iPpsStart) = ($rhInfo->{_ROOT_START}); | |
my($iPpsBlock, $iPpsPos); | |
my $sWk; | |
my $iBlock; | |
my $iBaseCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::PpsSize(); | |
$iPpsBlock = int($iPos / $iBaseCnt); | |
$iPpsPos = $iPos % $iBaseCnt; | |
$iBlock = _getNthBlockNo($iPpsStart, $iPpsBlock, $rhInfo); | |
return undef unless(defined($iBlock)); | |
_setFilePos($iBlock, OLE::Storage_Lite::PpsSize()*$iPpsPos, $rhInfo); | |
$rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::PpsSize()); | |
return undef unless($sWk); | |
my $iNmSize = unpack("v", substr($sWk, 0x40, 2)); | |
$iNmSize = ($iNmSize > 2)? $iNmSize - 2 : $iNmSize; | |
my $sNm= substr($sWk, 0, $iNmSize); | |
my $iType = unpack("C", substr($sWk, 0x42, 2)); | |
my $lPpsPrev = unpack("V", substr($sWk, 0x44, OLE::Storage_Lite::LongIntSize())); | |
my $lPpsNext = unpack("V", substr($sWk, 0x48, OLE::Storage_Lite::LongIntSize())); | |
my $lDirPps = unpack("V", substr($sWk, 0x4C, OLE::Storage_Lite::LongIntSize())); | |
my @raTime1st = | |
(($iType == OLE::Storage_Lite::PpsType_Root()) or ($iType == OLE::Storage_Lite::PpsType_Dir()))? | |
OLEDate2Local(substr($sWk, 0x64, 8)) : undef , | |
my @raTime2nd = | |
(($iType == OLE::Storage_Lite::PpsType_Root()) or ($iType == OLE::Storage_Lite::PpsType_Dir()))? | |
OLEDate2Local(substr($sWk, 0x6C, 8)) : undef, | |
my($iStart, $iSize) = unpack("VV", substr($sWk, 0x74, 8)); | |
if($bData) { | |
my $sData = _getData($iType, $iStart, $iSize, $rhInfo); | |
return OLE::Storage_Lite::PPS->new( | |
$iPos, $sNm, $iType, $lPpsPrev, $lPpsNext, $lDirPps, | |
\@raTime1st, \@raTime2nd, $iStart, $iSize, $sData, undef); | |
} | |
else { | |
return OLE::Storage_Lite::PPS->new( | |
$iPos, $sNm, $iType, $lPpsPrev, $lPpsNext, $lDirPps, | |
\@raTime1st, \@raTime2nd, $iStart, $iSize, undef, undef); | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _setFilePos (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _setFilePos($$$){ | |
my($iBlock, $iPos, $rhInfo) = @_; | |
$rhInfo->{_FILEH_}->seek(($iBlock+1)*$rhInfo->{_BIG_BLOCK_SIZE}+$iPos, 0); | |
} | |
#------------------------------------------------------------------------------ | |
# _getNthBlockNo (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _getNthBlockNo($$$){ | |
my($iStBlock, $iNth, $rhInfo) = @_; | |
my $iSv; | |
my $iNext = $iStBlock; | |
for(my $i =0; $i<$iNth; $i++) { | |
$iSv = $iNext; | |
$iNext = _getNextBlockNo($iSv, $rhInfo); | |
return undef unless _isNormalBlock($iNext); | |
} | |
return $iNext; | |
} | |
#------------------------------------------------------------------------------ | |
# _getData (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _getData($$$$) | |
{ | |
my($iType, $iBlock, $iSize, $rhInfo) = @_; | |
if ($iType == OLE::Storage_Lite::PpsType_File()) { | |
if($iSize < OLE::Storage_Lite::DataSizeSmall()) { | |
return _getSmallData($iBlock, $iSize, $rhInfo); | |
} | |
else { | |
return _getBigData($iBlock, $iSize, $rhInfo); | |
} | |
} | |
elsif($iType == OLE::Storage_Lite::PpsType_Root()) { #Root | |
return _getBigData($iBlock, $iSize, $rhInfo); | |
} | |
elsif($iType == OLE::Storage_Lite::PpsType_Dir()) { # Directory | |
return undef; | |
} | |
} | |
#------------------------------------------------------------------------------ | |
# _getBigData (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _getBigData($$$) | |
{ | |
my($iBlock, $iSize, $rhInfo) = @_; | |
my($iRest, $sWk, $sRes); | |
return '' unless(_isNormalBlock($iBlock)); | |
$iRest = $iSize; | |
my($i, $iGetSize, $iNext); | |
$sRes = ''; | |
my @aKeys= sort({$a<=>$b} keys(%{$rhInfo->{_BBD_INFO}})); | |
while ($iRest > 0) { | |
my @aRes = grep($_ >= $iBlock, @aKeys); | |
my $iNKey = $aRes[0]; | |
$i = $iNKey - $iBlock; | |
$iNext = $rhInfo->{_BBD_INFO}{$iNKey}; | |
_setFilePos($iBlock, 0, $rhInfo); | |
my $iGetSize = ($rhInfo->{_BIG_BLOCK_SIZE} * ($i+1)); | |
$iGetSize = $iRest if($iRest < $iGetSize); | |
$rhInfo->{_FILEH_}->read( $sWk, $iGetSize); | |
$sRes .= $sWk; | |
$iRest -= $iGetSize; | |
$iBlock= $iNext; | |
} | |
return $sRes; | |
} | |
#------------------------------------------------------------------------------ | |
# _getNextBlockNo (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _getNextBlockNo($$){ | |
my($iBlockNo, $rhInfo) = @_; | |
my $iRes = $rhInfo->{_BBD_INFO}->{$iBlockNo}; | |
return defined($iRes)? $iRes: $iBlockNo+1; | |
} | |
#------------------------------------------------------------------------------ | |
# _isNormalBlock (OLE::Storage_Lite) | |
# 0xFFFFFFFC : BDList, 0xFFFFFFFD : BBD, | |
# 0xFFFFFFFE: End of Chain 0xFFFFFFFF : unused | |
#------------------------------------------------------------------------------ | |
sub _isNormalBlock($){ | |
my($iBlock) = @_; | |
return ($iBlock < 0xFFFFFFFC)? 1: undef; | |
} | |
#------------------------------------------------------------------------------ | |
# _getSmallData (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _getSmallData($$$) | |
{ | |
my($iSmBlock, $iSize, $rhInfo) = @_; | |
my($sRes, $sWk); | |
my $iRest = $iSize; | |
$sRes = ''; | |
while ($iRest > 0) { | |
_setFilePosSmall($iSmBlock, $rhInfo); | |
$rhInfo->{_FILEH_}->read($sWk, | |
($iRest >= $rhInfo->{_SMALL_BLOCK_SIZE})? | |
$rhInfo->{_SMALL_BLOCK_SIZE}: $iRest); | |
$sRes .= $sWk; | |
$iRest -= $rhInfo->{_SMALL_BLOCK_SIZE}; | |
$iSmBlock= _getNextSmallBlockNo($iSmBlock, $rhInfo); | |
} | |
return $sRes; | |
} | |
#------------------------------------------------------------------------------ | |
# _setFilePosSmall(OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _setFilePosSmall($$) | |
{ | |
my($iSmBlock, $rhInfo) = @_; | |
my $iSmStart = $rhInfo->{_SB_START}; | |
my $iBaseCnt = $rhInfo->{_BIG_BLOCK_SIZE} / $rhInfo->{_SMALL_BLOCK_SIZE}; | |
my $iNth = int($iSmBlock/$iBaseCnt); | |
my $iPos = $iSmBlock % $iBaseCnt; | |
my $iBlk = _getNthBlockNo($iSmStart, $iNth, $rhInfo); | |
_setFilePos($iBlk, $iPos * $rhInfo->{_SMALL_BLOCK_SIZE}, $rhInfo); | |
} | |
#------------------------------------------------------------------------------ | |
# _getNextSmallBlockNo (OLE::Storage_Lite) | |
#------------------------------------------------------------------------------ | |
sub _getNextSmallBlockNo($$) | |
{ | |
my($iSmBlock, $rhInfo) = @_; | |
my($sWk); | |
my $iBaseCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize(); | |
my $iNth = int($iSmBlock/$iBaseCnt); | |
my $iPos = $iSmBlock % $iBaseCnt; | |
my $iBlk = _getNthBlockNo($rhInfo->{_SBD_START}, $iNth, $rhInfo); | |
_setFilePos($iBlk, $iPos * OLE::Storage_Lite::LongIntSize(), $rhInfo); | |
$rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()); | |
return unpack("V", $sWk); | |
} | |
#------------------------------------------------------------------------------ | |
# Asc2Ucs: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub Asc2Ucs($) | |
{ | |
my($sAsc) = @_; | |
return join("\x00", split //, $sAsc) . "\x00"; | |
} | |
#------------------------------------------------------------------------------ | |
# Ucs2Asc: OLE::Storage_Lite | |
#------------------------------------------------------------------------------ | |
sub Ucs2Asc($) | |
{ | |
my($sUcs) = @_; | |
return join('', map(pack('c', $_), unpack('v*', $sUcs))); | |
} | |
#------------------------------------------------------------------------------ | |
# OLEDate2Local() | |
# | |
# Convert from a Window FILETIME structure to a localtime array. FILETIME is | |
# a 64-bit value representing the number of 100-nanosecond intervals since | |
# January 1 1601. | |
# | |
# We first convert the FILETIME to seconds and then subtract the difference | |
# between the 1601 epoch and the 1970 Unix epoch. | |
# | |
sub OLEDate2Local { | |
my $oletime = shift; | |
# Unpack the FILETIME into high and low longs. | |
my ( $lo, $hi ) = unpack 'V2', $oletime; | |
# Convert the longs to a double. | |
my $nanoseconds = $hi * 2**32 + $lo; | |
# Convert the 100 nanosecond units into seconds. | |
my $time = $nanoseconds / 1e7; | |
# Subtract the number of seconds between the 1601 and 1970 epochs. | |
$time -= 11644473600; | |
# Convert to a localtime (actually gmtime) structure. | |
my @localtime = gmtime($time); | |
return @localtime; | |
} | |
#------------------------------------------------------------------------------ | |
# LocalDate2OLE() | |
# | |
# Convert from a a localtime array to a Window FILETIME structure. FILETIME is | |
# a 64-bit value representing the number of 100-nanosecond intervals since | |
# January 1 1601. | |
# | |
# We first convert the localtime (actually gmtime) to seconds and then add the | |
# difference between the 1601 epoch and the 1970 Unix epoch. We convert that to | |
# 100 nanosecond units, divide it into high and low longs and return it as a | |
# packed 64bit structure. | |
# | |
sub LocalDate2OLE { | |
my $localtime = shift; | |
return "\x00" x 8 unless $localtime; | |
# Convert from localtime (actually gmtime) to seconds. | |
my $time = timegm( @{$localtime} ); | |
# Add the number of seconds between the 1601 and 1970 epochs. | |
$time += 11644473600; | |
# The FILETIME seconds are in units of 100 nanoseconds. | |
my $nanoseconds = $time * 1E7; | |
use POSIX 'fmod'; | |
# Pack the total nanoseconds into 64 bits... | |
my $hi = int( $nanoseconds / 2**32 ); | |
my $lo = fmod($nanoseconds, 2**32); | |
my $oletime = pack "VV", $lo, $hi; | |
return $oletime; | |
} | |
1; | |
__END__ | |
=head1 NAME | |
OLE::Storage_Lite - Simple Class for OLE document interface. | |
=head1 SYNOPSIS | |
use OLE::Storage_Lite; | |
# Initialize. | |
# From a file | |
my $oOl = OLE::Storage_Lite->new("some.xls"); | |
# From a filehandle object | |
use IO::File; | |
my $oIo = new IO::File; | |
$oIo->open("<iofile.xls"); | |
binmode($oIo); | |
my $oOl = OLE::Storage_Lite->new($oFile); | |
# Read data | |
my $oPps = $oOl->getPpsTree(1); | |
# Save Data | |
# To a File | |
$oPps->save("kaba.xls"); #kaba.xls | |
$oPps->save('-'); #STDOUT | |
# To a filehandle object | |
my $oIo = new IO::File; | |
$oIo->open(">iofile.xls"); | |
bimode($oIo); | |
$oPps->save($oIo); | |
=head1 DESCRIPTION | |
OLE::Storage_Lite allows you to read and write an OLE structured file. | |
OLE::Storage_Lite::PPS is a class representing PPS. OLE::Storage_Lite::PPS::Root, OLE::Storage_Lite::PPS::File and OLE::Storage_Lite::PPS::Dir | |
are subclasses of OLE::Storage_Lite::PPS. | |
=head2 new() | |
Constructor. | |
$oOle = OLE::Storage_Lite->new($sFile); | |
Creates a OLE::Storage_Lite object for C<$sFile>. C<$sFile> must be a correct file name. | |
The C<new()> constructor also accepts a valid filehandle. Remember to C<binmode()> the filehandle first. | |
=head2 getPpsTree() | |
$oPpsRoot = $oOle->getPpsTree([$bData]); | |
Returns PPS as an OLE::Storage_Lite::PPS::Root object. | |
Other PPS objects will be included as its children. | |
If C<$bData> is true, the objects will have data in the file. | |
=head2 getPpsSearch() | |
$oPpsRoot = $oOle->getPpsTree($raName [, $bData][, $iCase] ); | |
Returns PPSs as OLE::Storage_Lite::PPS objects that has the name specified in C<$raName> array. | |
If C<$bData> is true, the objects will have data in the file. | |
If C<$iCase> is true, search is case insensitive. | |
=head2 getNthPps() | |
$oPpsRoot = $oOle->getNthPps($iNth [, $bData]); | |
Returns PPS as C<OLE::Storage_Lite::PPS> object specified number C<$iNth>. | |
If C<$bData> is true, the objects will have data in the file. | |
=head2 Asc2Ucs() | |
$sUcs2 = OLE::Storage_Lite::Asc2Ucs($sAsc>); | |
Utility function. Just adds 0x00 after every characters in C<$sAsc>. | |
=head2 Ucs2Asc() | |
$sAsc = OLE::Storage_Lite::Ucs2Asc($sUcs2); | |
Utility function. Just deletes 0x00 after words in C<$sUcs>. | |
=head1 OLE::Storage_Lite::PPS | |
OLE::Storage_Lite::PPS has these properties: | |
=over 4 | |
=item No | |
Order number in saving. | |
=item Name | |
Its name in UCS2 (a.k.a Unicode). | |
=item Type | |
Its type (1:Dir, 2:File (Data), 5: Root) | |
=item PrevPps | |
Previous pps (as No) | |
=item NextPps | |
Next pps (as No) | |
=item DirPps | |
Dir pps (as No). | |
=item Time1st | |
Timestamp 1st in array ref as similar fomat of localtime. | |
=item Time2nd | |
Timestamp 2nd in array ref as similar fomat of localtime. | |
=item StartBlock | |
Start block number | |
=item Size | |
Size of the pps | |
=item Data | |
Its data | |
=item Child | |
Its child PPSs in array ref | |
=back | |
=head1 OLE::Storage_Lite::PPS::Root | |
OLE::Storage_Lite::PPS::Root has 2 methods. | |
=head2 new() | |
$oRoot = OLE::Storage_Lite::PPS::Root->new( | |
$raTime1st, | |
$raTime2nd, | |
$raChild); | |
Constructor. | |
C<$raTime1st>, C<$raTime2nd> are array refs with ($iSec, $iMin, $iHour, $iDay, $iMon, $iYear). | |
$iSec means seconds, $iMin means minutes. $iHour means hours. | |
$iDay means day. $iMon is month -1. $iYear is year - 1900. | |
C<$raChild> is a array ref of children PPSs. | |
=head2 save() | |
$oRoot = $oRoot>->save( | |
$sFile, | |
$bNoAs); | |
Saves information into C<$sFile>. If C<$sFile> is '-', this will use STDOUT. | |
The C<new()> constructor also accepts a valid filehandle. Remember to C<binmode()> the filehandle first. | |
If C<$bNoAs> is defined, this function will use the No of PPSs for saving order. | |
If C<$bNoAs> is undefined, this will calculate PPS saving order. | |
=head1 OLE::Storage_Lite::PPS::Dir | |
OLE::Storage_Lite::PPS::Dir has 1 method. | |
=head2 new() | |
$oRoot = OLE::Storage_Lite::PPS::Dir->new( | |
$sName, | |
[, $raTime1st] | |
[, $raTime2nd] | |
[, $raChild>]); | |
Constructor. | |
C<$sName> is a name of the PPS. | |
C<$raTime1st>, C<$raTime2nd> is a array ref as | |
($iSec, $iMin, $iHour, $iDay, $iMon, $iYear). | |
$iSec means seconds, $iMin means minutes. $iHour means hours. | |
$iDay means day. $iMon is month -1. $iYear is year - 1900. | |
C<$raChild> is a array ref of children PPSs. | |
=head1 OLE::Storage_Lite::PPS::File | |
OLE::Storage_Lite::PPS::File has 3 method. | |
=head2 new | |
$oRoot = OLE::Storage_Lite::PPS::File->new($sName, $sData); | |
C<$sName> is name of the PPS. | |
C<$sData> is data of the PPS. | |
=head2 newFile() | |
$oRoot = OLE::Storage_Lite::PPS::File->newFile($sName, $sFile); | |
This function makes to use file handle for geting and storing data. | |
C<$sName> is name of the PPS. | |
If C<$sFile> is scalar, it assumes that is a filename. | |
If C<$sFile> is an IO::Handle object, it uses that specified handle. | |
If C<$sFile> is undef or '', it uses temporary file. | |
CAUTION: Take care C<$sFile> will be updated by C<append> method. | |
So if you want to use IO::Handle and append a data to it, | |
you should open the handle with "r+". | |
=head2 append() | |
$oRoot = $oPps->append($sData); | |
appends specified data to that PPS. | |
C<$sData> is appending data for that PPS. | |
=head1 CAUTION | |
A saved file with VBA (a.k.a Macros) by this module will not work correctly. | |
However modules can get the same information from the file, | |
the file occurs a error in application(Word, Excel ...). | |
=head1 DEPRECATED FEATURES | |
Older 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. | |
=head1 COPYRIGHT | |
The OLE::Storage_Lite module is Copyright (c) 2000,2001 Kawai Takanori. Japan. | |
All rights reserved. | |
You may distribute under the terms of either the GNU General Public | |
License or the Artistic License, as specified in the Perl README file. | |
=head1 ACKNOWLEDGEMENTS | |
First of all, I would like to acknowledge to Martin Schwartz and his module OLE::Storage. | |
=head1 AUTHOR | |
Kawai Takanori kwitknr@cpan.org | |
This module is currently maintained by John McNamara jmcnamara@cpan.org | |
=head1 SEE ALSO | |
OLE::Storage | |
Documentation 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 | |
The Digital Imaging Group have also detailed the OLE format in the JPEG2000 specification: see Appendix A of http://www.i3a.org/pdf/wg1n1017.pdf | |
=cut |