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
|
/*
* XADResourceFork.m
*
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#import "XADResourceFork.h"
#import "CSMemoryHandle.h"
#define ResourceMapHeader 22 // 16+4+2 bytes reserved for in-memory structures.
#define ResourceMapEntryHeader 2 // 2-byte type count actually part of type list.
#define ResourceMapEntrySize 8 // 4+2+2 bytes per type list entry.
@implementation XADResourceFork
+(XADResourceFork *)resourceForkWithHandle:(CSHandle *)handle
{
if(!handle) return nil;
XADResourceFork *fork=[[self new] autorelease];
[fork parseFromHandle:handle];
return fork;
}
+(XADResourceFork *)resourceForkWithHandle:(CSHandle *)handle error:(XADError *)errorptr
{
@try { return [self resourceForkWithHandle:handle]; }
@catch(id exception) { if(errorptr) *errorptr=[XADException parseException:exception]; }
return nil;
}
-(id)init
{
if((self=[super init]))
{
resources=nil;
}
return self;
}
-(void)dealloc
{
[resources release];
[super dealloc];
}
-(void)parseFromHandle:(CSHandle *)handle
{
off_t pos=[handle offsetInFile];
off_t dataoffset=[handle readUInt32BE];
off_t mapoffset=[handle readUInt32BE];
off_t datalength=[handle readUInt32BE];
off_t maplength=[handle readUInt32BE];
CSHandle *datahandle=[handle nonCopiedSubHandleFrom:pos+dataoffset length:datalength];
NSMutableDictionary *dataobjects=[self _parseResourceDataFromHandle:datahandle];
// Load the map into memory so that traversing its data structures
// doesn't cause countless seeks in compressed or encrypted input streams
[handle seekToFileOffset:pos+mapoffset];
NSData *mapdata=[handle readDataOfLength:(int)maplength];
CSHandle *maphandle=[CSMemoryHandle memoryHandleForReadingData:mapdata];
[resources release];
resources=[[self _parseMapFromHandle:maphandle withDataObjects:dataobjects] retain];
}
-(NSData *)resourceDataForType:(uint32_t)type identifier:(int)identifier
{
NSNumber *typekey=[NSNumber numberWithUnsignedInt:type];
NSNumber *identifierkey=[NSNumber numberWithInt:identifier];
NSDictionary *resourcesoftype=[resources objectForKey:typekey];
NSDictionary *resource=[resourcesoftype objectForKey:identifierkey];
return [resource objectForKey:@"Data"];
}
-(NSMutableDictionary *)_parseResourceDataFromHandle:(CSHandle *)handle
{
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
while(![handle atEndOfFile])
{
NSNumber *key=[NSNumber numberWithUnsignedLongLong:[handle offsetInFile]];
uint32_t length=[handle readUInt32BE];
NSData *data=[handle readDataOfLength:length];
[dict setObject:data forKey:key];
}
return dict;
}
-(NSDictionary *)_parseMapFromHandle:(CSHandle *)handle withDataObjects:(NSMutableDictionary *)dataobjects
{
[handle skipBytes:ResourceMapHeader];
/*int forkattributes=*/[handle readUInt16BE];
int typelistoffset=[handle readInt16BE];
int namelistoffset=[handle readInt16BE];
int typecount=[handle readInt16BE]+1;
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithCapacity:typecount];
for(int i=0;i<typecount;i++)
{
[handle seekToFileOffset:typelistoffset+i*ResourceMapEntrySize+ResourceMapEntryHeader];
uint32_t type=[handle readID];
int count=[handle readInt16BE]+1;
int offset=[handle readInt16BE];
[handle seekToFileOffset:typelistoffset+offset];
NSDictionary *references=[self _parseReferencesFromHandle:handle count:count];
[dict setObject:references forKey:[NSNumber numberWithUnsignedInt:type]];
}
NSEnumerator *typeenumerator=[dict keyEnumerator];
NSNumber *type;
while(type=[typeenumerator nextObject])
{
NSDictionary *resourcesoftype=[dict objectForKey:type];
NSEnumerator *identifierenumerator=[resourcesoftype keyEnumerator];
NSNumber *identifier;
while(identifier=[identifierenumerator nextObject])
{
NSMutableDictionary *resource=[resourcesoftype objectForKey:identifier];
[resource setObject:type forKey:@"Type"];
// Resolve the name (if any).
NSNumber *nameoffset=[resource objectForKey:@"NameOffset"];
if(nameoffset)
{
// untested
[handle seekToFileOffset:namelistoffset+[nameoffset intValue]];
int length=[handle readUInt8];
NSData *namedata=[handle readDataOfLength:length];
[resource setObject:namedata forKey:@"NameData"];
}
// Resolve the data.
NSNumber *dataoffset=[resource objectForKey:@"DataOffset"];
NSData *data=[dataobjects objectForKey:dataoffset];
[resource setObject:data forKey:@"Data"];
}
}
return dict;
}
-(NSDictionary *)_parseReferencesFromHandle:(CSHandle *)handle count:(int)count
{
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithCapacity:count];
for(int i=0;i<count;i++)
{
int identifier=[handle readInt16BE];
int nameoffset=[handle readInt16BE];
uint32_t attrsandoffset=[handle readUInt32BE];
int attrs=(attrsandoffset>>24)&0xff;
off_t offset=attrsandoffset&0xffffff;
/*reserved=*/[handle readUInt32BE];
NSNumber *key=[NSNumber numberWithInt:identifier];
NSMutableDictionary *resource=[NSMutableDictionary dictionaryWithObjectsAndKeys:
key,@"ID",
[NSNumber numberWithInt:attrs],@"Attributes",
[NSNumber numberWithUnsignedLongLong:offset],@"DataOffset",
nil];
if(nameoffset!=-1) [resource setObject:[NSNumber numberWithInt:nameoffset] forKey:@"NameOffset"];
[dict setObject:resource forKey:key];
}
return dict;
}
@end
|