#import "RelocManager.h"
#import <stdlib.h>
#import <string.h>
#import <mach-o/loader.h>
#import <libc.h>

int slideWarpM = 0;

@implementation RelocManager

-init
{
    rmFlags.invalid = YES;
    rmFlags.shouldSortRelocs = YES;
    return self;
}

-free
{
    [self invalidate];
    return [super free];
}

int compareRelocsSort(const void *v1, const void *v2)
{
    const Reloc *r1 = v1, *r2 = v2;
    pointer_t a1 = r1->address, a2 = r2->address;
    return a1 < a2 ? -1 : (a1 == a2 ? 0 : 1);
}

int compareRelocsSearch(const void *v1, const void *v2)
{
    const Reloc *r1 = v1, *r2 = v2;
    pointer_t address = r1->address;
    return address < r2->address ? -1 : (address >= r2->maxAddress ? 1 : 0);
}

-(Reloc *)relocFor: (const void *)pointer
{
    Reloc *reloc;
    int count;
    BOOL found, tooFar;
    if (rmFlags.invalid)
	[self readInAllRelocs];
    if (lastReloc
	&& (lastReloc->address <= (pointer_t)pointer) 
	&& ((pointer_t)pointer < (lastReloc->maxAddress)))
	return lastReloc;
    else {
#if 1 
	Reloc keyReloc;
	keyReloc.address = (pointer_t)pointer;
	if (reloc = bsearch(&keyReloc, relocs, numRelocs, relocSize, compareRelocsSearch))
	    found = YES;
	else
	    found = NO;
#else
	reloc = relocs;
	for (count = numRelocs, found = NO, tooFar = NO;
	     count && !found && !tooFar; count--) {
	    if ((pointer_t)pointer < (reloc->maxAddress)) {
		if (reloc->address <= (pointer_t)pointer)
		    found = YES;
		else
		    tooFar = YES;
	    } else
		((void *)reloc) += relocSize;
	}
#endif
	if (found && (reloc->rFlags.readIn || [self readInReloc: reloc])) {
	    lastReloc = reloc;
	    return reloc;
	} else
	    return NULL;
    }
}

-(BOOL)readInReloc: (Reloc *)reloc
{
    [self subclassResponsibility: _cmd];
    return NO;
}

-(void)_readInAllRelocs
{
    [self subclassResponsibility: _cmd];
}

-(void)readInAllRelocs
{
    [self _readInAllRelocs];
    if (rmFlags.shouldSortRelocs)
	qsort(relocs, numRelocs, relocSize, compareRelocsSort);
}

-invalidate
{
    if (rmFlags.invalid)
	return self;
    else {
	if (relocs) {
	    free(relocs); relocs = NULL;
	}
	numRelocs = 0;
	lastReloc = NULL;
	rmFlags.invalid = YES;
	return self;
    }
}

-(Reloc *)oldRelocFor: (const void *)pointer
{
   int count;
   BOOL found;
   Reloc *reloc;
    if (lastReloc
	&& (lastReloc->data <= (pointer_t)pointer) 
	&& ((pointer_t)pointer < lastReloc->maxData))
	return lastReloc;
    else {
	reloc = relocs;
	for (count = numRelocs, found = NO; count && !found; count--) {
	    if (reloc->rFlags.readIn && (reloc->data <= (pointer_t)pointer) 
		&& ((pointer_t)pointer < reloc->maxData))
		found = YES;
	    else
		((void *)reloc) += relocSize;
	}
	if (found) {
	    lastReloc = reloc;
	    return reloc;
	} else
	    return NULL;
    }
}

-(void *)originalPointerFor: (const void *)pointer
{	
    Reloc *reloc = [self oldRelocFor: pointer];
    if (reloc)
	return (void *)(reloc->address + (pointer - reloc->data));
    else 
	return NULL;
}

-(void *)pointerFor: (const void *)pointer
{
    Reloc *reloc;
    if (pointer && (reloc = [self  relocFor: pointer])) 
	return (void *)((pointer_t)pointer + reloc->displacement);
    else
	return NULL;
}

-(char *)pointerForString: (const char *)pointer isNullTerminated: (BOOL *)isNT
{
    char *retPointer;
    Reloc *reloc;
    char *string;
    if (pointer && (reloc = [self relocFor: pointer])) {
    	retPointer = (char *)((pointer_t)pointer + reloc->displacement);
	for (string = retPointer;
	     *string && ((pointer_t)string < reloc->maxData);
	     string++);
	*isNT = *string ? NO : YES;
	return retPointer;
    } else{
	*isNT = NO;
	return NULL;
    }
}

-(char *)pointerForString: (const char *)string
{
    BOOL isNT;
    char *retPointer;
    if ((retPointer = [self pointerForString: string isNullTerminated: &isNT])
	&& isNT)
	return retPointer;
    else
	return NULL;
}

-(void *)pointerFor: (const void *)pointer withSize: (int)size
{
    Reloc *reloc;
    pointer_t newPointer;
    if (pointer && (reloc = [self relocFor: pointer])) {
	newPointer = (pointer_t)pointer + reloc->displacement;
	if ((newPointer + size) <= reloc->maxData)
	    return (void *)newPointer;
	else
	    return NULL;
    } else
	return NULL;
}

-pointerForID: (const id)pointer
{
    Class theClass;
    struct _ObjectID {
	@defs(Object)
    } *newID;
    
    if (pointer
        && (newID = [self pointerFor: pointer withSize: sizeof(id)])
	&& (theClass = [self pointerFor: newID->isa
	                       withSize: sizeof(Class)])
	&& (newID
	    = [self pointerFor: pointer withSize: theClass->instance_size]))
	return (id)newID;
    else
	return nil;
}
		    
-(int)getDataAt: (const void *)start for: (int)numBytes into: (void *)data
{
    Reloc *reloc = [self relocFor: start];
    int numBytesInReloc;
    
    if (reloc) {
        numBytesInReloc = reloc->maxAddress - (int)start;
        if (numBytes > numBytesInReloc)
	    numBytes = numBytesInReloc;
	memcpy(data,
	       (void *)(reloc->data + ((pointer_t)start - reloc->address)),
	       numBytes);
	return numBytes;
    } else
	return 0;
}

-(struct mach_header *)getMachHeader
{
    [self subclassResponsibility: _cmd];
    return NULL;
}

-(unsigned)getSlide
{
    [self subclassResponsibility: _cmd];
    return 0;
}

-(int)getNumMachHeaders
{
    [self subclassResponsibility: _cmd];
    return 0;
}

-(struct mach_header **)getMachHeaders
{
    [self subclassResponsibility: _cmd];
    return NULL;
}

-(unsigned*)getSlides
{
    [self subclassResponsibility: _cmd];
    return NULL;
}

-(struct mach_header **)getMachHeadersWithNames: (char ***)names
{
    [self subclassResponsibility: _cmd];
    return NULL;
}

-(unsigned*)getSlidesWithNames: (char ***)names
{
    [self subclassResponsibility: _cmd];
    return NULL;
}

-(void *)getSectData: (STR)segName 
	     section: (STR)sectName 
		size: (int *)pSize 
	   forHeader: (struct mach_header *)header
           withSlide: (unsigned)slide
{
    void *data = getsectdatafromheader(header, segName, sectName, pSize);
    return [self pointerFor: (void*) ((char*) data + slide) withSize: *pSize];
}

-(void *)getSectData: (STR)segName 
	     section: (STR)sectName 
		size: (int *)pSize 
{
    return [self getSectData: segName 
		     section: sectName 
			size: pSize 
		   forHeader: [self getMachHeader]
	           withSlide: [self getSlide]];
}

-(const struct section *)getSeg: (STR)segName sect: (STR)sectName
{
    return getsectbynamefromheader([self getMachHeader], segName, sectName);
}

-(const struct section *)firstSection
{
    struct mach_header *header = [self getMachHeader];
    int i;
    struct load_command *loadCmd;
    
    for (i = 0, loadCmd = (struct load_command *)(header + 1);
	 i < header->ncmds;
	 i++, ((void *)loadCmd) += loadCmd->cmdsize) {
	if (loadCmd->cmd == LC_SEGMENT) {
	    struct segment_command *segCmd = (struct segment_command *)loadCmd;
	    if (segCmd->nsects)
		return (struct section *)(segCmd + 1);
	}
    }
    return NULL;
}
	    
-(unsigned)getMaximumAddressForHeader: (struct mach_header *)header withSlide:(unsigned)slide
{
    int i;
    struct load_command *loadCmd;
    unsigned maxAddr = 0;
    
    for (i = 0, loadCmd = (struct load_command *)(header + 1);
	 i < header->ncmds;
	 i++, ((void *)loadCmd) += loadCmd->cmdsize) {
	if (loadCmd->cmd == LC_SEGMENT) {
	    struct segment_command *segCmd = (struct segment_command *)loadCmd;
	    if ((segCmd->vmaddr + segCmd->vmsize + slide) > maxAddr)
		maxAddr = segCmd->vmaddr + segCmd->vmsize;
	}
    }
    return maxAddr;
}

-(struct segment_command *)getSegment: (const char *)segName
{
    int i;
    struct load_command *loadCmd;
    struct segment_command *foundSeg = NULL;
    struct mach_header *header = [self getMachHeader];
    
    for (i = 0, loadCmd = (struct load_command *)(header + 1);
	 i < header->ncmds && !foundSeg;
	 i++, ((void *)loadCmd) += loadCmd->cmdsize) {
	if (loadCmd->cmd == LC_SEGMENT) {
	    struct segment_command *segCmd = (struct segment_command *)loadCmd;
	    if (strcmp(segName, segCmd->segname) == 0)
		foundSeg = segCmd;
	}
    }
    return foundSeg;
}

-(unsigned)getMaximumAddressForSegment: (const char *)segName
{
    int i;
    struct load_command *loadCmd;
    unsigned maxAddr = 0;
    struct mach_header *header = [self getMachHeader];
    unsigned int slide = [self getSlide];
    
    for (i = 0, loadCmd = (struct load_command *)(header + 1);
	 i < header->ncmds;
	 i++, ((void *)loadCmd) += loadCmd->cmdsize) {
	if (loadCmd->cmd == LC_SEGMENT) {
	    struct segment_command *segCmd = (struct segment_command *)loadCmd;
	    struct section *section;
	    int nSects;
	    
	    for (nSects = segCmd->nsects,
	         section = (struct section *)(segCmd + 1);
	         nSects;
		 nSects--, section++) {
		if (strcmp(section->segname, segName) == 0) {
		    if ((section->addr + section->size + slide) > maxAddr)
			maxAddr = (section->addr + section->size + slide);
		}
	    }
	}
    }
    return maxAddr;
}

-(unsigned)getMaximumAddress
{
    return [self getMaximumAddressForHeader: [self getMachHeader]
		 withSlide: [self getSlide]];
}

-(unsigned)getMinimumAddressForSegment: (const char*)segName
{
  int i;
  struct load_command *loadCmd;
  unsigned minAddr = 0xffffffffU;
  struct mach_header *header = [self getMachHeader];
  unsigned int slide = [self getSlide];
    
  for (i = 0, loadCmd = (struct load_command *)(header + 1);
       i < header->ncmds;
       i++, ((void *)loadCmd) += loadCmd->cmdsize) {
    if (loadCmd->cmd == LC_SEGMENT) {
      struct segment_command *segCmd = (struct segment_command *)loadCmd;
      struct section *section;
      int nSects;
	    
      for (nSects = segCmd->nsects,
	   section = (struct section *)(segCmd + 1);
	   nSects;
	   nSects--, section++) {
	if (strcmp(section->segname, segName) == 0) {
	  if ((section->addr + slide) < minAddr)
	    minAddr = (section->addr + slide);
	}
      }
    }
  }
  
  return minAddr;
}

-(unsigned)getMaximumTextAddress
{
    return [self getMaximumAddressForSegment: "__TEXT"];
}

-(unsigned)getMaximumDataAddress
{
    return [self getMaximumAddressForSegment: "__DATA"];
}

@end
