PageRenderTime 66ms CodeModel.GetById 13ms app.highlight 49ms RepoModel.GetById 1ms app.codeStats 0ms

/core/sdk-objc/GMResourceFork.m

http://macfuse.googlecode.com/
Objective C | 305 lines | 202 code | 38 blank | 65 comment | 11 complexity | fb951f2e7ad1d06435b727a9a82dc15f MD5 | raw file
  1// ================================================================
  2// Copyright (c) 2007, Google Inc.
  3// All rights reserved.
  4//
  5// Redistribution and use in source and binary forms, with or without
  6// modification, are permitted provided that the following conditions are
  7// met:
  8//
  9// * Redistributions of source code must retain the above copyright
 10//   notice, this list of conditions and the following disclaimer.
 11// * Redistributions in binary form must reproduce the above
 12//   copyright notice, this list of conditions and the following disclaimer
 13//   in the documentation and/or other materials provided with the
 14//   distribution.
 15// * Neither the name of Google Inc. nor the names of its
 16//   contributors may be used to endorse or promote products derived from
 17//   this software without specific prior written permission.
 18//
 19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30// ================================================================
 31//
 32//  GMResourceFork.m
 33//  MacFUSE
 34//
 35//  Created by ted on 12/29/07.
 36//
 37// See the following URL for documentation on resource fork format:
 38//  http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
 39//
 40#import "GMResourceFork.h"
 41
 42// The format for a resource fork is as follows ('+' means one-or-more):
 43//
 44// ResourceForkHeader
 45// {ResourceDataItem, <data_for_resource>}+
 46// ResourceMapHeader
 47// ResourceTypeListHeader
 48// ResourceTypeListItem+
 49// ResourceReferenceListItem+
 50// {ResourceNameListItem, <name_for_resource>}+
 51//
 52typedef struct {
 53  UInt32 resourceDataOffset;    // Offset from beginning to resource data.
 54  UInt32 resourceMapOffset;     // Offset from beginning to resource map.
 55  UInt32 resourceDataLength;    // Length of entire data segment in bytes.
 56  UInt32 resourceMapLength;     // Length of resource map in bytes.
 57} __attribute__((packed)) ResourceForkHeader;
 58
 59typedef struct {
 60  UInt32 dataLength;  // Length of data that follows.
 61  // Followed by: variable length byte[] of data.
 62} __attribute__((packed)) ResourceDataItem;
 63
 64typedef struct {
 65  // The next three fields should be zero'd out. It looks like they are reserved
 66  // for in-memory use by an entity loading the resource fork.
 67  char reservedForResourceForkHeader[sizeof(ResourceForkHeader)];
 68  UInt32 reservedForHandle;
 69  UInt16 reservedForFileReferenceNumber;
 70  
 71  SInt16 resourceForkAttributes;  // ResFileAttributes attribs of resource fork.
 72  UInt16 typeListOffset;  // Offset from beginning of map to resource type list.
 73  UInt16 nameListOffset;  // Offset from beginning of map to resource name list.
 74} __attribute__((packed)) ResourceMapHeader;
 75
 76typedef struct {
 77  UInt16 numTypesMinusOne;  // Number of types in the map minus 1.
 78} __attribute__((packed)) ResourceTypeListHeader;
 79
 80typedef struct {
 81  ResType type;        // FourCharCode resource type, i.e. 'icns'
 82  UInt16 numMinusOne;  // Number of resources of this type in map minus 1.
 83  UInt16 referenceListOffset;  // Offset from beginning of resource type list to 
 84                               // the reference list for this type.
 85} __attribute__((packed)) ResourceTypeListItem;
 86
 87typedef struct {
 88  SInt16 resid;  // ResID type; resource ID
 89  SInt16 nameListOffset;  // Offset from beginning of resource name list to 
 90                          // resource name for this resource. A value of -1 is
 91                          // used when the resource does not have a name.
 92  UInt8 attributes;  // ResAttributes?: resource attributes.
 93  UInt8 resourceDataOffset1;  // These three bytes are the offset from beginning
 94  UInt8 resourceDataOffset2;  // of resource data to data for this resource.
 95  UInt8 resourceDataOffset3;
 96  UInt32 reservedForHandleToResource;  // Reserved, zero out.
 97} __attribute__((packed)) ResourceReferenceListItem;
 98
 99typedef struct {
100  UInt8 nameLength;  // Length of name in bytes.
101  // Followed by: variable length char[] for resource name.
102} __attribute__((packed)) ResourceNameListItem;
103
104@implementation GMResource
105
106+ (GMResource *)resourceWithType:(ResType)resType
107                           resID:(ResID)resID
108                            name:(NSString *)name  // May be nil
109                            data:(NSData *)data {
110  return [[[GMResource alloc] 
111           initWithType:resType resID:resID name:name data:data] autorelease];
112}
113
114- (id)init {
115  return [self initWithType:0 resID:0 name:nil data:nil];
116}
117
118- (id)initWithType:(ResType)resType
119             resID:(ResID)resID 
120              name:(NSString *)name
121              data:(NSData *)data {
122  if ((self = [super init])) {
123    if (data == nil) {
124      [self release];
125      return nil;
126    }
127    resType_ = resType;
128    resID_ = resID;
129    name_ = [name retain];
130    data_ = [data retain];
131  }
132  return self;
133}
134
135- (void)dealloc {
136  [name_ release];
137  [data_ release];
138  [super dealloc];
139}
140
141- (ResID)resID {
142  return resID_;
143}
144- (ResType)resType {
145  return resType_;
146}
147- (NSString *)name {
148  return name_;
149}
150- (NSData *)data {
151  return data_;
152}
153
154@end
155
156@implementation GMResourceFork
157
158+ (GMResourceFork *)resourceFork {
159  return [[[GMResourceFork alloc] init] autorelease];
160}
161
162- (id)init {
163  if ((self = [super init])) {
164    resourcesByType_ = [[NSMutableDictionary alloc] init];
165  }
166  return self;    
167}
168
169- (void)dealloc {
170  [resourcesByType_ release];
171  [super dealloc];
172}
173
174// Add a new resource.
175- (void)addResourceWithType:(ResType)resType
176                      resID:(ResID)resID
177                       name:(NSString *)name
178                       data:(NSData *)data {
179  GMResource* resource = [GMResource resourceWithType:resType
180                                                resID:resID
181                                                 name:name
182                                                 data:data];
183  [self addResource:resource];
184}
185
186- (void)addResource:(GMResource *)resource {
187  ResType type = [resource resType];
188  NSNumber* key = [NSNumber numberWithLong:type];
189  NSMutableArray* resources = [resourcesByType_ objectForKey:key];
190  if (resources == nil) {
191    resources = [NSMutableArray array];
192    [resourcesByType_ setObject:resources forKey:key];
193  }
194  [resources addObject:resource];
195}
196
197
198// Constructs the raw data for the resource fork containing all added resources.
199- (NSData *)data {
200  NSMutableData* resourceData = [NSMutableData data];
201  NSMutableData* typeListData = [NSMutableData data];
202  NSMutableData* referenceListData = [NSMutableData data];
203  NSMutableData* nameListData = [NSMutableData data];
204
205  NSArray* keys = [resourcesByType_ allKeys];
206  int refListStartOffset = sizeof(ResourceTypeListHeader) + 
207    ([keys count] * sizeof(ResourceTypeListItem));
208
209  // For each resource type.
210  for ( int i = 0; i < [keys count]; ++i ) {
211    NSArray* resources = [resourcesByType_ objectForKey:[keys objectAtIndex:i]];
212
213    // -- Append the ResourceTypeListItem to typeListData --
214    ResourceTypeListItem typeItem;
215    memset(&typeItem, 0, sizeof(typeItem));
216    UInt16 refListOffset = refListStartOffset + [referenceListData length];
217    ResType type = [[resources lastObject] resType];
218    typeItem.type = htonl(type);
219    typeItem.numMinusOne = htons([resources count] - 1);
220    typeItem.referenceListOffset = htons(refListOffset);
221    [typeListData appendBytes:&typeItem length:sizeof(typeItem)];
222    
223    // For each resource of that type.
224    for ( int j = 0; j < [resources count]; ++j ) {
225      GMResource* resource = [resources objectAtIndex:j];
226      NSString* name = [resource name];
227      
228      // -- Append the ResourceReferenceListItem to referenceListData --
229      ResourceReferenceListItem referenceItem;
230      memset(&referenceItem, 0, sizeof(referenceItem));
231      UInt32 dataOffset = [resourceData length];
232      referenceItem.resid = htons([resource resID]);
233      referenceItem.nameListOffset = 
234        htons((name == nil) ? (SInt16)(-1) : [nameListData length]);
235      referenceItem.attributes = 0;  // TODO: Support attributes?
236      referenceItem.resourceDataOffset1 = (dataOffset & 0x00FF0000) >> 16;
237      referenceItem.resourceDataOffset2 = (dataOffset & 0x0000FF00) >> 8;
238      referenceItem.resourceDataOffset3 = (dataOffset & 0x000000FF);
239      [referenceListData appendBytes:&referenceItem length:sizeof(referenceItem)];
240
241      // -- Append the ResourceNameListItem and name data nameListData --
242      if ([resource name] != nil) {
243        ResourceNameListItem nameItem;
244        memset(&nameItem, 0, sizeof(nameItem));
245        NSString* name = [resource name];
246        int nameLen = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];    
247        nameItem.nameLength = nameLen;      
248        [nameListData appendBytes:&nameItem length:sizeof(nameItem)];
249        [nameListData appendBytes:[name UTF8String] length:nameLen];
250      }
251
252      // -- Append the ResourceDataItem and resource data to resourceData --
253      ResourceDataItem dataItem;
254      memset(&dataItem, 0, sizeof(dataItem));
255      dataItem.dataLength = htonl([[resource data] length]);
256      [resourceData appendBytes:&dataItem length:sizeof(dataItem)];
257      [resourceData appendData:[resource data]];
258    }
259  }
260
261  ResourceForkHeader forkHeader;
262  memset(&forkHeader, 0, sizeof(forkHeader));
263  ResourceMapHeader mapHeader;
264  memset(&mapHeader, 0, sizeof(mapHeader));
265  ResourceTypeListHeader typeListHeader;
266  memset(&typeListHeader, 0, sizeof(typeListHeader));
267  
268  // It looks like OS X prefers the resource data to start at offset 256 bytes.
269  UInt32 dataOffset = sizeof(forkHeader) > 256 ? sizeof(forkHeader) : 256;
270  UInt32 dataLen = [resourceData length];
271  UInt32 mapOffset = dataOffset + dataLen;
272  UInt32 mapLen = sizeof(ResourceMapHeader) +
273                  sizeof(ResourceTypeListHeader) +
274                  [typeListData length] +
275                  [referenceListData length] +
276                  [nameListData length];
277
278  forkHeader.resourceDataOffset = htonl(dataOffset);
279  forkHeader.resourceMapOffset = htonl(mapOffset);
280  forkHeader.resourceDataLength = htonl(dataLen);
281  forkHeader.resourceMapLength = htonl(mapLen);
282  
283  mapHeader.resourceForkAttributes = htons(0);  // TODO: Support attributes?
284  mapHeader.typeListOffset = htons(sizeof(mapHeader));
285  mapHeader.nameListOffset = htons(sizeof(mapHeader) + 
286                                   sizeof(ResourceTypeListHeader) +
287                                   [typeListData length] +
288                                   [referenceListData length]);
289  
290  typeListHeader.numTypesMinusOne = htons([resourcesByType_ count] - 1);
291
292  NSMutableData* data = [NSMutableData data];  
293  [data appendBytes:&forkHeader length:sizeof(forkHeader)];
294  [data setLength:dataOffset];
295  [data appendData:resourceData];
296  [data appendBytes:&mapHeader length:sizeof(mapHeader)];
297  [data appendBytes:&typeListHeader length:sizeof(typeListHeader)];
298  [data appendData:typeListData];
299  [data appendData:referenceListData];
300  [data appendData:nameListData];
301  
302  return data;
303}
304
305@end