         /*
 * Borland doesnt seem to use threads so I won't support them.
 * Also, since we are doing the flat model all segment and group relative
 * fixups are done relative to the virtual base, so we just ignore them
 */
#include <stdio.h>
#include "common.h"
#include "module.h"
#include "error.h"
#include "fixup.h"
#include "segment.h"
#include "public.h"
#include "extern.h"
#include "virdef.h"

extern uint prm_base;
extern SEGMENT *fixupseg;
extern uint fixupbase;

BYTE *exebuffer;		/* exe program file buffer */

// Error if threads detected

static void BadThread(char *name)
{
  fatal("Fixup threads not supported in module %s",name);
}
// Error if fixup overflow
static void FixupError(char *name)
{
	Error("Fixup overflow in module %s",name);
}
// Read a fixup record.  Skip the locat bytes if this is modend field
int ReadFixupRecord(BOOL is32, BYTE **buffer, int *size, char *name, 
		uint *address, uint *tsize, BOOL modend)
{
  uint locat1, locat2, location, offset, fixupbyte, framemethod, targetmethod;
  int displacement = 0, target=0, targetindex, targetVIRDEF;
	BOOL segrelative;

  *tsize = 0;
  // Read location bytes
  if (!modend ) {
    locat1 = ReadByte(buffer,size);
    locat2 = ReadByte(buffer,size);
    if (!(locat1 & FS_FIXUP))
      BadThread(name);
  }
  
  // Read the locat fields
  segrelative = locat1 & FS_SEGRELATIVE;
  location = FS_LOCATION(locat1);
  offset = FS_OFFSET(locat1,locat2);

  // Read the fixup byte and fields
  fixupbyte = ReadByte(buffer,size);
  if (fixupbyte & (FI_FRAMEABSENT | FI_TARGETABSENT))
    BadThread(name);

  framemethod = FI_FRAME(fixupbyte);
  targetmethod = FI_TARGET(fixupbyte);

  // Read frame and target indices
  if (framemethod < 3)
    ReadIndex(buffer, size);
  targetindex = ReadByte(buffer, size);
	if (targetindex == VIRDEFSEG) {
		targetindex = 0;
		targetVIRDEF = ReadIndex(buffer, size);
	}
	else {
		(*buffer)--;
		(*size)++;
		targetindex = ReadIndex(buffer,size);
	}

  // Read target displacement
  if (!(targetmethod & 4))
    displacement = ReadSizedValue(buffer, size, is32);
  
  // If not a modend record set up the address to fix up
  if (!modend) {
      *address = fixupbase + offset;
	}

  // Ignore the frame type, this is virtual
  // Go on to target method
	switch(targetmethod) {
		case TT_GRPDEF:
		case TT_GRPONLY:
			fatal("Group-relative fixups not supported in module %s",name);
		case TT_SEGDEF:
		case TT_SEGONLY:
			{
				// Patch in case VIRDEF used in place of segment
				if (targetindex == 0) {
					PUBLIC *p = GetExtern(targetVIRDEF);
					target = p->absoffset + displacement;
				}
				else {
				  SEGMENT *p = GetSegment(targetindex);
				  target = p->absoffset  + displacement;
				}
			}
			break;
		case TT_EXTDEF:
		case TT_EXTONLY:
			{
				PUBLIC *p = GetExtern(targetindex);
				if (p)
				  target = p->absoffset + displacement;
			}
			break;
		default:
			fatal("Undefined fixup target reference in module %s", name);
	}

  // Modify the target to be self-relative if the segrelative bit not set
  if (!modend)
    switch(location) {
	  	case LO_SELECT:
		  case LO_FARPTR16:
      case LO_HIGHORDER:
		  case LO_FARPTR32:
		   fatal("Fixup location type not supported in module %s",name);
      case LO_8BIT:
	  		*tsize = 1;
		  	if (!segrelative) {
			  	target = target - *address -1;
			    if ((target > 127) || (target < -128))
				    FixupError(name);
  			}
        else
		  		if (target > 255)
			  		FixupError(name);
  			break;
			
      case LO_LOADER16:
		  case LO_16BIT:
			  *tsize = 2;
  			if (!segrelative) {
	  			target = target - *address -2;
			    if ((target > 16383) || (target < -16384))
		  		  FixupError(name);
	  		}
        else
			  	if (target > 65535)
		  			FixupError(name);
	  		break;
  		case LO_LOADER32:
		  case LO_32BIT:
			  *tsize = 4;
		  	if (!segrelative)
	  			target = target - *address -4;
  			break;
		  default:
		   fatal("Undefined fixup record type in module %s",name);
	  }
  // Return the target
  return(target);
}
// Read OMF fixups record
void ReadFixups(BOOL is32,BYTE *buffer, int size, char *name, uint pass)
{
  // Quit if pass 1
  if (pass == 1) return;
  if (fixupseg == 0) return;
  // While something in the record do it
	while(size > 0) {
    uint address, tsize=0;
		int target;
    BYTE *position = exebuffer;
		target = ReadFixupRecord(is32, &buffer, &size, name, &address, &tsize,FALSE);
    position +=address-prm_base;
#ifdef DEBUG
    {
      uint ttarget=target;
		  switch(tsize) {
			  case 1:
				  ttarget+=*position;
				  break;
			  case 2:
					
				  ttarget +=*((ushort *)position) ;
				  break;
			  case 4:
				  ttarget +=*((uint*)position) ;
				  break;
		  }
		  printf("Fixup module %s segment %s: address %x, type %x, target %x\n",
			  name,fixupseg->segname, address, tsize, ttarget);
		}
#endif
		switch(tsize) {
			case 1:
				*position+=(BYTE)target;
				break;
			case 2:
				*((ushort *)position) += (ushort)target;
				break;
			case 4:
				*((uint*)position) += (uint) target;
				break;
		}
  }
  // Make sure we have read the whole record and nothing more
  CheckSize(size);
}