1 /*- 2 * Copyright (C) 2008 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.hfsexplorer.types.resff; 19 20 import java.io.PrintStream; 21 import java.util.ArrayList; 22 import java.util.List; 23 import org.catacombae.csjc.PrintableStruct; 24 import org.catacombae.io.RuntimeIOException; 25 import org.catacombae.util.Util; 26 import org.catacombae.io.SynchronizedReadableRandomAccess; 27 import org.catacombae.util.Util.Pair; 28 29 /** This class was generated by CStructToJavaClass. */ 30 public class ResourceMap implements PrintableStruct { 31 /* 32 * struct ResourceMap 33 * size: minimum 30 or 38 bytes 34 * description: 35 * 36 * BP Size Type Identifier Description 37 * ------------------------------------------------------------------------------------------------------------------- 38 * 0 1*16 UInt8[16] reserved1 // Reserved for copy of resource header. 39 * 16 4 UInt32 reserved2 // Reserved for handle to next resource map. 40 * 20 2 UInt16 reserved3 // Reserved for file reference number. 41 * 22 2 UInt16 resourceForkAttributes // Resource fork attributes 42 * 24 2 UInt16 typeListOffset // Offset from beginning of map to resource type list. 43 * 26 2 UInt16 nameListOffset // Offset from beginning of map to resource name list. 44 * 28 2 SInt16 typeCount // Number of types in the map minus 1. 45 * 30 8*? ResourceType[typeCount+1] resourceTypeList // Resource type list. 46 */ 47 48 private static final boolean DEBUG = Util.booleanEnabledByProperties(true, 49 "org.catacombae.debug", 50 "org.catacombae.hfsexplorer.debug", 51 "org.catacombae.hfsexplorer.types.debug", 52 "org.catacombae.hfsexplorer.types.resff.debug", 53 "org.catacombae.hfsexplorer.types.resff." + 54 ResourceMap.class.getSimpleName() + ".debug"); 55 56 public static final int STRUCTSIZE = 46; 57 58 private final byte[] reserved1 = new byte[1*16]; 59 private final byte[] reserved2 = new byte[4]; 60 private final byte[] reserved3 = new byte[2]; 61 private final byte[] resourceForkAttributes = new byte[2]; 62 private final byte[] typeListOffset = new byte[2]; 63 private final byte[] nameListOffset = new byte[2]; 64 private final byte[] typeCount = new byte[2]; 65 private final ResourceType[] resourceTypeList; 66 private final List<Pair<ResourceType, ReferenceListEntry[]>> referenceList; 67 private final List<Pair<ReferenceListEntry, ResourceName>> resourceNameList; 68 ResourceMap(SynchronizedReadableRandomAccess stream, final long offset)69 public ResourceMap(SynchronizedReadableRandomAccess stream, final long offset) { 70 byte[] data = new byte[30]; 71 72 final int bytesRead = stream.readFrom(offset, data); 73 if(bytesRead != data.length) { 74 throw new RuntimeIOException("Error while reading the resource " + 75 "map header from offset " + offset + " (" + 76 (bytesRead == -1 ? "End of file" : 77 (bytesRead + " / " + data.length + " bytes read")) + ")."); 78 } 79 80 System.arraycopy(data, 0, reserved1, 0, 1 * 16); 81 System.arraycopy(data, 16, reserved2, 0, 4); 82 System.arraycopy(data, 20, reserved3, 0, 2); 83 System.arraycopy(data, 22, resourceForkAttributes, 0, 2); 84 System.arraycopy(data, 24, typeListOffset, 0, 2); 85 System.arraycopy(data, 26, nameListOffset, 0, 2); 86 System.arraycopy(data, 28, typeCount, 0, 2); 87 88 if(DEBUG) { 89 System.err.println("ResourceMap(): typeListOffset = " + 90 getTypeListOffset()); 91 System.err.println("ResourceMap(): nameListOffset = " + 92 getNameListOffset()); 93 System.err.println("ResourceMap(): typeCount = " + 94 getTypeCount()); 95 } 96 97 final int resourceTypeListOffset = getTypeListOffset(); 98 final int resourceNameListOffset = getNameListOffset(); 99 100 { 101 long curOffset = offset + resourceTypeListOffset + 2; // +2 for typeCount, which is included in the offset 102 byte[] curResTypeData = new byte[ResourceType.length()]; 103 resourceTypeList = new ResourceType[getTypeCount() + 1]; // typeCount is a SIGNED integer. 104 for(int i = 0; i < resourceTypeList.length; ++i) { 105 stream.readFullyFrom(curOffset, curResTypeData); 106 resourceTypeList[i] = new ResourceType(curResTypeData, 0); 107 curOffset += curResTypeData.length; 108 109 if(DEBUG) { 110 System.err.println("Read type:"); 111 resourceTypeList[i].print(System.err, " "); 112 } 113 } 114 } 115 116 int numberOfRefListEntries = 0; 117 { 118 byte[] curListEntryData = new byte[ReferenceListEntry.length()]; 119 referenceList = new ArrayList<Pair<ResourceType, ReferenceListEntry[]>>(resourceTypeList.length); 120 for(int i = 0; i < resourceTypeList.length; ++i) { 121 ResourceType curType = resourceTypeList[i]; 122 if(DEBUG) { 123 System.err.println("Processing instances of type:"); 124 curType.print(System.err, " "); 125 } 126 127 ReferenceListEntry[] curList = new ReferenceListEntry[curType.getInstanceCount() + 1]; 128 129 long curOffset = offset + resourceTypeListOffset + Util.unsign(curType.getReferenceListOffset()); 130 for(int j = 0; j < curList.length; ++j) { 131 stream.readFullyFrom(curOffset, curListEntryData); 132 curList[j] = new ReferenceListEntry(curListEntryData, 0); 133 curOffset += curListEntryData.length; 134 135 if(DEBUG) { 136 System.err.println("Read ReferenceListEntry:"); 137 curList[j].print(System.err, " "); 138 } 139 } 140 141 numberOfRefListEntries += curList.length; 142 referenceList.add(new Pair<ResourceType, ReferenceListEntry[]>(curType, curList)); 143 } 144 } 145 146 { 147 resourceNameList = new ArrayList<Pair<ReferenceListEntry, ResourceName>>(numberOfRefListEntries); 148 149 for(Pair<ResourceType, ReferenceListEntry[]> p : referenceList) { 150 for(ReferenceListEntry e : p.getB()) { 151 long resNameOffset = e.getResourceNameOffset(); 152 if(resNameOffset != -1) { 153 long nameOffset = offset + resourceNameListOffset + resNameOffset; 154 ResourceName resName = new ResourceName(stream, nameOffset); 155 resourceNameList.add(new Pair<ReferenceListEntry, ResourceName>(e, resName)); 156 157 if(DEBUG) { 158 System.err.println("Read ResourceName:"); 159 resName.print(System.err, " "); 160 } 161 } 162 } 163 } 164 } 165 } 166 length()167 public static int length() { return STRUCTSIZE; } 168 maxSize()169 public int maxSize() { 170 /* Non-variable fields: 30 bytes 171 * Number of ResourceTypes can be from 1 (typeCount=0) to 172 * 65536 (typeCount=65535) */ 173 return 30 + ResourceType.length()*(65535+1); 174 } 175 occupiedSize()176 public int occupiedSize() { 177 return 30 + resourceTypeList.length*ResourceType.length(); 178 } 179 180 /** // Reserved for copy of resource header. */ getReserved1()181 public byte[] getReserved1() { return Util.readByteArrayBE(reserved1); } 182 /** // Reserved for handle to next resource map. */ getReserved2()183 public int getReserved2() { return Util.readIntBE(reserved2); } 184 /** // Reserved for file reference number. */ getReserved3()185 public short getReserved3() { return Util.readShortBE(reserved3); } 186 /** // Resource fork attributes */ getResourceForkAttributes()187 public short getResourceForkAttributes() { return Util.readShortBE(resourceForkAttributes); } 188 /** // Offset from beginning of map to resource type list. */ getTypeListOffset()189 public int getTypeListOffset() { 190 return Util.unsign(getRawTypeListOffset()); 191 } 192 /** // Offset from beginning of map to resource name list. */ getNameListOffset()193 public int getNameListOffset() { 194 return Util.unsign(getRawNameListOffset()); 195 } 196 /** // Number of types in the map minus 1. */ getTypeCount()197 public short getTypeCount() { return Util.readShortBE(typeCount); } 198 getRawTypeListOffset()199 public short getRawTypeListOffset() { 200 return Util.readShortBE(typeListOffset); 201 } getRawNameListOffset()202 public short getRawNameListOffset() { 203 return Util.readShortBE(nameListOffset); 204 } 205 /** Resource type list. */ getResourceTypeList()206 public ResourceType[] getResourceTypeList() { 207 return Util.arrayCopy(resourceTypeList, new ResourceType[resourceTypeList.length]); 208 } 209 getReferenceList()210 public List<Pair<ResourceType, ReferenceListEntry[]>> getReferenceList() { 211 return new ArrayList<Pair<ResourceType, ReferenceListEntry[]>>(referenceList); 212 } getResourceNameList()213 public List<Pair<ReferenceListEntry, ResourceName>> getResourceNameList() { 214 return new ArrayList<Pair<ReferenceListEntry, ResourceName>>(resourceNameList); 215 } 216 217 /** 218 * Searches for the reference list entries associated with the supplied ResourceType. 219 * 220 * @param resType 221 * @return the requested entries, or null if none were found. NOTE: null should not normally be 222 * returned by this method for a valid resType which has been retrieved through the current map 223 * instance. null can be treated as an internal error. 224 */ getReferencesByType(ResourceType resType)225 public ReferenceListEntry[] getReferencesByType(ResourceType resType) { 226 for(Pair<ResourceType, ReferenceListEntry[]> entry : referenceList) { 227 if(entry.getA() == resType) 228 return Util.arrayCopy(entry.getB(), new ReferenceListEntry[entry.getB().length]); 229 } 230 return null; 231 } 232 233 /** 234 * Searches for the ResourceName associated with the supplied ReferenceListEntry. 235 * 236 * @param entry 237 * @return the requested name, or null if no name was defined for this resource. 238 */ getNameByReferenceListEntry(ReferenceListEntry entry)239 public ResourceName getNameByReferenceListEntry(ReferenceListEntry entry) { 240 for(Pair<ReferenceListEntry, ResourceName> listEntry : resourceNameList) { 241 if(listEntry.getA() == entry) 242 return listEntry.getB(); 243 } 244 return null; 245 } 246 printFields(PrintStream ps, String prefix)247 public void printFields(PrintStream ps, String prefix) { 248 ps.println(prefix + " reserved1: " + getReserved1()); 249 ps.println(prefix + " reserved2: " + getReserved2()); 250 ps.println(prefix + " reserved3: " + getReserved3()); 251 ps.println(prefix + " resourceForkAttributes: " + getResourceForkAttributes()); 252 ps.println(prefix + " typeListOffset: " + getTypeListOffset()); 253 ps.println(prefix + " nameListOffset: " + getNameListOffset()); 254 ps.println(prefix + " typeCount: " + getTypeCount()); 255 ps.println(prefix + " resourceTypeList: "); 256 for(int i = 0; i < resourceTypeList.length; ++i) { 257 ps.println(prefix + " [" + i + "]:"); 258 resourceTypeList[i].print(ps, prefix + " "); 259 } 260 261 ps.println(prefix + " referenceList: "); 262 { 263 int i = 0; 264 for(Pair<ResourceType, ReferenceListEntry[]> entry : referenceList) { 265 for(ReferenceListEntry refListEntry : entry.getB()) { 266 ps.println(prefix + " [" + i++ + "]:"); 267 refListEntry.print(ps, prefix + " "); 268 } 269 } 270 } 271 272 ps.println(prefix + " resourceNameList: "); 273 { 274 int i = 0; 275 for(Pair<ReferenceListEntry, ResourceName> entry : resourceNameList) { 276 ps.println(prefix + " [" + i++ + "]:"); 277 entry.getB().print(ps, prefix + " "); 278 } 279 } 280 } 281 print(PrintStream ps, String prefix)282 public void print(PrintStream ps, String prefix) { 283 ps.println(prefix + "ResourceMap:"); 284 printFields(ps, prefix); 285 } 286 287 /* 288 public byte[] getBytes() { 289 byte[] result = new byte[length()]; 290 int offset = 0; 291 System.arraycopy(this.reserved1, 0, result, offset, this.reserved1.length); offset += this.reserved1.length; 292 System.arraycopy(this.reserved2, 0, result, offset, this.reserved2.length); offset += this.reserved2.length; 293 System.arraycopy(this.reserved3, 0, result, offset, this.reserved3.length); offset += this.reserved3.length; 294 System.arraycopy(this.resourceForkAttributes, 0, result, offset, this.resourceForkAttributes.length); offset += this.resourceForkAttributes.length; 295 System.arraycopy(this.typeListOffset, 0, result, offset, this.typeListOffset.length); offset += this.typeListOffset.length; 296 System.arraycopy(this.nameListOffset, 0, result, offset, this.nameListOffset.length); offset += this.nameListOffset.length; 297 System.arraycopy(this.typeCount, 0, result, offset, this.typeCount.length); offset += this.typeCount.length; 298 for(int i = 0; i < resourceTypeList.length; ++i) { 299 byte[] tempData = this.resourceTypeList[i].getBytes(); 300 System.arraycopy(tempData, 0, result, offset, tempData.length); offset += tempData.length; 301 } 302 return result; 303 }*/ 304 } 305