package jxl.read.biff; import common.Logger; import java.util.ArrayList; import java.util.Iterator; import jxl.WorkbookSettings; import jxl.biff.BaseCompoundFile; import jxl.biff.IntegerHelper; public final class CompoundFile extends BaseCompoundFile { private static Logger logger = Logger.getLogger(CompoundFile.class); private byte[] data; private int numBigBlockDepotBlocks; private int sbdStartBlock; private int rootStartBlock; private int extensionBlock; private int numExtensionBlocks; private byte[] rootEntry; private int[] bigBlockChain; private int[] smallBlockChain; private int[] bigBlockDepotBlocks; private ArrayList propertySets; private WorkbookSettings settings; private BaseCompoundFile.PropertyStorage rootEntryPropertyStorage; public CompoundFile(byte[] d, WorkbookSettings ws) throws BiffException { this.data = d; this.settings = ws; for (int i = 0; i < IDENTIFIER.length; i++) { if (this.data[i] != IDENTIFIER[i]) throw new BiffException(BiffException.unrecognizedOLEFile); } this.propertySets = new ArrayList(); this.numBigBlockDepotBlocks = IntegerHelper.getInt(this.data[44], this.data[45], this.data[46], this.data[47]); this.sbdStartBlock = IntegerHelper.getInt(this.data[60], this.data[61], this.data[62], this.data[63]); this.rootStartBlock = IntegerHelper.getInt(this.data[48], this.data[49], this.data[50], this.data[51]); this.extensionBlock = IntegerHelper.getInt(this.data[68], this.data[69], this.data[70], this.data[71]); this.numExtensionBlocks = IntegerHelper.getInt(this.data[72], this.data[73], this.data[74], this.data[75]); this.bigBlockDepotBlocks = new int[this.numBigBlockDepotBlocks]; int pos = 76; int bbdBlocks = this.numBigBlockDepotBlocks; if (this.numExtensionBlocks != 0) bbdBlocks = 109; for (int k = 0; k < bbdBlocks; k++) { this.bigBlockDepotBlocks[k] = IntegerHelper.getInt(d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); pos += 4; } for (int j = 0; j < this.numExtensionBlocks; j++) { pos = (this.extensionBlock + 1) * 512; int blocksToRead = Math.min(this.numBigBlockDepotBlocks - bbdBlocks, 127); for (int m = bbdBlocks; m < bbdBlocks + blocksToRead; m++) { this.bigBlockDepotBlocks[m] = IntegerHelper.getInt(d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); pos += 4; } bbdBlocks += blocksToRead; if (bbdBlocks < this.numBigBlockDepotBlocks) this.extensionBlock = IntegerHelper.getInt(d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); } readBigBlockDepot(); readSmallBlockDepot(); this.rootEntry = readData(this.rootStartBlock); readPropertySets(); } private void readBigBlockDepot() { int pos = 0; int index = 0; this.bigBlockChain = new int[this.numBigBlockDepotBlocks * 512 / 4]; for (int i = 0; i < this.numBigBlockDepotBlocks; i++) { pos = (this.bigBlockDepotBlocks[i] + 1) * 512; for (int j = 0; j < 128; j++) { this.bigBlockChain[index] = IntegerHelper.getInt(this.data[pos], this.data[pos + 1], this.data[pos + 2], this.data[pos + 3]); pos += 4; index++; } } } private void readSmallBlockDepot() { int pos = 0; int index = 0; int sbdBlock = this.sbdStartBlock; this.smallBlockChain = new int[0]; if (sbdBlock == -1) { logger.warn("invalid small block depot number"); return; } while (sbdBlock != -2) { int[] oldChain = this.smallBlockChain; this.smallBlockChain = new int[this.smallBlockChain.length + 128]; System.arraycopy(oldChain, 0, this.smallBlockChain, 0, oldChain.length); pos = (sbdBlock + 1) * 512; for (int j = 0; j < 128; j++) { this.smallBlockChain[index] = IntegerHelper.getInt(this.data[pos], this.data[pos + 1], this.data[pos + 2], this.data[pos + 3]); pos += 4; index++; } sbdBlock = this.bigBlockChain[sbdBlock]; } } private void readPropertySets() { int offset = 0; byte[] d = null; while (offset < this.rootEntry.length) { d = new byte[128]; System.arraycopy(this.rootEntry, offset, d, 0, d.length); BaseCompoundFile.PropertyStorage ps = new BaseCompoundFile.PropertyStorage(this, d); if (ps.name == null || ps.name.length() == 0) if (ps.type == 5) { ps.name = "Root Entry"; logger.warn("Property storage name for " + ps.type + " is empty - setting to " + "Root Entry"); } else if (ps.size != 0) { logger.warn("Property storage type " + ps.type + " is non-empty and has no associated name"); } this.propertySets.add(ps); if (ps.name.equalsIgnoreCase("Root Entry")) this.rootEntryPropertyStorage = ps; offset += 128; } if (this.rootEntryPropertyStorage == null) this.rootEntryPropertyStorage = this.propertySets.get(0); } public byte[] getStream(String streamName) throws BiffException { BaseCompoundFile.PropertyStorage ps = findPropertyStorage(streamName, this.rootEntryPropertyStorage); if (ps == null) ps = getPropertyStorage(streamName); if (ps.size >= 4096 || streamName.equalsIgnoreCase("Root Entry")) return getBigBlockStream(ps); return getSmallBlockStream(ps); } public byte[] getStream(int psIndex) throws BiffException { BaseCompoundFile.PropertyStorage ps = getPropertyStorage(psIndex); if (ps.size >= 4096 || ps.name.equalsIgnoreCase("Root Entry")) return getBigBlockStream(ps); return getSmallBlockStream(ps); } public BaseCompoundFile.PropertyStorage findPropertyStorage(String name) { return findPropertyStorage(name, this.rootEntryPropertyStorage); } private BaseCompoundFile.PropertyStorage findPropertyStorage(String name, BaseCompoundFile.PropertyStorage base) { if (base.child == -1) return null; BaseCompoundFile.PropertyStorage child = getPropertyStorage(base.child); if (child.name.equalsIgnoreCase(name)) return child; BaseCompoundFile.PropertyStorage prev = child; while (prev.previous != -1) { prev = getPropertyStorage(prev.previous); if (prev.name.equalsIgnoreCase(name)) return prev; } BaseCompoundFile.PropertyStorage next = child; while (next.next != -1) { next = getPropertyStorage(next.next); if (next.name.equalsIgnoreCase(name)) return next; } return findPropertyStorage(name, child); } private BaseCompoundFile.PropertyStorage getPropertyStorage(String name) throws BiffException { Iterator i = this.propertySets.iterator(); boolean found = false; boolean multiple = false; BaseCompoundFile.PropertyStorage ps = null; while (i.hasNext()) { BaseCompoundFile.PropertyStorage ps2 = i.next(); if (ps2.name.equalsIgnoreCase(name)) { multiple = (found == true); found = true; ps = ps2; } } if (multiple) logger.warn("found multiple copies of property set " + name); if (!found) throw new BiffException(BiffException.streamNotFound); return ps; } private BaseCompoundFile.PropertyStorage getPropertyStorage(int index) { return this.propertySets.get(index); } private byte[] getBigBlockStream(BaseCompoundFile.PropertyStorage ps) { int numBlocks = ps.size / 512; if (ps.size % 512 != 0) numBlocks++; byte[] streamData = new byte[numBlocks * 512]; int block = ps.startBlock; int count = 0; int pos = 0; while (block != -2 && count < numBlocks) { pos = (block + 1) * 512; System.arraycopy(this.data, pos, streamData, count * 512, 512); count++; block = this.bigBlockChain[block]; } if (block != -2 && count == numBlocks) logger.warn("Property storage size inconsistent with block chain."); return streamData; } private byte[] getSmallBlockStream(BaseCompoundFile.PropertyStorage ps) throws BiffException { byte[] rootdata = readData(this.rootEntryPropertyStorage.startBlock); byte[] sbdata = new byte[0]; int block = ps.startBlock; int pos = 0; while (block != -2) { byte[] olddata = sbdata; sbdata = new byte[olddata.length + 64]; System.arraycopy(olddata, 0, sbdata, 0, olddata.length); pos = block * 64; System.arraycopy(rootdata, pos, sbdata, olddata.length, 64); block = this.smallBlockChain[block]; if (block == -1) { logger.warn("Incorrect terminator for small block stream " + ps.name); block = -2; } } return sbdata; } private byte[] readData(int bl) throws BiffException { int block = bl; int pos = 0; byte[] entry = new byte[0]; while (block != -2) { byte[] oldEntry = entry; entry = new byte[oldEntry.length + 512]; System.arraycopy(oldEntry, 0, entry, 0, oldEntry.length); pos = (block + 1) * 512; System.arraycopy(this.data, pos, entry, oldEntry.length, 512); if (this.bigBlockChain[block] == block) throw new BiffException(BiffException.corruptFileFormat); block = this.bigBlockChain[block]; } return entry; } public int getNumberOfPropertySets() { return this.propertySets.size(); } public BaseCompoundFile.PropertyStorage getPropertySet(int index) { return getPropertyStorage(index); } }