2017-02-24 05:11:45 +00:00
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint eqnull:true */
var Base64 = ( function ( ) {
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ;
return {
2017-07-28 17:53:08 +00:00
decode : function ( input /*:string*/ ) /*:string*/ {
2017-02-24 05:11:45 +00:00
var o = "" ;
2017-07-28 17:53:08 +00:00
var c1 /*:number*/ , c2 /*:number*/ , c3 /*:number*/ ;
var e1 /*:number*/ , e2 /*:number*/ , e3 /*:number*/ , e4 /*:number*/ ;
2017-02-24 05:11:45 +00:00
input = input . replace ( /[^\w\+\/\=]/g , "" ) ;
for ( var i = 0 ; i < input . length ; ) {
e1 = map . indexOf ( input . charAt ( i ++ ) ) ;
e2 = map . indexOf ( input . charAt ( i ++ ) ) ;
c1 = ( e1 << 2 ) | ( e2 >> 4 ) ;
o += String . fromCharCode ( c1 ) ;
e3 = map . indexOf ( input . charAt ( i ++ ) ) ;
c2 = ( ( e2 & 15 ) << 4 ) | ( e3 >> 2 ) ;
if ( e3 !== 64 ) { o += String . fromCharCode ( c2 ) ; }
e4 = map . indexOf ( input . charAt ( i ++ ) ) ;
c3 = ( ( e3 & 3 ) << 6 ) | e4 ;
if ( e4 !== 64 ) { o += String . fromCharCode ( c3 ) ; }
}
return o ;
}
} ;
} ) ( ) ;
var chr0 = /\u0000/g , chr1 = /[\u0001-\u0006]/ ;
2017-07-28 17:53:08 +00:00
var s2a = function _s2a ( s /*:string*/ ) /*:RawBytes*/ { return s . split ( "" ) . map ( function ( x ) { return x . charCodeAt ( 0 ) & 0xff ; } ) ; } ;
var _s2a = s2a ;
var _ _toBuffer = function ( bufs /*:Array<Array<RawBytes> >*/ ) /*:RawBytes*/ { var x = [ ] ; for ( var i = 0 ; i < bufs [ 0 ] . length ; ++ i ) { x . push . apply ( x , bufs [ 0 ] [ i ] ) ; } return x ; } ;
var _ _ _toBuffer = _ _toBuffer ;
var _ _utf16le = function ( b /*:RawBytes|CFBlob*/ , s /*:number*/ , e /*:number*/ ) /*:string*/ { var ss /*:Array<string>*/ = [ ] ; for ( var i = s ; i < e ; i += 2 ) ss . push ( String . fromCharCode ( _ _readUInt16LE ( b , i ) ) ) ; return ss . join ( "" ) . replace ( chr0 , '' ) . replace ( chr1 , '!' ) ; } ;
var _ _ _utf16le = _ _utf16le ;
var _ _hexlify = function ( b /*:RawBytes|CFBlob*/ , s /*:number*/ , l /*:number*/ ) /*:string*/ { var ss /*:Array<string>*/ = [ ] ; for ( var i = s ; i < s + l ; ++ i ) ss . push ( ( "0" + b [ i ] . toString ( 16 ) ) . slice ( - 2 ) ) ; return ss . join ( "" ) ; } ;
var _ _ _hexlify = _ _hexlify ;
var _ _bconcat = function ( bufs /*:Array<RawBytes>*/ ) /*:RawBytes*/ {
if ( Array . isArray ( bufs [ 0 ] ) /*:: && bufs[0] instanceof Array*/ ) return /*::(*/ [ ] . concat . apply ( [ ] , bufs ) /*:: :any)*/ ;
var maxlen = 0 , i = 0 ;
for ( i = 0 ; i < bufs . length ; ++ i ) maxlen += bufs [ i ] . length ;
var o = new Uint8Array ( maxlen ) ;
for ( i = 0 , maxlen = 0 ; i < bufs . length ; maxlen += bufs [ i ] . length , ++ i ) o . set ( bufs [ i ] , maxlen ) ;
return o ;
} ;
var bconcat = _ _bconcat ;
2017-02-24 05:11:45 +00:00
if ( typeof Buffer !== "undefined" ) {
2017-07-28 17:53:08 +00:00
_ _utf16le = function ( b /*:RawBytes|CFBlob*/ , s /*:number*/ , e /*:number*/ ) /*:string*/ {
if ( ! Buffer . isBuffer ( b ) /*:: || !(b instanceof Buffer)*/ ) return _ _ _utf16le ( b , s , e ) ;
2017-02-24 05:11:45 +00:00
return b . toString ( 'utf16le' , s , e ) . replace ( chr0 , '' ) . replace ( chr1 , '!' ) ;
} ;
2017-07-28 17:53:08 +00:00
_ _hexlify = function ( b /*:RawBytes|CFBlob*/ , s /*:number*/ , l /*:number*/ ) /*:string*/ { return Buffer . isBuffer ( b ) /*:: && b instanceof Buffer*/ ? b . toString ( 'hex' , s , s + l ) : _ _ _hexlify ( b , s , l ) ; } ;
_ _toBuffer = function ( bufs /*:Array<Array<RawBytes>>*/ ) /*:RawBytes*/ { return ( bufs [ 0 ] . length > 0 && Buffer . isBuffer ( bufs [ 0 ] [ 0 ] ) ) ? Buffer . concat ( ( bufs [ 0 ] /*:any*/ ) ) : _ _ _toBuffer ( bufs ) ; } ;
s2a = function ( s /*:string*/ ) /*:RawBytes*/ { return new Buffer ( s , "binary" ) ; } ;
bconcat = function ( bufs /*:Array<RawBytes>*/ ) /*:RawBytes*/ { return Buffer . isBuffer ( bufs [ 0 ] ) ? Buffer . concat ( /*::(*/ bufs /*:: :any)*/ ) : _ _bconcat ( bufs ) ; } ;
2017-02-24 05:11:45 +00:00
}
2017-07-28 17:53:08 +00:00
var _ _readUInt8 = function ( b /*:RawBytes|CFBlob*/ , idx /*:number*/ ) /*:number*/ { return b [ idx ] ; } ;
var _ _readUInt16LE = function ( b /*:RawBytes|CFBlob*/ , idx /*:number*/ ) /*:number*/ { return b [ idx + 1 ] * ( 1 << 8 ) + b [ idx ] ; } ;
var _ _readInt16LE = function ( b /*:RawBytes|CFBlob*/ , idx /*:number*/ ) /*:number*/ { var u = b [ idx + 1 ] * ( 1 << 8 ) + b [ idx ] ; return ( u < 0x8000 ) ? u : ( 0xffff - u + 1 ) * - 1 ; } ;
var _ _readUInt32LE = function ( b /*:RawBytes|CFBlob*/ , idx /*:number*/ ) /*:number*/ { return b [ idx + 3 ] * ( 1 << 24 ) + ( b [ idx + 2 ] << 16 ) + ( b [ idx + 1 ] << 8 ) + b [ idx ] ; } ;
var _ _readInt32LE = function ( b /*:RawBytes|CFBlob*/ , idx /*:number*/ ) /*:number*/ { return ( b [ idx + 3 ] << 24 ) + ( b [ idx + 2 ] << 16 ) + ( b [ idx + 1 ] << 8 ) + b [ idx ] ; } ;
2017-02-24 05:11:45 +00:00
2017-07-28 17:53:08 +00:00
function ReadShift ( size /*:number*/ , t /*:?string*/ ) /*:number|string*/ {
var oI /*:: :number = 0*/ , oS /*:: :string = ""*/ , type = 0 ;
2017-02-24 05:11:45 +00:00
switch ( size ) {
case 1 : oI = _ _readUInt8 ( this , this . l ) ; break ;
case 2 : oI = ( t !== 'i' ? _ _readUInt16LE : _ _readInt16LE ) ( this , this . l ) ; break ;
case 4 : oI = _ _readInt32LE ( this , this . l ) ; break ;
case 16 : type = 2 ; oS = _ _hexlify ( this , this . l , size ) ;
}
this . l += size ; if ( type === 0 ) return oI ; return oS ;
}
2017-07-28 17:53:08 +00:00
function CheckField ( hexstr /*:string*/ , fld /*:string*/ ) /*:void*/ {
2017-02-24 05:11:45 +00:00
var m = _ _hexlify ( this , this . l , hexstr . length >> 1 ) ;
2017-03-30 21:34:37 +00:00
if ( m !== hexstr ) throw new Error ( fld + 'Expected ' + hexstr + ' saw ' + m ) ;
2017-02-24 05:11:45 +00:00
this . l += hexstr . length >> 1 ;
}
2017-07-28 17:53:08 +00:00
function prep _blob ( blob /*:CFBlob*/ , pos /*:number*/ ) /*:void*/ {
2017-02-24 05:11:45 +00:00
blob . l = pos ;
2017-07-28 17:53:08 +00:00
blob . read _shift = /*::(*/ ReadShift /*:: :any)*/ ;
2017-02-24 05:11:45 +00:00
blob . chk = CheckField ;
}
/ * : :
2017-07-28 17:53:08 +00:00
declare var DO _NOT _EXPORT _CFB : ? boolean ;
type SectorEntry = {
name ? : string ;
nodes ? : Array < number > ;
data : RawBytes ;
} ;
2017-02-24 05:11:45 +00:00
type SectorList = {
2017-07-28 17:53:08 +00:00
[ k : string | number ] : SectorEntry ;
2017-02-24 05:11:45 +00:00
name : ? string ;
2017-07-28 17:53:08 +00:00
fat _addrs : Array < number > ;
2017-02-24 05:11:45 +00:00
ssz : number ;
}
2017-07-28 17:53:08 +00:00
type CFBFiles = { [ n : string ] : CFBEntry } ;
2017-02-24 05:11:45 +00:00
* /
/* [MS-CFB] v20130118 */
var CFB = ( function _CFB ( ) {
2017-07-28 17:53:08 +00:00
var exports /*:CFBModule*/ = /*::(*/ { } /*:: :any)*/ ;
exports . version = '0.12.0' ;
function parse ( file /*:RawBytes*/ , options /*:CFBReadOpts*/ ) /*:CFBContainer*/ {
2017-02-24 05:11:45 +00:00
var mver = 3 ; // major version
var ssz = 512 ; // sector size
var nmfs = 0 ; // number of mini FAT sectors
var ndfs = 0 ; // number of DIFAT sectors
var dir _start = 0 ; // first directory sector location
var minifat _start = 0 ; // first mini FAT sector location
var difat _start = 0 ; // first mini FAT sector location
2017-07-28 17:53:08 +00:00
var fat _addrs /*:Array<number>*/ = [ ] ; // locations of FAT sectors
2017-02-24 05:11:45 +00:00
/* [MS-CFB] 2.2 Compound File Header */
2017-07-28 17:53:08 +00:00
var blob /*:CFBlob*/ = /*::(*/ file . slice ( 0 , 512 ) /*:: :any)*/ ;
2017-02-24 05:11:45 +00:00
prep _blob ( blob , 0 ) ;
/* major version */
var mv = check _get _mver ( blob ) ;
mver = mv [ 0 ] ;
switch ( mver ) {
case 3 : ssz = 512 ; break ; case 4 : ssz = 4096 ; break ;
2017-03-30 21:34:37 +00:00
default : throw new Error ( "Major Version: Expected 3 or 4 saw " + mver ) ;
2017-02-24 05:11:45 +00:00
}
/* reprocess header */
2017-07-28 17:53:08 +00:00
if ( ssz !== 512 ) { blob = /*::(*/ file . slice ( 0 , ssz ) /*:: :any)*/ ; prep _blob ( blob , 28 /* blob.l */ ) ; }
2017-02-24 05:11:45 +00:00
/* Save header for final object */
2017-07-28 17:53:08 +00:00
var header /*:RawBytes*/ = file . slice ( 0 , ssz ) ;
2017-02-24 05:11:45 +00:00
check _shifts ( blob , mver ) ;
// Number of Directory Sectors
2017-07-28 17:53:08 +00:00
var nds /*:number*/ = blob . read _shift ( 4 , 'i' ) ;
2017-03-30 21:34:37 +00:00
if ( mver === 3 && nds !== 0 ) throw new Error ( '# Directory Sectors: Expected 0 saw ' + nds ) ;
2017-02-24 05:11:45 +00:00
// Number of FAT Sectors
//var nfs = blob.read_shift(4, 'i');
blob . l += 4 ;
// First Directory Sector Location
dir _start = blob . read _shift ( 4 , 'i' ) ;
// Transaction Signature
blob . l += 4 ;
// Mini Stream Cutoff Size
blob . chk ( '00100000' , 'Mini Stream Cutoff Size: ' ) ;
// First Mini FAT Sector Location
minifat _start = blob . read _shift ( 4 , 'i' ) ;
// Number of Mini FAT Sectors
nmfs = blob . read _shift ( 4 , 'i' ) ;
// First DIFAT sector location
difat _start = blob . read _shift ( 4 , 'i' ) ;
// Number of DIFAT Sectors
ndfs = blob . read _shift ( 4 , 'i' ) ;
// Grab FAT Sector Locations
2017-07-28 17:53:08 +00:00
for ( var q = - 1 , j = 0 ; j < 109 ; ++ j ) { /* 109 = (512 - blob.l)>>>2; */
2017-02-24 05:11:45 +00:00
q = blob . read _shift ( 4 , 'i' ) ;
if ( q < 0 ) break ;
fat _addrs [ j ] = q ;
}
/** Break the file up into sectors */
2017-07-28 17:53:08 +00:00
var sectors /*:Array<RawBytes>*/ = sectorify ( file , ssz ) ;
2017-02-24 05:11:45 +00:00
sleuth _fat ( difat _start , ndfs , sectors , ssz , fat _addrs ) ;
/** Chains */
var sector _list /*:SectorList*/ = make _sector _list ( sectors , dir _start , fat _addrs , ssz ) ;
sector _list [ dir _start ] . name = "!Directory" ;
if ( nmfs > 0 && minifat _start !== ENDOFCHAIN ) sector _list [ minifat _start ] . name = "!MiniFAT" ;
sector _list [ fat _addrs [ 0 ] ] . name = "!FAT" ;
sector _list . fat _addrs = fat _addrs ;
sector _list . ssz = ssz ;
/* [MS-CFB] 2.6.1 Compound File Directory Entry */
2017-07-28 17:53:08 +00:00
var files /*:CFBFiles*/ = { } , Paths /*:Array<string>*/ = [ ] , FileIndex /*:CFBFileIndex*/ = [ ] , FullPaths /*:Array<string>*/ = [ ] , FullPathDir = { } ;
2017-02-24 05:11:45 +00:00
read _directory ( dir _start , sector _list , sectors , Paths , nmfs , files , FileIndex ) ;
build _full _paths ( FileIndex , FullPathDir , FullPaths , Paths ) ;
2017-07-28 17:53:08 +00:00
var root _name /*:string*/ = Paths . shift ( ) ;
2017-02-24 05:11:45 +00:00
/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
var find _path = make _find _path ( FullPaths , Paths , FileIndex , files , root _name ) ;
return {
raw : { header : header , sectors : sectors } ,
FileIndex : FileIndex ,
FullPaths : FullPaths ,
FullPathDir : FullPathDir ,
find : find _path
} ;
} // parse
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
2017-07-28 17:53:08 +00:00
function check _get _mver ( blob /*:CFBlob*/ ) /*:[number, number]*/ {
2017-02-24 05:11:45 +00:00
// header signature 8
blob . chk ( HEADER _SIGNATURE , 'Header Signature: ' ) ;
// clsid 16
blob . chk ( HEADER _CLSID , 'CLSID: ' ) ;
// minor version 2
2017-07-28 17:53:08 +00:00
var mver /*:number*/ = blob . read _shift ( 2 , 'u' ) ;
2017-02-24 05:11:45 +00:00
return [ blob . read _shift ( 2 , 'u' ) , mver ] ;
}
2017-07-28 17:53:08 +00:00
function check _shifts ( blob /*:CFBlob*/ , mver /*:number*/ ) /*:void*/ {
2017-02-24 05:11:45 +00:00
var shift = 0x09 ;
// Byte Order
2017-03-30 21:34:37 +00:00
//blob.chk('feff', 'Byte Order: '); // note: some writers put 0xffff
blob . l += 2 ;
2017-02-24 05:11:45 +00:00
// Sector Shift
switch ( ( shift = blob . read _shift ( 2 ) ) ) {
2017-03-30 21:34:37 +00:00
case 0x09 : if ( mver != 3 ) throw new Error ( 'Sector Shift: Expected 9 saw ' + shift ) ; break ;
case 0x0c : if ( mver != 4 ) throw new Error ( 'Sector Shift: Expected 12 saw ' + shift ) ; break ;
default : throw new Error ( 'Sector Shift: Expected 9 or 12 saw ' + shift ) ;
2017-02-24 05:11:45 +00:00
}
// Mini Sector Shift
blob . chk ( '0600' , 'Mini Sector Shift: ' ) ;
// Reserved
blob . chk ( '000000000000' , 'Reserved: ' ) ;
}
/** Break the file up into sectors */
2017-07-28 17:53:08 +00:00
function sectorify ( file /*:RawBytes*/ , ssz /*:number*/ ) /*:Array<RawBytes>*/ {
2017-02-24 05:11:45 +00:00
var nsectors = Math . ceil ( file . length / ssz ) - 1 ;
2017-07-28 17:53:08 +00:00
var sectors /*:Array<RawBytes>*/ = [ ] ;
2017-02-24 05:11:45 +00:00
for ( var i = 1 ; i < nsectors ; ++ i ) sectors [ i - 1 ] = file . slice ( i * ssz , ( i + 1 ) * ssz ) ;
sectors [ nsectors - 1 ] = file . slice ( nsectors * ssz ) ;
return sectors ;
}
/* [MS-CFB] 2.6.4 Red-Black Tree */
2017-07-28 17:53:08 +00:00
function build _full _paths ( FI /*:CFBFileIndex*/ , FPD /*:CFBFullPathDir*/ , FP /*:Array<string>*/ , Paths /*:Array<string>*/ ) /*:void*/ {
2017-02-24 05:11:45 +00:00
var i = 0 , L = 0 , R = 0 , C = 0 , j = 0 , pl = Paths . length ;
2017-07-28 17:53:08 +00:00
var dad /*:Array<number>*/ = [ ] , q /*:Array<number>*/ = [ ] ;
2017-02-24 05:11:45 +00:00
for ( ; i < pl ; ++ i ) { dad [ i ] = q [ i ] = i ; FP [ i ] = Paths [ i ] ; }
for ( ; j < q . length ; ++ j ) {
i = q [ j ] ;
L = FI [ i ] . L ; R = FI [ i ] . R ; C = FI [ i ] . C ;
if ( dad [ i ] === i ) {
if ( L !== - 1 /*NOSTREAM*/ && dad [ L ] !== L ) dad [ i ] = dad [ L ] ;
if ( R !== - 1 && dad [ R ] !== R ) dad [ i ] = dad [ R ] ;
}
if ( C !== - 1 /*NOSTREAM*/ ) dad [ C ] = i ;
if ( L !== - 1 ) { dad [ L ] = dad [ i ] ; q . push ( L ) ; }
if ( R !== - 1 ) { dad [ R ] = dad [ i ] ; q . push ( R ) ; }
}
for ( i = 1 ; i !== pl ; ++ i ) if ( dad [ i ] === i ) {
if ( R !== - 1 /*NOSTREAM*/ && dad [ R ] !== R ) dad [ i ] = dad [ R ] ;
else if ( L !== - 1 && dad [ L ] !== L ) dad [ i ] = dad [ L ] ;
}
for ( i = 1 ; i < pl ; ++ i ) {
if ( FI [ i ] . type === 0 /* unknown */ ) continue ;
j = dad [ i ] ;
if ( j === 0 ) FP [ i ] = FP [ 0 ] + "/" + FP [ i ] ;
else while ( j !== 0 ) {
FP [ i ] = FP [ j ] + "/" + FP [ i ] ;
j = dad [ j ] ;
}
dad [ i ] = 0 ;
}
FP [ 0 ] += "/" ;
for ( i = 1 ; i < pl ; ++ i ) {
if ( FI [ i ] . type !== 2 /* stream */ ) FP [ i ] += "/" ;
FPD [ FP [ i ] ] = FI [ i ] ;
}
}
/* [MS-CFB] 2.6.4 */
2017-07-28 17:53:08 +00:00
function make _find _path ( FullPaths /*:Array<string>*/ , Paths /*:Array<string>*/ , FileIndex /*:CFBFileIndex*/ , files /*:CFBFiles*/ , root _name /*:string*/ ) /*:CFBFindPath*/ {
var UCFullPaths /*:Array<string>*/ = [ ] ;
var UCPaths /*:Array<string>*/ = [ ] , i = 0 ;
2017-02-24 05:11:45 +00:00
for ( i = 0 ; i < FullPaths . length ; ++ i ) UCFullPaths [ i ] = FullPaths [ i ] . toUpperCase ( ) . replace ( chr0 , '' ) . replace ( chr1 , '!' ) ;
for ( i = 0 ; i < Paths . length ; ++ i ) UCPaths [ i ] = Paths [ i ] . toUpperCase ( ) . replace ( chr0 , '' ) . replace ( chr1 , '!' ) ;
2017-07-28 17:53:08 +00:00
return function find _path ( path /*:string*/ ) /*:?CFBEntry*/ {
var k /*:boolean*/ = false ;
2017-02-24 05:11:45 +00:00
if ( path . charCodeAt ( 0 ) === 47 /* "/" */ ) { k = true ; path = root _name + path ; }
else k = path . indexOf ( "/" ) !== - 1 ;
2017-07-28 17:53:08 +00:00
var UCPath /*:string*/ = path . toUpperCase ( ) . replace ( chr0 , '' ) . replace ( chr1 , '!' ) ;
var w /*:number*/ = k === true ? UCFullPaths . indexOf ( UCPath ) : UCPaths . indexOf ( UCPath ) ;
2017-02-24 05:11:45 +00:00
if ( w === - 1 ) return null ;
return k === true ? FileIndex [ w ] : files [ Paths [ w ] ] ;
} ;
}
/ * * C h a s e d o w n t h e r e s t o f t h e D I F A T c h a i n t o b u i l d a c o m p r e h e n s i v e l i s t
DIFAT chains by storing the next sector number as the last 32 bytes * /
2017-07-28 17:53:08 +00:00
function sleuth _fat ( idx /*:number*/ , cnt /*:number*/ , sectors /*:Array<RawBytes>*/ , ssz /*:number*/ , fat _addrs ) /*:void*/ {
var q /*:number*/ = ENDOFCHAIN ;
2017-02-24 05:11:45 +00:00
if ( idx === ENDOFCHAIN ) {
2017-03-30 21:34:37 +00:00
if ( cnt !== 0 ) throw new Error ( "DIFAT chain shorter than expected" ) ;
2017-02-24 05:11:45 +00:00
} else if ( idx !== - 1 /*FREESECT*/ ) {
var sector = sectors [ idx ] , m = ( ssz >>> 2 ) - 1 ;
if ( ! sector ) return ;
for ( var i = 0 ; i < m ; ++ i ) {
if ( ( q = _ _readInt32LE ( sector , i * 4 ) ) === ENDOFCHAIN ) break ;
fat _addrs . push ( q ) ;
}
sleuth _fat ( _ _readInt32LE ( sector , ssz - 4 ) , cnt - 1 , sectors , ssz , fat _addrs ) ;
}
}
/** Follow the linked list of sectors for a given starting point */
2017-07-28 17:53:08 +00:00
function get _sector _list ( sectors /*:Array<RawBytes>*/ , start /*:number*/ , fat _addrs /*:Array<number>*/ , ssz /*:number*/ , chkd /*:?Array<boolean>*/ ) /*:SectorEntry*/ {
2017-02-24 05:11:45 +00:00
var sl = sectors . length ;
2017-07-28 17:53:08 +00:00
var buf /*:Array<number>*/ = [ ] , buf _chain /*:Array<any>*/ = [ ] ;
if ( ! chkd ) chkd = [ ] ;
var modulus = ssz - 1 , j = 0 , jj = 0 ;
2017-02-24 05:11:45 +00:00
for ( j = start ; j >= 0 ; ) {
chkd [ j ] = true ;
buf [ buf . length ] = j ;
buf _chain . push ( sectors [ j ] ) ;
var addr = fat _addrs [ Math . floor ( j * 4 / ssz ) ] ;
jj = ( ( j * 4 ) & modulus ) ;
2017-03-30 21:34:37 +00:00
if ( ssz < 4 + jj ) throw new Error ( "FAT boundary crossed: " + j + " 4 " + ssz ) ;
2017-02-24 05:11:45 +00:00
if ( ! sectors [ addr ] ) break ;
j = _ _readInt32LE ( sectors [ addr ] , jj ) ;
}
return { nodes : buf , data : _ _toBuffer ( [ buf _chain ] ) } ;
}
/** Chase down the sector linked lists */
2017-07-28 17:53:08 +00:00
function make _sector _list ( sectors /*:Array<RawBytes>*/ , dir _start /*:number*/ , fat _addrs /*:Array<number>*/ , ssz /*:number*/ ) /*:SectorList*/ {
var sl = sectors . length , sector _list /*:SectorList*/ = ( [ ] /*:any*/ ) ;
var chkd /*:Array<boolean>*/ = [ ] , buf /*:Array<number>*/ = [ ] , buf _chain /*:Array<RawBytes>*/ = [ ] ;
var modulus = ssz - 1 , i = 0 , j = 0 , k = 0 , jj = 0 ;
2017-02-24 05:11:45 +00:00
for ( i = 0 ; i < sl ; ++ i ) {
2017-07-28 17:53:08 +00:00
buf = ( [ ] /*:Array<number>*/ ) ;
2017-02-24 05:11:45 +00:00
k = ( i + dir _start ) ; if ( k >= sl ) k -= sl ;
2017-07-28 17:53:08 +00:00
if ( chkd [ k ] ) continue ;
2017-02-24 05:11:45 +00:00
buf _chain = [ ] ;
for ( j = k ; j >= 0 ; ) {
chkd [ j ] = true ;
buf [ buf . length ] = j ;
buf _chain . push ( sectors [ j ] ) ;
2017-07-28 17:53:08 +00:00
var addr /*:number*/ = fat _addrs [ Math . floor ( j * 4 / ssz ) ] ;
2017-02-24 05:11:45 +00:00
jj = ( ( j * 4 ) & modulus ) ;
2017-03-30 21:34:37 +00:00
if ( ssz < 4 + jj ) throw new Error ( "FAT boundary crossed: " + j + " 4 " + ssz ) ;
2017-02-24 05:11:45 +00:00
if ( ! sectors [ addr ] ) break ;
j = _ _readInt32LE ( sectors [ addr ] , jj ) ;
}
2017-07-28 17:53:08 +00:00
sector _list [ k ] = ( { nodes : buf , data : _ _toBuffer ( [ buf _chain ] ) } /*:SectorEntry*/ ) ;
2017-02-24 05:11:45 +00:00
}
return sector _list ;
}
/* [MS-CFB] 2.6.1 Compound File Directory Entry */
2017-07-28 17:53:08 +00:00
function read _directory ( dir _start /*:number*/ , sector _list /*:SectorList*/ , sectors /*:Array<RawBytes>*/ , Paths /*:Array<string>*/ , nmfs , files , FileIndex ) {
2017-02-24 05:11:45 +00:00
var minifat _store = 0 , pl = ( Paths . length ? 2 : 0 ) ;
var sector = sector _list [ dir _start ] . data ;
2017-07-28 17:53:08 +00:00
var i = 0 , namelen = 0 , name ;
2017-02-24 05:11:45 +00:00
for ( ; i < sector . length ; i += 128 ) {
2017-07-28 17:53:08 +00:00
var blob /*:CFBlob*/ = /*::(*/ sector . slice ( i , i + 128 ) /*:: :any)*/ ;
2017-02-24 05:11:45 +00:00
prep _blob ( blob , 64 ) ;
namelen = blob . read _shift ( 2 ) ;
if ( namelen === 0 ) continue ;
name = _ _utf16le ( blob , 0 , namelen - pl ) ;
Paths . push ( name ) ;
2017-07-28 17:53:08 +00:00
var o /*:CFBEntry*/ = ( {
2017-02-24 05:11:45 +00:00
name : name ,
type : blob . read _shift ( 1 ) ,
color : blob . read _shift ( 1 ) ,
L : blob . read _shift ( 4 , 'i' ) ,
R : blob . read _shift ( 4 , 'i' ) ,
C : blob . read _shift ( 4 , 'i' ) ,
clsid : blob . read _shift ( 16 ) ,
2017-07-28 17:53:08 +00:00
state : blob . read _shift ( 4 , 'i' ) ,
start : 0 ,
size : 0
} ) ;
var ctime /*:number*/ = blob . read _shift ( 2 ) + blob . read _shift ( 2 ) + blob . read _shift ( 2 ) + blob . read _shift ( 2 ) ;
if ( ctime !== 0 ) o . ct = read _date ( blob , blob . l - 8 ) ;
var mtime /*:number*/ = blob . read _shift ( 2 ) + blob . read _shift ( 2 ) + blob . read _shift ( 2 ) + blob . read _shift ( 2 ) ;
if ( mtime !== 0 ) o . mt = read _date ( blob , blob . l - 8 ) ;
2017-02-24 05:11:45 +00:00
o . start = blob . read _shift ( 4 , 'i' ) ;
o . size = blob . read _shift ( 4 , 'i' ) ;
if ( o . type === 5 ) { /* root */
minifat _store = o . start ;
if ( nmfs > 0 && minifat _store !== ENDOFCHAIN ) sector _list [ minifat _store ] . name = "!StreamData" ;
/*minifat_size = o.size;*/
} else if ( o . size >= 4096 /* MSCSZ */ ) {
o . storage = 'fat' ;
if ( sector _list [ o . start ] === undefined ) sector _list [ o . start ] = get _sector _list ( sectors , o . start , sector _list . fat _addrs , sector _list . ssz ) ;
sector _list [ o . start ] . name = o . name ;
2017-07-28 17:53:08 +00:00
o . content = ( sector _list [ o . start ] . data . slice ( 0 , o . size ) /*:any*/ ) ;
2017-02-24 05:11:45 +00:00
prep _blob ( o . content , 0 ) ;
} else {
o . storage = 'minifat' ;
if ( minifat _store !== ENDOFCHAIN && o . start !== ENDOFCHAIN ) {
2017-07-28 17:53:08 +00:00
o . content = ( sector _list [ minifat _store ] . data . slice ( o . start * MSSZ , o . start * MSSZ + o . size ) /*:any*/ ) ;
2017-02-24 05:11:45 +00:00
prep _blob ( o . content , 0 ) ;
}
}
files [ name ] = o ;
FileIndex . push ( o ) ;
}
}
2017-07-28 17:53:08 +00:00
function read _date ( blob /*:RawBytes|CFBlob*/ , offset /*:number*/ ) /*:Date*/ {
2017-02-24 05:11:45 +00:00
return new Date ( ( ( ( _ _readUInt32LE ( blob , offset + 4 ) / 1e7 ) * Math . pow ( 2 , 32 ) + _ _readUInt32LE ( blob , offset ) / 1e7 ) - 11644473600 ) * 1000 ) ;
}
2017-07-28 17:53:08 +00:00
var fs /*:: = require('fs'); */ ;
function readFileSync ( filename /*:string*/ , options /*:CFBReadOpts*/ ) {
if ( fs == null ) fs = require ( 'fs' ) ;
2017-02-24 05:11:45 +00:00
return parse ( fs . readFileSync ( filename ) , options ) ;
}
2017-07-28 17:53:08 +00:00
function readSync ( blob /*:RawBytes|string*/ , options /*:CFBReadOpts*/ ) {
switch ( options && options . type || "base64" ) {
case "file" : /*:: if(typeof blob !== 'string') throw "Must pass a filename when type='file'"; */ return readFileSync ( blob , options ) ;
case "base64" : /*:: if(typeof blob !== 'string') throw "Must pass a base64-encoded binary string when type='file'"; */ return parse ( s2a ( Base64 . decode ( blob ) ) , options ) ;
case "binary" : /*:: if(typeof blob !== 'string') throw "Must pass a binary string when type='file'"; */ return parse ( s2a ( blob ) , options ) ;
2017-02-24 05:11:45 +00:00
}
2017-07-28 17:53:08 +00:00
return parse ( /*::typeof blob == 'string' ? new Buffer(blob, 'utf-8') : */ blob , options ) ;
2017-02-24 05:11:45 +00:00
}
/** CFB Constants */
var MSSZ = 64 ; /* Mini Sector Size = 1<<6 */
//var MSCSZ = 4096; /* Mini Stream Cutoff Size */
/* 2.1 Compound File Sector Numbers and Types */
var ENDOFCHAIN = - 2 ;
/* 2.2 Compound File Header */
var HEADER _SIGNATURE = 'd0cf11e0a1b11ae1' ;
var HEADER _CLSID = '00000000000000000000000000000000' ;
var consts = {
/* 2.1 Compund File Sector Numbers and Types */
MAXREGSECT : - 6 ,
DIFSECT : - 4 ,
FATSECT : - 3 ,
ENDOFCHAIN : ENDOFCHAIN ,
FREESECT : - 1 ,
/* 2.2 Compound File Header */
HEADER _SIGNATURE : HEADER _SIGNATURE ,
HEADER _MINOR _VERSION : '3e00' ,
MAXREGSID : - 6 ,
NOSTREAM : - 1 ,
HEADER _CLSID : HEADER _CLSID ,
/* 2.6.1 Compound File Directory Entry */
EntryTypes : [ 'unknown' , 'storage' , 'stream' , 'lockbytes' , 'property' , 'root' ]
} ;
exports . read = readSync ;
exports . parse = parse ;
exports . utils = {
ReadShift : ReadShift ,
CheckField : CheckField ,
prep _blob : prep _blob ,
bconcat : bconcat ,
consts : consts
} ;
return exports ;
} ) ( ) ;
if ( typeof require !== 'undefined' && typeof module !== 'undefined' && typeof DO _NOT _EXPORT _CFB === 'undefined' ) { module . exports = CFB ; }