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