//  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 "CDSegmentCommand.h"

#include <mach-o/swap.h>
#import <Foundation/Foundation.h>
#import "CDFatFile.h"
#import "CDMachOFile.h"
#import "CDSection.h"

@implementation CDSegmentCommand

- (id)initWithPointer:(const void *)ptr machOFile:(CDMachOFile *)aMachOFile;
{
    char buf[17];

    if ([super initWithPointer:ptr machOFile:aMachOFile] == nil)
        return nil;

    segmentCommand = *(struct segment_command *)ptr;
    if ([aMachOFile hasDifferentByteOrder] == YES)
        swap_segment_command(&segmentCommand, CD_THIS_BYTE_ORDER);

    memcpy(buf, segmentCommand.segname, 16);
    buf[16] = 0;
    name = [[NSString alloc] initWithBytes:buf length:strlen(buf) encoding:NSASCIIStringEncoding];
    // Is segmentCommand->segname always going to be NULL terminated?
    //name = [[NSString alloc] initWithBytes:segmentCommand->segname length:strlen(segmentCommand->segname) encoding:NSASCIIStringEncoding];

    [self _processSectionsWithPointer:ptr + sizeof(struct segment_command)];

    return self;
}

- (void)_processSectionsWithPointer:(const void *)ptr;
{
    NSMutableArray *sects;
    int count, index;

    // PRECONDITION: sections == nil
    // POSTCONDITION: sections != nil

    sects = [[NSMutableArray alloc] init];

    count = segmentCommand.nsects;
    for (index = 0; index < count; index++) {
        CDSection *section;

        section = [[CDSection alloc] initWithPointer:ptr segment:self];
        [sects addObject:section];
        [section release];

        ptr += sizeof(struct section);
    }

    sections = [[NSArray alloc] initWithArray:sects];
    [sects release];
}

- (void)dealloc;
{
    [name release];
    [sections release];

    [super dealloc];
}

- (NSString *)name;
{
    return name;
}

- (unsigned long)vmaddr;
{
    return segmentCommand.vmaddr;
}

- (unsigned long)fileoff;
{
    return segmentCommand.fileoff;
}

- (unsigned long)flags;
{
    return segmentCommand.flags;
}

- (NSArray *)sections;
{
    return sections;
}

- (BOOL)isProtected;
{
    return (segmentCommand.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1;
}

- (NSString *)flagDescription;
{
    NSMutableArray *setFlags;
    unsigned long flags;

    setFlags = [NSMutableArray array];
    flags = [self flags];
    if (flags & SG_HIGHVM)
        [setFlags addObject:@"HIGHVM"];
    if (flags & SG_FVMLIB)
        [setFlags addObject:@"FVMLIB"];
    if (flags & SG_NORELOC)
        [setFlags addObject:@"NORELOC"];
    if (flags & SG_PROTECTED_VERSION_1)
        [setFlags addObject:@"PROTECTED_VERSION_1"];

    if ([setFlags count] == 0)
        return @"(none)";

    return [setFlags componentsJoinedByString:@" "];
}

- (NSString *)extraDescription;
{
    return [NSString stringWithFormat:@"name: '%@', vmaddr: 0x%08x - 0x%08x [0x%08x], offset: %d, flags: 0x%x (%@), nsects: %d, sections: %@",
                     name, segmentCommand.vmaddr, segmentCommand.vmaddr + segmentCommand.vmsize - 1, segmentCommand.vmsize, segmentCommand.fileoff,
                     [self flags], [self flagDescription], segmentCommand.nsects, sections];
}

// TODO (2003-12-06): Might want to make this a range.
- (BOOL)containsAddress:(unsigned long)vmaddr;
{
    return (vmaddr >= segmentCommand.vmaddr) && (vmaddr < segmentCommand.vmaddr + segmentCommand.vmsize);
}

- (CDSection *)sectionContainingVMAddr:(unsigned long)vmaddr;
{
    int count, index;

    count = [sections count];
    for (index = 0; index < count; index++) {
        CDSection *aSection;

        aSection = [sections objectAtIndex:index];
        if ([aSection containsAddress:vmaddr] == YES)
            return aSection;
    }

    return nil;
}

- (unsigned long)segmentOffsetForVMAddr:(unsigned long)vmaddr;
{
    CDSection *section;

    section = [self sectionContainingVMAddr:vmaddr];
    NSLog(@"section: %@", section);

    return [section segmentOffsetForVMAddr:vmaddr];
}

- (CDSection *)sectionWithName:(NSString *)aName;
{
    int count, index;

    count = [sections count];
    for (index = 0; index < count; index++) {
        CDSection *aSection;

        aSection = [sections objectAtIndex:index];
        if ([[aSection sectionName] isEqual:aName] == YES)
            return aSection;
    }

    return nil;
}

- (void)appendToString:(NSMutableString *)resultString verbose:(BOOL)isVerbose;
{
    [super appendToString:resultString verbose:isVerbose];

    [resultString appendFormat:@"  segname %@\n", [self name]];
    [resultString appendFormat:@"   vmaddr 0x%08x\n", segmentCommand.vmaddr];
    [resultString appendFormat:@"   vmsize 0x%08x\n", segmentCommand.vmsize];
    [resultString appendFormat:@"  fileoff %d\n", segmentCommand.fileoff];
    [resultString appendFormat:@" filesize %d\n", segmentCommand.filesize];
    [resultString appendFormat:@"  maxprot 0x%08x\n", segmentCommand.maxprot];
    [resultString appendFormat:@" initprot 0x%08x\n", segmentCommand.initprot];
    [resultString appendFormat:@"   nsects %d\n", segmentCommand.nsects];

    if (isVerbose)
        [resultString appendFormat:@"    flags %@\n", [self flagDescription]];
    else
        [resultString appendFormat:@"    flags 0x%x\n", segmentCommand.flags];
}

@end
