Skip to content

Commit

Permalink
Work in progress. Everything's implemented, though not everything is …
Browse files Browse the repository at this point in the history
…working; the output directory gets created, but not output files.
  • Loading branch information
Peter Hosey authored and Peter Hosey committed Oct 29, 2011
1 parent 994620e commit e70827b
Show file tree
Hide file tree
Showing 8 changed files with 524 additions and 7 deletions.
24 changes: 18 additions & 6 deletions icon-extract.xcodeproj/project.pbxproj
Expand Up @@ -9,8 +9,10 @@
/* Begin PBXBuildFile section */
31067A13145C667500509BE2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 31067A12145C667500509BE2 /* Foundation.framework */; };
31067A16145C667500509BE2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 31067A15145C667500509BE2 /* main.m */; };
31067A22145C67D400509BE2 /* PRHExtractIconFromFileOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 31067A21145C67D400509BE2 /* PRHExtractIconFromFileOperation.m */; };
31067A22145C67D400509BE2 /* PRHExtractIconsFromFileOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 31067A21145C67D400509BE2 /* PRHExtractIconsFromFileOperation.m */; };
31067A24145C682A00509BE2 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 31067A23145C682A00509BE2 /* Carbon.framework */; };
31067A27145C77BF00509BE2 /* PRHResourceEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 31067A26145C77BF00509BE2 /* PRHResourceEnumerator.m */; };
31067A2A145C894F00509BE2 /* BundleResourceSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 31067A29145C894F00509BE2 /* BundleResourceSupport.m */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand All @@ -30,9 +32,13 @@
31067A12145C667500509BE2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
31067A15145C667500509BE2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
31067A18145C667500509BE2 /* icon-extract-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "icon-extract-Prefix.pch"; sourceTree = "<group>"; };
31067A20145C67D400509BE2 /* PRHExtractIconFromFileOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PRHExtractIconFromFileOperation.h; sourceTree = "<group>"; };
31067A21145C67D400509BE2 /* PRHExtractIconFromFileOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PRHExtractIconFromFileOperation.m; sourceTree = "<group>"; };
31067A20145C67D400509BE2 /* PRHExtractIconsFromFileOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PRHExtractIconsFromFileOperation.h; sourceTree = "<group>"; };
31067A21145C67D400509BE2 /* PRHExtractIconsFromFileOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PRHExtractIconsFromFileOperation.m; sourceTree = "<group>"; };
31067A23145C682A00509BE2 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
31067A25145C77BF00509BE2 /* PRHResourceEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PRHResourceEnumerator.h; sourceTree = "<group>"; };
31067A26145C77BF00509BE2 /* PRHResourceEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PRHResourceEnumerator.m; sourceTree = "<group>"; };
31067A28145C894F00509BE2 /* BundleResourceSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BundleResourceSupport.h; sourceTree = "<group>"; };
31067A29145C894F00509BE2 /* BundleResourceSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BundleResourceSupport.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -79,8 +85,12 @@
children = (
31067A15145C667500509BE2 /* main.m */,
31067A17145C667500509BE2 /* Supporting Files */,
31067A20145C67D400509BE2 /* PRHExtractIconFromFileOperation.h */,
31067A21145C67D400509BE2 /* PRHExtractIconFromFileOperation.m */,
31067A20145C67D400509BE2 /* PRHExtractIconsFromFileOperation.h */,
31067A21145C67D400509BE2 /* PRHExtractIconsFromFileOperation.m */,
31067A25145C77BF00509BE2 /* PRHResourceEnumerator.h */,
31067A26145C77BF00509BE2 /* PRHResourceEnumerator.m */,
31067A28145C894F00509BE2 /* BundleResourceSupport.h */,
31067A29145C894F00509BE2 /* BundleResourceSupport.m */,
);
path = "icon-extract";
sourceTree = "<group>";
Expand Down Expand Up @@ -144,7 +154,9 @@
buildActionMask = 2147483647;
files = (
31067A16145C667500509BE2 /* main.m in Sources */,
31067A22145C67D400509BE2 /* PRHExtractIconFromFileOperation.m in Sources */,
31067A22145C67D400509BE2 /* PRHExtractIconsFromFileOperation.m in Sources */,
31067A27145C77BF00509BE2 /* PRHResourceEnumerator.m in Sources */,
31067A2A145C894F00509BE2 /* BundleResourceSupport.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
29 changes: 29 additions & 0 deletions icon-extract/BundleResourceSupport.h
@@ -0,0 +1,29 @@
//
// BundleResourceSupport.h
// icon-extract
//
// Created by Peter Hosey on 2011-10-29.
// Copyright (c) 2011 Peter Hosey. All rights reserved.
//

#pragma options align=packed
struct ResourceIDPair {
ResID localID;
ResID resourceID;
};

struct BundleLocalIDToToResourceIDMappingList {
ResType resourceType; //In the file reference mapping, always 'FREF'
UInt16 mappingCount;
struct ResourceIDPair mappings[1];
};
struct BundleResource {
ResType applicationSignature;
ResID applicationSignatureResourceID;
UInt16 arrayCount;
struct BundleLocalIDToToResourceIDMappingList iconMappingList;
struct BundleLocalIDToToResourceIDMappingList frefMappingList;
};
#pragma options align=reset

extern void PRHInstallBundleResourceFlipper(void);
100 changes: 100 additions & 0 deletions icon-extract/BundleResourceSupport.m
@@ -0,0 +1,100 @@
//
// BundleResourceSupport.m
// icon-extract
//
// Created by Peter Hosey on 2011-10-29.
// Copyright (c) 2011 Peter Hosey. All rights reserved.
//

#import "BundleResourceSupport.h"

static OSStatus flipBundleMembers(
OSType dataDomain,
OSType dataType,
SInt16 id,
void *dataPtr,
ByteCount dataSize,
Boolean currentlyNative,
void *refcon
);

extern void PRHInstallBundleResourceFlipper(void) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CoreEndianInstallFlipper(kCoreEndianResourceManagerDomain, 'BNDL', flipBundleMembers, NULL);
});
};

static inline void swapAccordingToSize(void *ptr, size_t size) {
switch(size) {
case 1:
break;
case 2:;
UInt16 *ptr16 = ptr;
*ptr16 = OSSwapInt16(*ptr16);
break;
case 4:;
UInt32 *ptr32 = ptr;
*ptr32 = OSSwapInt32(*ptr32);
break;
case 8:;
UInt64 *ptr64 = ptr;
*ptr64 = OSSwapInt64(*ptr64);
break;
}
}
#define IF_WITHIN_RANGE(start, member, dataSize) \
if (((((void *)&(member)) - (void *)start) + sizeof(member)) <= dataSize)
#define SWAP_IF_WITHIN_RANGE(start, member, dataSize) do{\
if (((((void *)&(member)) - (void *)start) + sizeof(member)) <= dataSize) { \
swapAccordingToSize(&(member), sizeof(member)); \
swapped = true; \
} else \
swapped = false; \
}while(0)

static bool swapMembersInMappingList(struct BundleResource *resPtr, struct BundleLocalIDToToResourceIDMappingList *mappingListPtr, ByteCount dataSize, bool currentlyNative) {
void *dataPtr = resPtr;
bool swapped = true;

size_t expectedSizeFromIconMappingListStart = dataSize - ((void *)mappingListPtr - dataPtr);
SWAP_IF_WITHIN_RANGE(mappingListPtr, mappingListPtr->resourceType, expectedSizeFromIconMappingListStart);
UInt16 mappingCount = 0;
IF_WITHIN_RANGE(mappingListPtr, mappingListPtr->mappingCount, expectedSizeFromIconMappingListStart) {
mappingCount = currentlyNative ? mappingListPtr->mappingCount : OSSwapInt16(mappingListPtr->mappingCount);
}
SWAP_IF_WITHIN_RANGE(mappingListPtr, mappingListPtr->mappingCount, expectedSizeFromIconMappingListStart);

struct ResourceIDPair *mappingsPtr = mappingListPtr->mappings;
size_t expectedSizeFromIconMappingsStart = dataSize - ((void *)mappingsPtr - dataPtr);
for (UInt16 i = 0; i < mappingCount; ++i) {
SWAP_IF_WITHIN_RANGE(mappingsPtr, mappingsPtr[i].localID, expectedSizeFromIconMappingsStart);
SWAP_IF_WITHIN_RANGE(mappingsPtr, mappingsPtr[i].resourceID, expectedSizeFromIconMappingsStart);
}

return swapped;
}

static OSStatus flipBundleMembers(
OSType dataDomain,
OSType dataType,
SInt16 id,
void *dataPtr,
ByteCount dataSize,
Boolean currentlyNative,
void *refcon
) {
bool swapped = true;
struct BundleResource *resPtr = dataPtr;
SWAP_IF_WITHIN_RANGE(resPtr, resPtr->applicationSignature, dataSize);
SWAP_IF_WITHIN_RANGE(resPtr, resPtr->applicationSignatureResourceID, dataSize);
SWAP_IF_WITHIN_RANGE(resPtr, resPtr->arrayCount, dataSize);

if (swapped) {
swapped = swapMembersInMappingList(resPtr, &(resPtr->iconMappingList), dataSize, currentlyNative);
if (swapped)
swapped = swapMembersInMappingList(resPtr, &(resPtr->frefMappingList), dataSize, currentlyNative);
}

return noErr;
}
14 changes: 14 additions & 0 deletions icon-extract/PRHExtractIconsFromFileOperation.h
@@ -0,0 +1,14 @@
//
// PRHExtractIconFromFileOperation.h
// icon-extract
//
// Created by Peter Hosey on 2011-10-29.
// Copyright (c) 2011 Peter Hosey. All rights reserved.
//

@interface PRHExtractIconsFromFileOperation : NSOperation

@property(copy) NSURL *sourceURL;
@property(copy) NSURL *destinationDirectoryURL;

@end
191 changes: 191 additions & 0 deletions icon-extract/PRHExtractIconsFromFileOperation.m
@@ -0,0 +1,191 @@
//
// PRHExtractIconFromFileOperation.m
// icon-extract
//
// Created by Peter Hosey on 2011-10-29.
// Copyright (c) 2011 Peter Hosey. All rights reserved.
//

#import "PRHExtractIconsFromFileOperation.h"

#import "PRHResourceEnumerator.h"
#import "BundleResourceSupport.h"

@implementation PRHExtractIconsFromFileOperation
{
FSRef sourceRef;
NSMutableData *iconFamilyElementsData; //The “elements” member of the IconFamilyResource structure.
NSString *destinationFilenameFormat;
}

@synthesize sourceURL;
@synthesize destinationDirectoryURL;

- (NSData *) dataForResourceWithType:(ResType)type ID:(ResID)ID {
NSData *data = nil;

Handle resH = Get1Resource(type, ID);
if (resH) {
LoadResource(resH);

HLock(resH);
data = [NSData dataWithBytes:*resH length:GetHandleSize(resH)];
HUnlock(resH);
}

return data;
}
- (OSStatus) extractExistingIconFamilyWithID:(ResID)ID {
NSData *icnsData = [self dataForResourceWithType:kIconFamilyType ID:ID];
iconFamilyElementsData = [icnsData mutableCopy];
return ResError();
}
- (OSStatus) extractIconOfType:(ResType)type ID:(ResID)ID {
NSData *data = [self dataForResourceWithType:type ID:ID];
OSStatus err = data ? noErr : ResError();
if (data) {
[iconFamilyElementsData appendBytes:&type length:sizeof(ResType)];
SInt32 size = (SInt32)OSSwapHostToBigInt32([data length]);
[iconFamilyElementsData appendBytes:&size length:sizeof(size)];
[iconFamilyElementsData appendData:data];
}
return err;
}
- (OSStatus) extractAllIconsWithID:(ResID)ID {
OSStatus err = noErr;

err = [self extractExistingIconFamilyWithID:ID];

if (err == noErr)
err = [self extractIconOfType:kThumbnail32BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kThumbnail8BitMask ID:ID];
if (err == noErr)
err = [self extractIconOfType:kHuge32BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kHuge8BitMask ID:ID];
if (err == noErr)
err = [self extractIconOfType:kHuge8BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kHuge4BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kHuge1BitMask ID:ID];
if (err == noErr)
err = [self extractIconOfType:kLarge32BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kLarge8BitMask ID:ID];
if (err == noErr)
err = [self extractIconOfType:kLarge8BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kLarge4BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kLarge1BitMask ID:ID];
if (err == noErr)
err = [self extractIconOfType:kSmall32BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kSmall8BitMask ID:ID];
if (err == noErr)
err = [self extractIconOfType:kSmall8BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kSmall4BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kSmall1BitMask ID:ID];
if (err == noErr)
err = [self extractIconOfType:kMini8BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kMini4BitData ID:ID];
if (err == noErr)
err = [self extractIconOfType:kMini1BitMask ID:ID];

return err;
}
- (OSStatus) extractIconsFromForkName:(struct HFSUniStr255 *)forkName {
ResFileRefNum refnum;
OSStatus err;
err = FSOpenResourceFile(&sourceRef, forkName->length, forkName->unicode, fsRdPerm, &refnum);
if (err != noErr) {
if (err != eofErr)
NSLog(@"FSOpenResourceFile returned %li/%s", (long)err, GetMacOSStatusCommentString(err));
return err;
}

NSMutableSet *processedIconResourceIDs = [NSMutableSet new];

PRHResourceEnumerator *bundlesEnum = [PRHResourceEnumerator newWithResourceType:'BNDL'];
for (PRHResource *bundleResource in bundlesEnum) {
const struct BundleResource *bundlePtr = [bundleResource.data bytes];
const struct ResourceIDPair *mappings = bundlePtr->iconMappingList.mappings;
for (UInt16 i = 0; i < bundlePtr->iconMappingList.mappingCount; ++i) {
const ResID iconResourceID = mappings[i].resourceID;

iconFamilyElementsData = [NSMutableData new];
[self extractAllIconsWithID:iconResourceID];
[processedIconResourceIDs addObject:[NSNumber numberWithShort:iconResourceID]];

NSString *destinationFilename = [NSString stringWithFormat:destinationFilenameFormat, (__bridge_transfer NSString *)UTCreateStringForOSType('BNDL'), iconResourceID];
NSURL *destinationURL = [self.destinationDirectoryURL URLByAppendingPathComponent:destinationFilename isDirectory:NO];
NSError *error = nil;
[iconFamilyElementsData writeToURL:destinationURL options:NSDataWritingAtomic error:&error];
NSLog(@"Wrote icons found by %@ to %@: error is %@", bundleResource, destinationURL, error);
}
}

PRHResourceEnumerator *modernIconsEnum = [PRHResourceEnumerator newWithResourceType:kIconFamilyType];
PRHResource *iconResource;
for (iconResource in modernIconsEnum) {
if (![processedIconResourceIDs containsObject:[NSNumber numberWithShort:iconResource.ID]])
{
iconFamilyElementsData = [NSMutableData new];
[self extractAllIconsWithID:iconResource.ID];
[processedIconResourceIDs addObject:[NSNumber numberWithShort:iconResource.ID]];

NSString *destinationFilename = [NSString stringWithFormat:destinationFilenameFormat, (__bridge_transfer NSString *)UTCreateStringForOSType(kIconFamilyType), iconResource.ID];
NSURL *destinationURL = [self.destinationDirectoryURL URLByAppendingPathComponent:destinationFilename isDirectory:NO];
NSError *error = nil;
[iconFamilyElementsData writeToURL:destinationURL options:NSDataWritingAtomic error:&error];
NSLog(@"Wrote icons found by %@ to %@: error is %@", iconResource, destinationURL, error);
}
}

PRHResourceEnumerator *eightBitIconsEnum = [PRHResourceEnumerator newWithResourceType:kLarge8BitData];
for (iconResource in eightBitIconsEnum) {
if (![processedIconResourceIDs containsObject:[NSNumber numberWithShort:iconResource.ID]])
{
iconFamilyElementsData = [NSMutableData new];
[self extractAllIconsWithID:iconResource.ID];
[processedIconResourceIDs addObject:[NSNumber numberWithShort:iconResource.ID]];

NSString *destinationFilename = [NSString stringWithFormat:destinationFilenameFormat, (__bridge_transfer NSString *)UTCreateStringForOSType(kLarge8BitData), iconResource.ID];
NSURL *destinationURL = [self.destinationDirectoryURL URLByAppendingPathComponent:destinationFilename isDirectory:NO];
NSError *error = nil;
[iconFamilyElementsData writeToURL:destinationURL options:NSDataWritingAtomic error:&error];
NSLog(@"Wrote icons found by %@ to %@: error is %@", iconResource, destinationURL, error);
}
}

CloseResFile(refnum);
err = ResError();
return err;
}
- (void) main {
NSLog(@"%s starting", __func__);
bool success = CFURLGetFSRef((__bridge CFURLRef)self.sourceURL, &sourceRef);
if (!success) return;

NSString *sourceFilename = [self.sourceURL lastPathComponent];
NSString *sourceBaseFilename = [sourceFilename stringByDeletingPathExtension];
destinationFilenameFormat = [sourceBaseFilename stringByAppendingString:@"-%@-%hd.icns"];

OSStatus err;
struct HFSUniStr255 forkName;

err = FSGetResourceForkName(&forkName);
[self extractIconsFromForkName:&forkName];
err = FSGetDataForkName(&forkName);
[self extractIconsFromForkName:&forkName];

destinationFilenameFormat = nil;
NSLog(@"%s finished", __func__);
}

@end

0 comments on commit e70827b

Please sign in to comment.