1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.millscript.office.compound;
22
23 import org.millscript.commons.alert.alerts.IOAlert;
24 import org.millscript.office.endianness.BigEndianDecoder;
25 import org.millscript.office.endianness.EndianDecoder;
26 import org.millscript.office.endianness.LittleEndianDecoder;
27
28 import java.io.IOException;
29 import java.io.RandomAccessFile;
30
31
32 public class Header {
33
34 public static final byte[] COMPOUND_DOCUMENT_FILE_IDENT = new byte[] { -48, -49, 17, -32, -95, -79, 26, -31 };
35
36 private final int directorySID;
37
38 private final EndianDecoder endianDecoder;
39
40 private final int[] masterSectorAllocationTable;
41
42 private final int masterSectorAllocationTableSID;
43
44 private final int minimumStandardStreamSize;
45
46 private final int[] sectorAllocationTable;
47
48 private final int sectorsInMasterSectorAllocationTable;
49
50 private final int sectorsInSectorAllocationTable;
51
52 private final int sectorsInShortSectorAllocationTable;
53
54 private final int sectorSize;
55
56 private final int[] shortSectorAllocationTable;
57
58 private final int shortSectorAllocationTableSID;
59
60 private final int shortSectorSize;
61
62 public Header( final RandomAccessFile randomFile ) throws IOException {
63
64 final byte[] header = new byte[ 512 ];
65 randomFile.readFully( header );
66 for ( int i = 0; i < 8; i++ ) {
67 if ( header[ i ] != COMPOUND_DOCUMENT_FILE_IDENT[ i ] ) {
68 throw new IOAlert(
69 "This is not a compound document"
70 ).mishap();
71 }
72 }
73
74 if ( header[ 28 ] == -2 && header[ 29 ] == -1 ) {
75
76 this.endianDecoder = new LittleEndianDecoder();
77 } else if ( header[ 28 ] == -1 && header[ 29 ] == -2 ) {
78
79 this.endianDecoder = new BigEndianDecoder();
80 } else {
81
82 throw new IOAlert(
83 "Invalid byte order identifier"
84 ).culprit(
85 "first byte",
86 header[ 28 ]
87 ).culprit(
88 "second byte",
89 header[ 29 ]
90 ).mishap();
91 }
92
93 final int ssz = this.endianDecoder.decode2ByteInt( header, 30 );
94 if ( ssz < 7 ) {
95 throw new IOAlert(
96 "Sector size cannot be less than 7(i.e. 128 bytes)"
97 ).culprit(
98 "sector size",
99 ssz
100 ).mishap();
101 } else {
102 this.sectorSize = 2 << ssz - 1;
103 }
104
105 final int sssz = this.endianDecoder.decode2ByteInt( header, 32 );
106 if ( sssz > ssz ) {
107 throw new IOAlert(
108 "Short sector size cannot be larger than the normal sector size"
109 ).culprit(
110 "short sector size",
111 sssz
112 ).culprit(
113 "sector size",
114 ssz
115 ).mishap();
116 }
117 this.shortSectorSize = 2 << sssz - 1;
118
119 this.sectorsInSectorAllocationTable = this.endianDecoder.decode4ByteInt( header, 44 );
120
121 this.directorySID = this.endianDecoder.decode4ByteInt( header, 48 );
122
123 this.minimumStandardStreamSize = this.endianDecoder.decode4ByteInt( header, 56 );
124
125 this.shortSectorAllocationTableSID = this.endianDecoder.decode4ByteInt( header, 60 );
126
127 this.sectorsInShortSectorAllocationTable = this.endianDecoder.decode4ByteInt( header, 64 );
128
129 this.masterSectorAllocationTableSID = this.endianDecoder.decode4ByteInt( header, 68 );
130
131 this.sectorsInMasterSectorAllocationTable = this.endianDecoder.decode4ByteInt( header, 72 );
132
133 this.masterSectorAllocationTable = this.readMasterSectorAllocationTable( randomFile, header );
134
135 this.sectorAllocationTable = this.readSectorAllocationTable( randomFile );
136
137 this.shortSectorAllocationTable = this.readShortSectorAllocationTable( randomFile );
138 }
139
140 /**
141 * @return Returns the directorySID.
142 */
143 public int getDirectorySID() {
144 return directorySID;
145 }
146
147 /**
148 * @return Returns the endianDecoder.
149 */
150 public EndianDecoder getEndianDecoder() {
151 return endianDecoder;
152 }
153
154 /**
155 * @return Returns the masterSectorAllocationTableSID.
156 */
157 public int getMasterSectorAllocationTableSID() {
158 return masterSectorAllocationTableSID;
159 }
160
161 /**
162 * @return Returns the minimumStandardStreamSize.
163 */
164 public int getMinimumStandardStreamSize() {
165 return minimumStandardStreamSize;
166 }
167
168 public int[] getSectorChain( final int sid ) {
169
170 int index = 1;
171 int currentSid = this.sectorAllocationTable[ sid ];
172 while ( currentSid != -2 ) {
173 switch ( currentSid ) {
174 case -1:
175 throw new IOAlert(
176 "Unexpected end of chain in sector allocation table"
177 ).mishap();
178 case -3:
179 throw new IOAlert(
180 "Unexpected sector allocation table sector in chain"
181 ).mishap();
182 case -4:
183 throw new IOAlert(
184 "Unexpected master sector allocation table sector in chain"
185 ).mishap();
186 }
187 index++;
188 currentSid = this.sectorAllocationTable[ currentSid ];
189 }
190
191 final int[] chain = new int[ index ];
192
193 index = 0;
194
195 currentSid = sid;
196
197 while ( currentSid != -2 ) {
198 chain[ index ] = currentSid;
199 index++;
200
201 currentSid = this.sectorAllocationTable[ currentSid ];
202 }
203 return chain;
204 }
205
206 /**
207 * Returns the offset of the start of the sector with the given sector id.
208 *
209 * @param sid the id of the sector whose offset is required
210 * @return the offset of the specified sector within the file
211 */
212 public long getSectorOffset( final int sid ) {
213 return 512 + sid * this.sectorSize;
214 }
215
216 /**
217 * @return Returns the sectorsInMasterSectorAllocationTable.
218 */
219 public int getSectorsInMasterSectorAllocationTable() {
220 return sectorsInMasterSectorAllocationTable;
221 }
222
223 /**
224 * @return Returns the sectorsInSectorAllocationTable.
225 */
226 public int getSectorsInSectorAllocationTable() {
227 return sectorsInSectorAllocationTable;
228 }
229
230 /**
231 * @return Returns the sectorsInShortSectorAllocationTable.
232 */
233 public int getSectorsInShortSectorAllocationTable() {
234 return sectorsInShortSectorAllocationTable;
235 }
236
237 /**
238 * @return Returns the sectorSize.
239 */
240 public int getSectorSize() {
241 return sectorSize;
242 }
243
244 /**
245 * @return Returns the shortSectorAllocationTableSID.
246 */
247 public int getShortSectorAllocationTableSID() {
248 return shortSectorAllocationTableSID;
249 }
250
251 public int[] getShortSectorChain( final int sid ) {
252
253 int index = 1;
254 int currentSid = this.shortSectorAllocationTable[ sid ];
255 while ( currentSid != -2 ) {
256 switch ( currentSid ) {
257 case -1:
258 throw new IOAlert(
259 "Unexpected end of chain in short sector allocation table"
260 ).mishap();
261 case -3:
262 throw new IOAlert(
263 "Unexpected sector allocation table sector in chain"
264 ).mishap();
265 case -4:
266 throw new IOAlert(
267 "Unexpected master sector allocation table sector in chain"
268 ).mishap();
269 }
270 index++;
271 currentSid = this.shortSectorAllocationTable[ currentSid ];
272 }
273
274 final int[] chain = new int[ index ];
275
276 index = 0;
277
278 currentSid = sid;
279
280 while ( currentSid != -2 ) {
281 chain[ index ] = currentSid;
282 index++;
283
284 currentSid = this.shortSectorAllocationTable[ currentSid ];
285 }
286 return chain;
287 }
288
289 /**
290 * @return Returns the shortSectorSize.
291 */
292 public int getShortSectorSize() {
293 return shortSectorSize;
294 }
295
296 /**
297 * Checks if the specified directory entry refers to a short stream.
298 *
299 * @param entry the directory entry to test
300 * @return <code>true</code> if the specified directory entry is for a
301 * short stream and <code>false</code> otherwise
302 */
303 public boolean isShortStream( final DirectoryEntry entry ) {
304 return entry.getStreamSize() < this.getMinimumStandardStreamSize();
305 }
306
307 public int[] readMasterSectorAllocationTable( final RandomAccessFile randomFile, final byte[] header ) throws IOException {
308 final int[] mSAT = new int[ 109 + this.sectorSize * this.sectorsInMasterSectorAllocationTable ];
309 byte[] buffer = header;
310 int offset = 76;
311
312
313 for ( int i = 0; i < mSAT.length; offset += 4, i++ ) {
314 final int sid = this.endianDecoder.decode4ByteInt( buffer, offset );
315 if ( sid == -2 ) {
316
317
318 if ( i + 1 != mSAT.length ) {
319
320
321
322 final int[] retval = new int[ i ];
323 System.arraycopy( mSAT, 0, retval, 0, i );
324 return retval;
325 } else {
326 break;
327 }
328 } else if ( sid == -1 ) {
329
330
331 if ( this.masterSectorAllocationTableSID == -2 ) {
332
333
334 if ( i + 1 != mSAT.length ) {
335
336
337
338 final int[] retval = new int[ i ];
339 System.arraycopy( mSAT, 0, retval, 0, i );
340 return retval;
341 } else {
342 break;
343 }
344 } else {
345
346 throw new IOAlert(
347 "Unexpected end of master sector allocation table"
348 ).mishap();
349 }
350 } else if ( offset + 4 == buffer.length && i + 1 != mSAT.length ) {
351
352
353
354 randomFile.seek( this.getSectorOffset( sid ) );
355 buffer = new byte[ this.sectorSize ];
356 randomFile.readFully( buffer );
357 offset = 0;
358 final int sid2 = this.endianDecoder.decode4ByteInt( buffer, offset );
359 mSAT[ i ] = sid2;
360 } else {
361 mSAT[ i ] = sid;
362 }
363 }
364 return mSAT;
365 }
366
367 public int[] readSectorAllocationTable( final RandomAccessFile randomFile ) throws IOException {
368 final int[] SAT = new int[ this.sectorSize * this.sectorsInSectorAllocationTable >> 2 ];
369 int satOffset = 0;
370 for ( int i = 0; i < this.masterSectorAllocationTable.length; i++ ) {
371
372 final byte[] sector = new byte[ this.sectorSize ];
373
374 randomFile.seek(
375 this.getSectorOffset( this.masterSectorAllocationTable[ i ] )
376 );
377 randomFile.readFully( sector );
378
379 for ( int sectorOffset = 0; sectorOffset < sector.length; sectorOffset += 4 ) {
380 SAT[ satOffset++ ] = this.endianDecoder.decode4ByteInt( sector, sectorOffset );
381 }
382 }
383 return SAT;
384 }
385
386 public int[] readShortSectorAllocationTable( final RandomAccessFile randomFile ) throws IOException {
387 final int[] SSAT = new int[ this.sectorSize * this.sectorsInShortSectorAllocationTable >> 2 ];
388 int ssatOffset = 0;
389 final int[] shortSATSectorChain = this.getSectorChain( this.shortSectorAllocationTableSID );
390 for ( int i = 0; i < shortSATSectorChain.length; i++ ) {
391
392 final byte[] sector = new byte[ this.sectorSize ];
393
394 randomFile.seek(
395 this.getSectorOffset( shortSATSectorChain[ i ] )
396 );
397 randomFile.readFully( sector );
398
399 for ( int sectorOffset = 0; sectorOffset < sector.length; sectorOffset += 4 ) {
400 SSAT[ ssatOffset++ ] = this.endianDecoder.decode4ByteInt( sector, sectorOffset );
401 }
402 }
403 return SSAT;
404 }
405
406 }