Class: Reader

Inherits:
Object
  • Object
show all
Defined in:
lib/surpass/document.rb

Constant Summary collapse

DIR_ENTRY_SIZE =
128

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file) ⇒ Reader

Returns a new instance of Reader.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/surpass/document.rb', line 28

def initialize(file)
  @streams = {}
  
  file = File.open(file, 'rb') unless file.respond_to?(:read)
  doc = file.read
  @header, @data = doc[0...512], doc[512..-1]
  
  build_header
  build_msat
  build_sat
  build_directory
  build_short_sectors_data

  if @short_sectors_data.length > 0
    build_ssat
  else
    @ssat_start_sid = -2
    @total_ssat_sectors = 0
    @ssat = [-2]
  end

  @dir_entry_list[1..-1].each do |d|
    did, sz, name, t, c, did_left, did_right, did_root, dentry_start_sid, stream_size = d
    stream_data = ''
    if stream_size > 0
      if stream_size > @min_stream_size
        args = [@data, @sat, dentry_start_sid, @sect_size]
      else
        args = [@short_sectors_data, @ssat, dentry_start_sid, @short_sect_size]
      end
      stream_data = stream_data(*args)
    end
    # BAD IDEA: names may be equal. NEED use full paths...
    @streams[name] = stream_data if !name.length == 0
  end
end

Instance Attribute Details

#byte_orderObject

Returns the value of attribute byte_order.



11
12
13
# File 'lib/surpass/document.rb', line 11

def byte_order
  @byte_order
end

#dataObject

Returns the value of attribute data.



5
6
7
# File 'lib/surpass/document.rb', line 5

def data
  @data
end

#dir_entry_listObject

Returns the value of attribute dir_entry_list.



26
27
28
# File 'lib/surpass/document.rb', line 26

def dir_entry_list
  @dir_entry_list
end

#dir_start_sidObject

Returns the value of attribute dir_start_sid.



16
17
18
# File 'lib/surpass/document.rb', line 16

def dir_start_sid
  @dir_start_sid
end

#doc_magicObject

Returns the value of attribute doc_magic.



7
8
9
# File 'lib/surpass/document.rb', line 7

def doc_magic
  @doc_magic
end

#file_uidObject

Returns the value of attribute file_uid.



8
9
10
# File 'lib/surpass/document.rb', line 8

def file_uid
  @file_uid
end

#headerObject

Returns the value of attribute header.



4
5
6
# File 'lib/surpass/document.rb', line 4

def header
  @header
end

#min_stream_sizeObject

Returns the value of attribute min_stream_size.



17
18
19
# File 'lib/surpass/document.rb', line 17

def min_stream_size
  @min_stream_size
end

#msatObject

Returns the value of attribute msat.



23
24
25
# File 'lib/surpass/document.rb', line 23

def msat
  @msat
end

#msat_start_sidObject

Returns the value of attribute msat_start_sid.



20
21
22
# File 'lib/surpass/document.rb', line 20

def msat_start_sid
  @msat_start_sid
end

#rev_numObject

Returns the value of attribute rev_num.



9
10
11
# File 'lib/surpass/document.rb', line 9

def rev_num
  @rev_num
end

#satObject

Returns the value of attribute sat.



24
25
26
# File 'lib/surpass/document.rb', line 24

def sat
  @sat
end

#sect_sizeObject

Returns the value of attribute sect_size.



12
13
14
# File 'lib/surpass/document.rb', line 12

def sect_size
  @sect_size
end

#short_sect_sizeObject

Returns the value of attribute short_sect_size.



13
14
15
# File 'lib/surpass/document.rb', line 13

def short_sect_size
  @short_sect_size
end

#ssatObject

Returns the value of attribute ssat.



25
26
27
# File 'lib/surpass/document.rb', line 25

def ssat
  @ssat
end

#ssat_start_sidObject

Returns the value of attribute ssat_start_sid.



18
19
20
# File 'lib/surpass/document.rb', line 18

def ssat_start_sid
  @ssat_start_sid
end

#total_msat_sectorsObject

Returns the value of attribute total_msat_sectors.



21
22
23
# File 'lib/surpass/document.rb', line 21

def total_msat_sectors
  @total_msat_sectors
end

#total_sat_sectorsObject

Returns the value of attribute total_sat_sectors.



15
16
17
# File 'lib/surpass/document.rb', line 15

def total_sat_sectors
  @total_sat_sectors
end

#total_ssat_sectorsObject

Returns the value of attribute total_ssat_sectors.



19
20
21
# File 'lib/surpass/document.rb', line 19

def total_ssat_sectors
  @total_ssat_sectors
end

#ver_numObject

Returns the value of attribute ver_num.



10
11
12
# File 'lib/surpass/document.rb', line 10

def ver_num
  @ver_num
end

Instance Method Details

#build_directoryObject



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/surpass/document.rb', line 110

def build_directory
  dir_stream = stream_data(@data, @sat, @dir_start_sid, @sect_size)
  @dir_entry_list = []
  i = 0
  while i < dir_stream.length do
    dentry = dir_stream[i...(i+DIR_ENTRY_SIZE)]
    i += DIR_ENTRY_SIZE
    
    did = @dir_entry_list.length
    sz = dentry[64...66].unpack('C')[0]
    
    if sz > 0
      name = dentry[0...(sz-2)] # TODO unicode
    else
      name = ''
    end
    
    t = dentry[66...67].unpack('C')[0]
    c = dentry[67...68].unpack('C')[0]
    did_left = dentry[68...72].unpack('V')[0]
    did_right = dentry[72...76].unpack('V')[0]
    did_root = dentry[76...80].unpack('V')[0]
    dentry_start_sid = dentry[116...120].unpack('V')[0]
    stream_size = dentry[120...124].unpack('V')[0]
    
    @dir_entry_list << [did, sz, name, t, c, did_left, did_right, did_root, dentry_start_sid, stream_size]
  end
end

#build_headerObject



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/surpass/document.rb', line 65

def build_header
  @doc_magic = @header[0...8]
  raise 'Not an OLE file.' unless @doc_magic === "\320\317\021\340\241\261\032\341"

  @file_uid              = @header[8...24]
  @rev_num               = @header[24...26]
  @ver_num               = @header[26...28]
  @byte_order            = @header[28...30]
  @log2_sect_size       = @header[30...32].unpack('v')[0]
  @log2_short_sect_size = @header[32...34].unpack('v')[0]
  @total_sat_sectors    = @header[44...48].unpack('V')[0]
  @dir_start_sid        = @header[48...52].unpack('V')[0]
  @min_stream_size      = @header[56...60].unpack('V')[0]
  @ssat_start_sid       = @header[60...64].unpack('V')[0]
  @total_ssat_sectors   = @header[64...68].unpack('V')[0]
  @msat_start_sid       = @header[68...72].unpack('V')[0]
  @total_msat_sectors   = @header[72...76].unpack('V')[0]

  @sect_size        = 1 << @log2_sect_size
  @short_sect_size  = 1 << @log2_short_sect_size
end

#build_msatObject



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/surpass/document.rb', line 87

def build_msat
  @msat = @header[76..-1].unpack('V109')
  next_sector = @msat_start_sid
  while next_sector > 0 do
    raise "not implemented"
    start = next_sector * @sect_size
    finish = (next_sector + 1) * @sect_size
    msat_sector = @data[start...finish]
    @msat << msat_sector
    next_sector = msat_sector[-1]
  end
end

#build_satObject



100
101
102
103
# File 'lib/surpass/document.rb', line 100

def build_sat
  sat_stream = @msat.collect {|i| i >= 0 ? @data[(i*@sect_size)...((i+1)*@sect_size)] : '' }.join
  @sat = sat_stream.unpack('V*')
end

#build_short_sectors_dataObject



139
140
141
142
143
144
145
146
147
148
# File 'lib/surpass/document.rb', line 139

def build_short_sectors_data
  did, sz, name, t, c, did_left, did_right, did_root, dentry_start_sid, stream_size = @dir_entry_list[0]
  raise unless t == 0x05 # Short-Stream Container Stream (SSCS) resides in Root Storage

  if stream_size == 0
    @short_sectors_data = ''
  else
    @short_sectors_data = stream_data(@data, @sat, dentry_start_sid, @sect_size)
  end
end

#build_ssatObject



105
106
107
108
# File 'lib/surpass/document.rb', line 105

def build_ssat
  ssat_stream = stream_data(@data, @sat, @ssat_start_sid, @sect_size)
  @ssat = ssat_stream.unpack('V*')
end

#stream_data(data, sat, start_sid, sect_size) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/surpass/document.rb', line 150

def stream_data(data, sat, start_sid, sect_size)
  sid = start_sid
  chunks = [[sid, sid]]
  stream_data = ''
  while sat[sid] >= 0 do
      next_in_chain = sat[sid]
      last_chunk_start, last_chunk_finish = chunks[-1]
      if next_in_chain == last_chunk_finish + 1
          chunks[-1] = last_chunk_start, next_in_chain
      else
          chunks << [next_in_chain, next_in_chain]
      end
      sid = next_in_chain
  end
  
  chunks.each do |s, f|
    stream_data += data[s*sect_size...(f+1)*sect_size]
  end
  stream_data
end