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