//  This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
//  Copyright (C) 1997-1998, 2000-2001, 2004-2007  Steve Nygard

#import "CDObjCSegmentProcessor-Private.h"

#import <Foundation/Foundation.h>
#import "CDMachOFile.h"
#import "CDOCCategory.h"
#import "CDOCClass.h"
#import "CDOCIvar.h"
#import "CDOCMethod.h"
#import "CDOCModule.h"
#import "CDOCProtocol.h"
#import "CDOCSymtab.h"
#import "CDSection.h"
#import "CDSegmentCommand.h"
#import "CDSymbol.h"
#import "CDSymbolTable.h"
#import "NSArray-Extensions.h"

void swap_cd_objc_module(struct cd_objc_module *cd_objc_module)
{
    cd_objc_module->version = NSSwapLong(cd_objc_module->version);
    cd_objc_module->size = NSSwapLong(cd_objc_module->size);
    cd_objc_module->name = NSSwapLong(cd_objc_module->name);
    cd_objc_module->symtab = NSSwapLong(cd_objc_module->symtab);
}

void swap_cd_objc_symtab(struct cd_objc_symtab *cd_objc_symtab)
{
    cd_objc_symtab->sel_ref_cnt = NSSwapLong(cd_objc_symtab->sel_ref_cnt);
    cd_objc_symtab->refs = NSSwapLong(cd_objc_symtab->refs);
    cd_objc_symtab->cls_def_count = NSSwapShort(cd_objc_symtab->cls_def_count);
    cd_objc_symtab->cat_def_count = NSSwapShort(cd_objc_symtab->cat_def_count);
}

void swap_cd_objc_class(struct cd_objc_class *cd_objc_class)
{
    cd_objc_class->isa = NSSwapLong(cd_objc_class->isa);
    cd_objc_class->super_class = NSSwapLong(cd_objc_class->super_class);
    cd_objc_class->name = NSSwapLong(cd_objc_class->name);
    cd_objc_class->version = NSSwapLong(cd_objc_class->version);
    cd_objc_class->info = NSSwapLong(cd_objc_class->info);
    cd_objc_class->instance_size = NSSwapLong(cd_objc_class->instance_size);
    cd_objc_class->ivars = NSSwapLong(cd_objc_class->ivars);
    cd_objc_class->methods = NSSwapLong(cd_objc_class->methods);
    cd_objc_class->cache = NSSwapLong(cd_objc_class->cache);
    cd_objc_class->protocols = NSSwapLong(cd_objc_class->protocols);
}

#ifdef HANDLE_OBJC2_ABI
void swap_cd_objc2_class(struct cd_objc2_class *cd_objc2_class)
{
    cd_objc2_class->isa = NSSwapLong(cd_objc2_class->isa);
    cd_objc2_class->super_class = NSSwapLong(cd_objc2_class->super_class);
    cd_objc2_class->empty_cache = NSSwapLong(cd_objc2_class->empty_cache);
    cd_objc2_class->empty_vtable = NSSwapLong(cd_objc2_class->empty_vtable);
    cd_objc2_class->class_info = NSSwapLong(cd_objc2_class->class_info);
}

void swap_cd_objc2_class_info(struct cd_objc2_class_info * cd_objc2_class_info)
{
    cd_objc2_class_info->unknown_0 = NSSwapLong(cd_objc2_class_info->unknown_0);
    cd_objc2_class_info->size_of_super = NSSwapLong(cd_objc2_class_info->size_of_super);
    cd_objc2_class_info->size_include_self = NSSwapLong(cd_objc2_class_info->size_include_self);
    cd_objc2_class_info->unknown_3 = NSSwapLong(cd_objc2_class_info->unknown_3);
    cd_objc2_class_info->name = NSSwapLong(cd_objc2_class_info->name);
    cd_objc2_class_info->methods = NSSwapLong(cd_objc2_class_info->methods);
    cd_objc2_class_info->protocols = NSSwapLong(cd_objc2_class_info->protocols);
    cd_objc2_class_info->ivars = NSSwapLong(cd_objc2_class_info->ivars);
    cd_objc2_class_info->unknown_4 = NSSwapLong(cd_objc2_class_info->unknown_4);
    cd_objc2_class_info->properties = NSSwapLong(cd_objc2_class_info->properties);
    // cd_objc2_class->version = NSSwapLong(cd_objc_class->version);
    // cd_objc2_class->info = NSSwapLong(cd_objc_class->info);
    // cd_objc2_class->instance_size = NSSwapLong(cd_objc_class->instance_size);
    // cd_objc2_class->cache = NSSwapLong(cd_objc_class->cache);
}

void swap_cd_objc2_ivar_list(struct cd_objc2_ivar_list * cd_objc2_ivar_list)
{
     cd_objc2_ivar_list->unknown = NSSwapLong(cd_objc2_ivar_list->unknown);
     cd_objc2_ivar_list->ivar_count = NSSwapLong(cd_objc2_ivar_list->ivar_count);
}

void swap_cd_objc2_ivar(struct cd_objc2_ivar * cd_objc2_ivar)
{
     cd_objc2_ivar->instance_addr = NSSwapLong(cd_objc2_ivar->instance_addr);
     cd_objc2_ivar->name = NSSwapLong(cd_objc2_ivar->name);
     cd_objc2_ivar->type = NSSwapLong(cd_objc2_ivar->type);
     cd_objc2_ivar->unknown = NSSwapLong(cd_objc2_ivar->unknown);
     cd_objc2_ivar->size = NSSwapLong(cd_objc2_ivar->size);
}

#endif

void swap_cd_objc_category(struct cd_objc_category *cd_objc_category)
{
    cd_objc_category->category_name = NSSwapLong(cd_objc_category->category_name);
    cd_objc_category->class_name = NSSwapLong(cd_objc_category->class_name);
    cd_objc_category->methods = NSSwapLong(cd_objc_category->methods);
    cd_objc_category->class_methods = NSSwapLong(cd_objc_category->class_methods);
    cd_objc_category->protocols = NSSwapLong(cd_objc_category->protocols);
}

#ifdef HANDLE_OBJC2_ABI
void swap_cd_objc2_category(struct cd_objc2_category *cd_objc2_category)
{
    cd_objc2_category->category_name = NSSwapLong(cd_objc2_category->category_name);
    cd_objc2_category->class = NSSwapLong(cd_objc2_category->class);
    cd_objc2_category->methods = NSSwapLong(cd_objc2_category->methods);
    cd_objc2_category->class_methods = NSSwapLong(cd_objc2_category->class_methods);
    cd_objc2_category->protocols = NSSwapLong(cd_objc2_category->protocols);
}
#endif

void swap_cd_objc_ivar_list(struct cd_objc_ivar_list *cd_objc_ivar_list)
{
    cd_objc_ivar_list->ivar_count = NSSwapLong(cd_objc_ivar_list->ivar_count);
}

void swap_cd_objc_ivar(struct cd_objc_ivar *cd_objc_ivar)
{
    cd_objc_ivar->name = NSSwapLong(cd_objc_ivar->name);
    cd_objc_ivar->type = NSSwapLong(cd_objc_ivar->type);
    cd_objc_ivar->offset = NSSwapInt(cd_objc_ivar->offset);
}

void swap_cd_objc_method_list(struct cd_objc_method_list *cd_objc_method_list)
{
    cd_objc_method_list->method_count = NSSwapLong(cd_objc_method_list->method_count);
}

#ifdef HANDLE_OBJC2_ABI
void swap_cd_objc2_method_list(struct cd_objc2_method_list *cd_objc2_method_list)
{
    cd_objc2_method_list->unknown = NSSwapLong(cd_objc2_method_list->unknown);
    cd_objc2_method_list->method_count = NSSwapLong(cd_objc2_method_list->method_count);
}
#endif

void swap_cd_objc_method(struct cd_objc_method *cd_objc_method)
{
    cd_objc_method->name = NSSwapLong(cd_objc_method->name);
    cd_objc_method->types = NSSwapLong(cd_objc_method->types);
    cd_objc_method->imp = NSSwapLong(cd_objc_method->imp);
}

#ifdef HANDLE_OBJC2_ABI
void swap_cd_objc2_method(struct cd_objc2_method *cd_objc2_method)
{
    cd_objc2_method->name = NSSwapLong(cd_objc2_method->name);
    cd_objc2_method->types = NSSwapLong(cd_objc2_method->types);
    cd_objc2_method->imp = NSSwapLong(cd_objc2_method->imp);
}
#endif

void swap_cd_objc_protocol_list(struct cd_objc_protocol_list *cd_objc_protocol_list)
{
    cd_objc_protocol_list->next = NSSwapLong(cd_objc_protocol_list->next);
    cd_objc_protocol_list->count = NSSwapLong(cd_objc_protocol_list->count);
}

#ifdef HANDLE_OBJC2_ABI
void swap_cd_objc2_protocol_list(struct cd_objc2_protocol_list *cd_objc2_protocol_list)
{
    cd_objc2_protocol_list->count = NSSwapLong(cd_objc2_protocol_list->count);
}
#endif

void swap_cd_objc_protocol(struct cd_objc_protocol *cd_objc_protocol)
{
    cd_objc_protocol->isa = NSSwapLong(cd_objc_protocol->isa);
    cd_objc_protocol->protocol_name = NSSwapLong(cd_objc_protocol->protocol_name);
    cd_objc_protocol->protocol_list = NSSwapLong(cd_objc_protocol->protocol_list);
    cd_objc_protocol->instance_methods = NSSwapLong(cd_objc_protocol->instance_methods);
    cd_objc_protocol->class_methods = NSSwapLong(cd_objc_protocol->class_methods);
}

#ifdef HANDLE_OBJC2_ABI
void swap_cd_objc2_protocol(struct cd_objc2_protocol *cd_objc2_protocol)
{
    cd_objc2_protocol->all_zero_0 = NSSwapLong(cd_objc2_protocol->all_zero_0);
    cd_objc2_protocol->protocol_name = NSSwapLong(cd_objc2_protocol->protocol_name);
    cd_objc2_protocol->super_protocol_list = NSSwapLong(cd_objc2_protocol->super_protocol_list);
    cd_objc2_protocol->instance_methods = NSSwapLong(cd_objc2_protocol->instance_methods);
    cd_objc2_protocol->class_methods = NSSwapLong(cd_objc2_protocol->class_methods);
    cd_objc2_protocol->all_zero_1 = NSSwapLong(cd_objc2_protocol->all_zero_1);
    cd_objc2_protocol->all_zero_2 = NSSwapLong(cd_objc2_protocol->all_zero_2);
    cd_objc2_protocol->all_zero_3 = NSSwapLong(cd_objc2_protocol->all_zero_3);
    cd_objc2_protocol->unknown = NSSwapLong(cd_objc2_protocol->unknown);
    cd_objc2_protocol->all_zero_4 = NSSwapLong(cd_objc2_protocol->all_zero_4);
}
#endif

void swap_cd_objc_protocol_method_list(struct cd_objc_protocol_method_list *cd_objc_protocol_method_list)
{
    cd_objc_protocol_method_list->method_count = NSSwapLong(cd_objc_protocol_method_list->method_count);
}

void swap_cd_objc_protocol_method(struct cd_objc_protocol_method *cd_objc_protocol_method)
{
    cd_objc_protocol_method->name = NSSwapLong(cd_objc_protocol_method->name);
    cd_objc_protocol_method->types = NSSwapLong(cd_objc_protocol_method->types);
}

@implementation CDObjCSegmentProcessor (Private)

- (void)processModules;
{
    CDSegmentCommand *objcSegment;
    CDSection *moduleSection;
    const void *ptr;
    struct cd_objc_module objcModule;
    int count, index;

    objcSegment = [machOFile segmentWithName:@"__OBJC"];
    moduleSection = [objcSegment sectionWithName:@"__module_info"];

#ifdef HANDLE_OBJC2_ABI
    if ( objcSegment == nil || moduleSection == nil ) {
       // Create dumb CDOCModule 
       CDOCModule * aModule = [[CDOCModule alloc]init];
       [aModule setVersion:0];
       [aModule setName:@"stub"];
       [aModule setSymtab:[self processSymtab2]];
       
       [modules addObject:aModule];
       
       [aModule release];
       
       return;
   }    
#endif
    
    ptr = [moduleSection dataPointer];
    count = [moduleSection size] / sizeof(struct cd_objc_module);
    for (index = 0; index < count; index++, ptr += sizeof(struct cd_objc_module)) {
        CDSegmentCommand *aSegment;
        CDOCModule *aModule;

        objcModule = *(struct cd_objc_module *)ptr;
        if ([machOFile hasDifferentByteOrder] == YES)
            swap_cd_objc_module(&objcModule);

        assert(objcModule.size == sizeof(struct cd_objc_module)); // Because this is what we're assuming.
        aSegment = [machOFile segmentContainingAddress:objcModule.symtab];

        aModule = [[CDOCModule alloc] init];
        [aModule setVersion:objcModule.version];
        [aModule setName:[machOFile stringFromVMAddr:objcModule.name]];
        [aModule setSymtab:[self processSymtab:objcModule.symtab]];
        [modules addObject:aModule];

        [aModule release];
    }
}

- (CDOCSymtab *)processSymtab:(unsigned long)symtab;
{
    CDOCSymtab *aSymtab;

    const void *ptr;
    struct cd_objc_symtab objcSymtab;
    const unsigned long *defs;
    int index, defIndex;

    // TODO: Should we convert to pointer here or in caller?
    ptr = [machOFile pointerFromVMAddr:symtab segmentName:@"__OBJC"];
    if (ptr == NULL) {
        return nil;
    }

    objcSymtab = *(struct cd_objc_symtab *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc_symtab(&objcSymtab);

    aSymtab = [[[CDOCSymtab alloc] init] autorelease];

    defs = (unsigned long *)(ptr + sizeof(struct cd_objc_symtab));
    defIndex = 0;

    if (objcSymtab.cls_def_count > 0) {
        for (index = 0; index < objcSymtab.cls_def_count; index++, defs++, defIndex++) {
            CDOCClass *aClass;

            if ([machOFile hasDifferentByteOrder] == YES)
                aClass = [self processClassDefinition:NSSwapLong(*defs)];
            else
                aClass = [self processClassDefinition:*defs];

            if (aClass != nil)
                [aSymtab addClass:aClass];
        }
    }

    if (objcSymtab.cat_def_count > 0) {
        for (index = 0; index < objcSymtab.cat_def_count; index++, defs++, defIndex++) {
            CDOCCategory *aCategory;

            if ([machOFile hasDifferentByteOrder] == YES)
                aCategory = [self processCategoryDefinition:NSSwapLong(*defs)];
            else
                aCategory = [self processCategoryDefinition:*defs];

            if (aCategory != nil)
                [aSymtab addCategory:aCategory];
        }
    }

    return aSymtab;
}

#ifdef HANDLE_OBJC2_ABI
- (CDOCSymtab *)processSymtab2
{
    CDOCSymtab * aSymtab = [[[CDOCSymtab alloc] init] autorelease];
    
    [self processClassListSection2:aSymtab];
    [self processCategoryListSection2:aSymtab];
    
    return aSymtab;
}

- (void)processClassListSection2:(CDOCSymtab *)aSymtab
{
     CDSegmentCommand * dataSegment = [machOFile segmentWithName:@"__DATA"];
     CDSection * classListSection = [dataSegment sectionWithName:@"__objc_classlist"];
     
     // Entire classlist section are all addresses
     int classCount = [classListSection size] / sizeof(uint32_t);
     unsigned long * ptr = (unsigned long *)[machOFile pointerFromVMAddr:[classListSection addr]];
     
     int i;
     
     for ( i = 0; i < classCount; i++, ptr++ ) 
         [aSymtab addClass:[self processClassDefinition2:(*ptr)]];
}

- (void)processCategoryListSection2:(CDOCSymtab *)aSymtab
{
     CDSegmentCommand * dataSegment = [machOFile segmentWithName:@"__DATA"];
     CDSection * catListSection = [dataSegment sectionWithName:@"__objc_catlist"];
     
     // Entire classlist section are all addresses
     int catCount = [catListSection size] / sizeof(uint32_t);
     unsigned long * ptr = (unsigned long *)[machOFile pointerFromVMAddr:[catListSection addr]];

     int i;     
     for ( i = 0; i < catCount; i++, ptr++ ) 
         [aSymtab addCategory:[self processCategoryDefinition2:(*ptr)]];
}

#endif


- (CDOCClass *)processClassDefinition:(unsigned long)defRef;
{
    const void *ptr;
    struct cd_objc_class objcClass;
    CDOCClass *aClass;
    int index;
    NSString *name;

    ptr = [machOFile pointerFromVMAddr:defRef];
    if (ptr == NULL)
        return nil;

    objcClass = *(struct cd_objc_class *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc_class(&objcClass);

    name = [machOFile stringFromVMAddr:objcClass.name];
    if (name == nil)
        return nil;

    aClass = [[[CDOCClass alloc] init] autorelease];
    [aClass setName:name];
    [aClass setSuperClassName:[machOFile stringFromVMAddr:objcClass.super_class]];

    // Process ivars
    if (objcClass.ivars != 0) {
        ptr = [machOFile pointerFromVMAddr:objcClass.ivars];

        if (ptr != NULL) {
            struct cd_objc_ivar_list ivar_list;
            struct cd_objc_ivar ivar;
            NSMutableArray *ivars;

            ivar_list = *(struct cd_objc_ivar_list *)ptr;
            if ([machOFile hasDifferentByteOrder] == YES)
                swap_cd_objc_ivar_list(&ivar_list);

            ptr += sizeof(struct cd_objc_ivar_list);
            ivars = [[NSMutableArray alloc] init];

            for (index = 0; index < ivar_list.ivar_count; index++, ptr += sizeof(struct cd_objc_ivar)) { // TODO (2005-07-28): Not sure about that increment for ptr2
                NSString *name, *type;

                ivar = *(struct cd_objc_ivar *)ptr;
                if ([machOFile hasDifferentByteOrder] == YES)
                    swap_cd_objc_ivar(&ivar);

                name = [machOFile stringFromVMAddr:ivar.name];
                type = [machOFile stringFromVMAddr:ivar.type];
                // bitfields don't need names.
                // NSIconRefBitmapImageRep in AppKit on 10.5 has a single-bit bitfield, plus an unnamed 31-bit field.
                if (type != nil) {
                    CDOCIvar *anIvar;

                    anIvar = [[CDOCIvar alloc] initWithName:name type:type offset:ivar.offset];
                    [ivars addObject:anIvar];
                    [anIvar release];
                }
            }

            [aClass setIvars:[NSArray arrayWithArray:ivars]];
            [ivars release];
        }
    }

    // Process methods
    [aClass setInstanceMethods:[self processMethods:objcClass.methods]];

    // Process meta class
    {
        ptr = [machOFile pointerFromVMAddr:objcClass.isa];
        if (ptr != NULL) {
            struct cd_objc_class metaClass;

            metaClass = *(struct cd_objc_class *)ptr;
            if ([machOFile hasDifferentByteOrder] == YES)
                swap_cd_objc_class(&metaClass);
            //assert(metaClass.info & CLS_CLASS);

            // Process class methods
            [aClass setClassMethods:[self processMethods:metaClass.methods]];
        }
    }

    // Process protocols
    [aClass addProtocolsFromArray:[self processProtocolList:objcClass.protocols]];

    return aClass;
}

#ifdef HANDLE_OBJC2_ABI

- (CDOCClass *)processClassDefinition2:(unsigned long)classDefAddr;
{
    const void *ptr;
    struct cd_objc2_class objcClass;
    struct cd_objc2_class_info classInfo;
    CDOCClass *aClass;
    // int index;
    NSString *name;

    // Get class
    ptr = [machOFile pointerFromVMAddr:classDefAddr];
    if (ptr == NULL)
        return nil;

    objcClass = *(struct cd_objc2_class *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc2_class(&objcClass);

    // Get super class.
    
    // Search super class in Symtab first. We just need a super class name only so we don't need to get
    // detail information about super. 
	NSString * superNameFound = [[machOFile symbolTable] findSymbolByValue:objcClass.super_class];
	
	// but sometimes super name is not in symbol table...
	if ( superNameFound == nil ) {
	   // It must be an address to cd_objc2_class
	   ptr = [machOFile pointerFromVMAddr:objcClass.super_class];
	   struct cd_objc2_class * pSuperClass = (struct cd_objc2_class *)ptr;
	   ptr = [machOFile pointerFromVMAddr:pSuperClass->class_info];
	   struct cd_objc2_class_info * pSuperClassInfo = (struct cd_objc2_class_info *)ptr;
	   
	   superNameFound = [machOFile stringFromVMAddr:pSuperClassInfo->name];
	}
	
	//
    NSString * superName = [NSString stringWithString:superNameFound];
	
    // ptr = [machOFile pointerFromVMAddr:objcClass.super_class];
    if ( superName != nil ) {
       if ( [superName hasPrefix:@"_OBJC_CLASS_$_"] ) 
          superName = [superName substringFromIndex:14];
/*		
       else
          superName = nil;
*/ 
    }
    
    // Get class information
    ptr = [machOFile pointerFromVMAddr:objcClass.class_info];
    
    classInfo = *(struct cd_objc2_class_info *)ptr;
    if ( [machOFile hasDifferentByteOrder] )
       swap_cd_objc2_class_info(&classInfo);
    
    // name
    name = [machOFile stringFromVMAddr:classInfo.name];
    if (name == nil)
        return nil;

    aClass = [[[CDOCClass alloc] init] autorelease];
    [aClass setName:name];    
    [aClass setSuperClassName:superName];

    // NSLog(@"Class: %@, super is %@\n", name, superName);
    
    // Process ivars
    if (classInfo.ivars != 0) {
        ptr = [machOFile pointerFromVMAddr:classInfo.ivars];

        if (ptr != NULL) {
            struct cd_objc2_ivar_list ivar_list;
            struct cd_objc2_ivar ivar;
            NSMutableArray *ivars;

            ivar_list = *(struct cd_objc2_ivar_list *)ptr;
            if ([machOFile hasDifferentByteOrder] == YES)
                swap_cd_objc2_ivar_list(&ivar_list);

            ptr += sizeof(struct cd_objc2_ivar_list);
            ivars = [[NSMutableArray alloc] init];

            long index;
            long offset = classInfo.size_of_super; // To count ivar offset
            
            for (index = 0; index < ivar_list.ivar_count; index++, ptr += sizeof(struct cd_objc2_ivar)) { // TODO (2005-07-28): Not sure about that increment for ptr2
                NSString *name, *type;

                ivar = *(struct cd_objc2_ivar *)ptr;
                if ([machOFile hasDifferentByteOrder] == YES)
                    swap_cd_objc2_ivar(&ivar);

                name = [machOFile stringFromVMAddr:ivar.name];
                type = [machOFile stringFromVMAddr:ivar.type];
                
                
                // bitfields don't need names.
                // NSIconRefBitmapImageRep in AppKit on 10.5 has a single-bit bitfield, plus an unnamed 31-bit field.
                if (type != nil) {
                    CDOCIvar *anIvar;

                    anIvar = [[CDOCIvar alloc] initWithName:name type:type offset:offset /*ivar.offset*/];
                    
                    // NSLog(@"ivar: %@, type %@\n", name, type);
                    
                    [ivars addObject:anIvar];
                    [anIvar release];
                }
                
                offset += ivar.size;
            }

            [aClass setIvars:[NSArray arrayWithArray:ivars]];
            [ivars release];
        }
    }

    // Process methods
    [aClass setInstanceMethods:[self processMethods2:classInfo.methods]];

    // Process meta class
    {
        ptr = [machOFile pointerFromVMAddr:objcClass.isa];
        if (ptr != NULL) {
            struct cd_objc2_class metaClass;

            metaClass = *(struct cd_objc2_class *)ptr;
            if ([machOFile hasDifferentByteOrder] == YES)
                swap_cd_objc2_class(&metaClass);
            //assert(metaClass.info & CLS_CLASS);

            ptr = [machOFile pointerFromVMAddr:metaClass.class_info];
            
            classInfo = *(struct cd_objc2_class_info *)ptr;
            
            // Process class methods
            [aClass setClassMethods:[self processMethods2:classInfo.methods]];
        }
    }

    // Process protocols
    [aClass addProtocolsFromArray:[self processProtocolList2:classInfo.protocols]];

    return aClass;
}

#endif

- (NSArray *)processProtocolList:(unsigned long)protocolListAddr;
{
    const void *ptr;
    struct cd_objc_protocol_list protocolList;
    const unsigned long *protocolPtrs;
    NSMutableArray *protocols;
    int index;

    protocols = [[[NSMutableArray alloc] init] autorelease];;

    if (protocolListAddr == 0)
        return protocols;

    ptr = [machOFile pointerFromVMAddr:protocolListAddr];
    if (ptr == NULL)
        return nil;

    protocolList = *(struct cd_objc_protocol_list *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc_protocol_list(&protocolList);

    protocolPtrs = ptr + sizeof(struct cd_objc_protocol_list);
    for (index = 0; index < protocolList.count; index++, protocolPtrs++) {
        CDOCProtocol *protocol;

        if ([machOFile hasDifferentByteOrder] == YES)
            protocol = [self processProtocol:NSSwapLong(*protocolPtrs)];
        else
            protocol = [self processProtocol:*protocolPtrs];

        if (protocol != nil)
            [protocols addObject:protocol];
    }

    return protocols;
}

#ifdef HANDLE_OBJC2_ABI
- (NSArray *)processProtocolList2:(unsigned long)protocolListAddr;
{
    const void *ptr;
    struct cd_objc2_protocol_list protocolList;
    const unsigned long *protocolPtrs;
    NSMutableArray *protocols;
    int index;

    protocols = [[[NSMutableArray alloc] init] autorelease];;

    if (protocolListAddr == 0)
        return protocols;

    ptr = [machOFile pointerFromVMAddr:protocolListAddr];
    if (ptr == NULL)
        return nil;

    protocolList = *(struct cd_objc2_protocol_list *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc2_protocol_list(&protocolList);

    protocolPtrs = ((struct cd_objc2_protocol_list *)ptr)->protocols;
    
    for (index = 0; index < protocolList.count; index++, protocolPtrs++) {
        CDOCProtocol *protocol;

        if ([machOFile hasDifferentByteOrder] == YES)
            protocol = [self processProtocol2:NSSwapLong(*protocolPtrs)];
        else
            protocol = [self processProtocol2:*protocolPtrs];

        if (protocol != nil)
            [protocols addObject:protocol];
    }

    return protocols;
}
#endif

- (CDOCProtocol *)processProtocol:(unsigned long)protocolAddr;
{
    const void *ptr;
    struct cd_objc_protocol protocol;
    CDOCProtocol *aProtocol;
    NSString *name;
    NSArray *protocols;

    ptr = [machOFile pointerFromVMAddr:protocolAddr];
    if (ptr == NULL)
        return nil;

    protocol = *(struct cd_objc_protocol *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc_protocol(&protocol);

    name = [machOFile stringFromVMAddr:protocol.protocol_name];
    if (name == nil)
        return nil;

    protocols = [self processProtocolList:protocol.protocol_list];

    aProtocol = [protocolsByName objectForKey:name];
    if (aProtocol == nil) {
        aProtocol = [[[CDOCProtocol alloc] init] autorelease];
        [aProtocol setName:name];

        [protocolsByName setObject:aProtocol forKey:name];
    }

    [aProtocol addProtocolsFromArray:protocols];
    if ([[aProtocol instanceMethods] count] == 0)
        [aProtocol setInstanceMethods:[self processProtocolMethods:protocol.instance_methods]];

    if ([[aProtocol classMethods] count] == 0)
        [aProtocol setClassMethods:[self processProtocolMethods:protocol.class_methods]];

    // TODO (2003-12-09): Maybe we should add any missing methods.  But then we'd lose the original order.

    return aProtocol;
}


#ifdef HANDLE_OBJC2_ABI

- (CDOCProtocol *)processProtocol2:(unsigned long)protocolAddr;
{
    const void *ptr;
    struct cd_objc2_protocol protocol;
    CDOCProtocol *aProtocol;
    NSString *name;
    NSArray *protocols;
    
    ptr = [machOFile pointerFromVMAddr:protocolAddr];
    if (ptr == NULL)
        return nil;

    protocol = *(struct cd_objc2_protocol *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc2_protocol(&protocol);

    name = [machOFile stringFromVMAddr:protocol.protocol_name];
    if (name == nil)
        return nil;

    protocols = [self processProtocolList2:protocol.super_protocol_list];

    // Yes, it may have duplications
    aProtocol = [protocolsByName objectForKey:name];
    if (aProtocol == nil) {
        aProtocol = [[[CDOCProtocol alloc] init] autorelease];
        [aProtocol setName:name];

        [protocolsByName setObject:aProtocol forKey:name];
    }

    [aProtocol addProtocolsFromArray:protocols];
    if ([[aProtocol instanceMethods] count] == 0)
        [aProtocol setInstanceMethods:[self processMethods2:protocol.instance_methods]];

    if ([[aProtocol classMethods] count] == 0)
        [aProtocol setClassMethods:[self processMethods2:protocol.class_methods]];

    // TODO (2003-12-09): Maybe we should add any missing methods.  But then we'd lose the original order.

    return aProtocol;
}

#endif // HANDLE_OBJC_ABI

- (NSArray *)processProtocolMethods:(unsigned long)methodsAddr;
{
    const void *ptr;
    NSMutableArray *methods;
    struct cd_objc_protocol_method_list methodList;
    struct cd_objc_protocol_method method;
    int index;

    methods = [NSMutableArray array];
    if (methodsAddr == 0)
        return methods;

    ptr = [machOFile pointerFromVMAddr:methodsAddr];
    if (ptr == NULL)
        return nil;

    methodList = *(struct cd_objc_protocol_method_list *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc_protocol_method_list(&methodList);

    ptr += sizeof(struct cd_objc_protocol_method_list);

    for (index = 0; index < methodList.method_count; index++, ptr += sizeof(struct cd_objc_protocol_method)) {
        NSString *name, *type;

        method = *(struct cd_objc_protocol_method *)ptr;
        if ([machOFile hasDifferentByteOrder] == YES)
            swap_cd_objc_protocol_method(&method);

        name = [machOFile stringFromVMAddr:method.name];
        type = [machOFile stringFromVMAddr:method.types];
        if (name != nil && type != nil) {
            CDOCMethod *aMethod;

            aMethod = [[CDOCMethod alloc] initWithName:name type:type imp:0];
            [methods addObject:aMethod];
            [aMethod release];
        }
    }

    return [methods reversedArray];
}

#ifdef HANDLE_OBJC2_ABI
- (NSArray *)processMethods2:(unsigned long)methodsAddr;
{
    const void *ptr;
    NSMutableArray *methods;
    struct cd_objc2_method_list methodList;
    struct cd_objc2_method method;
    int index;

    methods = [NSMutableArray array];
    if (methodsAddr == 0)
        return methods;

    ptr = [machOFile pointerFromVMAddr:methodsAddr];
    if (ptr == NULL)
        return nil;

    methodList = *(struct cd_objc2_method_list *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc2_method_list(&methodList);

    for (index = 0; index < methodList.method_count; index++) {
        NSString *name, *type;

        method = ((struct cd_objc2_method_list *)ptr)->methods[index]; 
        if ([machOFile hasDifferentByteOrder] == YES)
            swap_cd_objc2_method(&method);

        name = [machOFile stringFromVMAddr:method.name];
        type = [machOFile stringFromVMAddr:method.types];
        if (name != nil && type != nil) {
            CDOCMethod *aMethod;

            aMethod = [[CDOCMethod alloc] initWithName:name type:type imp:method.imp];
            [methods addObject:aMethod];
            [aMethod release];
        }
    }

    return [methods reversedArray];
}
#endif

- (NSArray *)processMethods:(unsigned long)methodsAddr;
{
    const void *ptr;
    NSMutableArray *methods;
    struct cd_objc_method_list methodList;
    struct cd_objc_method method;
    int index;

    methods = [NSMutableArray array];
    if (methodsAddr == 0)
        return methods;

    ptr = [machOFile pointerFromVMAddr:methodsAddr];
    if (ptr == NULL)
        return nil;

    methodList = *(struct cd_objc_method_list *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc_method_list(&methodList);

    ptr += sizeof(struct cd_objc_method_list);

    for (index = 0; index < methodList.method_count; index++, ptr += sizeof(struct cd_objc_method)) {
        NSString *name, *type;

        method = *(struct cd_objc_method *)ptr;
        if ([machOFile hasDifferentByteOrder] == YES)
            swap_cd_objc_method(&method);

        name = [machOFile stringFromVMAddr:method.name];
        type = [machOFile stringFromVMAddr:method.types];
        if (name != nil && type != nil) {
            CDOCMethod *aMethod;

            aMethod = [[CDOCMethod alloc] initWithName:name type:type imp:method.imp];
            [methods addObject:aMethod];
            [aMethod release];
        }
    }

    return [methods reversedArray];
}

- (CDOCCategory *)processCategoryDefinition:(unsigned long)defRef;
{
    const void *ptr;
    struct cd_objc_category objcCategory;
    NSString *name;
    CDOCCategory *aCategory;

    ptr = [machOFile pointerFromVMAddr:defRef];
    if (ptr == NULL)
        return nil;

    objcCategory = *(struct cd_objc_category *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc_category(&objcCategory);

    name = [machOFile stringFromVMAddr:objcCategory.category_name];
    if (name == nil)
        return nil;

    aCategory = [[[CDOCCategory alloc] init] autorelease];
    [aCategory setName:name];
    [aCategory setClassName:[machOFile stringFromVMAddr:objcCategory.class_name]];

    // Process methods
    [aCategory setInstanceMethods:[self processMethods:objcCategory.methods]];
    [aCategory setClassMethods:[self processMethods:objcCategory.class_methods]];

    // Process protocols
    [aCategory addProtocolsFromArray:[self processProtocolList:objcCategory.protocols]];

    return aCategory;
}

#ifdef HANDLE_OBJC2_ABI
- (CDOCCategory *)processCategoryDefinition2:(unsigned long)defRef;
{
    const void *ptr;
    struct cd_objc2_category objcCategory;
    NSString *name;
    CDOCCategory *aCategory;

    //
    ptr = [machOFile pointerFromVMAddr:defRef];
    if (ptr == NULL)
        return nil;

    objcCategory = *(struct cd_objc2_category *)ptr;
    if ([machOFile hasDifferentByteOrder] == YES)
        swap_cd_objc2_category(&objcCategory);

    // name
    name = [machOFile stringFromVMAddr:objcCategory.category_name];
    if (name == nil)
        return nil;

    // class - We just need name
	NSString * categoryClassNameFound = [[machOFile symbolTable] findSymbolByValue:objcCategory.class];
	// Sometimes we have to dig into class definitions
	if ( categoryClassNameFound == nil ) {
	   ptr = [machOFile pointerFromVMAddr:objcCategory.class];
	   struct cd_objc2_class * pCategoryClass = (struct cd_objc2_class *)ptr;
	   ptr = [machOFile pointerFromVMAddr:pCategoryClass->class_info];
	   struct cd_objc2_class_info * pCategoryClassInfo = (struct cd_objc2_class_info *)ptr;
	   categoryClassNameFound = [machOFile stringFromVMAddr:pCategoryClassInfo->name];
	}
	
    NSString * class_name = [NSString stringWithString:categoryClassNameFound];
	
    if ( class_name != nil ) {
       if ( [class_name hasPrefix:@"_OBJC_CLASS_$_"] )
          class_name = [class_name substringFromIndex:14];
/*		
       else
          class_name = nil;
*/ 
    }
    
    // Set CDOCCategory
    aCategory = [[[CDOCCategory alloc] init] autorelease];
    [aCategory setName:name];
    [aCategory setClassName:class_name];

    // Process methods
    [aCategory setInstanceMethods:[self processMethods2:objcCategory.methods]];
    [aCategory setClassMethods:[self processMethods2:objcCategory.class_methods]];

    // Process protocols
    [aCategory addProtocolsFromArray:[self processProtocolList2:objcCategory.protocols]];

    return aCategory;
}
#endif

// Protocols can reference other protocols, so we can't try to create them
// in order.  Instead we create them lazily and just make sure we reference
// all available protocols.

- (void)processProtocolSection;
{
    CDSegmentCommand *objcSegment;
    CDSection *protocolSection;
    unsigned long addr;
    CDOCProtocol *aProtocol;
    int count, index;

    objcSegment = [machOFile segmentWithName:@"__OBJC"];
    protocolSection = [objcSegment sectionWithName:@"__protocol"];

#ifdef HANDLE_OBJC2_ABI
    // For objective-C 2.0 ABI. no __OBJC segment there, and contents in __protocol are moved to __DATA segment, __objc_protolist section
    if ( objcSegment == nil ) {
       objcSegment = [machOFile segmentWithName:@"__DATA"];
       protocolSection = [objcSegment sectionWithName:@"__objc_protolist"];
       
       addr = [protocolSection addr];
       
       // __objc_protolost are all addresses, which point to cd_objc2_protocol structures
       count = [protocolSection size] / sizeof(uint32_t);
       
       for ( index = 0; index < count; index++, addr += sizeof(uint32_t) ) {
       
           const void * ptr = [machOFile pointerFromVMAddr:addr];
           if (ptr == NULL)
              continue;

           unsigned long protocolAddr = *(unsigned long *)ptr;    
           if ( [machOFile hasDifferentByteOrder] )
              protocolAddr = NSSwapLong(protocolAddr);
              
           aProtocol = [self processProtocol2:protocolAddr];
           
        }
        
        return;
    }
#endif    
    
    addr = [protocolSection addr];

    count = [protocolSection size] / sizeof(struct cd_objc_protocol);
    for (index = 0; index < count; index++, addr += sizeof(struct cd_objc_protocol))
        aProtocol = [self processProtocol:addr];
        
}


- (void)checkUnreferencedProtocols;
{
    NSLog(@" > %s", _cmd);
    NSLog(@"<  %s", _cmd);
}

@end
