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