1 /*- 2 * Copyright (C) 2006 Erik Larsson 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package org.catacombae.hfs.types.hfsplus; 19 20 import org.catacombae.util.Util; 21 import java.io.PrintStream; 22 import org.catacombae.csjc.PrintableStruct; 23 import org.catacombae.csjc.StructElements; 24 import org.catacombae.csjc.structelements.Dictionary; 25 import org.catacombae.csjc.structelements.IntegerFieldRepresentation; 26 27 public class BTHeaderRec implements PrintableStruct, StructElements { 28 /* 29 * struct BTHeaderRec 30 * size: 106 bytes 31 * description: 32 * 33 * BP Size Type Identifier Description 34 * ----------------------------------------------------------- 35 * 0 2 UInt16 treeDepth 36 * 2 4 UInt32 rootNode 37 * 6 4 UInt32 leafRecords 38 * 10 4 UInt32 firstLeafNode 39 * 14 4 UInt32 lastLeafNode 40 * 18 2 UInt16 nodeSize 41 * 20 2 UInt16 maxKeyLength 42 * 22 4 UInt32 totalNodes 43 * 26 4 UInt32 freeNodes 44 * 30 2 UInt16 reserved1 45 * 32 4 UInt32 clumpSize // misaligned 46 * 36 1 UInt8 btreeType 47 * 37 1 UInt8 keyCompareType 48 * 38 4 UInt32 attributes // long aligned again 49 * 42 4*16 UInt32[16] reserved3 50 */ 51 52 /** Case folding (case-insensitive). Possible return value from getKeyCompareType(). */ 53 public static final byte kHFSCaseFolding = (byte)0xCF; 54 /** Binary compare (case-sensitive). Possible return value from getKeyCompareType(). */ 55 public static final byte kHFSBinaryCompare = (byte)0xBC; 56 57 private final byte[] treeDepth = new byte[2]; 58 private final byte[] rootNode = new byte[4]; 59 private final byte[] leafRecords = new byte[4]; 60 private final byte[] firstLeafNode = new byte[4]; 61 private final byte[] lastLeafNode = new byte[4]; 62 private final byte[] nodeSize = new byte[2]; 63 private final byte[] maxKeyLength = new byte[2]; 64 private final byte[] totalNodes = new byte[4]; 65 private final byte[] freeNodes = new byte[4]; 66 private final byte[] reserved1 = new byte[2]; 67 private final byte[] clumpSize = new byte[4]; 68 private final byte[] btreeType = new byte[1]; 69 private final byte[] keyCompareType = new byte[1]; 70 private final byte[] attributes = new byte[4]; 71 private final byte[] reserved3 = new byte[4*16]; 72 BTHeaderRec(byte[] data, int offset)73 public BTHeaderRec(byte[] data, int offset) { 74 System.arraycopy(data, offset+0, treeDepth, 0, 2); 75 System.arraycopy(data, offset+2, rootNode, 0, 4); 76 System.arraycopy(data, offset+6, leafRecords, 0, 4); 77 System.arraycopy(data, offset+10, firstLeafNode, 0, 4); 78 System.arraycopy(data, offset+14, lastLeafNode, 0, 4); 79 System.arraycopy(data, offset+18, nodeSize, 0, 2); 80 System.arraycopy(data, offset+20, maxKeyLength, 0, 2); 81 System.arraycopy(data, offset+22, totalNodes, 0, 4); 82 System.arraycopy(data, offset+26, freeNodes, 0, 4); 83 System.arraycopy(data, offset+30, reserved1, 0, 2); 84 System.arraycopy(data, offset+32, clumpSize, 0, 4); 85 System.arraycopy(data, offset+36, btreeType, 0, 1); 86 System.arraycopy(data, offset+37, keyCompareType, 0, 1); 87 System.arraycopy(data, offset+38, attributes, 0, 4); 88 System.arraycopy(data, offset+42, reserved3, 0, 4*16); 89 } 90 getTreeDepth()91 public short getTreeDepth() { return Util.readShortBE(treeDepth); } getRootNode()92 public int getRootNode() { return Util.readIntBE(rootNode); } getLeafRecords()93 public int getLeafRecords() { return Util.readIntBE(leafRecords); } getFirstLeafNode()94 public int getFirstLeafNode() { return Util.readIntBE(firstLeafNode); } getLastLeafNode()95 public int getLastLeafNode() { return Util.readIntBE(lastLeafNode); } getNodeSize()96 public short getNodeSize() { return Util.readShortBE(nodeSize); } getMaxKeyLength()97 public short getMaxKeyLength() { return Util.readShortBE(maxKeyLength); } getTotalNodes()98 public int getTotalNodes() { return Util.readIntBE(totalNodes); } getFreeNodes()99 public int getFreeNodes() { return Util.readIntBE(freeNodes); } getReserved1()100 public short getReserved1() { return Util.readShortBE(reserved1); } getClumpSize()101 public int getClumpSize() { return Util.readIntBE(clumpSize); } getBtreeType()102 public byte getBtreeType() { return Util.readByteBE(btreeType); } 103 /** Specifies what type of key compare algorithm to use. For HFS+ volumes, this field is to be treated as reserved. 104 For HFSX (at least version 5) volumes, the value of this field will be one of the constants 105 <code>kHFSCaseFolding</code> or <code>kHFSBinaryCompare</code> defined as static constants of this class. */ getKeyCompareType()106 public byte getKeyCompareType() { return Util.readByteBE(keyCompareType); } getAttributes()107 public int getAttributes() { return Util.readIntBE(attributes); } getReserved3()108 public int[] getReserved3() { return Util.readIntArrayBE(reserved3); } 109 110 // Access to attributes: isBTBadCloseSet()111 public boolean isBTBadCloseSet() { return Util.getBit(getAttributes(), 0); } isBTBigKeysSet()112 public boolean isBTBigKeysSet() { return Util.getBit(getAttributes(), 1); } isBTVariableIndexKeysSet()113 public boolean isBTVariableIndexKeysSet() { return Util.getBit(getAttributes(), 2); } 114 length()115 public static int length() { return 106; } 116 printFields(PrintStream ps, String prefix)117 public void printFields(PrintStream ps, String prefix) { 118 ps.println(prefix + " treeDepth: " + getTreeDepth()); 119 ps.println(prefix + " rootNode: " + getRootNode()); 120 ps.println(prefix + " leafRecords: " + getLeafRecords()); 121 ps.println(prefix + " firstLeafNode: " + getFirstLeafNode()); 122 ps.println(prefix + " lastLeafNode: " + getLastLeafNode()); 123 ps.println(prefix + " nodeSize: " + getNodeSize()); 124 ps.println(prefix + " maxKeyLength: " + getMaxKeyLength()); 125 ps.println(prefix + " totalNodes: " + getTotalNodes()); 126 ps.println(prefix + " freeNodes: " + getFreeNodes()); 127 ps.println(prefix + " reserved1: " + getReserved1()); 128 ps.println(prefix + " clumpSize: " + getClumpSize()); 129 ps.println(prefix + " btreeType: " + getBtreeType()); 130 ps.println(prefix + " keyCompareType: " + getKeyCompareType()); 131 ps.println(prefix + " attributes: " + getAttributes()); 132 ps.println(prefix + " reserved3: " + getReserved3()); 133 } 134 print(PrintStream ps, String prefix)135 public void print(PrintStream ps, String prefix) { 136 ps.println(prefix + "BTHeaderRec:"); 137 printFields(ps, prefix); 138 } 139 getBytes()140 public byte[] getBytes() { 141 byte[] result = new byte[length()]; 142 int offset = 0; 143 144 System.arraycopy(treeDepth, 0, result, offset, treeDepth.length); offset += treeDepth.length; 145 System.arraycopy(rootNode, 0, result, offset, rootNode.length); offset += rootNode.length; 146 System.arraycopy(leafRecords, 0, result, offset, leafRecords.length); offset += leafRecords.length; 147 System.arraycopy(firstLeafNode, 0, result, offset, firstLeafNode.length); offset += firstLeafNode.length; 148 System.arraycopy(lastLeafNode, 0, result, offset, lastLeafNode.length); offset += lastLeafNode.length; 149 System.arraycopy(nodeSize, 0, result, offset, nodeSize.length); offset += nodeSize.length; 150 System.arraycopy(maxKeyLength, 0, result, offset, maxKeyLength.length); offset += maxKeyLength.length; 151 System.arraycopy(totalNodes, 0, result, offset, totalNodes.length); offset += totalNodes.length; 152 System.arraycopy(freeNodes, 0, result, offset, freeNodes.length); offset += freeNodes.length; 153 System.arraycopy(reserved1, 0, result, offset, reserved1.length); offset += reserved1.length; 154 System.arraycopy(clumpSize, 0, result, offset, clumpSize.length); offset += clumpSize.length; 155 System.arraycopy(btreeType, 0, result, offset, btreeType.length); offset += btreeType.length; 156 System.arraycopy(keyCompareType, 0, result, offset, keyCompareType.length); offset += keyCompareType.length; 157 System.arraycopy(attributes, 0, result, offset, attributes.length); offset += attributes.length; 158 System.arraycopy(reserved3, 0, result, offset, reserved3.length); offset += reserved3.length; 159 160 return result; 161 } 162 getStructElements()163 public Dictionary getStructElements() { 164 DictionaryBuilder db = 165 new DictionaryBuilder(BTHeaderRec.class.getSimpleName()); 166 167 db.addUIntBE("treeDepth", treeDepth, "Depth"); 168 db.addUIntBE("rootNode", rootNode, "Root node"); 169 db.addUIntBE("leafRecords", leafRecords, "Number of leaf records"); 170 db.addUIntBE("firstLeafNode", firstLeafNode, "First leaf node"); 171 db.addUIntBE("lastLeafNode", lastLeafNode, "Last leaf node"); 172 db.addUIntBE("nodeSize", nodeSize, "Node size"); 173 db.addUIntBE("maxKeyLength", maxKeyLength, "Maximum key length"); 174 db.addUIntBE("totalNodes", totalNodes, "Total number of nodes"); 175 db.addUIntBE("freeNodes", freeNodes, "Number of free nodes"); 176 177 /* No need to add 'reserved1' to dictionary because it's not a field 178 * that we want to expose. */ 179 180 db.addUIntBE("clumpSize", clumpSize, "Clump size"); 181 db.addUIntBE("btreeType", btreeType, "B-tree type"); 182 db.addUIntBE("keyCompareType", keyCompareType, "Key compare type", 183 IntegerFieldRepresentation.HEXADECIMAL); 184 185 DictionaryBuilder attributesDict = new DictionaryBuilder(""); 186 attributesDict.addFlag("kBTBadClose", attributes, 0, "B-tree was not " + 187 "closed properly"); 188 attributesDict.addFlag("kBTBigKeys", attributes, 1, "B-tree uses big " + 189 "keys"); 190 attributesDict.addFlag("kBTVariableIndexKeys", attributes, 2, 191 "B-tree uses variable-length index node keys"); 192 193 db.add("attributes", attributesDict.getResult(), "Attributes"); 194 195 /* No need to add 'reserved3' to dictionary because it's not a field 196 * that we want to expose. */ 197 198 return db.getResult(); 199 } 200 } 201