/* $Id: LX.CPP,v 1.4 1999-07-07 08:11:10 sandervl Exp $ */

/*
 * PE2LX LX code
 *
 * Copyright 1998 Sander van Leeuwen (sandervl@xs4all.nl)
 * Copyright 1998 Knut St. Osmundsen
 * Copyright 1998 Peter Fitzsimmons
 *
 *
 * Project Odin Software License can be found in LICENSE.TXT
 *
 */
#define INCL_DOSFILEMGR          /* File Manager values      */
#define INCL_DOSERRORS           /* DOS Error values         */
#define INCL_DOSPROCESS          /* DOS Process values       */
#define INCL_DOSMISC             /* DOS Miscellanous values  */
#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream.h>
#include <string.h>
#include <assert.h>
#include "lx.h"
#include "misc.h"   /*PLF Wed  98-03-18 02:00:21*/
#include <versionos2.h>

#define ORD_REGISTERRESOURCEINFO        1203
#define ORD_KERNEL32DLLEXITLIST         1208
#define ORD_REGISTERDLL                 1209

#define ZERO(a)         (memset(&a, 0, sizeof(a)))

#ifndef max /*PLF Sat  97-06-21 22:36:14*/
#define max(a,b)  ((a>b) ? (a) : (b))
#endif

#ifndef min
#define min(a,b)  ((a<b) ? (a) : (b))
#endif

//In pe.cpp
char *hex(ULONG num);

LXHeader OS2Exe;

//Borrowed dos header from executables generated by Watcom
static unsigned char szDosHeader[128] = {
        0x4D, 0x5A, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
        0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
        0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x74, 0x68,
        0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6E, 0x20, 0x4F, 0x53, 0x2F, 0x32, 0x20, 0x33, 0x32,
        0x2D, 0x62, 0x69, 0x74, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x0D,
        0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

//Register module in kernel32 (resource managment)
// EXE:
//push  [esp+8]			;reserved (to use same startup code as dll)
//push  [esp+8]                 ;instance handle
//push  internal pe2lx version
//push  version resource id
//push  name_lookup_tableid
//push  orgres_lookup_tableid
//push  tib_object_address
//push  tib_object_size
//push  tib_index_address
//push  tib_callback_address
//push  original_entrypoint
//mov   ecx, KERNEL32:RegisterResourceInfo (stdcall)
//call  ecx
//ret
//
// DLL:
//push  [esp+8]			;attach or detach
//push  [esp+8]                 ;instance handle
//push  internal pe2lx version
//push  version resource id
//push  name_lookup_tableid
//push  orgres_lookup_tableid
//push  tib_object_address
//push  tib_index_address
//push  tib_callback_address
//mov   ecx, KERNEL32:RegisterDll (stdcall)
//push  original_entrypoint
//call  ecx
//ret

//SvL: 18-7-'98: offsets for internal pe2lx version & version resource id
#define EXE_OFF_PE2LXVER     (9)
#define EXE_OFF_VERRESID     (14)
#define EXE_OFF_NAMETABLE    (19)
#define EXE_OFF_ORGRESTABLE  (24)
#define EXE_OFF_TIBOBJ_ADDR  (29)
#define EXE_OFF_TIBINIT_SIZE (34)
#define EXE_OFF_TIBOBJ_SIZE  (39)
#define EXE_OFF_TIB_INDEX    (44)
#define EXE_OFF_TIB_CALLBACK_ADDR  (49)
#define EXE_OFF_ENTRYPOINT   (54)
#define EXE_OFF_ENTRYFIX     (59)
#define EXE_TIB_SIZE         sizeof(szEXETIBFix)

#define SIZE_TIBCALL         5

#define EXTRA_FIXUPS         1  //extra kernel32 import
#define EXTRA_TLS_FIXUPS     3  //3 data fixups for TLS support (start, index & callback)

static unsigned char szEXETIBFix[] = {
//push  [esp+8]
        0xFF, 0x74, 0x24, 0x08,
//push  [esp+4]
        0xFF, 0x74, 0x24, 0x08,
//SvL: 18-7-'98: push internal pe2lx version
        0x68, 0x00, 0x00, 0x00, 0x00,
//SvL: 18-7-'98: push version resource id (-1 = no version resource present)
        0x68, 0xFF, 0xFF, 0xFF, 0xFF,
//push  [table id1] (kernel32 proc depends on 0x77777777 & 0x88888888!!)
//Svl: So does void LXHeader::SaveConvertedNames()!
        0x68, 0x77, 0x77, 0x77, 0x77,
//push  [table id1]
        0x68, 0x88, 0x88, 0x88, 0x88,
//push  tib_object_address
        0x68, 0x00, 0x00, 0x00, 0x00,
//push  tib_initialized_size
        0x68, 0x00, 0x00, 0x00, 0x00,
//push  tib_object_size
        0x68, 0x00, 0x00, 0x00, 0x00,
//push  tib_index_address
        0x68, 0x00, 0x00, 0x00, 0x00,
//push  tib_callback_address
        0x68, 0x00, 0x00, 0x00, 0x00,
//push  original_entrypoint
        0x68, 0x00, 0x00, 0x00, 0x00,
//mov   ecx, KERNEL32:RegisterResourceInfo (stdcall)
        0xB9, 0x99, 0x99, 0x99, 0x99,
//call  ecx
        0xFF, 0xD1,
//ret
        0xC3};

//******************************************************************************
//******************************************************************************

LXHeader::LXHeader() : fFlags(0), EntryAddress(0), IsEXE(TRUE),
                       impmodules(NULL), impnames(NULL), impfixuprec(NULL),
                       nrimpfixups(0), StackSize(0), nrsections(0),
                       nrintfixups(0), intfixuprec(NULL), os2resource(NULL), nrids(0),
                       nrresources(0), curresource(0), exports(NULL), resids(NULL),
                       nrexports(0), curexport(0), expbundle(NULL), curexpb(NULL),
                       cvtname(NULL), curcvtname(0), nrcvtnames(0), datapage(NULL),
                       cvtnametableid(0), orgrestableid(0), orgrestable(NULL), nrorgres(0),
                       kernel32Object(-1), fConsole(FALSE),
		       tlsAddress(0), tlsIndexAddr(0), tlsInitSize(0), tlsTotalSize(0), tlsCallBackAddr(0),
                       VersionResourceId(-1), impnamesize(0), impmodulesize(0)
{
  ZERO(LXHdr);

  LXHdr.e32_magic[0] = E32MAGIC1;
  LXHdr.e32_magic[1] = E32MAGIC2;
  LXHdr.e32_border   = E32LEBO;
  LXHdr.e32_worder   = E32LEWO;
  LXHdr.e32_level    = E32LEVEL;
  LXHdr.e32_cpu      = E32CPU386;
  LXHdr.e32_os       = 0x01;            //OS/2
  LXHdr.e32_ver      = 0;               //who cares which version...
  LXHdr.e32_pagesize = 4096;            //default
  LXHdr.e32_pageshift= 0;               //default (doet watcom zo)
  LXHdr.e32_fixupsum = 0;               //no checksum supported
  LXHdr.e32_ldrsum   = 0;               //no checksum supported
  LXHdr.e32_mflags   = 0;
  LXHdr.e32_startobj = 1;       //code
  LXHdr.e32_objcnt   = 0;
  LXHdr.e32_objtab   = sizeof(LXHdr);   //begin hiermee vlak na exe hdr
  ZERO(ResSection);
  memset(&PESection[0], 0, sizeof(PESection));
}
//******************************************************************************
//******************************************************************************
LXHeader::~LXHeader()
{
  if(impmodules)         free(impmodules);
  if(impnames)           free(impnames);
  if(impfixuprec)        free(impfixuprec);
  if(os2resource)        free(os2resource);
  if(resids)             free(resids);
  if(cvtname)            free(cvtname);
  if(orgrestable)        free(orgrestable);
  if(ResSection.rawdata) free(ResSection.rawdata);
  if(exports)            free(exports);
  if(expbundle)          free(expbundle);
  if(datapage)           free(datapage);
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetNoFixups()
{
  LXHdr.e32_mflags |= E32NOINTFIX;
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetExeType(BOOL IsEXE)
{
  this->IsEXE = IsEXE;
  if(IsEXE) {
  	LXHdr.e32_mflags |= E32MODEXE;
  }
  else {//Assuming per process initialization/termination...
        LXHdr.e32_mflags |= E32LIBINIT | E32LIBTERM | E32MODDLL;
  }
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetEntryAddress(int address)
{
  LXHdr.e32_eip = address;
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetStackSize(int size)
{
  if(IsEXE) {
        //Open32 requires a stack size of at least 64k
//      if(size < 64*1024)      StackSize = 64*1024;
        if(size < 128*1024)     StackSize = 128*1024;
        else                    StackSize = size;
        //KSO - Aug 5 1998 8:13pm: (Borlandfix) stacksize in TIB depend on this.
        LXHdr.e32_stacksize = StackSize;
  }
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetModuleName(char *filename)
{
char fname[CCHMAXPATH];

  strcpy(fname, filename);
  strcpy(modulename, StripExtension(StripPath(fname)));
  UpCase(modulename);
}
//******************************************************************************
//******************************************************************************
void LXHeader::StoreSection(char *rawdata, int rawsize, int virtualsize, int address, int type)
{
#ifndef RING0
static int idx = 0, i;
Section TmpSec[MAX_SECTION];

  //Several exes have sections with virtual size 0
  if(virtualsize == 0)  virtualsize = rawsize;

  assert(rawdata);

  if(nrsections == 0) {
        PESection[0].type        = type;
        PESection[0].rawdata     = rawdata;
        PESection[0].address     = address;
        PESection[0].rawsize     = rawsize;
//SvL: 5-9-'98
        PESection[0].virtualsize = max(virtualsize,rawsize);
//      PESection[0].virtualsize = virtualsize;
        PESection[0].nrpages     = ((PESection[0].virtualsize + 0xFFF) & ~0xFFF) >> PAGE_SHIFT;
        PESection[0].endaddress  = address + PESection[0].nrpages*PAGE_SIZE - 1;
  }
  else { //anders op volgorde toevoegen
        memset(&TmpSec[0], 0, sizeof(TmpSec));
        i = 0;
        while(i < idx && PESection[i].address < address) {
                memcpy(&TmpSec[i], &PESection[i], sizeof(PESection[i]));
                i++;
                assert( i < MAX_SECTION );  /*PLF Mon  98-02-09 23:44:23*/
        }
        TmpSec[i].type        = type;
        TmpSec[i].rawdata     = rawdata;
        TmpSec[i].address     = address;
        TmpSec[i].rawsize     = rawsize;
        TmpSec[i].virtualsize = max(virtualsize,rawsize);
        TmpSec[i].nrpages     = ((TmpSec[i].virtualsize + 0xFFF) & ~0xFFF) >> PAGE_SHIFT;
        TmpSec[i].endaddress  = address + TmpSec[i].nrpages*PAGE_SIZE - 1;
        while(i < idx && PESection[i].rawdata) {
                assert( i+1 < MAX_SECTION );  /*PLF Mon  98-02-09 23:44:23*/
                memcpy(&TmpSec[i+1], &PESection[i], sizeof(PESection[i]));
                i++;
        }
        memcpy(&PESection[0], &TmpSec[0], (idx+1)*sizeof(PESection[0]));
  }
  idx++;
  nrsections++;
#else
        int i,j;


        //Several exes have sections with virtual size 0
        if (virtualsize == 0)  virtualsize = rawsize;

        /* sorry this was not optimized! */
        /*find place in section table (sort on address)*/
        i = 0;
        while (i < nrsections/*idx?*/ && PESection[i].address < address )
                i++;

        if ( i != nrsections )
        {
                /* move sections from this one entry back */
                j = nrsections;
                while(j > i)
                {
                        memcpy(&PESection[j], &PESection[j-1], sizeof(PESection[j-1]));
                        j--;
                }
        }

        PESection[i].type        = type;
        #ifndef RING0
                PESection[i].rawdata  = rawdata;
        #else
                PESection[i].rawdata  = rawdata ? (rawdata) : ~0;
        #endif
        PESection[i].address     = address;
#if 1
        PESection[i].rawsize     = min(rawsize,virtualsize);
        PESection[i].virtualsize = max(virtualsize,rawsize);
#else
        PESection[i].rawsize     = rawsize;//min(rawsize,virtualsize);
        PESection[i].virtualsize = virtualsize;//max(virtualsize,rawsize);
#endif
        PESection[i].nrpages     = ((virtualsize + 0xFFF) & ~0xFFF) >> PAGE_SHIFT;
        PESection[i].endaddress  = address + PESection[i].nrpages*PAGE_SIZE - 1;


        /* Inc number of sections */
        nrsections++;
        /* it wasn't that hard was it? - this code will at least not cause a stack overflow. */
#endif
}
//******************************************************************************
//SvL: Align code section at 64 kb boundary
//SvL: Put adjacent data section in one big section and align it to a 64 kb
//     boundary
//ASSUMPTIONS:  - Code section comes first
//              - (data) sections that aren't adjacent are aligned at 64kb boundary
//I could fix the second assumption, but I'm too lazy right now
//******************************************************************************
void LXHeader::AlignSections()
{
int     i, j, idx = 0, rawsize;
Section CombinedData;

  //Put TIBFix code before actual win32 code (prevents overlaps)!
  PESection[0].address       -= PAGE_SIZE;
  PESection[0].nrpages++;

  if(!(LXHdr.e32_mflags & E32NOINTFIX))
        return; //rest not needed if there are internal fixups

  PESection[0].nrinvalidpages = (PESection[0].address & 0xFFFF) >> PAGE_SHIFT;
  PESection[0].address       &= 0xFFFF0000;

  //SvL: Concatenate code & data sections into one LX object
  memcpy(&CombinedData, &PESection[0], sizeof(Section));

  //Insert invalid pages to align section at 64 kb boundary
  CombinedData.type           = SECTION_COMBINEDDATA;
  CombinedData.rawdata        = NULL;   //not used here (just use the originals)

  //too much, but just to be sure (in case we need to add extra invalid pages)
  //TODO: This should be done safer in the future
  datapage = (DataPage *)malloc((GetNrPages()+256)*sizeof(DataPage));

  //process pages of first section (code)
  rawsize = CombinedData.rawsize;

  datapage[0].size  = EXE_TIB_SIZE;
  datapage[0].flags = 0;
  idx               = 1;
  for(j=1;j<CombinedData.nrpages;j++) {
        if(rawsize < PAGE_SIZE) {
                datapage[idx].size = rawsize;
                rawsize            = 0;
        }
        else {
                datapage[idx].size = PAGE_SIZE;
                rawsize           -= PAGE_SIZE;
        }
        datapage[idx].flags= 0;
        idx++;
  }

  for(i=1;i<nrsections;i++) {
        if(PESection[i].address != CombinedData.endaddress + 1) {
                if((PESection[i].address & 0xFFFF) == 0)        //Watcom generated exe
                        return; //all sections are already properly aligned
                cout << "Section not aligned at 64 kb boundary!!!" << endl;
                //Damn, more work to do.
                int insertpages;

                insertpages = (PESection[i].address - CombinedData.endaddress-1) >> PAGE_SHIFT;
                for(j=0;j<insertpages;j++) {
                        datapage[idx].size  = 0;
                        datapage[idx].flags = ZEROED;
                        idx++;
                }
                CombinedData.nrpages += insertpages;
        }

        CombinedData.nrpages     += PESection[i].nrpages;
        CombinedData.rawsize     += PESection[i].rawsize;
//SvL: 5-9-'98
        CombinedData.virtualsize += max(PESection[i].rawsize, PESection[i].virtualsize);
        CombinedData.endaddress   = PESection[i].endaddress;

        rawsize = PESection[i].rawsize;
        for(j=0;j<PESection[i].nrpages;j++) {
                if(PESection[i].type == SECTION_UNINITDATA || rawsize == 0) {
                        datapage[idx].size  = 0;
                        datapage[idx].flags = ZEROED;
                }
                else {
                        if(rawsize < PAGE_SIZE) {
                                datapage[idx].size = rawsize;
                                rawsize            = 0;
                        }
                        else {
                                datapage[idx].size = PAGE_SIZE;
                                rawsize           -= PAGE_SIZE;
                        }
                        datapage[idx].flags= 0;
                }
                idx++;
        }
        PESection[i].fInvalid = TRUE;
  }
  memcpy(&PESection[nrsections++], &CombinedData, sizeof(Section));
  PESection[0].fInvalid = TRUE;
}
//******************************************************************************
//******************************************************************************
int LXHeader::GetNrObjects()
{
 int nrobjs = 0, i;

  for(i=0;i<nrsections;i++) {
        if(PESection[i].fInvalid == FALSE)
                nrobjs++;
  }
  return(nrobjs);
}
//******************************************************************************
//******************************************************************************
#ifdef __WATCOMC__
#pragma off (unreferenced)
#endif
void LXHeader::AddNameExport(int address, char *name, int ordinal)
#ifdef __WATCOMC__
#pragma on (unreferenced)
#endif
{
  if(strlen(name) > (127-3))    name[126] = 0;  //OS/2 maximum

  *curexport = (BYTE)strlen(name);
  curexport++;
  strcpy(curexport, name);
  curexport += strlen(name);
  *(USHORT *)curexport = (USHORT)(ordinal);
  curexport += sizeof(USHORT);
}
//******************************************************************************
//******************************************************************************
void LXHeader::AddOrdExport(int address, int ordinal)
{
 static int lastord = 0;
 int i;

  if(ordinal > lastord+1) {//add empty record(s)
   int diff = ordinal - lastord - 1;
        while(diff) {
                curexpb->b32_cnt  = min(diff, 255);
                diff             -= curexpb->b32_cnt;
                curexpb->b32_type = EMPTY;
                curexpb = (exportbundle *)((int)curexpb + 2*sizeof(char));
        }
  }

  curexpb->b32_cnt    = 1;
  curexpb->b32_type   = ENTRY32;
  i                   = GetRealSectionByAddress(address);
  curexpb->b32_obj    = (USHORT)(i + 1); //start at 1 instead of 0

  curexpb->e32_flags  = E32EXPORT | E32SHARED;
  curexpb->e32_offset = address - PESection[i].address;
  lastord             = ordinal;
  curexpb++;
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetNrExtFixups(int nr)
{
int len;

  exports   = (char *)malloc(64+nr*(127+1+2));  //length byte, name (max 127) + ordinal (USHORT)
  memset(exports, 0, 64+nr*(127+1+2));
  if(nr > 0) {
        expbundle = (exportbundle *)malloc(nr*max(sizeof(exportbundle), sizeof(forwardbundle)));
        memset(expbundle, 0, nr*max(sizeof(exportbundle), sizeof(forwardbundle)));
  }
  curexpb    = expbundle;

  curexport  = exports;
  //First entry in resident name table is module name
  strcpy(&curexport[1], modulename);
  len                  = strlen(&curexport[1]);
  *curexport           = (char)len;
  curexport           += len+1;
  *(USHORT *)curexport = 0;
  curexport           += sizeof(USHORT);
#if 0
  //first entry in export table is unused
  curexpb->b32_cnt    = 1;
  curexpb->b32_type   = EMPTY;
  curexpb = (exportbundle *)&curexpb->b32_obj;
#endif
}
//******************************************************************************
//TODO: finish this
//forward gives the DLL name and name of the export (i.e. MYDLL.expfunc or MYDLL.#27)
//******************************************************************************
#ifdef __WATCOMC__
#pragma off (unreferenced)
#endif
void LXHeader::AddForwarder(char *name, int ordinal, char *forward)
#ifdef __WATCOMC__
#pragma on (unreferenced)
#endif
{
#if 0

  *curexport = (BYTE)strlen(name);
  curexport++;
  strncpy(curexport, name, 127);
  curexport += strlen(name);
  *(USHORT *)curexport = (USHORT)ordinal;
  curexport += sizeof(USHORT);

  nrexports++;
#endif
}
//******************************************************************************
//iets te veel, but who cares
//******************************************************************************
void LXHeader::SetNrOff32Fixups(int nr)
{
  intfixuprec = (intfixup *)malloc((nr+EXTRA_TLS_FIXUPS)*sizeof(intfixup));
  memset(intfixuprec, 0, (nr+EXTRA_TLS_FIXUPS)*sizeof(intfixup));
}
//******************************************************************************
//SvL: TLS Section support
//Add three interal fixups. One for start of TLS section, one for DWORD that
//receives the TLS index and one for the array of TLS callbacks
//Also one for the entrypoint address.
//******************************************************************************
void LXHeader::AddExtraFixups()
{
  *(ULONG *)&szEXETIBFix[EXE_OFF_TIBOBJ_ADDR] = tlsAddress;
  *(ULONG *)&szEXETIBFix[EXE_OFF_TIBINIT_SIZE] = tlsInitSize;
  *(ULONG *)&szEXETIBFix[EXE_OFF_TIBOBJ_SIZE] = tlsTotalSize;
  *(ULONG *)&szEXETIBFix[EXE_OFF_TIB_INDEX]   = tlsIndexAddr;
  *(ULONG *)&szEXETIBFix[EXE_OFF_TIB_CALLBACK_ADDR] = tlsCallBackAddr;
  *(ULONG *)&szEXETIBFix[EXE_OFF_ENTRYPOINT] = EntryAddress;

  if(LXHdr.e32_mflags & E32NOINTFIX)
	return;		//no internal fixups, so we don't have to add them either

  AddOff32Fixup(PESection[0].address + EXE_OFF_ENTRYPOINT, TRUE);

  if(tlsAddress == 0)
	return;	//no TLS section in this image

  AddOff32Fixup(PESection[0].address + EXE_OFF_TIBOBJ_ADDR, TRUE);
  AddOff32Fixup(PESection[0].address + EXE_OFF_TIB_INDEX, TRUE);
  AddOff32Fixup(PESection[0].address + EXE_OFF_TIB_CALLBACK_ADDR, TRUE);

  return;
}
//******************************************************************************
//SvL: if fLookatStartupCode is set to true, we'll read the szEXETIBFix
//     fixup addresses instead of those in the section
//     (section 0 is main code section which doesn't include szEXETIBFix)
//******************************************************************************
void LXHeader::AddOff32Fixup(int address, BOOL fLookatStartupCode)
{
 static intfixup  crossedpage;
 static BOOL      fCrossed = FALSE;
 static int       crossedpageaddress = 0;
 intfixup  *frec;
 int type, targetaddr;

  frec             = &intfixuprec[nrintfixups];
  //Check if we need to insert a crossed page fixup
  if(fCrossed == TRUE && address > crossedpageaddress) {
        memcpy((char *)frec, (char *)&crossedpage, sizeof(crossedpage));
        fCrossed           = FALSE;
        crossedpageaddress = 0;
        nrintfixups++;
        frec++;
  }
  frec->nr_stype   = 0x07;              //32 bits offset fixup
  //internal fixup + 32 bits offset + 16 bits object nr
  frec->nr_flags   = 0x00 | 0x10 | 0x40;
  frec->r32_soff   = (SHORT)(address & 0xFFF);

  type = GetSectionByAddress(address);
  if(type == -1) {
        cout << "Oops, fixup error 1.... at " << hex(address) << endl;
        return;     //hmmmmmmmmmm
  }
  frec->srcpage  = (USHORT)GetAddressPage(address);
  frec->srcobj   = (USHORT)type;

  //SvL: address could be aligned at 64 kb boundary, so correct address calculation
  if(type == 0) { // +1 for tibfix page
        assert(PESection[type].type & SECTION_CODE);
	if(fLookatStartupCode)
	{
		targetaddr = *(int *)(&szEXETIBFix[0] + (address & 0xFF));
	}
	else {
        	targetaddr = *(int *)(PESection[type].rawdata + (address - (PESection[type].address + (PESection[type].nrinvalidpages+1)*PAGE_SIZE)));
	}
  }
  else  targetaddr = *(int *)(PESection[type].rawdata + (address - (PESection[type].address + PESection[type].nrinvalidpages*PAGE_SIZE)));

  //SvL: The target object might be the combined data section, so don't use
  //     GetSectionByAddress
  type = GetRealSectionByAddress(targetaddr);
  if(type == -1){
        cout << "Oops, fixup error 2.... at " << hex(targetaddr) << endl;
        return;     //hmmmmmmmmmm
  }

  //SvL: If it's in the combined data section, the index is incorrect, so
  //     correct it
  if(PESection[type].type == SECTION_COMBINEDDATA)
        frec->targetobj  = (USHORT)(1+1);       //ASSUMPTION
  else  frec->targetobj  = (USHORT)(type+1); //vanaf 1
  frec->targetaddr = targetaddr - PESection[type].address; //relatief
//  cout << "src address " << frec->r32_soff << " srcpage " << frec->srcpage << " target " << frec->targetaddr << " frec " << (int)frec << endl;

  //SvL: Check for fixups that cross page boundaries
  //     If I read the lx doc right, you need to add two fixups in
  //     this case, one for original page and one for original page + 1
  //TODO: Doesn't work if the last page contains such a fixup!
  if(frec->r32_soff + 4 > PAGE_SIZE) {
        memcpy((char *)&crossedpage, (char *)frec, sizeof(crossedpage));
        //crossed page boundary -> negative offset in next page
        crossedpage.r32_soff  = (SHORT)((frec->r32_soff & 0xFFF) - PAGE_SIZE);
        crossedpage.srcpage   = frec->srcpage+1;
        crossedpageaddress    = (address + PAGE_SIZE) & ~0xFFF;
        fCrossed              = TRUE;
  }

  nrintfixups++;
}
//******************************************************************************
//SvL: 30-10-'97, assumption about sorting of fixups is incorrect, so:
//ASSUMPTION: namefixup & ordfixup of same size
//******************************************************************************
void LXHeader::StoreAndSortImport(namefixup *newfrec)
{
 namefixup *frec;
 namefixup *tmp;
 int        i;

  if(nrimpfixups != 0) {
        frec = &impfixuprec[nrimpfixups - 1];

        if(newfrec->srcpage >= frec->srcpage) {
                memcpy(&impfixuprec[nrimpfixups], newfrec, sizeof(namefixup));
                nrimpfixups++;
                return;
        }
  }
  else {
        memcpy(&impfixuprec[0], newfrec, sizeof(namefixup));
        nrimpfixups++;
        return;
  }
  tmp = (namefixup *)malloc(nrimpfixups*sizeof(namefixup));

  frec = &impfixuprec[0];
  for(i=0;i<nrimpfixups;i++) {
        if(newfrec->srcpage < frec->srcpage) {
                memcpy(tmp, frec, (nrimpfixups-i)*sizeof(namefixup));
                memcpy(frec, newfrec, sizeof(namefixup));
                memcpy(frec+1, tmp, (nrimpfixups-i)*sizeof(namefixup));
                break;
        }
        frec++;
  }
  assert(i != nrimpfixups);
  free(tmp);
  nrimpfixups++;
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetNoNameImports()
{
  impnames    = (char *)malloc(16);
  memset(impnames, 0, 16);
  impnameoff  = impnames + 1;   //SvL: Bugfix: base 1
  impnamesize = 0;              //include ending 0
}
//******************************************************************************
//******************************************************************************
BOOL LXHeader::StoreImportByName(char *modname, int idxmod, char *name, int offset)
{
 namefixup frec;

  if(impfixuprec == NULL) {
        impfixuprec = (namefixup *)malloc(MAX_IMPORT*sizeof(namefixup));
        memset(impfixuprec, 0, MAX_IMPORT*sizeof(namefixup));
  }
  if(impnames == NULL) {
        impnames   = (char *)malloc(128*1024);  //moet genoeg zijn
        memset(impnames, 0, 128*1024);
        impnameoff  = impnames + 1;     //SvL: Bugfix: base 1
        impnamesize = 1;                //include ending 0
  }

  if(nrimpfixups >= MAX_IMPORT) {
        cout << "Oops, too many imported functions...." << endl;
        return(FALSE);
  }

  frec.nr_stype   = 0x07;         //32 bits offset
  frec.nr_flags   = 0x02 | 0x40; //import by name + mod offset = 16 bits
  frec.r32_soff   = (SHORT)(offset & 0xFFF);    //offset within specific page
  frec.r32_objmod = (USHORT)(idxmod+1);   //vanaf 1!!!!!

  impnameoff[0]    = (char)strlen(name);
  strcpy(&impnameoff[1], name);
  frec.proc       = (USHORT)((int)impnameoff-(int)impnames); //32 bits target flag not set!
  frec.srcpage    = (USHORT)GetAddressPage(offset);

  impnamesize     += impnameoff[0]+1;
  impnameoff      += impnameoff[0]+1;

  StoreAndSortImport(&frec);
  return(TRUE);
}
//******************************************************************************
//******************************************************************************
BOOL LXHeader::StoreImportByOrd(int idxmod, int ordinal, int offset)
{
 ordfixup frec;

  if(impfixuprec == NULL) {
        impfixuprec = (namefixup *)malloc(MAX_IMPORT*sizeof(namefixup));
        memset(impfixuprec, 0, MAX_IMPORT*sizeof(namefixup));
  }

  if(nrimpfixups >= MAX_IMPORT) {
        cout << "Oops, too many imported functions...." << endl;
        return(FALSE);
  }

  frec.nr_stype   = 0x07;               //32 bits offset
  frec.nr_flags   = 0x01 | 0x40;        //import by ordinal + mod offset = 16 bits
  frec.r32_soff   = (SHORT)(offset & 0xFFF);    //offset within specific page
  frec.r32_objmod = (USHORT)(idxmod+1);   //vanaf 1!!!!!
  frec.ord        = (USHORT)ordinal;
  frec.srcpage    = (USHORT)GetAddressPage(offset);

  StoreAndSortImport((namefixup *)&frec);
  return(TRUE);
}
//******************************************************************************
//******************************************************************************
void LXHeader::StoreImportModules(char *modules, int nrmod)
{
int size = 0, i;
char *src, *dest;

///-4*nrmod = - 4 * .dll
  impmodules       = (char *)malloc(nrmod*9 + 32);  /*PLF Sun  97-06-22 00:51:12*/
  impmodulesize    = 0;

  src  = modules;
  dest = impmodules;
  for(i=0;i<nrmod;i++) {
        size = min(strlen(src), 12);
        dest[0] = (char)(size-4);
        dest++;
        strncpy(dest, src, size-4);
        dest[size-4] = 0;   // !!!
        UpCase(dest);

/* @@@PH 98/06/13 NTDLL is available now */
//      if(strcmp(dest, "NTDLL") == 0) {
//              strcpy(dest, "KERNEL32");
//              dest[-1] = 8;   //change module name length
//              dest    += 8;
//      }
//        else


        if(strcmp(dest, "NETAPI32") == 0) {//SvL: Name conflict with MUGLIB\DLL\NETAPI32.DLL
                strcpy(dest, "WNETAP32");
                dest += size-4;
        }
#if 0
	else
        if(strcmp(dest, "OLE32") == 0) {//SvL: Name conflict with Lotus OLE32 dll
              strcpy(dest, "OLE32OS2");
              dest[-1] = 8;   //change module name length
              dest    += 8;
        }
#endif
        else {
                if(strcmp(dest, "KERNEL32") == 0) {
                        kernel32Object = i;
                }
                dest += size-4;
        }
        src  += strlen(src) + 1;        //+ null byte
  }
  if(kernel32Object == -1) { //add kernel32
        kernel32Object = nrmod;
        dest[0] = 8;
        strcpy(&dest[1], "KERNEL32");
        dest += 9;
        nrmod++;
  }
  LXHdr.e32_impmodcnt = nrmod;
  dest[0] = 0;          //end of array
  impmodulesize = (int)dest - (int)impmodules;
}
//******************************************************************************
char *sysmodules[] = {"KERNEL32",
                      "USER32",
                      "GDI32",
                      "WINMM",
                      "DSOUND",
                      "DDRAW",
                      "WNETAP32",
                      "MPR",
                      "OLE32",
                      "ADVAPI32",
                      "COMMDLG",
                      "NTDLL",
                      "WINSPOOL",
                      "SHELL32",
                      "TAPI32",
                      "CAPI32",
                      "VERSION",
                      "WSOCK32"};

//******************************************************************************
BOOL LXHeader::IsSystemModule(char *mod, int size)
{
 int i;

  for(i=0;i<sizeof(sysmodules)/sizeof(char *);i++) {
        if(!strncmp(mod, sysmodules[i], size))
                return(TRUE);
  }
  return(FALSE);
}
//******************************************************************************
//******************************************************************************
int LXHeader::FindName(char *table, int index)
{
int i, offset = 0;

  for(i=0;i<index-1;i++) {
        offset += table[0] + 1;
        table = table + table[0] + 1;
  }
  return(offset);
}
//******************************************************************************
//******************************************************************************
BOOL LXHeader::SaveNewExeFile(char *filename)
{
 HFILE  win32handle;
 ULONG  ulAction       = 0;      /* Action taken by DosOpen */
 ULONG  ulLocal        = 0;      /* File pointer position after DosSetFilePtr */
 ULONG  ulWrite;
 APIRET rc;
 struct o32_obj objtable;
 struct o32_map *objpage;
 int pagetblidx, i, j, k, idx, rawsize, objectsize;
 int nrpages, pagedataoffset, fFirstCode = FALSE;
 int *fixuptable;
 char modname[128];
 intfixup      *frec;
 namefixup     *imprec;
 realintfixup  *frealrec, *currealrec;

  rc = DosOpen(filename,                       /* File path name */
               &win32handle,                   /* File handle */
               &ulAction,                      /* Action taken */
               0L,                             /* File primary allocation */
               0L,                             /* File attribute */
               OPEN_ACTION_CREATE_IF_NEW |
               OPEN_ACTION_REPLACE_IF_EXISTS,     /* Open function type */
               OPEN_FLAGS_NOINHERIT |
               OPEN_SHARE_DENYNONE  |
               OPEN_ACCESS_READWRITE,          /* Open mode of the file */
               0L);                            /* No extended attribute */

  if (rc != NO_ERROR) {
       cout << "DosOpen returned " << rc << endl;
       return(FALSE);
  }
  /* Move the file pointer back to the beginning of the file */
  DosSetFilePtr(win32handle, 0L, FILE_BEGIN, &ulLocal);

  rc = DosWrite(win32handle, szDosHeader, sizeof(szDosHeader), &ulWrite);
  if(rc != NO_ERROR) {
        cout << "DosWrite returned " << rc << endl;
        DosClose(win32handle);                /* Close the file */
        return(FALSE);
  }
  //write preliminary lx header
  DosWrite(win32handle, &LXHdr, sizeof(LXHdr), &ulWrite);

  //First take care of the resources (if present), so GetNrPages()
  //works correctly
  if(nrresources) {
        ResSection.virtualsize = ResSection.curoff;
        ResSection.rawsize     = ResSection.curoff;
  } //done with resources

  pagetblidx            = 1;    //start met 1
  objtable.o32_reserved = 0;
  pagedataoffset        = 0;
  nrpages               = GetNrPages();
  objpage               = (struct o32_map *)malloc((nrpages)*sizeof(o32_map));

  LXHdr.e32_objcnt      = GetNrObjects();
  LXHdr.e32_mpages      = 0;
  idx                   = -1;
  int            objcnt = 1;

  for(i=0;i<nrsections;i++) {
        objtable.o32_pagemap     = pagetblidx;
        if(PESection[i].fInvalid)       continue;

        switch(PESection[i].type) {
                case (SECTION_CODE | SECTION_IMPORT):
                case SECTION_CODE:
                        objtable.o32_flags = OBJREAD | NSEXRD | OBJBIGDEF;
                        if(fFirstCode == FALSE)
                                LXHdr.e32_startobj = i+1;
                        break;
                case SECTION_IMPORT:
                        objtable.o32_flags = OBJREAD | OBJBIGDEF;
                        break;
                case SECTION_INITDATA:
                        objtable.o32_flags = OBJREAD | OBJWRITE | OBJBIGDEF;
                        break;
                case SECTION_COMBINEDDATA:
                        objtable.o32_flags = OBJREAD | OBJWRITE | OBJBIGDEF;
                        LXHdr.e32_startobj = 1;
                        break;
                case SECTION_RESOURCE_ORG:
                case SECTION_UNINITDATA:
                        //SvL: Zero filled flag maps on Permanent flag, so ditch it
                        objtable.o32_flags = OBJREAD | OBJWRITE | OBJBIGDEF; //read/write/big
                        break;
                case SECTION_READONLYDATA:
                        objtable.o32_flags = OBJREAD | OBJBIGDEF;
                        break;
        }
        //OS/2 doesn't seem to mind if the sizes of all the pages in an object
        //exceeds the object size....
        if(PESection[i].type == SECTION_UNINITDATA)
                objectsize = PESection[i].virtualsize;  //rawsize == 0
        else    objectsize = PESection[i].rawsize;

        objtable.o32_mapsize = PESection[i].nrpages;

        objtable.o32_mapsize    += PESection[i].nrinvalidpages;
        objtable.o32_base        = PESection[i].address;
        objtable.o32_size        = objtable.o32_mapsize*PAGE_SIZE;

        rawsize = PESection[i].rawsize;
        for(j=0;j<objtable.o32_mapsize;j++) {
                idx = pagetblidx+j-1;
                objpage[idx].o32_pagedataoffset = pagedataoffset;

                if(j < PESection[i].nrinvalidpages) {
                        objpage[idx].o32_pagesize       = 0;
//SvL: Invalid pages are not properly handled by the OS/2 loader -> make 'em zeroed
                        objpage[idx].o32_pageflags      = ZEROED;
                }
                else {
                        if(PESection[i].type == SECTION_COMBINEDDATA) {
                                if(j == PESection[i].nrinvalidpages) {
                                        objpage[idx].o32_pagesize        = (USHORT)(EXE_TIB_SIZE);
                                        objpage[idx].o32_pageflags       = 0;
                                        pagedataoffset                  += EXE_TIB_SIZE;
                                        LXHdr.e32_mpages++;
                                        LXHdr.e32_eip                    = PAGE_SIZE*PESection[i].nrinvalidpages;
                                        objtable.o32_size                = objtable.o32_mapsize*PAGE_SIZE;
                                }
                                else {
                                        int didx = j - PESection[i].nrinvalidpages;
                                        objpage[idx].o32_pagesize  = datapage[didx].size;
                                        objpage[idx].o32_pageflags = datapage[didx].flags;
                                        pagedataoffset += objpage[idx].o32_pagesize;
                                        if(objpage[idx].o32_pageflags == 0)
                                                LXHdr.e32_mpages++;
                                }
                        }
                        else
                        if(PESection[i].type == SECTION_UNINITDATA || rawsize == 0) {
                                objpage[idx].o32_pagesize       = 0;
                                objpage[idx].o32_pagedataoffset = pagedataoffset;
                                objpage[idx].o32_pageflags      = ZEROED;
                        }
                        else //take care of TIBFix code!
                        if(fFirstCode == FALSE && PESection[i].type & SECTION_CODE && j == PESection[i].nrinvalidpages) {
                                objpage[idx].o32_pagesize        = (USHORT)EXE_TIB_SIZE;
                                objpage[idx].o32_pageflags       = 0;
                                pagedataoffset                  += EXE_TIB_SIZE;
                                LXHdr.e32_mpages++;

                                LXHdr.e32_eip                    = PAGE_SIZE*PESection[i].nrinvalidpages;
                        }
                        else {
                                if(rawsize > PAGE_SIZE)
                                        objpage[idx].o32_pagesize = PAGE_SIZE;
                                else    objpage[idx].o32_pagesize = rawsize;
                                rawsize                        -= objpage[idx].o32_pagesize;
                                objpage[idx].o32_pageflags      = 0;
                                LXHdr.e32_mpages++;
                                pagedataoffset                 += objpage[idx].o32_pagesize;
                        }
                }
        }

        if(IsEXE && i == nrsections - 1)
        {//SvL: 25-7-'97 Add stack pages to last data object
                LXHdr.e32_stackobj     = objcnt;
                LXHdr.e32_autodata     = LXHdr.e32_stackobj;
                objtable.o32_flags    |= OBJWRITE;
                objtable.o32_size     += StackSize;
                LXHdr.e32_esp          = objtable.o32_size; //highest address in object
        }

        //Write object table entry to file
        DosWrite(win32handle, &objtable, sizeof(objtable), &ulWrite);

        if(PESection[i].type & SECTION_CODE)
                fFirstCode = TRUE;      //dealt with first code section

        pagetblidx    += objtable.o32_mapsize;
        objcnt++;
  }
  if(nrresources) {
        //Correct count in case there are string tables which aren't completely filled
        nrresources = curresource;
        //Process Resource Object
        LXHdr.e32_objcnt++;
        objtable.o32_pagemap   = pagetblidx;
        objtable.o32_flags     = NSSHARED | OBJREAD | OBJRSRC | OBJBIGDEF;
        objtable.o32_mapsize   = ResSection.rawsize >> PAGE_SHIFT;

        objtable.o32_base      = 0;     //SvL: Set to 0 in PM exes (plf)
        objtable.o32_size      = ResSection.rawsize;

        for(j=0;j<objtable.o32_mapsize;j++) {
                idx = pagetblidx+j-1;
                objpage[idx].o32_pagedataoffset = pagedataoffset;
                objpage[idx].o32_pagesize       = PAGE_SIZE;
                objpage[idx].o32_pageflags      = 0;
                LXHdr.e32_mpages++;
                pagedataoffset                 += PAGE_SIZE;
        }
        if((objtable.o32_size & 0xFFF) != 0) {
                objtable.o32_mapsize++;
                LXHdr.e32_mpages++;
                idx++;
                objpage[idx].o32_pagedataoffset = pagedataoffset;
                objpage[idx].o32_pagesize       = (USHORT)(objtable.o32_size & 0xFFF);
                objpage[idx].o32_pageflags      = 0;
                pagedataoffset                 += objpage[idx].o32_pagesize;
        }

        //Write resource object table to file
        DosWrite(win32handle, &objtable, sizeof(objtable), &ulWrite);
        pagetblidx    += objtable.o32_mapsize;

        //fill in object nr's in resource table entries
        for(i=0;i<nrresources;i++) {
                os2resource[i].obj = LXHdr.e32_objcnt;
        }
  }

  //Write object page table entries to file
  DosWrite(win32handle, objpage, sizeof(*objpage)*nrpages, &ulWrite);

  //object page records offset:
  LXHdr.e32_objmap    = LXHdr.e32_objtab + LXHdr.e32_objcnt*sizeof(objtable);
  //size tables vanaf object table t/m impproc (eigenlijk pagesum ook..)
  LXHdr.e32_ldrsize   = LXHdr.e32_objcnt*sizeof(objtable) + nrpages*sizeof(o32_map);

  //resource table:
  LXHdr.e32_rsrctab   = LXHdr.e32_ldrsize + LXHdr.e32_objtab;
  LXHdr.e32_rsrccnt   = nrresources;
  if(nrresources) {
        DosWrite(win32handle, (char *)os2resource, nrresources*sizeof(rsrc32), &ulWrite);
  }
  LXHdr.e32_ldrsize  += nrresources*sizeof(rsrc32);

  //resident name table + entry table:
  LXHdr.e32_restab    = LXHdr.e32_rsrctab + nrresources*sizeof(rsrc32);
  if(IsEXE == TRUE) {
    int modlen;
        //save resident name table
        memset(modname, 0, sizeof(modname));
        strcpy(&modname[1], modulename);
        UpCase(&modname[1]);
        modlen = strlen(&modname[1]);
        modname[0] = (char)modlen;
        modlen += 1 + 2 + 1; //len byte + ord word + 0 byte
        DosWrite(win32handle, modname, modlen, &ulWrite);

        //save entry table
        LXHdr.e32_enttab    = LXHdr.e32_restab + modlen;        //points to 0 byte
        modname[0] = 0;
        DosWrite(win32handle, modname, 1, &ulWrite);
        LXHdr.e32_ldrsize  += 1 + modlen;
  }
  else {//write DLL export information
     int len = 0;

        //save resident name table
        if(exports) {
                len = curexport - exports + 1;  // + 0 byte to signal end of table
                rc = DosWrite(win32handle, exports, len, &ulWrite);
                LXHdr.e32_ldrsize += len;
        }

        //save entry table
        LXHdr.e32_enttab = LXHdr.e32_restab + len;
        //TODO: Forwarders!
        len = (int)curexpb - (int)expbundle + 1; // + 0 byte to signal end of table
        if(len > 1) {
                rc = DosWrite(win32handle, expbundle, len, &ulWrite);
                LXHdr.e32_ldrsize += len;
        }
  }

  //#pages * offset naar import fixups + 1 eind offset
  LXHdr.e32_fpagetab  = LXHdr.e32_ldrsize + LXHdr.e32_objtab;
  LXHdr.e32_frectab   = LXHdr.e32_fpagetab + (nrpages+1)*sizeof(int);
  fixuptable = (int *)malloc((nrpages+1)*sizeof(int));
  for(i=0;i<nrpages+1;i++)  fixuptable[i] = 0;

  //dump alle fixup records in 1 memory block en bepaal offsets in fixup page table
  frec       = &intfixuprec[0];
  imprec     = &impfixuprec[0];
  frealrec   = (realintfixup *)malloc(nrintfixups*sizeof(realintfixup)+(nrimpfixups+EXTRA_FIXUPS)*sizeof(realnamefixup));
  currealrec = frealrec;
  j          = 0;
  k          = 0;
  for(i=0;i<nrpages;i++) {
        fixuptable[i] = (int)currealrec - (int)frealrec;
        if(i == 0) {//add kernel32:registerresourceinfo import
          realordfixup *kerord = (realordfixup *)currealrec;
                kerord->nr_stype   = 0x07;              //32 bits offset
                kerord->nr_flags   = 0x01 | 0x40;       //import by ordinal + mod offset = 16 bits
                kerord->r32_soff   = EXE_OFF_ENTRYFIX;
                kerord->r32_objmod = kernel32Object + 1;
                if(IsEXE)
                        kerord->ord        = ORD_REGISTERRESOURCEINFO;
                else    kerord->ord        = ORD_REGISTERDLL;

                currealrec = (realintfixup *)((int)currealrec + sizeof(realnamefixup));
        }
        //SvL: 16-9-'97, for multiple import pages
        while(k < nrimpfixups && imprec->srcpage == i) {
                //sizeof(realordfixup) == sizeof(realnamefixup)
                memcpy(currealrec, imprec, sizeof(realnamefixup));
                currealrec = (realintfixup *)((int)currealrec + sizeof(realnamefixup));
                imprec++;
                k++;
        }
        while( j < nrintfixups && frec->srcpage == i) {
                memcpy(currealrec, frec, sizeof(realintfixup));
//  cout << "src address " << frec->r32_soff << " srcpage " << frec->srcpage << " target " << frec->targetaddr << " frec " << (int)frec << endl;
                frec++;
                currealrec++;
                j++;
        }
  }
  fixuptable[nrpages] = (int)currealrec - (int)frealrec;

  //Add extra kernel32 imports
  nrimpfixups += EXTRA_FIXUPS;

  //Write fixup page table
  DosWrite(win32handle, fixuptable, (nrpages+1)*sizeof(int), &ulWrite);
  //Write fixup record table
  DosWrite(win32handle, frealrec, nrintfixups*sizeof(realintfixup)+nrimpfixups*sizeof(realnamefixup), &ulWrite);
  free(frealrec);
  free(fixuptable);

  //fixup page + record + import name + module table size
  LXHdr.e32_fixupsize = (nrpages+1)*sizeof(int) + nrimpfixups*sizeof(realnamefixup) + nrintfixups*sizeof(realintfixup) + impmodulesize + impnamesize;

  //size tables vanaf object table t/m impproc (eigenlijk pagesum ook..)
  LXHdr.e32_ldrsize  += LXHdr.e32_fixupsize;

  //offset naar module name array
  LXHdr.e32_impmod    = LXHdr.e32_frectab + nrimpfixups*sizeof(realnamefixup) + nrintfixups*sizeof(realintfixup);
  //offset naar proc name array
  LXHdr.e32_impproc   = LXHdr.e32_impmod + impmodulesize;
  //Write Import Module array
  DosWrite(win32handle, impmodules, impmodulesize, &ulWrite);
  //Write Import Procedure Name array
  if(impnamesize) {
  	DosWrite(win32handle, impnames, impnamesize, &ulWrite);
  }

  //pointer naar begin code/data
  LXHdr.e32_datapage  = LXHdr.e32_impproc + impnamesize + LXHEADER_OFFSET;      //vanaf begin ipv lxheader

  fFirstCode = FALSE;   //TIBfix only for first code object
  for(i=0;i<nrsections;i++) {
        //zero filled pages, niet nodig om op te slaan
        if(PESection[i].type != SECTION_UNINITDATA && PESection[i].rawdata) {
                //Put our special TIB fix to code section first! (SvL: 30-7-'97)
                if(PESection[i].type & SECTION_CODE && fFirstCode == FALSE) {
                        DosWrite(win32handle, szEXETIBFix, EXE_TIB_SIZE, &ulWrite);
                        fFirstCode = TRUE;
                }
                DosWrite(win32handle, PESection[i].rawdata, PESection[i].rawsize, &ulWrite);
        }
  }
  if(nrresources)
        DosWrite(win32handle, ResSection.rawdata, ResSection.rawsize, &ulWrite);

  DosSetFilePtr(win32handle, LXHEADER_OFFSET, FILE_BEGIN, &ulLocal);
  //write final lx header
  DosWrite(win32handle, &LXHdr, sizeof(LXHdr), &ulWrite);

  free(objpage);
  DosClose(win32handle);                /* Close the file */
  return(TRUE);
}
//******************************************************************************
//Requires fixing the resource size (as done in savenewexe)
//******************************************************************************
int LXHeader::GetNrPages()
{
 int i, nrpages = 0;

  for(i=0;i<nrsections;i++) {
        if(PESection[i].fInvalid)
                continue;
        nrpages += PESection[i].nrpages + PESection[i].nrinvalidpages;
  }
//SvL: 13 aug 1998
#if 0
  if(IsEXE) {
        nrpages += StackSize >> PAGE_SHIFT;
        if(StackSize & 0xFFF)   nrpages++;
  }
#endif
  if(nrresources) {
        nrpages += ResSection.virtualsize >> PAGE_SHIFT;
        if(ResSection.rawsize & 0xFFF) nrpages++;
  }
  return(nrpages);
}
//******************************************************************************
//******************************************************************************
void LXHeader::UpCase(char *mixedcase)
{
 int i;

  for(i=0;i<strlen(mixedcase);i++) {
        if(mixedcase[i] >= 'a' && mixedcase[i] <= 'z') {
                mixedcase[i] += 'A' - 'a';
        }
  }
}
//******************************************************************************
//******************************************************************************
void LXHeader::SetModuleType(int type)
{
  LXHdr.e32_mflags   |= E32PMAPI;

  if(IsEXE) //don't set it for dlls (in case this flag is accidentally set in the header)
        fConsole = (type == SYSTEM_CHARACTER);
}
//******************************************************************************
//******************************************************************************
int LXHeader::GetAddressPage(int address)
{
 int i, page = 0;

  for(i=0;i<nrsections;i++) {
        if(PESection[i].fInvalid)
                continue;
        if(address > PESection[i].endaddress) {
                page += PESection[i].nrpages + PESection[i].nrinvalidpages;
        }
        else {
                page += ((address - PESection[i].address) >> PAGE_SHIFT);
                break;
        }
  }
  return(page);
}
//******************************************************************************
//******************************************************************************
int LXHeader::GetSection(int type)
{
 int i;

  for(i=0;i<nrsections;i++) {
        if(PESection[i].type & type) {
                return(i);
        }
  }
  return(-1);
}
//******************************************************************************
//SvL: Ignore combined data section, since we use the rawdata pointer
//******************************************************************************
int LXHeader::GetSectionByAddress(int address)
{
 int i;

  for(i=0;i<nrsections;i++) {
        if(PESection[i].rawdata) {
                if(PESection[i].address <= address && PESection[i].endaddress > address ) {
                        return(i);
                }
        }
  }
  return(-1);
}
//******************************************************************************
//SvL: Ignore sections combined into one data section
//******************************************************************************
int LXHeader::GetRealSectionByAddress(int address)
{
 int i;

  for(i=0;i<nrsections;i++) {
        if(PESection[i].fInvalid)       continue;
        if(PESection[i].rawdata || PESection[i].type == SECTION_COMBINEDDATA) {
                if(PESection[i].address <= address && PESection[i].endaddress > address ) {
                        return(i);
                }
        }
  }
  return(-1);
}
//******************************************************************************
//******************************************************************************
int LXHeader::GetSectionLEAddress(int address)
{
 int i;

  for(i=0;i<nrsections;i++) {
        if(PESection[i].fInvalid)       continue;
        if(PESection[i].rawdata) {
                if(PESection[i].address > address) {
                        return(i-1);
                }
        }
  }
  return(-1);
}
//******************************************************************************
//******************************************************************************
char *LXHeader::StripPath(char *path)           //niets aan de string veranderen!!
{
  while(strchr(path, '\\') != NULL) {
        path++;
  }
  return(path);
}
/******************************************************************************/
/******************************************************************************/
char *LXHeader::StripExtension(char *fname)
{
 char *tmp = fname;

  while(strchr(fname, '.') != NULL) {
        fname++;
  }
  fname--;
  *fname = 0;
  return(tmp);
}
/******************************************************************************/
/******************************************************************************/
int LXHeader::GetUniqueId()
{
 int i, j, id;

  for(j=0;j<65536;j++) {
        for(i=0;i<nrids;i++) {
                if(uniqueId == resids[i]) {
                        break;
                }
        }
        if(i == nrids) {
                id = uniqueId;
                resids[nrids] = id;
                nrids++;
                uniqueId = (uniqueId + 1) & 0xFFFF;
                return(id);
        }
        uniqueId = (uniqueId + 1) & 0xFFFF;
  }
  cout << "No ids left!!!!" << endl;
  assert(FALSE);
  return(0);
}
/******************************************************************************/
/******************************************************************************/
void LXHeader::SetNrResources(int cnt)
{
  cout << "Nr of resources: " << cnt << endl;

  //+2 for resource name lookup table and original res lookup table
  nrresources = cnt+2;
  //reserve space for original win32 resources + strings
  os2resource = (rsrc32 *)malloc((nrresources*16)*sizeof(rsrc32));
  memset(os2resource, 0, nrresources*16*sizeof(rsrc32));
  cvtname    = (NameId *)malloc(cnt*(sizeof(NameId)+256));
  curcvtname = (NameId *)((char *)cvtname+sizeof(USHORT));      //first USHORT = nr of converted names
  memset(cvtname, 0, cnt*(sizeof(NameId)+256));

  //temporary storage for used resource ids (*16 since menus use getuniqueid too)
  resids = (int *)malloc(nrresources*16*sizeof(int));
  memset(resids, 0, nrresources*16*sizeof(int));

  //table used in LockResource (kernel32) to return the original win32 resource
  //layout:     1st ULONG       nr of entries
  //            nth ULONG (n>1) 0xAAAABBBB, AAAA=org id, BBBB=OS/2 id
  orgrestable = (ULONG *)malloc((nrresources+1)*sizeof(ULONG));
  memset(orgrestable, 0, (nrresources+1)*sizeof(ULONG));
}
//******************************************************************************
//******************************************************************************
void LXHeader::StoreResourceId(int id)
{
  resids[nrids] = id;
  nrids++;
}
/******************************************************************************/
/******************************************************************************/
void LXHeader::StoreWin32Resource(int id, int type, int size, char *resourcedata)
{
 int newid = GetUniqueId();

  //First entry used for nr of entries, so + 1
  orgrestable[nrorgres+1] = ((id & 0xFFFF) << 16) + (newid & 0xFFFF);
  nrorgres++;

  //store them all as rcdata
  StoreResource(newid, RT_RCDATA, size, resourcedata);
}
/******************************************************************************/
/******************************************************************************/
void LXHeader::StoreResource(int id, int type, int size, char *resourcedata)
{
 static BOOL fFirstIcon = FALSE;
 char       *tmpbuf;
 int         i, newsize;

  if(type == RT_POINTER && fFirstIcon == FALSE && curresource > 0) {
        //put this one in front of all resources so OS/2 recognizes
        //it as the program object icon
        tmpbuf = (char *)malloc(curresource*sizeof(rsrc32)+ResSection.curoff);
        memcpy(tmpbuf, (char *)os2resource, curresource*sizeof(rsrc32));
        memcpy((char *)&os2resource[1], tmpbuf, curresource*sizeof(rsrc32));
        memcpy(tmpbuf, ResSection.rawdata, ResSection.curoff);
        memcpy(ResSection.rawdata, resourcedata, size);
        //SvL: Align all resources on 4 byte boundary
        newsize = (size + 3) & ~3;
        for(i=0;i<newsize-size;i++) {
                ResSection.rawdata[size+i] = 0;
        }
        memcpy(ResSection.rawdata+newsize, tmpbuf, ResSection.curoff);

        os2resource[0].type    = (USHORT)type;
        os2resource[0].name    = (USHORT)id;
        os2resource[0].cb      = size;
        os2resource[0].offset  = 0;
        //modify all offsets
        for(i=1;i<=curresource;i++) {
                os2resource[i].offset += newsize;
        }
        free(tmpbuf);
  }
  else {
        assert(ResSection.curoff + size < ResSection.rawsize);
        memcpy(ResSection.rawdata+ResSection.curoff, resourcedata, size);
        //SvL: Align all resources on 4 byte boundary
        newsize = (size + 3) & ~3;
        for(i=0;i<newsize-size;i++) {
                ResSection.rawdata[ResSection.curoff+size+i] = 0;
        }
        os2resource[curresource].type   = (USHORT)type;
        os2resource[curresource].name   = (USHORT)id;
        os2resource[curresource].cb     = size;
        os2resource[curresource].offset = ResSection.curoff;
  }

  ResSection.curoff              += newsize;
  curresource++;
  return;
}
/******************************************************************************/
/******************************************************************************/
void LXHeader::SetResourceSize(int size)
{
  ResSection.virtualsize = ResSection.rawsize = size*4; //just to be sure
  ResSection.rawdata     = (char *)malloc(ResSection.rawsize);
  memset(ResSection.rawdata, 0, ResSection.rawsize);
}
/******************************************************************************/
/******************************************************************************/
int LXHeader::ConvertNametoId(char *name)
{
 NameId *resname = (NameId *)((USHORT *)cvtname + 1);
 int id;
 int i;

  for(i=0;i<nrcvtnames;i++) {//check if this name is already converted
        if(stricmp(name, resname->name) == 0)
                return(resname->id);
        resname = (NameId *)((int)resname + sizeof(NameId) + strlen(resname->name));
  }
  //Not found, so create one
  id = GetUniqueId();
  curcvtname->id = id;
  strcpy(curcvtname->name, name);
  curcvtname = (NameId *)((int)curcvtname + sizeof(NameId) + strlen(name));
  nrcvtnames++;
  return(id);
}
/******************************************************************************/
/******************************************************************************/
void LXHeader::SaveConvertedNames()
{
  if(nrcvtnames) {
        //first USHORT = nr of converted names
        *(USHORT *)cvtname = nrcvtnames;
        cvtnametableid = GetUniqueId();

        //fill in parameter for RegisterResourceInfo
        *(LONG *)&szEXETIBFix[EXE_OFF_NAMETABLE] = cvtnametableid;

        StoreResource(cvtnametableid, RT_RCDATA, (int)curcvtname - (int)cvtname, (char *)cvtname);
  }
  if(nrorgres) {
        //first ULONG = nr of original win32 resources stored in OS/2 exe/dll
        orgrestable[0] = nrorgres;
        orgrestableid = GetUniqueId();

        //fill in parameter for RegisterResourceInfo
        *(LONG *)&szEXETIBFix[EXE_OFF_ORGRESTABLE] = (fConsole << 24) | orgrestableid;

        StoreResource(orgrestableid, RT_RCDATA, (nrorgres+1)*sizeof(ULONG), (char *)orgrestable);
  }
  else {
        *(LONG *)&szEXETIBFix[EXE_OFF_ORGRESTABLE] = (fConsole << 24) | 0x888888;
  }
  //SvL: 18-7-'98: Store internal pe2lx version and version resource id
  *(LONG *)&szEXETIBFix[EXE_OFF_PE2LXVER] = PE2LX_VERSION;
  *(LONG *)&szEXETIBFix[EXE_OFF_VERRESID] = VersionResourceId;
}
/******************************************************************************/
/******************************************************************************/
int LXHeader::uniqueId = RESID_CONVERTEDNAMES;
