001: /*
002: * $RCSfile: StructuredStorage.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:55:41 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.codecimpl.fpx;
013:
014: import java.awt.RenderingHints;
015: import java.io.ByteArrayInputStream;
016: import java.io.InputStream;
017: import java.io.IOException;
018: import java.io.RandomAccessFile;
019: import java.util.StringTokenizer;
020: import com.sun.media.jai.codec.ByteArraySeekableStream;
021: import com.sun.media.jai.codec.FileSeekableStream;
022: import com.sun.media.jai.codec.SeekableStream;
023: import com.sun.media.jai.codec.SegmentedSeekableStream;
024:
025: //
026: // NOTE -- all 'long' variables are really at most 32 bits,
027: // corresponding to Microsoft 'ULONG' variables.
028: //
029:
030: // Temporary (?) assumptions:
031: //
032: // All streams, including the ministream, are shorter than 2GB (size < 2GB)
033: //
034: // There are < 2^31 directory entries (#streams < 2^31)
035: //
036:
037: class SSDirectoryEntry {
038:
039: int index;
040: String name;
041: long size;
042: long startSector;
043: long SIDLeftSibling;
044: long SIDRightSibling;
045: long SIDChild;
046:
047: public SSDirectoryEntry(int index, String name, long size,
048: long startSector, long SIDLeftSibling,
049: long SIDRightSibling, long SIDChild) {
050: this .name = name;
051: this .index = index;
052: this .size = size;
053: this .startSector = startSector;
054: this .SIDLeftSibling = SIDLeftSibling;
055: this .SIDRightSibling = SIDRightSibling;
056: this .SIDChild = SIDChild;
057:
058: // System.out.println("Got a directory entry named " + name +
059: // " (index " + index + ")");
060: // System.out.println("Start sector = " + startSector);
061: }
062:
063: public String getName() {
064: return name;
065: }
066:
067: public long getSize() {
068: return size;
069: }
070:
071: public long getStartSector() {
072: return startSector;
073: }
074:
075: public long getSIDLeftSibling() {
076: return SIDLeftSibling;
077: }
078:
079: public long getSIDRightSibling() {
080: return SIDRightSibling;
081: }
082:
083: public long getSIDChild() {
084: return SIDChild;
085: }
086: }
087:
088: public class StructuredStorage {
089:
090: // Chain terminator
091: private static final long FAT_ENDOFCHAIN = 0xFFFFFFFEL;
092:
093: // Free sector
094: private static final long FAT_FREESECT = 0xFFFFFFFFL;
095:
096: SeekableStream file;
097:
098: // Header fields
099: private int sectorShift; // ULONG -- must be between 1 and 31
100: private int miniSectorShift;
101: private long csectFat;
102: private long sectDirStart;
103: private long miniSectorCutoff;
104: private long sectMiniFatStart;
105: private long csectMiniFat;
106: private long sectDifStart;
107: private long csectDif;
108: private long[] sectFat;
109:
110: // FAT, MiniFAT, and ministream in unrolled format
111: // private long[] FAT; // ULONG -- only 2G entries max
112: private long[] MINIFAT; // ULONG -- only 2G entries max
113: private SSDirectoryEntry[] DIR;
114:
115: private SeekableStream miniStream;
116: private SeekableStream FATStream;
117:
118: // The index of the current directory
119: long cwdIndex = -1L;
120:
121: public StructuredStorage(SeekableStream file) throws IOException {
122: this .file = file;
123:
124: // Read fields from the header
125: getHeader();
126:
127: // Read the FAT
128: getFat();
129:
130: // Read the MiniFAT
131: getMiniFat();
132:
133: // Read the directory
134: getDirectory();
135:
136: // Read the MiniStream
137: getMiniStream();
138: }
139:
140: private void getHeader() throws IOException {
141: file.seek(0x1e);
142: this .sectorShift = file.readUnsignedShortLE();
143: // System.out.println("sectorShift = " + sectorShift);
144:
145: file.seek(0x20);
146: this .miniSectorShift = file.readUnsignedShortLE();
147: // System.out.println("miniSectorShift = " + miniSectorShift);
148:
149: file.seek(0x2c);
150: this .csectFat = file.readUnsignedIntLE();
151: // System.out.println("csectFat = " + csectFat);
152:
153: file.seek(0x30);
154: this .sectDirStart = file.readUnsignedIntLE();
155: // System.out.println("sectDirStart = " + sectDirStart);
156:
157: file.seek(0x38);
158: this .miniSectorCutoff = file.readUnsignedIntLE();
159: // System.out.println("miniSectorCutoff = " + miniSectorCutoff);
160:
161: file.seek(0x3c);
162: this .sectMiniFatStart = file.readUnsignedIntLE();
163: // System.out.println("sectMiniFatStart = " + sectMiniFatStart);
164:
165: file.seek(0x40);
166: this .csectMiniFat = file.readUnsignedIntLE();
167: // System.out.println("csectMiniFat = " + csectMiniFat);
168:
169: file.seek(0x44);
170: this .sectDifStart = file.readUnsignedIntLE();
171: // System.out.println("sectDifStart = " + sectDifStart);
172:
173: file.seek(0x48);
174: this .csectDif = file.readUnsignedIntLE();
175: // System.out.println("csectDif = " + csectDif);
176:
177: this .sectFat = new long[109];
178: file.seek(0x4c);
179: for (int i = 0; i < 109; i++) {
180: this .sectFat[i] = file.readUnsignedIntLE();
181: }
182: }
183:
184: private void getFat() throws IOException {
185: int size = getSectorSize();
186: int sectsPerFat = size / 4;
187: int fatsPerDif = size / 4 - 1;
188: // int index = 0;
189:
190: // this.FAT =
191: // new long[(int)((csectFat + csectDif*fatsPerDif)*sectsPerFat)];
192:
193: /*
194: System.out.println("FAT has " +
195: ((int)((csectFat + csectDif*fatsPerDif)*sectsPerFat)) + " entries.");
196:
197: System.out.println("csectFat = " + csectFat);
198: System.out.println("csectDif = " + csectDif);
199: System.out.println("fatsPerDif = " + fatsPerDif);
200: System.out.println("sectsPerFat = " + sectsPerFat);
201: */
202:
203: int numFATSectors = (int) (csectFat + csectDif * fatsPerDif);
204: long[] FATSectors = new long[numFATSectors];
205: int count = 0;
206:
207: for (int i = 0; i < 109; i++) {
208: long sector = sectFat[i];
209: if (sector == FAT_FREESECT) {
210: break;
211: }
212:
213: FATSectors[count++] = getOffsetOfSector(sectFat[i]);
214: // readFatSector(sector, index);
215: // index += sectsPerFat;
216: }
217:
218: if (csectDif > 0) {
219: long dif = sectDifStart;
220: byte[] difBuf = new byte[size];
221:
222: for (int i = 0; i < csectDif; i++) {
223: readSector(dif, difBuf, 0);
224: for (int j = 0; j < fatsPerDif; j++) {
225: int sec = FPXUtils.getIntLE(difBuf, 4 * j);
226: FATSectors[count++] = getOffsetOfSector(sec);
227: // readFatSector(sec, index);
228: // index += sectsPerFat;
229: }
230:
231: dif = FPXUtils.getIntLE(difBuf, size - 4);
232: }
233: }
234:
235: FATStream = new SegmentedSeekableStream(file, FATSectors, size,
236: numFATSectors * size, true);
237: }
238:
239: private void getMiniFat() throws IOException {
240: int size = getSectorSize();
241: int sectsPerFat = size / 4;
242: int index = 0;
243:
244: this .MINIFAT = new long[(int) (csectMiniFat * sectsPerFat)];
245:
246: long sector = sectMiniFatStart;
247: // System.out.println("minifat start sector = " + sector);
248: byte[] buf = new byte[size];
249: while (sector != FAT_ENDOFCHAIN) {
250: // System.out.println("minifat sector = " + sector);
251: readSector(sector, buf, 0);
252: for (int j = 0; j < sectsPerFat; j++) {
253: MINIFAT[index++] = FPXUtils.getIntLE(buf, 4 * j);
254: }
255: sector = getFATSector(sector);
256: }
257: }
258:
259: private void getDirectory() throws IOException {
260: int size = getSectorSize();
261: long sector = sectDirStart;
262:
263: // Count the length of the directory in sectors
264: int numDirectorySectors = 0;
265: while (sector != FAT_ENDOFCHAIN) {
266: sector = getFATSector(sector);
267: ++numDirectorySectors;
268: }
269:
270: int directoryEntries = 4 * numDirectorySectors;
271: this .DIR = new SSDirectoryEntry[directoryEntries];
272:
273: sector = sectDirStart;
274: byte[] buf = new byte[size];
275: int index = 0;
276: while (sector != FAT_ENDOFCHAIN) {
277: readSector(sector, buf, 0);
278:
279: int offset = 0;
280: for (int i = 0; i < 4; i++) { // 4 dirents per sector
281: // We divide the length by 2 for now even though
282: // the spec says not to...
283: int length = FPXUtils.getShortLE(buf, offset + 0x40);
284: // System.out.println("\n\nDirent name length = " + length);
285:
286: /*
287: FPXUtils.dumpBuffer(buf, offset, 128, 0);
288:
289: for (int j = 0; j < 32; j++) {
290: int c = FPXUtils.getShortLE(buf, offset + 2*j);
291: System.out.println("name[" + (2*j) + "] = " + c +
292: " '" + (char)c + "'");
293: }
294: */
295:
296: String name = FPXUtils.getString(buf, offset + 0x00,
297: length);
298: long SIDLeftSibling = FPXUtils.getUnsignedIntLE(buf,
299: offset + 0x44);
300: long SIDRightSibling = FPXUtils.getUnsignedIntLE(buf,
301: offset + 0x48);
302: long SIDChild = FPXUtils.getUnsignedIntLE(buf,
303: offset + 0x4c);
304: long startSector = FPXUtils.getUnsignedIntLE(buf,
305: offset + 0x74);
306: long streamSize = FPXUtils.getUnsignedIntLE(buf,
307: offset + 0x78);
308:
309: DIR[index] = new SSDirectoryEntry(index, name,
310: streamSize, startSector, SIDLeftSibling,
311: SIDRightSibling, SIDChild);
312: ++index;
313: offset += 128;
314: }
315:
316: sector = getFATSector(sector);
317: }
318: }
319:
320: private void getMiniStream() throws IOException {
321: int length = getLength(0L);
322: int sectorSize = getSectorSize();
323: int sectors = (int) ((length + sectorSize - 1) / sectorSize);
324:
325: long[] segmentPositions = new long[sectors];
326:
327: long sector = getStartSector(0);
328: // int offset = 0;
329: for (int i = 0; i < sectors - 1; i++) {
330: segmentPositions[i] = getOffsetOfSector(sector);
331: sector = getFATSector(sector);
332: if (sector == FAT_ENDOFCHAIN)
333: break;
334: }
335: segmentPositions[sectors - 1] = getOffsetOfSector(sector);
336:
337: miniStream = new SegmentedSeekableStream(file,
338: segmentPositions, sectorSize, length, true);
339: }
340:
341: /*
342: private void readFatSector(long sector, int index) throws IOException {
343: int sectsPerFat = getSectorSize()/4;
344: long offset = getOffsetOfSector(sector);
345:
346: file.seek(offset);
347: for (int i = 0; i < sectsPerFat; i++) {
348: FAT[index] = file.readUnsignedIntLE();
349: // System.out.println("FAT[" + index + "] = " + FAT[index]);
350: index++;
351: }
352: }
353: */
354:
355: private int getSectorSize() {
356: return 1 << sectorShift;
357: }
358:
359: private long getOffsetOfSector(long sector) {
360: return sector * getSectorSize() + 512;
361: }
362:
363: private int getMiniSectorSize() {
364: return 1 << miniSectorShift;
365: }
366:
367: private long getOffsetOfMiniSector(long sector) {
368: return sector * getMiniSectorSize();
369: }
370:
371: private void readMiniSector(long sector, byte[] buf, int offset,
372: int length) throws IOException {
373: miniStream.seek(getOffsetOfMiniSector(sector));
374: miniStream.read(buf, offset, length);
375: }
376:
377: private void readMiniSector(long sector, byte[] buf, int offset)
378: throws IOException {
379: readMiniSector(sector, buf, offset, getMiniSectorSize());
380: }
381:
382: private void readSector(long sector, byte[] buf, int offset,
383: int length) throws IOException {
384: file.seek(getOffsetOfSector(sector));
385: file.read(buf, offset, length);
386: }
387:
388: private void readSector(long sector, byte[] buf, int offset)
389: throws IOException {
390: readSector(sector, buf, offset, getSectorSize());
391: }
392:
393: private SSDirectoryEntry getDirectoryEntry(long index) {
394: // Assume #streams < 2^31
395: return DIR[(int) index];
396: }
397:
398: private long getStartSector(long index) {
399: // Assume #streams < 2^31
400: return DIR[(int) index].getStartSector();
401: }
402:
403: private int getLength(long index) {
404: // Assume #streams < 2^31
405: // Assume size < 2GB
406: return (int) DIR[(int) index].getSize();
407: }
408:
409: private long getFATSector(long sector) throws IOException {
410: FATStream.seek(4 * sector);
411: return FATStream.readUnsignedIntLE();
412: // return FAT[(int)sector];
413: }
414:
415: private long getMiniFATSector(long sector) {
416: return MINIFAT[(int) sector];
417: }
418:
419: private int getCurrentIndex() {
420: return -1;
421: }
422:
423: private int getIndex(String name, int index) {
424: return -1;
425: }
426:
427: private long searchDirectory(String name, long index) {
428: if (index == FAT_FREESECT) {
429: return -1L;
430: }
431:
432: SSDirectoryEntry dirent = getDirectoryEntry(index);
433: /*
434: System.out.println("Comparing " + name + " (" + name.length() +
435: ") against " +
436: dirent.getName() + " (" +
437: (dirent.getName()).length() + ") index " +
438: index);
439: */
440:
441: if (name.equals(dirent.getName())) {
442: // System.out.println("Matched!");
443: return index;
444: } else {
445: long lindex = searchDirectory(name, dirent
446: .getSIDLeftSibling());
447: if (lindex != -1L) {
448: return lindex;
449: }
450:
451: long rindex = searchDirectory(name, dirent
452: .getSIDRightSibling());
453: if (rindex != -1L) {
454: return rindex;
455: }
456: }
457:
458: return -1L;
459: }
460:
461: // Public methods
462:
463: public void changeDirectoryToRoot() {
464: cwdIndex = getDirectoryEntry(0L).getSIDChild();
465: }
466:
467: public boolean changeDirectory(String name) {
468: long index = searchDirectory(name, cwdIndex);
469: if (index != -1L) {
470: cwdIndex = getDirectoryEntry(index).getSIDChild();
471: // System.out.println("changeDirectory: setting cwdIndex to " +
472: // cwdIndex);
473: return true;
474: } else {
475: return false;
476: }
477: }
478:
479: /*
480: public SSDirectory[] getDirectoryEntries() {
481: }
482: */
483:
484: private long getStreamIndex(String name) {
485: // Move down the directory hierarchy
486: long index = cwdIndex;
487: // System.out.println("start index = " + index);
488:
489: StringTokenizer st = new StringTokenizer(name, "/");
490: boolean firstTime = true;
491: while (st.hasMoreTokens()) {
492: String tok = st.nextToken();
493:
494: // System.out.println("Token = " + tok);
495: if (!firstTime) {
496: index = getDirectoryEntry(index).getSIDChild();
497: } else {
498: firstTime = false;
499: }
500: index = searchDirectory(tok, index);
501: // System.out.println("index = " + index);
502: }
503:
504: return index;
505: }
506:
507: public byte[] getStreamAsBytes(String name) throws IOException {
508: long index = getStreamIndex(name);
509: if (index == -1L) {
510: return null;
511: }
512:
513: // Cast index to int (streams < 2^31) and cast stream size to an
514: // int (size < 2GB)
515: int length = getLength(index);
516: byte[] buf = new byte[length];
517:
518: if (length > miniSectorCutoff) {
519: int sectorSize = getSectorSize();
520: int sectors = (int) ((length + sectorSize - 1) / sectorSize);
521:
522: long sector = getStartSector(index);
523: int offset = 0;
524: for (int i = 0; i < sectors - 1; i++) {
525: readSector(sector, buf, offset, sectorSize);
526: offset += sectorSize;
527: sector = getFATSector(sector);
528: // System.out.println("next sector = " + sector);
529: if (sector == FAT_ENDOFCHAIN)
530: break;
531: }
532:
533: readSector(sector, buf, offset, length - offset);
534: } else {
535: int sectorSize = getMiniSectorSize();
536: int sectors = (int) ((length + sectorSize - 1) / sectorSize);
537:
538: long sector = getStartSector(index);
539:
540: // Assume ministream size < 2GB
541: int offset = 0;
542: for (int i = 0; i < sectors - 1; i++) {
543: long miniSectorOffset = getOffsetOfMiniSector(sector);
544: readMiniSector(sector, buf, offset, sectorSize);
545: offset += sectorSize;
546: sector = getMiniFATSector(sector);
547: }
548: readMiniSector(sector, buf, offset, length - offset);
549: }
550:
551: return buf;
552: }
553:
554: public SeekableStream getStream(String name) throws IOException {
555: long index = getStreamIndex(name);
556: if (index == -1L) {
557: return null;
558: }
559:
560: // Cast index to int (streams < 2^31) and cast stream size to an
561: // int (size < 2GB)
562: int length = getLength(index);
563:
564: long[] segmentPositions;
565: int sectorSize, sectors;
566:
567: if (length > miniSectorCutoff) {
568: sectorSize = getSectorSize();
569: sectors = (int) ((length + sectorSize - 1) / sectorSize);
570: segmentPositions = new long[sectors];
571:
572: long sector = getStartSector(index);
573: for (int i = 0; i < sectors - 1; i++) {
574: segmentPositions[i] = getOffsetOfSector(sector);
575: sector = getFATSector(sector);
576: if (sector == FAT_ENDOFCHAIN)
577: break;
578: }
579: segmentPositions[sectors - 1] = getOffsetOfSector(sector);
580:
581: return new SegmentedSeekableStream(file, segmentPositions,
582: sectorSize, length, true);
583: } else {
584: sectorSize = getMiniSectorSize();
585: sectors = (int) ((length + sectorSize - 1) / sectorSize);
586: segmentPositions = new long[sectors];
587:
588: long sector = getStartSector(index);
589: for (int i = 0; i < sectors - 1; i++) {
590: segmentPositions[i] = getOffsetOfMiniSector(sector);
591: sector = getMiniFATSector(sector);
592: }
593: segmentPositions[sectors - 1] = getOffsetOfMiniSector(sector);
594:
595: return new SegmentedSeekableStream(miniStream,
596: segmentPositions, sectorSize, length, true);
597: }
598: }
599:
600: public static void main(String[] args) {
601: try {
602: RandomAccessFile f = new RandomAccessFile(args[0], "r");
603: SeekableStream sis = new FileSeekableStream(f);
604: StructuredStorage ss = new StructuredStorage(sis);
605:
606: ss.changeDirectoryToRoot();
607:
608: byte[] s = ss.getStreamAsBytes("SummaryInformation");
609:
610: PropertySet ps = new PropertySet(
611: new ByteArraySeekableStream(s));
612:
613: // Get the thumbnail property
614: byte[] thumb = ps.getBlob(17);
615:
616: // Emit it as a BMP file
617: System.out.print("BM");
618: int fs = (thumb.length - 8) + 14 + 40;
619: System.out.print((char) (fs & 0xff));
620: System.out.print((char) ((fs >> 8) & 0xff));
621: System.out.print((char) ((fs >> 16) & 0xff));
622: System.out.print((char) ((fs >> 24) & 0xff));
623: System.out.print((char) 0);
624: System.out.print((char) 0);
625: System.out.print((char) 0);
626: System.out.print((char) 0);
627: System.out.print('6');
628: System.out.print((char) 0);
629: System.out.print((char) 0);
630: System.out.print((char) 0);
631: for (int i = 8; i < thumb.length; i++) {
632: System.out.print((char) (thumb[i] & 0xff));
633: }
634:
635: /*
636: ss.changeDirectory("Data Object Store 000001");
637: SeekableStream imageContents =
638: ss.getStream("Image Contents");
639: */
640:
641: } catch (Exception e) {
642: e.printStackTrace();
643: }
644: }
645: }
|