2015-04-02 20:32:22 +00:00
/* [MS-DTYP] 2.3.3 FILETIME */
/* [MS-OLEDS] 2.1.3 FILETIME (Packet Version) */
/* [MS-OLEPS] 2.8 FILETIME (Packet Version) */
function parse _FILETIME ( blob ) {
var dwLowDateTime = blob . read _shift ( 4 ) , dwHighDateTime = blob . read _shift ( 4 ) ;
return new Date ( ( ( dwHighDateTime / 1e7 * Math . pow ( 2 , 32 ) + dwLowDateTime / 1e7 ) - 11644473600 ) * 1000 ) . toISOString ( ) . replace ( /\.000/ , "" ) ;
}
2018-05-05 06:34:37 +00:00
function write _FILETIME ( time /*:string|Date*/ ) {
var date = ( typeof time == "string" ) ? new Date ( Date . parse ( time ) ) : time ;
var t = date . getTime ( ) / 1000 + 11644473600 ;
var l = t % Math . pow ( 2 , 32 ) , h = ( t - l ) / Math . pow ( 2 , 32 ) ;
l *= 1e7 ; h *= 1e7 ;
var w = ( l / Math . pow ( 2 , 32 ) ) | 0 ;
if ( w > 0 ) { l = l % Math . pow ( 2 , 32 ) ; h += w ; }
var o = new _buf ( 8 ) ; o . write _shift ( 4 , l ) ; o . write _shift ( 4 , h ) ; return o ;
}
2015-04-02 20:32:22 +00:00
/* [MS-OSHARED] 2.3.3.1.4 Lpstr */
2017-07-27 20:07:51 +00:00
function parse _lpstr ( blob , type , pad /*:?number*/ ) {
2017-12-30 05:40:35 +00:00
var start = blob . l ;
var str = blob . read _shift ( 0 , 'lpstr-cp' ) ;
if ( pad ) while ( ( blob . l - start ) & 3 ) ++ blob . l ;
2015-04-02 20:32:22 +00:00
return str ;
}
/* [MS-OSHARED] 2.3.3.1.6 Lpwstr */
function parse _lpwstr ( blob , type , pad ) {
var str = blob . read _shift ( 0 , 'lpwstr' ) ;
if ( pad ) blob . l += ( 4 - ( ( str . length + 1 ) & 3 ) ) & 3 ;
return str ;
}
/* [MS-OSHARED] 2.3.3.1.11 VtString */
/* [MS-OSHARED] 2.3.3.1.12 VtUnalignedString */
function parse _VtStringBase ( blob , stringType , pad ) {
if ( stringType === 0x1F /*VT_LPWSTR*/ ) return parse _lpwstr ( blob ) ;
return parse _lpstr ( blob , stringType , pad ) ;
}
2017-08-05 06:32:57 +00:00
function parse _VtString ( blob , t /*:number*/ , pad /*:?boolean*/ ) { return parse _VtStringBase ( blob , t , pad === false ? 0 : 4 ) ; }
function parse _VtUnalignedString ( blob , t /*:number*/ ) { if ( ! t ) throw new Error ( "VtUnalignedString must have positive length" ) ; return parse _VtStringBase ( blob , t , 0 ) ; }
2015-04-02 20:32:22 +00:00
2021-10-03 01:41:36 +00:00
/* [MS-OSHARED] 2.3.3.1.7 VtVecLpwstrValue */
function parse _VtVecLpwstrValue ( blob ) /*:Array<string>*/ {
var length = blob . read _shift ( 4 ) ;
var ret /*:Array<string>*/ = [ ] ;
for ( var i = 0 ; i != length ; ++ i ) {
var start = blob . l ;
ret [ i ] = blob . read _shift ( 0 , 'lpwstr' ) . replace ( chr0 , '' ) ;
if ( ( blob . l - start ) & 0x02 ) blob . l += 2 ;
}
return ret ;
}
2015-04-02 20:32:22 +00:00
/* [MS-OSHARED] 2.3.3.1.9 VtVecUnalignedLpstrValue */
2017-12-30 05:40:35 +00:00
function parse _VtVecUnalignedLpstrValue ( blob ) /*:Array<string>*/ {
2015-04-02 20:32:22 +00:00
var length = blob . read _shift ( 4 ) ;
2017-12-30 05:40:35 +00:00
var ret /*:Array<string>*/ = [ ] ;
for ( var i = 0 ; i != length ; ++ i ) ret [ i ] = blob . read _shift ( 0 , 'lpstr-cp' ) . replace ( chr0 , '' ) ;
2015-04-02 20:32:22 +00:00
return ret ;
}
/* [MS-OSHARED] 2.3.3.1.13 VtHeadingPair */
function parse _VtHeadingPair ( blob ) {
2021-10-03 01:41:36 +00:00
var start = blob . l ;
2015-04-02 20:32:22 +00:00
var headingString = parse _TypedPropertyValue ( blob , VT _USTR ) ;
2021-10-03 01:41:36 +00:00
if ( blob [ blob . l ] == 0x00 && blob [ blob . l + 1 ] == 0x00 && ( ( blob . l - start ) & 0x02 ) ) blob . l += 2 ;
2015-04-02 20:32:22 +00:00
var headerParts = parse _TypedPropertyValue ( blob , VT _I4 ) ;
return [ headingString , headerParts ] ;
}
/* [MS-OSHARED] 2.3.3.1.14 VtVecHeadingPairValue */
function parse _VtVecHeadingPairValue ( blob ) {
var cElements = blob . read _shift ( 4 ) ;
var out = [ ] ;
2021-10-03 01:41:36 +00:00
for ( var i = 0 ; i < cElements / 2 ; ++ i ) out . push ( parse _VtHeadingPair ( blob ) ) ;
2015-04-02 20:32:22 +00:00
return out ;
}
/* [MS-OLEPS] 2.18.1 Dictionary (uses 2.17, 2.16) */
function parse _dictionary ( blob , CodePage ) {
var cnt = blob . read _shift ( 4 ) ;
2017-03-05 00:56:31 +00:00
var dict /*:{[number]:string}*/ = ( { } /*:any*/ ) ;
2015-04-02 20:32:22 +00:00
for ( var j = 0 ; j != cnt ; ++ j ) {
var pid = blob . read _shift ( 4 ) ;
var len = blob . read _shift ( 4 ) ;
dict [ pid ] = blob . read _shift ( len , ( CodePage === 0x4B0 ? 'utf16le' : 'utf8' ) ) . replace ( chr0 , '' ) . replace ( chr1 , '!' ) ;
2018-05-05 06:34:37 +00:00
if ( CodePage === 0x4B0 && ( len % 2 ) ) blob . l += 2 ;
2015-04-02 20:32:22 +00:00
}
if ( blob . l & 3 ) blob . l = ( blob . l >> 2 + 1 ) << 2 ;
return dict ;
}
/* [MS-OLEPS] 2.9 BLOB */
function parse _BLOB ( blob ) {
var size = blob . read _shift ( 4 ) ;
var bytes = blob . slice ( blob . l , blob . l + size ) ;
2018-02-21 07:01:34 +00:00
blob . l += size ;
2017-02-10 19:23:01 +00:00
if ( ( size & 3 ) > 0 ) blob . l += ( 4 - ( size & 3 ) ) & 3 ;
2015-04-02 20:32:22 +00:00
return bytes ;
}
/* [MS-OLEPS] 2.11 ClipboardData */
function parse _ClipboardData ( blob ) {
// TODO
var o = { } ;
o . Size = blob . read _shift ( 4 ) ;
//o.Format = blob.read_shift(4);
2018-02-21 07:01:34 +00:00
blob . l += o . Size + 3 - ( o . Size - 1 ) % 4 ;
2015-04-02 20:32:22 +00:00
return o ;
}
/* [MS-OLEPS] 2.15 TypedPropertyValue */
2017-10-17 00:14:32 +00:00
function parse _TypedPropertyValue ( blob , type /*:number*/ , _opts ) /*:any*/ {
2015-04-02 20:32:22 +00:00
var t = blob . read _shift ( 2 ) , ret , opts = _opts || { } ;
blob . l += 2 ;
if ( type !== VT _VARIANT )
2021-10-03 01:41:36 +00:00
if ( t !== type && VT _CUSTOM . indexOf ( type ) === - 1 && ! ( ( type & 0xFFFE ) == 0x101E && ( t & 0xFFFE ) == 0x101E ) ) throw new Error ( 'Expected type ' + type + ' saw ' + t ) ;
2015-04-02 20:32:22 +00:00
switch ( type === VT _VARIANT ? t : type ) {
case 0x02 /*VT_I2*/ : ret = blob . read _shift ( 2 , 'i' ) ; if ( ! opts . raw ) blob . l += 2 ; return ret ;
case 0x03 /*VT_I4*/ : ret = blob . read _shift ( 4 , 'i' ) ; return ret ;
case 0x0B /*VT_BOOL*/ : return blob . read _shift ( 4 ) !== 0x0 ;
case 0x13 /*VT_UI4*/ : ret = blob . read _shift ( 4 ) ; return ret ;
case 0x1E /*VT_LPSTR*/ : return parse _lpstr ( blob , t , 4 ) . replace ( chr0 , '' ) ;
case 0x1F /*VT_LPWSTR*/ : return parse _lpwstr ( blob ) ;
case 0x40 /*VT_FILETIME*/ : return parse _FILETIME ( blob ) ;
case 0x41 /*VT_BLOB*/ : return parse _BLOB ( blob ) ;
case 0x47 /*VT_CF*/ : return parse _ClipboardData ( blob ) ;
2017-08-05 06:32:57 +00:00
case 0x50 /*VT_STRING*/ : return parse _VtString ( blob , t , ! opts . raw ) . replace ( chr0 , '' ) ;
2017-07-05 22:27:54 +00:00
case 0x51 /*VT_USTR*/ : return parse _VtUnalignedString ( blob , t /*, 4*/ ) . replace ( chr0 , '' ) ;
2021-10-03 01:41:36 +00:00
case 0x100C /*VT_VECTOR|VT_VARIANT*/ : return parse _VtVecHeadingPairValue ( blob ) ;
case 0x101E /*VT_VECTOR|VT_LPSTR*/ :
case 0x101F /*VT_VECTOR|VT_LPWSTR*/ :
return t == 0x101F ? parse _VtVecLpwstrValue ( blob ) : parse _VtVecUnalignedLpstrValue ( blob ) ;
2015-04-02 20:32:22 +00:00
default : throw new Error ( "TypedPropertyValue unrecognized type " + type + " " + t ) ;
}
}
2018-05-05 06:34:37 +00:00
function write _TypedPropertyValue ( type /*:number*/ , value ) {
var o = new _buf ( 4 ) , p = new _buf ( 4 ) ;
o . write _shift ( 4 , type == 0x50 ? 0x1F : type ) ;
switch ( type ) {
case 0x03 /*VT_I4*/ : p . write _shift ( - 4 , value ) ; break ;
case 0x05 /*VT_I4*/ : p = new _buf ( 8 ) ; p . write _shift ( 8 , value , 'f' ) ; break ;
case 0x0B /*VT_BOOL*/ : p . write _shift ( 4 , value ? 0x01 : 0x00 ) ; break ;
2018-05-20 01:34:59 +00:00
case 0x40 /*VT_FILETIME*/ : /*:: if(typeof value !== "string" && !(value instanceof Date)) throw "unreachable"; */ p = write _FILETIME ( value ) ; break ;
2018-05-05 06:34:37 +00:00
case 0x1F /*VT_LPWSTR*/ :
case 0x50 /*VT_STRING*/ :
2018-05-20 01:34:59 +00:00
/*:: if(typeof value !== "string") throw "unreachable"; */
2018-05-05 06:34:37 +00:00
p = new _buf ( 4 + 2 * ( value . length + 1 ) + ( value . length % 2 ? 0 : 2 ) ) ;
p . write _shift ( 4 , value . length + 1 ) ;
p . write _shift ( 0 , value , "dbcs" ) ;
while ( p . l != p . length ) p . write _shift ( 1 , 0 ) ;
break ;
default : throw new Error ( "TypedPropertyValue unrecognized type " + type + " " + value ) ;
}
return bconcat ( [ o , p ] ) ;
}
2015-04-02 20:32:22 +00:00
/* [MS-OLEPS] 2.20 PropertySet */
function parse _PropertySet ( blob , PIDSI ) {
var start _addr = blob . l ;
var size = blob . read _shift ( 4 ) ;
var NumProps = blob . read _shift ( 4 ) ;
var Props = [ ] , i = 0 ;
var CodePage = 0 ;
2017-03-05 00:56:31 +00:00
var Dictionary = - 1 , DictObj /*:{[number]:string}*/ = ( { } /*:any*/ ) ;
2015-04-02 20:32:22 +00:00
for ( i = 0 ; i != NumProps ; ++ i ) {
var PropID = blob . read _shift ( 4 ) ;
var Offset = blob . read _shift ( 4 ) ;
Props [ i ] = [ PropID , Offset + start _addr ] ;
}
2017-12-30 05:40:35 +00:00
Props . sort ( function ( x , y ) { return x [ 1 ] - y [ 1 ] ; } ) ;
2015-04-02 20:32:22 +00:00
var PropH = { } ;
for ( i = 0 ; i != NumProps ; ++ i ) {
if ( blob . l !== Props [ i ] [ 1 ] ) {
var fail = true ;
if ( i > 0 && PIDSI ) switch ( PIDSI [ Props [ i - 1 ] [ 0 ] ] . t ) {
2017-12-30 05:40:35 +00:00
case 0x02 /*VT_I2*/ : if ( blob . l + 2 === Props [ i ] [ 1 ] ) { blob . l += 2 ; fail = false ; } break ;
2015-04-02 20:32:22 +00:00
case 0x50 /*VT_STRING*/ : if ( blob . l <= Props [ i ] [ 1 ] ) { blob . l = Props [ i ] [ 1 ] ; fail = false ; } break ;
case 0x100C /*VT_VECTOR|VT_VARIANT*/ : if ( blob . l <= Props [ i ] [ 1 ] ) { blob . l = Props [ i ] [ 1 ] ; fail = false ; } break ;
}
2017-12-30 05:40:35 +00:00
if ( ( ! PIDSI || i == 0 ) && blob . l <= Props [ i ] [ 1 ] ) { fail = false ; blob . l = Props [ i ] [ 1 ] ; }
2015-04-02 20:32:22 +00:00
if ( fail ) throw new Error ( "Read Error: Expected address " + Props [ i ] [ 1 ] + ' at ' + blob . l + ' :' + i ) ;
}
if ( PIDSI ) {
var piddsi = PIDSI [ Props [ i ] [ 0 ] ] ;
PropH [ piddsi . n ] = parse _TypedPropertyValue ( blob , piddsi . t , { raw : true } ) ;
2018-05-05 06:34:37 +00:00
if ( piddsi . p === 'version' ) PropH [ piddsi . n ] = String ( PropH [ piddsi . n ] >> 16 ) + "." + ( "0000" + String ( PropH [ piddsi . n ] & 0xFFFF ) ) . slice ( - 4 ) ;
2015-04-02 20:32:22 +00:00
if ( piddsi . n == "CodePage" ) switch ( PropH [ piddsi . n ] ) {
case 0 : PropH [ piddsi . n ] = 1252 ;
/* falls through */
2017-05-09 18:07:57 +00:00
case 874 :
case 932 :
case 936 :
case 949 :
case 950 :
case 1250 :
case 1251 :
case 1253 :
case 1254 :
case 1255 :
case 1256 :
case 1257 :
case 1258 :
case 10000 :
case 1200 :
case 1201 :
case 1252 :
case 65000 : case - 536 :
case 65001 : case - 535 :
2017-10-17 00:14:32 +00:00
set _cp ( CodePage = ( PropH [ piddsi . n ] >>> 0 ) & 0xFFFF ) ; break ;
2015-04-02 20:32:22 +00:00
default : throw new Error ( "Unsupported CodePage: " + PropH [ piddsi . n ] ) ;
}
} else {
if ( Props [ i ] [ 0 ] === 0x1 ) {
2017-10-17 00:14:32 +00:00
CodePage = PropH . CodePage = ( parse _TypedPropertyValue ( blob , VT _I2 ) /*:number*/ ) ;
2015-04-02 20:32:22 +00:00
set _cp ( CodePage ) ;
if ( Dictionary !== - 1 ) {
var oldpos = blob . l ;
blob . l = Props [ Dictionary ] [ 1 ] ;
DictObj = parse _dictionary ( blob , CodePage ) ;
blob . l = oldpos ;
}
} else if ( Props [ i ] [ 0 ] === 0 ) {
if ( CodePage === 0 ) { Dictionary = i ; blob . l = Props [ i + 1 ] [ 1 ] ; continue ; }
DictObj = parse _dictionary ( blob , CodePage ) ;
} else {
var name = DictObj [ Props [ i ] [ 0 ] ] ;
var val ;
/* [MS-OSHARED] 2.3.3.2.3.1.2 + PROPVARIANT */
switch ( blob [ blob . l ] ) {
case 0x41 /*VT_BLOB*/ : blob . l += 4 ; val = parse _BLOB ( blob ) ; break ;
2018-05-05 06:34:37 +00:00
case 0x1E /*VT_LPSTR*/ : blob . l += 4 ; val = parse _VtString ( blob , blob [ blob . l - 4 ] ) . replace ( /\u0000+$/ , "" ) ; break ;
case 0x1F /*VT_LPWSTR*/ : blob . l += 4 ; val = parse _VtString ( blob , blob [ blob . l - 4 ] ) . replace ( /\u0000+$/ , "" ) ; break ;
2015-04-02 20:32:22 +00:00
case 0x03 /*VT_I4*/ : blob . l += 4 ; val = blob . read _shift ( 4 , 'i' ) ; break ;
case 0x13 /*VT_UI4*/ : blob . l += 4 ; val = blob . read _shift ( 4 ) ; break ;
case 0x05 /*VT_R8*/ : blob . l += 4 ; val = blob . read _shift ( 8 , 'f' ) ; break ;
case 0x0B /*VT_BOOL*/ : blob . l += 4 ; val = parsebool ( blob , 4 ) ; break ;
2017-03-23 01:18:40 +00:00
case 0x40 /*VT_FILETIME*/ : blob . l += 4 ; val = parseDate ( parse _FILETIME ( blob ) ) ; break ;
2015-04-02 20:32:22 +00:00
default : throw new Error ( "unparsed value: " + blob [ blob . l ] ) ;
}
PropH [ name ] = val ;
}
}
}
blob . l = start _addr + size ; /* step ahead to skip padding */
return PropH ;
}
2018-05-05 06:34:37 +00:00
var XLSPSSkip = [ "CodePage" , "Thumbnail" , "_PID_LINKBASE" , "_PID_HLINKS" , "SystemIdentifier" , "FMTID" ] . concat ( PseudoPropsPairs ) ;
function guess _property _type ( val /*:any*/ ) /*:number*/ {
switch ( typeof val ) {
case "boolean" : return 0x0B ;
case "number" : return ( ( val | 0 ) == val ) ? 0x03 : 0x05 ;
case "string" : return 0x1F ;
case "object" : if ( val instanceof Date ) return 0x40 ; break ;
}
return - 1 ;
}
function write _PropertySet ( entries , RE , PIDSI ) {
var hdr = new _buf ( 8 ) , piao = [ ] , prop = [ ] ;
var sz = 8 , i = 0 ;
var pr = new _buf ( 8 ) , pio = new _buf ( 8 ) ;
pr . write _shift ( 4 , 0x0002 ) ;
pr . write _shift ( 4 , 0x04B0 ) ;
pio . write _shift ( 4 , 0x0001 ) ;
prop . push ( pr ) ; piao . push ( pio ) ;
sz += 8 + pr . length ;
if ( ! RE ) {
pio = new _buf ( 8 ) ;
pio . write _shift ( 4 , 0 ) ;
piao . unshift ( pio ) ;
var bufs = [ new _buf ( 4 ) ] ;
bufs [ 0 ] . write _shift ( 4 , entries . length ) ;
for ( i = 0 ; i < entries . length ; ++ i ) {
var value = entries [ i ] [ 0 ] ;
pr = new _buf ( 4 + 4 + 2 * ( value . length + 1 ) + ( value . length % 2 ? 0 : 2 ) ) ;
pr . write _shift ( 4 , i + 2 ) ;
pr . write _shift ( 4 , value . length + 1 ) ;
pr . write _shift ( 0 , value , "dbcs" ) ;
while ( pr . l != pr . length ) pr . write _shift ( 1 , 0 ) ;
bufs . push ( pr ) ;
}
pr = bconcat ( bufs ) ;
prop . unshift ( pr ) ;
sz += 8 + pr . length ;
}
for ( i = 0 ; i < entries . length ; ++ i ) {
if ( RE && ! RE [ entries [ i ] [ 0 ] ] ) continue ;
if ( XLSPSSkip . indexOf ( entries [ i ] [ 0 ] ) > - 1 ) continue ;
if ( entries [ i ] [ 1 ] == null ) continue ;
var val = entries [ i ] [ 1 ] , idx = 0 ;
if ( RE ) {
idx = + RE [ entries [ i ] [ 0 ] ] ;
2018-05-20 01:34:59 +00:00
var pinfo = ( PIDSI /*:: || {}*/ ) [ idx ] /*:: || {} */ ;
if ( pinfo . p == "version" && typeof val == "string" ) {
/*:: if(typeof val !== "string") throw "unreachable"; */
var arr = val . split ( "." ) ;
val = ( ( + arr [ 0 ] ) << 16 ) + ( ( + arr [ 1 ] ) || 0 ) ;
}
2018-05-05 06:34:37 +00:00
pr = write _TypedPropertyValue ( pinfo . t , val ) ;
} else {
var T = guess _property _type ( val ) ;
if ( T == - 1 ) { T = 0x1F ; val = String ( val ) ; }
pr = write _TypedPropertyValue ( T , val ) ;
}
prop . push ( pr ) ;
pio = new _buf ( 8 ) ;
pio . write _shift ( 4 , ! RE ? 2 + i : idx ) ;
piao . push ( pio ) ;
sz += 8 + pr . length ;
}
var w = 8 * ( prop . length + 1 ) ;
for ( i = 0 ; i < prop . length ; ++ i ) { piao [ i ] . write _shift ( 4 , w ) ; w += prop [ i ] . length ; }
hdr . write _shift ( 4 , sz ) ;
hdr . write _shift ( 4 , prop . length ) ;
return bconcat ( [ hdr ] . concat ( piao ) . concat ( prop ) ) ;
}
2015-04-02 20:32:22 +00:00
/* [MS-OLEPS] 2.21 PropertySetStream */
2018-02-14 20:06:35 +00:00
function parse _PropertySetStream ( file , PIDSI , clsid ) {
2015-04-02 20:32:22 +00:00
var blob = file . content ;
2017-09-30 06:18:11 +00:00
if ( ! blob ) return ( { } /*:any*/ ) ;
2015-04-02 20:32:22 +00:00
prep _blob ( blob , 0 ) ;
2017-02-10 19:23:01 +00:00
var NumSets , FMTID0 , FMTID1 , Offset0 , Offset1 = 0 ;
2015-04-02 20:32:22 +00:00
blob . chk ( 'feff' , 'Byte Order: ' ) ;
2018-01-23 09:07:51 +00:00
/*var vers = */ blob . read _shift ( 2 ) ; // TODO: check version
2015-04-02 20:32:22 +00:00
var SystemIdentifier = blob . read _shift ( 4 ) ;
2018-02-14 20:06:35 +00:00
var CLSID = blob . read _shift ( 16 ) ;
if ( CLSID !== CFB . utils . consts . HEADER _CLSID && CLSID !== clsid ) throw new Error ( "Bad PropertySet CLSID " + CLSID ) ;
2015-04-02 20:32:22 +00:00
NumSets = blob . read _shift ( 4 ) ;
2017-03-27 21:35:15 +00:00
if ( NumSets !== 1 && NumSets !== 2 ) throw new Error ( "Unrecognized #Sets: " + NumSets ) ;
2015-04-02 20:32:22 +00:00
FMTID0 = blob . read _shift ( 16 ) ; Offset0 = blob . read _shift ( 4 ) ;
2017-03-27 21:35:15 +00:00
if ( NumSets === 1 && Offset0 !== blob . l ) throw new Error ( "Length mismatch: " + Offset0 + " !== " + blob . l ) ;
2015-04-02 20:32:22 +00:00
else if ( NumSets === 2 ) { FMTID1 = blob . read _shift ( 16 ) ; Offset1 = blob . read _shift ( 4 ) ; }
var PSet0 = parse _PropertySet ( blob , PIDSI ) ;
2017-02-10 19:23:01 +00:00
var rval = ( { SystemIdentifier : SystemIdentifier } /*:any*/ ) ;
2015-04-02 20:32:22 +00:00
for ( var y in PSet0 ) rval [ y ] = PSet0 [ y ] ;
//rval.blob = blob;
rval . FMTID = FMTID0 ;
//rval.PSet0 = PSet0;
if ( NumSets === 1 ) return rval ;
2018-02-21 07:01:34 +00:00
if ( Offset1 - blob . l == 2 ) blob . l += 2 ;
2017-02-10 19:23:01 +00:00
if ( blob . l !== Offset1 ) throw new Error ( "Length mismatch 2: " + blob . l + " !== " + Offset1 ) ;
2015-04-02 20:32:22 +00:00
var PSet1 ;
2017-05-09 18:07:57 +00:00
try { PSet1 = parse _PropertySet ( blob , null ) ; } catch ( e ) { /* empty */ }
2015-04-02 20:32:22 +00:00
for ( y in PSet1 ) rval [ y ] = PSet1 [ y ] ;
rval . FMTID = [ FMTID0 , FMTID1 ] ; // TODO: verify FMTID0/1
return rval ;
}
2018-05-20 01:34:59 +00:00
function write _PropertySetStream ( entries , clsid , RE , PIDSI /*:{[key:string|number]:any}*/ , entries2 /*:?any*/ , clsid2 /*:?any*/ ) {
2018-05-05 06:34:37 +00:00
var hdr = new _buf ( entries2 ? 68 : 48 ) ;
var bufs = [ hdr ] ;
hdr . write _shift ( 2 , 0xFFFE ) ;
hdr . write _shift ( 2 , 0x0000 ) ; /* TODO: type 1 props */
hdr . write _shift ( 4 , 0x32363237 ) ;
hdr . write _shift ( 16 , CFB . utils . consts . HEADER _CLSID , "hex" ) ;
hdr . write _shift ( 4 , ( entries2 ? 2 : 1 ) ) ;
hdr . write _shift ( 16 , clsid , "hex" ) ;
hdr . write _shift ( 4 , ( entries2 ? 68 : 48 ) ) ;
var ps0 = write _PropertySet ( entries , RE , PIDSI ) ;
bufs . push ( ps0 ) ;
2015-04-02 20:32:22 +00:00
2018-05-05 06:34:37 +00:00
if ( entries2 ) {
var ps1 = write _PropertySet ( entries2 , null , null ) ;
hdr . write _shift ( 16 , clsid2 , "hex" ) ;
hdr . write _shift ( 4 , 68 + ps0 . length ) ;
bufs . push ( ps1 ) ;
}
return bconcat ( bufs ) ;
}
2015-04-02 20:32:22 +00:00
function parsenoop2 ( blob , length ) { blob . read _shift ( length ) ; return null ; }
2017-09-22 22:18:51 +00:00
function writezeroes ( n , o ) { if ( ! o ) o = new _buf ( n ) ; for ( var j = 0 ; j < n ; ++ j ) o . write _shift ( 1 , 0 ) ; return o ; }
2015-04-02 20:32:22 +00:00
function parslurp ( blob , length , cb ) {
var arr = [ ] , target = blob . l + length ;
while ( blob . l < target ) arr . push ( cb ( blob , target - blob . l ) ) ;
if ( target !== blob . l ) throw new Error ( "Slurp error" ) ;
return arr ;
}
2017-09-22 22:18:51 +00:00
function parsebool ( blob , length /*:number*/ ) { return blob . read _shift ( length ) === 0x1 ; }
function writebool ( v /*:any*/ , o ) { if ( ! o ) o = new _buf ( 2 ) ; o . write _shift ( 2 , + ! ! v ) ; return o ; }
2015-04-02 20:32:22 +00:00
2017-07-05 22:27:54 +00:00
function parseuint16 ( blob /*::, length:?number, opts:?any*/ ) { return blob . read _shift ( 2 , 'u' ) ; }
2017-09-22 22:18:51 +00:00
function writeuint16 ( v /*:number*/ , o ) { if ( ! o ) o = new _buf ( 2 ) ; o . write _shift ( 2 , v ) ; return o ; }
2017-07-05 22:27:54 +00:00
function parseuint16a ( blob , length /*:: :?number, opts:?any*/ ) { return parslurp ( blob , length , parseuint16 ) ; }
2015-04-02 20:32:22 +00:00
/* --- 2.5 Structures --- */
/* [MS-XLS] 2.5.10 Bes (boolean or error) */
2017-07-05 22:27:54 +00:00
function parse _Bes ( blob /*::, length*/ ) {
2015-04-02 20:32:22 +00:00
var v = blob . read _shift ( 1 ) , t = blob . read _shift ( 1 ) ;
return t === 0x01 ? v : v === 0x01 ;
}
2017-09-22 22:18:51 +00:00
function write _Bes ( v , t /*:string*/ , o ) {
if ( ! o ) o = new _buf ( 2 ) ;
2021-09-26 22:51:19 +00:00
o . write _shift ( 1 , ( ( t == 'e' ) ? + v : + ! ! v ) ) ;
2018-02-14 20:06:35 +00:00
o . write _shift ( 1 , ( ( t == 'e' ) ? 1 : 0 ) ) ;
2017-09-22 22:18:51 +00:00
return o ;
}
2015-04-02 20:32:22 +00:00
/* [MS-XLS] 2.5.240 ShortXLUnicodeString */
function parse _ShortXLUnicodeString ( blob , length , opts ) {
2017-02-19 20:36:32 +00:00
var cch = blob . read _shift ( opts && opts . biff >= 12 ? 2 : 1 ) ;
2018-01-23 09:07:51 +00:00
var encoding = 'sbcs-cont' ;
2015-04-02 20:32:22 +00:00
var cp = current _codepage ;
if ( opts && opts . biff >= 8 ) current _codepage = 1200 ;
2017-02-19 20:36:32 +00:00
if ( ! opts || opts . biff == 8 ) {
2015-04-02 20:32:22 +00:00
var fHighByte = blob . read _shift ( 1 ) ;
2018-01-23 09:07:51 +00:00
if ( fHighByte ) { encoding = 'dbcs-cont' ; }
2017-02-19 20:36:32 +00:00
} else if ( opts . biff == 12 ) {
2018-01-23 09:07:51 +00:00
encoding = 'wstr' ;
2015-04-02 20:32:22 +00:00
}
2018-01-11 08:01:25 +00:00
if ( opts . biff >= 2 && opts . biff <= 5 ) encoding = 'cpstr' ;
2015-04-02 20:32:22 +00:00
var o = cch ? blob . read _shift ( cch , encoding ) : "" ;
current _codepage = cp ;
return o ;
}
/* 2.5.293 XLUnicodeRichExtendedString */
function parse _XLUnicodeRichExtendedString ( blob ) {
var cp = current _codepage ;
current _codepage = 1200 ;
var cch = blob . read _shift ( 2 ) , flags = blob . read _shift ( 1 ) ;
2018-01-23 09:07:51 +00:00
var /*fHighByte = flags & 0x1,*/ fExtSt = flags & 0x4 , fRichSt = flags & 0x8 ;
2015-04-02 20:32:22 +00:00
var width = 1 + ( flags & 0x1 ) ; // 0x0 -> utf8, 0x1 -> dbcs
2017-02-10 19:23:01 +00:00
var cRun = 0 , cbExtRst ;
2015-04-02 20:32:22 +00:00
var z = { } ;
if ( fRichSt ) cRun = blob . read _shift ( 2 ) ;
if ( fExtSt ) cbExtRst = blob . read _shift ( 4 ) ;
2017-12-30 05:40:35 +00:00
var encoding = width == 2 ? 'dbcs-cont' : 'sbcs-cont' ;
2015-04-02 20:32:22 +00:00
var msg = cch === 0 ? "" : blob . read _shift ( cch , encoding ) ;
if ( fRichSt ) blob . l += 4 * cRun ; //TODO: parse this
if ( fExtSt ) blob . l += cbExtRst ; //TODO: parse this
z . t = msg ;
if ( ! fRichSt ) { z . raw = "<t>" + z . t + "</t>" ; z . r = z . t ; }
current _codepage = cp ;
return z ;
}
2020-06-29 08:07:23 +00:00
function write _XLUnicodeRichExtendedString ( xlstr /*:: :XLString, opts*/ ) {
var str = ( xlstr . t || "" ) , nfmts = 1 ;
var hdr = new _buf ( 3 + ( nfmts > 1 ? 2 : 0 ) ) ;
hdr . write _shift ( 2 , str . length ) ;
hdr . write _shift ( 1 , ( nfmts > 1 ? 0x08 : 0x00 ) | 0x01 ) ;
if ( nfmts > 1 ) hdr . write _shift ( 2 , nfmts ) ;
var otext = new _buf ( 2 * str . length ) ;
otext . write _shift ( 2 * str . length , str , 'utf16le' ) ;
var out = [ hdr , otext ] ;
return bconcat ( out ) ;
}
2015-04-02 20:32:22 +00:00
/* 2.5.296 XLUnicodeStringNoCch */
function parse _XLUnicodeStringNoCch ( blob , cch , opts ) {
var retval ;
2017-02-19 20:36:32 +00:00
if ( opts ) {
2018-01-11 08:01:25 +00:00
if ( opts . biff >= 2 && opts . biff <= 5 ) return blob . read _shift ( cch , 'cpstr' ) ;
2017-02-19 20:36:32 +00:00
if ( opts . biff >= 12 ) return blob . read _shift ( cch , 'dbcs-cont' ) ;
}
2015-04-02 20:32:22 +00:00
var fHighByte = blob . read _shift ( 1 ) ;
if ( fHighByte === 0 ) { retval = blob . read _shift ( cch , 'sbcs-cont' ) ; }
else { retval = blob . read _shift ( cch , 'dbcs-cont' ) ; }
return retval ;
}
/* 2.5.294 XLUnicodeString */
function parse _XLUnicodeString ( blob , length , opts ) {
2017-02-19 20:36:32 +00:00
var cch = blob . read _shift ( opts && opts . biff == 2 ? 1 : 2 ) ;
2015-04-02 20:32:22 +00:00
if ( cch === 0 ) { blob . l ++ ; return "" ; }
return parse _XLUnicodeStringNoCch ( blob , cch , opts ) ;
}
/* BIFF5 override */
function parse _XLUnicodeString2 ( blob , length , opts ) {
2017-02-10 19:23:01 +00:00
if ( opts . biff > 5 ) return parse _XLUnicodeString ( blob , length , opts ) ;
2015-04-02 20:32:22 +00:00
var cch = blob . read _shift ( 1 ) ;
if ( cch === 0 ) { blob . l ++ ; return "" ; }
2018-01-11 08:01:25 +00:00
return blob . read _shift ( cch , ( opts . biff <= 4 || ! blob . lens ) ? 'cpstr' : 'sbcs-cont' ) ;
2015-04-02 20:32:22 +00:00
}
2017-10-27 16:25:54 +00:00
/* TODO: BIFF5 and lower, codepage awareness */
function write _XLUnicodeString ( str , opts , o ) {
if ( ! o ) o = new _buf ( 3 + 2 * str . length ) ;
o . write _shift ( 2 , str . length ) ;
o . write _shift ( 1 , 1 ) ;
o . write _shift ( 31 , str , 'utf16le' ) ;
return o ;
}
2015-04-02 20:32:22 +00:00
/* [MS-XLS] 2.5.61 ControlInfo */
2018-01-23 09:07:51 +00:00
function parse _ControlInfo ( blob /*::, length, opts*/ ) {
2017-07-26 08:35:28 +00:00
var flags = blob . read _shift ( 1 ) ;
blob . l ++ ;
var accel = blob . read _shift ( 2 ) ;
blob . l += 2 ;
return [ flags , accel ] ;
}
2015-04-02 20:32:22 +00:00
/* [MS-OSHARED] 2.3.7.6 URLMoniker TODO: flags */
2017-09-22 22:18:51 +00:00
function parse _URLMoniker ( blob /*::, length, opts*/ ) {
2015-04-02 20:32:22 +00:00
var len = blob . read _shift ( 4 ) , start = blob . l ;
var extra = false ;
if ( len > 24 ) {
/* look ahead */
blob . l += len - 24 ;
if ( blob . read _shift ( 16 ) === "795881f43b1d7f48af2c825dc4852763" ) extra = true ;
blob . l = start ;
}
var url = blob . read _shift ( ( extra ? len - 24 : len ) >> 1 , 'utf16le' ) . replace ( chr0 , "" ) ;
if ( extra ) blob . l += 24 ;
return url ;
2017-09-22 22:18:51 +00:00
}
2015-04-02 20:32:22 +00:00
/* [MS-OSHARED] 2.3.7.8 FileMoniker TODO: all fields */
2018-01-23 09:07:51 +00:00
function parse _FileMoniker ( blob /*::, length*/ ) {
2021-09-30 07:28:03 +00:00
var cAnti = blob . read _shift ( 2 ) ;
var preamble = "" ; while ( cAnti -- > 0 ) preamble += "../" ;
2017-12-30 05:40:35 +00:00
var ansiPath = blob . read _shift ( 0 , 'lpstr-ansi' ) ;
2018-01-23 09:07:51 +00:00
blob . l += 2 ; //var endServer = blob.read_shift(2);
2017-12-30 05:40:35 +00:00
if ( blob . read _shift ( 2 ) != 0xDEAD ) throw new Error ( "Bad FileMoniker" ) ;
var sz = blob . read _shift ( 4 ) ;
2021-09-30 07:28:03 +00:00
if ( sz === 0 ) return preamble + ansiPath . replace ( /\\/g , "/" ) ;
2017-12-30 05:40:35 +00:00
var bytes = blob . read _shift ( 4 ) ;
if ( blob . read _shift ( 2 ) != 3 ) throw new Error ( "Bad FileMoniker" ) ;
var unicodePath = blob . read _shift ( bytes >> 1 , 'utf16le' ) . replace ( chr0 , "" ) ;
2021-09-30 07:28:03 +00:00
return preamble + unicodePath ;
2017-09-22 22:18:51 +00:00
}
2015-04-02 20:32:22 +00:00
/* [MS-OSHARED] 2.3.7.2 HyperlinkMoniker TODO: all the monikers */
2017-09-22 22:18:51 +00:00
function parse _HyperlinkMoniker ( blob , length ) {
2015-04-02 20:32:22 +00:00
var clsid = blob . read _shift ( 16 ) ; length -= 16 ;
switch ( clsid ) {
case "e0c9ea79f9bace118c8200aa004ba90b" : return parse _URLMoniker ( blob , length ) ;
case "0303000000000000c000000000000046" : return parse _FileMoniker ( blob , length ) ;
2017-03-27 21:35:15 +00:00
default : throw new Error ( "Unsupported Moniker " + clsid ) ;
2015-04-02 20:32:22 +00:00
}
2017-09-22 22:18:51 +00:00
}
2015-04-02 20:32:22 +00:00
/* [MS-OSHARED] 2.3.7.9 HyperlinkString */
2018-01-23 09:07:51 +00:00
function parse _HyperlinkString ( blob /*::, length*/ ) {
2015-04-02 20:32:22 +00:00
var len = blob . read _shift ( 4 ) ;
2017-12-15 01:18:40 +00:00
var o = len > 0 ? blob . read _shift ( len , 'utf16le' ) . replace ( chr0 , "" ) : "" ;
2015-04-02 20:32:22 +00:00
return o ;
2017-09-22 22:18:51 +00:00
}
2021-09-30 07:28:03 +00:00
function write _HyperlinkString ( str /*:string*/ , o ) {
if ( ! o ) o = new _buf ( 6 + str . length * 2 ) ;
o . write _shift ( 4 , 1 + str . length ) ;
for ( var i = 0 ; i < str . length ; ++ i ) o . write _shift ( 2 , str . charCodeAt ( i ) ) ;
o . write _shift ( 2 , 0 ) ;
return o ;
}
2015-04-02 20:32:22 +00:00
2017-12-15 01:18:40 +00:00
/* [MS-OSHARED] 2.3.7.1 Hyperlink Object */
function parse _Hyperlink ( blob , length ) /*:Hyperlink*/ {
2015-04-02 20:32:22 +00:00
var end = blob . l + length ;
var sVer = blob . read _shift ( 4 ) ;
if ( sVer !== 2 ) throw new Error ( "Unrecognized streamVersion: " + sVer ) ;
var flags = blob . read _shift ( 2 ) ;
blob . l += 2 ;
2018-01-23 09:07:51 +00:00
var displayName , targetFrameName , moniker , oleMoniker , Loc = "" , guid , fileTime ;
2015-04-02 20:32:22 +00:00
if ( flags & 0x0010 ) displayName = parse _HyperlinkString ( blob , end - blob . l ) ;
if ( flags & 0x0080 ) targetFrameName = parse _HyperlinkString ( blob , end - blob . l ) ;
if ( ( flags & 0x0101 ) === 0x0101 ) moniker = parse _HyperlinkString ( blob , end - blob . l ) ;
if ( ( flags & 0x0101 ) === 0x0001 ) oleMoniker = parse _HyperlinkMoniker ( blob , end - blob . l ) ;
2018-01-23 09:07:51 +00:00
if ( flags & 0x0008 ) Loc = parse _HyperlinkString ( blob , end - blob . l ) ;
2015-04-02 20:32:22 +00:00
if ( flags & 0x0020 ) guid = blob . read _shift ( 16 ) ;
2017-07-26 08:35:28 +00:00
if ( flags & 0x0040 ) fileTime = parse _FILETIME ( blob /*, 8*/ ) ;
2015-04-02 20:32:22 +00:00
blob . l = end ;
2017-12-15 01:18:40 +00:00
var target = targetFrameName || moniker || oleMoniker || "" ;
2018-01-23 09:07:51 +00:00
if ( target && Loc ) target += "#" + Loc ;
if ( ! target ) target = "#" + Loc ;
2021-09-30 07:28:03 +00:00
if ( ( flags & 0x0002 ) && target . charAt ( 0 ) == "/" && target . charAt ( 1 ) != "/" ) target = "file://" + target ;
2018-01-23 09:07:51 +00:00
var out = ( { Target : target } /*:any*/ ) ;
if ( guid ) out . guid = guid ;
if ( fileTime ) out . time = fileTime ;
if ( displayName ) out . Tooltip = displayName ;
return out ;
2017-09-22 22:18:51 +00:00
}
2017-12-15 01:18:40 +00:00
function write _Hyperlink ( hl ) {
var out = new _buf ( 512 ) , i = 0 ;
var Target = hl . Target ;
2021-09-30 07:28:03 +00:00
if ( Target . slice ( 0 , 7 ) == "file://" ) Target = Target . slice ( 7 ) ;
var hashidx = Target . indexOf ( "#" ) ;
var F = hashidx > - 1 ? 0x1f : 0x17 ;
2017-12-15 01:18:40 +00:00
switch ( Target . charAt ( 0 ) ) { case "#" : F = 0x1c ; break ; case "." : F &= ~ 2 ; break ; }
out . write _shift ( 4 , 2 ) ; out . write _shift ( 4 , F ) ;
var data = [ 8 , 6815827 , 6619237 , 4849780 , 83 ] ; for ( i = 0 ; i < data . length ; ++ i ) out . write _shift ( 4 , data [ i ] ) ;
if ( F == 0x1C ) {
Target = Target . slice ( 1 ) ;
2021-09-30 07:28:03 +00:00
write _HyperlinkString ( Target , out ) ;
2017-12-15 01:18:40 +00:00
} else if ( F & 0x02 ) {
data = "e0 c9 ea 79 f9 ba ce 11 8c 82 00 aa 00 4b a9 0b" . split ( " " ) ;
for ( i = 0 ; i < data . length ; ++ i ) out . write _shift ( 1 , parseInt ( data [ i ] , 16 ) ) ;
2021-09-30 07:28:03 +00:00
var Pretarget = hashidx > - 1 ? Target . slice ( 0 , hashidx ) : Target ;
out . write _shift ( 4 , 2 * ( Pretarget . length + 1 ) ) ;
for ( i = 0 ; i < Pretarget . length ; ++ i ) out . write _shift ( 2 , Pretarget . charCodeAt ( i ) ) ;
2017-12-15 01:18:40 +00:00
out . write _shift ( 2 , 0 ) ;
2021-09-30 07:28:03 +00:00
if ( F & 0x08 ) write _HyperlinkString ( hashidx > - 1 ? Target . slice ( hashidx + 1 ) : "" , out ) ;
2017-12-15 01:18:40 +00:00
} else {
data = "03 03 00 00 00 00 00 00 c0 00 00 00 00 00 00 46" . split ( " " ) ;
for ( i = 0 ; i < data . length ; ++ i ) out . write _shift ( 1 , parseInt ( data [ i ] , 16 ) ) ;
var P = 0 ;
while ( Target . slice ( P * 3 , P * 3 + 3 ) == "../" || Target . slice ( P * 3 , P * 3 + 3 ) == "..\\" ) ++ P ;
out . write _shift ( 2 , P ) ;
2021-09-30 07:28:03 +00:00
out . write _shift ( 4 , Target . length - 3 * P + 1 ) ;
for ( i = 0 ; i < Target . length - 3 * P ; ++ i ) out . write _shift ( 1 , Target . charCodeAt ( i + 3 * P ) & 0xFF ) ;
2017-12-15 01:18:40 +00:00
out . write _shift ( 1 , 0 ) ;
out . write _shift ( 2 , 0xFFFF ) ;
out . write _shift ( 2 , 0xDEAD ) ;
for ( i = 0 ; i < 6 ; ++ i ) out . write _shift ( 4 , 0 ) ;
}
return out . slice ( 0 , out . l ) ;
}
2015-04-02 20:32:22 +00:00
/* 2.5.178 LongRGBA */
2018-01-23 09:07:51 +00:00
function parse _LongRGBA ( blob /*::, length*/ ) { var r = blob . read _shift ( 1 ) , g = blob . read _shift ( 1 ) , b = blob . read _shift ( 1 ) , a = blob . read _shift ( 1 ) ; return [ r , g , b , a ] ; }
2015-04-02 20:32:22 +00:00
/* 2.5.177 LongRGB */
function parse _LongRGB ( blob , length ) { var x = parse _LongRGBA ( blob , length ) ; x [ 3 ] = 0 ; return x ; }